1
/* Copyright (C) 2008 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18
Utility program that encapsulates process creation, monitoring
19
and bulletproof process cleanup
22
safe_process [options to safe_process] -- progname arg1 ... argn
24
To safeguard mysqld you would invoke safe_process with a few options
25
for safe_process itself followed by a double dash to indicate start
26
of the command line for the program you really want to start
28
$> safe_process --output=output.log -- mysqld --datadir=var/data1 ...
30
This would redirect output to output.log and then start mysqld,
31
once it has done that it will continue to monitor the child as well
34
The safe_process then checks the follwing things:
35
1. Child exits, propagate the childs return code to the parent
36
by exiting with the same return code as the child.
38
2. Parent dies, immediately kill the child and exit, thus the
39
parent does not need to properly cleanup any child, it is handled
42
3. Signal's recieced by the process will trigger same action as 2)
46
#include <sys/types.h>
49
#include <sys/resource.h>
61
char safe_process_name[32]= {0};
64
static void message(const char* fmt, ...)
69
fprintf(stderr, "%s: ", safe_process_name);
71
vfprintf(stderr, fmt, args);
72
fprintf(stderr, "\n");
78
static void die(const char* fmt, ...)
81
fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name);
83
vfprintf(stderr, fmt, args);
84
fprintf(stderr, "\n");
86
if (int last_err= errno)
87
fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err));
92
static void kill_child(void)
96
message("Killing child: %d", child_pid);
97
// Terminate whole process group
98
kill(-child_pid, SIGKILL);
100
pid_t ret_pid= waitpid(child_pid, &status, 0);
101
if (ret_pid == child_pid)
104
if (WIFEXITED(status))
106
// Process has exited, collect return status
107
exit_code= WEXITSTATUS(status);
108
message("Child exit: %d", exit_code);
109
// Exit with exit status of the child
113
if (WIFSIGNALED(status))
114
message("Child killed by signal: %d", WTERMSIG(status));
122
extern "C" void handle_abort(int sig)
124
message("Got signal %d, child_pid: %d, sending ABRT", sig, child_pid);
127
kill (-child_pid, SIGABRT); // Don't wait for it to terminate
132
extern "C" void handle_signal(int sig)
134
message("Got signal %d, child_pid: %d", sig, child_pid);
140
// Ignore further signals
141
signal(SIGTERM, SIG_IGN);
142
signal(SIGINT, SIG_IGN);
144
// Continune execution, allow the child to be started and
145
// finally terminated by monitor loop
149
int main(int argc, char* const argv[] )
151
char* const* child_argv= 0;
152
pid_t own_pid= getpid();
153
pid_t parent_pid= getppid();
156
/* Install signal handlers */
157
signal(SIGTERM, handle_signal);
158
signal(SIGINT, handle_signal);
159
signal(SIGCHLD, handle_signal);
160
signal(SIGABRT, handle_abort);
162
sprintf(safe_process_name, "safe_process[%d]", own_pid);
166
/* Parse arguments */
167
for (int i= 1; i < argc; i++) {
168
const char* arg= argv[i];
169
if (strcmp(arg, "--") == 0 && strlen(arg) == 2) {
170
/* Got the "--" delimiter */
172
die("No real args -> nothing to do");
173
child_argv= &argv[i+1];
176
if ( strcmp(arg, "--verbose") == 0 )
178
else if ( strncmp(arg, "--parent-pid", 10) == 0 )
180
/* Override parent_pid with a value provided by user */
182
if ((start= strstr(arg, "=")) == NULL)
183
die("Could not find start of option value in '%s'", arg);
184
start++; /* Step past = */
185
if ((parent_pid= atoi(start)) == 0)
186
die("Invalid value '%s' passed to --parent-id", start);
187
} else if ( strcmp(arg, "--nocore") == 0 )
189
nocore = true; // Don't allow the process to dump core
192
die("Unknown option: %s", arg);
195
if (!child_argv || *child_argv == 0)
196
die("nothing to do");
198
message("parent_pid: %d", parent_pid);
199
if (parent_pid == own_pid)
200
die("parent_pid is equal to own pid!");
205
die("Failed to create pipe");
207
/* Create the child process */
208
while((child_pid= fork()) == -1)
210
message("fork failed");
216
close(pfd[0]); // Close unused read end
218
// Use default signal handlers in child
219
signal(SIGTERM, SIG_DFL);
220
signal(SIGINT, SIG_DFL);
221
signal(SIGCHLD, SIG_DFL);
223
// Make this process it's own process group to be able to kill
224
// it and any childs(that hasn't changed group themself)
229
struct rlimit corelim = { 0, 0 };
230
if (setrlimit (RLIMIT_CORE, &corelim) < 0)
232
message("setrlimit failed, errno=%d", errno);
236
// Signal that child is ready
238
if ((write(pfd[1], &buf, 1)) < 1)
239
die("Failed to signal that child is ready");
243
if (execvp(child_argv[0], child_argv) < 0)
244
die("Failed to exec child");
247
close(pfd[1]); // Close unused write end
249
// Wait for child to signal it's ready
250
if ((read(pfd[0], &buf, 1)) < 1)
251
die("Failed to read signal from child");
254
die("Didn't get 37 from pipe");
255
close(pfd[0]); // Close read end
258
message("Started child %d, terminated: %d", child_pid, terminated);
262
// Check if parent is still alive
263
if (kill(parent_pid, 0) != 0){
264
message("Parent is not alive anymore");
268
// Check if child has exited, normally this will be
269
// detected immediately with SIGCHLD handler
271
pid_t ret_pid= waitpid(child_pid, &status, WNOHANG);
272
if (ret_pid == child_pid)
275
if (WIFEXITED(status))
277
// Process has exited, collect return status
278
ret_code= WEXITSTATUS(status);
279
message("Child exit: %d", ret_code);
280
// Exit with exit status of the child
284
if (WIFSIGNALED(status))
285
message("Child killed by signal: %d", WTERMSIG(status));