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