1 | /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. |
2 | Copyright (c) 2011, 2018, MariaDB Corporation |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
16 | |
17 | /**************************************************************************** |
18 | Add all options from files named "group".cnf from the default_directories |
19 | before the command line arguments. |
20 | On Windows defaults will also search in the Windows directory for a file |
21 | called 'group'.ini |
22 | As long as the program uses the last argument for conflicting |
23 | options one only have to add a call to "load_defaults" to enable |
24 | use of default values. |
25 | pre- and end 'blank space' are removed from options and values. The |
26 | following escape sequences are recognized in values: \b \t \n \r \\ |
27 | |
28 | The following arguments are handled automatically; If used, they must be |
29 | first argument on the command line! |
30 | --no-defaults ; no options are read. |
31 | --defaults-file=full-path-to-default-file ; Only this file will be read. |
32 | --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ |
33 | --defaults-group-suffix ; Also read groups with concat(group, suffix) |
34 | --print-defaults ; Print the modified command line and exit |
35 | ****************************************************************************/ |
36 | |
37 | #include "mysys_priv.h" |
38 | #include <my_default.h> |
39 | #include <m_string.h> |
40 | #include <m_ctype.h> |
41 | #include <my_dir.h> |
42 | #ifdef __WIN__ |
43 | #include <winbase.h> |
44 | #endif |
45 | |
46 | /** |
47 | arguments separator |
48 | |
49 | load_defaults() loads arguments from config file and put them |
50 | before the arguments from command line, this separator is used to |
51 | separate the arguments loaded from config file and arguments user |
52 | provided on command line. |
53 | |
54 | Options with value loaded from config file are always in the form |
55 | '--option=value', while for command line options, the value can be |
56 | given as the next argument. Thus we used a separator so that |
57 | handle_options() can distinguish them. |
58 | |
59 | Note: any other places that does not need to distinguish them |
60 | should skip the separator. |
61 | |
62 | The content of arguments separator does not matter, one should only |
63 | check the pointer, use "----args-separator----" here to ease debug |
64 | if someone misused it. |
65 | |
66 | The args separator will only be added when |
67 | my_getopt_use_args_seprator is set to TRUE before calling |
68 | load_defaults(); |
69 | |
70 | See BUG#25192 |
71 | */ |
72 | static const char *args_separator= "----args-separator----" ; |
73 | inline static void set_args_separator(char** arg) |
74 | { |
75 | DBUG_ASSERT(my_getopt_use_args_separator); |
76 | *arg= (char*)args_separator; |
77 | } |
78 | my_bool my_getopt_use_args_separator= FALSE; |
79 | my_bool my_getopt_is_args_separator(const char* arg) |
80 | { |
81 | return (arg == args_separator); |
82 | } |
83 | const char *my_defaults_file=0; |
84 | const char *my_defaults_group_suffix=0; |
85 | const char *=0; |
86 | |
87 | static char my_defaults_file_buffer[FN_REFLEN]; |
88 | static char [FN_REFLEN]; |
89 | |
90 | static my_bool defaults_already_read= FALSE; |
91 | |
92 | /* Which directories are searched for options (and in which order) */ |
93 | |
94 | #define MAX_DEFAULT_DIRS 7 |
95 | #define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1) /* Terminate with NULL */ |
96 | static const char **default_directories = NULL; |
97 | |
98 | #ifdef __WIN__ |
99 | static const char *f_extensions[]= { ".ini" , ".cnf" , 0 }; |
100 | #define NEWLINE "\r\n" |
101 | #else |
102 | static const char *f_extensions[]= { ".cnf" , 0 }; |
103 | #define NEWLINE "\n" |
104 | #endif |
105 | |
106 | static int handle_default_option(void *, const char *, const char *); |
107 | |
108 | /* |
109 | This structure defines the context that we pass to callback |
110 | function 'handle_default_option' used in search_default_file |
111 | to process each option. This context is used if search_default_file |
112 | was called from load_defaults. |
113 | */ |
114 | |
115 | struct handle_option_ctx |
116 | { |
117 | MEM_ROOT *alloc; |
118 | DYNAMIC_ARRAY *args; |
119 | TYPELIB *group; |
120 | }; |
121 | |
122 | static int search_default_file(Process_option_func func, void *func_ctx, |
123 | const char *dir, const char *config_file); |
124 | static int search_default_file_with_ext(Process_option_func func, |
125 | void *func_ctx, |
126 | const char *dir, const char *ext, |
127 | const char *config_file, int recursion_level); |
128 | |
129 | |
130 | /** |
131 | Create the list of default directories. |
132 | |
133 | @param alloc MEM_ROOT where the list of directories is stored |
134 | |
135 | @details |
136 | The directories searched, in order, are: |
137 | - Windows: GetSystemWindowsDirectory() |
138 | - Windows: GetWindowsDirectory() |
139 | - Windows: C:/ |
140 | - Windows: Directory above where the executable is located |
141 | - Unix: /etc/ or the value of DEFAULT_SYSCONFDIR, if defined |
142 | - Unix: /etc/mysql/ unless DEFAULT_SYSCONFDIR is defined |
143 | - ALL: getenv("MYSQL_HOME") |
144 | - ALL: --defaults-extra-file=<path> (run-time option) |
145 | - Unix: ~/ |
146 | |
147 | On all systems, if a directory is already in the list, it will be moved |
148 | to the end of the list. This avoids reading defaults files multiple times, |
149 | while ensuring the correct precedence. |
150 | |
151 | @retval NULL Failure (out of memory, probably) |
152 | @retval other Pointer to NULL-terminated array of default directories |
153 | */ |
154 | |
155 | static const char **init_default_directories(MEM_ROOT *alloc); |
156 | |
157 | |
158 | static char *remove_end_comment(char *ptr); |
159 | |
160 | |
161 | /* |
162 | Expand a file name so that the current working directory is added if |
163 | the name is relative. |
164 | |
165 | RETURNS |
166 | 0 All OK |
167 | 2 Out of memory or path to long |
168 | 3 Not able to get working directory |
169 | */ |
170 | |
171 | static int |
172 | fn_expand(const char *filename, char *result_buf) |
173 | { |
174 | char dir[FN_REFLEN]; |
175 | const int flags= MY_UNPACK_FILENAME | MY_SAFE_PATH | MY_RELATIVE_PATH; |
176 | DBUG_ENTER("fn_expand" ); |
177 | DBUG_PRINT("enter" , ("filename: %s, result_buf: %p" , |
178 | filename, result_buf)); |
179 | if (my_getwd(dir, sizeof(dir), MYF(0))) |
180 | DBUG_RETURN(3); |
181 | DBUG_PRINT("debug" , ("dir: %s" , dir)); |
182 | if (fn_format(result_buf, filename, dir, "" , flags) == NULL) |
183 | DBUG_RETURN(2); |
184 | DBUG_PRINT("return" , ("result: %s" , result_buf)); |
185 | DBUG_RETURN(0); |
186 | } |
187 | |
188 | /* |
189 | Process config files in default directories. |
190 | |
191 | SYNOPSIS |
192 | my_search_option_files() |
193 | conf_file Basename for configuration file to search for. |
194 | If this is a path, then only this file is read. |
195 | argc Pointer to argc of original program |
196 | argv Pointer to argv of original program |
197 | args_used Pointer to variable for storing the number of |
198 | arguments used. |
199 | func Pointer to the function to process options |
200 | func_ctx It's context. Usually it is the structure to |
201 | store additional options. |
202 | DESCRIPTION |
203 | Process the default options from argc & argv |
204 | Read through each found config file looks and calls 'func' to process |
205 | each option. |
206 | |
207 | NOTES |
208 | --defaults-group-suffix is only processed if we are called from |
209 | load_defaults(). |
210 | |
211 | |
212 | RETURN |
213 | 0 ok |
214 | 1 given cinf_file doesn't exist |
215 | 2 out of memory |
216 | 3 Can't get current working directory |
217 | |
218 | The global variable 'my_defaults_group_suffix' is updated with value for |
219 | --defaults_group_suffix |
220 | */ |
221 | |
222 | int my_search_option_files(const char *conf_file, int *argc, char ***argv, |
223 | uint *args_used, Process_option_func func, |
224 | void *func_ctx, const char **default_directories) |
225 | { |
226 | const char **dirs, *forced_default_file, *; |
227 | int error= 0; |
228 | DBUG_ENTER("my_search_option_files" ); |
229 | |
230 | /* Check if we want to force the use a specific default file */ |
231 | *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used, |
232 | (char **) &forced_default_file, |
233 | (char **) &forced_extra_defaults, |
234 | (char **) &my_defaults_group_suffix); |
235 | |
236 | if (! my_defaults_group_suffix) |
237 | my_defaults_group_suffix= getenv("MYSQL_GROUP_SUFFIX" ); |
238 | |
239 | if (forced_extra_defaults && !defaults_already_read) |
240 | { |
241 | int error= fn_expand(forced_extra_defaults, my_defaults_extra_file_buffer); |
242 | if (error) |
243 | DBUG_RETURN(error); |
244 | my_defaults_extra_file= my_defaults_extra_file_buffer; |
245 | } |
246 | |
247 | if (forced_default_file && !defaults_already_read) |
248 | { |
249 | int error= fn_expand(forced_default_file, my_defaults_file_buffer); |
250 | if (error) |
251 | DBUG_RETURN(error); |
252 | my_defaults_file= my_defaults_file_buffer; |
253 | } |
254 | |
255 | defaults_already_read= TRUE; |
256 | |
257 | /* |
258 | We can only handle 'defaults-group-suffix' if we are called from |
259 | load_defaults() as otherwise we can't know the type of 'func_ctx' |
260 | */ |
261 | |
262 | if (my_defaults_group_suffix && func == handle_default_option) |
263 | { |
264 | /* Handle --defaults-group-suffix= */ |
265 | uint i; |
266 | const char **; |
267 | const size_t instance_len= strlen(my_defaults_group_suffix); |
268 | struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; |
269 | char *ptr; |
270 | TYPELIB *group= ctx->group; |
271 | |
272 | if (!(extra_groups= |
273 | (const char**)alloc_root(ctx->alloc, |
274 | (2*group->count+1)*sizeof(char*)))) |
275 | DBUG_RETURN(2); |
276 | |
277 | for (i= 0; i < group->count; i++) |
278 | { |
279 | size_t len; |
280 | extra_groups[i]= group->type_names[i]; /** copy group */ |
281 | |
282 | len= strlen(extra_groups[i]); |
283 | if (!(ptr= alloc_root(ctx->alloc, (uint) (len+instance_len+1)))) |
284 | DBUG_RETURN(2); |
285 | |
286 | extra_groups[i+group->count]= ptr; |
287 | |
288 | /** Construct new group */ |
289 | memcpy(ptr, extra_groups[i], len); |
290 | memcpy(ptr+len, my_defaults_group_suffix, instance_len+1); |
291 | } |
292 | |
293 | group->count*= 2; |
294 | group->type_names= extra_groups; |
295 | group->type_names[group->count]= 0; |
296 | } |
297 | |
298 | if (my_defaults_file) |
299 | { |
300 | if ((error= search_default_file_with_ext(func, func_ctx, "" , "" , |
301 | my_defaults_file, 0)) < 0) |
302 | goto err; |
303 | if (error > 0) |
304 | { |
305 | fprintf(stderr, "Could not open required defaults file: %s\n" , |
306 | my_defaults_file); |
307 | goto err; |
308 | } |
309 | } |
310 | else if (dirname_length(conf_file)) |
311 | { |
312 | if ((error= search_default_file(func, func_ctx, NullS, conf_file)) < 0) |
313 | goto err; |
314 | } |
315 | else |
316 | { |
317 | for (dirs= default_directories ; *dirs; dirs++) |
318 | { |
319 | if (**dirs) |
320 | { |
321 | if (search_default_file(func, func_ctx, *dirs, conf_file) < 0) |
322 | goto err; |
323 | } |
324 | else if (my_defaults_extra_file) |
325 | { |
326 | if ((error= search_default_file_with_ext(func, func_ctx, "" , "" , |
327 | my_defaults_extra_file, 0)) < 0) |
328 | goto err; /* Fatal error */ |
329 | if (error > 0) |
330 | { |
331 | fprintf(stderr, "Could not open required defaults file: %s\n" , |
332 | my_defaults_extra_file); |
333 | goto err; |
334 | } |
335 | } |
336 | } |
337 | } |
338 | |
339 | DBUG_RETURN(0); |
340 | |
341 | err: |
342 | fprintf(stderr,"Fatal error in defaults handling. Program aborted\n" ); |
343 | DBUG_RETURN(1); |
344 | } |
345 | |
346 | |
347 | /* |
348 | The option handler for load_defaults. |
349 | |
350 | SYNOPSIS |
351 | handle_deault_option() |
352 | in_ctx Handler context. In this case it is a |
353 | handle_option_ctx structure. |
354 | group_name The name of the group the option belongs to. |
355 | option The very option to be processed. It is already |
356 | prepared to be used in argv (has -- prefix). If it |
357 | is NULL, we are handling a new group (section). |
358 | |
359 | DESCRIPTION |
360 | This handler checks whether a group is one of the listed and adds an option |
361 | to the array if yes. Some other handler can record, for instance, all |
362 | groups and their options, not knowing in advance the names and amount of |
363 | groups. |
364 | |
365 | RETURN |
366 | 0 - ok |
367 | 1 - error occurred |
368 | */ |
369 | |
370 | static int handle_default_option(void *in_ctx, const char *group_name, |
371 | const char *option) |
372 | { |
373 | char *tmp; |
374 | struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx; |
375 | |
376 | if (!option) |
377 | return 0; |
378 | |
379 | if (find_type((char *)group_name, ctx->group, FIND_TYPE_NO_PREFIX)) |
380 | { |
381 | if (!(tmp= alloc_root(ctx->alloc, strlen(option) + 1))) |
382 | return 1; |
383 | if (insert_dynamic(ctx->args, (uchar*) &tmp)) |
384 | return 1; |
385 | strmov(tmp, option); |
386 | } |
387 | |
388 | return 0; |
389 | } |
390 | |
391 | |
392 | /* |
393 | Gets options from the command line |
394 | |
395 | SYNOPSIS |
396 | get_defaults_options() |
397 | argc Pointer to argc of original program |
398 | argv Pointer to argv of original program |
399 | defaults --defaults-file option |
400 | extra_defaults --defaults-extra-file option |
401 | |
402 | RETURN |
403 | # Number of arguments used from *argv |
404 | defaults and extra_defaults will be set to option of the appropriate |
405 | items of argv array, or to NULL if there are no such options |
406 | */ |
407 | |
408 | int get_defaults_options(int argc, char **argv, |
409 | char **defaults, |
410 | char **, |
411 | char **group_suffix) |
412 | { |
413 | int org_argc= argc; |
414 | *defaults= *extra_defaults= *group_suffix= 0; |
415 | |
416 | while (argc >= 2) |
417 | { |
418 | /* Skip program name or previously handled argument */ |
419 | argv++; |
420 | if (!*defaults && is_prefix(*argv,"--defaults-file=" )) |
421 | { |
422 | *defaults= *argv + sizeof("--defaults-file=" )-1; |
423 | argc--; |
424 | continue; |
425 | } |
426 | if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file=" )) |
427 | { |
428 | *extra_defaults= *argv + sizeof("--defaults-extra-file=" )-1; |
429 | argc--; |
430 | continue; |
431 | } |
432 | if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix=" )) |
433 | { |
434 | *group_suffix= *argv + sizeof("--defaults-group-suffix=" )-1; |
435 | argc--; |
436 | continue; |
437 | } |
438 | break; |
439 | } |
440 | return org_argc - argc; |
441 | } |
442 | |
443 | /* |
444 | Wrapper around my_load_defaults() for interface compatibility. |
445 | |
446 | SYNOPSIS |
447 | load_defaults() |
448 | conf_file Basename for configuration file to search for. |
449 | If this is a path, then only this file is read. |
450 | groups Which [group] entrys to read. |
451 | Points to an null terminated array of pointers |
452 | argc Pointer to argc of original program |
453 | argv Pointer to argv of original program |
454 | |
455 | NOTES |
456 | |
457 | This function is NOT thread-safe as it uses a global pointer internally. |
458 | See also notes for my_load_defaults(). |
459 | |
460 | RETURN |
461 | 0 ok |
462 | 1 The given conf_file didn't exists |
463 | */ |
464 | int load_defaults(const char *conf_file, const char **groups, |
465 | int *argc, char ***argv) |
466 | { |
467 | return my_load_defaults(conf_file, groups, argc, argv, &default_directories); |
468 | } |
469 | |
470 | /* |
471 | Read options from configurations files |
472 | |
473 | SYNOPSIS |
474 | my_load_defaults() |
475 | conf_file Basename for configuration file to search for. |
476 | If this is a path, then only this file is read. |
477 | groups Which [group] entrys to read. |
478 | Points to an null terminated array of pointers |
479 | argc Pointer to argc of original program |
480 | argv Pointer to argv of original program |
481 | default_directories Pointer to a location where a pointer to the list |
482 | of default directories will be stored |
483 | |
484 | IMPLEMENTATION |
485 | |
486 | Read options from configuration files and put them BEFORE the arguments |
487 | that are already in argc and argv. This way the calling program can |
488 | easily command line options override options in configuration files |
489 | |
490 | NOTES |
491 | In case of fatal error, the function will print a warning and returns 2 |
492 | |
493 | To free used memory one should call free_defaults() with the argument |
494 | that was put in *argv |
495 | |
496 | RETURN |
497 | - If successful, 0 is returned. If 'default_directories' is not NULL, |
498 | a pointer to the array of default directory paths is stored to a location |
499 | it points to. That stored value must be passed to my_search_option_files() |
500 | later. |
501 | |
502 | - 1 is returned if the given conf_file didn't exist. In this case, the |
503 | value pointed to by default_directories is undefined. |
504 | */ |
505 | |
506 | |
507 | int my_load_defaults(const char *conf_file, const char **groups, |
508 | int *argc, char ***argv, const char ***default_directories) |
509 | { |
510 | DYNAMIC_ARRAY args; |
511 | TYPELIB group; |
512 | my_bool found_print_defaults= 0; |
513 | uint args_used= 0; |
514 | int error= 0; |
515 | MEM_ROOT alloc; |
516 | char *ptr,**res; |
517 | struct handle_option_ctx ctx; |
518 | const char **dirs; |
519 | uint args_sep= my_getopt_use_args_separator ? 1 : 0; |
520 | DBUG_ENTER("load_defaults" ); |
521 | |
522 | init_alloc_root(&alloc, "my_load_defaults" , 512, 0, MYF(0)); |
523 | if ((dirs= init_default_directories(&alloc)) == NULL) |
524 | goto err; |
525 | /* |
526 | Check if the user doesn't want any default option processing |
527 | --no-defaults is always the first option |
528 | */ |
529 | if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults" )) |
530 | { |
531 | /* remove the --no-defaults argument and return only the other arguments */ |
532 | uint i, j; |
533 | if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+ |
534 | (*argc + 1)*sizeof(char*)))) |
535 | goto err; |
536 | res= (char**) (ptr+sizeof(alloc)); |
537 | res[0]= **argv; /* Copy program name */ |
538 | j= 1; /* Start from 1 for the reset result args */ |
539 | if (my_getopt_use_args_separator) |
540 | { |
541 | /* set arguments separator */ |
542 | set_args_separator(&res[1]); |
543 | j++; |
544 | } |
545 | for (i=2 ; i < (uint) *argc ; i++, j++) |
546 | res[j]=argv[0][i]; |
547 | res[j]=0; /* End pointer */ |
548 | /* |
549 | Update the argc, if have not added args separator, then we have |
550 | to decrease argc because we have removed the "--no-defaults". |
551 | */ |
552 | if (!my_getopt_use_args_separator) |
553 | (*argc)--; |
554 | *argv=res; |
555 | *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ |
556 | if (default_directories) |
557 | *default_directories= dirs; |
558 | DBUG_RETURN(0); |
559 | } |
560 | |
561 | group.count=0; |
562 | group.name= "defaults" ; |
563 | group.type_names= groups; |
564 | |
565 | for (; *groups ; groups++) |
566 | group.count++; |
567 | |
568 | if (my_init_dynamic_array(&args, sizeof(char*), 128, 64, MYF(0))) |
569 | goto err; |
570 | |
571 | ctx.alloc= &alloc; |
572 | ctx.args= &args; |
573 | ctx.group= &group; |
574 | |
575 | if ((error= my_search_option_files(conf_file, argc, argv, &args_used, |
576 | handle_default_option, (void *) &ctx, |
577 | dirs))) |
578 | { |
579 | delete_dynamic(&args); |
580 | free_root(&alloc,MYF(0)); |
581 | DBUG_RETURN(error); |
582 | } |
583 | /* |
584 | Here error contains <> 0 only if we have a fully specified conf_file |
585 | or a forced default file |
586 | */ |
587 | if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+ |
588 | (args.elements + *argc + 1 + args_sep) *sizeof(char*)))) |
589 | goto err; |
590 | res= (char**) (ptr+sizeof(alloc)); |
591 | |
592 | /* copy name + found arguments + command line arguments to new array */ |
593 | res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ |
594 | memcpy((uchar*) (res+1), args.buffer, args.elements*sizeof(char*)); |
595 | /* Skip --defaults-xxx options */ |
596 | (*argc)-= args_used; |
597 | (*argv)+= args_used; |
598 | |
599 | /* |
600 | Check if we want to see the new argument list |
601 | This options must always be the last of the default options |
602 | */ |
603 | if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults" )) |
604 | { |
605 | found_print_defaults=1; |
606 | --*argc; ++*argv; /* skip argument */ |
607 | } |
608 | |
609 | if (my_getopt_use_args_separator) |
610 | { |
611 | /* set arguments separator for arguments from config file and |
612 | command line */ |
613 | set_args_separator(&res[args.elements+1]); |
614 | } |
615 | |
616 | if (*argc) |
617 | memcpy((uchar*) (res+1+args.elements+args_sep), (char*) ((*argv)+1), |
618 | (*argc-1)*sizeof(char*)); |
619 | res[args.elements+ *argc+args_sep]=0; /* last null */ |
620 | |
621 | (*argc)+=args.elements+args_sep; |
622 | *argv= (char**) res; |
623 | *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ |
624 | delete_dynamic(&args); |
625 | if (found_print_defaults) |
626 | { |
627 | int i; |
628 | printf("%s would have been started with the following arguments:\n" , |
629 | **argv); |
630 | for (i=1 ; i < *argc ; i++) |
631 | if (!my_getopt_is_args_separator((*argv)[i])) /* skip arguments separator */ |
632 | printf("%s " , (*argv)[i]); |
633 | puts("" ); |
634 | DBUG_RETURN(4); |
635 | } |
636 | |
637 | if (default_directories) |
638 | *default_directories= dirs; |
639 | |
640 | DBUG_RETURN(0); |
641 | |
642 | err: |
643 | fprintf(stderr,"Fatal error in defaults handling. Program aborted\n" ); |
644 | DBUG_RETURN(2); |
645 | } |
646 | |
647 | |
648 | void free_defaults(char **argv) |
649 | { |
650 | MEM_ROOT ptr; |
651 | memcpy(&ptr, ((char *) argv) - sizeof(ptr), sizeof(ptr)); |
652 | free_root(&ptr,MYF(0)); |
653 | } |
654 | |
655 | |
656 | static int search_default_file(Process_option_func opt_handler, |
657 | void *handler_ctx, |
658 | const char *dir, |
659 | const char *config_file) |
660 | { |
661 | char **ext; |
662 | const char *empty_list[]= { "" , 0 }; |
663 | my_bool have_ext= fn_ext(config_file)[0] != 0; |
664 | const char **exts_to_use= have_ext ? empty_list : f_extensions; |
665 | |
666 | for (ext= (char**) exts_to_use; *ext; ext++) |
667 | { |
668 | int error; |
669 | if ((error= search_default_file_with_ext(opt_handler, handler_ctx, |
670 | dir, *ext, |
671 | config_file, 0)) < 0) |
672 | return error; |
673 | } |
674 | return 0; |
675 | } |
676 | |
677 | |
678 | /* |
679 | Skip over keyword and get argument after keyword |
680 | |
681 | SYNOPSIS |
682 | get_argument() |
683 | keyword Include directive keyword |
684 | kwlen Length of keyword |
685 | ptr Pointer to the keword in the line under process |
686 | line line number |
687 | |
688 | RETURN |
689 | 0 error |
690 | # Returns pointer to the argument after the keyword. |
691 | */ |
692 | |
693 | static char *get_argument(const char *keyword, size_t kwlen, |
694 | char *ptr, char *name, uint line) |
695 | { |
696 | char *end; |
697 | |
698 | /* Skip over "include / includedir keyword" and following whitespace */ |
699 | |
700 | for (ptr+= kwlen - 1; |
701 | my_isspace(&my_charset_latin1, ptr[0]); |
702 | ptr++) |
703 | {} |
704 | |
705 | /* |
706 | Trim trailing whitespace from directory name |
707 | The -1 below is for the newline added by fgets() |
708 | Note that my_isspace() is true for \r and \n |
709 | */ |
710 | for (end= ptr + strlen(ptr) - 1; |
711 | my_isspace(&my_charset_latin1, *(end - 1)); |
712 | end--) |
713 | {} |
714 | end[0]= 0; /* Cut off end space */ |
715 | |
716 | /* Print error msg if there is nothing after !include* directive */ |
717 | if (end <= ptr) |
718 | { |
719 | fprintf(stderr, |
720 | "error: Wrong '!%s' directive in config file: %s at line %d\n" , |
721 | keyword, name, line); |
722 | return 0; |
723 | } |
724 | return ptr; |
725 | } |
726 | |
727 | |
728 | /* |
729 | Open a configuration file (if exists) and read given options from it |
730 | |
731 | SYNOPSIS |
732 | search_default_file_with_ext() |
733 | opt_handler Option handler function. It is used to process |
734 | every separate option. |
735 | handler_ctx Pointer to the structure to store actual |
736 | parameters of the function. |
737 | dir directory to read |
738 | ext Extension for configuration file |
739 | config_file Name of configuration file |
740 | group groups to read |
741 | recursion_level the level of recursion, got while processing |
742 | "!include" or "!includedir" |
743 | |
744 | RETURN |
745 | 0 Success |
746 | -1 Fatal error, abort |
747 | 1 File not found (Warning) |
748 | */ |
749 | |
750 | static int search_default_file_with_ext(Process_option_func opt_handler, |
751 | void *handler_ctx, |
752 | const char *dir, |
753 | const char *ext, |
754 | const char *config_file, |
755 | int recursion_level) |
756 | { |
757 | char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext; |
758 | char *value, option[4096+2], tmp[FN_REFLEN]; |
759 | static const char includedir_keyword[]= "includedir" ; |
760 | static const char include_keyword[]= "include" ; |
761 | const int max_recursion_level= 10; |
762 | MYSQL_FILE *fp; |
763 | uint line=0; |
764 | my_bool found_group=0; |
765 | uint i; |
766 | MY_DIR *search_dir; |
767 | FILEINFO *search_file; |
768 | |
769 | if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) |
770 | return 0; /* Ignore wrong paths */ |
771 | if (dir) |
772 | { |
773 | end=convert_dirname(name, dir, NullS); |
774 | if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ |
775 | *end++='.'; |
776 | strxmov(end,config_file,ext,NullS); |
777 | } |
778 | else |
779 | { |
780 | strmov(name,config_file); |
781 | } |
782 | fn_format(name,name,"" ,"" ,4); |
783 | #if !defined(__WIN__) |
784 | { |
785 | MY_STAT stat_info; |
786 | if (!my_stat(name,&stat_info,MYF(0))) |
787 | return 1; |
788 | /* |
789 | Ignore world-writable regular files. |
790 | This is mainly done to protect us to not read a file created by |
791 | the mysqld server, but the check is still valid in most context. |
792 | */ |
793 | if ((stat_info.st_mode & S_IWOTH) && |
794 | (stat_info.st_mode & S_IFMT) == S_IFREG) |
795 | { |
796 | fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n" , |
797 | name); |
798 | return 0; |
799 | } |
800 | } |
801 | #endif |
802 | if (!(fp= mysql_file_fopen(key_file_cnf, name, O_RDONLY, MYF(0)))) |
803 | return 1; /* Ignore wrong files */ |
804 | |
805 | while (mysql_file_fgets(buff, sizeof(buff) - 1, fp)) |
806 | { |
807 | line++; |
808 | /* Ignore comment and empty lines */ |
809 | for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++) |
810 | {} |
811 | |
812 | if (*ptr == '#' || *ptr == ';' || !*ptr) |
813 | continue; |
814 | |
815 | /* Configuration File Directives */ |
816 | if (*ptr == '!') |
817 | { |
818 | if (recursion_level >= max_recursion_level) |
819 | { |
820 | for (end= ptr + strlen(ptr) - 1; |
821 | my_isspace(&my_charset_latin1, *(end - 1)); |
822 | end--) |
823 | {} |
824 | end[0]= 0; |
825 | fprintf(stderr, |
826 | "Warning: skipping '%s' directive as maximum include" |
827 | "recursion level was reached in file %s at line %d\n" , |
828 | ptr, name, line); |
829 | continue; |
830 | } |
831 | |
832 | /* skip over `!' and following whitespace */ |
833 | for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++) |
834 | {} |
835 | |
836 | if ((!strncmp(ptr, includedir_keyword, |
837 | sizeof(includedir_keyword) - 1)) && |
838 | my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) |
839 | { |
840 | if (!(ptr= get_argument(includedir_keyword, |
841 | sizeof(includedir_keyword), |
842 | ptr, name, line))) |
843 | goto err; |
844 | |
845 | if (!(search_dir= my_dir(ptr, MYF(MY_WME | MY_WANT_SORT)))) |
846 | goto err; |
847 | |
848 | for (i= 0; i < (uint) search_dir->number_of_files; i++) |
849 | { |
850 | search_file= search_dir->dir_entry + i; |
851 | ext= fn_ext2(search_file->name); |
852 | |
853 | /* check extension */ |
854 | for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++) |
855 | { |
856 | if (!strcmp(ext, *tmp_ext)) |
857 | break; |
858 | } |
859 | |
860 | if (*tmp_ext) |
861 | { |
862 | fn_format(tmp, search_file->name, ptr, "" , |
863 | MY_UNPACK_FILENAME | MY_SAFE_PATH); |
864 | |
865 | search_default_file_with_ext(opt_handler, handler_ctx, "" , "" , tmp, |
866 | recursion_level + 1); |
867 | } |
868 | } |
869 | |
870 | my_dirend(search_dir); |
871 | } |
872 | else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && |
873 | my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) |
874 | { |
875 | if (!(ptr= get_argument(include_keyword, |
876 | sizeof(include_keyword), ptr, |
877 | name, line))) |
878 | goto err; |
879 | |
880 | search_default_file_with_ext(opt_handler, handler_ctx, "" , "" , ptr, |
881 | recursion_level + 1); |
882 | } |
883 | |
884 | continue; |
885 | } |
886 | |
887 | if (*ptr == '[') /* Group name */ |
888 | { |
889 | found_group=1; |
890 | if (!(end=(char *) strchr(++ptr,']'))) |
891 | { |
892 | fprintf(stderr, |
893 | "error: Wrong group definition in config file: %s at line %d\n" , |
894 | name,line); |
895 | goto err; |
896 | } |
897 | /* Remove end space */ |
898 | for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; |
899 | end[0]=0; |
900 | |
901 | strmake(curr_gr, ptr, MY_MIN((size_t) (end-ptr)+1, sizeof(curr_gr)-1)); |
902 | |
903 | /* signal that a new group is found */ |
904 | opt_handler(handler_ctx, curr_gr, NULL); |
905 | |
906 | continue; |
907 | } |
908 | if (!found_group) |
909 | { |
910 | fprintf(stderr, |
911 | "error: Found option without preceding group in config file: %s at line: %d\n" , |
912 | name,line); |
913 | goto err; |
914 | } |
915 | |
916 | |
917 | end= remove_end_comment(ptr); |
918 | if ((value= strchr(ptr, '='))) |
919 | end= value; |
920 | for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; |
921 | if (!value) |
922 | { |
923 | strmake(strmov(option,"--" ),ptr, (size_t) (end-ptr)); |
924 | if (opt_handler(handler_ctx, curr_gr, option)) |
925 | goto err; |
926 | } |
927 | else |
928 | { |
929 | /* Remove pre- and end space */ |
930 | char *value_end; |
931 | for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ; |
932 | value_end=strend(value); |
933 | /* |
934 | We don't have to test for value_end >= value as we know there is |
935 | an '=' before |
936 | */ |
937 | for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ; |
938 | if (value_end < value) /* Empty string */ |
939 | value_end=value; |
940 | |
941 | /* remove quotes around argument */ |
942 | if ((*value == '\"' || *value == '\'') && /* First char is quote */ |
943 | (value + 1 < value_end ) && /* String is longer than 1 */ |
944 | *value == value_end[-1] ) /* First char is equal to last char */ |
945 | { |
946 | value++; |
947 | value_end--; |
948 | } |
949 | ptr=strnmov(strmov(option,"--" ),ptr,(size_t) (end-ptr)); |
950 | *ptr++= '='; |
951 | |
952 | for ( ; value != value_end; value++) |
953 | { |
954 | if (*value == '\\' && value != value_end-1) |
955 | { |
956 | switch(*++value) { |
957 | case 'n': |
958 | *ptr++='\n'; |
959 | break; |
960 | case 't': |
961 | *ptr++= '\t'; |
962 | break; |
963 | case 'r': |
964 | *ptr++ = '\r'; |
965 | break; |
966 | case 'b': |
967 | *ptr++ = '\b'; |
968 | break; |
969 | case 's': |
970 | *ptr++= ' '; /* space */ |
971 | break; |
972 | case '\"': |
973 | *ptr++= '\"'; |
974 | break; |
975 | case '\'': |
976 | *ptr++= '\''; |
977 | break; |
978 | case '\\': |
979 | *ptr++= '\\'; |
980 | break; |
981 | default: /* Unknown; Keep '\' */ |
982 | *ptr++= '\\'; |
983 | *ptr++= *value; |
984 | break; |
985 | } |
986 | } |
987 | else |
988 | *ptr++= *value; |
989 | } |
990 | *ptr=0; |
991 | if (opt_handler(handler_ctx, curr_gr, option)) |
992 | goto err; |
993 | } |
994 | } |
995 | mysql_file_fclose(fp, MYF(0)); |
996 | return(0); |
997 | |
998 | err: |
999 | mysql_file_fclose(fp, MYF(0)); |
1000 | return -1; /* Fatal error */ |
1001 | } |
1002 | |
1003 | |
1004 | static char *(char *ptr) |
1005 | { |
1006 | char quote= 0; /* we are inside quote marks */ |
1007 | char escape= 0; /* symbol is protected by escape chagacter */ |
1008 | |
1009 | for (; *ptr; ptr++) |
1010 | { |
1011 | if ((*ptr == '\'' || *ptr == '\"') && !escape) |
1012 | { |
1013 | if (!quote) |
1014 | quote= *ptr; |
1015 | else if (quote == *ptr) |
1016 | quote= 0; |
1017 | } |
1018 | /* We are not inside a string */ |
1019 | if (!quote && *ptr == '#') |
1020 | { |
1021 | *ptr= 0; |
1022 | return ptr; |
1023 | } |
1024 | escape= (quote && *ptr == '\\' && !escape); |
1025 | } |
1026 | return ptr; |
1027 | } |
1028 | |
1029 | |
1030 | void my_print_default_files(const char *conf_file) |
1031 | { |
1032 | const char *empty_list[]= { "" , 0 }; |
1033 | my_bool have_ext= fn_ext(conf_file)[0] != 0; |
1034 | const char **exts_to_use= have_ext ? empty_list : f_extensions; |
1035 | char name[FN_REFLEN], **ext; |
1036 | |
1037 | puts("\nDefault options are read from the following files in the given order:" ); |
1038 | |
1039 | if (dirname_length(conf_file)) |
1040 | fputs(conf_file,stdout); |
1041 | else |
1042 | { |
1043 | const char **dirs; |
1044 | MEM_ROOT alloc; |
1045 | init_alloc_root(&alloc, "my_print_defaults" , 512, 0, MYF(0)); |
1046 | |
1047 | if ((dirs= init_default_directories(&alloc)) == NULL) |
1048 | { |
1049 | fputs("Internal error initializing default directories list" , stdout); |
1050 | } |
1051 | else |
1052 | { |
1053 | for ( ; *dirs; dirs++) |
1054 | { |
1055 | for (ext= (char**) exts_to_use; *ext; ext++) |
1056 | { |
1057 | const char *pos; |
1058 | char *end; |
1059 | if (**dirs) |
1060 | pos= *dirs; |
1061 | else if (my_defaults_extra_file) |
1062 | pos= my_defaults_extra_file; |
1063 | else |
1064 | continue; |
1065 | end= convert_dirname(name, pos, NullS); |
1066 | if (name[0] == FN_HOMELIB) /* Add . to filenames in home */ |
1067 | *end++= '.'; |
1068 | strxmov(end, conf_file, *ext, " " , NullS); |
1069 | fputs(name, stdout); |
1070 | } |
1071 | } |
1072 | } |
1073 | |
1074 | free_root(&alloc, MYF(0)); |
1075 | } |
1076 | puts("" ); |
1077 | } |
1078 | |
1079 | void print_defaults(const char *conf_file, const char **groups) |
1080 | { |
1081 | const char **groups_save= groups; |
1082 | my_print_default_files(conf_file); |
1083 | |
1084 | fputs("The following groups are read:" ,stdout); |
1085 | for ( ; *groups ; groups++) |
1086 | { |
1087 | fputc(' ',stdout); |
1088 | fputs(*groups,stdout); |
1089 | } |
1090 | |
1091 | if (my_defaults_group_suffix) |
1092 | { |
1093 | groups= groups_save; |
1094 | for ( ; *groups ; groups++) |
1095 | { |
1096 | fputc(' ',stdout); |
1097 | fputs(*groups,stdout); |
1098 | fputs(my_defaults_group_suffix,stdout); |
1099 | } |
1100 | } |
1101 | puts("\nThe following options may be given as the first argument:\n\ |
1102 | --print-defaults Print the program argument list and exit.\n\ |
1103 | --no-defaults Don't read default options from any option file.\n\ |
1104 | The following specify which files/extra groups are read (specified before remaining options):\n\ |
1105 | --defaults-file=# Only read default options from the given file #.\n\ |
1106 | --defaults-extra-file=# Read this file after the global files are read.\n\ |
1107 | --defaults-group-suffix=# Additionally read default groups with # appended as a suffix." ); |
1108 | } |
1109 | |
1110 | |
1111 | static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs) |
1112 | { |
1113 | char buf[FN_REFLEN]; |
1114 | size_t len; |
1115 | char *p; |
1116 | my_bool err __attribute__((unused)); |
1117 | |
1118 | len= normalize_dirname(buf, dir); |
1119 | if (!(p= strmake_root(alloc, buf, len))) |
1120 | return 1; /* Failure */ |
1121 | /* Should never fail if DEFAULT_DIRS_SIZE is correct size */ |
1122 | err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE); |
1123 | DBUG_ASSERT(err == FALSE); |
1124 | |
1125 | return 0; |
1126 | } |
1127 | |
1128 | #ifdef __WIN__ |
1129 | static const char *my_get_module_parent(char *buf, size_t size) |
1130 | { |
1131 | char *last= NULL; |
1132 | char *end; |
1133 | if (!GetModuleFileName(NULL, buf, (DWORD) size)) |
1134 | return NULL; |
1135 | end= strend(buf); |
1136 | |
1137 | /* |
1138 | Look for the second-to-last \ in the filename, but hang on |
1139 | to a pointer after the last \ in case we're in the root of |
1140 | a drive. |
1141 | */ |
1142 | for ( ; end > buf; end--) |
1143 | { |
1144 | if (*end == FN_LIBCHAR) |
1145 | { |
1146 | if (last) |
1147 | { |
1148 | /* Keep the last '\' as this works both with D:\ and a directory */ |
1149 | end[1]= 0; |
1150 | break; |
1151 | } |
1152 | last= end; |
1153 | } |
1154 | } |
1155 | |
1156 | return buf; |
1157 | } |
1158 | #endif /* __WIN__ */ |
1159 | |
1160 | |
1161 | static const char **init_default_directories(MEM_ROOT *alloc) |
1162 | { |
1163 | const char **dirs; |
1164 | char *env; |
1165 | int errors= 0; |
1166 | DBUG_ENTER("init_default_directories" ); |
1167 | |
1168 | dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *)); |
1169 | if (dirs == NULL) |
1170 | DBUG_RETURN(NULL); |
1171 | bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *)); |
1172 | |
1173 | #ifdef __WIN__ |
1174 | |
1175 | { |
1176 | char fname_buffer[FN_REFLEN]; |
1177 | if (GetSystemWindowsDirectory(fname_buffer, sizeof(fname_buffer))) |
1178 | errors += add_directory(alloc, fname_buffer, dirs); |
1179 | |
1180 | if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer))) |
1181 | errors += add_directory(alloc, fname_buffer, dirs); |
1182 | |
1183 | errors += add_directory(alloc, "C:/" , dirs); |
1184 | |
1185 | if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL) |
1186 | { |
1187 | errors += add_directory(alloc, fname_buffer, dirs); |
1188 | |
1189 | strcat_s(fname_buffer, sizeof(fname_buffer), "/data" ); |
1190 | errors += add_directory(alloc, fname_buffer, dirs); |
1191 | } |
1192 | } |
1193 | |
1194 | #else |
1195 | |
1196 | #if defined(DEFAULT_SYSCONFDIR) |
1197 | if (DEFAULT_SYSCONFDIR[0]) |
1198 | errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs); |
1199 | #else |
1200 | errors += add_directory(alloc, "/etc/" , dirs); |
1201 | errors += add_directory(alloc, "/etc/mysql/" , dirs); |
1202 | #endif /* DEFAULT_SYSCONFDIR */ |
1203 | |
1204 | #endif |
1205 | |
1206 | if ((env= getenv("MYSQL_HOME" ))) |
1207 | errors += add_directory(alloc, env, dirs); |
1208 | |
1209 | /* Placeholder for --defaults-extra-file=<path> */ |
1210 | errors += add_directory(alloc, "" , dirs); |
1211 | |
1212 | #if !defined(__WIN__) |
1213 | errors += add_directory(alloc, "~/" , dirs); |
1214 | #endif |
1215 | |
1216 | DBUG_RETURN(errors > 0 ? NULL : dirs); |
1217 | } |
1218 | |