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
54 if (i < MAX_CONFIG_DIRS) {
55 cfg_dirs[i]= strdup(directory);
56 return 0;
57 }
58 return 1;
59}
60
61void release_configuration_dirs()
62{
63 if (configuration_dirs)
64 {
65 int i= 0;
66 while (configuration_dirs[i])
67 free(configuration_dirs[i++]);
68 free(configuration_dirs);
69 }
70}
71
72char **get_default_configuration_dirs()
73{
74#ifdef _WIN32
75 char dirname[FN_REFLEN];
76#endif
77 char *env;
78
79 configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *));
80 if (!configuration_dirs)
81 goto end;
82
83#ifdef _WIN32
84 /* On Windows operating systems configuration files are stored in
85 1. System directory
86 2. Windows directory
87 3. C:\
88 */
89 if (!GetSystemDirectory(dirname, FN_REFLEN) ||
90 add_cfg_dir(configuration_dirs, dirname))
91 goto error;
92
93 if (!GetWindowsDirectory(dirname, FN_REFLEN) ||
94 add_cfg_dir(configuration_dirs, dirname))
95 goto error;
96
97 if (add_cfg_dir(configuration_dirs, "C:"))
98 goto error;
99
100 if (GetModuleFileName(NULL, dirname, FN_REFLEN))
101 {
102 PathRemoveFileSpec(dirname);
103 if (add_cfg_dir(configuration_dirs, dirname))
104 goto error;
105 }
106#else
107 /* on *nix platforms configuration files are stored in
108 1. SYSCONFDIR (if build happens inside server package, or
109 -DDEFAULT_SYSCONFDIR was specified
110 2. /etc
111 3. /etc/mysql
112 */
113#ifdef DEFAULT_SYSCONFDIR
114 if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR))
115 goto error;
116#else
117 if (add_cfg_dir(configuration_dirs, "/etc"))
118 goto error;
119 if (add_cfg_dir(configuration_dirs, "/etc/mysql"))
120 goto error;
121#endif
122#endif
123/* This differs from https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/ where MYSQL_HOME is not specified for Windows */
124 if ((env= getenv("MYSQL_HOME")) &&
125 add_cfg_dir(configuration_dirs, env))
126 goto error;
127end:
128 return configuration_dirs;
129error:
130 return NULL;
131}
132
133extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
134
135static my_bool is_group(char *ptr, const char **groups)
136{
137 while (*groups)
138 {
139 if (!strcmp(ptr, *groups))
140 return 1;
141 groups++;
142 }
143 return 0;
144}
145
146static my_bool _mariadb_read_options_from_file(MYSQL *mysql,
147 const char *config_file,
148 const char *group,
149 unsigned int recursion)
150{
151 uint line=0;
152 my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0;
153 char buff[4096],*ptr,*end,*value, *key= 0, *optval;
154 MA_FILE *file= NULL;
155 my_bool rc= 1;
156 const char *groups[5]= {"client",
157 "client-server",
158 "client-mariadb",
159 group,
160 NULL};
161 my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value);
162
163
164 /* if a plugin registered a hook we will call this hook, otherwise
165 * default (_mariadb_set_conf_option) will be called */
166 if (mysql->options.extension && mysql->options.extension->set_option)
167 set_option= mysql->options.extension->set_option;
168 else
169 set_option= _mariadb_set_conf_option;
170
171 if (!(file = ma_open(config_file, "r", NULL)))
172 goto err;
173
174 while (ma_gets(buff,sizeof(buff)-1,file))
175 {
176 line++;
177 key= 0;
178 /* Ignore comment and empty lines */
179 for (ptr=buff ; isspace(*ptr) ; ptr++ );
180 if (!is_escaped && (*ptr == '\"' || *ptr== '\''))
181 {
182 is_quoted= !is_quoted;
183 continue;
184 }
185 /* CONC- 327: !includedir and !include */
186 if (*ptr == '!')
187 {
188 char *val;
189 ptr++;
190 if (!(val= strchr(ptr, ' ')))
191 continue;
192 *val++= 0;
193 end= strchr(val, 0);
194 for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
195 *end= 0;
196 if (!strcmp(ptr, "includedir"))
197 _mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1);
198 else if (!strcmp(ptr, "include"))
199 _mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1);
200 continue;
201 }
202 if (*ptr == '#' || *ptr == ';' || !*ptr)
203 continue;
204 is_escaped= (*ptr == '\\');
205 if (*ptr == '[') /* Group name */
206 {
207 found_group=1;
208 if (!(end=(char *) strchr(++ptr,']')))
209 {
210 /* todo: set error */
211 goto err;
212 }
213 for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
214 end[0]=0;
215 read_values= is_group(ptr, groups);
216 continue;
217 }
218 if (!found_group)
219 {
220 /* todo: set error */
221 goto err;
222 }
223 if (!read_values)
224 continue;
225 if (!(end=value=strchr(ptr,'=')))
226 {
227 end=strchr(ptr, '\0'); /* Option without argument */
228 set_option(mysql, ptr, NULL);
229 }
230 if (!key)
231 key= ptr;
232 for ( ; isspace(end[-1]) ; end--) ;
233 *end= 0;
234 if (!value)
235 {
236 if (!key)
237 key= ptr;
238 }
239 else
240 {
241 /* Remove pre- and end space */
242 char *value_end;
243 *value= 0;
244 value++;
245 ptr= value;
246 for ( ; isspace(*value); value++) ;
247 value_end=strchr(value, '\0');
248 *value_end= 0;
249 optval= ptr;
250 for ( ; isspace(value_end[-1]) ; value_end--) ;
251 /* remove possible quotes */
252 if (*value == '\'' || *value == '\"')
253 {
254 value++;
255 if (value_end[-1] == '\'' || value_end[-1] == '\"')
256 value_end--;
257 }
258 if (value_end < value) /* Empty string */
259 value_end=value;
260 for ( ; value != value_end; value++)
261 {
262 if (*value == '\\' && value != value_end-1)
263 {
264 switch(*++value) {
265 case 'n':
266 *ptr++='\n';
267 break;
268 case 't':
269 *ptr++= '\t';
270 break;
271 case 'r':
272 *ptr++ = '\r';
273 break;
274 case 'b':
275 *ptr++ = '\b';
276 break;
277 case 's':
278 *ptr++= ' '; /* space */
279 break;
280 case '\"':
281 *ptr++= '\"';
282 break;
283 case '\'':
284 *ptr++= '\'';
285 break;
286 case '\\':
287 *ptr++= '\\';
288 break;
289 default: /* Unknown; Keep '\' */
290 *ptr++= '\\';
291 *ptr++= *value;
292 break;
293 }
294 }
295 else
296 *ptr++= *value;
297 }
298 *ptr=0;
299 set_option(mysql, key, optval);
300 key= optval= 0;
301 }
302 }
303 if (file)
304 ma_close(file);
305 rc= 0;
306
307err:
308 return rc;
309}
310
311
312my_bool _mariadb_read_options(MYSQL *mysql,
313 const char *config_dir,
314 const char *config_file,
315 const char *group,
316 unsigned int recursion)
317{
318 int i= 0,
319 exts,
320 errors= 0;
321 char filename[FN_REFLEN + 1];
322 unsigned int recursion_stop= 64;
323#ifndef _WIN32
324 char *env;
325#endif
326
327 if (recursion >= recursion_stop)
328 return 1;
329
330 if (config_file && config_file[0])
331 return _mariadb_read_options_from_file(mysql, config_file, group, recursion);
332
333 if (config_dir && config_dir[0])
334 {
335 for (exts= 0; ini_exts[exts]; exts++)
336 {
337 snprintf(filename, FN_REFLEN,
338 "%s%cmy.%s", config_dir, FN_LIBCHAR, ini_exts[exts]);
339 if (!access(filename, R_OK))
340 errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
341 }
342 return errors;
343 }
344
345 for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++)
346 {
347 for (exts= 0; ini_exts[exts]; exts++)
348 {
349 snprintf(filename, FN_REFLEN,
350 "%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]);
351 if (!access(filename, R_OK))
352 errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
353 }
354 }
355#ifndef _WIN32
356 /* special case: .my.cnf in Home directory */
357 if ((env= getenv("HOME")))
358 {
359 for (exts= 0; ini_exts[exts]; exts++)
360 {
361 snprintf(filename, FN_REFLEN,
362 "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]);
363 if (!access(filename, R_OK))
364 errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
365 }
366 }
367#endif
368 return errors;
369}
370