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-1335 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----";
set_args_separator(char ** arg)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;
my_getopt_is_args_separator(const char * arg)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 *my_defaults_extra_file=0;
86
87 static char my_defaults_file_buffer[FN_REFLEN];
88 static char my_defaults_extra_file_buffer[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
fn_expand(const char * filename,char * result_buf)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
my_search_option_files(const char * conf_file,int * argc,char *** argv,uint * args_used,Process_option_func func,void * func_ctx,const char ** default_directories)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, *forced_extra_defaults;
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 **extra_groups;
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
handle_default_option(void * in_ctx,const char * group_name,const char * option)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
get_defaults_options(int argc,char ** argv,char ** defaults,char ** extra_defaults,char ** group_suffix)408 int get_defaults_options(int argc, char **argv,
409 char **defaults,
410 char **extra_defaults,
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 */
load_defaults(const char * conf_file,const char ** groups,int * argc,char *** argv)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
my_load_defaults(const char * conf_file,const char ** groups,int * argc,char *** argv,const char *** default_directories)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
free_defaults(char ** argv)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
search_default_file(Process_option_func opt_handler,void * handler_ctx,const char * dir,const char * config_file)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
get_argument(const char * keyword,size_t kwlen,char * ptr,char * name,uint line)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
search_default_file_with_ext(Process_option_func opt_handler,void * handler_ctx,const char * dir,const char * ext,const char * config_file,int recursion_level)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 0;
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 if (strstr(name, "/etc") == name)
806 {
807 fprintf(stderr,
808 "error: Config file %s in invalid location, please move to or merge with /usr/local%s\n",
809 name,name);
810 goto err;
811 }
812
813 while (mysql_file_fgets(buff, sizeof(buff) - 1, fp))
814 {
815 line++;
816 /* Ignore comment and empty lines */
817 for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
818 {}
819
820 if (*ptr == '#' || *ptr == ';' || !*ptr)
821 continue;
822
823 /* Configuration File Directives */
824 if (*ptr == '!')
825 {
826 if (recursion_level >= max_recursion_level)
827 {
828 for (end= ptr + strlen(ptr) - 1;
829 my_isspace(&my_charset_latin1, *(end - 1));
830 end--)
831 {}
832 end[0]= 0;
833 fprintf(stderr,
834 "Warning: skipping '%s' directive as maximum include"
835 "recursion level was reached in file %s at line %d\n",
836 ptr, name, line);
837 continue;
838 }
839
840 /* skip over `!' and following whitespace */
841 for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
842 {}
843
844 if ((!strncmp(ptr, includedir_keyword,
845 sizeof(includedir_keyword) - 1)) &&
846 my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
847 {
848 if (!(ptr= get_argument(includedir_keyword,
849 sizeof(includedir_keyword),
850 ptr, name, line)))
851 goto err;
852
853 if (!(search_dir= my_dir(ptr, MYF(MY_WME | MY_WANT_SORT))))
854 goto err;
855
856 for (i= 0; i < (uint) search_dir->number_of_files; i++)
857 {
858 search_file= search_dir->dir_entry + i;
859 ext= fn_ext2(search_file->name);
860
861 /* check extension */
862 for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++)
863 {
864 if (!strcmp(ext, *tmp_ext))
865 break;
866 }
867
868 if (*tmp_ext)
869 {
870 fn_format(tmp, search_file->name, ptr, "",
871 MY_UNPACK_FILENAME | MY_SAFE_PATH);
872
873 search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp,
874 recursion_level + 1);
875 }
876 }
877
878 my_dirend(search_dir);
879 }
880 else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
881 my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
882 {
883 if (!(ptr= get_argument(include_keyword,
884 sizeof(include_keyword), ptr,
885 name, line)))
886 goto err;
887
888 search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
889 recursion_level + 1);
890 }
891
892 continue;
893 }
894
895 if (*ptr == '[') /* Group name */
896 {
897 found_group=1;
898 if (!(end=(char *) strchr(++ptr,']')))
899 {
900 fprintf(stderr,
901 "error: Wrong group definition in config file: %s at line %d\n",
902 name,line);
903 goto err;
904 }
905 /* Remove end space */
906 for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
907 end[0]=0;
908
909 strmake(curr_gr, ptr, MY_MIN((size_t) (end-ptr)+1, sizeof(curr_gr)-1));
910
911 /* signal that a new group is found */
912 opt_handler(handler_ctx, curr_gr, NULL);
913
914 continue;
915 }
916 if (!found_group)
917 {
918 fprintf(stderr,
919 "error: Found option without preceding group in config file: %s at line: %d\n",
920 name,line);
921 goto err;
922 }
923
924
925 end= remove_end_comment(ptr);
926 if ((value= strchr(ptr, '=')))
927 end= value;
928 for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
929 if (!value)
930 {
931 strmake(strmov(option,"--"),ptr, (size_t) (end-ptr));
932 if (opt_handler(handler_ctx, curr_gr, option))
933 goto err;
934 }
935 else
936 {
937 /* Remove pre- and end space */
938 char *value_end;
939 for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
940 value_end=strend(value);
941 /*
942 We don't have to test for value_end >= value as we know there is
943 an '=' before
944 */
945 for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
946 if (value_end < value) /* Empty string */
947 value_end=value;
948
949 /* remove quotes around argument */
950 if ((*value == '\"' || *value == '\'') && /* First char is quote */
951 (value + 1 < value_end ) && /* String is longer than 1 */
952 *value == value_end[-1] ) /* First char is equal to last char */
953 {
954 value++;
955 value_end--;
956 }
957 ptr=strnmov(strmov(option,"--"),ptr,(size_t) (end-ptr));
958 *ptr++= '=';
959
960 for ( ; value != value_end; value++)
961 {
962 if (*value == '\\' && value != value_end-1)
963 {
964 switch(*++value) {
965 case 'n':
966 *ptr++='\n';
967 break;
968 case 't':
969 *ptr++= '\t';
970 break;
971 case 'r':
972 *ptr++ = '\r';
973 break;
974 case 'b':
975 *ptr++ = '\b';
976 break;
977 case 's':
978 *ptr++= ' '; /* space */
979 break;
980 case '\"':
981 *ptr++= '\"';
982 break;
983 case '\'':
984 *ptr++= '\'';
985 break;
986 case '\\':
987 *ptr++= '\\';
988 break;
989 default: /* Unknown; Keep '\' */
990 *ptr++= '\\';
991 *ptr++= *value;
992 break;
993 }
994 }
995 else
996 *ptr++= *value;
997 }
998 *ptr=0;
999 if (opt_handler(handler_ctx, curr_gr, option))
1000 goto err;
1001 }
1002 }
1003 mysql_file_fclose(fp, MYF(0));
1004 return(0);
1005
1006 err:
1007 mysql_file_fclose(fp, MYF(0));
1008 return -1; /* Fatal error */
1009 }
1010
1011
remove_end_comment(char * ptr)1012 static char *remove_end_comment(char *ptr)
1013 {
1014 char quote= 0; /* we are inside quote marks */
1015 char escape= 0; /* symbol is protected by escape chagacter */
1016
1017 for (; *ptr; ptr++)
1018 {
1019 if ((*ptr == '\'' || *ptr == '\"') && !escape)
1020 {
1021 if (!quote)
1022 quote= *ptr;
1023 else if (quote == *ptr)
1024 quote= 0;
1025 }
1026 /* We are not inside a string */
1027 if (!quote && *ptr == '#')
1028 {
1029 *ptr= 0;
1030 return ptr;
1031 }
1032 escape= (quote && *ptr == '\\' && !escape);
1033 }
1034 return ptr;
1035 }
1036
1037
my_print_default_files(const char * conf_file)1038 void my_print_default_files(const char *conf_file)
1039 {
1040 const char *empty_list[]= { "", 0 };
1041 my_bool have_ext= fn_ext(conf_file)[0] != 0;
1042 const char **exts_to_use= have_ext ? empty_list : f_extensions;
1043 char name[FN_REFLEN], **ext;
1044
1045 puts("\nDefault options are read from the following files in the given order:");
1046 if (my_defaults_file)
1047 {
1048 puts(my_defaults_file);
1049 return;
1050 }
1051
1052 if (dirname_length(conf_file))
1053 fputs(conf_file,stdout);
1054 else
1055 {
1056 const char **dirs;
1057 MEM_ROOT alloc;
1058 init_alloc_root(&alloc, "my_print_defaults", 512, 0, MYF(0));
1059
1060 if ((dirs= init_default_directories(&alloc)) == NULL)
1061 {
1062 fputs("Internal error initializing default directories list", stdout);
1063 }
1064 else
1065 {
1066 for ( ; *dirs; dirs++)
1067 {
1068 for (ext= (char**) exts_to_use; *ext; ext++)
1069 {
1070 const char *pos;
1071 char *end;
1072 if (**dirs)
1073 pos= *dirs;
1074 else if (my_defaults_extra_file)
1075 {
1076 pos= my_defaults_extra_file;
1077 fputs(pos, stdout);
1078 fputs(" ", stdout);
1079 continue;
1080 }
1081 else
1082 continue;
1083 end= convert_dirname(name, pos, NullS);
1084 if (name[0] == FN_HOMELIB) /* Add . to filenames in home */
1085 *end++= '.';
1086 strxmov(end, conf_file, *ext, " ", NullS);
1087 if (strstr(name, "/etc") != name)
1088 fputs(name, stdout);
1089 }
1090 }
1091 }
1092
1093 free_root(&alloc, MYF(0));
1094 }
1095 puts("");
1096 }
1097
print_defaults(const char * conf_file,const char ** groups)1098 void print_defaults(const char *conf_file, const char **groups)
1099 {
1100 const char **groups_save= groups;
1101 my_print_default_files(conf_file);
1102
1103 fputs("The following groups are read:",stdout);
1104 for ( ; *groups ; groups++)
1105 {
1106 fputc(' ',stdout);
1107 fputs(*groups,stdout);
1108 }
1109
1110 if (my_defaults_group_suffix)
1111 {
1112 groups= groups_save;
1113 for ( ; *groups ; groups++)
1114 {
1115 fputc(' ',stdout);
1116 fputs(*groups,stdout);
1117 fputs(my_defaults_group_suffix,stdout);
1118 }
1119 }
1120 puts("\nThe following options may be given as the first argument:\n\
1121 --print-defaults Print the program argument list and exit.\n\
1122 --no-defaults Don't read default options from any option file.\n\
1123 The following specify which files/extra groups are read (specified before remaining options):\n\
1124 --defaults-file=# Only read default options from the given file #.\n\
1125 --defaults-extra-file=# Read this file after the global files are read.\n\
1126 --defaults-group-suffix=# Additionally read default groups with # appended as a suffix.");
1127 }
1128
1129
add_directory(MEM_ROOT * alloc,const char * dir,const char ** dirs)1130 static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs)
1131 {
1132 char buf[FN_REFLEN];
1133 size_t len;
1134 char *p;
1135 my_bool err __attribute__((unused));
1136
1137 len= normalize_dirname(buf, dir);
1138 if (!(p= strmake_root(alloc, buf, len)))
1139 return 1; /* Failure */
1140 /* Should never fail if DEFAULT_DIRS_SIZE is correct size */
1141 err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE);
1142 DBUG_ASSERT(err == FALSE);
1143
1144 return 0;
1145 }
1146
1147 #ifdef __WIN__
my_get_module_parent(char * buf,size_t size)1148 static const char *my_get_module_parent(char *buf, size_t size)
1149 {
1150 char *last= NULL;
1151 char *end;
1152 if (!GetModuleFileName(NULL, buf, (DWORD) size))
1153 return NULL;
1154 end= strend(buf);
1155
1156 /*
1157 Look for the second-to-last \ in the filename, but hang on
1158 to a pointer after the last \ in case we're in the root of
1159 a drive.
1160 */
1161 for ( ; end > buf; end--)
1162 {
1163 if (*end == FN_LIBCHAR)
1164 {
1165 if (last)
1166 {
1167 /* Keep the last '\' as this works both with D:\ and a directory */
1168 end[1]= 0;
1169 break;
1170 }
1171 last= end;
1172 }
1173 }
1174
1175 return buf;
1176 }
1177 #endif /* __WIN__ */
1178
1179
init_default_directories(MEM_ROOT * alloc)1180 static const char **init_default_directories(MEM_ROOT *alloc)
1181 {
1182 const char **dirs;
1183 char *env;
1184 int errors= 0;
1185 DBUG_ENTER("init_default_directories");
1186
1187 dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *));
1188 if (dirs == NULL)
1189 DBUG_RETURN(NULL);
1190 bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *));
1191
1192 #ifdef __WIN__
1193
1194 {
1195 char fname_buffer[FN_REFLEN];
1196 if (GetSystemWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
1197 errors += add_directory(alloc, fname_buffer, dirs);
1198
1199 if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
1200 errors += add_directory(alloc, fname_buffer, dirs);
1201
1202 errors += add_directory(alloc, "C:/", dirs);
1203
1204 if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL)
1205 {
1206 errors += add_directory(alloc, fname_buffer, dirs);
1207
1208 strcat_s(fname_buffer, sizeof(fname_buffer), "/data");
1209 errors += add_directory(alloc, fname_buffer, dirs);
1210 }
1211 }
1212
1213 #else
1214
1215 errors += add_directory(alloc, "/etc/", dirs);
1216 errors += add_directory(alloc, "/etc/mysql/", dirs);
1217 errors += add_directory(alloc, "/usr/local/etc/", dirs);
1218 errors += add_directory(alloc, "/usr/local/etc/mysql/", dirs);
1219
1220 #endif
1221
1222 if ((env= getenv("MYSQL_HOME")))
1223 errors += add_directory(alloc, env, dirs);
1224
1225 /* Placeholder for --defaults-extra-file=<path> */
1226 errors += add_directory(alloc, "", dirs);
1227
1228 #if !defined(__WIN__)
1229 errors += add_directory(alloc, "~/", dirs);
1230 #endif
1231
1232 DBUG_RETURN(errors > 0 ? NULL : dirs);
1233 }
1234