1 /*
2    Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
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, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24 
25 #include <m_string.h>
26 #include <mysql.h>
27 #include <my_getopt.h>
28 #include <my_dir.h>
29 #include <my_global.h>
30 #include <stdio.h>
31 #include <string.h>
32 
33 
34 #define SHOW_VERSION "1.0.0"
35 #define PRINT_VERSION do { printf("%s  Ver %s Distrib %s\n",    \
36                         my_progname, SHOW_VERSION, MYSQL_SERVER_VERSION);    \
37                       } while(0)
38 
39 /* Global variables. */
40 static uint my_end_arg= 0;
41 static uint opt_verbose=0;
42 static uint opt_no_defaults= 0;
43 static uint opt_print_defaults= 0;
44 static char *opt_datadir=0, *opt_basedir=0,
45             *opt_plugin_dir=0, *opt_plugin_ini=0,
46             *opt_mysqld=0, *opt_my_print_defaults=0;
47 static char bootstrap[FN_REFLEN];
48 
49 
50 /* plugin struct */
51 struct st_plugin
52 {
53   const char *name;           /* plugin name */
54   const char *so_name;        /* plugin so (library) name */
55   const char *components[16]; /* components to load */
56 } plugin_data;
57 
58 
59 /* Options */
60 static struct my_option my_long_options[] =
61 {
62   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
63     0, 0, 0, 0, 0, 0},
64   {"basedir", 'b', "The basedir for the server.",
65     0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
66   {"datadir", 'd', "The datadir for the server.",
67     0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
68   {"plugin-dir", 'p', "The plugin dir for the server.",
69     0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
70   {"plugin-ini", 'i', "Read plugin information from configuration file "
71    "specified instead of from <plugin-dir>/<plugin_name>.ini.",
72     0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
73   {"no-defaults", 'n', "Do not read values from configuration file.",
74     0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
75   {"print-defaults", 'P', "Show default values from configuration file.",
76     0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
77   {"mysqld", 'm', "Path to mysqld executable. Example: /sbin/temp1/mysql/bin",
78     0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
79   {"my-print-defaults", 'f', "Path to my_print_defaults executable. "
80    "Example: /source/temp11/extra",
81     0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
82   {"verbose", 'v',
83     "More verbose output; you can use this multiple times to get even more "
84     "verbose output.",
85     0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
86   {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
87     NO_ARG, 0, 0, 0, 0, 0, 0},
88   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
89 };
90 
91 
92 /* Methods */
93 static int process_options(int argc, char *argv[], char *operation);
94 static int check_access();
95 static int find_tool(const char *tool_name, char *tool_path);
96 static int find_plugin(char *tp_path);
97 static int build_bootstrap_file(char *operation, char *bootstrap);
98 static int dump_bootstrap_file(char *bootstrap_file);
99 static int bootstrap_server(char *server_path, char *bootstrap_file);
100 
101 
main(int argc,char * argv[])102 int main(int argc,char *argv[])
103 {
104   int error= 0;
105   char tp_path[FN_REFLEN];
106   char server_path[FN_REFLEN];
107   char operation[16];
108 
109   MY_INIT(argv[0]);
110   plugin_data.name= 0; // initialize name
111 
112   /*
113     The following operations comprise the method for enabling or disabling
114     a plugin. We begin by processing the command options then check the
115     directories specified for --datadir, --basedir, --plugin-dir, and
116     --plugin-ini (if specified). If the directories are Ok, we then look
117     for the mysqld executable and the plugin soname. Finally, we build a
118     bootstrap command file for use in bootstraping the server.
119 
120     If any step fails, the method issues an error message and the tool exits.
121 
122       1) Parse, execute, and verify command options.
123       2) Check access to directories.
124       3) Look for mysqld executable.
125       4) Look for the plugin.
126       5) Build a bootstrap file with commands to enable or disable plugin.
127 
128   */
129   if ((error= process_options(argc, argv, operation)) ||
130       (error= check_access()) ||
131       (error= find_tool("mysqld" FN_EXEEXT, server_path)) ||
132       (error= find_plugin(tp_path)) ||
133       (error= build_bootstrap_file(operation, bootstrap)))
134     goto exit;
135 
136   /* Dump the bootstrap file if --verbose specified. */
137   if (opt_verbose && ((error= dump_bootstrap_file(bootstrap))))
138     goto exit;
139 
140   /* Start the server in bootstrap mode and execute bootstrap commands */
141   error= bootstrap_server(server_path, bootstrap);
142 
143 exit:
144   /* Remove file */
145   my_delete(bootstrap, MYF(0));
146   if (opt_verbose && error == 0)
147   {
148     printf("# Operation succeeded.\n");
149   }
150 
151   my_end(my_end_arg);
152   exit(error ? 1 : 0);
153   return 0;        /* No compiler warnings */
154 }
155 
156 
157 /**
158   Get a temporary file name.
159 
160   @param[out]  filename   The file name of the temporary file
161   @param[in]   ext        An extension for the file (optional)
162 
163   @retval int error = 1, success = 0
164 */
165 
make_tempfile(char * filename,const char * ext)166 static int make_tempfile(char *filename, const char *ext)
167 {
168   int fd= 0;
169 
170   if ((fd=create_temp_file(filename, NullS, ext, O_CREAT | O_WRONLY,
171          MYF(MY_WME))) < 0)
172   {
173     fprintf(stderr, "ERROR: Cannot generate temporary file. Error code: %d.\n",
174             fd);
175     return 1;
176   }
177   my_close(fd, MYF(0));
178   return 0;
179 }
180 
181 
182 /**
183   Get the value of an option from a string read from my_print_defaults output.
184 
185   @param[in]  line   The line (string) read from the file
186   @param[in]  item   The option to search for (e.g. --datadir)
187 
188   @returns NULL if not found, string containing value if found
189 */
190 
get_value(char * line,const char * item)191 static char *get_value(char *line, const char *item)
192 {
193   char *destination= 0;
194   int item_len= (int)strlen(item);
195   int line_len = (int)strlen(line);
196 
197   if ((strncasecmp(line, item, item_len) == 0))
198   {
199     int start= 0;
200     char *s= 0;
201 
202     s = line + item_len + 1;
203     destination= my_strndup(s, line_len - start, MYF(MY_FAE));
204     destination[line_len - item_len - 2]= 0;
205   }
206   return destination;
207 }
208 
209 
210 /**
211   Run a command in a shell.
212 
213   This function will attempt to execute the command specified by using the
214   popen() method to open a shell and execute the command passed and store the
215   output in a result file. If the --verbose option was specified, it will open
216   the result file and print the contents to stdout.
217 
218   @param[in]  cmd   The command to execute.
219   @param[in]  mode  The mode for popen() (e.g. "r", "w", "rw")
220 
221   @return int error code or 0 for success.
222 */
223 
run_command(char * cmd,const char * mode)224 static int run_command(char* cmd, const char *mode)
225 {
226   char buf[512]= {0};
227   FILE *res_file;
228   int error;
229 
230   if (!(res_file= popen(cmd, mode)))
231     return -1;
232 
233   if (opt_verbose)
234   {
235     while (fgets(buf, sizeof(buf), res_file))
236     {
237       fprintf(stdout, "%s", buf);
238     }
239   }
240   error= pclose(res_file);
241   return error;
242 }
243 
244 
245 #ifdef __WIN__
246 /**
247   Check to see if there are spaces in a path.
248 
249   @param[in]  path  The Windows path to examine.
250 
251   @retval int spaces found = 1, no spaces = 0
252 */
has_spaces(const char * path)253 static int has_spaces(const char *path)
254 {
255   if (strchr(path, ' ') != NULL)
256     return 1;
257   return 0;
258 }
259 
260 
261 /**
262   Convert a Unix path to a Windows path.
263 
264   @param[in]  path  The Windows path to examine.
265 
266   @returns string containing path with / changed to \\
267 */
convert_path(const char * argument)268 static char *convert_path(const char *argument)
269 {
270   /* Convert / to \\ to make Windows paths */
271   char *winfilename= my_strdup(argument, MYF(MY_FAE));
272   char *pos, *end;
273   int length= strlen(argument);
274 
275   for (pos= winfilename, end= pos+length ; pos < end ; pos++)
276   {
277     if (*pos == '/')
278     {
279       *pos= '\\';
280     }
281   }
282   return winfilename;
283 }
284 
285 
286 /**
287   Add quotes if the path has spaces in it.
288 
289   @param[in]  path  The Windows path to examine.
290 
291   @returns string containing excaped quotes if spaces found in path
292 */
add_quotes(const char * path)293 static char *add_quotes(const char *path)
294 {
295   char windows_cmd_friendly[FN_REFLEN];
296 
297   if (has_spaces(path))
298     snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly),
299              "\"%s\"", path);
300   else
301     snprintf(windows_cmd_friendly, sizeof(windows_cmd_friendly),
302              "%s", path);
303   return my_strdup(windows_cmd_friendly, MYF(MY_FAE));
304 }
305 #endif
306 
307 
308 /**
309   Get the default values from the my.cnf file.
310 
311   This method gets the default values for the following parameters:
312 
313   --datadir
314   --basedir
315   --plugin-dir
316   --plugin-ini
317 
318   These values are used if the user has not specified a value.
319 
320   @retval int error = 1, success = 0
321 */
322 
get_default_values()323 static int get_default_values()
324 {
325   char tool_path[FN_REFLEN];
326   char defaults_cmd[FN_REFLEN];
327   char defaults_file[FN_REFLEN];
328   char line[FN_REFLEN];
329   int error= 0;
330   int ret= 0;
331   FILE *file= 0;
332 
333   memset(tool_path, 0, FN_REFLEN);
334   if ((error= find_tool("my_print_defaults" FN_EXEEXT, tool_path)))
335     goto exit;
336   else
337   {
338     if ((error= make_tempfile(defaults_file, "txt")))
339       goto exit;
340 
341 #ifdef __WIN__
342     {
343       char *format_str= 0;
344 
345       if (has_spaces(tool_path) || has_spaces(defaults_file))
346         format_str = "\"%s mysqld > %s\"";
347       else
348         format_str = "%s mysqld > %s";
349 
350       snprintf(defaults_cmd, sizeof(defaults_cmd), format_str,
351                add_quotes(tool_path), add_quotes(defaults_file));
352       if (opt_verbose)
353       {
354         printf("# my_print_defaults found: %s\n", tool_path);
355       }
356     }
357 #else
358     snprintf(defaults_cmd, sizeof(defaults_cmd),
359              "%s mysqld > %s", tool_path, defaults_file);
360 #endif
361 
362     /* Execute the command */
363     if (opt_verbose)
364     {
365       printf("# Command: %s\n", defaults_cmd);
366     }
367     error= run_command(defaults_cmd, "r");
368     if (error)
369     {
370       fprintf(stderr, "ERROR: my_print_defaults failed. Error code: %d.\n",
371               ret);
372       goto exit;
373     }
374     /* Now open the file and read the defaults we want. */
375     file= fopen(defaults_file, "r");
376     while (fgets(line, FN_REFLEN, file) != NULL)
377     {
378       char *value= 0;
379 
380       if ((opt_datadir == 0) && ((value= get_value(line, "--datadir"))))
381       {
382         opt_datadir= my_strdup(value, MYF(MY_FAE));
383       }
384       if ((opt_basedir == 0) && ((value= get_value(line, "--basedir"))))
385       {
386         opt_basedir= my_strdup(value, MYF(MY_FAE));
387       }
388       if ((opt_plugin_dir == 0) && ((value= get_value(line, "--plugin_dir"))))
389       {
390         opt_plugin_dir= my_strdup(value, MYF(MY_FAE));
391       }
392       if ((opt_plugin_ini == 0) && ((value= get_value(line, "--plugin_ini"))))
393       {
394         opt_plugin_ini= my_strdup(value, MYF(MY_FAE));
395       }
396     }
397   }
398 exit:
399   if (file)
400   {
401     fclose(file);
402     /* Remove file */
403     my_delete(defaults_file, MYF(0));
404   }
405   return error;
406 }
407 
408 
409 /**
410   Print usage.
411 */
412 
usage(void)413 static void usage(void)
414 {
415   PRINT_VERSION;
416   puts("Copyright (c) 2011, 2016, Oracle and/or its affiliates. "
417        "All rights reserved.\n");
418   puts("Enable or disable plugins.");
419   printf("\nUsage: %s [options] <plugin> ENABLE|DISABLE\n\nOptions:\n",
420      my_progname);
421   my_print_help(my_long_options);
422   puts("\n");
423 }
424 
425 
426 /**
427   Print the default values as read from the my.cnf file.
428 
429   This method displays the default values for the following parameters:
430 
431   --datadir
432   --basedir
433   --plugin-dir
434   --plugin-ini
435 
436 */
437 
print_default_values(void)438 static void print_default_values(void)
439 {
440   printf("%s would have been started with the following arguments:\n",
441          my_progname);
442   get_default_values();
443   if (opt_datadir)
444   {
445     printf("--datadir=%s ", opt_datadir);
446   }
447   if (opt_basedir)
448   {
449     printf("--basedir=%s ", opt_basedir);
450   }
451   if (opt_plugin_dir)
452   {
453     printf("--plugin_dir=%s ", opt_plugin_dir);
454   }
455   if (opt_plugin_ini)
456   {
457     printf("--plugin_ini=%s ", opt_plugin_ini);
458   }
459   if (opt_mysqld)
460   {
461     printf("--mysqld=%s ", opt_mysqld);
462   }
463   if (opt_my_print_defaults)
464   {
465     printf("--my_print_defaults=%s ", opt_my_print_defaults);
466   }
467   printf("\n");
468 }
469 
470 
471 /**
472   Process the arguments and identify an option and store its value.
473 
474   @param[in]  optid      The single character shortcut for the argument.
475   @param[in]  my_option  Structure of legal options.
476   @param[in]  argument   The argument value to process.
477 */
478 
479 static my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)480 get_one_option(int optid,
481                const struct my_option *opt MY_ATTRIBUTE((unused)),
482                char *argument)
483 {
484   switch(optid) {
485   case 'n':
486     opt_no_defaults++;
487     break;
488   case 'P':
489     opt_print_defaults++;
490     print_default_values();
491     break;
492   case 'v':
493     opt_verbose++;
494     break;
495   case 'V':
496     PRINT_VERSION;
497     exit(0);
498     break;
499   case '?':
500   case 'I':          /* Info */
501     usage();
502     exit(0);
503   case 'd':
504     opt_datadir= my_strdup(argument, MYF(MY_FAE));
505     break;
506   case 'b':
507     opt_basedir= my_strdup(argument, MYF(MY_FAE));
508     break;
509   case 'p':
510     opt_plugin_dir= my_strdup(argument, MYF(MY_FAE));
511     break;
512   case 'i':
513     opt_plugin_ini= my_strdup(argument, MYF(MY_FAE));
514     break;
515   case 'm':
516     opt_mysqld= my_strdup(argument, MYF(MY_FAE));
517     break;
518   case 'f':
519     opt_my_print_defaults= my_strdup(argument, MYF(MY_FAE));
520     break;
521   }
522   return 0;
523 }
524 
525 
526 /**
527   Check to see if a file exists.
528 
529   @param[in]  filename  File to locate.
530 
531   @retval int file not found = 1, file found = 0
532 */
533 
file_exists(char * filename)534 static int file_exists(char * filename)
535 {
536   MY_STAT stat_arg;
537 
538   if (!my_stat(filename, &stat_arg, MYF(0)))
539   {
540     return 0;
541   }
542   return 1;
543 }
544 
545 
546 /**
547   Search a specific path and sub directory for a file name.
548 
549   @param[in]  base_path  Original path to use.
550   @param[in]  tool_name  Name of the tool to locate.
551   @param[in]  subdir     The sub directory to search.
552   @param[out] tool_path  If tool found, return complete path.
553 
554   @retval int error = 1, success = 0
555 */
556 
search_dir(const char * base_path,const char * tool_name,const char * subdir,char * tool_path)557 static int search_dir(const char * base_path, const char *tool_name,
558                       const char *subdir, char *tool_path)
559 {
560   char new_path[FN_REFLEN];
561   char source_path[FN_REFLEN];
562 #if __WIN__
563   char win_abs_path[FN_REFLEN];
564   char self_name[FN_REFLEN];
565   const char *last_fn_libchar;
566 #endif
567 
568   if ((strlen(base_path) + strlen(subdir) + 1) > FN_REFLEN)
569   {
570     fprintf(stderr, "WARNING: Search path is too long\n");
571     return 1;
572   }
573   strcpy(source_path, base_path);
574   strcat(source_path, subdir);
575   fn_format(new_path, tool_name, source_path, "", MY_UNPACK_FILENAME);
576   if (file_exists(new_path))
577   {
578     strcpy(tool_path, new_path);
579     return 0;
580   }
581 
582 #if __WIN__
583   /*
584     On Windows above code will not be able to find the file since
585     path names are not absolute and file_exists works only with
586     absolute path names. Try to get the absolute path of current
587     exe and see if the file exists relative to the path of exe.
588   */
589   if (GetModuleFileName(NULL, self_name, FN_REFLEN -1) == 0)
590     strncpy(self_name, my_progname, FN_REFLEN - 1);
591   self_name[FN_REFLEN - 1]= '\0';
592 
593   last_fn_libchar= strrchr(self_name, FN_LIBCHAR);
594   if (NULL != last_fn_libchar)
595   {
596     strncpy(win_abs_path, self_name, last_fn_libchar - self_name + 1 );
597     win_abs_path[(last_fn_libchar - self_name + 1)]= 0;
598     strncat(win_abs_path, new_path,
599             sizeof(win_abs_path) - strlen(win_abs_path) - 1);
600     if (file_exists(win_abs_path))
601     {
602       strcpy(tool_path, win_abs_path);
603       return 0;
604     }
605   }
606 #endif
607   return 1;
608 }
609 
610 
611 /**
612   Search known common paths and sub directories for a file name.
613 
614   @param[in]  base_path  Original path to use.
615   @param[in]  tool_name  Name of the tool to locate.
616   @param[out] tool_path  If tool found, return complete path.
617 
618   @retval int error = 1, success = 0
619 */
620 
search_paths(const char * base_path,const char * tool_name,char * tool_path)621 static int search_paths(const char *base_path, const char *tool_name,
622                         char *tool_path)
623 {
624   int i= 0;
625 
626   static const char *paths[]= {
627     "", "/share/",  "/scripts/", "/bin/", "/sbin/", "/libexec/",
628     "/mysql/", "/sql/",
629   };
630   for (i = 0 ; i < (int)array_elements(paths); i++)
631   {
632     if (!search_dir(base_path, tool_name, paths[i], tool_path))
633     {
634       return 0;
635     }
636   }
637   return 1;
638 }
639 
640 
641 /**
642   Read the plugin ini file.
643 
644   This function attempts to read the plugin config file from the plugin_dir
645   path saving the data in the the st_plugin structure. If the file is not
646   found or the file cannot be read, an error is generated.
647 
648   @retval int error = 1, success = 0
649 */
650 
load_plugin_data(char * plugin_name,char * config_file)651 static int load_plugin_data(char *plugin_name, char *config_file)
652 {
653   FILE *file_ptr;
654   char path[FN_REFLEN];
655   char line[1024];
656   char *reason= 0;
657   char *res;
658   int i= -1;
659 
660   if (opt_plugin_ini == 0)
661   {
662     fn_format(path, config_file, opt_plugin_dir, "", MYF(0));
663     opt_plugin_ini= my_strdup(path, MYF(MY_FAE));
664   }
665   if (!file_exists(opt_plugin_ini))
666   {
667     reason= (char *)"File does not exist.";
668     goto error;
669   }
670 
671   file_ptr= fopen(opt_plugin_ini, "r");
672   if (file_ptr == NULL)
673   {
674     reason= (char *)"Cannot open file.";
675     goto error;
676   }
677 
678   /* save name */
679   plugin_data.name= my_strdup(plugin_name, MYF(MY_WME));
680 
681   /* Read plugin components */
682   while (i < 16)
683   {
684     res= fgets(line, sizeof(line), file_ptr);
685     /* strip /n */
686     if (line[strlen(line)-1] == '\n')
687     {
688       line[strlen(line)-1]= '\0';
689     }
690     if (res == NULL)
691     {
692       if (i < 1)
693       {
694         reason= (char *)"Bad format in plugin configuration file.";
695         fclose(file_ptr);
696         goto error;
697       }
698       break;
699     }
700     if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines
701     {
702       continue;
703     }
704     if (i == -1) // if first pass, read this line as so_name
705     {
706       /* Add proper file extension for soname */
707       strcat(line, FN_SOEXT);
708       /* save so_name */
709       plugin_data.so_name= my_strdup(line, MYF(MY_WME|MY_ZEROFILL));
710       i++;
711     }
712     else
713     {
714       if (strlen(line) > 0)
715       {
716         plugin_data.components[i]= my_strdup(line, MYF(MY_WME));
717         i++;
718       }
719       else
720       {
721         plugin_data.components[i]= NULL;
722       }
723     }
724   }
725 
726   fclose(file_ptr);
727   return 0;
728 
729 error:
730   fprintf(stderr, "ERROR: Cannot read plugin config file %s. %s\n",
731           plugin_name, reason);
732   return 1;
733 }
734 
735 
736 /**
737   Check the options for validity.
738 
739   This function checks the arguments for validity issuing the appropriate
740   error message if arguments are missing or invalid. On success, @operation
741   is set to either "ENABLE" or "DISABLE".
742 
743   @param[in]  argc       The number of arguments.
744   @param[in]  argv       The arguments.
745   @param[out] operation  The operation chosen (enable|disable)
746 
747   @retval int error = 1, success = 0
748 */
749 
check_options(int argc,char ** argv,char * operation)750 static int check_options(int argc, char **argv, char *operation)
751 {
752   int i= 0;                    // loop counter
753   int num_found= 0;            // number of options found (shortcut loop)
754   char config_file[FN_REFLEN]; // configuration file name
755   char plugin_name[FN_REFLEN]; // plugin name
756 
757   /* Form prefix strings for the options. */
758   const char *basedir_prefix = "--basedir=";
759   int basedir_len= strlen(basedir_prefix);
760   const char *datadir_prefix = "--datadir=";
761   int datadir_len= strlen(datadir_prefix);
762   const char *plugin_dir_prefix = "--plugin_dir=";
763   int plugin_dir_len= strlen(plugin_dir_prefix);
764 
765   strcpy(plugin_name, "");
766   for (i = 0; i < argc && num_found < 5; i++)
767   {
768 
769     if (!argv[i])
770     {
771       continue;
772     }
773     if ((strcasecmp(argv[i], "ENABLE") == 0) ||
774         (strcasecmp(argv[i], "DISABLE") == 0))
775     {
776       strcpy(operation, argv[i]);
777       num_found++;
778     }
779     else if ((strncasecmp(argv[i], basedir_prefix, basedir_len) == 0) &&
780              !opt_basedir)
781     {
782       opt_basedir= my_strndup(argv[i]+basedir_len,
783                               strlen(argv[i])-basedir_len, MYF(MY_FAE));
784       num_found++;
785     }
786     else if ((strncasecmp(argv[i], datadir_prefix, datadir_len) == 0) &&
787              !opt_datadir)
788     {
789       opt_datadir= my_strndup(argv[i]+datadir_len,
790                               strlen(argv[i])-datadir_len, MYF(MY_FAE));
791       num_found++;
792     }
793     else if ((strncasecmp(argv[i], plugin_dir_prefix, plugin_dir_len) == 0) &&
794              !opt_plugin_dir)
795     {
796       opt_plugin_dir= my_strndup(argv[i]+plugin_dir_len,
797                                  strlen(argv[i])-plugin_dir_len, MYF(MY_FAE));
798       num_found++;
799     }
800     /* read the plugin config file and check for match against argument */
801     else
802     {
803       if (strlen(argv[i]) + 4 + 1 > FN_REFLEN)
804       {
805         fprintf(stderr, "ERROR: argument is too long.\n");
806         return 1;
807       }
808       strcpy(plugin_name, argv[i]);
809       strcpy(config_file, argv[i]);
810       strcat(config_file, ".ini");
811     }
812   }
813 
814   if (!opt_basedir)
815   {
816     fprintf(stderr, "ERROR: Missing --basedir option.\n");
817     return 1;
818   }
819 
820   if (!opt_datadir)
821   {
822     fprintf(stderr, "ERROR: Missing --datadir option.\n");
823     return 1;
824   }
825 
826   if (!opt_plugin_dir)
827   {
828     fprintf(stderr, "ERROR: Missing --plugin_dir option.\n");
829     return 1;
830   }
831   /* If a plugin was specified, read the config file. */
832   else if (strlen(plugin_name) > 0)
833   {
834     if (load_plugin_data(plugin_name, config_file))
835     {
836       return 1;
837     }
838     if (strcasecmp(plugin_data.name, plugin_name) != 0)
839     {
840       fprintf(stderr, "ERROR: plugin name requested does not match config "
841               "file data.\n");
842       return 1;
843     }
844   }
845   else
846   {
847     fprintf(stderr, "ERROR: No plugin specified.\n");
848     return 1;
849   }
850 
851   if ((strlen(operation) == 0))
852   {
853     fprintf(stderr, "ERROR: missing operation. Please specify either "
854             "'<plugin> ENABLE' or '<plugin> DISABLE'.\n");
855     return 1;
856   }
857 
858   return 0;
859 }
860 
861 
862 /**
863   Parse, execute, and verify command options.
864 
865   This method handles all of the option processing including the optional
866   features for displaying data (--print-defaults, --help ,etc.) that do not
867   result in an attempt to ENABLE or DISABLE of a plugin.
868 
869   @param[in]   arc        Count of arguments
870   @param[in]   argv       Array of arguments
871   @param[out]  operation  Operation (ENABLE or DISABLE)
872 
873   @retval int error = 1, success = 0, exit program = -1
874 */
875 
process_options(int argc,char * argv[],char * operation)876 static int process_options(int argc, char *argv[], char *operation)
877 {
878   int error= 0;
879   int i= 0;
880 
881   /* Parse and execute command-line options */
882   if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
883     goto exit;
884 
885   /* If the print defaults option used, exit. */
886   if (opt_print_defaults)
887   {
888     error= -1;
889     goto exit;
890   }
891 
892   /* Add a trailing directory separator if not present */
893   if (opt_basedir)
894   {
895     i= (int)strlength(opt_basedir);
896     if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2)
897     {
898       char buff[FN_REFLEN];
899       memset(buff, 0, sizeof(buff));
900 
901       strncpy(buff, opt_basedir, sizeof(buff) - 1);
902 #ifdef __WIN__
903       strncat(buff, "/", sizeof(buff) - strlen(buff) - 1);
904 #else
905       strncat(buff, FN_DIRSEP, sizeof(buff) - strlen(buff) - 1);
906 #endif
907       buff[sizeof(buff) - 1]= 0;
908       my_free(opt_basedir);
909       opt_basedir= my_strdup(buff, MYF(MY_FAE));
910     }
911   }
912 
913   /*
914     If the user did not specify the option to skip loading defaults from a
915     config file and the required options are not present or there was an error
916     generated when the defaults were read from the file, exit.
917   */
918   if (!opt_no_defaults && ((error= get_default_values())))
919   {
920     error= -1;
921     goto exit;
922   }
923 
924   /*
925    Check to ensure required options are present and validate the operation.
926    Note: this method also validates the plugin specified by attempting to
927    read a configuration file named <plugin_name>.ini from the --plugin-dir
928    or --plugin-ini location if the --plugin-ini option presented.
929   */
930   strcpy(operation, "");
931   if ((error = check_options(argc, argv, operation)))
932   {
933     goto exit;
934   }
935 
936   if (opt_verbose)
937   {
938     printf("#    basedir = %s\n", opt_basedir);
939     printf("# plugin_dir = %s\n", opt_plugin_dir);
940     printf("#    datadir = %s\n", opt_datadir);
941     printf("# plugin_ini = %s\n", opt_plugin_ini);
942   }
943 
944 exit:
945   return error;
946 }
947 
948 
949 /**
950   Check access
951 
952   This method checks to ensure all of the directories (opt_basedir,
953   opt_plugin_dir, opt_datadir, and opt_plugin_ini) are accessible by
954   the user.
955 
956   @retval int error = 1, success = 0
957 */
958 
check_access()959 static int check_access()
960 {
961   int error= 0;
962 
963   if ((error= my_access(opt_basedir, F_OK)))
964   {
965     fprintf(stderr, "ERROR: Cannot access basedir at '%s'.\n",
966             opt_basedir);
967     goto exit;
968   }
969   if ((error= my_access(opt_plugin_dir, F_OK)))
970   {
971     fprintf(stderr, "ERROR: Cannot access plugin_dir at '%s'.\n",
972             opt_plugin_dir);
973     goto exit;
974   }
975   if ((error= my_access(opt_datadir, F_OK)))
976   {
977     fprintf(stderr, "ERROR: Cannot access datadir at '%s'.\n",
978             opt_datadir);
979     goto exit;
980   }
981   if (opt_plugin_ini && (error= my_access(opt_plugin_ini, F_OK)))
982   {
983     fprintf(stderr, "ERROR: Cannot access plugin config file at '%s'.\n",
984             opt_plugin_ini);
985     goto exit;
986   }
987   if (opt_mysqld && (error= my_access(opt_mysqld, F_OK)))
988   {
989     fprintf(stderr, "ERROR: Cannot access mysqld path '%s'.\n",
990             opt_mysqld);
991     goto exit;
992   }
993   if (opt_my_print_defaults && (error= my_access(opt_my_print_defaults, F_OK)))
994   {
995     fprintf(stderr, "ERROR: Cannot access my-print-defaults path '%s'.\n",
996             opt_my_print_defaults);
997     goto exit;
998   }
999 
1000 exit:
1001   return error;
1002 }
1003 
1004 
1005 /**
1006   Locate the tool and form tool path.
1007 
1008   @param[in]  tool_name  Name of the tool to locate.
1009   @param[out] tool_path  If tool found, return complete path.
1010 
1011   @retval int error = 1, success = 0
1012 */
1013 
find_tool(const char * tool_name,char * tool_path)1014 static int find_tool(const char *tool_name, char *tool_path)
1015 {
1016   int i= 0;
1017 
1018   const char *paths[]= {
1019     opt_mysqld, opt_basedir, opt_my_print_defaults, "",
1020     "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share",
1021     "/extra", "/extra/debug", "/../../extra/debug",
1022     "/release/", "/extra/release", "/../../extra/release",
1023     "/bin", "/usr/bin", "/mysql/bin"
1024   };
1025   for (i= 0; i < (int)array_elements(paths); i++)
1026   {
1027     if (paths[i] && !(search_paths(paths[i], tool_name, tool_path)))
1028       goto found;
1029   }
1030   fprintf(stderr, "WARNING: Cannot find %s.\n", tool_name);
1031   return 1;
1032 found:
1033   if (opt_verbose)
1034     printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path);
1035   return 0;
1036 }
1037 
1038 
1039 /**
1040   Find the plugin library.
1041 
1042   This function attempts to use the @c plugin_dir option passed on the
1043   command line to locate the plugin.
1044 
1045   @param[out] tp_path   The actual path to plugin with FN_SOEXT applied.
1046 
1047   @retval int error = 1, success = 0
1048 */
1049 
find_plugin(char * tp_path)1050 static int find_plugin(char *tp_path)
1051 {
1052   /* Check for existance of plugin */
1053   fn_format(tp_path, plugin_data.so_name, opt_plugin_dir, "", MYF(0));
1054   if (!file_exists(tp_path))
1055   {
1056     fprintf(stderr, "ERROR: The plugin library is missing or in a different"
1057             " location.\n");
1058     return 1;
1059   }
1060   else if (opt_verbose)
1061   {
1062     printf("# Found plugin '%s' as '%s'\n", plugin_data.name, tp_path);
1063   }
1064   return 0;
1065 }
1066 
1067 
1068 /**
1069   Build the boostrap file.
1070 
1071   Create a new file and populate it with SQL commands to ENABLE or DISABLE
1072   the plugin via REPLACE and DELETE operations on the mysql.plugin table.
1073 
1074   param[in]  operation  The type of operation (ENABLE or DISABLE)
1075   param[out] bootstrap  A FILE* pointer
1076 
1077   @retval int error = 1, success = 0
1078 */
1079 
build_bootstrap_file(char * operation,char * bootstrap)1080 static int build_bootstrap_file(char *operation, char *bootstrap)
1081 {
1082   int error= 0;
1083   FILE *file= 0;
1084 
1085   /*
1086     Perform plugin operation : ENABLE or DISABLE
1087 
1088     The following creates a temporary bootstrap file and populates it with
1089     the appropriate SQL commands for the operation. For ENABLE, REPLACE
1090     statements are created. For DISABLE, DELETE statements are created. The
1091     values for these statements are derived from the plugin_data read from the
1092     <plugin_name>.ini configuration file. Once the file is built, a call to
1093     mysqld is made in read only, bootstrap modes to read the SQL statements
1094     and execute them.
1095 
1096     Note: Replace was used so that if a user loads a newer version of a
1097           library with a different library name, the new library name is
1098           used for symbols that match.
1099   */
1100   if ((error= make_tempfile(bootstrap, "sql")))
1101   {
1102     /* Fail if we cannot create a temporary file for the bootstrap commands. */
1103     fprintf(stderr, "ERROR: Cannot create bootstrap file.\n");
1104     goto exit;
1105   }
1106   if ((file= fopen(bootstrap, "w+")) == NULL)
1107   {
1108     fprintf(stderr, "ERROR: Cannot open bootstrap file for writing.\n");
1109     error= 1;
1110     goto exit;
1111   }
1112   if (strcasecmp(operation, "enable") == 0)
1113   {
1114     int i= 0;
1115     fprintf(file, "REPLACE INTO mysql.plugin VALUES ");
1116     for (i= 0; i < (int)array_elements(plugin_data.components); i++)
1117     {
1118       /* stop when we read the end of the symbol list - marked with NULL */
1119       if (plugin_data.components[i] == NULL)
1120       {
1121         break;
1122       }
1123       if (i > 0)
1124       {
1125         fprintf(file, ", ");
1126       }
1127       fprintf(file, "('%s','%s')",
1128               plugin_data.components[i], plugin_data.so_name);
1129     }
1130     fprintf(file, ";\n");
1131     if (opt_verbose)
1132     {
1133       printf("# Enabling %s...\n", plugin_data.name);
1134     }
1135   }
1136   else
1137   {
1138     fprintf(file,
1139             "DELETE FROM mysql.plugin WHERE dl = '%s';", plugin_data.so_name);
1140     if (opt_verbose)
1141     {
1142       printf("# Disabling %s...\n", plugin_data.name);
1143     }
1144   }
1145 
1146 exit:
1147   fclose(file);
1148   return error;
1149 }
1150 
1151 
1152 /**
1153   Dump bootstrap file.
1154 
1155   Read the contents of the bootstrap file and print it out.
1156 
1157   @param[in]  bootstrap_file  Name of bootstrap file to read
1158 
1159   @retval int error = 1, success = 0
1160 */
1161 
dump_bootstrap_file(char * bootstrap_file)1162 static int dump_bootstrap_file(char *bootstrap_file)
1163 {
1164   char *ret= 0;
1165   int error= 0;
1166   char query_str[512];
1167   FILE *file= 0;
1168 
1169   if ((file= fopen(bootstrap_file, "r")) == NULL)
1170   {
1171     fprintf(stderr, "ERROR: Cannot open bootstrap file for reading.\n");
1172     error= 1;
1173     goto exit;
1174   }
1175   ret= fgets(query_str, 512, file);
1176   if (ret == 0)
1177   {
1178     fprintf(stderr, "ERROR: Cannot read bootstrap file.\n");
1179     error= 1;
1180     goto exit;
1181   }
1182   printf("# Query: %s\n", query_str);
1183 
1184 exit:
1185   if (file)
1186   {
1187     fclose(file);
1188   }
1189   return error;
1190 }
1191 
1192 
1193 /**
1194   Bootstrap the server
1195 
1196   Create a command line sequence to launch mysqld in bootstrap mode. This
1197   will allow mysqld to launch a minimal server instance to read and
1198   execute SQL commands from a file piped in (the boostrap file). We use
1199   the --no-defaults option to skip reading values from the config file.
1200 
1201   The bootstrap mode skips loading of plugins and many other subsystems.
1202   This allows the mysql_plugin tool to insert the correct rows into the
1203   mysql.plugin table (for ENABLE) or delete the rows (for DISABLE). Once
1204   the server is launched in normal mode, the plugin will be loaded
1205   (for ENABLE) or not loaded (for DISABLE). In this way, we avoid the
1206   (sometimes) complicated LOAD PLUGIN commands.
1207 
1208   @param[in]  server_path     Path to server executable
1209   @param[in]  bootstrap_file  Name of bootstrap file to read
1210 
1211   @retval int error = 1, success = 0
1212 */
1213 
bootstrap_server(char * server_path,char * bootstrap_file)1214 static int bootstrap_server(char *server_path, char *bootstrap_file)
1215 {
1216   char bootstrap_cmd[FN_REFLEN];
1217   int error= 0;
1218 
1219 #ifdef __WIN__
1220   char *format_str= 0;
1221   const char *verbose_str= NULL;
1222 
1223 
1224   if (opt_verbose)
1225     verbose_str= "--console";
1226   else
1227     verbose_str= "";
1228   if (has_spaces(opt_datadir) || has_spaces(opt_basedir) ||
1229       has_spaces(bootstrap_file))
1230     format_str= "\"%s %s --bootstrap --datadir=%s --basedir=%s < %s\"";
1231   else
1232     format_str= "%s %s --bootstrap --datadir=%s --basedir=%s < %s";
1233 
1234   snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), format_str,
1235            add_quotes(convert_path(server_path)), verbose_str,
1236            add_quotes(opt_datadir), add_quotes(opt_basedir),
1237            add_quotes(bootstrap_file));
1238 #else
1239   snprintf(bootstrap_cmd, sizeof(bootstrap_cmd),
1240            "%s --no-defaults --bootstrap --datadir=%s --basedir=%s"
1241            " < %s", server_path, opt_datadir, opt_basedir, bootstrap_file);
1242 #endif
1243 
1244   /* Execute the command */
1245   if (opt_verbose)
1246   {
1247     printf("# Command: %s\n", bootstrap_cmd);
1248   }
1249   error= run_command(bootstrap_cmd, "r");
1250   if (error)
1251     fprintf(stderr,
1252             "ERROR: Unexpected result from bootstrap. Error code: %d.\n",
1253             error);
1254 
1255   return error;
1256 }
1257