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 | |