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 | |
54 | if (i < MAX_CONFIG_DIRS) { |
55 | cfg_dirs[i]= strdup(directory); |
56 | return 0; |
57 | } |
58 | return 1; |
59 | } |
60 | |
61 | void 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 | |
72 | char **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; |
127 | end: |
128 | return configuration_dirs; |
129 | error: |
130 | return NULL; |
131 | } |
132 | |
133 | extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value); |
134 | |
135 | static 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 | |
146 | static 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 | |
307 | err: |
308 | return rc; |
309 | } |
310 | |
311 | |
312 | my_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 | |