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