MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
safe_process.cc
1 /* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
2 
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.
6 
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.
11 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
15 
16 
17 /*
18  Utility program that encapsulates process creation, monitoring
19  and bulletproof process cleanup
20 
21  Usage:
22  safe_process [options to safe_process] -- progname arg1 ... argn
23 
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
27 
28  $> safe_process --output=output.log -- mysqld --datadir=var/data1 ...
29 
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
32  as the parent.
33 
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.
37 
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
40  automatically.
41 
42  3. Signal's recieced by the process will trigger same action as 2)
43 
44 */
45 
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 #include <sys/time.h>
49 #include <sys/resource.h>
50 #include <unistd.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <signal.h>
55 #include <string.h>
56 #include <errno.h>
57 
58 int verbose= 0;
59 int terminated= 0;
60 pid_t child_pid= -1;
61 char safe_process_name[32]= {0};
62 
63 
64 static void message(const char* fmt, ...)
65 {
66  if (!verbose)
67  return;
68  va_list args;
69  fprintf(stderr, "%s: ", safe_process_name);
70  va_start(args, fmt);
71  vfprintf(stderr, fmt, args);
72  fprintf(stderr, "\n");
73  va_end(args);
74  fflush(stderr);
75 }
76 
77 
78 static void die(const char* fmt, ...)
79 {
80  va_list args;
81  fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name);
82  va_start(args, fmt);
83  vfprintf(stderr, fmt, args);
84  fprintf(stderr, "\n");
85  va_end(args);
86  if (int last_err= errno)
87  fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err));
88  exit(1);
89 }
90 
91 
92 static void kill_child(void)
93 {
94  int status= 0;
95 
96  message("Killing child: %d", child_pid);
97  // Terminate whole process group
98  kill(-child_pid, SIGKILL);
99 
100  pid_t ret_pid= waitpid(child_pid, &status, 0);
101  if (ret_pid == child_pid)
102  {
103  int exit_code= 1;
104  if (WIFEXITED(status))
105  {
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
110  exit(exit_code);
111  }
112 
113  if (WIFSIGNALED(status))
114  message("Child killed by signal: %d", WTERMSIG(status));
115 
116  exit(exit_code);
117  }
118  exit(1);
119 }
120 
121 
122 extern "C" void handle_abort(int sig)
123 {
124  message("Got signal %d, child_pid: %d, sending ABRT", sig, child_pid);
125 
126  if (child_pid > 0) {
127  kill (-child_pid, SIGABRT); // Don't wait for it to terminate
128  }
129 }
130 
131 
132 extern "C" void handle_signal(int sig)
133 {
134  message("Got signal %d, child_pid: %d", sig, child_pid);
135  terminated= 1;
136 
137  if (child_pid > 0)
138  kill_child();
139 
140  // Ignore further signals
141  signal(SIGTERM, SIG_IGN);
142  signal(SIGINT, SIG_IGN);
143 
144  // Continune execution, allow the child to be started and
145  // finally terminated by monitor loop
146 }
147 
148 
149 int main(int argc, char* const argv[] )
150 {
151  char* const* child_argv= 0;
152  pid_t own_pid= getpid();
153  pid_t parent_pid= getppid();
154  bool nocore = false;
155  struct sigaction sa,sa_abort;
156 
157  sa.sa_handler= handle_signal;
158  sa.sa_flags= SA_NOCLDSTOP;
159  sigemptyset(&sa.sa_mask);
160 
161  sa_abort.sa_handler= handle_abort;
162  sigemptyset(&sa_abort.sa_mask);
163  /* Install signal handlers */
164  sigaction(SIGTERM, &sa,NULL);
165  sigaction(SIGINT, &sa,NULL);
166  sigaction(SIGCHLD, &sa,NULL);
167  sigaction(SIGABRT, &sa_abort,NULL);
168 
169  sprintf(safe_process_name, "safe_process[%ld]", (long) own_pid);
170 
171  message("Started");
172 
173  /* Parse arguments */
174  for (int i= 1; i < argc; i++) {
175  const char* arg= argv[i];
176  if (strcmp(arg, "--") == 0 && strlen(arg) == 2) {
177  /* Got the "--" delimiter */
178  if (i >= argc)
179  die("No real args -> nothing to do");
180  child_argv= &argv[i+1];
181  break;
182  } else {
183  if ( strcmp(arg, "--verbose") == 0 )
184  verbose++;
185  else if ( strncmp(arg, "--parent-pid", 12) == 0 )
186  {
187  /* Override parent_pid with a value provided by user */
188  const char* start;
189  if ((start= strstr(arg, "=")) == NULL)
190  die("Could not find start of option value in '%s'", arg);
191  start++; /* Step past = */
192  if ((parent_pid= atoi(start)) == 0)
193  die("Invalid value '%s' passed to --parent-id", start);
194  }
195  else if ( strcmp(arg, "--nocore") == 0 )
196  {
197  nocore = true; // Don't allow the process to dump core
198  }
199  else if ( strncmp (arg, "--env ", 6) == 0 )
200  {
201  putenv(strdup(arg+6));
202  }
203  else
204  die("Unknown option: %s", arg);
205  }
206  }
207  if (!child_argv || *child_argv == 0)
208  die("nothing to do");
209 
210  message("parent_pid: %d", parent_pid);
211  if (parent_pid == own_pid)
212  die("parent_pid is equal to own pid!");
213 
214  char buf;
215  int pfd[2];
216  if (pipe(pfd) == -1)
217  die("Failed to create pipe");
218 
219  /* Create the child process */
220  while((child_pid= fork()) == -1)
221  {
222  message("fork failed");
223  sleep(1);
224  }
225 
226  if (child_pid == 0)
227  {
228  close(pfd[0]); // Close unused read end
229 
230  // Use default signal handlers in child
231  signal(SIGTERM, SIG_DFL);
232  signal(SIGINT, SIG_DFL);
233  signal(SIGCHLD, SIG_DFL);
234 
235  // Make this process it's own process group to be able to kill
236  // it and any childs(that hasn't changed group themself)
237  setpgid(0, 0);
238 
239  if (nocore)
240  {
241  struct rlimit corelim = { 0, 0 };
242  if (setrlimit (RLIMIT_CORE, &corelim) < 0)
243  {
244  message("setrlimit failed, errno=%d", errno);
245  }
246  }
247 
248  // Signal that child is ready
249  buf= 37;
250  if ((write(pfd[1], &buf, 1)) < 1)
251  die("Failed to signal that child is ready");
252  // Close write end
253  close(pfd[1]);
254 
255  if (execvp(child_argv[0], child_argv) < 0)
256  die("Failed to exec child");
257  }
258 
259  close(pfd[1]); // Close unused write end
260 
261  // Wait for child to signal it's ready
262  if ((read(pfd[0], &buf, 1)) < 1)
263  die("Failed to read signal from child");
264 
265  if (buf != 37)
266  die("Didn't get 37 from pipe");
267  close(pfd[0]); // Close read end
268 
269  /* Monitor loop */
270  message("Started child %d, terminated: %d", child_pid, terminated);
271 
272  while(!terminated)
273  {
274  // Check if parent is still alive
275  if (kill(parent_pid, 0) != 0){
276  message("Parent is not alive anymore");
277  break;
278  }
279 
280  // Check if child has exited, normally this will be
281  // detected immediately with SIGCHLD handler
282  int status= 0;
283  pid_t ret_pid= waitpid(child_pid, &status, WNOHANG);
284  if (ret_pid == child_pid)
285  {
286  int ret_code= 2;
287  if (WIFEXITED(status))
288  {
289  // Process has exited, collect return status
290  ret_code= WEXITSTATUS(status);
291  message("Child exit: %d", ret_code);
292  // Exit with exit status of the child
293  exit(ret_code);
294  }
295 
296  if (WIFSIGNALED(status))
297  message("Child killed by signal: %d", WTERMSIG(status));
298 
299  exit(ret_code);
300  }
301  sleep(1);
302  }
303  kill_child();
304 
305  return 1;
306 }
307