]>
Commit | Line | Data |
---|---|---|
a74aeac6 PR |
1 | /* |
2 | * kilall5.c Kill all processes except processes that have the | |
3 | * same session id, so that the shell that called us | |
4 | * won't be killed. Typically used in shutdown scripts. | |
5 | * | |
6 | * pidof.c Tries to get the pid of the process[es] named. | |
7 | * | |
8 | * Version: 2.86 30-Jul-2004 MvS | |
9 | * | |
10 | * Usage: killall5 [-][signal] | |
11 | * pidof [-s] [-o omitpid [-o omitpid]] program [program..] | |
12 | * | |
13 | * Authors: Miquel van Smoorenburg, miquels@cistron.nl | |
14 | * | |
15 | * Riku Meskanen, <mesrik@jyu.fi> | |
16 | * - return all running pids of given program name | |
17 | * - single shot '-s' option for backwards combatibility | |
18 | * - omit pid '-o' option and %PPID (parent pid metavariable) | |
19 | * - syslog() only if not a connected to controlling terminal | |
20 | * - swapped out programs pids are caught now | |
21 | * | |
40fc1ac0 DWF |
22 | * Werner Fink |
23 | * - make omit dynamic | |
24 | * - provide '-n' to skip stat(2) syscall on network based FS | |
25 | * | |
a74aeac6 PR |
26 | * This file is part of the sysvinit suite, |
27 | * Copyright (C) 1991-2004 Miquel van Smoorenburg. | |
28 | * | |
29 | * This program is free software; you can redistribute it and/or modify | |
30 | * it under the terms of the GNU General Public License as published by | |
31 | * the Free Software Foundation; either version 2 of the License, or | |
32 | * (at your option) any later version. | |
33 | * | |
34 | * This program is distributed in the hope that it will be useful, | |
35 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
36 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
37 | * GNU General Public License for more details. | |
38 | * | |
39 | * You should have received a copy of the GNU General Public License | |
40 | * along with this program; if not, write to the Free Software | |
41 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
42 | */ | |
40fc1ac0 | 43 | #include <dirent.h> |
a74aeac6 | 44 | #include <errno.h> |
40fc1ac0 | 45 | #include <getopt.h> |
64953d94 | 46 | #include <limits.h> |
40fc1ac0 DWF |
47 | #include <mntent.h> |
48 | #include <stdarg.h> | |
49 | #include <stdio.h> | |
a74aeac6 | 50 | #include <stdlib.h> |
a74aeac6 | 51 | #include <signal.h> |
40fc1ac0 | 52 | #include <string.h> |
a74aeac6 | 53 | #include <syslog.h> |
a74aeac6 | 54 | #include <sys/mman.h> |
40fc1ac0 DWF |
55 | #include <sys/param.h> |
56 | #include <sys/stat.h> | |
57 | #include <sys/types.h> | |
58 | #include <sys/wait.h> | |
59 | #include <unistd.h> | |
a74aeac6 PR |
60 | |
61 | char *Version = "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl"; | |
62 | ||
64953d94 DWF |
63 | #ifndef PATH_MAX |
64 | # ifdef MAXPATHLEN | |
65 | # define PATH_MAX MAXPATHLEN | |
66 | # else | |
67 | # define PATH_MAX 2048 | |
68 | # endif | |
69 | #endif | |
70 | ||
a74aeac6 | 71 | #define STATNAMELEN 15 |
40fc1ac0 | 72 | #define DO_NETFS 2 |
a74aeac6 PR |
73 | #define DO_STAT 1 |
74 | #define NO_STAT 0 | |
75 | ||
76 | /* Info about a process. */ | |
77 | typedef struct proc { | |
c5ae561e | 78 | char *pathname; /* full path to executable */ |
a74aeac6 PR |
79 | char *argv0; /* Name as found out from argv[0] */ |
80 | char *argv0base; /* `basename argv[1]` */ | |
81 | char *argv1; /* Name as found out from argv[1] */ | |
82 | char *argv1base; /* `basename argv[1]` */ | |
83 | char *statname; /* the statname without braces */ | |
84 | ino_t ino; /* Inode number */ | |
85 | dev_t dev; /* Device it is on */ | |
86 | pid_t pid; /* Process ID. */ | |
40fc1ac0 DWF |
87 | pid_t sid; /* Session ID. */ |
88 | char kernel; /* Kernel thread or zombie. */ | |
89 | char nfs; /* Name found on network FS. */ | |
a74aeac6 PR |
90 | struct proc *next; /* Pointer to next struct. */ |
91 | } PROC; | |
92 | ||
93 | /* pid queue */ | |
94 | ||
95 | typedef struct pidq { | |
96 | PROC *proc; | |
97 | struct pidq *next; | |
98 | } PIDQ; | |
99 | ||
100 | typedef struct { | |
101 | PIDQ *head; | |
102 | PIDQ *tail; | |
103 | PIDQ *next; | |
104 | } PIDQ_HEAD; | |
105 | ||
40fc1ac0 DWF |
106 | typedef struct _s_omit { |
107 | struct _s_omit *next; | |
108 | struct _s_omit *prev; | |
109 | pid_t pid; | |
110 | } OMIT; | |
111 | ||
112 | typedef struct _s_shadow | |
113 | { | |
114 | struct _s_shadow *next; | |
115 | struct _s_shadow *prev; | |
116 | size_t nlen; | |
117 | char * name; | |
118 | } SHADOW; | |
119 | ||
120 | typedef struct _s_nfs | |
121 | { | |
122 | struct _s_nfs *next; /* Pointer to next struct. */ | |
123 | struct _s_nfs *prev; /* Pointer to previous st. */ | |
124 | SHADOW *shadow; /* Pointer to shadows */ | |
40fc1ac0 | 125 | size_t nlen; |
58608f87 | 126 | char * name; |
40fc1ac0 DWF |
127 | } NFS; |
128 | ||
a74aeac6 PR |
129 | /* List of processes. */ |
130 | PROC *plist; | |
131 | ||
40fc1ac0 DWF |
132 | /* List of processes to omit. */ |
133 | OMIT *omit; | |
134 | ||
135 | /* List of NFS mountes partitions. */ | |
136 | NFS *nlist; | |
137 | ||
a74aeac6 PR |
138 | /* Did we stop all processes ? */ |
139 | int sent_sigstop; | |
140 | ||
141 | int scripts_too = 0; | |
142 | ||
143 | char *progname; /* the name of the running program */ | |
144 | #ifdef __GNUC__ | |
145 | __attribute__ ((format (printf, 2, 3))) | |
146 | #endif | |
147 | void nsyslog(int pri, char *fmt, ...); | |
148 | ||
40fc1ac0 DWF |
149 | #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) |
150 | # ifndef inline | |
151 | # define inline __inline__ | |
152 | # endif | |
153 | # ifndef restrict | |
154 | # define restrict __restrict__ | |
155 | # endif | |
156 | #endif | |
157 | #define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) | |
158 | ||
a74aeac6 PR |
159 | /* |
160 | * Malloc space, barf if out of memory. | |
161 | */ | |
40fc1ac0 DWF |
162 | #ifdef __GNUC__ |
163 | static void *xmalloc(size_t) __attribute__ ((__malloc__)); | |
164 | #endif | |
165 | static void *xmalloc(size_t bytes) | |
a74aeac6 PR |
166 | { |
167 | void *p; | |
168 | ||
169 | if ((p = malloc(bytes)) == NULL) { | |
170 | if (sent_sigstop) kill(-1, SIGCONT); | |
171 | nsyslog(LOG_ERR, "out of memory"); | |
172 | exit(1); | |
173 | } | |
174 | return p; | |
175 | } | |
176 | ||
40fc1ac0 DWF |
177 | #ifdef __GNUC__ |
178 | static inline void xmemalign(void **, size_t, size_t) __attribute__ ((__nonnull__ (1))); | |
179 | #endif | |
180 | static inline void xmemalign(void **memptr, size_t alignment, size_t size) | |
181 | { | |
182 | if ((posix_memalign(memptr, alignment, size)) < 0) { | |
183 | if (sent_sigstop) kill(-1, SIGCONT); | |
184 | nsyslog(LOG_ERR, "out of memory"); | |
185 | exit(1); | |
186 | } | |
187 | } | |
188 | ||
a74aeac6 PR |
189 | /* |
190 | * See if the proc filesystem is there. Mount if needed. | |
191 | */ | |
192 | int mount_proc(void) | |
193 | { | |
194 | struct stat st; | |
195 | char *args[] = { "mount", "-t", "proc", "proc", "/proc", 0 }; | |
196 | pid_t pid, rc; | |
197 | int wst; | |
198 | int did_mount = 0; | |
199 | ||
200 | /* Stat /proc/version to see if /proc is mounted. */ | |
201 | if (stat("/proc/version", &st) < 0 && errno == ENOENT) { | |
202 | ||
203 | /* It's not there, so mount it. */ | |
204 | if ((pid = fork()) < 0) { | |
205 | nsyslog(LOG_ERR, "cannot fork"); | |
206 | exit(1); | |
207 | } | |
208 | if (pid == 0) { | |
209 | /* Try a few mount binaries. */ | |
a74aeac6 | 210 | execv("/bin/mount", args); |
c5ae561e | 211 | execv("/sbin/mount", args); |
a74aeac6 PR |
212 | |
213 | /* Okay, I give up. */ | |
214 | nsyslog(LOG_ERR, "cannot execute mount"); | |
215 | exit(1); | |
216 | } | |
217 | /* Wait for child. */ | |
218 | while ((rc = wait(&wst)) != pid) | |
219 | if (rc < 0 && errno == ECHILD) | |
220 | break; | |
221 | if (rc != pid || WEXITSTATUS(wst) != 0) | |
222 | nsyslog(LOG_ERR, "mount returned non-zero exit status"); | |
223 | ||
224 | did_mount = 1; | |
225 | } | |
226 | ||
227 | /* See if mount succeeded. */ | |
228 | if (stat("/proc/version", &st) < 0) { | |
229 | if (errno == ENOENT) | |
230 | nsyslog(LOG_ERR, "/proc not mounted, failed to mount."); | |
231 | else | |
232 | nsyslog(LOG_ERR, "/proc unavailable."); | |
233 | exit(1); | |
234 | } | |
235 | ||
236 | return did_mount; | |
237 | } | |
238 | ||
40fc1ac0 DWF |
239 | static inline int isnetfs(const char * type) |
240 | { | |
241 | static const char* netfs[] = {"nfs", "nfs4", "smbfs", "cifs", "afs", "ncpfs", (char*)0}; | |
242 | int n; | |
243 | for (n = 0; netfs[n]; n++) { | |
244 | if (!strcasecmp(netfs[n], type)) | |
245 | return 1; | |
246 | } | |
247 | return 0; | |
248 | } | |
249 | ||
250 | /* | |
251 | * Remember all NFS typed partitions. | |
252 | */ | |
253 | void init_nfs(void) | |
254 | { | |
255 | struct stat st; | |
256 | struct mntent * ent; | |
257 | FILE * mnt; | |
21421926 | 258 | |
40fc1ac0 | 259 | nlist = (NFS*)0; |
21421926 | 260 | |
40fc1ac0 DWF |
261 | if (stat("/proc/version", &st) < 0) |
262 | return; | |
263 | if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) | |
264 | return; | |
21421926 | 265 | |
40fc1ac0 DWF |
266 | while ((ent = getmntent(mnt))) { |
267 | if (isnetfs(ent->mnt_type)) { | |
268 | size_t nlen = strlen(ent->mnt_dir); | |
269 | NFS *restrict p; | |
270 | xmemalign((void*)&p, sizeof(void*), alignof(NFS)+(nlen+1)); | |
271 | p->name = ((char*)p)+alignof(NFS); | |
272 | p->nlen = nlen; | |
273 | p->shadow = (SHADOW*)0; | |
21421926 | 274 | |
40fc1ac0 DWF |
275 | strcpy(p->name, ent->mnt_dir); |
276 | if (nlist) | |
277 | nlist->prev = p; | |
278 | p->next = nlist; | |
279 | p->prev = (NFS*)0; | |
280 | nlist = p; | |
281 | } | |
282 | } | |
283 | endmntent(mnt); | |
21421926 | 284 | |
40fc1ac0 DWF |
285 | if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) |
286 | return; | |
21421926 | 287 | |
40fc1ac0 DWF |
288 | while ((ent = getmntent(mnt))) { |
289 | NFS *p; | |
21421926 | 290 | |
40fc1ac0 DWF |
291 | for (p = nlist; p; p = p->next) { |
292 | SHADOW * restrict s; | |
293 | size_t nlen; | |
21421926 | 294 | |
40fc1ac0 DWF |
295 | if (strcmp(ent->mnt_dir, p->name) == 0) |
296 | continue; | |
297 | if (strncmp(ent->mnt_dir, p->name, p->nlen) != 0) | |
298 | continue; | |
21421926 | 299 | |
40fc1ac0 DWF |
300 | nlen = strlen(ent->mnt_dir); |
301 | xmemalign((void*)&s, sizeof(void*), alignof(SHADOW)+(nlen+1)); | |
302 | s->name = ((char*)s)+alignof(SHADOW); | |
303 | s->nlen = nlen; | |
21421926 | 304 | |
40fc1ac0 DWF |
305 | strcpy(s->name, ent->mnt_dir); |
306 | if (p->shadow) | |
307 | p->shadow->prev = s; | |
308 | s->next = p->shadow; | |
309 | s->prev = (SHADOW*)0; | |
310 | p->shadow = s; | |
311 | } | |
312 | } | |
313 | endmntent(mnt); | |
314 | } | |
315 | ||
316 | static void clear_shadow(SHADOW *restrict shadow) | |
317 | { | |
318 | SHADOW *s, *n, *l; | |
21421926 | 319 | |
40fc1ac0 DWF |
320 | n = shadow; |
321 | l = (SHADOW*)0; | |
322 | for (s = shadow; n; s = n) { | |
323 | l = s->prev; | |
324 | n = s->next; | |
325 | if (s == shadow) { | |
326 | if (n) n->prev = (SHADOW*)0; | |
327 | shadow = n; | |
328 | } else if (l) { | |
329 | if (n) n->prev = l; | |
330 | l->next = n; | |
331 | } | |
332 | free(s); | |
333 | } | |
334 | } | |
335 | ||
336 | static void clear_mnt(void) | |
337 | { | |
338 | NFS *p, *n, *l; | |
21421926 | 339 | |
40fc1ac0 DWF |
340 | n = nlist; |
341 | l = (NFS*)0; | |
342 | for (p = nlist; n; p = n) { | |
343 | l = p->prev; | |
344 | n = p->next; | |
345 | if (p == nlist) { | |
346 | if (n) n->prev = (NFS*)0; | |
347 | nlist = n; | |
348 | } else if (l) { | |
349 | if (n) n->prev = l; | |
350 | l->next = n; | |
351 | } | |
352 | if (p->shadow) | |
353 | clear_shadow(p->shadow); | |
354 | free(p); | |
355 | } | |
356 | } | |
357 | ||
358 | /* | |
58608f87 | 359 | * Check if path is a shadow off a NFS partition. |
40fc1ac0 DWF |
360 | */ |
361 | static int shadow(SHADOW *restrict this, const char *restrict name, const size_t nlen) | |
362 | { | |
363 | SHADOW *s; | |
21421926 | 364 | |
40fc1ac0 DWF |
365 | if (!this) |
366 | goto out; | |
367 | for (s = this; s; s = s->next) { | |
368 | if (nlen < s->nlen) | |
369 | continue; | |
370 | if (name[s->nlen] != '\0' && name[s->nlen] != '/') | |
371 | continue; | |
372 | if (strncmp(name, s->name, s->nlen) == 0) | |
373 | return 1; | |
374 | } | |
375 | out: | |
376 | return 0; | |
377 | } | |
378 | ||
7d4c7f43 PR |
379 | /* |
380 | * Get the maximal number of symlinks to follow. | |
381 | */ | |
382 | static int maxsymlinks(void) | |
383 | { | |
384 | int v = sysconf(_SC_SYMLOOP_MAX); | |
385 | #ifdef MAXSYMLINKS | |
386 | if (v == -1) | |
387 | return MAXSYMLINKS; | |
388 | #endif | |
389 | return v; | |
390 | } | |
391 | ||
40fc1ac0 DWF |
392 | /* |
393 | * Check path is located on a network based partition. | |
394 | */ | |
395 | int check4nfs(const char * path, char * real) | |
396 | { | |
397 | char buf[PATH_MAX+1]; | |
398 | const char *curr; | |
7d4c7f43 | 399 | int deep = maxsymlinks(); |
21421926 | 400 | |
40fc1ac0 | 401 | if (!nlist) return 0; |
21421926 | 402 | |
40fc1ac0 DWF |
403 | curr = path; |
404 | do { | |
405 | const char *prev; | |
406 | int len; | |
21421926 | 407 | |
40fc1ac0 DWF |
408 | if ((prev = strdupa(curr)) == NULL) { |
409 | nsyslog(LOG_ERR, "strdupa(): %s\n", strerror(errno)); | |
410 | return 0; | |
411 | } | |
21421926 | 412 | |
40fc1ac0 DWF |
413 | errno = 0; |
414 | if ((len = readlink(curr, buf, PATH_MAX)) < 0) | |
415 | break; | |
416 | buf[len] = '\0'; | |
21421926 | 417 | |
40fc1ac0 DWF |
418 | if (buf[0] != '/') { |
419 | const char *slash; | |
21421926 | 420 | |
40fc1ac0 DWF |
421 | if ((slash = strrchr(prev, '/'))) { |
422 | size_t off = slash - prev + 1; | |
21421926 | 423 | |
40fc1ac0 DWF |
424 | if (off + len > PATH_MAX) |
425 | len = PATH_MAX - off; | |
21421926 | 426 | |
40fc1ac0 DWF |
427 | memmove(&buf[off], &buf[0], len + 1); |
428 | memcpy(&buf[0], prev, off); | |
429 | } | |
430 | } | |
431 | curr = &buf[0]; | |
21421926 | 432 | |
40fc1ac0 | 433 | if (deep-- <= 0) return 0; |
21421926 | 434 | |
40fc1ac0 | 435 | } while (1); |
21421926 | 436 | |
40fc1ac0 | 437 | if (real) strcpy(real, curr); |
21421926 | 438 | |
40fc1ac0 DWF |
439 | if (errno == EINVAL) { |
440 | const size_t nlen = strlen(curr); | |
441 | NFS *p; | |
442 | for (p = nlist; p; p = p->next) { | |
443 | if (nlen < p->nlen) | |
444 | continue; | |
445 | if (curr[p->nlen] != '\0' && curr[p->nlen] != '/') | |
446 | continue; | |
447 | if (!strncmp(curr, p->name, p->nlen)) { | |
448 | if (shadow(p->shadow, curr, nlen)) | |
449 | continue; | |
450 | return 1; | |
451 | } | |
452 | } | |
453 | } | |
21421926 | 454 | |
40fc1ac0 DWF |
455 | return 0; |
456 | } | |
457 | ||
a74aeac6 PR |
458 | int readarg(FILE *fp, char *buf, int sz) |
459 | { | |
460 | int c = 0, f = 0; | |
461 | ||
462 | while (f < (sz-1) && (c = fgetc(fp)) != EOF && c) | |
463 | buf[f++] = c; | |
464 | buf[f] = 0; | |
465 | ||
466 | return (c == EOF && f == 0) ? c : f; | |
467 | } | |
468 | ||
469 | /* | |
470 | * Read the proc filesystem. | |
471 | * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse). | |
472 | */ | |
473 | int readproc(int do_stat) | |
474 | { | |
475 | DIR *dir; | |
476 | FILE *fp; | |
477 | PROC *p, *n; | |
478 | struct dirent *d; | |
479 | struct stat st; | |
9e8dafc6 DWF |
480 | char path[PATH_MAX+1]; |
481 | char buf[PATH_MAX+1]; | |
a74aeac6 PR |
482 | char *s, *q; |
483 | unsigned long startcode, endcode; | |
484 | int pid, f; | |
aef3f4ec | 485 | ssize_t len; |
a74aeac6 PR |
486 | |
487 | /* Open the /proc directory. */ | |
488 | if (chdir("/proc") == -1) { | |
489 | nsyslog(LOG_ERR, "chdir /proc failed"); | |
490 | return -1; | |
491 | } | |
492 | if ((dir = opendir(".")) == NULL) { | |
493 | nsyslog(LOG_ERR, "cannot opendir(/proc)"); | |
494 | return -1; | |
495 | } | |
496 | ||
497 | /* Free the already existing process list. */ | |
498 | n = plist; | |
499 | for (p = plist; n; p = n) { | |
500 | n = p->next; | |
501 | if (p->argv0) free(p->argv0); | |
502 | if (p->argv1) free(p->argv1); | |
40fc1ac0 | 503 | if (p->statname) free(p->statname); |
c5ae561e | 504 | free(p->pathname); |
a74aeac6 PR |
505 | free(p); |
506 | } | |
507 | plist = NULL; | |
508 | ||
509 | /* Walk through the directory. */ | |
510 | while ((d = readdir(dir)) != NULL) { | |
511 | ||
512 | /* See if this is a process */ | |
513 | if ((pid = atoi(d->d_name)) == 0) continue; | |
514 | ||
515 | /* Get a PROC struct . */ | |
516 | p = (PROC *)xmalloc(sizeof(PROC)); | |
517 | memset(p, 0, sizeof(PROC)); | |
518 | ||
519 | /* Open the status file. */ | |
520 | snprintf(path, sizeof(path), "%s/stat", d->d_name); | |
521 | ||
522 | /* Read SID & statname from it. */ | |
523 | if ((fp = fopen(path, "r")) != NULL) { | |
9e94f6aa DWF |
524 | size_t len; |
525 | ||
526 | len = fread(buf, sizeof(char), sizeof(buf)-1, fp); | |
527 | buf[len] = '\0'; | |
c5ae561e DWF |
528 | |
529 | if (buf[0] == '\0') { | |
530 | nsyslog(LOG_ERR, | |
531 | "can't read from %s\n", path); | |
532 | fclose(fp); | |
533 | free(p); | |
534 | continue; | |
535 | } | |
a74aeac6 PR |
536 | |
537 | /* See if name starts with '(' */ | |
538 | s = buf; | |
c5ae561e DWF |
539 | while (*s && *s != ' ') s++; |
540 | if (*s) s++; | |
a74aeac6 PR |
541 | if (*s == '(') { |
542 | /* Read program name. */ | |
543 | q = strrchr(buf, ')'); | |
544 | if (q == NULL) { | |
545 | p->sid = 0; | |
546 | nsyslog(LOG_ERR, | |
547 | "can't get program name from /proc/%s\n", | |
548 | path); | |
c5ae561e | 549 | fclose(fp); |
40fc1ac0 DWF |
550 | if (p->argv0) free(p->argv0); |
551 | if (p->argv1) free(p->argv1); | |
552 | if (p->statname) free(p->statname); | |
c5ae561e | 553 | free(p->pathname); |
a74aeac6 PR |
554 | free(p); |
555 | continue; | |
556 | } | |
557 | s++; | |
558 | } else { | |
559 | q = s; | |
c5ae561e | 560 | while (*q && *q != ' ') q++; |
a74aeac6 | 561 | } |
c5ae561e | 562 | if (*q) *q++ = 0; |
a74aeac6 PR |
563 | while (*q == ' ') q++; |
564 | p->statname = (char *)xmalloc(strlen(s)+1); | |
565 | strcpy(p->statname, s); | |
566 | ||
567 | /* Get session, startcode, endcode. */ | |
568 | startcode = endcode = 0; | |
569 | if (sscanf(q, "%*c %*d %*d %d %*d %*d %*u %*u " | |
570 | "%*u %*u %*u %*u %*u %*d %*d " | |
571 | "%*d %*d %*d %*d %*u %*u %*d " | |
572 | "%*u %lu %lu", | |
573 | &p->sid, &startcode, &endcode) != 3) { | |
574 | p->sid = 0; | |
575 | nsyslog(LOG_ERR, "can't read sid from %s\n", | |
576 | path); | |
c5ae561e | 577 | fclose(fp); |
40fc1ac0 DWF |
578 | if (p->argv0) free(p->argv0); |
579 | if (p->argv1) free(p->argv1); | |
580 | if (p->statname) free(p->statname); | |
c5ae561e | 581 | free(p->pathname); |
a74aeac6 PR |
582 | free(p); |
583 | continue; | |
584 | } | |
585 | if (startcode == 0 && endcode == 0) | |
586 | p->kernel = 1; | |
587 | fclose(fp); | |
588 | } else { | |
589 | /* Process disappeared.. */ | |
40fc1ac0 DWF |
590 | if (p->argv0) free(p->argv0); |
591 | if (p->argv1) free(p->argv1); | |
592 | if (p->statname) free(p->statname); | |
c5ae561e | 593 | free(p->pathname); |
a74aeac6 PR |
594 | free(p); |
595 | continue; | |
596 | } | |
597 | ||
598 | snprintf(path, sizeof(path), "%s/cmdline", d->d_name); | |
599 | if ((fp = fopen(path, "r")) != NULL) { | |
600 | ||
601 | /* Now read argv[0] */ | |
602 | f = readarg(fp, buf, sizeof(buf)); | |
603 | ||
604 | if (buf[0]) { | |
605 | /* Store the name into malloced memory. */ | |
606 | p->argv0 = (char *)xmalloc(f + 1); | |
607 | strcpy(p->argv0, buf); | |
608 | ||
609 | /* Get a pointer to the basename. */ | |
610 | p->argv0base = strrchr(p->argv0, '/'); | |
611 | if (p->argv0base != NULL) | |
612 | p->argv0base++; | |
613 | else | |
614 | p->argv0base = p->argv0; | |
615 | } | |
616 | ||
617 | /* And read argv[1] */ | |
618 | while ((f = readarg(fp, buf, sizeof(buf))) != EOF) | |
619 | if (buf[0] != '-') break; | |
620 | ||
621 | if (buf[0]) { | |
622 | /* Store the name into malloced memory. */ | |
623 | p->argv1 = (char *)xmalloc(f + 1); | |
624 | strcpy(p->argv1, buf); | |
625 | ||
626 | /* Get a pointer to the basename. */ | |
627 | p->argv1base = strrchr(p->argv1, '/'); | |
628 | if (p->argv1base != NULL) | |
629 | p->argv1base++; | |
630 | else | |
631 | p->argv1base = p->argv1; | |
632 | } | |
633 | ||
634 | fclose(fp); | |
635 | ||
636 | } else { | |
637 | /* Process disappeared.. */ | |
40fc1ac0 DWF |
638 | if (p->argv0) free(p->argv0); |
639 | if (p->argv1) free(p->argv1); | |
640 | if (p->statname) free(p->statname); | |
c5ae561e | 641 | free(p->pathname); |
a74aeac6 PR |
642 | free(p); |
643 | continue; | |
644 | } | |
645 | ||
646 | /* Try to stat the executable. */ | |
647 | snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name); | |
40fc1ac0 DWF |
648 | |
649 | p->nfs = 0; | |
650 | ||
651 | switch (do_stat) { | |
652 | case DO_NETFS: | |
653 | if ((p->nfs = check4nfs(path, buf))) | |
aef3f4ec | 654 | goto link; |
40fc1ac0 | 655 | case DO_STAT: |
7e088ce6 DWF |
656 | if (stat(path, &st) != 0) { |
657 | char * ptr; | |
658 | ||
659 | len = readlink(path, buf, PATH_MAX); | |
660 | if (len <= 0) | |
661 | break; | |
662 | buf[len] = '\0'; | |
663 | ||
664 | ptr = strstr(buf, " (deleted)"); | |
665 | if (!ptr) | |
666 | break; | |
667 | *ptr = '\0'; | |
668 | len -= strlen(" (deleted)"); | |
669 | ||
670 | if (stat(buf, &st) != 0) | |
671 | break; | |
672 | p->dev = st.st_dev; | |
673 | p->ino = st.st_ino; | |
674 | p->pathname = (char *)xmalloc(len + 1); | |
675 | memcpy(p->pathname, buf, len); | |
676 | p->pathname[len] = '\0'; | |
677 | ||
678 | /* All done */ | |
40fc1ac0 | 679 | break; |
7e088ce6 DWF |
680 | } |
681 | ||
a74aeac6 PR |
682 | p->dev = st.st_dev; |
683 | p->ino = st.st_ino; | |
aef3f4ec DWF |
684 | |
685 | /* Fall through */ | |
40fc1ac0 | 686 | default: |
aef3f4ec DWF |
687 | link: |
688 | len = readlink(path, buf, PATH_MAX); | |
689 | if (len > 0) { | |
690 | p->pathname = (char *)xmalloc(len + 1); | |
691 | memcpy(p->pathname, buf, len); | |
692 | p->pathname[len] = '\0'; | |
693 | } | |
40fc1ac0 | 694 | break; |
a74aeac6 PR |
695 | } |
696 | ||
697 | /* Link it into the list. */ | |
698 | p->next = plist; | |
699 | plist = p; | |
700 | p->pid = pid; | |
701 | } | |
702 | closedir(dir); | |
703 | ||
704 | /* Done. */ | |
705 | return 0; | |
706 | } | |
707 | ||
708 | PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q) | |
709 | { | |
710 | q->head = q->next = q->tail = NULL; | |
711 | return q; | |
712 | } | |
713 | ||
714 | int empty_q(PIDQ_HEAD *q) | |
715 | { | |
716 | return (q->head == NULL); | |
717 | } | |
718 | ||
719 | int add_pid_to_q(PIDQ_HEAD *q, PROC *p) | |
720 | { | |
721 | PIDQ *tmp; | |
722 | ||
723 | tmp = (PIDQ *)xmalloc(sizeof(PIDQ)); | |
724 | ||
725 | tmp->proc = p; | |
726 | tmp->next = NULL; | |
727 | ||
728 | if (empty_q(q)) { | |
729 | q->head = tmp; | |
730 | q->tail = tmp; | |
731 | } else { | |
732 | q->tail->next = tmp; | |
733 | q->tail = tmp; | |
734 | } | |
735 | return 0; | |
736 | } | |
737 | ||
738 | PROC *get_next_from_pid_q(PIDQ_HEAD *q) | |
739 | { | |
740 | PROC *p; | |
741 | PIDQ *tmp = q->head; | |
742 | ||
743 | if (!empty_q(q)) { | |
744 | p = q->head->proc; | |
745 | q->head = tmp->next; | |
746 | free(tmp); | |
747 | return p; | |
748 | } | |
749 | ||
750 | return NULL; | |
751 | } | |
752 | ||
753 | /* Try to get the process ID of a given process. */ | |
754 | PIDQ_HEAD *pidof(char *prog) | |
755 | { | |
756 | PROC *p; | |
757 | PIDQ_HEAD *q; | |
758 | struct stat st; | |
759 | char *s; | |
40fc1ac0 | 760 | int nfs = 0; |
a74aeac6 PR |
761 | int dostat = 0; |
762 | int foundone = 0; | |
763 | int ok = 0; | |
aef3f4ec | 764 | const int root = (getuid() == 0); |
40fc1ac0 | 765 | char real[PATH_MAX+1]; |
a74aeac6 PR |
766 | |
767 | if (! prog) | |
768 | return NULL; | |
769 | ||
40fc1ac0 DWF |
770 | /* Try to stat the executable. */ |
771 | if (prog[0] == '/') { | |
772 | memset(&real[0], 0, sizeof(real)); | |
773 | ||
774 | if (check4nfs(prog, real)) | |
775 | nfs++; | |
776 | ||
777 | if (real[0] != '\0') | |
778 | prog = &real[0]; /* Binary located on network FS. */ | |
779 | ||
780 | if ((nfs == 0) && (stat(prog, &st) == 0)) | |
781 | dostat++; /* Binary located on a local FS. */ | |
782 | } | |
783 | ||
a74aeac6 PR |
784 | /* Get basename of program. */ |
785 | if ((s = strrchr(prog, '/')) == NULL) | |
786 | s = prog; | |
787 | else | |
788 | s++; | |
789 | ||
790 | if (! *s) | |
791 | return NULL; | |
792 | ||
793 | q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD)); | |
794 | q = init_pid_q(q); | |
795 | ||
a74aeac6 | 796 | /* First try to find a match based on dev/ino pair. */ |
40fc1ac0 | 797 | if (dostat && !nfs) { |
a74aeac6 | 798 | for (p = plist; p; p = p->next) { |
40fc1ac0 DWF |
799 | if (p->nfs) |
800 | continue; | |
a74aeac6 PR |
801 | if (p->dev == st.st_dev && p->ino == st.st_ino) { |
802 | add_pid_to_q(q, p); | |
803 | foundone++; | |
804 | } | |
805 | } | |
806 | } | |
807 | ||
40fc1ac0 DWF |
808 | /* Second try to find a match based on full path name on |
809 | * network FS located binaries */ | |
810 | if (!foundone && nfs) { | |
811 | for (p = plist; p; p = p->next) { | |
aef3f4ec DWF |
812 | if (!p->pathname) |
813 | continue; | |
accef4ed DWF |
814 | if (!p->nfs) |
815 | continue; | |
aef3f4ec | 816 | if (strcmp(prog, p->pathname) != 0) |
40fc1ac0 DWF |
817 | continue; |
818 | add_pid_to_q(q, p); | |
819 | foundone++; | |
820 | } | |
821 | } | |
822 | ||
a74aeac6 PR |
823 | /* If we didn't find a match based on dev/ino, try the name. */ |
824 | if (!foundone) for (p = plist; p; p = p->next) { | |
c5ae561e | 825 | if (prog[0] == '/') { |
aef3f4ec DWF |
826 | if (!p->pathname) { |
827 | if (root) | |
828 | continue; | |
829 | goto fallback; | |
830 | } | |
c5ae561e DWF |
831 | if (strcmp(prog, p->pathname)) { |
832 | int len = strlen(prog); | |
833 | if (strncmp(prog, p->pathname, len)) | |
aef3f4ec DWF |
834 | { |
835 | if (scripts_too) | |
836 | goto fallback; | |
c5ae561e | 837 | continue; |
aef3f4ec | 838 | } |
c5ae561e | 839 | if (strcmp(" (deleted)", p->pathname + len)) |
aef3f4ec DWF |
840 | { |
841 | if (scripts_too) | |
842 | goto fallback; | |
c5ae561e | 843 | continue; |
aef3f4ec | 844 | } |
c5ae561e DWF |
845 | } |
846 | add_pid_to_q(q, p); | |
847 | continue; | |
848 | } | |
849 | ||
aef3f4ec | 850 | fallback: |
a74aeac6 PR |
851 | ok = 0; |
852 | ||
853 | /* matching nonmatching | |
854 | * proc name prog name prog name | |
855 | * --- ----------- ------------ | |
856 | * b b, p/b, q/b | |
857 | * p/b b, p/b q/b | |
858 | * | |
859 | * Algorithm: Match if: | |
860 | * cmd = arg | |
861 | * or cmd = base(arg) | |
862 | * or base(cmd) = arg | |
863 | * | |
864 | * Specifically, do not match just because base(cmd) = base(arg) | |
865 | * as was done in earlier versions of this program, since this | |
866 | * allows /aaa/foo to match /bbb/foo . | |
867 | */ | |
868 | ok |= | |
869 | (p->argv0 && strcmp(p->argv0, prog) == 0) | |
870 | || (p->argv0 && s != prog && strcmp(p->argv0, s) == 0) | |
871 | || (p->argv0base && strcmp(p->argv0base, prog) == 0); | |
872 | ||
873 | /* For scripts, compare argv[1] as well. */ | |
874 | if ( | |
875 | scripts_too && p->statname && p->argv1base | |
876 | && !strncmp(p->statname, p->argv1base, STATNAMELEN) | |
877 | ) { | |
878 | ok |= | |
879 | (p->argv1 && strcmp(p->argv1, prog) == 0) | |
880 | || (p->argv1 && s != prog && strcmp(p->argv1, s) == 0) | |
881 | || (p->argv1base && strcmp(p->argv1base, prog) == 0); | |
882 | } | |
883 | ||
884 | /* | |
885 | * if we have a space in argv0, process probably | |
886 | * used setproctitle so try statname. | |
887 | */ | |
888 | if (strlen(s) <= STATNAMELEN && | |
889 | (p->argv0 == NULL || | |
890 | p->argv0[0] == 0 || | |
891 | strchr(p->argv0, ' '))) { | |
892 | ok |= (strcmp(p->statname, s) == 0); | |
893 | } | |
40fc1ac0 DWF |
894 | |
895 | /* | |
896 | * if we have a `-' as the first character, process | |
897 | * probably used as a login shell | |
898 | */ | |
899 | if (strlen(s) <= STATNAMELEN && | |
900 | p->argv1 == NULL && | |
901 | (p->argv0 != NULL && | |
902 | p->argv0[0] == '-')) { | |
903 | ok |= (strcmp(p->statname, s) == 0); | |
904 | } | |
905 | ||
a74aeac6 PR |
906 | if (ok) add_pid_to_q(q, p); |
907 | } | |
908 | ||
40fc1ac0 | 909 | return q; |
a74aeac6 PR |
910 | } |
911 | ||
912 | /* Give usage message and exit. */ | |
913 | void usage(void) | |
914 | { | |
915 | nsyslog(LOG_ERR, "only one argument, a signal number, allowed"); | |
916 | closelog(); | |
917 | exit(1); | |
918 | } | |
919 | ||
920 | /* write to syslog file if not open terminal */ | |
921 | #ifdef __GNUC__ | |
922 | __attribute__ ((format (printf, 2, 3))) | |
923 | #endif | |
924 | void nsyslog(int pri, char *fmt, ...) | |
925 | { | |
926 | va_list args; | |
927 | ||
928 | va_start(args, fmt); | |
929 | ||
930 | if (ttyname(0) == NULL) { | |
931 | vsyslog(pri, fmt, args); | |
932 | } else { | |
933 | fprintf(stderr, "%s: ",progname); | |
934 | vfprintf(stderr, fmt, args); | |
935 | fprintf(stderr, "\n"); | |
936 | } | |
937 | ||
938 | va_end(args); | |
939 | } | |
940 | ||
941 | #define PIDOF_SINGLE 0x01 | |
942 | #define PIDOF_OMIT 0x02 | |
40fc1ac0 | 943 | #define PIDOF_NETFS 0x04 |
a74aeac6 PR |
944 | |
945 | /* | |
946 | * Pidof functionality. | |
947 | */ | |
948 | int main_pidof(int argc, char **argv) | |
949 | { | |
950 | PIDQ_HEAD *q; | |
951 | PROC *p; | |
40fc1ac0 | 952 | char *token, *here; |
a74aeac6 PR |
953 | int f; |
954 | int first = 1; | |
40fc1ac0 | 955 | int opt, flags = 0; |
a74aeac6 PR |
956 | int chroot_check = 0; |
957 | struct stat st; | |
958 | char tmp[512]; | |
959 | ||
40fc1ac0 DWF |
960 | omit = (OMIT*)0; |
961 | nlist = (NFS*)0; | |
a74aeac6 PR |
962 | opterr = 0; |
963 | ||
40fc1ac0 DWF |
964 | if ((token = getenv("PIDOF_NETFS")) && (strcmp(token,"no") != 0)) |
965 | flags |= PIDOF_NETFS; | |
966 | ||
967 | while ((opt = getopt(argc,argv,"hco:sxn")) != EOF) switch (opt) { | |
a74aeac6 PR |
968 | case '?': |
969 | nsyslog(LOG_ERR,"invalid options on command line!\n"); | |
970 | closelog(); | |
971 | exit(1); | |
972 | case 'c': | |
973 | if (geteuid() == 0) chroot_check = 1; | |
974 | break; | |
975 | case 'o': | |
40fc1ac0 DWF |
976 | here = optarg; |
977 | while ((token = strsep(&here, ",;:"))) { | |
978 | OMIT *restrict optr; | |
979 | pid_t opid; | |
980 | ||
981 | if (strcmp("%PPID", token) == 0) | |
982 | opid = getppid(); | |
983 | else | |
984 | opid = (pid_t)atoi(token); | |
985 | ||
986 | if (opid < 1) { | |
987 | nsyslog(LOG_ERR, | |
988 | "illegal omit pid value " | |
989 | "(%s)!\n", token); | |
990 | continue; | |
991 | } | |
992 | xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); | |
993 | optr->next = omit; | |
994 | optr->prev = (OMIT*)0; | |
995 | optr->pid = opid; | |
996 | omit = optr; | |
a74aeac6 | 997 | } |
a74aeac6 PR |
998 | flags |= PIDOF_OMIT; |
999 | break; | |
1000 | case 's': | |
1001 | flags |= PIDOF_SINGLE; | |
1002 | break; | |
1003 | case 'x': | |
1004 | scripts_too++; | |
1005 | break; | |
40fc1ac0 DWF |
1006 | case 'n': |
1007 | flags |= PIDOF_NETFS; | |
1008 | break; | |
a74aeac6 PR |
1009 | default: |
1010 | /* Nothing */ | |
1011 | break; | |
1012 | } | |
1013 | argc -= optind; | |
1014 | argv += optind; | |
1015 | ||
1016 | /* Check if we are in a chroot */ | |
1017 | if (chroot_check) { | |
1018 | snprintf(tmp, 512, "/proc/%d/root", getpid()); | |
1019 | if (stat(tmp, &st) < 0) { | |
1020 | nsyslog(LOG_ERR, "stat failed for %s!\n", tmp); | |
1021 | closelog(); | |
1022 | exit(1); | |
1023 | } | |
1024 | } | |
1025 | ||
40fc1ac0 DWF |
1026 | if (flags & PIDOF_NETFS) |
1027 | init_nfs(); /* Which network based FS are online? */ | |
1028 | ||
a74aeac6 | 1029 | /* Print out process-ID's one by one. */ |
40fc1ac0 DWF |
1030 | readproc((flags & PIDOF_NETFS) ? DO_NETFS : DO_STAT); |
1031 | ||
a74aeac6 PR |
1032 | for(f = 0; f < argc; f++) { |
1033 | if ((q = pidof(argv[f])) != NULL) { | |
40fc1ac0 | 1034 | pid_t spid = 0; |
a74aeac6 | 1035 | while ((p = get_next_from_pid_q(q))) { |
40fc1ac0 DWF |
1036 | if ((flags & PIDOF_OMIT) && omit) { |
1037 | OMIT * optr; | |
1038 | for (optr = omit; optr; optr = optr->next) { | |
1039 | if (optr->pid == p->pid) | |
a74aeac6 | 1040 | break; |
40fc1ac0 DWF |
1041 | } |
1042 | ||
a74aeac6 PR |
1043 | /* |
1044 | * On a match, continue with | |
1045 | * the for loop above. | |
1046 | */ | |
40fc1ac0 | 1047 | if (optr) |
a74aeac6 PR |
1048 | continue; |
1049 | } | |
1050 | if (flags & PIDOF_SINGLE) { | |
1051 | if (spid) | |
1052 | continue; | |
1053 | else | |
1054 | spid = 1; | |
1055 | } | |
1056 | if (chroot_check) { | |
1057 | struct stat st2; | |
1058 | snprintf(tmp, 512, "/proc/%d/root", | |
1059 | p->pid); | |
1060 | if (stat(tmp, &st2) < 0 || | |
1061 | st.st_dev != st2.st_dev || | |
1062 | st.st_ino != st2.st_ino) { | |
1063 | continue; | |
1064 | } | |
1065 | } | |
1066 | if (!first) | |
1067 | printf(" "); | |
1068 | printf("%d", p->pid); | |
1069 | first = 0; | |
1070 | } | |
1071 | } | |
1072 | } | |
1073 | if (!first) | |
1074 | printf("\n"); | |
40fc1ac0 DWF |
1075 | |
1076 | clear_mnt(); | |
1077 | ||
a74aeac6 PR |
1078 | closelog(); |
1079 | return(first ? 1 : 0); | |
1080 | } | |
1081 | ||
a74aeac6 PR |
1082 | /* Main for either killall or pidof. */ |
1083 | int main(int argc, char **argv) | |
1084 | { | |
1085 | PROC *p; | |
1086 | int pid, sid = -1; | |
a74aeac6 | 1087 | int sig = SIGKILL; |
40fc1ac0 | 1088 | int c; |
a74aeac6 PR |
1089 | |
1090 | /* return non-zero if no process was killed */ | |
1091 | int retval = 2; | |
1092 | ||
1093 | /* Get program name. */ | |
1094 | if ((progname = strrchr(argv[0], '/')) == NULL) | |
1095 | progname = argv[0]; | |
1096 | else | |
1097 | progname++; | |
1098 | ||
1099 | /* Now connect to syslog. */ | |
1100 | openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON); | |
1101 | ||
1102 | /* Were we called as 'pidof' ? */ | |
1103 | if (strcmp(progname, "pidof") == 0) | |
1104 | return main_pidof(argc, argv); | |
1105 | ||
1106 | /* Right, so we are "killall". */ | |
40fc1ac0 | 1107 | omit = (OMIT*)0; |
a74aeac6 PR |
1108 | |
1109 | if (argc > 1) { | |
40fc1ac0 DWF |
1110 | for (c = 1; c < argc; c++) { |
1111 | if (argv[c][0] == '-') (argv[c])++; | |
1112 | if (argv[c][0] == 'o') { | |
1113 | char * token, * here; | |
1114 | ||
1115 | if (++c >= argc) | |
1116 | usage(); | |
1117 | ||
1118 | here = argv[c]; | |
1119 | while ((token = strsep(&here, ",;:"))) { | |
1120 | OMIT *restrict optr; | |
1121 | pid_t opid = (pid_t)atoi(token); | |
1122 | ||
1123 | if (opid < 1) { | |
1124 | nsyslog(LOG_ERR, | |
1125 | "illegal omit pid value " | |
1126 | "(%s)!\n", token); | |
1127 | continue; | |
1128 | } | |
1129 | xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); | |
1130 | optr->next = omit; | |
1131 | optr->prev = (OMIT*)0; | |
1132 | optr->pid = opid; | |
1133 | omit = optr; | |
a74aeac6 | 1134 | } |
a74aeac6 PR |
1135 | } |
1136 | else if ((sig = atoi(argv[1])) <= 0 || sig > 31) | |
1137 | usage(); | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | /* First get the /proc filesystem online. */ | |
1142 | mount_proc(); | |
1143 | ||
1144 | /* | |
1145 | * Ignoring SIGKILL and SIGSTOP do not make sense, but | |
1146 | * someday kill(-1, sig) might kill ourself if we don't | |
1147 | * do this. This certainly is a valid concern for SIGTERM- | |
1148 | * Linux 2.1 might send the calling process the signal too. | |
1149 | */ | |
1150 | signal(SIGTERM, SIG_IGN); | |
1151 | signal(SIGSTOP, SIG_IGN); | |
1152 | signal(SIGKILL, SIG_IGN); | |
1153 | ||
1154 | /* lock us into memory */ | |
1155 | mlockall(MCL_CURRENT | MCL_FUTURE); | |
1156 | ||
1157 | /* Now stop all processes. */ | |
1158 | kill(-1, SIGSTOP); | |
1159 | sent_sigstop = 1; | |
1160 | ||
1161 | /* Read /proc filesystem */ | |
1162 | if (readproc(NO_STAT) < 0) { | |
1163 | kill(-1, SIGCONT); | |
1164 | return(1); | |
1165 | } | |
1166 | ||
1167 | /* Now kill all processes except init (pid 1) and our session. */ | |
1168 | sid = (int)getsid(0); | |
1169 | pid = (int)getpid(); | |
1170 | for (p = plist; p; p = p->next) { | |
1171 | if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel) | |
1172 | continue; | |
40fc1ac0 | 1173 | |
a74aeac6 | 1174 | if (omit) { |
40fc1ac0 DWF |
1175 | OMIT * optr; |
1176 | for (optr = omit; optr; optr = optr->next) { | |
1177 | if (optr->pid == p->pid) | |
a74aeac6 | 1178 | break; |
40fc1ac0 DWF |
1179 | } |
1180 | ||
a74aeac6 | 1181 | /* On a match, continue with the for loop above. */ |
40fc1ac0 | 1182 | if (optr) |
a74aeac6 PR |
1183 | continue; |
1184 | } | |
40fc1ac0 | 1185 | |
a74aeac6 PR |
1186 | kill(p->pid, sig); |
1187 | retval = 0; | |
1188 | } | |
1189 | ||
1190 | /* And let them continue. */ | |
1191 | kill(-1, SIGCONT); | |
1192 | ||
1193 | /* Done. */ | |
1194 | closelog(); | |
1195 | ||
1196 | /* Force the kernel to run the scheduler */ | |
1197 | usleep(1); | |
1198 | ||
1199 | return retval; | |
1200 | } |