1# -*- cperl -*-
2# Copyright (c) 2006 MySQL AB, 2008 Sun Microsystems, Inc.
3# Use is subject to license terms.
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
25# This is a library file used by the Perl version of mysql-test-run,
26# and is part of the translation of the Bourne shell script with the
27# same name.
28
29use strict;
30
31# Private IM-related operations.
32
33sub mtr_im_kill_process ($$$$);
34sub mtr_im_load_pids ($);
35sub mtr_im_terminate ($);
36sub mtr_im_check_alive ($);
37sub mtr_im_check_main_alive ($);
38sub mtr_im_check_angel_alive ($);
39sub mtr_im_check_mysqlds_alive ($);
40sub mtr_im_check_mysqld_alive ($);
41sub mtr_im_cleanup ($);
42sub mtr_im_rm_file ($);
43sub mtr_im_errlog ($);
44sub mtr_im_kill ($);
45sub mtr_im_wait_for_connection ($$$);
46sub mtr_im_wait_for_mysqld($$$);
47
48# Public IM-related operations.
49
50sub mtr_im_start ($$);
51sub mtr_im_stop ($);
52
53##############################################################################
54#
55#  Private operations.
56#
57##############################################################################
58
59sub mtr_im_kill_process ($$$$) {
60  my $pid_lst= shift;
61  my $signal= shift;
62  my $total_retries= shift;
63  my $timeout= shift;
64
65  my %pids;
66
67  foreach my $pid ( @{$pid_lst} )
68  {
69    $pids{$pid}= 1;
70  }
71
72  for ( my $cur_attempt= 1; $cur_attempt <= $total_retries; ++$cur_attempt )
73  {
74    foreach my $pid ( keys %pids )
75    {
76      mtr_debug("Sending $signal to $pid...");
77
78      kill($signal, $pid);
79
80      unless ( kill (0, $pid) )
81      {
82        mtr_debug("Process $pid died.");
83        delete $pids{$pid};
84      }
85    }
86
87    return if scalar keys %pids == 0;
88
89    mtr_debug("Sleeping $timeout second(s) waiting for processes to die...");
90
91    sleep($timeout);
92  }
93
94  mtr_debug("Process(es) " .
95            join(' ', keys %pids) .
96            " is still alive after $total_retries " .
97            "of sending signal $signal.");
98}
99
100###########################################################################
101
102sub mtr_im_load_pids($) {
103  my $im= shift;
104
105  mtr_debug("Loading PID files...");
106
107  # Obtain mysqld-process pids.
108
109  my $instances = $im->{'instances'};
110
111  for ( my $idx= 0; $idx < 2; ++$idx )
112  {
113    mtr_debug("IM-guarded mysqld[$idx] PID file: '" .
114              $instances->[$idx]->{'path_pid'} . "'.");
115
116    my $mysqld_pid;
117
118    if ( -r $instances->[$idx]->{'path_pid'} )
119    {
120      $mysqld_pid= mtr_get_pid_from_file($instances->[$idx]->{'path_pid'});
121      mtr_debug("IM-guarded mysqld[$idx] PID: $mysqld_pid.");
122    }
123    else
124    {
125      $mysqld_pid= undef;
126      mtr_debug("IM-guarded mysqld[$idx]: no PID file.");
127    }
128
129    $instances->[$idx]->{'pid'}= $mysqld_pid;
130  }
131
132  # Re-read Instance Manager PIDs from the file, since during tests Instance
133  # Manager could have been restarted, so its PIDs could have been changed.
134
135  #   - IM-main
136
137  mtr_debug("IM-main PID file: '$im->{path_pid}'.");
138
139  if ( -f $im->{'path_pid'} )
140  {
141    $im->{'pid'} =
142      mtr_get_pid_from_file($im->{'path_pid'});
143
144    mtr_debug("IM-main PID: $im->{pid}.");
145  }
146  else
147  {
148    mtr_debug("IM-main: no PID file.");
149    $im->{'pid'}= undef;
150  }
151
152  #   - IM-angel
153
154  mtr_debug("IM-angel PID file: '$im->{path_angel_pid}'.");
155
156  if ( -f $im->{'path_angel_pid'} )
157  {
158    $im->{'angel_pid'} =
159      mtr_get_pid_from_file($im->{'path_angel_pid'});
160
161    mtr_debug("IM-angel PID: $im->{'angel_pid'}.");
162  }
163  else
164  {
165    mtr_debug("IM-angel: no PID file.");
166    $im->{'angel_pid'} = undef;
167  }
168}
169
170###########################################################################
171
172sub mtr_im_terminate($) {
173  my $im= shift;
174
175  # Load pids from pid-files. We should do it first of all, because IM deletes
176  # them on shutdown.
177
178  mtr_im_load_pids($im);
179
180  mtr_debug("Shutting Instance Manager down...");
181
182  # Ignoring SIGCHLD so that all children could rest in peace.
183
184  start_reap_all();
185
186  # Send SIGTERM to IM-main.
187
188  if ( defined $im->{'pid'} )
189  {
190    mtr_debug("IM-main pid: $im->{pid}.");
191    mtr_debug("Stopping IM-main...");
192
193    mtr_im_kill_process([ $im->{'pid'} ], 'TERM', 10, 1);
194  }
195  else
196  {
197    mtr_debug("IM-main pid: n/a.");
198  }
199
200  # If IM-angel was alive, wait for it to die.
201
202  if ( defined $im->{'angel_pid'} )
203  {
204    mtr_debug("IM-angel pid: $im->{'angel_pid'}.");
205    mtr_debug("Waiting for IM-angel to die...");
206
207    my $total_attempts= 10;
208
209    for ( my $cur_attempt=1; $cur_attempt <= $total_attempts; ++$cur_attempt )
210    {
211      unless ( kill (0, $im->{'angel_pid'}) )
212      {
213        mtr_debug("IM-angel died.");
214        last;
215      }
216
217      sleep(1);
218    }
219  }
220  else
221  {
222    mtr_debug("IM-angel pid: n/a.");
223  }
224
225  stop_reap_all();
226
227  # Re-load PIDs.
228
229  mtr_im_load_pids($im);
230}
231
232###########################################################################
233
234sub mtr_im_check_alive($) {
235  my $im= shift;
236
237  mtr_debug("Checking whether IM-components are alive...");
238
239  return 1 if mtr_im_check_main_alive($im);
240
241  return 1 if mtr_im_check_angel_alive($im);
242
243  return 1 if mtr_im_check_mysqlds_alive($im);
244
245  return 0;
246}
247
248###########################################################################
249
250sub mtr_im_check_main_alive($) {
251  my $im= shift;
252
253  # Check that the process, that we know to be IM's, is dead.
254
255  if ( defined $im->{'pid'} )
256  {
257    if ( kill (0, $im->{'pid'}) )
258    {
259      mtr_debug("IM-main (PID: $im->{pid}) is alive.");
260      return 1;
261    }
262    else
263    {
264      mtr_debug("IM-main (PID: $im->{pid}) is dead.");
265    }
266  }
267  else
268  {
269    mtr_debug("No PID file for IM-main.");
270  }
271
272  # Check that IM does not accept client connections.
273
274  if ( mtr_ping_port($im->{'port'}) )
275  {
276    mtr_debug("IM-main (port: $im->{port}) " .
277              "is accepting connections.");
278
279    mtr_im_errlog("IM-main is accepting connections on port " .
280                  "$im->{port}, but there is no " .
281                  "process information.");
282    return 1;
283  }
284  else
285  {
286    mtr_debug("IM-main (port: $im->{port}) " .
287              "does not accept connections.");
288    return 0;
289  }
290}
291
292###########################################################################
293
294sub mtr_im_check_angel_alive($) {
295  my $im= shift;
296
297  # Check that the process, that we know to be the Angel, is dead.
298
299  if ( defined $im->{'angel_pid'} )
300  {
301    if ( kill (0, $im->{'angel_pid'}) )
302    {
303      mtr_debug("IM-angel (PID: $im->{angel_pid}) is alive.");
304      return 1;
305    }
306    else
307    {
308      mtr_debug("IM-angel (PID: $im->{angel_pid}) is dead.");
309      return 0;
310    }
311  }
312  else
313  {
314    mtr_debug("No PID file for IM-angel.");
315    return 0;
316  }
317}
318
319###########################################################################
320
321sub mtr_im_check_mysqlds_alive($) {
322  my $im= shift;
323
324  mtr_debug("Checking for IM-guarded mysqld instances...");
325
326  my $instances = $im->{'instances'};
327
328  for ( my $idx= 0; $idx < 2; ++$idx )
329  {
330    mtr_debug("Checking mysqld[$idx]...");
331
332    return 1
333      if mtr_im_check_mysqld_alive($instances->[$idx]);
334  }
335}
336
337###########################################################################
338
339sub mtr_im_check_mysqld_alive($) {
340  my $mysqld_instance= shift;
341
342  # Check that the process is dead.
343
344  if ( defined $mysqld_instance->{'pid'} )
345  {
346    if ( kill (0, $mysqld_instance->{'pid'}) )
347    {
348      mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is alive.");
349      return 1;
350    }
351    else
352    {
353      mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is dead.");
354    }
355  }
356  else
357  {
358    mtr_debug("No PID file for mysqld instance.");
359  }
360
361  # Check that mysqld does not accept client connections.
362
363  if ( mtr_ping_port($mysqld_instance->{'port'}) )
364  {
365    mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " .
366              "is accepting connections.");
367
368    mtr_im_errlog("Mysqld is accepting connections on port " .
369                  "$mysqld_instance->{port}, but there is no " .
370                  "process information.");
371    return 1;
372  }
373  else
374  {
375    mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " .
376              "does not accept connections.");
377    return 0;
378  }
379}
380
381###########################################################################
382
383sub mtr_im_cleanup($) {
384  my $im= shift;
385
386  mtr_im_rm_file($im->{'path_pid'});
387  mtr_im_rm_file($im->{'path_sock'});
388
389  mtr_im_rm_file($im->{'path_angel_pid'});
390
391  for ( my $idx= 0; $idx < 2; ++$idx )
392  {
393    mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_pid'});
394    mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_sock'});
395  }
396}
397
398###########################################################################
399
400sub mtr_im_rm_file($)
401{
402  my $file_path= shift;
403
404  if ( -f $file_path )
405  {
406    mtr_debug("Removing '$file_path'...");
407
408    unless ( unlink($file_path) )
409    {
410      mtr_warning("Can not remove '$file_path'.")
411    }
412  }
413  else
414  {
415    mtr_debug("File '$file_path' does not exist already.");
416  }
417}
418
419###########################################################################
420
421sub mtr_im_errlog($) {
422  my $msg= shift;
423
424  # Complain in error log so that a warning will be shown.
425  #
426  # TODO: unless BUG#20761 is fixed, we will print the warning to stdout, so
427  # that it can be seen on console and does not produce pushbuild error.
428
429  # my $errlog= "$opt_vardir/log/mysql-test-run.pl.err";
430  #
431  # open (ERRLOG, ">>$errlog") ||
432  #   mtr_error("Can not open error log ($errlog)");
433  #
434  # my $ts= localtime();
435  # print ERRLOG
436  #   "Warning: [$ts] $msg\n";
437  #
438  # close ERRLOG;
439
440  my $ts= localtime();
441  print "Warning: [$ts] $msg\n";
442}
443
444###########################################################################
445
446sub mtr_im_kill($) {
447  my $im= shift;
448
449  # Re-load PIDs. That can be useful because some processes could have been
450  # restarted.
451
452  mtr_im_load_pids($im);
453
454  # Ignoring SIGCHLD so that all children could rest in peace.
455
456  start_reap_all();
457
458  # Kill IM-angel first of all.
459
460  if ( defined $im->{'angel_pid'} )
461  {
462    mtr_debug("Killing IM-angel (PID: $im->{angel_pid})...");
463    mtr_im_kill_process([ $im->{'angel_pid'} ], 'KILL', 10, 1)
464  }
465  else
466  {
467    mtr_debug("IM-angel is dead.");
468  }
469
470  # Re-load PIDs again.
471
472  mtr_im_load_pids($im);
473
474  # Kill IM-main.
475
476  if ( defined $im->{'pid'} )
477  {
478    mtr_debug("Killing IM-main (PID: $im->pid})...");
479    mtr_im_kill_process([ $im->{'pid'} ], 'KILL', 10, 1);
480  }
481  else
482  {
483    mtr_debug("IM-main is dead.");
484  }
485
486  # Re-load PIDs again.
487
488  mtr_im_load_pids($im);
489
490  # Kill guarded mysqld instances.
491
492  my @mysqld_pids;
493
494  mtr_debug("Collecting PIDs of mysqld instances to kill...");
495
496  for ( my $idx= 0; $idx < 2; ++$idx )
497  {
498    my $pid= $im->{'instances'}->[$idx]->{'pid'};
499
500    unless ( defined $pid )
501    {
502      next;
503    }
504
505    mtr_debug("  - IM-guarded mysqld[$idx] PID: $pid.");
506
507    push (@mysqld_pids, $pid);
508  }
509
510  if ( scalar @mysqld_pids > 0 )
511  {
512    mtr_debug("Killing IM-guarded mysqld instances...");
513    mtr_im_kill_process(\@mysqld_pids, 'KILL', 10, 1);
514  }
515
516  # That's all.
517
518  stop_reap_all();
519}
520
521##############################################################################
522
523sub mtr_im_wait_for_connection($$$) {
524  my $im= shift;
525  my $total_attempts= shift;
526  my $connect_timeout= shift;
527
528  mtr_debug("Waiting for IM on port $im->{port} " .
529            "to start accepting connections...");
530
531  for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt )
532  {
533    mtr_debug("Trying to connect to IM ($cur_attempt of $total_attempts)...");
534
535    if ( mtr_ping_port($im->{'port'}) )
536    {
537      mtr_debug("IM is accepting connections " .
538                "on port $im->{port}.");
539      return 1;
540    }
541
542    mtr_debug("Sleeping $connect_timeout...");
543    sleep($connect_timeout);
544  }
545
546  mtr_debug("IM does not accept connections " .
547            "on port $im->{port} after " .
548            ($total_attempts * $connect_timeout) . " seconds.");
549
550  return 0;
551}
552
553##############################################################################
554
555sub mtr_im_wait_for_mysqld($$$) {
556  my $mysqld= shift;
557  my $total_attempts= shift;
558  my $connect_timeout= shift;
559
560  mtr_debug("Waiting for IM-guarded mysqld on port $mysqld->{port} " .
561            "to start accepting connections...");
562
563  for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt )
564  {
565    mtr_debug("Trying to connect to mysqld " .
566              "($cur_attempt of $total_attempts)...");
567
568    if ( mtr_ping_port($mysqld->{'port'}) )
569    {
570      mtr_debug("Mysqld is accepting connections " .
571                "on port $mysqld->{port}.");
572      return 1;
573    }
574
575    mtr_debug("Sleeping $connect_timeout...");
576    sleep($connect_timeout);
577  }
578
579  mtr_debug("Mysqld does not accept connections " .
580            "on port $mysqld->{port} after " .
581            ($total_attempts * $connect_timeout) . " seconds.");
582
583  return 0;
584}
585
586##############################################################################
587#
588#  Public operations.
589#
590##############################################################################
591
592sub mtr_im_start($$) {
593  my $im = shift;
594  my $opts = shift;
595
596  mtr_debug("Starting Instance Manager...");
597
598  my $args;
599  mtr_init_args(\$args);
600  mtr_add_arg($args, "--defaults-file=%s", $im->{'defaults_file'});
601
602  foreach my $opt ( @{$opts} )
603  {
604    mtr_add_arg($args, $opt);
605  }
606
607  $im->{'spawner_pid'} =
608    mtr_spawn(
609      $::exe_im,                        # path to the executable
610      $args,                            # cmd-line args
611      '',                               # stdin
612      $im->{'path_log'},                # stdout
613      $im->{'path_err'},                # stderr
614      '',                               # pid file path (not used)
615      { append_log_file => 1 }          # append log files
616      );
617
618  unless ( $im->{'spawner_pid'} )
619  {
620    mtr_error('Could not start Instance Manager.')
621  }
622
623  # Instance Manager can be run in daemon mode. In this case, it creates
624  # several processes and the parent process, created by mtr_spawn(), exits just
625  # after start. So, we have to obtain Instance Manager PID from the PID file.
626
627  mtr_debug("Waiting for IM to create PID file (" .
628            "path: '$im->{path_pid}'; " .
629            "timeout: $im->{start_timeout})...");
630
631  unless ( sleep_until_file_created($im->{'path_pid'},
632                                    $im->{'start_timeout'},
633                                    -1) ) # real PID is still unknown
634  {
635    mtr_debug("IM has not created PID file in $im->{start_timeout} secs.");
636    mtr_debug("Aborting test suite...");
637
638    mtr_kill_leftovers();
639
640    mtr_report("IM has not created PID file in $im->{start_timeout} secs.");
641    return 0;
642  }
643
644  $im->{'pid'}= mtr_get_pid_from_file($im->{'path_pid'});
645
646  mtr_debug("Instance Manager started. PID: $im->{pid}.");
647
648  # Wait until we can connect to IM.
649
650  my $IM_CONNECT_TIMEOUT= 30;
651
652  unless ( mtr_im_wait_for_connection($im,
653                                      $IM_CONNECT_TIMEOUT, 1) )
654  {
655    mtr_debug("Can not connect to Instance Manager " .
656              "in $IM_CONNECT_TIMEOUT seconds after start.");
657    mtr_debug("Aborting test suite...");
658
659    mtr_kill_leftovers();
660
661    mtr_report("Can not connect to Instance Manager " .
662               "in $IM_CONNECT_TIMEOUT seconds after start.");
663    return 0;
664  }
665
666  # Wait for IM to start guarded instances:
667  #   - wait for PID files;
668
669  mtr_debug("Waiting for guarded mysqlds instances to create PID files...");
670
671  for ( my $idx= 0; $idx < 2; ++$idx )
672  {
673    my $mysqld= $im->{'instances'}->[$idx];
674
675    if ( exists $mysqld->{'nonguarded'} )
676    {
677      next;
678    }
679
680    mtr_debug("Waiting for mysqld[$idx] to create PID file (" .
681              "path: '$mysqld->{path_pid}'; " .
682              "timeout: $mysqld->{start_timeout})...");
683
684    unless ( sleep_until_file_created($mysqld->{'path_pid'},
685                                      $mysqld->{'start_timeout'},
686                                      -1) ) # real PID is still unknown
687    {
688      mtr_debug("mysqld[$idx] has not created PID file in " .
689                 "$mysqld->{start_timeout} secs.");
690      mtr_debug("Aborting test suite...");
691
692      mtr_kill_leftovers();
693
694      mtr_report("mysqld[$idx] has not created PID file in " .
695                 "$mysqld->{start_timeout} secs.");
696      return 0;
697    }
698
699    mtr_debug("PID file for mysqld[$idx] ($mysqld->{path_pid} created.");
700  }
701
702  # Wait until we can connect to guarded mysqld-instances
703  # (in other words -- wait for IM to start guarded instances).
704
705  mtr_debug("Waiting for guarded mysqlds to start accepting connections...");
706
707  for ( my $idx= 0; $idx < 2; ++$idx )
708  {
709    my $mysqld= $im->{'instances'}->[$idx];
710
711    if ( exists $mysqld->{'nonguarded'} )
712    {
713      next;
714    }
715
716    mtr_debug("Waiting for mysqld[$idx] to accept connection...");
717
718    unless ( mtr_im_wait_for_mysqld($mysqld, 30, 1) )
719    {
720      mtr_debug("Can not connect to mysqld[$idx] " .
721                "in $IM_CONNECT_TIMEOUT seconds after start.");
722      mtr_debug("Aborting test suite...");
723
724      mtr_kill_leftovers();
725
726      mtr_report("Can not connect to mysqld[$idx] " .
727                 "in $IM_CONNECT_TIMEOUT seconds after start.");
728      return 0;
729    }
730
731    mtr_debug("mysqld[$idx] started.");
732  }
733
734  mtr_debug("Instance Manager and its components are up and running.");
735
736  return 1;
737}
738
739##############################################################################
740
741sub mtr_im_stop($) {
742  my $im= shift;
743
744  mtr_debug("Stopping Instance Manager...");
745
746  # Try graceful shutdown.
747
748  mtr_im_terminate($im);
749
750  # Check that all processes died.
751
752  unless ( mtr_im_check_alive($im) )
753  {
754    mtr_debug("Instance Manager has been stopped successfully.");
755    mtr_im_cleanup($im);
756    return 1;
757  }
758
759  # Instance Manager don't want to die. We should kill it.
760
761  mtr_im_errlog("Instance Manager did not shutdown gracefully.");
762
763  mtr_im_kill($im);
764
765  # Check again that all IM-related processes have been killed.
766
767  my $im_is_alive= mtr_im_check_alive($im);
768
769  mtr_im_cleanup($im);
770
771  if ( $im_is_alive )
772  {
773    mtr_debug("Can not kill Instance Manager or its children.");
774    return 0;
775  }
776
777  mtr_debug("Instance Manager has been killed successfully.");
778  return 1;
779}
780
781###########################################################################
782
7831;
784