1 /* Copyright (c) 2008, 2015, 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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22 
23 
24 /*
25   Utility program that encapsulates process creation, monitoring
26   and bulletproof process cleanup
27 
28   Usage:
29     safe_process [options to safe_process] -- progname arg1 ... argn
30 
31   To safeguard mysqld you would invoke safe_process with a few options
32   for safe_process itself followed by a double dash to indicate start
33   of the command line for the program you really want to start
34 
35   $> safe_process --output=output.log -- mysqld --datadir=var/data1 ...
36 
37   This would redirect output to output.log and then start mysqld,
38   once it has done that it will continue to monitor the child as well
39   as the parent.
40 
41   The safe_process then checks the follwing things:
42   1. Child exits, propagate the childs return code to the parent
43      by exiting with the same return code as the child.
44 
45   2. Parent dies, immediately kill the child and exit, thus the
46      parent does not need to properly cleanup any child, it is handled
47      automatically.
48 
49   3. Signal's recieced by the process will trigger same action as 2)
50 
51 */
52 
53 #include <sys/types.h>
54 #include <sys/wait.h>
55 #include <sys/time.h>
56 #include <sys/resource.h>
57 #include <unistd.h>
58 #include <stdarg.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <signal.h>
62 #include <string.h>
63 #include <errno.h>
64 
65 int verbose= 0;
66 volatile sig_atomic_t terminated= 0;
67 volatile sig_atomic_t aborted= 0;
68 pid_t child_pid= -1;
69 char safe_process_name[32]= {0};
70 
71 static void print_message(const char* fmt, ...)
72 __attribute__((format(printf, 1, 2)));
print_message(const char * fmt,...)73 static void print_message(const char* fmt, ...)
74 {
75   va_list args;
76   fprintf(stderr, "%s: ", safe_process_name);
77   va_start(args, fmt);
78   vfprintf(stderr, fmt, args);
79   fprintf(stderr, "\n");
80   va_end(args);
81   fflush(stderr);
82 }
83 
84 static void message(const char* fmt, ...)
85 __attribute__((format(printf, 1, 2)));
message(const char * fmt,...)86 static void message(const char* fmt, ...)
87 {
88   if (!verbose)
89     return;
90   va_list args;
91   fprintf(stderr, "%s: ", safe_process_name);
92   va_start(args, fmt);
93   vfprintf(stderr, fmt, args);
94   fprintf(stderr, "\n");
95   va_end(args);
96   fflush(stderr);
97 }
98 
99 static void die(const char* fmt, ...)
100 __attribute__((format(printf, 1, 2)));
die(const char * fmt,...)101 static void die(const char* fmt, ...)
102 {
103   va_list args;
104   fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name);
105   va_start(args, fmt);
106   vfprintf(stderr, fmt, args);
107   fprintf(stderr, "\n");
108   va_end(args);
109   if (int last_err= errno)
110     fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err));
111   exit(1);
112 }
113 
114 
wait_pid(void)115 static void wait_pid(void)
116 {
117   int status= 0;
118 
119   pid_t ret_pid;
120   while ((ret_pid = waitpid(child_pid, &status, 0)) < 0)
121   {
122     if (errno != EINTR)
123       die("Failure to wait for child %d, errno %d",
124           static_cast<int>(child_pid), errno);
125   }
126 
127   if (ret_pid == child_pid)
128   {
129     int exit_code= 1;
130     if (WIFEXITED(status))
131     {
132       // Process has exited, collect return status
133       exit_code= WEXITSTATUS(status);
134       // Print info about the exit_code except for 62 which occurs when
135       // test is skipped
136       if (exit_code != 0 && exit_code != 62)
137         print_message("Child process: %d, exit: %d",
138                       static_cast<int>(child_pid), exit_code);
139       else
140         message("Child process: %d, exit %d",
141                 static_cast<int>(child_pid), exit_code);
142       // Exit with exit status of the child
143       exit(exit_code);
144     }
145 
146     if (WIFSIGNALED(status))
147       print_message("Child process: %d, killed by signal: %d",
148                     static_cast<int>(child_pid), WTERMSIG(status));
149 
150     exit(exit_code);
151   }
152   else
153   {
154     print_message("The waitpid returned: %d", static_cast<int>(ret_pid));
155     exit(1);
156   }
157   return;
158 }
159 
abort_child(void)160 static void abort_child(void)
161 {
162   message("Aborting child: %d", static_cast<int>(child_pid));
163   kill (-child_pid, SIGABRT);
164   wait_pid();
165 }
166 
kill_child(void)167 static void kill_child(void)
168 {
169   // Terminate whole process group
170   message("Killing child: %d", static_cast<int>(child_pid));
171   kill(-child_pid, SIGKILL);
172   wait_pid();
173 }
174 
handle_abort(int sig)175 extern "C" void handle_abort(int sig)
176 {
177   aborted= sig;
178   print_message("Child process: %d, aborted by signal: %d",
179                 static_cast<int>(child_pid), sig);
180 }
181 
182 
handle_signal(int sig)183 extern "C" void handle_signal(int sig)
184 {
185   terminated= sig;
186   message("Got SIGCHLD from process: %d", static_cast<int>(child_pid));
187 }
188 
189 
main(int argc,char * const argv[])190 int main(int argc, char* const argv[] )
191 {
192   char* const* child_argv= 0;
193   pid_t own_pid= getpid();
194   pid_t parent_pid= getppid();
195   bool nocore = false;
196   struct sigaction sa,sa_abort;
197 
198   sa.sa_handler= handle_signal;
199   sa.sa_flags= SA_NOCLDSTOP;
200   sigemptyset(&sa.sa_mask);
201 
202   sa_abort.sa_handler= handle_abort;
203   sa_abort.sa_flags= 0;
204   sigemptyset(&sa_abort.sa_mask);
205   /* Install signal handlers */
206   sigaction(SIGTERM, &sa,NULL);
207   sigaction(SIGSEGV, &sa, NULL);
208   sigaction(SIGINT, &sa,NULL);
209   sigaction(SIGCHLD, &sa,NULL);
210   sigaction(SIGABRT, &sa_abort,NULL);
211 
212   sprintf(safe_process_name, "safe_process[%ld]", (long) own_pid);
213 
214 
215   /* Parse arguments */
216   for (int i= 1; i < argc; i++) {
217     const char* arg= argv[i];
218     if (strcmp(arg, "--") == 0 && strlen(arg) == 2) {
219       /* Got the "--" delimiter */
220       if (i >= argc)
221         die("No real args -> nothing to do");
222       child_argv= &argv[i+1];
223       break;
224     }
225     else
226     {
227       if ( strcmp(arg, "--verbose") == 0 )
228       {
229         verbose++;
230       }
231       else if ( strncmp(arg, "--parent-pid", 12) == 0 )
232       {
233         /* Override parent_pid with a value provided by user */
234         const char* start;
235         if ((start= strstr(arg, "=")) == NULL)
236           die("Could not find start of option value in '%s'", arg);
237         start++; /* Step past = */
238         if ((parent_pid= atoi(start)) == 0)
239           die("Invalid value '%s' passed to --parent-id", start);
240       }
241       else if ( strcmp(arg, "--nocore") == 0 )
242       {
243         nocore = true;	// Don't allow the process to dump core
244       }
245       else if ( strncmp (arg, "--env ", 6) == 0 )
246       {
247 	putenv(strdup(arg+6));
248       }
249       else
250         die("Unknown option: %s", arg);
251     }
252   }
253   if (!child_argv || *child_argv == 0)
254     die("nothing to do");
255 
256   message("parent_pid: %d", static_cast<int>(parent_pid));
257 
258   if (parent_pid == own_pid)
259     die("parent_pid is equal to own pid!");
260 
261   char buf;
262   int pfd[2];
263   if (pipe(pfd) == -1)
264     die("Failed to create pipe");
265 
266   /* Create the child process */
267   while((child_pid= fork()) == -1)
268   {
269     message("fork failed");
270     sleep(1);
271   }
272 
273   if (child_pid == 0)
274   {
275     close(pfd[0]); // Close unused read end
276 
277     // Use default signal handlers in child
278     signal(SIGTERM, SIG_DFL);
279     signal(SIGINT,  SIG_DFL);
280     signal(SIGCHLD, SIG_DFL);
281     signal(SIGABRT, SIG_DFL);
282 
283     // Make this process it's own process group to be able to kill
284     // it and any childs(that hasn't changed group themself)
285     setpgid(0, 0);
286 
287     if (nocore)
288     {
289       struct rlimit corelim = { 0, 0 };
290       if (setrlimit (RLIMIT_CORE, &corelim) < 0)
291       {
292         message("setrlimit failed, errno=%d", errno);
293       }
294     }
295 
296     // Signal that child is ready
297     buf= 37;
298     if ((write(pfd[1], &buf, 1)) < 1)
299       die("Failed to signal that child is ready");
300     // Close write end
301     close(pfd[1]);
302 
303     if (execvp(child_argv[0], child_argv) < 0)
304       die("Failed to exec child");
305   }
306 
307   close(pfd[1]); // Close unused write end
308 
309   // Wait for child to signal it's ready
310   while ((read(pfd[0], &buf, 1)) < 1 )
311   {
312     //make sure that safe_process comes back even
313     //if any signal was raised
314     if (errno != EINTR)
315       die("Failed to read signal from child %d", errno);
316   }
317   if (buf != 37)
318     die("Didn't get 37 from pipe");
319   close(pfd[0]); // Close read end
320 
321   /* Monitor loop */
322   message("Started child: %d", static_cast<int>(child_pid));
323 
324   while(1)
325   {
326     // Check if parent is still alive
327     if (kill(parent_pid, 0) != 0)
328     {
329       print_message("Parent is not alive anymore, parent pid %d:",
330                     static_cast<int>(parent_pid));
331       kill_child();
332     }
333 
334     if(terminated)
335     {
336       kill_child();
337     }
338     if(aborted)
339     {
340       message("Got signal: %d, child_pid: %d",
341               static_cast<int>(terminated), static_cast<int>(child_pid));
342       abort_child();
343     }
344     sleep(1);
345   }
346   kill_child();
347 
348   return 1;
349 }
350 
351