/src/openvswitch/lib/process.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
3 | | * |
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: |
7 | | * |
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. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | #include "process.h" |
19 | | #include <errno.h> |
20 | | #include <fcntl.h> |
21 | | #include <signal.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <sys/resource.h> |
25 | | #include <sys/stat.h> |
26 | | #include <sys/wait.h> |
27 | | #include <unistd.h> |
28 | | #include "coverage.h" |
29 | | #include "openvswitch/dynamic-string.h" |
30 | | #include "fatal-signal.h" |
31 | | #include "openvswitch/list.h" |
32 | | #include "ovs-thread.h" |
33 | | #include "openvswitch/poll-loop.h" |
34 | | #include "signals.h" |
35 | | #include "socket-util.h" |
36 | | #include "timeval.h" |
37 | | #include "util.h" |
38 | | #include "openvswitch/vlog.h" |
39 | | |
40 | | VLOG_DEFINE_THIS_MODULE(process); |
41 | | |
42 | | COVERAGE_DEFINE(process_start); |
43 | | |
44 | | #ifdef __linux__ |
45 | | #define LINUX 1 |
46 | | #include <asm/param.h> |
47 | | #else |
48 | | #define LINUX 0 |
49 | | #endif |
50 | | |
51 | | struct process { |
52 | | struct ovs_list node; |
53 | | char *name; |
54 | | pid_t pid; |
55 | | |
56 | | /* State. */ |
57 | | bool exited; |
58 | | int status; |
59 | | }; |
60 | | |
61 | | struct raw_process_info { |
62 | | unsigned long int vsz; /* Virtual size, in kB. */ |
63 | | unsigned long int rss; /* Resident set size, in kB. */ |
64 | | long long int uptime; /* ms since started. */ |
65 | | long long int cputime; /* ms of CPU used during 'uptime'. */ |
66 | | pid_t ppid; /* Parent. */ |
67 | | int core_id; /* Core id last executed on. */ |
68 | | char name[18]; /* Name. */ |
69 | | }; |
70 | | |
71 | | /* Pipe used to signal child termination. */ |
72 | | static int fds[2]; |
73 | | |
74 | | /* All processes. */ |
75 | | static struct ovs_list all_processes = OVS_LIST_INITIALIZER(&all_processes); |
76 | | |
77 | | static void sigchld_handler(int signr OVS_UNUSED); |
78 | | |
79 | | /* Initializes the process subsystem (if it is not already initialized). Calls |
80 | | * exit() if initialization fails. |
81 | | * |
82 | | * This function may not be called after creating any additional threads. |
83 | | * |
84 | | * Calling this function is optional; it will be called automatically by |
85 | | * process_start() if necessary. Calling it explicitly allows the client to |
86 | | * prevent the process from exiting at an unexpected time. */ |
87 | | void |
88 | | process_init(void) |
89 | 0 | { |
90 | 0 | #ifndef _WIN32 |
91 | 0 | static bool inited; |
92 | 0 | struct sigaction sa; |
93 | |
|
94 | 0 | assert_single_threaded(); |
95 | 0 | if (inited) { |
96 | 0 | return; |
97 | 0 | } |
98 | 0 | inited = true; |
99 | | |
100 | | /* Create notification pipe. */ |
101 | 0 | xpipe_nonblocking(fds); |
102 | | |
103 | | /* Set up child termination signal handler. */ |
104 | 0 | memset(&sa, 0, sizeof sa); |
105 | 0 | sa.sa_handler = sigchld_handler; |
106 | 0 | sigemptyset(&sa.sa_mask); |
107 | 0 | sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; |
108 | 0 | xsigaction(SIGCHLD, &sa, NULL); |
109 | 0 | #endif |
110 | 0 | } |
111 | | |
112 | | char * |
113 | | process_escape_args(char **argv) |
114 | 0 | { |
115 | 0 | struct ds ds = DS_EMPTY_INITIALIZER; |
116 | 0 | char **argp; |
117 | 0 | for (argp = argv; *argp; argp++) { |
118 | 0 | const char *arg = *argp; |
119 | 0 | const char *p; |
120 | 0 | if (argp != argv) { |
121 | 0 | ds_put_char(&ds, ' '); |
122 | 0 | } |
123 | 0 | if (arg[strcspn(arg, " \t\r\n\v\\\'\"")]) { |
124 | 0 | ds_put_char(&ds, '"'); |
125 | 0 | for (p = arg; *p; p++) { |
126 | 0 | if (*p == '\\' || *p == '\"') { |
127 | 0 | ds_put_char(&ds, '\\'); |
128 | 0 | } |
129 | 0 | ds_put_char(&ds, *p); |
130 | 0 | } |
131 | 0 | ds_put_char(&ds, '"'); |
132 | 0 | } else { |
133 | 0 | ds_put_cstr(&ds, arg); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | return ds_cstr(&ds); |
137 | 0 | } |
138 | | |
139 | | /* Prepare to start a process whose command-line arguments are given by the |
140 | | * null-terminated 'argv' array. Returns 0 if successful, otherwise a |
141 | | * positive errno value. */ |
142 | | static int |
143 | | process_prestart(char **argv) |
144 | 0 | { |
145 | 0 | char *binary; |
146 | |
|
147 | 0 | process_init(); |
148 | | |
149 | | /* Log the process to be started. */ |
150 | 0 | if (VLOG_IS_DBG_ENABLED()) { |
151 | 0 | char *args = process_escape_args(argv); |
152 | 0 | VLOG_DBG("starting subprocess: %s", args); |
153 | 0 | free(args); |
154 | 0 | } |
155 | | |
156 | | /* execvp() will search PATH too, but the error in that case is more |
157 | | * obscure, since it is only reported post-fork. */ |
158 | 0 | binary = process_search_path(argv[0]); |
159 | 0 | if (!binary) { |
160 | 0 | VLOG_ERR("%s not found in PATH", argv[0]); |
161 | 0 | return ENOENT; |
162 | 0 | } |
163 | 0 | free(binary); |
164 | |
|
165 | 0 | return 0; |
166 | 0 | } |
167 | | |
168 | | /* Creates and returns a new struct process with the specified 'name' and |
169 | | * 'pid'. */ |
170 | | static struct process * |
171 | | process_register(const char *name, pid_t pid) |
172 | 0 | { |
173 | 0 | struct process *p; |
174 | 0 | const char *slash; |
175 | |
|
176 | 0 | p = xzalloc(sizeof *p); |
177 | 0 | p->pid = pid; |
178 | 0 | slash = strrchr(name, '/'); |
179 | 0 | p->name = xstrdup(slash ? slash + 1 : name); |
180 | 0 | p->exited = false; |
181 | |
|
182 | 0 | ovs_list_push_back(&all_processes, &p->node); |
183 | |
|
184 | 0 | return p; |
185 | 0 | } |
186 | | |
187 | | #ifndef _WIN32 |
188 | | static bool |
189 | | rlim_is_finite(rlim_t limit) |
190 | 0 | { |
191 | 0 | if (limit == RLIM_INFINITY) { |
192 | 0 | return false; |
193 | 0 | } |
194 | | |
195 | 0 | #ifdef RLIM_SAVED_CUR /* FreeBSD 8.0 lacks RLIM_SAVED_CUR. */ |
196 | 0 | if (limit == RLIM_SAVED_CUR) { |
197 | 0 | return false; |
198 | 0 | } |
199 | 0 | #endif |
200 | | |
201 | 0 | #ifdef RLIM_SAVED_MAX /* FreeBSD 8.0 lacks RLIM_SAVED_MAX. */ |
202 | 0 | if (limit == RLIM_SAVED_MAX) { |
203 | 0 | return false; |
204 | 0 | } |
205 | 0 | #endif |
206 | | |
207 | 0 | return true; |
208 | 0 | } |
209 | | |
210 | | /* Returns the maximum valid FD value, plus 1. */ |
211 | | static int |
212 | | get_max_fds(void) |
213 | 0 | { |
214 | 0 | static int max_fds; |
215 | |
|
216 | 0 | if (!max_fds) { |
217 | 0 | struct rlimit r; |
218 | 0 | if (!getrlimit(RLIMIT_NOFILE, &r) && rlim_is_finite(r.rlim_cur)) { |
219 | 0 | max_fds = r.rlim_cur; |
220 | 0 | } else { |
221 | 0 | VLOG_WARN("failed to obtain fd limit, defaulting to 1024"); |
222 | 0 | max_fds = 1024; |
223 | 0 | } |
224 | 0 | } |
225 | |
|
226 | 0 | return max_fds; |
227 | 0 | } |
228 | | #endif /* _WIN32 */ |
229 | | |
230 | | /* Starts a subprocess with the arguments in the null-terminated argv[] array. |
231 | | * argv[0] is used as the name of the process. Searches the PATH environment |
232 | | * variable to find the program to execute. |
233 | | * |
234 | | * This function may not be called after creating any additional threads. |
235 | | * |
236 | | * All file descriptors are closed before executing the subprocess, except for |
237 | | * fds 0, 1, and 2. |
238 | | * |
239 | | * Returns 0 if successful, otherwise a positive errno value indicating the |
240 | | * error. If successful, '*pp' is assigned a new struct process that may be |
241 | | * used to query the process's status. On failure, '*pp' is set to NULL. */ |
242 | | int |
243 | | process_start(char **argv, struct process **pp) |
244 | 0 | { |
245 | 0 | #ifndef _WIN32 |
246 | 0 | pid_t pid; |
247 | 0 | int error; |
248 | 0 | sigset_t prev_mask; |
249 | |
|
250 | 0 | assert_single_threaded(); |
251 | |
|
252 | 0 | *pp = NULL; |
253 | 0 | COVERAGE_INC(process_start); |
254 | 0 | error = process_prestart(argv); |
255 | 0 | if (error) { |
256 | 0 | return error; |
257 | 0 | } |
258 | | |
259 | 0 | fatal_signal_block(&prev_mask); |
260 | 0 | pid = fork(); |
261 | 0 | if (pid < 0) { |
262 | 0 | VLOG_WARN("fork failed: %s", ovs_strerror(errno)); |
263 | 0 | error = errno; |
264 | 0 | } else if (pid) { |
265 | | /* Running in parent process. */ |
266 | 0 | *pp = process_register(argv[0], pid); |
267 | 0 | error = 0; |
268 | 0 | } else { |
269 | | /* Running in child process. */ |
270 | 0 | int fd_max = get_max_fds(); |
271 | 0 | int fd; |
272 | |
|
273 | 0 | fatal_signal_fork(); |
274 | 0 | for (fd = 3; fd < fd_max; fd++) { |
275 | 0 | close(fd); |
276 | 0 | } |
277 | 0 | xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL); |
278 | 0 | execvp(argv[0], argv); |
279 | 0 | fprintf(stderr, "execvp(\"%s\") failed: %s\n", |
280 | 0 | argv[0], ovs_strerror(errno)); |
281 | 0 | _exit(1); |
282 | 0 | } |
283 | 0 | xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL); |
284 | 0 | return error; |
285 | | #else |
286 | | *pp = NULL; |
287 | | return ENOSYS; |
288 | | #endif |
289 | 0 | } |
290 | | |
291 | | /* Destroys process 'p'. */ |
292 | | void |
293 | | process_destroy(struct process *p) |
294 | 0 | { |
295 | 0 | if (p) { |
296 | 0 | ovs_list_remove(&p->node); |
297 | 0 | free(p->name); |
298 | 0 | free(p); |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | /* Sends signal 'signr' to process 'p'. Returns 0 if successful, otherwise a |
303 | | * positive errno value. */ |
304 | | int |
305 | | process_kill(const struct process *p, int signr) |
306 | 0 | { |
307 | 0 | #ifndef _WIN32 |
308 | 0 | return (p->exited ? ESRCH |
309 | 0 | : !kill(p->pid, signr) ? 0 |
310 | 0 | : errno); |
311 | | #else |
312 | | return ENOSYS; |
313 | | #endif |
314 | 0 | } |
315 | | |
316 | | /* Returns the pid of process 'p'. */ |
317 | | pid_t |
318 | | process_pid(const struct process *p) |
319 | 0 | { |
320 | 0 | return p->pid; |
321 | 0 | } |
322 | | |
323 | | /* Returns the name of process 'p' (the name passed to process_start() with any |
324 | | * leading directories stripped). */ |
325 | | const char * |
326 | | process_name(const struct process *p) |
327 | 0 | { |
328 | 0 | return p->name; |
329 | 0 | } |
330 | | |
331 | | /* Returns true if process 'p' has exited, false otherwise. */ |
332 | | bool |
333 | | process_exited(struct process *p) |
334 | 0 | { |
335 | 0 | return p->exited; |
336 | 0 | } |
337 | | |
338 | | /* Returns process 'p''s exit status, as reported by waitpid(2). |
339 | | * process_status(p) may be called only after process_exited(p) has returned |
340 | | * true. */ |
341 | | int |
342 | | process_status(const struct process *p) |
343 | 0 | { |
344 | 0 | ovs_assert(p->exited); |
345 | 0 | return p->status; |
346 | 0 | } |
347 | | |
348 | | int |
349 | | count_crashes(pid_t pid) |
350 | 0 | { |
351 | 0 | char file_name[128]; |
352 | 0 | const char *paren; |
353 | 0 | char line[128]; |
354 | 0 | int crashes = 0; |
355 | 0 | FILE *stream; |
356 | |
|
357 | 0 | ovs_assert(LINUX); |
358 | |
|
359 | 0 | sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid); |
360 | 0 | stream = fopen(file_name, "r"); |
361 | 0 | if (!stream) { |
362 | 0 | VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); |
363 | 0 | goto exit; |
364 | 0 | } |
365 | | |
366 | 0 | if (!fgets(line, sizeof line, stream)) { |
367 | 0 | VLOG_WARN_ONCE("%s: read failed (%s)", file_name, |
368 | 0 | feof(stream) ? "end of file" : ovs_strerror(errno)); |
369 | 0 | goto exit_close; |
370 | 0 | } |
371 | | |
372 | 0 | paren = strchr(line, '('); |
373 | 0 | if (paren) { |
374 | 0 | int x; |
375 | 0 | if (ovs_scan(paren + 1, "%d", &x)) { |
376 | 0 | crashes = x; |
377 | 0 | } |
378 | 0 | } |
379 | |
|
380 | 0 | exit_close: |
381 | 0 | fclose(stream); |
382 | 0 | exit: |
383 | 0 | return crashes; |
384 | 0 | } |
385 | | |
386 | | static unsigned long long int |
387 | | ticks_to_ms(unsigned long long int ticks) |
388 | 0 | { |
389 | 0 | ovs_assert(LINUX); |
390 | |
|
391 | 0 | #ifndef USER_HZ |
392 | 0 | #define USER_HZ 100 |
393 | 0 | #endif |
394 | |
|
395 | 0 | #if USER_HZ == 100 /* Common case. */ |
396 | 0 | return ticks * (1000 / USER_HZ); |
397 | | #else /* Alpha and some other architectures. */ |
398 | | double factor = 1000.0 / USER_HZ; |
399 | | return ticks * factor + 0.5; |
400 | | #endif |
401 | 0 | } |
402 | | |
403 | | static bool |
404 | | get_raw_process_info(pid_t pid, struct raw_process_info *raw) |
405 | 0 | { |
406 | 0 | unsigned long long int vsize, rss, start_time, utime, stime; |
407 | 0 | long long int start_msec; |
408 | 0 | unsigned long ppid; |
409 | 0 | char file_name[128]; |
410 | 0 | FILE *stream; |
411 | 0 | int n; |
412 | |
|
413 | 0 | ovs_assert(LINUX); |
414 | |
|
415 | 0 | sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid); |
416 | 0 | stream = fopen(file_name, "r"); |
417 | 0 | if (!stream) { |
418 | 0 | VLOG_ERR_ONCE("%s: open failed (%s)", |
419 | 0 | file_name, ovs_strerror(errno)); |
420 | 0 | return false; |
421 | 0 | } |
422 | | |
423 | 0 | n = fscanf(stream, |
424 | 0 | "%*d " /* (1. pid) */ |
425 | 0 | "(%17[^)]) " /* 2. process name */ |
426 | 0 | "%*c " /* (3. state) */ |
427 | 0 | "%lu " /* 4. ppid */ |
428 | 0 | "%*d " /* (5. pgid) */ |
429 | 0 | "%*d " /* (6. sid) */ |
430 | 0 | "%*d " /* (7. tty_nr) */ |
431 | 0 | "%*d " /* (8. tty_pgrp) */ |
432 | 0 | "%*u " /* (9. flags) */ |
433 | 0 | "%*u " /* (10. min_flt) */ |
434 | 0 | "%*u " /* (11. cmin_flt) */ |
435 | 0 | "%*u " /* (12. maj_flt) */ |
436 | 0 | "%*u " /* (13. cmaj_flt) */ |
437 | 0 | "%llu " /* 14. utime */ |
438 | 0 | "%llu " /* 15. stime */ |
439 | 0 | "%*d " /* (16. cutime) */ |
440 | 0 | "%*d " /* (17. cstime) */ |
441 | 0 | "%*d " /* (18. priority) */ |
442 | 0 | "%*d " /* (19. nice) */ |
443 | 0 | "%*d " /* (20. num_threads) */ |
444 | 0 | "%*d " /* (21. always 0) */ |
445 | 0 | "%llu " /* 22. start_time */ |
446 | 0 | "%llu " /* 23. vsize */ |
447 | 0 | "%llu " /* 24. rss */ |
448 | 0 | "%*u " /* (25. rsslim) */ |
449 | 0 | "%*u " /* (26. start_code) */ |
450 | 0 | "%*u " /* (27. end_code) */ |
451 | 0 | "%*u " /* (28. start_stack) */ |
452 | 0 | "%*u " /* (29. esp) */ |
453 | 0 | "%*u " /* (30. eip) */ |
454 | 0 | "%*u " /* (31. pending signals) */ |
455 | 0 | "%*u " /* (32. blocked signals) */ |
456 | 0 | "%*u " /* (33. ignored signals) */ |
457 | 0 | "%*u " /* (34. caught signals) */ |
458 | 0 | "%*u " /* (35. whcan) */ |
459 | 0 | "%*u " /* (36. always 0) */ |
460 | 0 | "%*u " /* (37. always 0) */ |
461 | 0 | "%*d " /* (38. exit_signal) */ |
462 | 0 | "%d " /* 39. task_cpu */ |
463 | | #if 0 |
464 | | /* These are here for documentation but #if'd out to save |
465 | | * actually parsing them from the stream for no benefit. */ |
466 | | "%*u " /* (40. rt_priority) */ |
467 | | "%*u " /* (41. policy) */ |
468 | | "%*llu " /* (42. blkio_ticks) */ |
469 | | "%*lu " /* (43. gtime) */ |
470 | | "%*ld" /* (44. cgtime) */ |
471 | | #endif |
472 | 0 | , raw->name, &ppid, &utime, &stime, &start_time, |
473 | 0 | &vsize, &rss, &raw->core_id); |
474 | 0 | fclose(stream); |
475 | 0 | if (n != 8) { |
476 | 0 | VLOG_ERR_ONCE("%s: fscanf failed", file_name); |
477 | 0 | return false; |
478 | 0 | } |
479 | | |
480 | 0 | start_msec = get_boot_time() + ticks_to_ms(start_time); |
481 | |
|
482 | 0 | raw->vsz = vsize / 1024; |
483 | 0 | raw->rss = rss * (get_page_size() / 1024); |
484 | 0 | raw->uptime = time_wall_msec() - start_msec; |
485 | 0 | raw->cputime = ticks_to_ms(utime + stime); |
486 | 0 | raw->ppid = ppid; |
487 | |
|
488 | 0 | return true; |
489 | 0 | } |
490 | | |
491 | | bool |
492 | | get_process_info(pid_t pid, struct process_info *pinfo) |
493 | 0 | { |
494 | 0 | struct raw_process_info child; |
495 | |
|
496 | 0 | ovs_assert(LINUX); |
497 | 0 | if (!get_raw_process_info(pid, &child)) { |
498 | 0 | return false; |
499 | 0 | } |
500 | | |
501 | 0 | ovs_strlcpy(pinfo->name, child.name, sizeof pinfo->name); |
502 | 0 | pinfo->vsz = child.vsz; |
503 | 0 | pinfo->rss = child.rss; |
504 | 0 | pinfo->booted = child.uptime; |
505 | 0 | pinfo->crashes = 0; |
506 | 0 | pinfo->uptime = child.uptime; |
507 | 0 | pinfo->cputime = child.cputime; |
508 | 0 | pinfo->core_id = child.core_id; |
509 | |
|
510 | 0 | if (child.ppid) { |
511 | 0 | struct raw_process_info parent; |
512 | |
|
513 | 0 | get_raw_process_info(child.ppid, &parent); |
514 | 0 | if (!strcmp(child.name, parent.name)) { |
515 | 0 | pinfo->booted = parent.uptime; |
516 | 0 | pinfo->crashes = count_crashes(child.ppid); |
517 | 0 | } |
518 | 0 | } |
519 | |
|
520 | 0 | return true; |
521 | 0 | } |
522 | | |
523 | | /* Given 'status', which is a process status in the form reported by waitpid(2) |
524 | | * and returned by process_status(), returns a string describing how the |
525 | | * process terminated. The caller is responsible for freeing the string when |
526 | | * it is no longer needed. */ |
527 | | char * |
528 | | process_status_msg(int status) |
529 | 0 | { |
530 | 0 | struct ds ds = DS_EMPTY_INITIALIZER; |
531 | 0 | #ifndef _WIN32 |
532 | 0 | if (WIFEXITED(status)) { |
533 | 0 | ds_put_format(&ds, "exit status %d", WEXITSTATUS(status)); |
534 | 0 | } else if (WIFSIGNALED(status)) { |
535 | 0 | char namebuf[SIGNAL_NAME_BUFSIZE]; |
536 | |
|
537 | 0 | ds_put_format(&ds, "killed (%s)", |
538 | 0 | signal_name(WTERMSIG(status), namebuf, sizeof namebuf)); |
539 | 0 | } else if (WIFSTOPPED(status)) { |
540 | 0 | char namebuf[SIGNAL_NAME_BUFSIZE]; |
541 | |
|
542 | 0 | ds_put_format(&ds, "stopped (%s)", |
543 | 0 | signal_name(WSTOPSIG(status), namebuf, sizeof namebuf)); |
544 | 0 | } else { |
545 | 0 | ds_put_format(&ds, "terminated abnormally (%x)", status); |
546 | 0 | } |
547 | 0 | if (WCOREDUMP(status)) { |
548 | 0 | ds_put_cstr(&ds, ", core dumped"); |
549 | 0 | } |
550 | | #else |
551 | | ds_put_cstr(&ds, "function not supported."); |
552 | | #endif |
553 | 0 | return ds_cstr(&ds); |
554 | 0 | } |
555 | | |
556 | | /* Executes periodic maintenance activities required by the process module. */ |
557 | | void |
558 | | process_run(void) |
559 | 0 | { |
560 | 0 | #ifndef _WIN32 |
561 | 0 | char buf[_POSIX_PIPE_BUF]; |
562 | |
|
563 | 0 | if (!ovs_list_is_empty(&all_processes) && read(fds[0], buf, sizeof buf) > 0) { |
564 | 0 | struct process *p; |
565 | |
|
566 | 0 | LIST_FOR_EACH (p, node, &all_processes) { |
567 | 0 | if (!p->exited) { |
568 | 0 | int retval, status; |
569 | 0 | do { |
570 | 0 | retval = waitpid(p->pid, &status, WNOHANG); |
571 | 0 | } while (retval == -1 && errno == EINTR); |
572 | 0 | if (retval == p->pid) { |
573 | 0 | p->exited = true; |
574 | 0 | p->status = status; |
575 | 0 | } else if (retval < 0) { |
576 | 0 | VLOG_WARN("waitpid: %s", ovs_strerror(errno)); |
577 | 0 | p->exited = true; |
578 | 0 | p->status = -1; |
579 | 0 | } |
580 | 0 | } |
581 | 0 | } |
582 | 0 | } |
583 | 0 | #endif |
584 | 0 | } |
585 | | |
586 | | /* Causes the next call to poll_block() to wake up when process 'p' has |
587 | | * exited. */ |
588 | | void |
589 | | process_wait(struct process *p) |
590 | 0 | { |
591 | 0 | #ifndef _WIN32 |
592 | 0 | if (p->exited) { |
593 | 0 | poll_immediate_wake(); |
594 | 0 | } else { |
595 | 0 | poll_fd_wait(fds[0], POLLIN); |
596 | 0 | } |
597 | | #else |
598 | | OVS_NOT_REACHED(); |
599 | | #endif |
600 | 0 | } |
601 | | |
602 | | char * |
603 | | process_search_path(const char *name) |
604 | 0 | { |
605 | 0 | char *save_ptr = NULL; |
606 | 0 | char *path, *dir; |
607 | 0 | struct stat s; |
608 | |
|
609 | 0 | if (strchr(name, '/') || !getenv("PATH")) { |
610 | 0 | return stat(name, &s) == 0 ? xstrdup(name) : NULL; |
611 | 0 | } |
612 | | |
613 | 0 | path = xstrdup(getenv("PATH")); |
614 | 0 | for (dir = strtok_r(path, ":", &save_ptr); dir; |
615 | 0 | dir = strtok_r(NULL, ":", &save_ptr)) { |
616 | 0 | char *file = xasprintf("%s/%s", dir, name); |
617 | 0 | if (stat(file, &s) == 0) { |
618 | 0 | free(path); |
619 | 0 | return file; |
620 | 0 | } |
621 | 0 | free(file); |
622 | 0 | } |
623 | 0 | free(path); |
624 | 0 | return NULL; |
625 | 0 | } |
626 | | |
627 | | static void |
628 | | sigchld_handler(int signr OVS_UNUSED) |
629 | 0 | { |
630 | 0 | ignore(write(fds[1], "", 1)); |
631 | 0 | } |