1 /* Copyright (c) 2008, 2021, Oracle and/or its affiliates.
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 #include <string>
65 
66 #include "my_config.h"
67 
68 int verbose= 0;
69 volatile sig_atomic_t terminated= 0;
70 volatile sig_atomic_t aborted= 0;
71 pid_t child_pid= -1;
72 char safe_process_name[32]= {0};
73 
74 static void print_message(const char* fmt, ...)
75 __attribute__((format(printf, 1, 2)));
print_message(const char * fmt,...)76 static void print_message(const char* fmt, ...)
77 {
78   va_list args;
79   fprintf(stderr, "%s: ", safe_process_name);
80   va_start(args, fmt);
81   vfprintf(stderr, fmt, args);
82   fprintf(stderr, "\n");
83   va_end(args);
84   fflush(stderr);
85 }
86 
87 static void message(const char* fmt, ...)
88 __attribute__((format(printf, 1, 2)));
message(const char * fmt,...)89 static void message(const char* fmt, ...)
90 {
91   if (!verbose)
92     return;
93   va_list args;
94   fprintf(stderr, "%s: ", safe_process_name);
95   va_start(args, fmt);
96   vfprintf(stderr, fmt, args);
97   fprintf(stderr, "\n");
98   va_end(args);
99   fflush(stderr);
100 }
101 
102 static void die(const char* fmt, ...)
103 __attribute__((format(printf, 1, 2)));
die(const char * fmt,...)104 static void die(const char* fmt, ...)
105 {
106   va_list args;
107   fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name);
108   va_start(args, fmt);
109   vfprintf(stderr, fmt, args);
110   fprintf(stderr, "\n");
111   va_end(args);
112   if (int last_err= errno)
113     fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err));
114   exit(1);
115 }
116 
117 
wait_pid(void)118 static void wait_pid(void)
119 {
120   int status= 0;
121 
122   pid_t ret_pid;
123   while ((ret_pid = waitpid(child_pid, &status, 0)) < 0)
124   {
125     if (errno != EINTR)
126       die("Failure to wait for child %d, errno %d",
127           static_cast<int>(child_pid), errno);
128   }
129 
130   if (ret_pid == child_pid)
131   {
132     int exit_code= 1;
133     if (WIFEXITED(status))
134     {
135       // Process has exited, collect return status
136       exit_code= WEXITSTATUS(status);
137       // Print info about the exit_code except for 62 which occurs when
138       // test is skipped
139       if (exit_code != 0 && exit_code != 62)
140         print_message("Child process: %d, exit: %d",
141                       static_cast<int>(child_pid), exit_code);
142       else
143         message("Child process: %d, exit %d",
144                 static_cast<int>(child_pid), exit_code);
145       // Exit with exit status of the child
146       exit(exit_code);
147     }
148 
149     if (WIFSIGNALED(status))
150       print_message("Child process: %d, killed by signal: %d",
151                     static_cast<int>(child_pid), WTERMSIG(status));
152 
153     exit(exit_code);
154   }
155   else
156   {
157     print_message("The waitpid returned: %d", static_cast<int>(ret_pid));
158     exit(1);
159   }
160   return;
161 }
162 
abort_child(void)163 static void abort_child(void)
164 {
165   message("Aborting child: %d", static_cast<int>(child_pid));
166   kill (-child_pid, SIGABRT);
167   wait_pid();
168 }
169 
kill_child(void)170 static void kill_child(void)
171 {
172   // Terminate whole process group
173   message("Killing child: %d", static_cast<int>(child_pid));
174   kill(-child_pid, SIGKILL);
175   wait_pid();
176 }
177 
handle_abort(int sig)178 extern "C" void handle_abort(int sig)
179 {
180   aborted= sig;
181   print_message("Child process: %d, aborted by signal: %d",
182                 static_cast<int>(child_pid), sig);
183 }
184 
185 
handle_signal(int sig)186 extern "C" void handle_signal(int sig)
187 {
188   terminated= sig;
189   message("Got SIGCHLD from process: %d", static_cast<int>(child_pid));
190 }
191 
192 
main(int argc,char * const argv[])193 int main(int argc, char* const argv[] )
194 {
195   char* const* child_argv= 0;
196   pid_t own_pid= getpid();
197   pid_t parent_pid= getppid();
198   bool nocore = false;
199   struct sigaction sa,sa_abort;
200 
201   sa.sa_handler= handle_signal;
202   sa.sa_flags= SA_NOCLDSTOP;
203   sigemptyset(&sa.sa_mask);
204 
205   sa_abort.sa_handler= handle_abort;
206   sa_abort.sa_flags= 0;
207   sigemptyset(&sa_abort.sa_mask);
208   /* Install signal handlers */
209   sigaction(SIGTERM, &sa,NULL);
210   sigaction(SIGSEGV, &sa, NULL);
211   sigaction(SIGINT, &sa,NULL);
212   sigaction(SIGCHLD, &sa,NULL);
213   sigaction(SIGABRT, &sa_abort,NULL);
214 
215   sprintf(safe_process_name, "safe_process[%ld]", (long) own_pid);
216 
217 
218   /* Parse arguments */
219   for (int i= 1; i < argc; i++) {
220     const char* arg= argv[i];
221     if (strcmp(arg, "--") == 0 && strlen(arg) == 2) {
222       /* Got the "--" delimiter */
223       if (i >= argc)
224         die("No real args -> nothing to do");
225       child_argv= &argv[i+1];
226       break;
227     }
228     else
229     {
230       if ( strcmp(arg, "--verbose") == 0 )
231       {
232         verbose++;
233       }
234       else if ( strncmp(arg, "--parent-pid", 12) == 0 )
235       {
236         /* Override parent_pid with a value provided by user */
237         const char* start;
238         if ((start= strstr(arg, "=")) == NULL)
239           die("Could not find start of option value in '%s'", arg);
240         start++; /* Step past = */
241         if ((parent_pid= atoi(start)) == 0)
242           die("Invalid value '%s' passed to --parent-id", start);
243       }
244       else if ( strcmp(arg, "--nocore") == 0 )
245       {
246         nocore = true;	// Don't allow the process to dump core
247       }
248       else if ( strncmp (arg, "--env ", 6) == 0 )
249       {
250 	putenv(strdup(arg+6));
251       }
252       else
253         die("Unknown option: %s", arg);
254     }
255   }
256   if (!child_argv || *child_argv == 0)
257     die("nothing to do");
258 
259   message("parent_pid: %d", static_cast<int>(parent_pid));
260 
261   if (parent_pid == own_pid)
262     die("parent_pid is equal to own pid!");
263 
264   char buf;
265   int pfd[2];
266   if (pipe(pfd) == -1)
267     die("Failed to create pipe");
268 
269   /* Create the child process */
270   while((child_pid= fork()) == -1)
271   {
272     message("fork failed");
273     sleep(1);
274   }
275 
276   if (child_pid == 0)
277   {
278     close(pfd[0]); // Close unused read end
279 
280     // Use default signal handlers in child
281     signal(SIGTERM, SIG_DFL);
282     signal(SIGINT,  SIG_DFL);
283     signal(SIGCHLD, SIG_DFL);
284     signal(SIGABRT, SIG_DFL);
285 
286     // Make this process it's own process group to be able to kill
287     // it and any childs(that hasn't changed group themself)
288     setpgid(0, 0);
289 
290     if (nocore)
291     {
292       struct rlimit corelim = { 0, 0 };
293       if (setrlimit (RLIMIT_CORE, &corelim) < 0)
294       {
295         message("setrlimit failed, errno=%d", errno);
296       }
297     }
298 
299     // Signal that child is ready
300     buf= 37;
301     if ((write(pfd[1], &buf, 1)) < 1)
302       die("Failed to signal that child is ready");
303     // Close write end
304     close(pfd[1]);
305 
306 #if defined(HAVE_ASAN) && defined(HAVE_TIRPC)
307 #include "asan_library_name.h"
308     std::string ld_preload = "LD_PRELOAD=";
309     if (strlen(asan_library_name) > 0) {
310       ld_preload.append(asan_library_name);
311       ld_preload.append(":");
312     }
313     ld_preload.append("/lib64/libtirpc.so");
314     putenv(strdup(ld_preload.c_str()));
315 #endif
316     if (execvp(child_argv[0], child_argv) < 0)
317       die("Failed to exec child");
318   }
319 
320   close(pfd[1]); // Close unused write end
321 
322   // Wait for child to signal it's ready
323   while ((read(pfd[0], &buf, 1)) < 1 )
324   {
325     //make sure that safe_process comes back even
326     //if any signal was raised
327     if (errno != EINTR)
328       die("Failed to read signal from child %d", errno);
329   }
330   if (buf != 37)
331     die("Didn't get 37 from pipe");
332   close(pfd[0]); // Close read end
333 
334   /* Monitor loop */
335   message("Started child: %d", static_cast<int>(child_pid));
336 
337   while(1)
338   {
339     // Check if parent is still alive
340     if (kill(parent_pid, 0) != 0)
341     {
342       print_message("Parent is not alive anymore, parent pid %d:",
343                     static_cast<int>(parent_pid));
344       kill_child();
345     }
346 
347     if(terminated)
348     {
349       kill_child();
350     }
351     if(aborted)
352     {
353       message("Got signal: %d, child_pid: %d",
354               static_cast<int>(terminated), static_cast<int>(child_pid));
355       abort_child();
356     }
357     sleep(1);
358   }
359   kill_child();
360 
361   return 1;
362 }
363 
364