]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
e93af6a4 | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "process.h" | |
064af421 BP |
19 | #include <errno.h> |
20 | #include <fcntl.h> | |
21 | #include <signal.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
194db781 | 24 | #include <sys/resource.h> |
064af421 BP |
25 | #include <sys/stat.h> |
26 | #include <sys/wait.h> | |
27 | #include <unistd.h> | |
28 | #include "coverage.h" | |
3e8a2ad1 | 29 | #include "openvswitch/dynamic-string.h" |
52dc2ef4 | 30 | #include "fatal-signal.h" |
b19bab5b | 31 | #include "openvswitch/list.h" |
728a8b14 | 32 | #include "ovs-thread.h" |
064af421 | 33 | #include "poll-loop.h" |
b725cf02 | 34 | #include "signals.h" |
064af421 BP |
35 | #include "socket-util.h" |
36 | #include "util.h" | |
e6211adc | 37 | #include "openvswitch/vlog.h" |
064af421 | 38 | |
d98e6007 | 39 | VLOG_DEFINE_THIS_MODULE(process); |
5136ce49 | 40 | |
d76f09ea BP |
41 | COVERAGE_DEFINE(process_start); |
42 | ||
064af421 | 43 | struct process { |
ca6ba700 | 44 | struct ovs_list node; |
064af421 BP |
45 | char *name; |
46 | pid_t pid; | |
47 | ||
57d90319 BP |
48 | /* State. */ |
49 | bool exited; | |
50 | int status; | |
064af421 BP |
51 | }; |
52 | ||
53 | /* Pipe used to signal child termination. */ | |
54 | static int fds[2]; | |
55 | ||
56 | /* All processes. */ | |
55951e15 | 57 | static struct ovs_list all_processes = OVS_LIST_INITIALIZER(&all_processes); |
064af421 | 58 | |
67a4917b | 59 | static void sigchld_handler(int signr OVS_UNUSED); |
064af421 BP |
60 | |
61 | /* Initializes the process subsystem (if it is not already initialized). Calls | |
62 | * exit() if initialization fails. | |
63 | * | |
ff412c8c BP |
64 | * This function may not be called after creating any additional threads. |
65 | * | |
064af421 BP |
66 | * Calling this function is optional; it will be called automatically by |
67 | * process_start() if necessary. Calling it explicitly allows the client to | |
68 | * prevent the process from exiting at an unexpected time. */ | |
69 | void | |
70 | process_init(void) | |
71 | { | |
41064650 | 72 | #ifndef _WIN32 |
064af421 BP |
73 | static bool inited; |
74 | struct sigaction sa; | |
75 | ||
728a8b14 | 76 | assert_single_threaded(); |
064af421 BP |
77 | if (inited) { |
78 | return; | |
79 | } | |
80 | inited = true; | |
81 | ||
82 | /* Create notification pipe. */ | |
c0d95206 | 83 | xpipe_nonblocking(fds); |
064af421 BP |
84 | |
85 | /* Set up child termination signal handler. */ | |
86 | memset(&sa, 0, sizeof sa); | |
87 | sa.sa_handler = sigchld_handler; | |
88 | sigemptyset(&sa.sa_mask); | |
89 | sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; | |
279c9e03 | 90 | xsigaction(SIGCHLD, &sa, NULL); |
41064650 | 91 | #endif |
064af421 BP |
92 | } |
93 | ||
94 | char * | |
95 | process_escape_args(char **argv) | |
96 | { | |
97 | struct ds ds = DS_EMPTY_INITIALIZER; | |
98 | char **argp; | |
99 | for (argp = argv; *argp; argp++) { | |
100 | const char *arg = *argp; | |
101 | const char *p; | |
102 | if (argp != argv) { | |
103 | ds_put_char(&ds, ' '); | |
104 | } | |
7aee8e5a | 105 | if (arg[strcspn(arg, " \t\r\n\v\\\'\"")]) { |
064af421 BP |
106 | ds_put_char(&ds, '"'); |
107 | for (p = arg; *p; p++) { | |
108 | if (*p == '\\' || *p == '\"') { | |
109 | ds_put_char(&ds, '\\'); | |
110 | } | |
111 | ds_put_char(&ds, *p); | |
112 | } | |
113 | ds_put_char(&ds, '"'); | |
114 | } else { | |
115 | ds_put_cstr(&ds, arg); | |
116 | } | |
117 | } | |
118 | return ds_cstr(&ds); | |
119 | } | |
120 | ||
1fa39e1d BP |
121 | /* Prepare to start a process whose command-line arguments are given by the |
122 | * null-terminated 'argv' array. Returns 0 if successful, otherwise a | |
123 | * positive errno value. */ | |
124 | static int | |
125 | process_prestart(char **argv) | |
126 | { | |
127 | char *binary; | |
128 | ||
129 | process_init(); | |
130 | ||
131 | /* Log the process to be started. */ | |
132 | if (VLOG_IS_DBG_ENABLED()) { | |
133 | char *args = process_escape_args(argv); | |
134 | VLOG_DBG("starting subprocess: %s", args); | |
135 | free(args); | |
136 | } | |
137 | ||
138 | /* execvp() will search PATH too, but the error in that case is more | |
139 | * obscure, since it is only reported post-fork. */ | |
140 | binary = process_search_path(argv[0]); | |
141 | if (!binary) { | |
142 | VLOG_ERR("%s not found in PATH", argv[0]); | |
143 | return ENOENT; | |
144 | } | |
145 | free(binary); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | /* Creates and returns a new struct process with the specified 'name' and | |
57d90319 | 151 | * 'pid'. */ |
1fa39e1d BP |
152 | static struct process * |
153 | process_register(const char *name, pid_t pid) | |
154 | { | |
155 | struct process *p; | |
156 | const char *slash; | |
157 | ||
ec6fde61 | 158 | p = xzalloc(sizeof *p); |
1fa39e1d BP |
159 | p->pid = pid; |
160 | slash = strrchr(name, '/'); | |
161 | p->name = xstrdup(slash ? slash + 1 : name); | |
162 | p->exited = false; | |
163 | ||
417e7e66 | 164 | ovs_list_push_back(&all_processes, &p->node); |
1fa39e1d BP |
165 | |
166 | return p; | |
167 | } | |
168 | ||
4f57ad10 GS |
169 | #ifndef _WIN32 |
170 | static bool | |
171 | rlim_is_finite(rlim_t limit) | |
172 | { | |
173 | if (limit == RLIM_INFINITY) { | |
174 | return false; | |
175 | } | |
176 | ||
177 | #ifdef RLIM_SAVED_CUR /* FreeBSD 8.0 lacks RLIM_SAVED_CUR. */ | |
178 | if (limit == RLIM_SAVED_CUR) { | |
179 | return false; | |
180 | } | |
181 | #endif | |
182 | ||
183 | #ifdef RLIM_SAVED_MAX /* FreeBSD 8.0 lacks RLIM_SAVED_MAX. */ | |
184 | if (limit == RLIM_SAVED_MAX) { | |
185 | return false; | |
186 | } | |
187 | #endif | |
188 | ||
189 | return true; | |
190 | } | |
191 | ||
192 | /* Returns the maximum valid FD value, plus 1. */ | |
193 | static int | |
194 | get_max_fds(void) | |
195 | { | |
196 | static int max_fds; | |
197 | ||
198 | if (!max_fds) { | |
199 | struct rlimit r; | |
200 | if (!getrlimit(RLIMIT_NOFILE, &r) && rlim_is_finite(r.rlim_cur)) { | |
201 | max_fds = r.rlim_cur; | |
202 | } else { | |
203 | VLOG_WARN("failed to obtain fd limit, defaulting to 1024"); | |
204 | max_fds = 1024; | |
205 | } | |
206 | } | |
207 | ||
208 | return max_fds; | |
209 | } | |
210 | #endif /* _WIN32 */ | |
211 | ||
064af421 BP |
212 | /* Starts a subprocess with the arguments in the null-terminated argv[] array. |
213 | * argv[0] is used as the name of the process. Searches the PATH environment | |
214 | * variable to find the program to execute. | |
215 | * | |
ff412c8c BP |
216 | * This function may not be called after creating any additional threads. |
217 | * | |
064af421 | 218 | * All file descriptors are closed before executing the subprocess, except for |
e1208bc4 | 219 | * fds 0, 1, and 2. |
064af421 BP |
220 | * |
221 | * Returns 0 if successful, otherwise a positive errno value indicating the | |
222 | * error. If successful, '*pp' is assigned a new struct process that may be | |
223 | * used to query the process's status. On failure, '*pp' is set to NULL. */ | |
224 | int | |
e1208bc4 | 225 | process_start(char **argv, struct process **pp) |
064af421 | 226 | { |
41064650 | 227 | #ifndef _WIN32 |
064af421 | 228 | pid_t pid; |
1fa39e1d | 229 | int error; |
1481a755 | 230 | sigset_t prev_mask; |
064af421 | 231 | |
728a8b14 BP |
232 | assert_single_threaded(); |
233 | ||
064af421 | 234 | *pp = NULL; |
064af421 | 235 | COVERAGE_INC(process_start); |
1fa39e1d BP |
236 | error = process_prestart(argv); |
237 | if (error) { | |
238 | return error; | |
064af421 | 239 | } |
064af421 | 240 | |
1481a755 | 241 | fatal_signal_block(&prev_mask); |
064af421 BP |
242 | pid = fork(); |
243 | if (pid < 0) { | |
10a89ef0 | 244 | VLOG_WARN("fork failed: %s", ovs_strerror(errno)); |
1481a755 | 245 | error = errno; |
064af421 BP |
246 | } else if (pid) { |
247 | /* Running in parent process. */ | |
1fa39e1d | 248 | *pp = process_register(argv[0], pid); |
1481a755 | 249 | error = 0; |
064af421 BP |
250 | } else { |
251 | /* Running in child process. */ | |
252 | int fd_max = get_max_fds(); | |
253 | int fd; | |
254 | ||
52dc2ef4 | 255 | fatal_signal_fork(); |
e1208bc4 BP |
256 | for (fd = 3; fd < fd_max; fd++) { |
257 | close(fd); | |
fe81a298 | 258 | } |
1481a755 | 259 | xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL); |
064af421 BP |
260 | execvp(argv[0], argv); |
261 | fprintf(stderr, "execvp(\"%s\") failed: %s\n", | |
10a89ef0 | 262 | argv[0], ovs_strerror(errno)); |
064af421 BP |
263 | _exit(1); |
264 | } | |
1481a755 AA |
265 | xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL); |
266 | return error; | |
41064650 GS |
267 | #else |
268 | *pp = NULL; | |
269 | return ENOSYS; | |
270 | #endif | |
064af421 BP |
271 | } |
272 | ||
273 | /* Destroys process 'p'. */ | |
274 | void | |
275 | process_destroy(struct process *p) | |
276 | { | |
277 | if (p) { | |
417e7e66 | 278 | ovs_list_remove(&p->node); |
064af421 BP |
279 | free(p->name); |
280 | free(p); | |
281 | } | |
282 | } | |
283 | ||
284 | /* Sends signal 'signr' to process 'p'. Returns 0 if successful, otherwise a | |
285 | * positive errno value. */ | |
286 | int | |
287 | process_kill(const struct process *p, int signr) | |
288 | { | |
41064650 | 289 | #ifndef _WIN32 |
064af421 BP |
290 | return (p->exited ? ESRCH |
291 | : !kill(p->pid, signr) ? 0 | |
292 | : errno); | |
41064650 GS |
293 | #else |
294 | return ENOSYS; | |
295 | #endif | |
064af421 BP |
296 | } |
297 | ||
298 | /* Returns the pid of process 'p'. */ | |
299 | pid_t | |
300 | process_pid(const struct process *p) | |
301 | { | |
302 | return p->pid; | |
303 | } | |
304 | ||
305 | /* Returns the name of process 'p' (the name passed to process_start() with any | |
306 | * leading directories stripped). */ | |
307 | const char * | |
308 | process_name(const struct process *p) | |
309 | { | |
310 | return p->name; | |
311 | } | |
312 | ||
313 | /* Returns true if process 'p' has exited, false otherwise. */ | |
314 | bool | |
315 | process_exited(struct process *p) | |
316 | { | |
57d90319 | 317 | return p->exited; |
064af421 BP |
318 | } |
319 | ||
320 | /* Returns process 'p''s exit status, as reported by waitpid(2). | |
321 | * process_status(p) may be called only after process_exited(p) has returned | |
322 | * true. */ | |
323 | int | |
324 | process_status(const struct process *p) | |
325 | { | |
cb22974d | 326 | ovs_assert(p->exited); |
064af421 BP |
327 | return p->status; |
328 | } | |
329 | ||
064af421 BP |
330 | /* Given 'status', which is a process status in the form reported by waitpid(2) |
331 | * and returned by process_status(), returns a string describing how the | |
332 | * process terminated. The caller is responsible for freeing the string when | |
333 | * it is no longer needed. */ | |
334 | char * | |
335 | process_status_msg(int status) | |
336 | { | |
337 | struct ds ds = DS_EMPTY_INITIALIZER; | |
41064650 | 338 | #ifndef _WIN32 |
064af421 BP |
339 | if (WIFEXITED(status)) { |
340 | ds_put_format(&ds, "exit status %d", WEXITSTATUS(status)); | |
b725cf02 | 341 | } else if (WIFSIGNALED(status)) { |
eee8089c BP |
342 | char namebuf[SIGNAL_NAME_BUFSIZE]; |
343 | ||
344 | ds_put_format(&ds, "killed (%s)", | |
345 | signal_name(WTERMSIG(status), namebuf, sizeof namebuf)); | |
b725cf02 | 346 | } else if (WIFSTOPPED(status)) { |
eee8089c BP |
347 | char namebuf[SIGNAL_NAME_BUFSIZE]; |
348 | ||
349 | ds_put_format(&ds, "stopped (%s)", | |
350 | signal_name(WSTOPSIG(status), namebuf, sizeof namebuf)); | |
064af421 BP |
351 | } else { |
352 | ds_put_format(&ds, "terminated abnormally (%x)", status); | |
353 | } | |
354 | if (WCOREDUMP(status)) { | |
355 | ds_put_cstr(&ds, ", core dumped"); | |
356 | } | |
41064650 GS |
357 | #else |
358 | ds_put_cstr(&ds, "function not supported."); | |
359 | #endif | |
064af421 BP |
360 | return ds_cstr(&ds); |
361 | } | |
362 | ||
57d90319 BP |
363 | /* Executes periodic maintenance activities required by the process module. */ |
364 | void | |
365 | process_run(void) | |
366 | { | |
41064650 | 367 | #ifndef _WIN32 |
57d90319 BP |
368 | char buf[_POSIX_PIPE_BUF]; |
369 | ||
417e7e66 | 370 | if (!ovs_list_is_empty(&all_processes) && read(fds[0], buf, sizeof buf) > 0) { |
57d90319 BP |
371 | struct process *p; |
372 | ||
373 | LIST_FOR_EACH (p, node, &all_processes) { | |
374 | if (!p->exited) { | |
375 | int retval, status; | |
376 | do { | |
377 | retval = waitpid(p->pid, &status, WNOHANG); | |
378 | } while (retval == -1 && errno == EINTR); | |
379 | if (retval == p->pid) { | |
380 | p->exited = true; | |
381 | p->status = status; | |
382 | } else if (retval < 0) { | |
10a89ef0 | 383 | VLOG_WARN("waitpid: %s", ovs_strerror(errno)); |
57d90319 BP |
384 | p->exited = true; |
385 | p->status = -1; | |
386 | } | |
387 | } | |
388 | } | |
389 | } | |
41064650 | 390 | #endif |
57d90319 BP |
391 | } |
392 | ||
393 | ||
064af421 BP |
394 | /* Causes the next call to poll_block() to wake up when process 'p' has |
395 | * exited. */ | |
396 | void | |
397 | process_wait(struct process *p) | |
398 | { | |
41064650 | 399 | #ifndef _WIN32 |
064af421 BP |
400 | if (p->exited) { |
401 | poll_immediate_wake(); | |
402 | } else { | |
403 | poll_fd_wait(fds[0], POLLIN); | |
404 | } | |
41064650 GS |
405 | #else |
406 | OVS_NOT_REACHED(); | |
407 | #endif | |
064af421 BP |
408 | } |
409 | ||
410 | char * | |
411 | process_search_path(const char *name) | |
412 | { | |
413 | char *save_ptr = NULL; | |
414 | char *path, *dir; | |
415 | struct stat s; | |
416 | ||
417 | if (strchr(name, '/') || !getenv("PATH")) { | |
418 | return stat(name, &s) == 0 ? xstrdup(name) : NULL; | |
419 | } | |
420 | ||
421 | path = xstrdup(getenv("PATH")); | |
422 | for (dir = strtok_r(path, ":", &save_ptr); dir; | |
423 | dir = strtok_r(NULL, ":", &save_ptr)) { | |
424 | char *file = xasprintf("%s/%s", dir, name); | |
425 | if (stat(file, &s) == 0) { | |
426 | free(path); | |
427 | return file; | |
428 | } | |
429 | free(file); | |
430 | } | |
431 | free(path); | |
432 | return NULL; | |
433 | } | |
434 | \f | |
435 | static void | |
67a4917b | 436 | sigchld_handler(int signr OVS_UNUSED) |
064af421 | 437 | { |
18b9283b | 438 | ignore(write(fds[1], "", 1)); |
064af421 | 439 | } |