| 1 | /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB |
| 2 | 2016 MariaDB Corporation AB |
| 3 | |
| 4 | This library is free software; you can redistribute it and/or |
| 5 | modify it under the terms of the GNU Library General Public |
| 6 | License as published by the Free Software Foundation; either |
| 7 | version 2 of the License, or (at your option) any later version. |
| 8 | |
| 9 | This library is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | Library General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU Library General Public |
| 15 | License along with this library; if not, write to the Free |
| 16 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| 17 | MA 02111-1301, USA */ |
| 18 | |
| 19 | #include <ma_global.h> |
| 20 | #include <ma_sys.h> |
| 21 | #include "ma_string.h" |
| 22 | #include <ctype.h> |
| 23 | #include "mariadb_ctype.h" |
| 24 | #include <mysql.h> |
| 25 | #include <ma_common.h> |
| 26 | #include <mariadb/ma_io.h> |
| 27 | |
| 28 | #ifdef _WIN32 |
| 29 | #include <io.h> |
| 30 | #include "shlwapi.h" |
| 31 | |
| 32 | static const char *ini_exts[]= {"ini" , "cnf" , 0}; |
| 33 | #define R_OK 4 |
| 34 | #else |
| 35 | #include <unistd.h> |
| 36 | static const char *ini_exts[]= {"cnf" , 0}; |
| 37 | #endif |
| 38 | |
| 39 | char **configuration_dirs= NULL; |
| 40 | #define MAX_CONFIG_DIRS 6 |
| 41 | |
| 42 | my_bool _mariadb_read_options(MYSQL *mysql, |
| 43 | const char *config_dir, |
| 44 | const char *config_file, |
| 45 | const char *group, |
| 46 | unsigned int recursion); |
| 47 | |
| 48 | static int add_cfg_dir(char **cfg_dirs, const char *directory) |
| 49 | { |
| 50 | int i; |
| 51 | |
| 52 | for (i = 0; i < MAX_CONFIG_DIRS && cfg_dirs[i]; i++) |
| 53 | if (!strcmp(cfg_dirs[i], directory)) /* already present */ |
| 54 | return 0; |
| 55 | |
| 56 | if (i < MAX_CONFIG_DIRS) { |
| 57 | cfg_dirs[i]= strdup(directory); |
| 58 | return 0; |
| 59 | } |
| 60 | return 1; |
| 61 | } |
| 62 | |
| 63 | void release_configuration_dirs() |
| 64 | { |
| 65 | if (configuration_dirs) |
| 66 | { |
| 67 | int i= 0; |
| 68 | while (configuration_dirs[i]) |
| 69 | free(configuration_dirs[i++]); |
| 70 | free(configuration_dirs); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | char **get_default_configuration_dirs() |
| 75 | { |
| 76 | #ifdef _WIN32 |
| 77 | char dirname[FN_REFLEN]; |
| 78 | #endif |
| 79 | char *env; |
| 80 | |
| 81 | configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *)); |
| 82 | if (!configuration_dirs) |
| 83 | goto end; |
| 84 | |
| 85 | #ifdef _WIN32 |
| 86 | /* On Windows operating systems configuration files are stored in |
| 87 | 1. System Windows directory |
| 88 | 2. System directory |
| 89 | 3. Windows directory |
| 90 | 4. C:\ |
| 91 | */ |
| 92 | |
| 93 | if (!GetSystemWindowsDirectory(dirname, FN_REFLEN) || |
| 94 | add_cfg_dir(configuration_dirs, dirname)) |
| 95 | goto error; |
| 96 | |
| 97 | if (!GetWindowsDirectory(dirname, FN_REFLEN) || |
| 98 | add_cfg_dir(configuration_dirs, dirname)) |
| 99 | goto error; |
| 100 | |
| 101 | if (add_cfg_dir(configuration_dirs, "C:" )) |
| 102 | goto error; |
| 103 | |
| 104 | if (GetModuleFileName(NULL, dirname, FN_REFLEN)) |
| 105 | { |
| 106 | PathRemoveFileSpec(dirname); |
| 107 | if (add_cfg_dir(configuration_dirs, dirname)) |
| 108 | goto error; |
| 109 | } |
| 110 | #else |
| 111 | /* on *nix platforms configuration files are stored in |
| 112 | 1. SYSCONFDIR (if build happens inside server package, or |
| 113 | -DDEFAULT_SYSCONFDIR was specified |
| 114 | 2. /etc |
| 115 | 3. /etc/mysql |
| 116 | */ |
| 117 | #ifdef DEFAULT_SYSCONFDIR |
| 118 | if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR)) |
| 119 | goto error; |
| 120 | #else |
| 121 | if (add_cfg_dir(configuration_dirs, "/etc" )) |
| 122 | goto error; |
| 123 | if (add_cfg_dir(configuration_dirs, "/etc/mysql" )) |
| 124 | goto error; |
| 125 | #endif |
| 126 | #endif |
| 127 | /* This differs from https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/ where MYSQL_HOME is not specified for Windows */ |
| 128 | if ((env= getenv("MYSQL_HOME" )) && |
| 129 | add_cfg_dir(configuration_dirs, env)) |
| 130 | goto error; |
| 131 | end: |
| 132 | return configuration_dirs; |
| 133 | error: |
| 134 | return NULL; |
| 135 | } |
| 136 | |
| 137 | extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value); |
| 138 | |
| 139 | static my_bool is_group(char *ptr, const char **groups) |
| 140 | { |
| 141 | while (*groups) |
| 142 | { |
| 143 | if (!strcmp(ptr, *groups)) |
| 144 | return 1; |
| 145 | groups++; |
| 146 | } |
| 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | static my_bool _mariadb_read_options_from_file(MYSQL *mysql, |
| 151 | const char *config_file, |
| 152 | const char *group, |
| 153 | unsigned int recursion) |
| 154 | { |
| 155 | uint line=0; |
| 156 | my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0; |
| 157 | char buff[4096],*ptr,*end,*value, *key= 0, *optval; |
| 158 | MA_FILE *file= NULL; |
| 159 | my_bool rc= 1; |
| 160 | const char *groups[5]= {"client" , |
| 161 | "client-server" , |
| 162 | "client-mariadb" , |
| 163 | group, |
| 164 | NULL}; |
| 165 | my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value); |
| 166 | |
| 167 | |
| 168 | /* if a plugin registered a hook we will call this hook, otherwise |
| 169 | * default (_mariadb_set_conf_option) will be called */ |
| 170 | if (mysql->options.extension && mysql->options.extension->set_option) |
| 171 | set_option= mysql->options.extension->set_option; |
| 172 | else |
| 173 | set_option= _mariadb_set_conf_option; |
| 174 | |
| 175 | if (!(file = ma_open(config_file, "r" , NULL))) |
| 176 | goto err; |
| 177 | |
| 178 | while (ma_gets(buff,sizeof(buff)-1,file)) |
| 179 | { |
| 180 | line++; |
| 181 | key= 0; |
| 182 | /* Ignore comment and empty lines */ |
| 183 | for (ptr=buff ; isspace(*ptr) ; ptr++ ); |
| 184 | if (!is_escaped && (*ptr == '\"' || *ptr== '\'')) |
| 185 | { |
| 186 | is_quoted= !is_quoted; |
| 187 | continue; |
| 188 | } |
| 189 | /* CONC- 327: !includedir and !include */ |
| 190 | if (*ptr == '!') |
| 191 | { |
| 192 | char *val; |
| 193 | ptr++; |
| 194 | if (!(val= strchr(ptr, ' '))) |
| 195 | continue; |
| 196 | *val++= 0; |
| 197 | end= strchr(val, 0); |
| 198 | for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ |
| 199 | *end= 0; |
| 200 | if (!strcmp(ptr, "includedir" )) |
| 201 | _mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1); |
| 202 | else if (!strcmp(ptr, "include" )) |
| 203 | _mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1); |
| 204 | continue; |
| 205 | } |
| 206 | if (*ptr == '#' || *ptr == ';' || !*ptr) |
| 207 | continue; |
| 208 | is_escaped= (*ptr == '\\'); |
| 209 | if (*ptr == '[') /* Group name */ |
| 210 | { |
| 211 | found_group=1; |
| 212 | if (!(end=(char *) strchr(++ptr,']'))) |
| 213 | { |
| 214 | /* todo: set error */ |
| 215 | goto err; |
| 216 | } |
| 217 | for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ |
| 218 | end[0]=0; |
| 219 | read_values= is_group(ptr, groups); |
| 220 | continue; |
| 221 | } |
| 222 | if (!found_group) |
| 223 | { |
| 224 | /* todo: set error */ |
| 225 | goto err; |
| 226 | } |
| 227 | if (!read_values) |
| 228 | continue; |
| 229 | if (!(end=value=strchr(ptr,'='))) |
| 230 | { |
| 231 | end=strchr(ptr, '\0'); /* Option without argument */ |
| 232 | set_option(mysql, ptr, NULL); |
| 233 | } |
| 234 | if (!key) |
| 235 | key= ptr; |
| 236 | for ( ; isspace(end[-1]) ; end--) ; |
| 237 | *end= 0; |
| 238 | if (value) |
| 239 | { |
| 240 | /* Remove pre- and end space */ |
| 241 | char *value_end; |
| 242 | *value= 0; |
| 243 | value++; |
| 244 | ptr= value; |
| 245 | for ( ; isspace(*value); value++) ; |
| 246 | value_end=strchr(value, '\0'); |
| 247 | *value_end= 0; |
| 248 | optval= ptr; |
| 249 | for ( ; isspace(value_end[-1]) ; value_end--) ; |
| 250 | /* remove possible quotes */ |
| 251 | if (*value == '\'' || *value == '\"') |
| 252 | { |
| 253 | value++; |
| 254 | if (value_end[-1] == '\'' || value_end[-1] == '\"') |
| 255 | value_end--; |
| 256 | } |
| 257 | if (value_end < value) /* Empty string */ |
| 258 | value_end=value; |
| 259 | for ( ; value != value_end; value++) |
| 260 | { |
| 261 | if (*value == '\\' && value != value_end-1) |
| 262 | { |
| 263 | switch(*++value) { |
| 264 | case 'n': |
| 265 | *ptr++='\n'; |
| 266 | break; |
| 267 | case 't': |
| 268 | *ptr++= '\t'; |
| 269 | break; |
| 270 | case 'r': |
| 271 | *ptr++ = '\r'; |
| 272 | break; |
| 273 | case 'b': |
| 274 | *ptr++ = '\b'; |
| 275 | break; |
| 276 | case 's': |
| 277 | *ptr++= ' '; /* space */ |
| 278 | break; |
| 279 | case '\"': |
| 280 | *ptr++= '\"'; |
| 281 | break; |
| 282 | case '\'': |
| 283 | *ptr++= '\''; |
| 284 | break; |
| 285 | case '\\': |
| 286 | *ptr++= '\\'; |
| 287 | break; |
| 288 | default: /* Unknown; Keep '\' */ |
| 289 | *ptr++= '\\'; |
| 290 | *ptr++= *value; |
| 291 | break; |
| 292 | } |
| 293 | } |
| 294 | else |
| 295 | *ptr++= *value; |
| 296 | } |
| 297 | *ptr=0; |
| 298 | set_option(mysql, key, optval); |
| 299 | key= optval= 0; |
| 300 | } |
| 301 | } |
| 302 | rc= 0; |
| 303 | |
| 304 | err: |
| 305 | if (file) |
| 306 | ma_close(file); |
| 307 | return rc; |
| 308 | } |
| 309 | |
| 310 | |
| 311 | my_bool _mariadb_read_options(MYSQL *mysql, |
| 312 | const char *config_dir, |
| 313 | const char *config_file, |
| 314 | const char *group, |
| 315 | unsigned int recursion) |
| 316 | { |
| 317 | int i= 0, |
| 318 | exts, |
| 319 | errors= 0; |
| 320 | char filename[FN_REFLEN + 1]; |
| 321 | unsigned int recursion_stop= 64; |
| 322 | #ifndef _WIN32 |
| 323 | char *env; |
| 324 | #endif |
| 325 | |
| 326 | if (recursion >= recursion_stop) |
| 327 | return 1; |
| 328 | |
| 329 | if (config_file && config_file[0]) |
| 330 | return _mariadb_read_options_from_file(mysql, config_file, group, recursion); |
| 331 | |
| 332 | if (config_dir && config_dir[0]) |
| 333 | { |
| 334 | for (exts= 0; ini_exts[exts]; exts++) |
| 335 | { |
| 336 | snprintf(filename, FN_REFLEN, |
| 337 | "%s%cmy.%s" , config_dir, FN_LIBCHAR, ini_exts[exts]); |
| 338 | if (!access(filename, R_OK)) |
| 339 | errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion); |
| 340 | } |
| 341 | return errors; |
| 342 | } |
| 343 | |
| 344 | for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++) |
| 345 | { |
| 346 | for (exts= 0; ini_exts[exts]; exts++) |
| 347 | { |
| 348 | snprintf(filename, FN_REFLEN, |
| 349 | "%s%cmy.%s" , configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]); |
| 350 | if (!access(filename, R_OK)) |
| 351 | errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion); |
| 352 | } |
| 353 | } |
| 354 | #ifndef _WIN32 |
| 355 | /* special case: .my.cnf in Home directory */ |
| 356 | if ((env= getenv("HOME" ))) |
| 357 | { |
| 358 | for (exts= 0; ini_exts[exts]; exts++) |
| 359 | { |
| 360 | snprintf(filename, FN_REFLEN, |
| 361 | "%s%c.my.%s" , env, FN_LIBCHAR, ini_exts[exts]); |
| 362 | if (!access(filename, R_OK)) |
| 363 | errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion); |
| 364 | } |
| 365 | } |
| 366 | #endif |
| 367 | return errors; |
| 368 | } |
| 369 | |