Improve handling of unexpected 'status' in process_status_msg().
[sliver-openvswitch.git] / lib / process.c
1 /* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include <config.h>
35 #include "process.h"
36 #include <assert.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
44 #include <unistd.h>
45 #include "dynamic-string.h"
46 #include "list.h"
47 #include "poll-loop.h"
48 #include "socket-util.h"
49 #include "util.h"
50
51 #define THIS_MODULE VLM_process
52 #include "vlog.h"
53
54 struct process {
55     struct list node;
56     char *name;
57     pid_t pid;
58
59     /* Modified by signal handler. */
60     volatile bool exited;
61     volatile int status;
62 };
63
64 /* Pipe used to signal child termination. */
65 static int fds[2];
66
67 /* All processes. */
68 static struct list all_processes = LIST_INITIALIZER(&all_processes);
69
70 static void block_sigchld(sigset_t *);
71 static void unblock_sigchld(const sigset_t *);
72 static void sigchld_handler(int signr UNUSED);
73 static bool is_member(int x, const int *array, size_t);
74 static bool find_in_path(const char *name);
75
76 /* Initializes the process subsystem (if it is not already initialized).  Calls
77  * exit() if initialization fails.
78  *
79  * Calling this function is optional; it will be called automatically by
80  * process_start() if necessary.  Calling it explicitly allows the client to
81  * prevent the process from exiting at an unexpected time. */
82 void
83 process_init(void)
84 {
85     static bool inited;
86     struct sigaction sa;
87
88     if (inited) {
89         return;
90     }
91     inited = true;
92
93     /* Create notification pipe. */
94     if (pipe(fds)) {
95         ofp_fatal(errno, "could not create pipe");
96     }
97     set_nonblocking(fds[0]);
98     set_nonblocking(fds[1]);
99
100     /* Set up child termination signal handler. */
101     memset(&sa, 0, sizeof sa);
102     sa.sa_handler = sigchld_handler;
103     sigemptyset(&sa.sa_mask);
104     sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
105     if (sigaction(SIGCHLD, &sa, NULL)) {
106         ofp_fatal(errno, "sigaction(SIGCHLD) failed");
107     }
108 }
109
110 char *
111 process_escape_args(char **argv)
112 {
113     struct ds ds = DS_EMPTY_INITIALIZER;
114     char **argp;
115     for (argp = argv; *argp; argp++) {
116         const char *arg = *argp;
117         const char *p;
118         if (argp != argv) {
119             ds_put_char(&ds, ' ');
120         }
121         if (arg[strcspn(arg, " \t\r\n\v\\")]) {
122             ds_put_char(&ds, '"');
123             for (p = arg; *p; p++) {
124                 if (*p == '\\' || *p == '\"') {
125                     ds_put_char(&ds, '\\');
126                 }
127                 ds_put_char(&ds, *p);
128             }
129             ds_put_char(&ds, '"');
130         } else {
131             ds_put_cstr(&ds, arg);
132         }
133     }
134     return ds_cstr(&ds);
135 }
136
137 /* Starts a subprocess with the arguments in the null-terminated argv[] array.
138  * argv[0] is used as the name of the process.  Searches the PATH environment
139  * variable to find the program to execute.
140  *
141  * All file descriptors are closed before executing the subprocess, except for
142  * fds 0, 1, and 2 and the 'n_keep_fds' fds listed in 'keep_fds'.  Also, any of
143  * the 'n_null_fds' fds listed in 'null_fds' are replaced by /dev/null.
144  *
145  * Returns 0 if successful, otherwise a positive errno value indicating the
146  * error.  If successful, '*pp' is assigned a new struct process that may be
147  * used to query the process's status.  On failure, '*pp' is set to NULL. */
148 int
149 process_start(char **argv,
150               const int keep_fds[], size_t n_keep_fds,
151               const int null_fds[], size_t n_null_fds,
152               struct process **pp)
153 {
154     sigset_t oldsigs;
155     pid_t pid;
156
157     *pp = NULL;
158     process_init();
159
160     if (VLOG_IS_DBG_ENABLED()) {
161         char *args = process_escape_args(argv);
162         VLOG_DBG("starting subprocess: %s", args);
163         free(args);
164     }
165
166     /* execvp() will search PATH too, but the error in that case is more
167      * obscure, since it is only reported post-fork. */
168     if (!find_in_path(argv[0])) {
169         VLOG_ERR("%s not found in PATH", argv[0]);
170         return ENOENT;
171     }
172
173     block_sigchld(&oldsigs);
174     pid = fork();
175     if (pid < 0) {
176         unblock_sigchld(&oldsigs);
177         VLOG_WARN("fork failed: %s", strerror(errno));
178         return errno;
179     } else if (pid) {
180         /* Running in parent process. */
181         struct process *p;
182         const char *slash;
183
184         p = xcalloc(1, sizeof *p);
185         p->pid = pid;
186         slash = strrchr(argv[0], '/');
187         p->name = xstrdup(slash ? slash + 1 : argv[0]);
188         p->exited = false;
189
190         list_push_back(&all_processes, &p->node);
191         unblock_sigchld(&oldsigs);
192
193         *pp = p;
194         return 0;
195     } else {
196         /* Running in child process. */
197         int fd_max = get_max_fds();
198         int fd;
199
200         unblock_sigchld(&oldsigs);
201         for (fd = 0; fd < fd_max; fd++) {
202             if (is_member(fd, null_fds, n_null_fds)) {
203                 int nullfd = open("/dev/null", O_RDWR);
204                 dup2(nullfd, fd);
205                 close(nullfd);
206             } else if (fd >= 3 && !is_member(fd, keep_fds, n_keep_fds)) {
207                 close(fd);
208             }
209         }
210         execvp(argv[0], argv);
211         fprintf(stderr, "execvp(\"%s\") failed: %s\n",
212                 argv[0], strerror(errno));
213         _exit(1);
214     }
215 }
216
217 /* Destroys process 'p'. */
218 void
219 process_destroy(struct process *p)
220 {
221     if (p) {
222         sigset_t oldsigs;
223
224         block_sigchld(&oldsigs);
225         list_remove(&p->node);
226         unblock_sigchld(&oldsigs);
227
228         free(p->name);
229         free(p);
230     }
231 }
232
233 /* Sends signal 'signr' to process 'p'.  Returns 0 if successful, otherwise a
234  * positive errno value. */
235 int
236 process_kill(const struct process *p, int signr)
237 {
238     return (p->exited ? ESRCH
239             : !kill(p->pid, signr) ? 0
240             : errno);
241 }
242
243 /* Returns the pid of process 'p'. */
244 pid_t
245 process_pid(const struct process *p)
246 {
247     return p->pid;
248 }
249
250 /* Returns the name of process 'p' (the name passed to process_start() with any
251  * leading directories stripped). */
252 const char *
253 process_name(const struct process *p)
254 {
255     return p->name;
256 }
257
258 /* Returns true if process 'p' has exited, false otherwise. */
259 bool
260 process_exited(struct process *p)
261 {
262     if (p->exited) {
263         return true;
264     } else {
265         char buf[_POSIX_PIPE_BUF];
266         read(fds[0], buf, sizeof buf);
267         return false;
268     }
269 }
270
271 /* Returns process 'p''s exit status, as reported by waitpid(2).
272  * process_status(p) may be called only after process_exited(p) has returned
273  * true. */
274 int
275 process_status(const struct process *p)
276 {
277     assert(p->exited);
278     return p->status;
279 }
280
281 int
282 process_run(char **argv,
283             const int keep_fds[], size_t n_keep_fds,
284             const int null_fds[], size_t n_null_fds,
285             int *status)
286 {
287     struct process *p;
288     int retval;
289
290     retval = process_start(argv, keep_fds, n_keep_fds, null_fds, n_null_fds,
291                            &p);
292     if (retval) {
293         *status = 0;
294         return retval;
295     }
296
297     while (!process_exited(p)) {
298         process_wait(p);
299         poll_block();
300     }
301     *status = process_status(p);
302     process_destroy(p);
303     return 0;
304 }
305
306 /* Given 'status', which is a process status in the form reported by waitpid(2)
307  * and returned by process_status(), returns a string describing how the
308  * process terminated.  The caller is responsible for freeing the string when
309  * it is no longer needed. */
310 char *
311 process_status_msg(int status)
312 {
313     struct ds ds = DS_EMPTY_INITIALIZER;
314     if (WIFEXITED(status)) {
315         ds_put_format(&ds, "exit status %d", WEXITSTATUS(status));
316     } else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
317         int signr = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
318         const char *name = NULL;
319 #ifdef HAVE_STRSIGNAL
320         name = strsignal(signr);
321 #endif
322         ds_put_format(&ds, "%s by signal %d",
323                       WIFSIGNALED(status) ? "killed" : "stopped", signr);
324         if (name) {
325             ds_put_format(&ds, " (%s)", name);
326         }
327     } else {
328         ds_put_format(&ds, "terminated abnormally (%x)", status);
329     }
330     if (WCOREDUMP(status)) {
331         ds_put_cstr(&ds, ", core dumped");
332     }
333     return ds_cstr(&ds);
334 }
335
336 /* Causes the next call to poll_block() to wake up when process 'p' has
337  * exited. */
338 void
339 process_wait(struct process *p)
340 {
341     if (p->exited) {
342         poll_immediate_wake();
343     } else {
344         poll_fd_wait(fds[0], POLLIN);
345     }
346 }
347 \f
348 static void
349 sigchld_handler(int signr UNUSED)
350 {
351     struct process *p;
352
353     LIST_FOR_EACH (p, struct process, node, &all_processes) {
354         if (!p->exited) {
355             int retval, status;
356             do {
357                 retval = waitpid(p->pid, &status, WNOHANG);
358             } while (retval == -1 && errno == EINTR);
359             if (retval == p->pid) {
360                 p->exited = true;
361                 p->status = status;
362             } else if (retval < 0) {
363                 /* XXX We want to log something but we're in a signal
364                  * handler. */
365                 p->exited = true;
366                 p->status = -1;
367             }
368         }
369     }
370     write(fds[1], "", 1);
371 }
372
373 static bool
374 is_member(int x, const int *array, size_t n)
375 {
376     size_t i;
377
378     for (i = 0; i < n; i++) {
379         if (array[i] == x) {
380             return true;
381         }
382     }
383     return false;
384 }
385
386 static void
387 block_sigchld(sigset_t *oldsigs)
388 {
389     sigset_t sigchld;
390     sigemptyset(&sigchld);
391     sigaddset(&sigchld, SIGCHLD);
392     if (sigprocmask(SIG_BLOCK, &sigchld, oldsigs)) {
393         ofp_fatal(errno, "sigprocmask");
394     }
395 }
396
397 static void
398 unblock_sigchld(const sigset_t *oldsigs)
399 {
400     if (sigprocmask(SIG_SETMASK, oldsigs, NULL)) {
401         ofp_fatal(errno, "sigprocmask");
402     }
403 }
404
405 static bool
406 find_in_path(const char *name)
407 {
408     char *save_ptr = NULL;
409     char *path, *dir;
410     struct stat s;
411
412     if (strchr(name, '/') || !getenv("PATH")) {
413         return stat(name, &s) == 0;
414     }
415
416     path = xstrdup(getenv("PATH"));
417     for (dir = strtok_r(path, ":", &save_ptr); dir;
418          dir = strtok_r(NULL, ":", &save_ptr)) {
419         char *file = xasprintf("%s/%s", dir, name);
420         if (stat(file, &s) == 0) {
421             free(file);
422             free(path);
423             return true;
424         }
425         free(file);
426     }
427     free(path);
428     return false;
429 }