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