1#!@PERL_PATH@
2
3# Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License, version 2.0,
7# as published by the Free Software Foundation.
8#
9# This program is also distributed with certain software (including
10# but not limited to OpenSSL) that is licensed under separate terms,
11# as designated in a particular file or component or in included license
12# documentation.  The authors of MySQL hereby grant you an additional
13# permission to link the program and your derivative works with the
14# separately licensed software that they have included with MySQL.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU General Public License, version 2.0, for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24
25use Getopt::Long;
26use POSIX qw(strftime getcwd);
27use File::Path qw(mkpath);
28
29$|=1;
30$VER="2.16";
31
32my @defaults_options;   #  Leading --no-defaults, --defaults-file, etc.
33
34$opt_example       = 0;
35$opt_help          = 0;
36$opt_log           = undef();
37$opt_mysqladmin    = "@bindir@/mysqladmin";
38$opt_mysqld        = "@libexecdir@/mysqld";
39$opt_no_log        = 0;
40$opt_password      = undef();
41$opt_tcp_ip        = 0;
42$opt_user          = "root";
43$opt_version       = 0;
44$opt_silent        = 0;
45$opt_verbose       = 0;
46
47my $my_print_defaults_exists= 1;
48my $logdir= undef();
49
50my ($mysqld, $mysqladmin, $groupids, $homedir, $my_progname);
51
52$homedir = $ENV{HOME};
53$my_progname = $0;
54$my_progname =~ s/.*[\/]//;
55
56
57if (defined($ENV{UMASK})) {
58  my $UMASK = $ENV{UMASK};
59  my $m;
60  my $fmode = "0640";
61
62  if(($UMASK =~ m/[^0246]/) || ($UMASK =~ m/^[^0]/) || (length($UMASK) != 4)) {
63    printf("UMASK must be a 3-digit mode with an additional leading 0 to indicate octal.\n");
64    printf("The first digit will be corrected to 6, the others may be 0, 2, 4, or 6.\n"); }
65  else {
66    $fmode= substr $UMASK, 2, 2;
67    $fmode= "06${fmode}"; }
68
69  if($fmode != $UMASK) {
70    printf("UMASK corrected from $UMASK to $fmode ...\n"); }
71
72  $fmode= oct($fmode);
73
74  umask($fmode);
75}
76
77
78main();
79
80####
81#### main sub routine
82####
83
84sub main
85{
86  my $flag_exit= 0;
87
88  if (!defined(my_which(my_print_defaults)))
89  {
90    # We can't throw out yet, since --version, --help, or --example may
91    # have been given
92    print "WARNING: my_print_defaults command not found.\n";
93    print "Please make sure you have this command available and\n";
94    print "in your path. The command is available from the latest\n";
95    print "MySQL distribution.\n";
96    $my_print_defaults_exists= 0;
97  }
98
99  # Remove leading defaults options from @ARGV
100  while (@ARGV > 0)
101  {
102    last unless $ARGV[0] =~
103      /^--(?:no-defaults$|(?:defaults-file|defaults-extra-file)=)/;
104    push @defaults_options, (shift @ARGV);
105  }
106
107  foreach (@defaults_options)
108  {
109    $_ = quote_shell_word($_);
110  }
111
112  # Add [mysqld_multi] options to front of @ARGV, ready for GetOptions()
113  unshift @ARGV, defaults_for_group('mysqld_multi');
114
115  # We've already handled --no-defaults, --defaults-file, etc.
116  if (!GetOptions("help", "example", "version", "mysqld=s", "mysqladmin=s",
117                  "user=s", "password=s", "log=s", "no-log",
118                  "tcp-ip",  "silent", "verbose"))
119  {
120    $flag_exit= 1;
121  }
122  usage() if ($opt_help);
123
124  if ($opt_verbose && $opt_silent)
125  {
126    print "Both --verbose and --silent have been given. Some of the warnings ";
127    print "will be disabled\nand some will be enabled.\n\n";
128  }
129
130  init_log() if (!defined($opt_log));
131  $groupids = $ARGV[1];
132  if ($opt_version)
133  {
134    print "$my_progname version $VER by Jani Tolonen\n";
135    exit(0);
136  }
137  example() if ($opt_example);
138  if ($flag_exit)
139  {
140    print "Error with an option, see $my_progname --help for more info.\n";
141    exit(1);
142  }
143  if (!defined(my_which(my_print_defaults)))
144  {
145    print "ABORT: Can't find command 'my_print_defaults'.\n";
146    print "This command is available from the latest MySQL\n";
147    print "distribution. Please make sure you have the command\n";
148    print "in your PATH.\n";
149    exit(1);
150  }
151  usage() if (!defined($ARGV[0]) ||
152	      (!($ARGV[0] =~ m/^start$/i) &&
153	       !($ARGV[0] =~ m/^stop$/i) &&
154	       !($ARGV[0] =~ m/^reload$/i) &&
155	       !($ARGV[0] =~ m/^report$/i)));
156
157  if (!$opt_no_log)
158  {
159    w2log("$my_progname log file version $VER; run: ",
160	  "$opt_log", 1, 0);
161  }
162  else
163  {
164    print "$my_progname log file version $VER; run: ";
165    print strftime "%a %b %e %H:%M:%S %Y", localtime;
166    print "\n";
167  }
168  if (($ARGV[0] =~ m/^start$/i) || ($ARGV[0] =~ m/^reload$/i))
169  {
170    if (!defined(($mysqld= my_which($opt_mysqld))) && $opt_verbose)
171    {
172      print "WARNING: Couldn't find the default mysqld binary.\n";
173      print "Tried: $opt_mysqld\n";
174      print "This is OK, if you are using option \"mysqld=...\" in ";
175      print "groups [mysqldN] separately for each.\n\n";
176    }
177    if ($ARGV[0] =~ m/^start$/i) {
178      start_mysqlds();
179    } elsif ($ARGV[0] =~ m/^reload$/i) {
180      reload_mysqlds();
181    }
182  }
183  else
184  {
185    if (!defined(($mysqladmin= my_which($opt_mysqladmin))) && $opt_verbose)
186    {
187      print "WARNING: Couldn't find the default mysqladmin binary.\n";
188      print "Tried: $opt_mysqladmin\n";
189      print "This is OK, if you are using option \"mysqladmin=...\" in ";
190      print "groups [mysqldN] separately for each.\n\n";
191    }
192    if ($ARGV[0] =~ m/^report$/i)
193    {
194      report_mysqlds();
195    }
196    else
197    {
198      stop_mysqlds();
199    }
200  }
201}
202
203#
204# Quote word for shell
205#
206
207sub quote_shell_word
208{
209  my ($option)= @_;
210
211  $option =~ s!([^\w=./-])!\\$1!g;
212  return $option;
213}
214
215sub defaults_for_group
216{
217  my ($group) = @_;
218
219  return () unless $my_print_defaults_exists;
220
221  my $com= join ' ', 'my_print_defaults', @defaults_options, $group;
222  my @defaults = `$com`;
223  chomp @defaults;
224  return @defaults;
225}
226
227####
228#### Init log file. Check for appropriate place for log file, in the following
229#### order:  my_print_defaults mysqld datadir, @datadir@
230####
231
232sub init_log
233{
234  foreach my $opt (defaults_for_group('mysqld'))
235  {
236    if ($opt =~ m/^--datadir=(.*)/ && -d "$1" && -w "$1")
237    {
238      $logdir= $1;
239    }
240  }
241  if (!defined($logdir))
242  {
243    $logdir= "@datadir@" if (-d "@datadir@" && -w "@datadir@");
244  }
245  if (!defined($logdir))
246  {
247    # Log file was not specified and we could not log to a standard place,
248    # so log file be disabled for now.
249    if (!$opt_silent)
250    {
251      print "WARNING: Log file disabled. Maybe directory or file isn't writable?\n";
252    }
253    $opt_no_log= 1;
254  }
255  else
256  {
257    $opt_log= "$logdir/mysqld_multi.log";
258  }
259}
260
261####
262#### Report living and not running MySQL servers
263####
264
265sub report_mysqlds
266{
267  my (@groups, $com, $i, @options, $pec);
268
269  print "Reporting MySQL servers\n";
270  if (!$opt_no_log)
271  {
272    w2log("\nReporting MySQL servers","$opt_log",0,0);
273  }
274  @groups = &find_groups($groupids);
275  for ($i = 0; defined($groups[$i]); $i++)
276  {
277    $com= get_mysqladmin_options($i, @groups);
278    $com.= " ping >> /dev/null 2>&1";
279    system($com);
280    $pec = $? >> 8;
281    if ($pec)
282    {
283      print "MySQL server from group: $groups[$i] is not running\n";
284      if (!$opt_no_log)
285      {
286	w2log("MySQL server from group: $groups[$i] is not running",
287	      "$opt_log", 0, 0);
288      }
289    }
290    else
291    {
292      print "MySQL server from group: $groups[$i] is running\n";
293      if (!$opt_no_log)
294      {
295	w2log("MySQL server from group: $groups[$i] is running",
296	      "$opt_log", 0, 0);
297      }
298    }
299  }
300  if (!$i)
301  {
302    print "No groups to be reported (check your GNRs)\n";
303    if (!$opt_no_log)
304    {
305      w2log("No groups to be reported (check your GNRs)", "$opt_log", 0, 0);
306    }
307  }
308}
309
310####
311#### start multiple servers
312####
313
314sub start_mysqlds()
315{
316  my (@groups, $com, $tmp, $i, @options, $j, $mysqld_found, $info_sent);
317
318  if (!$opt_no_log)
319  {
320    w2log("\nStarting MySQL servers\n","$opt_log",0,0);
321  }
322  else
323  {
324    print "\nStarting MySQL servers\n";
325  }
326  @groups = &find_groups($groupids);
327  for ($i = 0; defined($groups[$i]); $i++)
328  {
329    @options = defaults_for_group($groups[$i]);
330
331    $basedir_found= 0; # The default
332    $mysqld_found= 1; # The default
333    $mysqld_found= 0 if (!length($mysqld));
334
335    # Initialize the command.
336    $com= "$mysqld";
337
338    # Initialize the command option list, process the options,
339    # reset command if relevant, append key options to command line.
340    for ($j = 0, $tmp= ""; defined($options[$j]); $j++)
341    {
342      if ("--datadir=" eq substr($options[$j], 0, 10)) {
343        $datadir = $options[$j];
344        $datadir =~ s/\-\-datadir\=//;
345        eval { mkpath($datadir) };
346        if ($@) {
347          print "FATAL ERROR: Cannot create data directory $datadir: $!\n";
348          exit(1);
349        }
350        # Quote and append 'datadir' to command line.
351        $options[$j]= quote_shell_word($options[$j]);
352        $tmp.= " $options[$j]";
353      }
354      elsif ("--mysqladmin=" eq substr($options[$j], 0, 13))
355      {
356	# catch this and ignore
357      }
358      elsif ("--mysqld=" eq substr($options[$j], 0, 9))
359      {
360	$options[$j]=~ s/\-\-mysqld\=//;
361        # Reset command.
362	$com= $options[$j];
363        $mysqld_found= 1;
364      }
365      elsif ("--basedir=" eq substr($options[$j], 0, 10))
366      {
367        $basedir= $options[$j];
368        $basedir =~ s/^--basedir=//;
369        $basedir_found= 1;
370        # Quote and append 'basedir' to command line.
371        $options[$j]= quote_shell_word($options[$j]);
372        $tmp.= " $options[$j]";
373      }
374      else
375      {
376        # Quote and append additional options to command line.
377	$options[$j]= quote_shell_word($options[$j]);
378	$tmp.= " $options[$j]";
379      }
380    }
381    if ($opt_verbose && $com =~ m/\/(safe_mysqld|mysqld_safe)$/ && !$info_sent)
382    {
383      print "WARNING: $1 is being used to start mysqld. In this case you ";
384      print "may need to pass\n\"ledir=...\" under groups [mysqldN] to ";
385      print "$1 in order to find the actual mysqld binary.\n";
386      print "ledir (library executable directory) should be the path to the ";
387      print "wanted mysqld binary.\n\n";
388      $info_sent= 1;
389    }
390    # Prepare command line by appending command and option list, and redirect output.
391    $com.= $tmp;
392    $com.= " >> $opt_log 2>&1" if (!$opt_no_log);
393    if (!$mysqld_found)
394    {
395      print "\n";
396      print "FATAL ERROR: Tried to start mysqld under group [$groups[$i]], ";
397      print "but no mysqld binary was found.\n";
398      print "Please add \"mysqld=...\" in group [mysqld_multi], or add it to ";
399      print "group [$groups[$i]] separately.\n";
400      exit(1);
401    }
402    if ($basedir_found)
403    {
404      $curdir=getcwd();
405      chdir($basedir) or die "Can't change to datadir $basedir";
406    }
407    # Prepare datadir by initializing the server, unless this is already done.
408    if (! -d $datadir."/mysql") {
409      if (-w $datadir) {
410        print "\n\nInstalling new database in $datadir\n\n";
411        system($com." --initialize");
412      } else {
413        print "\n";
414        print "FATAL ERROR: Tried to create mysqld under group [$groups[$i]],\n";
415        print "but the data directory is not writable.\n";
416        print "data directory used: $datadir\n";
417        exit(1);
418      }
419    }
420    if (! -d $datadir."/mysql") {
421      print "\n";
422      print "FATAL ERROR: Tried to start mysqld under group [$groups[$i]],\n";
423      print "but no data directory was found or could be created.\n";
424      print "data directory used: $datadir\n";
425      exit(1);
426    }
427    # Start the command in the background.
428    system($com." &");
429    if ($basedir_found)
430    {
431      chdir($curdir) or die "Can't change back to original dir $curdir";
432    }
433  }
434  if (!$i && !$opt_no_log)
435  {
436    w2log("No MySQL servers to be started (check your GNRs)",
437	  "$opt_log", 0, 0);
438  }
439}
440
441####
442#### reload multiple servers
443####
444
445sub reload_mysqlds()
446{
447  my (@groups, $com, $tmp, $i, @options, $j);
448
449  if (!$opt_no_log)
450  {
451    w2log("\nReloading MySQL servers\n","$opt_log",0,0);
452  }
453  else
454  {
455    print "\nReloading MySQL servers\n";
456  }
457  @groups = &find_groups($groupids);
458  for ($i = 0; defined($groups[$i]); $i++)
459  {
460    $mysqld_server = $mysqld;
461    @options = defaults_for_group($groups[$i]);
462
463    for ($j = 0, $tmp= ""; defined($options[$j]); $j++)
464    {
465      if ("--mysqladmin=" eq substr($options[$j], 0, 13))
466      {
467        # catch this and ignore
468      }
469      elsif ("--mysqld=" eq substr($options[$j], 0, 9))
470      {
471        $options[$j] =~ s/\-\-mysqld\=//;
472        $mysqld_server = $options[$j];
473      }
474      elsif ("--pid-file=" eq substr($options[$j], 0, 11))
475      {
476        $options[$j] =~ s/\-\-pid-file\=//;
477        $pid_file = $options[$j];
478      }
479    }
480    $com = "killproc -p $pid_file -HUP $mysqld_server";
481    system($com);
482
483    $com = "touch $pid_file";
484    system($com);
485  }
486  if (!$i && !$opt_no_log)
487  {
488    w2log("No MySQL servers to be reloaded (check your GNRs)",
489         "$opt_log", 0, 0);
490  }
491}
492
493###
494#### stop multiple servers
495####
496
497sub stop_mysqlds()
498{
499  my (@groups, $com, $i, @options);
500
501  if (!$opt_no_log)
502  {
503    w2log("\nStopping MySQL servers\n","$opt_log",0,0);
504  }
505  else
506  {
507    print "\nStopping MySQL servers\n";
508  }
509  @groups = &find_groups($groupids);
510  for ($i = 0; defined($groups[$i]); $i++)
511  {
512    $com= get_mysqladmin_options($i, @groups);
513    $com.= " shutdown";
514    $com.= " >> $opt_log 2>&1" if (!$opt_no_log);
515    $com.= " &";
516    system($com);
517  }
518  if (!$i && !$opt_no_log)
519  {
520    w2log("No MySQL servers to be stopped (check your GNRs)",
521	  "$opt_log", 0, 0);
522  }
523}
524
525####
526#### Sub function for mysqladmin option parsing
527####
528
529sub get_mysqladmin_options
530{
531  my ($i, @groups)= @_;
532  my ($mysqladmin_found, $com, $tmp, $j);
533
534  @options = defaults_for_group($groups[$i]);
535
536  $mysqladmin_found= 1; # The default
537  $mysqladmin_found= 0 if (!length($mysqladmin));
538  $com = "$mysqladmin";
539  $tmp = " -u $opt_user";
540  if (defined($opt_password)) {
541    my $pw= $opt_password;
542    # Protect single quotes in password
543    $pw =~ s/'/'"'"'/g;
544    $tmp.= " -p'$pw'";
545  }
546  $tmp.= $opt_tcp_ip ? " -h 127.0.0.1" : "";
547  for ($j = 0; defined($options[$j]); $j++)
548  {
549    if ("--mysqladmin=" eq substr($options[$j], 0, 13))
550    {
551      $options[$j]=~ s/\-\-mysqladmin\=//;
552      $com= $options[$j];
553      $mysqladmin_found= 1;
554    }
555    elsif ((($options[$j] =~ m/^(\-\-socket\=)(.*)$/) && !$opt_tcp_ip) ||
556	   ($options[$j] =~ m/^(\-\-port\=)(.*)$/))
557    {
558      $tmp.= " $options[$j]";
559    }
560  }
561  if (!$mysqladmin_found)
562  {
563    print "\n";
564    print "FATAL ERROR: Tried to use mysqladmin in group [$groups[$i]], ";
565    print "but no mysqladmin binary was found.\n";
566    print "Please add \"mysqladmin=...\" in group [mysqld_multi], or ";
567    print "in group [$groups[$i]].\n";
568    exit(1);
569  }
570  $com.= $tmp;
571  return $com;
572}
573
574# Return a list of option files which can be opened.  Similar, but not
575# identical, to behavior of my_search_option_files()
576sub list_defaults_files
577{
578  my %opt;
579  foreach (@defaults_options)
580  {
581    return () if /^--no-defaults$/;
582    $opt{$1} = $2 if /^--defaults-(extra-file|file)=(.*)$/;
583  }
584
585  return ($opt{file}) if exists $opt{file};
586
587  my %seen;  # Don't list the same file more than once
588  return grep { defined $_ and not $seen{$_}++ and -f $_ and -r $_ }
589              ('/etc/my.cnf',
590               '/etc/mysql/my.cnf',
591               '@sysconfdir@/my.cnf',
592               ($ENV{MYSQL_HOME} ? "$ENV{MYSQL_HOME}/my.cnf" : undef),
593               $opt{'extra-file'},
594               ($ENV{HOME} ? "$ENV{HOME}/.my.cnf" : undef));
595}
596
597
598# Takes a specification of GNRs (see --help), and returns a list of matching
599# groups which actually are mentioned in a relevant config file
600sub find_groups
601{
602  my ($raw_gids) = @_;
603
604  my %gids;
605  my @groups;
606
607  if (defined($raw_gids))
608  {
609    # Make a hash of the wanted group ids
610    foreach my $raw_gid (split ',', $raw_gids)
611    {
612      # Match 123 or 123-456
613      my ($start, $end) = ($raw_gid =~ /^\s*(\d+)(?:\s*-\s*(\d+))?\s*$/);
614      $end = $start if not defined $end;
615      if (not defined $start or $end < $start or $start < 0)
616      {
617        print "ABORT: Bad GNR: $raw_gid; see $my_progname --help\n";
618        exit(1);
619      }
620
621      foreach my $i ($start .. $end)
622      {
623        # Use $i + 0 to normalize numbers (002 + 0 -> 2)
624        $gids{$i + 0}= 1;
625      }
626    }
627  }
628
629  my @defaults_files = list_defaults_files();
630  #warn "@{[sort keys %gids]} -> @defaults_files\n";
631  foreach my $file (@defaults_files)
632  {
633    next unless open CONF, "< $file";
634
635    while (<CONF>)
636    {
637      if (/^\s*\[\s*(mysqld)(\d+)\s*\]\s*$/)
638      {
639        #warn "Found a group: $1$2\n";
640        # Use $2 + 0 to normalize numbers (002 + 0 -> 2)
641        if (not defined($raw_gids) or $gids{$2 + 0})
642        {
643          push @groups, "$1$2";
644        }
645      }
646    }
647
648    close CONF;
649  }
650  return @groups;
651}
652
653####
654#### w2log: Write to a logfile.
655#### 1.arg: append to the log file (given string, or from a file. if a file,
656####        file will be read from $opt_logdir)
657#### 2.arg: logfile -name (w2log assumes that the logfile is in $opt_logdir).
658#### 3.arg. 0 | 1, if true, print current date to the logfile. 3. arg will
659####        be ignored, if 1. arg is a file.
660#### 4.arg. 0 | 1, if true, first argument is a file, else a string
661####
662
663sub w2log
664{
665  my ($msg, $file, $date_flag, $is_file)= @_;
666  my (@data);
667
668  open (LOGFILE, ">>$opt_log")
669    or die "FATAL: w2log: Couldn't open log file: $opt_log\n";
670
671  if ($is_file)
672  {
673    open (FROMFILE, "<$msg") && (@data=<FROMFILE>) &&
674      close(FROMFILE)
675	or die "FATAL: w2log: Couldn't open file: $msg\n";
676    foreach my $line (@data)
677    {
678      print LOGFILE "$line";
679    }
680  }
681  else
682  {
683    print LOGFILE "$msg";
684    print LOGFILE strftime "%a %b %e %H:%M:%S %Y", localtime if ($date_flag);
685    print LOGFILE "\n";
686  }
687  close (LOGFILE);
688  return;
689}
690
691####
692#### my_which is used, because we can't assume that every system has the
693#### which -command. my_which can take only one argument at a time.
694#### Return values: requested system command with the first found path,
695#### or undefined, if not found.
696####
697
698sub my_which
699{
700  my ($command) = @_;
701  my (@paths, $path);
702
703 # If the argument is not 'my_print_defaults' then it would be of the format
704 # <absolute_path>/<program>
705 return $command if ($command ne 'my_print_defaults' && -f $command &&
706                     -x $command);
707
708  @paths = split(':', $ENV{'PATH'});
709  foreach $path (@paths)
710  {
711    $path .= "/$command";
712    return $path if (-f $path && -x $path);
713  }
714  return undef();
715}
716
717
718####
719#### example
720####
721
722sub example
723{
724  print <<EOF;
725# This is an example of a my.cnf file for $my_progname.
726# Usually this file is located in home dir ~/.my.cnf or /etc/my.cnf
727#
728# SOME IMPORTANT NOTES FOLLOW:
729#
730# 1.COMMON USER
731#
732#   Make sure that the MySQL user, who is stopping the mysqld services, has
733#   the same password to all MySQL servers being accessed by $my_progname.
734#   This user needs to have the 'Shutdown_priv' -privilege, but for security
735#   reasons should have no other privileges. It is advised that you create a
736#   common 'multi_admin' user for all MySQL servers being controlled by
737#   $my_progname. Here is an example how to do it:
738#
739#   GRANT SHUTDOWN ON *.* TO multi_admin\@localhost IDENTIFIED BY 'password'
740#
741#   You will need to apply the above to all MySQL servers that are being
742#   controlled by $my_progname. 'multi_admin' will shutdown the servers
743#   using 'mysqladmin' -binary, when '$my_progname stop' is being called.
744#
745# 2.PID-FILE
746#
747#   If you are using mysqld_safe to start mysqld, make sure that every
748#   MySQL server has a separate pid-file. In order to use mysqld_safe
749#   via $my_progname, you need to use two options:
750#
751#   mysqld=/path/to/mysqld_safe
752#   ledir=/path/to/mysqld-binary/
753#
754#   ledir (library executable directory), is an option that only mysqld_safe
755#   accepts, so you will get an error if you try to pass it to mysqld directly.
756#   For this reason you might want to use the above options within [mysqld#]
757#   group directly.
758#
759# 3.DATA DIRECTORY
760#
761#   It is NOT advised to run many MySQL servers within the same data directory.
762#   You can do so, but please make sure to understand and deal with the
763#   underlying caveats. In short they are:
764#   - Speed penalty
765#   - Risk of table/data corruption
766#   - Data synchronising problems between the running servers
767#   - Heavily media (disk) bound
768#   - Relies on the system (external) file locking
769#   - Is not applicable with all table types. (Such as InnoDB)
770#     Trying so will end up with undesirable results.
771#
772# 4.TCP/IP Port
773#
774#   Every server requires one and it must be unique.
775#
776# 5.[mysqld#] Groups
777#
778#   In the example below the first and the fifth mysqld group was
779#   intentionally left out. You may have 'gaps' in the config file. This
780#   gives you more flexibility.
781#
782# 6.MySQL Server User
783#
784#   You can pass the user=... option inside [mysqld#] groups. This
785#   can be very handy in some cases, but then you need to run $my_progname
786#   as UNIX root.
787#
788# 7.A Start-up Manage Script for $my_progname
789#
790#   In the recent MySQL distributions you can find a file called
791#   mysqld_multi.server.sh. It is a wrapper for $my_progname. This can
792#   be used to start and stop multiple servers during boot and shutdown.
793#
794#   You can place the file in /etc/init.d/mysqld_multi.server.sh and
795#   make the needed symbolic links to it from various run levels
796#   (as per Linux/Unix standard). You may even replace the
797#   /etc/init.d/mysql.server script with it.
798#
799#   Before using, you must create a my.cnf file either in @sysconfdir@/my.cnf
800#   or /root/.my.cnf and add the [mysqld_multi] and [mysqld#] groups.
801#
802#   The script can be found from support-files/mysqld_multi.server.sh
803#   in MySQL distribution. (Verify the script before using)
804#
805
806[mysqld_multi]
807mysqld     = @bindir@/mysqld_safe
808mysqladmin = @bindir@/mysqladmin
809user       = multi_admin
810password   = my_password
811
812[mysqld2]
813socket     = /tmp/mysql.sock2
814port       = 3307
815pid-file   = @localstatedir@2/hostname.pid2
816datadir    = @localstatedir@2
817language   = @datadir@/mysql/english
818user       = unix_user1
819
820[mysqld3]
821mysqld     = /path/to/mysqld_safe
822ledir      = /path/to/mysqld-binary/
823mysqladmin = /path/to/mysqladmin
824socket     = /tmp/mysql.sock3
825port       = 3308
826pid-file   = @localstatedir@3/hostname.pid3
827datadir    = @localstatedir@3
828language   = @datadir@/mysql/swedish
829user       = unix_user2
830
831[mysqld4]
832socket     = /tmp/mysql.sock4
833port       = 3309
834pid-file   = @localstatedir@4/hostname.pid4
835datadir    = @localstatedir@4
836language   = @datadir@/mysql/estonia
837user       = unix_user3
838 
839[mysqld6]
840socket     = /tmp/mysql.sock6
841port       = 3311
842pid-file   = @localstatedir@6/hostname.pid6
843datadir    = @localstatedir@6
844language   = @datadir@/mysql/japanese
845user       = unix_user4
846EOF
847  exit(0);
848}
849
850####
851#### usage
852####
853
854sub usage
855{
856  print <<EOF;
857$my_progname version $VER by Jani Tolonen
858
859Description:
860$my_progname can be used to start, reload, or stop any number of separate
861mysqld processes running in different TCP/IP ports and UNIX sockets.
862
863$my_progname can read group [mysqld_multi] from my.cnf file. You may
864want to put options mysqld=... and mysqladmin=... there.  Since
865version 2.10 these options can also be given under groups [mysqld#],
866which gives more control over different versions.  One can have the
867default mysqld and mysqladmin under group [mysqld_multi], but this is
868not mandatory. Please note that if mysqld or mysqladmin is missing
869from both [mysqld_multi] and [mysqld#], a group that is tried to be
870used, $my_progname will abort with an error.
871
872$my_progname will search for groups named [mysqld#] from my.cnf (or
873the given --defaults-extra-file=...), where '#' can be any positive 
874integer starting from 1. These groups should be the same as the regular
875[mysqld] group, but with those port, socket and any other options
876that are to be used with each separate mysqld process. The number
877in the group name has another function; it can be used for starting,
878reloading, stopping, or reporting any specific mysqld server.
879
880Usage: $my_progname [OPTIONS] {start|reload|stop|report} [GNR,GNR,GNR...]
881or     $my_progname [OPTIONS] {start|reload|stop|report} [GNR-GNR,GNR,GNR-GNR,...]
882
883The GNR means the group number. You can start, reload, stop or report any GNR,
884or several of them at the same time. (See --example) The GNRs list can
885be comma separated or a dash combined. The latter means that all the
886GNRs between GNR1-GNR2 will be affected. Without GNR argument all the
887groups found will either be started, reloaded, stopped, or reported. Note that
888syntax for specifying GNRs must appear without spaces.
889
890Options:
891
892These options must be given before any others:
893--no-defaults      Do not read any defaults file
894--defaults-file=...  Read only this configuration file, do not read the
895                   standard system-wide and user-specific files
896--defaults-extra-file=...  Read this configuration file in addition to the
897                   standard system-wide and user-specific files
898Using:  @{[join ' ', @defaults_options]}
899
900--example          Give an example of a config file with extra information.
901--help             Print this help and exit.
902--log=...          Log file. Full path to and the name for the log file. NOTE:
903                   If the file exists, everything will be appended.
904                   Using: $opt_log
905--mysqladmin=...   mysqladmin binary to be used for a server shutdown.
906                   Since version 2.10 this can be given within groups [mysqld#]
907                   Using: $mysqladmin
908--mysqld=...       mysqld binary to be used. Note that you can give mysqld_safe
909                   to this option also. The options are passed to mysqld. Just
910                   make sure you have mysqld in your PATH or fix mysqld_safe.
911                   Using: $mysqld
912                   Please note: Since mysqld_multi version 2.3 you can also
913                   give this option inside groups [mysqld#] in ~/.my.cnf,
914                   where '#' stands for an integer (number) of the group in
915                   question. This will be recognised as a special option and
916                   will not be passed to the mysqld. This will allow one to
917                   start different mysqld versions with mysqld_multi.
918--no-log           Print to stdout instead of the log file. By default the log
919                   file is turned on.
920--password=...     Password for mysqladmin user.
921--silent           Disable warnings.
922--tcp-ip           Connect to the MySQL server(s) via the TCP/IP port instead
923                   of the UNIX socket. This affects stopping and reporting.
924                   If a socket file is missing, the server may still be
925                   running, but can be accessed only via the TCP/IP port.
926                   By default connecting is done via the UNIX socket.
927--user=...         mysqladmin user. Using: $opt_user
928--verbose          Be more verbose.
929--version          Print the version number and exit.
930EOF
931  exit(0);
932}
933