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