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   static const char format_cmd[]= "%s mysqld > %s";
326   char tool_path[FN_REFLEN];
327   char defaults_file[FN_REFLEN];
328   char defaults_cmd[sizeof(defaults_file) + sizeof(tool_path) +
329                     sizeof(format_cmd)];
330   char line[FN_REFLEN];
331   int error= 0;
332   int ret= 0;
333   FILE *file= 0;
334 
335   memset(tool_path, 0, FN_REFLEN);
336   if ((error= find_tool("my_print_defaults" FN_EXEEXT, tool_path)))
337     goto exit;
338   else
339   {
340     if ((error= make_tempfile(defaults_file, "txt")))
341       goto exit;
342 
343 #ifdef __WIN__
344     {
345       char *format_str= 0;
346 
347       if (has_spaces(tool_path) || has_spaces(defaults_file))
348         format_str = "\"%s mysqld > %s\"";
349       else
350         format_str = "%s mysqld > %s";
351 
352       snprintf(defaults_cmd, sizeof(defaults_cmd), format_str,
353                add_quotes(tool_path), add_quotes(defaults_file));
354       if (opt_verbose)
355       {
356         printf("# my_print_defaults found: %s\n", tool_path);
357       }
358     }
359 #else
360     snprintf(defaults_cmd, sizeof(defaults_cmd),
361              format_cmd, tool_path, defaults_file);
362 #endif
363 
364     /* Execute the command */
365     if (opt_verbose)
366     {
367       printf("# Command: %s\n", defaults_cmd);
368     }
369     error= run_command(defaults_cmd, "r");
370     if (error)
371     {
372       fprintf(stderr, "ERROR: my_print_defaults failed. Error code: %d.\n",
373               ret);
374       goto exit;
375     }
376     /* Now open the file and read the defaults we want. */
377     file= fopen(defaults_file, "r");
378     while (fgets(line, FN_REFLEN, file) != NULL)
379     {
380       char *value= 0;
381 
382       if ((opt_datadir == 0) && ((value= get_value(line, "--datadir"))))
383       {
384         opt_datadir= my_strdup(value, MYF(MY_FAE));
385       }
386       if ((opt_basedir == 0) && ((value= get_value(line, "--basedir"))))
387       {
388         opt_basedir= my_strdup(value, MYF(MY_FAE));
389       }
390       if ((opt_plugin_dir == 0) && ((value= get_value(line, "--plugin_dir"))))
391       {
392         opt_plugin_dir= my_strdup(value, MYF(MY_FAE));
393       }
394       if ((opt_plugin_ini == 0) && ((value= get_value(line, "--plugin_ini"))))
395       {
396         opt_plugin_ini= my_strdup(value, MYF(MY_FAE));
397       }
398     }
399   }
400 exit:
401   if (file)
402   {
403     fclose(file);
404     /* Remove file */
405     my_delete(defaults_file, MYF(0));
406   }
407   return error;
408 }
409 
410 
411 /**
412   Print usage.
413 */
414 
usage(void)415 static void usage(void)
416 {
417   PRINT_VERSION;
418   puts("Copyright (c) 2011, 2016, Oracle and/or its affiliates. "
419        "All rights reserved.\n");
420   puts("Enable or disable plugins.");
421   printf("\nUsage: %s [options] <plugin> ENABLE|DISABLE\n\nOptions:\n",
422      my_progname);
423   my_print_help(my_long_options);
424   puts("\n");
425 }
426 
427 
428 /**
429   Print the default values as read from the my.cnf file.
430 
431   This method displays the default values for the following parameters:
432 
433   --datadir
434   --basedir
435   --plugin-dir
436   --plugin-ini
437 
438 */
439 
print_default_values(void)440 static void print_default_values(void)
441 {
442   printf("%s would have been started with the following arguments:\n",
443          my_progname);
444   get_default_values();
445   if (opt_datadir)
446   {
447     printf("--datadir=%s ", opt_datadir);
448   }
449   if (opt_basedir)
450   {
451     printf("--basedir=%s ", opt_basedir);
452   }
453   if (opt_plugin_dir)
454   {
455     printf("--plugin_dir=%s ", opt_plugin_dir);
456   }
457   if (opt_plugin_ini)
458   {
459     printf("--plugin_ini=%s ", opt_plugin_ini);
460   }
461   if (opt_mysqld)
462   {
463     printf("--mysqld=%s ", opt_mysqld);
464   }
465   if (opt_my_print_defaults)
466   {
467     printf("--my_print_defaults=%s ", opt_my_print_defaults);
468   }
469   printf("\n");
470 }
471 
472 
473 /**
474   Process the arguments and identify an option and store its value.
475 
476   @param[in]  optid      The single character shortcut for the argument.
477   @param[in]  my_option  Structure of legal options.
478   @param[in]  argument   The argument value to process.
479 */
480 
481 static my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)482 get_one_option(int optid,
483                const struct my_option *opt MY_ATTRIBUTE((unused)),
484                char *argument)
485 {
486   switch(optid) {
487   case 'n':
488     opt_no_defaults++;
489     break;
490   case 'P':
491     opt_print_defaults++;
492     print_default_values();
493     break;
494   case 'v':
495     opt_verbose++;
496     break;
497   case 'V':
498     PRINT_VERSION;
499     exit(0);
500     break;
501   case '?':
502   case 'I':          /* Info */
503     usage();
504     exit(0);
505   case 'd':
506     opt_datadir= my_strdup(argument, MYF(MY_FAE));
507     break;
508   case 'b':
509     opt_basedir= my_strdup(argument, MYF(MY_FAE));
510     break;
511   case 'p':
512     opt_plugin_dir= my_strdup(argument, MYF(MY_FAE));
513     break;
514   case 'i':
515     opt_plugin_ini= my_strdup(argument, MYF(MY_FAE));
516     break;
517   case 'm':
518     opt_mysqld= my_strdup(argument, MYF(MY_FAE));
519     break;
520   case 'f':
521     opt_my_print_defaults= my_strdup(argument, MYF(MY_FAE));
522     break;
523   }
524   return 0;
525 }
526 
527 
528 /**
529   Check to see if a file exists.
530 
531   @param[in]  filename  File to locate.
532 
533   @retval int file not found = 1, file found = 0
534 */
535 
file_exists(char * filename)536 static int file_exists(char * filename)
537 {
538   MY_STAT stat_arg;
539 
540   if (!my_stat(filename, &stat_arg, MYF(0)))
541   {
542     return 0;
543   }
544   return 1;
545 }
546 
547 
548 /**
549   Search a specific path and sub directory for a file name.
550 
551   @param[in]  base_path  Original path to use.
552   @param[in]  tool_name  Name of the tool to locate.
553   @param[in]  subdir     The sub directory to search.
554   @param[out] tool_path  If tool found, return complete path.
555 
556   @retval int error = 1, success = 0
557 */
558 
search_dir(const char * base_path,const char * tool_name,const char * subdir,char * tool_path)559 static int search_dir(const char * base_path, const char *tool_name,
560                       const char *subdir, char *tool_path)
561 {
562   char new_path[FN_REFLEN];
563   char source_path[FN_REFLEN];
564 #if __WIN__
565   char win_abs_path[FN_REFLEN];
566   char self_name[FN_REFLEN];
567   const char *last_fn_libchar;
568 #endif
569 
570   if ((strlen(base_path) + strlen(subdir) + 1) > FN_REFLEN)
571   {
572     fprintf(stderr, "WARNING: Search path is too long\n");
573     return 1;
574   }
575   strcpy(source_path, base_path);
576   strcat(source_path, subdir);
577   fn_format(new_path, tool_name, source_path, "", MY_UNPACK_FILENAME);
578   if (file_exists(new_path))
579   {
580     strcpy(tool_path, new_path);
581     return 0;
582   }
583 
584 #if __WIN__
585   /*
586     On Windows above code will not be able to find the file since
587     path names are not absolute and file_exists works only with
588     absolute path names. Try to get the absolute path of current
589     exe and see if the file exists relative to the path of exe.
590   */
591   if (GetModuleFileName(NULL, self_name, FN_REFLEN -1) == 0)
592     strncpy(self_name, my_progname, FN_REFLEN - 1);
593   self_name[FN_REFLEN - 1]= '\0';
594 
595   last_fn_libchar= strrchr(self_name, FN_LIBCHAR);
596   if (NULL != last_fn_libchar)
597   {
598     strncpy(win_abs_path, self_name, last_fn_libchar - self_name + 1 );
599     win_abs_path[(last_fn_libchar - self_name + 1)]= 0;
600     strncat(win_abs_path, new_path,
601             sizeof(win_abs_path) - strlen(win_abs_path) - 1);
602     if (file_exists(win_abs_path))
603     {
604       strcpy(tool_path, win_abs_path);
605       return 0;
606     }
607   }
608 #endif
609   return 1;
610 }
611 
612 
613 /**
614   Search known common paths and sub directories for a file name.
615 
616   @param[in]  base_path  Original path to use.
617   @param[in]  tool_name  Name of the tool to locate.
618   @param[out] tool_path  If tool found, return complete path.
619 
620   @retval int error = 1, success = 0
621 */
622 
search_paths(const char * base_path,const char * tool_name,char * tool_path)623 static int search_paths(const char *base_path, const char *tool_name,
624                         char *tool_path)
625 {
626   int i= 0;
627 
628   static const char *paths[]= {
629     "", "/share/",  "/scripts/", "/bin/", "/sbin/", "/libexec/",
630     "/mysql/", "/sql/",
631   };
632   for (i = 0 ; i < (int)array_elements(paths); i++)
633   {
634     if (!search_dir(base_path, tool_name, paths[i], tool_path))
635     {
636       return 0;
637     }
638   }
639   return 1;
640 }
641 
642 
643 /**
644   Read the plugin ini file.
645 
646   This function attempts to read the plugin config file from the plugin_dir
647   path saving the data in the the st_plugin structure. If the file is not
648   found or the file cannot be read, an error is generated.
649 
650   @retval int error = 1, success = 0
651 */
652 
load_plugin_data(char * plugin_name,char * config_file)653 static int load_plugin_data(char *plugin_name, char *config_file)
654 {
655   FILE *file_ptr;
656   char path[FN_REFLEN];
657   char line[1024];
658   char *reason= 0;
659   char *res;
660   int i= -1;
661 
662   if (opt_plugin_ini == 0)
663   {
664     fn_format(path, config_file, opt_plugin_dir, "", MYF(0));
665     opt_plugin_ini= my_strdup(path, MYF(MY_FAE));
666   }
667   if (!file_exists(opt_plugin_ini))
668   {
669     reason= (char *)"File does not exist.";
670     goto error;
671   }
672 
673   file_ptr= fopen(opt_plugin_ini, "r");
674   if (file_ptr == NULL)
675   {
676     reason= (char *)"Cannot open file.";
677     goto error;
678   }
679 
680   /* save name */
681   plugin_data.name= my_strdup(plugin_name, MYF(MY_WME));
682 
683   /* Read plugin components */
684   while (i < 16)
685   {
686     res= fgets(line, sizeof(line), file_ptr);
687     /* strip /n */
688     if (line[strlen(line)-1] == '\n')
689     {
690       line[strlen(line)-1]= '\0';
691     }
692     if (res == NULL)
693     {
694       if (i < 1)
695       {
696         reason= (char *)"Bad format in plugin configuration file.";
697         fclose(file_ptr);
698         goto error;
699       }
700       break;
701     }
702     if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines
703     {
704       continue;
705     }
706     if (i == -1) // if first pass, read this line as so_name
707     {
708       /* Add proper file extension for soname */
709       strcat(line, FN_SOEXT);
710       /* save so_name */
711       plugin_data.so_name= my_strdup(line, MYF(MY_WME|MY_ZEROFILL));
712       i++;
713     }
714     else
715     {
716       if (strlen(line) > 0)
717       {
718         plugin_data.components[i]= my_strdup(line, MYF(MY_WME));
719         i++;
720       }
721       else
722       {
723         plugin_data.components[i]= NULL;
724       }
725     }
726   }
727 
728   fclose(file_ptr);
729   return 0;
730 
731 error:
732   fprintf(stderr, "ERROR: Cannot read plugin config file %s. %s\n",
733           plugin_name, reason);
734   return 1;
735 }
736 
737 
738 /**
739   Check the options for validity.
740 
741   This function checks the arguments for validity issuing the appropriate
742   error message if arguments are missing or invalid. On success, @operation
743   is set to either "ENABLE" or "DISABLE".
744 
745   @param[in]  argc       The number of arguments.
746   @param[in]  argv       The arguments.
747   @param[out] operation  The operation chosen (enable|disable)
748 
749   @retval int error = 1, success = 0
750 */
751 
check_options(int argc,char ** argv,char * operation)752 static int check_options(int argc, char **argv, char *operation)
753 {
754   int i= 0;                    // loop counter
755   int num_found= 0;            // number of options found (shortcut loop)
756   char config_file[FN_REFLEN]; // configuration file name
757   char plugin_name[FN_REFLEN]; // plugin name
758 
759   /* Form prefix strings for the options. */
760   const char *basedir_prefix = "--basedir=";
761   int basedir_len= strlen(basedir_prefix);
762   const char *datadir_prefix = "--datadir=";
763   int datadir_len= strlen(datadir_prefix);
764   const char *plugin_dir_prefix = "--plugin_dir=";
765   int plugin_dir_len= strlen(plugin_dir_prefix);
766 
767   strcpy(plugin_name, "");
768   for (i = 0; i < argc && num_found < 5; i++)
769   {
770 
771     if (!argv[i])
772     {
773       continue;
774     }
775     if ((strcasecmp(argv[i], "ENABLE") == 0) ||
776         (strcasecmp(argv[i], "DISABLE") == 0))
777     {
778       strcpy(operation, argv[i]);
779       num_found++;
780     }
781     else if ((strncasecmp(argv[i], basedir_prefix, basedir_len) == 0) &&
782              !opt_basedir)
783     {
784       opt_basedir= my_strndup(argv[i]+basedir_len,
785                               strlen(argv[i])-basedir_len, MYF(MY_FAE));
786       num_found++;
787     }
788     else if ((strncasecmp(argv[i], datadir_prefix, datadir_len) == 0) &&
789              !opt_datadir)
790     {
791       opt_datadir= my_strndup(argv[i]+datadir_len,
792                               strlen(argv[i])-datadir_len, MYF(MY_FAE));
793       num_found++;
794     }
795     else if ((strncasecmp(argv[i], plugin_dir_prefix, plugin_dir_len) == 0) &&
796              !opt_plugin_dir)
797     {
798       opt_plugin_dir= my_strndup(argv[i]+plugin_dir_len,
799                                  strlen(argv[i])-plugin_dir_len, MYF(MY_FAE));
800       num_found++;
801     }
802     /* read the plugin config file and check for match against argument */
803     else
804     {
805       if (strlen(argv[i]) + 4 + 1 > FN_REFLEN)
806       {
807         fprintf(stderr, "ERROR: argument is too long.\n");
808         return 1;
809       }
810       strcpy(plugin_name, argv[i]);
811       strcpy(config_file, argv[i]);
812       strcat(config_file, ".ini");
813     }
814   }
815 
816   if (!opt_basedir)
817   {
818     fprintf(stderr, "ERROR: Missing --basedir option.\n");
819     return 1;
820   }
821 
822   if (!opt_datadir)
823   {
824     fprintf(stderr, "ERROR: Missing --datadir option.\n");
825     return 1;
826   }
827 
828   if (!opt_plugin_dir)
829   {
830     fprintf(stderr, "ERROR: Missing --plugin_dir option.\n");
831     return 1;
832   }
833   /* If a plugin was specified, read the config file. */
834   else if (strlen(plugin_name) > 0)
835   {
836     if (load_plugin_data(plugin_name, config_file))
837     {
838       return 1;
839     }
840     if (strcasecmp(plugin_data.name, plugin_name) != 0)
841     {
842       fprintf(stderr, "ERROR: plugin name requested does not match config "
843               "file data.\n");
844       return 1;
845     }
846   }
847   else
848   {
849     fprintf(stderr, "ERROR: No plugin specified.\n");
850     return 1;
851   }
852 
853   if ((strlen(operation) == 0))
854   {
855     fprintf(stderr, "ERROR: missing operation. Please specify either "
856             "'<plugin> ENABLE' or '<plugin> DISABLE'.\n");
857     return 1;
858   }
859 
860   return 0;
861 }
862 
863 
864 /**
865   Parse, execute, and verify command options.
866 
867   This method handles all of the option processing including the optional
868   features for displaying data (--print-defaults, --help ,etc.) that do not
869   result in an attempt to ENABLE or DISABLE of a plugin.
870 
871   @param[in]   arc        Count of arguments
872   @param[in]   argv       Array of arguments
873   @param[out]  operation  Operation (ENABLE or DISABLE)
874 
875   @retval int error = 1, success = 0, exit program = -1
876 */
877 
process_options(int argc,char * argv[],char * operation)878 static int process_options(int argc, char *argv[], char *operation)
879 {
880   int error= 0;
881   int i= 0;
882 
883   /* Parse and execute command-line options */
884   if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
885     goto exit;
886 
887   /* If the print defaults option used, exit. */
888   if (opt_print_defaults)
889   {
890     error= -1;
891     goto exit;
892   }
893 
894   /* Add a trailing directory separator if not present */
895   if (opt_basedir)
896   {
897     i= (int)strlength(opt_basedir);
898     if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2)
899     {
900       char buff[FN_REFLEN];
901       memset(buff, 0, sizeof(buff));
902 
903       strncpy(buff, opt_basedir, sizeof(buff) - 1);
904 #ifdef __WIN__
905       strncat(buff, "/", sizeof(buff) - strlen(buff) - 1);
906 #else
907       strncat(buff, FN_DIRSEP, sizeof(buff) - strlen(buff) - 1);
908 #endif
909       buff[sizeof(buff) - 1]= 0;
910       my_free(opt_basedir);
911       opt_basedir= my_strdup(buff, MYF(MY_FAE));
912     }
913   }
914 
915   /*
916     If the user did not specify the option to skip loading defaults from a
917     config file and the required options are not present or there was an error
918     generated when the defaults were read from the file, exit.
919   */
920   if (!opt_no_defaults && ((error= get_default_values())))
921   {
922     error= -1;
923     goto exit;
924   }
925 
926   /*
927    Check to ensure required options are present and validate the operation.
928    Note: this method also validates the plugin specified by attempting to
929    read a configuration file named <plugin_name>.ini from the --plugin-dir
930    or --plugin-ini location if the --plugin-ini option presented.
931   */
932   strcpy(operation, "");
933   if ((error = check_options(argc, argv, operation)))
934   {
935     goto exit;
936   }
937 
938   if (opt_verbose)
939   {
940     printf("#    basedir = %s\n", opt_basedir);
941     printf("# plugin_dir = %s\n", opt_plugin_dir);
942     printf("#    datadir = %s\n", opt_datadir);
943     printf("# plugin_ini = %s\n", opt_plugin_ini);
944   }
945 
946 exit:
947   return error;
948 }
949 
950 
951 /**
952   Check access
953 
954   This method checks to ensure all of the directories (opt_basedir,
955   opt_plugin_dir, opt_datadir, and opt_plugin_ini) are accessible by
956   the user.
957 
958   @retval int error = 1, success = 0
959 */
960 
check_access()961 static int check_access()
962 {
963   int error= 0;
964 
965   if ((error= my_access(opt_basedir, F_OK)))
966   {
967     fprintf(stderr, "ERROR: Cannot access basedir at '%s'.\n",
968             opt_basedir);
969     goto exit;
970   }
971   if ((error= my_access(opt_plugin_dir, F_OK)))
972   {
973     fprintf(stderr, "ERROR: Cannot access plugin_dir at '%s'.\n",
974             opt_plugin_dir);
975     goto exit;
976   }
977   if ((error= my_access(opt_datadir, F_OK)))
978   {
979     fprintf(stderr, "ERROR: Cannot access datadir at '%s'.\n",
980             opt_datadir);
981     goto exit;
982   }
983   if (opt_plugin_ini && (error= my_access(opt_plugin_ini, F_OK)))
984   {
985     fprintf(stderr, "ERROR: Cannot access plugin config file at '%s'.\n",
986             opt_plugin_ini);
987     goto exit;
988   }
989   if (opt_mysqld && (error= my_access(opt_mysqld, F_OK)))
990   {
991     fprintf(stderr, "ERROR: Cannot access mysqld path '%s'.\n",
992             opt_mysqld);
993     goto exit;
994   }
995   if (opt_my_print_defaults && (error= my_access(opt_my_print_defaults, F_OK)))
996   {
997     fprintf(stderr, "ERROR: Cannot access my-print-defaults path '%s'.\n",
998             opt_my_print_defaults);
999     goto exit;
1000   }
1001 
1002 exit:
1003   return error;
1004 }
1005 
1006 
1007 /**
1008   Locate the tool and form tool path.
1009 
1010   @param[in]  tool_name  Name of the tool to locate.
1011   @param[out] tool_path  If tool found, return complete path.
1012 
1013   @retval int error = 1, success = 0
1014 */
1015 
find_tool(const char * tool_name,char * tool_path)1016 static int find_tool(const char *tool_name, char *tool_path)
1017 {
1018   int i= 0;
1019 
1020   const char *paths[]= {
1021     opt_mysqld, opt_basedir, opt_my_print_defaults, "",
1022     "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share",
1023     "/extra", "/extra/debug", "/../../extra/debug",
1024     "/release/", "/extra/release", "/../../extra/release",
1025     "/bin", "/usr/bin", "/mysql/bin"
1026   };
1027   for (i= 0; i < (int)array_elements(paths); i++)
1028   {
1029     if (paths[i] && !(search_paths(paths[i], tool_name, tool_path)))
1030       goto found;
1031   }
1032   fprintf(stderr, "WARNING: Cannot find %s.\n", tool_name);
1033   return 1;
1034 found:
1035   if (opt_verbose)
1036     printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path);
1037   return 0;
1038 }
1039 
1040 
1041 /**
1042   Find the plugin library.
1043 
1044   This function attempts to use the @c plugin_dir option passed on the
1045   command line to locate the plugin.
1046 
1047   @param[out] tp_path   The actual path to plugin with FN_SOEXT applied.
1048 
1049   @retval int error = 1, success = 0
1050 */
1051 
find_plugin(char * tp_path)1052 static int find_plugin(char *tp_path)
1053 {
1054   /* Check for existance of plugin */
1055   fn_format(tp_path, plugin_data.so_name, opt_plugin_dir, "", MYF(0));
1056   if (!file_exists(tp_path))
1057   {
1058     fprintf(stderr, "ERROR: The plugin library is missing or in a different"
1059             " location.\n");
1060     return 1;
1061   }
1062   else if (opt_verbose)
1063   {
1064     printf("# Found plugin '%s' as '%s'\n", plugin_data.name, tp_path);
1065   }
1066   return 0;
1067 }
1068 
1069 
1070 /**
1071   Build the boostrap file.
1072 
1073   Create a new file and populate it with SQL commands to ENABLE or DISABLE
1074   the plugin via REPLACE and DELETE operations on the mysql.plugin table.
1075 
1076   param[in]  operation  The type of operation (ENABLE or DISABLE)
1077   param[out] bootstrap  A FILE* pointer
1078 
1079   @retval int error = 1, success = 0
1080 */
1081 
build_bootstrap_file(char * operation,char * bootstrap)1082 static int build_bootstrap_file(char *operation, char *bootstrap)
1083 {
1084   int error= 0;
1085   FILE *file= 0;
1086 
1087   /*
1088     Perform plugin operation : ENABLE or DISABLE
1089 
1090     The following creates a temporary bootstrap file and populates it with
1091     the appropriate SQL commands for the operation. For ENABLE, REPLACE
1092     statements are created. For DISABLE, DELETE statements are created. The
1093     values for these statements are derived from the plugin_data read from the
1094     <plugin_name>.ini configuration file. Once the file is built, a call to
1095     mysqld is made in read only, bootstrap modes to read the SQL statements
1096     and execute them.
1097 
1098     Note: Replace was used so that if a user loads a newer version of a
1099           library with a different library name, the new library name is
1100           used for symbols that match.
1101   */
1102   if ((error= make_tempfile(bootstrap, "sql")))
1103   {
1104     /* Fail if we cannot create a temporary file for the bootstrap commands. */
1105     fprintf(stderr, "ERROR: Cannot create bootstrap file.\n");
1106     goto exit;
1107   }
1108   if ((file= fopen(bootstrap, "w+")) == NULL)
1109   {
1110     fprintf(stderr, "ERROR: Cannot open bootstrap file for writing.\n");
1111     error= 1;
1112     goto exit;
1113   }
1114   if (strcasecmp(operation, "enable") == 0)
1115   {
1116     int i= 0;
1117     fprintf(file, "REPLACE INTO mysql.plugin VALUES ");
1118     for (i= 0; i < (int)array_elements(plugin_data.components); i++)
1119     {
1120       /* stop when we read the end of the symbol list - marked with NULL */
1121       if (plugin_data.components[i] == NULL)
1122       {
1123         break;
1124       }
1125       if (i > 0)
1126       {
1127         fprintf(file, ", ");
1128       }
1129       fprintf(file, "('%s','%s')",
1130               plugin_data.components[i], plugin_data.so_name);
1131     }
1132     fprintf(file, ";\n");
1133     if (opt_verbose)
1134     {
1135       printf("# Enabling %s...\n", plugin_data.name);
1136     }
1137   }
1138   else
1139   {
1140     fprintf(file,
1141             "DELETE FROM mysql.plugin WHERE dl = '%s';", plugin_data.so_name);
1142     if (opt_verbose)
1143     {
1144       printf("# Disabling %s...\n", plugin_data.name);
1145     }
1146   }
1147 
1148 exit:
1149   fclose(file);
1150   return error;
1151 }
1152 
1153 
1154 /**
1155   Dump bootstrap file.
1156 
1157   Read the contents of the bootstrap file and print it out.
1158 
1159   @param[in]  bootstrap_file  Name of bootstrap file to read
1160 
1161   @retval int error = 1, success = 0
1162 */
1163 
dump_bootstrap_file(char * bootstrap_file)1164 static int dump_bootstrap_file(char *bootstrap_file)
1165 {
1166   char *ret= 0;
1167   int error= 0;
1168   char query_str[512];
1169   FILE *file= 0;
1170 
1171   if ((file= fopen(bootstrap_file, "r")) == NULL)
1172   {
1173     fprintf(stderr, "ERROR: Cannot open bootstrap file for reading.\n");
1174     error= 1;
1175     goto exit;
1176   }
1177   ret= fgets(query_str, 512, file);
1178   if (ret == 0)
1179   {
1180     fprintf(stderr, "ERROR: Cannot read bootstrap file.\n");
1181     error= 1;
1182     goto exit;
1183   }
1184   printf("# Query: %s\n", query_str);
1185 
1186 exit:
1187   if (file)
1188   {
1189     fclose(file);
1190   }
1191   return error;
1192 }
1193 
1194 
1195 /**
1196   Bootstrap the server
1197 
1198   Create a command line sequence to launch mysqld in bootstrap mode. This
1199   will allow mysqld to launch a minimal server instance to read and
1200   execute SQL commands from a file piped in (the boostrap file). We use
1201   the --no-defaults option to skip reading values from the config file.
1202 
1203   The bootstrap mode skips loading of plugins and many other subsystems.
1204   This allows the mysql_plugin tool to insert the correct rows into the
1205   mysql.plugin table (for ENABLE) or delete the rows (for DISABLE). Once
1206   the server is launched in normal mode, the plugin will be loaded
1207   (for ENABLE) or not loaded (for DISABLE). In this way, we avoid the
1208   (sometimes) complicated LOAD PLUGIN commands.
1209 
1210   @param[in]  server_path     Path to server executable
1211   @param[in]  bootstrap_file  Name of bootstrap file to read
1212 
1213   @retval int error = 1, success = 0
1214 */
1215 
bootstrap_server(char * server_path,char * bootstrap_file)1216 static int bootstrap_server(char *server_path, char *bootstrap_file)
1217 {
1218   static const char format_cmd[]= "%s --no-defaults --bootstrap --datadir=%s "
1219                            "--basedir=%s < %s";
1220   char bootstrap_cmd[FN_REFLEN * 2 + sizeof(format_cmd)];
1221   int error= 0;
1222 
1223 #ifdef __WIN__
1224   char *format_str= 0;
1225   const char *verbose_str= NULL;
1226 
1227 
1228   if (opt_verbose)
1229     verbose_str= "--console";
1230   else
1231     verbose_str= "";
1232   if (has_spaces(opt_datadir) || has_spaces(opt_basedir) ||
1233       has_spaces(bootstrap_file))
1234     format_str= "\"%s %s --bootstrap --datadir=%s --basedir=%s < %s\"";
1235   else
1236     format_str= "%s %s --bootstrap --datadir=%s --basedir=%s < %s";
1237 
1238   snprintf(bootstrap_cmd, sizeof(bootstrap_cmd), format_str,
1239            add_quotes(convert_path(server_path)), verbose_str,
1240            add_quotes(opt_datadir), add_quotes(opt_basedir),
1241            add_quotes(bootstrap_file));
1242 #else
1243   snprintf(bootstrap_cmd, sizeof(bootstrap_cmd),
1244            format_cmd, server_path, opt_datadir, opt_basedir, bootstrap_file);
1245 #endif
1246 
1247   /* Execute the command */
1248   if (opt_verbose)
1249   {
1250     printf("# Command: %s\n", bootstrap_cmd);
1251   }
1252   error= run_command(bootstrap_cmd, "r");
1253   if (error)
1254     fprintf(stderr,
1255             "ERROR: Unexpected result from bootstrap. Error code: %d.\n",
1256             error);
1257 
1258   return error;
1259 }
1260