1 /* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
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:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
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
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.
45 #include "dynamic-string.h"
47 #include "poll-loop.h"
48 #include "socket-util.h"
51 #define THIS_MODULE VLM_process
59 /* Modified by signal handler. */
64 /* Pipe used to signal child termination. */
68 static struct list all_processes = LIST_INITIALIZER(&all_processes);
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);
76 /* Initializes the process subsystem (if it is not already initialized). Calls
77 * exit() if initialization fails.
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. */
93 /* Create notification pipe. */
95 ofp_fatal(errno, "could not create pipe");
97 set_nonblocking(fds[0]);
98 set_nonblocking(fds[1]);
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");
111 process_escape_args(char **argv)
113 struct ds ds = DS_EMPTY_INITIALIZER;
115 for (argp = argv; *argp; argp++) {
116 const char *arg = *argp;
119 ds_put_char(&ds, ' ');
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, '\\');
127 ds_put_char(&ds, *p);
129 ds_put_char(&ds, '"');
131 ds_put_cstr(&ds, arg);
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.
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.
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. */
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,
160 if (VLOG_IS_DBG_ENABLED()) {
161 char *args = process_escape_args(argv);
162 VLOG_DBG("starting subprocess: %s", args);
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]);
173 block_sigchld(&oldsigs);
176 unblock_sigchld(&oldsigs);
177 VLOG_WARN("fork failed: %s", strerror(errno));
180 /* Running in parent process. */
184 p = xcalloc(1, sizeof *p);
186 slash = strrchr(argv[0], '/');
187 p->name = xstrdup(slash ? slash + 1 : argv[0]);
190 list_push_back(&all_processes, &p->node);
191 unblock_sigchld(&oldsigs);
196 /* Running in child process. */
197 int fd_max = get_max_fds();
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);
206 } else if (fd >= 3 && !is_member(fd, keep_fds, n_keep_fds)) {
210 execvp(argv[0], argv);
211 fprintf(stderr, "execvp(\"%s\") failed: %s\n",
212 argv[0], strerror(errno));
217 /* Destroys process 'p'. */
219 process_destroy(struct process *p)
224 block_sigchld(&oldsigs);
225 list_remove(&p->node);
226 unblock_sigchld(&oldsigs);
233 /* Sends signal 'signr' to process 'p'. Returns 0 if successful, otherwise a
234 * positive errno value. */
236 process_kill(const struct process *p, int signr)
238 return (p->exited ? ESRCH
239 : !kill(p->pid, signr) ? 0
243 /* Returns the pid of process 'p'. */
245 process_pid(const struct process *p)
250 /* Returns the name of process 'p' (the name passed to process_start() with any
251 * leading directories stripped). */
253 process_name(const struct process *p)
258 /* Returns true if process 'p' has exited, false otherwise. */
260 process_exited(struct process *p)
265 char buf[_POSIX_PIPE_BUF];
266 read(fds[0], buf, sizeof buf);
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
275 process_status(const struct process *p)
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,
290 retval = process_start(argv, keep_fds, n_keep_fds, null_fds, n_null_fds,
297 while (!process_exited(p)) {
301 *status = process_status(p);
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. */
311 process_status_msg(int status)
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)) {
317 const char *name = NULL;
318 #ifdef HAVE_STRSIGNAL
319 name = strsignal(WTERMSIG(status));
321 ds_put_format(&ds, "killed by signal %d", WTERMSIG(status));
323 ds_put_format(&ds, " (%s)", name);
326 if (WCOREDUMP(status)) {
327 ds_put_cstr(&ds, ", core dumped");
332 /* Causes the next call to poll_block() to wake up when process 'p' has
335 process_wait(struct process *p)
338 poll_immediate_wake();
340 poll_fd_wait(fds[0], POLLIN);
345 sigchld_handler(int signr UNUSED)
349 LIST_FOR_EACH (p, struct process, node, &all_processes) {
353 retval = waitpid(p->pid, &status, WNOHANG);
354 } while (retval == -1 && errno == EINTR);
355 if (retval == p->pid) {
358 } else if (retval < 0) {
359 /* XXX We want to log something but we're in a signal
366 write(fds[1], "", 1);
370 is_member(int x, const int *array, size_t n)
374 for (i = 0; i < n; i++) {
383 block_sigchld(sigset_t *oldsigs)
386 sigemptyset(&sigchld);
387 sigaddset(&sigchld, SIGCHLD);
388 if (sigprocmask(SIG_BLOCK, &sigchld, oldsigs)) {
389 ofp_fatal(errno, "sigprocmask");
394 unblock_sigchld(const sigset_t *oldsigs)
396 if (sigprocmask(SIG_SETMASK, oldsigs, NULL)) {
397 ofp_fatal(errno, "sigprocmask");
402 find_in_path(const char *name)
404 char *save_ptr = NULL;
408 if (strchr(name, '/') || !getenv("PATH")) {
409 return stat(name, &s) == 0;
412 path = xstrdup(getenv("PATH"));
413 for (dir = strtok_r(path, ":", &save_ptr); dir;
414 dir = strtok_r(NULL, ":", &save_ptr)) {
415 char *file = xasprintf("%s/%s", dir, name);
416 if (stat(file, &s) == 0) {