summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore1
-rw-r--r--configure.in2
-rw-r--r--mysql-test/Makefile.am2
-rw-r--r--mysql-test/lib/My/SafeProcess.pm35
-rw-r--r--mysql-test/lib/My/SafeProcess/Makefile.am21
-rw-r--r--mysql-test/lib/My/SafeProcess/safe_process.cc267
-rwxr-xr-xmysql-test/lib/t/SafeProcessStress.pl4
7 files changed, 318 insertions, 14 deletions
diff --git a/.bzrignore b/.bzrignore
index 7a8066fdba1..7721217165b 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -3005,3 +3005,4 @@ win/vs71cache.txt
win/vs8cache.txt
zlib/*.ds?
zlib/*.vcproj
+mysql-test/lib/My/SafeProcess/my_safe_process
diff --git a/configure.in b/configure.in
index 34e22562e1f..dafcf1f067c 100644
--- a/configure.in
+++ b/configure.in
@@ -2680,7 +2680,7 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl
server-tools/Makefile server-tools/instance-manager/Makefile dnl
cmd-line-utils/Makefile cmd-line-utils/libedit/Makefile dnl
libmysqld/Makefile libmysqld/examples/Makefile dnl
- mysql-test/Makefile dnl
+ mysql-test/Makefile mysql-test/lib/My/SafeProcess/Makefile dnl
netware/Makefile sql-bench/Makefile dnl
include/mysql_version.h plugin/Makefile win/Makefile)
diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am
index 13bcdbe73cf..b9ed71902f0 100644
--- a/mysql-test/Makefile.am
+++ b/mysql-test/Makefile.am
@@ -29,6 +29,8 @@ EXTRA_DIST = README \
test_SCRIPTS = mtr mysql-test-run
CLEANFILES = $(test_SCRIPTS)
+SUBDIRS = lib/My/SafeProcess
+
# Install all files and files in directories listed in EXTRA_DIST
install-data-local:
diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm
index 612096cceb7..ef3ba9146d1 100644
--- a/mysql-test/lib/My/SafeProcess.pm
+++ b/mysql-test/lib/My/SafeProcess.pm
@@ -90,22 +90,35 @@ my @safe_process_cmd;
my $safe_kill;
if (IS_WIN32PERL or IS_CYGWIN){
# Use my_safe_process.exe
- my $exe= my_find_bin(".", "lib/My/SafeProcess", "my_safe_process.exe");
- die "Could not find my_safe_process.exe" unless $exe;
+ my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"],
+ "my_safe_process");
+ die "Could not find my_safe_process" unless $exe;
push(@safe_process_cmd, $exe);
# Use my_safe_kill.exe
my $safe_kill= my_find_bin(".", "lib/My/SafeProcess", "my_safe_kill");
- die "Could not find my_safe_kill.exe" unless $safe_kill;
+ die "Could not find my_safe_kill" unless $safe_kill;
}
-else {
- # Use safe_process.pl
- my $script= "lib/My/SafeProcess/safe_process.pl";
- $script= "../$script" unless -f $script;
- die "Could not find safe_process.pl" unless -f $script;
-
- # Call $script with Perl interpreter
- push(@safe_process_cmd, $^X, $script);
+else
+{
+ my $use_safe_process_binary= 1;
+ if ($use_safe_process_binary) {
+ # Use my_safe_process
+ my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"],
+ "my_safe_process");
+ die "Could not find my_safe_process" unless $exe;
+ push(@safe_process_cmd, $exe);
+ }
+ else
+ {
+ # Use safe_process.pl
+ my $script= "lib/My/SafeProcess/safe_process.pl";
+ $script= "../$script" unless -f $script;
+ die "Could not find safe_process.pl" unless -f $script;
+
+ # Call $script with Perl interpreter
+ push(@safe_process_cmd, $^X, $script);
+ }
}
diff --git a/mysql-test/lib/My/SafeProcess/Makefile.am b/mysql-test/lib/My/SafeProcess/Makefile.am
new file mode 100644
index 00000000000..6ad43949b11
--- /dev/null
+++ b/mysql-test/lib/My/SafeProcess/Makefile.am
@@ -0,0 +1,21 @@
+# Copyright (C) 2000-2006 MySQL AB
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+bin_PROGRAMS = my_safe_process
+
+my_safe_process_SOURCES = safe_process.cc
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
diff --git a/mysql-test/lib/My/SafeProcess/safe_process.cc b/mysql-test/lib/My/SafeProcess/safe_process.cc
new file mode 100644
index 00000000000..71ba5f2f12d
--- /dev/null
+++ b/mysql-test/lib/My/SafeProcess/safe_process.cc
@@ -0,0 +1,267 @@
+/* Copyright (C) 2008 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/*
+ Utility program that encapsulates process creation, monitoring
+ and bulletproof process cleanup
+
+ Usage:
+ safe_process [options to safe_process] -- progname arg1 ... argn
+
+ To safeguard mysqld you would invoke safe_process with a few options
+ for safe_process itself followed by a double dash to indicate start
+ of the command line for the program you really want to start
+
+ $> safe_process --output=output.log -- mysqld --datadir=var/data1 ...
+
+ This would redirect output to output.log and then start mysqld,
+ once it has done that it will continue to monitor the child as well
+ as the parent.
+
+ The safe_process then checks the follwing things:
+ 1. Child exits, propagate the childs return code to the parent
+ by exiting with the same return code as the child.
+
+ 2. Parent dies, immediately kill the child and exit, thus the
+ parent does not need to properly cleanup any child, it is handled
+ automatically.
+
+ 3. Signal's recieced by the process will trigger same action as 2)
+
+*/
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+int verbose= 0;
+int terminated= 0;
+pid_t child_pid= -1;
+char safe_process_name[32]= {0};
+
+
+static void message(const char* fmt, ...)
+{
+ if (!verbose)
+ return;
+ va_list args;
+ fprintf(stderr, "%s: ", safe_process_name);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ fflush(stderr);
+}
+
+
+static void die(const char* fmt, ...)
+{
+ va_list args;
+ fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ if (int last_err= errno)
+ fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err));
+ exit(1);
+}
+
+
+static void kill_child (void)
+{
+ int exit_code= 1;
+ int status= 0;
+
+ message("Killing child: %d", child_pid);
+ // Terminate whole process group
+ kill(-child_pid, SIGKILL);
+
+ pid_t ret_pid= waitpid(child_pid, &status, 0);
+ if (ret_pid == child_pid)
+ {
+ if (WIFEXITED(status))
+ {
+ // Process has exited, collect return status
+ exit_code= WEXITSTATUS(status);
+ message("Child exit: %d", exit_code);
+ // Exit with exit status of the child
+ exit(exit_code);
+ }
+
+ if (WIFSIGNALED(status))
+ message("Child killed by signal: %d", WTERMSIG(status));
+
+ exit(exit_code);
+ }
+ exit(exit_code);
+}
+
+
+static void handle_signal (int sig)
+{
+ message("Got signal %d, child_pid: %d", sig, child_pid);
+ terminated= 1;
+
+ // Ignore further signals
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+
+ if (child_pid > 0)
+ kill_child();
+
+ // Continune execution, allow the child to be started and
+ // finally terminated by monitor loop
+}
+
+
+int main(int argc, char* const argv[] )
+{
+ char* const* child_argv= 0;
+ pid_t own_pid= getpid();
+ pid_t parent_pid= getppid();
+
+ /* Install signal handlers */
+ signal(SIGTERM, handle_signal);
+ signal(SIGINT, handle_signal);
+ signal(SIGCHLD, handle_signal);
+
+ sprintf(safe_process_name, "safe_process[%d]", own_pid);
+
+ message("Started");
+
+ /* Parse arguments */
+ for (int i= 1; i < argc; i++) {
+ const char* arg= argv[i];
+ if (strcmp(arg, "--") == 0 && strlen(arg) == 2) {
+ /* Got the "--" delimiter */
+ if (i >= argc)
+ die("No real args -> nothing to do");
+ child_argv= &argv[i+1];
+ break;
+ } else {
+ if ( strcmp(arg, "--verbose") == 0 )
+ verbose++;
+ else if ( strncmp(arg, "--parent-pid", 10) == 0 )
+ {
+ /* Override parent_pid with a value provided by user */
+ const char* start;
+ if ((start= strstr(arg, "=")) == NULL)
+ die("Could not find start of option value in '%s'", arg);
+ start++; /* Step past = */
+ if ((parent_pid= atoi(start)) == 0)
+ die("Invalid value '%s' passed to --parent-id", start);
+ }
+ else
+ die("Unknown option: %s", arg);
+ }
+ }
+ if (!child_argv || *child_argv == 0)
+ die("nothing to do");
+
+ message("parent_pid: %d", parent_pid);
+ if (parent_pid == own_pid)
+ die("parent_pid is equal to own pid!");
+
+ char buf;
+ int pfd[2];
+ if (pipe(pfd) == -1)
+ die("Failed to create pipe");
+
+ /* Create the child process */
+ while((child_pid= fork()) == -1)
+ {
+ message("fork failed");
+ sleep(1);
+ }
+
+ if (child_pid == 0)
+ {
+ close(pfd[0]); // Close unused read end
+
+ // Use default signal handlers in child
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+
+ // Make this process it's own process group to be able to kill
+ // it and any childs(that hasn't changed group themself)
+ setpgid(0, 0);
+
+ // Signal that child is ready
+ buf= 37;
+ write(pfd[1], &buf, 1);
+ // Close write end
+ close(pfd[1]);
+
+ if (execvp(child_argv[0], child_argv) < 0)
+ die("Failed to exec child");
+ }
+
+ close(pfd[1]); // Close unused write end
+
+ // Wait for child to signal it's ready
+ read(pfd[0], &buf, 1);
+ if(buf != 37)
+ die("Didn't get 37 from pipe");
+ close(pfd[0]); // Close read end
+
+ /* Monitor loop */
+ message("Started child %d, terminated: %d", child_pid, terminated);
+
+ while(!terminated)
+ {
+ // Check if parent is still alive
+ if (kill(parent_pid, 0) != 0){
+ message("Parent is not alive anymore");
+ break;
+ }
+
+ // Check if child has exited, normally this wil be
+ // detected immediately with SIGCHLD handler
+ int status= 0;
+ pid_t ret_pid= waitpid(child_pid, &status, WNOHANG);
+ if (ret_pid == child_pid)
+ {
+ int ret_code= 2;
+ if (WIFEXITED(status))
+ {
+ // Process has exited, collect return status
+ int ret_code= WEXITSTATUS(status);
+ message("Child exit: %d", ret_code);
+ // Exit with exit status of the child
+ exit(ret_code);
+ }
+
+ if (WIFSIGNALED(status))
+ message("Child killed by signal: %d", WTERMSIG(status));
+
+ exit(ret_code);
+ }
+ sleep(1);
+ }
+ kill_child();
+
+ exit(1);
+}
+
diff --git a/mysql-test/lib/t/SafeProcessStress.pl b/mysql-test/lib/t/SafeProcessStress.pl
index a31f3031262..0f7a59d67f0 100755
--- a/mysql-test/lib/t/SafeProcessStress.pl
+++ b/mysql-test/lib/t/SafeProcessStress.pl
@@ -11,7 +11,7 @@ use My::SafeProcess;
my $perl_path= $^X;
my $verbose= 0;
-my $loops= 1000;
+my $loops= 100;
print "kill one and wait for one\n";
for (1...$loops){
@@ -33,7 +33,7 @@ for (1...$loops){
foreach my $proc (@procs) {
$proc->kill();
- # dummyd will always be kiled and thus
+ # dummyd will always be killed and thus
# exit_status should have been set to 1
die "oops, exit_status: ", $proc->exit_status()
unless $proc->exit_status() == 1;