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
add_cfg_dir(char ** cfg_dirs,const char * directory)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
release_configuration_dirs()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
get_default_configuration_dirs()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 /* CONC-537: Read configuration files from MYSQL_HOME directory only if
128 MARIADB_HOME was not set */
129 if (!(env= getenv("MARIADB_HOME")))
130 env= getenv("MYSQL_HOME");
131 if (env && add_cfg_dir(configuration_dirs, env))
132 goto error;
133 end:
134 return configuration_dirs;
135 error:
136 return NULL;
137 }
138
139 extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
140
is_group(char * ptr,const char ** groups)141 static my_bool is_group(char *ptr, const char **groups)
142 {
143 while (*groups)
144 {
145 if (!strcmp(ptr, *groups))
146 return 1;
147 groups++;
148 }
149 return 0;
150 }
151
_mariadb_read_options_from_file(MYSQL * mysql,const char * config_file,const char * group,unsigned int recursion)152 static my_bool _mariadb_read_options_from_file(MYSQL *mysql,
153 const char *config_file,
154 const char *group,
155 unsigned int recursion)
156 {
157 uint line=0;
158 my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0;
159 char buff[4096],*ptr,*end,*value, *key= 0, *optval;
160 MA_FILE *file= NULL;
161 my_bool rc= 1;
162 const char *groups[5]= {"client",
163 "client-server",
164 "client-mariadb",
165 group,
166 NULL};
167 my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value);
168
169
170 /* if a plugin registered a hook we will call this hook, otherwise
171 * default (_mariadb_set_conf_option) will be called */
172 if (mysql->options.extension && mysql->options.extension->set_option)
173 set_option= mysql->options.extension->set_option;
174 else
175 set_option= _mariadb_set_conf_option;
176
177 if (!(file = ma_open(config_file, "r", NULL)))
178 goto err;
179
180 while (ma_gets(buff,sizeof(buff)-1,file))
181 {
182 line++;
183 key= 0;
184 /* Ignore comment and empty lines */
185 for (ptr=buff ; isspace(*ptr) ; ptr++ );
186 if (!is_escaped && (*ptr == '\"' || *ptr== '\''))
187 {
188 is_quoted= !is_quoted;
189 continue;
190 }
191 /* CONC- 327: !includedir and !include */
192 if (*ptr == '!')
193 {
194 char *val;
195 ptr++;
196 if (!(val= strchr(ptr, ' ')))
197 continue;
198 *val++= 0;
199 end= strchr(val, 0);
200 for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
201 *end= 0;
202 if (!strcmp(ptr, "includedir"))
203 _mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1);
204 else if (!strcmp(ptr, "include"))
205 _mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1);
206 continue;
207 }
208 if (*ptr == '#' || *ptr == ';' || !*ptr)
209 continue;
210 is_escaped= (*ptr == '\\');
211 if (*ptr == '[') /* Group name */
212 {
213 found_group=1;
214 if (!(end=(char *) strchr(++ptr,']')))
215 {
216 /* todo: set error */
217 goto err;
218 }
219 for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
220 end[0]=0;
221 read_values= is_group(ptr, groups);
222 continue;
223 }
224 if (!found_group)
225 {
226 /* todo: set error */
227 goto err;
228 }
229 if (!read_values)
230 continue;
231 if (!(end=value=strchr(ptr,'=')))
232 {
233 end=strchr(ptr, '\0'); /* Option without argument */
234 set_option(mysql, ptr, NULL);
235 }
236 if (!key)
237 key= ptr;
238 for ( ; isspace(end[-1]) ; end--) ;
239 *end= 0;
240 if (value)
241 {
242 /* Remove pre- and end space */
243 char *value_end;
244 *value= 0;
245 value++;
246 ptr= value;
247 for ( ; isspace(*value); value++) ;
248 value_end=strchr(value, '\0');
249 *value_end= 0;
250 optval= ptr;
251 for ( ; isspace(value_end[-1]) ; value_end--) ;
252 /* remove possible quotes */
253 if (*value == '\'' || *value == '\"')
254 {
255 value++;
256 if (value_end[-1] == '\'' || value_end[-1] == '\"')
257 value_end--;
258 }
259 if (value_end < value) /* Empty string */
260 value_end=value;
261 for ( ; value != value_end; value++)
262 {
263 if (*value == '\\' && value != value_end-1)
264 {
265 switch(*++value) {
266 case 'n':
267 *ptr++='\n';
268 break;
269 case 't':
270 *ptr++= '\t';
271 break;
272 case 'r':
273 *ptr++ = '\r';
274 break;
275 case 'b':
276 *ptr++ = '\b';
277 break;
278 case 's':
279 *ptr++= ' '; /* space */
280 break;
281 case '\"':
282 *ptr++= '\"';
283 break;
284 case '\'':
285 *ptr++= '\'';
286 break;
287 case '\\':
288 *ptr++= '\\';
289 break;
290 default: /* Unknown; Keep '\' */
291 *ptr++= '\\';
292 *ptr++= *value;
293 break;
294 }
295 }
296 else
297 *ptr++= *value;
298 }
299 *ptr=0;
300 set_option(mysql, key, optval);
301 key= optval= 0;
302 }
303 }
304 rc= 0;
305
306 err:
307 if (file)
308 ma_close(file);
309 return rc;
310 }
311
312
_mariadb_read_options(MYSQL * mysql,const char * config_dir,const char * config_file,const char * group,unsigned int recursion)313 my_bool _mariadb_read_options(MYSQL *mysql,
314 const char *config_dir,
315 const char *config_file,
316 const char *group,
317 unsigned int recursion)
318 {
319 int i= 0,
320 exts,
321 errors= 0;
322 char filename[FN_REFLEN + 1];
323 unsigned int recursion_stop= 64;
324 #ifndef _WIN32
325 char *env;
326 #endif
327
328 if (recursion >= recursion_stop)
329 return 1;
330
331 if (config_file && config_file[0])
332 return _mariadb_read_options_from_file(mysql, config_file, group, recursion);
333
334 if (config_dir && config_dir[0])
335 {
336 for (exts= 0; ini_exts[exts]; exts++)
337 {
338 snprintf(filename, FN_REFLEN,
339 "%s%cmy.%s", config_dir, FN_LIBCHAR, ini_exts[exts]);
340 if (!access(filename, R_OK))
341 errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
342 }
343 return errors;
344 }
345
346 for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++)
347 {
348 for (exts= 0; ini_exts[exts]; exts++)
349 {
350 snprintf(filename, FN_REFLEN,
351 "%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]);
352 if (!access(filename, R_OK))
353 errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
354 }
355 }
356 #ifndef _WIN32
357 /* special case: .my.cnf in Home directory */
358 if ((env= getenv("HOME")))
359 {
360 for (exts= 0; ini_exts[exts]; exts++)
361 {
362 snprintf(filename, FN_REFLEN,
363 "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]);
364 if (!access(filename, R_OK))
365 errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
366 }
367 }
368 #endif
369 return errors;
370 }
371