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