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
32static const char *ini_exts[]= {"ini", "cnf", 0};
33#define R_OK 4
34#else
35#include <unistd.h>
36static const char *ini_exts[]= {"cnf", 0};
37#endif
38
39char **configuration_dirs= NULL;
40#define MAX_CONFIG_DIRS 6
41
42my_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
48static 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
63void 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
74char **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;
131end:
132 return configuration_dirs;
133error:
134 return NULL;
135}
136
137extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
138
139static 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
150static 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
304err:
305 if (file)
306 ma_close(file);
307 return rc;
308}
309
310
311my_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