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