]>
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 | 379 | /* |
57620806 PR |
380 | * Get the maximal number of symlinks to follow. Use sysconf() on |
381 | * Hurd where the hardcoded value MAXSYMLINKS is not available. | |
7d4c7f43 PR |
382 | */ |
383 | static int maxsymlinks(void) | |
384 | { | |
385 | int v = sysconf(_SC_SYMLOOP_MAX); | |
386 | #ifdef MAXSYMLINKS | |
387 | if (v == -1) | |
388 | return MAXSYMLINKS; | |
389 | #endif | |
390 | return v; | |
391 | } | |
392 | ||
40fc1ac0 DWF |
393 | /* |
394 | * Check path is located on a network based partition. | |
395 | */ | |
396 | int check4nfs(const char * path, char * real) | |
397 | { | |
398 | char buf[PATH_MAX+1]; | |
399 | const char *curr; | |
7d4c7f43 | 400 | int deep = maxsymlinks(); |
21421926 | 401 | |
40fc1ac0 | 402 | if (!nlist) return 0; |
21421926 | 403 | |
40fc1ac0 DWF |
404 | curr = path; |
405 | do { | |
406 | const char *prev; | |
407 | int len; | |
21421926 | 408 | |
40fc1ac0 DWF |
409 | if ((prev = strdupa(curr)) == NULL) { |
410 | nsyslog(LOG_ERR, "strdupa(): %s\n", strerror(errno)); | |
411 | return 0; | |
412 | } | |
21421926 | 413 | |
40fc1ac0 DWF |
414 | errno = 0; |
415 | if ((len = readlink(curr, buf, PATH_MAX)) < 0) | |
416 | break; | |
417 | buf[len] = '\0'; | |
21421926 | 418 | |
40fc1ac0 DWF |
419 | if (buf[0] != '/') { |
420 | const char *slash; | |
21421926 | 421 | |
40fc1ac0 DWF |
422 | if ((slash = strrchr(prev, '/'))) { |
423 | size_t off = slash - prev + 1; | |
21421926 | 424 | |
40fc1ac0 DWF |
425 | if (off + len > PATH_MAX) |
426 | len = PATH_MAX - off; | |
21421926 | 427 | |
40fc1ac0 DWF |
428 | memmove(&buf[off], &buf[0], len + 1); |
429 | memcpy(&buf[0], prev, off); | |
430 | } | |
431 | } | |
432 | curr = &buf[0]; | |
21421926 | 433 | |
40fc1ac0 | 434 | if (deep-- <= 0) return 0; |
21421926 | 435 | |
40fc1ac0 | 436 | } while (1); |
21421926 | 437 | |
40fc1ac0 | 438 | if (real) strcpy(real, curr); |
21421926 | 439 | |
40fc1ac0 DWF |
440 | if (errno == EINVAL) { |
441 | const size_t nlen = strlen(curr); | |
442 | NFS *p; | |
443 | for (p = nlist; p; p = p->next) { | |
444 | if (nlen < p->nlen) | |
445 | continue; | |
446 | if (curr[p->nlen] != '\0' && curr[p->nlen] != '/') | |
447 | continue; | |
448 | if (!strncmp(curr, p->name, p->nlen)) { | |
449 | if (shadow(p->shadow, curr, nlen)) | |
450 | continue; | |
451 | return 1; | |
452 | } | |
453 | } | |
454 | } | |
21421926 | 455 | |
40fc1ac0 DWF |
456 | return 0; |
457 | } | |
458 | ||
a74aeac6 PR |
459 | int readarg(FILE *fp, char *buf, int sz) |
460 | { | |
461 | int c = 0, f = 0; | |
462 | ||
463 | while (f < (sz-1) && (c = fgetc(fp)) != EOF && c) | |
464 | buf[f++] = c; | |
465 | buf[f] = 0; | |
466 | ||
467 | return (c == EOF && f == 0) ? c : f; | |
468 | } | |
469 | ||
470 | /* | |
471 | * Read the proc filesystem. | |
472 | * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse). | |
473 | */ | |
474 | int readproc(int do_stat) | |
475 | { | |
476 | DIR *dir; | |
477 | FILE *fp; | |
478 | PROC *p, *n; | |
479 | struct dirent *d; | |
480 | struct stat st; | |
9e8dafc6 DWF |
481 | char path[PATH_MAX+1]; |
482 | char buf[PATH_MAX+1]; | |
a74aeac6 PR |
483 | char *s, *q; |
484 | unsigned long startcode, endcode; | |
485 | int pid, f; | |
aef3f4ec | 486 | ssize_t len; |
a74aeac6 PR |
487 | |
488 | /* Open the /proc directory. */ | |
489 | if (chdir("/proc") == -1) { | |
490 | nsyslog(LOG_ERR, "chdir /proc failed"); | |
491 | return -1; | |
492 | } | |
493 | if ((dir = opendir(".")) == NULL) { | |
494 | nsyslog(LOG_ERR, "cannot opendir(/proc)"); | |
495 | return -1; | |
496 | } | |
497 | ||
498 | /* Free the already existing process list. */ | |
499 | n = plist; | |
500 | for (p = plist; n; p = n) { | |
501 | n = p->next; | |
502 | if (p->argv0) free(p->argv0); | |
503 | if (p->argv1) free(p->argv1); | |
40fc1ac0 | 504 | if (p->statname) free(p->statname); |
c5ae561e | 505 | free(p->pathname); |
a74aeac6 PR |
506 | free(p); |
507 | } | |
508 | plist = NULL; | |
509 | ||
510 | /* Walk through the directory. */ | |
511 | while ((d = readdir(dir)) != NULL) { | |
512 | ||
513 | /* See if this is a process */ | |
514 | if ((pid = atoi(d->d_name)) == 0) continue; | |
515 | ||
516 | /* Get a PROC struct . */ | |
517 | p = (PROC *)xmalloc(sizeof(PROC)); | |
518 | memset(p, 0, sizeof(PROC)); | |
519 | ||
520 | /* Open the status file. */ | |
521 | snprintf(path, sizeof(path), "%s/stat", d->d_name); | |
522 | ||
523 | /* Read SID & statname from it. */ | |
524 | if ((fp = fopen(path, "r")) != NULL) { | |
9e94f6aa DWF |
525 | size_t len; |
526 | ||
527 | len = fread(buf, sizeof(char), sizeof(buf)-1, fp); | |
528 | buf[len] = '\0'; | |
c5ae561e DWF |
529 | |
530 | if (buf[0] == '\0') { | |
531 | nsyslog(LOG_ERR, | |
532 | "can't read from %s\n", path); | |
533 | fclose(fp); | |
534 | free(p); | |
535 | continue; | |
536 | } | |
a74aeac6 PR |
537 | |
538 | /* See if name starts with '(' */ | |
539 | s = buf; | |
c5ae561e DWF |
540 | while (*s && *s != ' ') s++; |
541 | if (*s) s++; | |
a74aeac6 PR |
542 | if (*s == '(') { |
543 | /* Read program name. */ | |
544 | q = strrchr(buf, ')'); | |
545 | if (q == NULL) { | |
546 | p->sid = 0; | |
547 | nsyslog(LOG_ERR, | |
548 | "can't get program name from /proc/%s\n", | |
549 | path); | |
c5ae561e | 550 | fclose(fp); |
40fc1ac0 DWF |
551 | if (p->argv0) free(p->argv0); |
552 | if (p->argv1) free(p->argv1); | |
553 | if (p->statname) free(p->statname); | |
c5ae561e | 554 | free(p->pathname); |
a74aeac6 PR |
555 | free(p); |
556 | continue; | |
557 | } | |
558 | s++; | |
559 | } else { | |
560 | q = s; | |
c5ae561e | 561 | while (*q && *q != ' ') q++; |
a74aeac6 | 562 | } |
c5ae561e | 563 | if (*q) *q++ = 0; |
a74aeac6 PR |
564 | while (*q == ' ') q++; |
565 | p->statname = (char *)xmalloc(strlen(s)+1); | |
566 | strcpy(p->statname, s); | |
567 | ||
568 | /* Get session, startcode, endcode. */ | |
569 | startcode = endcode = 0; | |
570 | if (sscanf(q, "%*c %*d %*d %d %*d %*d %*u %*u " | |
571 | "%*u %*u %*u %*u %*u %*d %*d " | |
572 | "%*d %*d %*d %*d %*u %*u %*d " | |
573 | "%*u %lu %lu", | |
574 | &p->sid, &startcode, &endcode) != 3) { | |
575 | p->sid = 0; | |
576 | nsyslog(LOG_ERR, "can't read sid from %s\n", | |
577 | path); | |
c5ae561e | 578 | fclose(fp); |
40fc1ac0 DWF |
579 | if (p->argv0) free(p->argv0); |
580 | if (p->argv1) free(p->argv1); | |
581 | if (p->statname) free(p->statname); | |
c5ae561e | 582 | free(p->pathname); |
a74aeac6 PR |
583 | free(p); |
584 | continue; | |
585 | } | |
586 | if (startcode == 0 && endcode == 0) | |
587 | p->kernel = 1; | |
588 | fclose(fp); | |
589 | } else { | |
590 | /* Process disappeared.. */ | |
40fc1ac0 DWF |
591 | if (p->argv0) free(p->argv0); |
592 | if (p->argv1) free(p->argv1); | |
593 | if (p->statname) free(p->statname); | |
c5ae561e | 594 | free(p->pathname); |
a74aeac6 PR |
595 | free(p); |
596 | continue; | |
597 | } | |
598 | ||
599 | snprintf(path, sizeof(path), "%s/cmdline", d->d_name); | |
600 | if ((fp = fopen(path, "r")) != NULL) { | |
601 | ||
602 | /* Now read argv[0] */ | |
603 | f = readarg(fp, buf, sizeof(buf)); | |
604 | ||
605 | if (buf[0]) { | |
606 | /* Store the name into malloced memory. */ | |
607 | p->argv0 = (char *)xmalloc(f + 1); | |
608 | strcpy(p->argv0, buf); | |
609 | ||
610 | /* Get a pointer to the basename. */ | |
611 | p->argv0base = strrchr(p->argv0, '/'); | |
612 | if (p->argv0base != NULL) | |
613 | p->argv0base++; | |
614 | else | |
615 | p->argv0base = p->argv0; | |
616 | } | |
617 | ||
618 | /* And read argv[1] */ | |
619 | while ((f = readarg(fp, buf, sizeof(buf))) != EOF) | |
620 | if (buf[0] != '-') break; | |
621 | ||
622 | if (buf[0]) { | |
623 | /* Store the name into malloced memory. */ | |
624 | p->argv1 = (char *)xmalloc(f + 1); | |
625 | strcpy(p->argv1, buf); | |
626 | ||
627 | /* Get a pointer to the basename. */ | |
628 | p->argv1base = strrchr(p->argv1, '/'); | |
629 | if (p->argv1base != NULL) | |
630 | p->argv1base++; | |
631 | else | |
632 | p->argv1base = p->argv1; | |
633 | } | |
634 | ||
635 | fclose(fp); | |
636 | ||
637 | } else { | |
638 | /* Process disappeared.. */ | |
40fc1ac0 DWF |
639 | if (p->argv0) free(p->argv0); |
640 | if (p->argv1) free(p->argv1); | |
641 | if (p->statname) free(p->statname); | |
c5ae561e | 642 | free(p->pathname); |
a74aeac6 PR |
643 | free(p); |
644 | continue; | |
645 | } | |
646 | ||
647 | /* Try to stat the executable. */ | |
648 | snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name); | |
40fc1ac0 DWF |
649 | |
650 | p->nfs = 0; | |
651 | ||
652 | switch (do_stat) { | |
653 | case DO_NETFS: | |
654 | if ((p->nfs = check4nfs(path, buf))) | |
aef3f4ec | 655 | goto link; |
40fc1ac0 | 656 | case DO_STAT: |
7e088ce6 DWF |
657 | if (stat(path, &st) != 0) { |
658 | char * ptr; | |
659 | ||
660 | len = readlink(path, buf, PATH_MAX); | |
661 | if (len <= 0) | |
662 | break; | |
663 | buf[len] = '\0'; | |
664 | ||
665 | ptr = strstr(buf, " (deleted)"); | |
666 | if (!ptr) | |
667 | break; | |
668 | *ptr = '\0'; | |
669 | len -= strlen(" (deleted)"); | |
670 | ||
671 | if (stat(buf, &st) != 0) | |
672 | break; | |
673 | p->dev = st.st_dev; | |
674 | p->ino = st.st_ino; | |
675 | p->pathname = (char *)xmalloc(len + 1); | |
676 | memcpy(p->pathname, buf, len); | |
677 | p->pathname[len] = '\0'; | |
678 | ||
679 | /* All done */ | |
40fc1ac0 | 680 | break; |
7e088ce6 DWF |
681 | } |
682 | ||
a74aeac6 PR |
683 | p->dev = st.st_dev; |
684 | p->ino = st.st_ino; | |
aef3f4ec DWF |
685 | |
686 | /* Fall through */ | |
40fc1ac0 | 687 | default: |
aef3f4ec DWF |
688 | link: |
689 | len = readlink(path, buf, PATH_MAX); | |
690 | if (len > 0) { | |
691 | p->pathname = (char *)xmalloc(len + 1); | |
692 | memcpy(p->pathname, buf, len); | |
693 | p->pathname[len] = '\0'; | |
694 | } | |
40fc1ac0 | 695 | break; |
a74aeac6 PR |
696 | } |
697 | ||
698 | /* Link it into the list. */ | |
699 | p->next = plist; | |
700 | plist = p; | |
701 | p->pid = pid; | |
702 | } | |
703 | closedir(dir); | |
704 | ||
705 | /* Done. */ | |
706 | return 0; | |
707 | } | |
708 | ||
709 | PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q) | |
710 | { | |
711 | q->head = q->next = q->tail = NULL; | |
712 | return q; | |
713 | } | |
714 | ||
715 | int empty_q(PIDQ_HEAD *q) | |
716 | { | |
717 | return (q->head == NULL); | |
718 | } | |
719 | ||
720 | int add_pid_to_q(PIDQ_HEAD *q, PROC *p) | |
721 | { | |
722 | PIDQ *tmp; | |
723 | ||
724 | tmp = (PIDQ *)xmalloc(sizeof(PIDQ)); | |
725 | ||
726 | tmp->proc = p; | |
727 | tmp->next = NULL; | |
728 | ||
729 | if (empty_q(q)) { | |
730 | q->head = tmp; | |
731 | q->tail = tmp; | |
732 | } else { | |
733 | q->tail->next = tmp; | |
734 | q->tail = tmp; | |
735 | } | |
736 | return 0; | |
737 | } | |
738 | ||
739 | PROC *get_next_from_pid_q(PIDQ_HEAD *q) | |
740 | { | |
741 | PROC *p; | |
742 | PIDQ *tmp = q->head; | |
743 | ||
744 | if (!empty_q(q)) { | |
745 | p = q->head->proc; | |
746 | q->head = tmp->next; | |
747 | free(tmp); | |
748 | return p; | |
749 | } | |
750 | ||
751 | return NULL; | |
752 | } | |
753 | ||
754 | /* Try to get the process ID of a given process. */ | |
755 | PIDQ_HEAD *pidof(char *prog) | |
756 | { | |
757 | PROC *p; | |
758 | PIDQ_HEAD *q; | |
759 | struct stat st; | |
760 | char *s; | |
40fc1ac0 | 761 | int nfs = 0; |
a74aeac6 PR |
762 | int dostat = 0; |
763 | int foundone = 0; | |
764 | int ok = 0; | |
aef3f4ec | 765 | const int root = (getuid() == 0); |
40fc1ac0 | 766 | char real[PATH_MAX+1]; |
a74aeac6 PR |
767 | |
768 | if (! prog) | |
769 | return NULL; | |
770 | ||
40fc1ac0 DWF |
771 | /* Try to stat the executable. */ |
772 | if (prog[0] == '/') { | |
773 | memset(&real[0], 0, sizeof(real)); | |
774 | ||
775 | if (check4nfs(prog, real)) | |
776 | nfs++; | |
777 | ||
778 | if (real[0] != '\0') | |
779 | prog = &real[0]; /* Binary located on network FS. */ | |
780 | ||
781 | if ((nfs == 0) && (stat(prog, &st) == 0)) | |
782 | dostat++; /* Binary located on a local FS. */ | |
783 | } | |
784 | ||
a74aeac6 PR |
785 | /* Get basename of program. */ |
786 | if ((s = strrchr(prog, '/')) == NULL) | |
787 | s = prog; | |
788 | else | |
789 | s++; | |
790 | ||
791 | if (! *s) | |
792 | return NULL; | |
793 | ||
794 | q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD)); | |
795 | q = init_pid_q(q); | |
796 | ||
a74aeac6 | 797 | /* First try to find a match based on dev/ino pair. */ |
40fc1ac0 | 798 | if (dostat && !nfs) { |
a74aeac6 | 799 | for (p = plist; p; p = p->next) { |
40fc1ac0 DWF |
800 | if (p->nfs) |
801 | continue; | |
a74aeac6 PR |
802 | if (p->dev == st.st_dev && p->ino == st.st_ino) { |
803 | add_pid_to_q(q, p); | |
804 | foundone++; | |
805 | } | |
806 | } | |
807 | } | |
808 | ||
40fc1ac0 DWF |
809 | /* Second try to find a match based on full path name on |
810 | * network FS located binaries */ | |
811 | if (!foundone && nfs) { | |
812 | for (p = plist; p; p = p->next) { | |
aef3f4ec DWF |
813 | if (!p->pathname) |
814 | continue; | |
accef4ed DWF |
815 | if (!p->nfs) |
816 | continue; | |
aef3f4ec | 817 | if (strcmp(prog, p->pathname) != 0) |
40fc1ac0 DWF |
818 | continue; |
819 | add_pid_to_q(q, p); | |
820 | foundone++; | |
821 | } | |
822 | } | |
823 | ||
a74aeac6 PR |
824 | /* If we didn't find a match based on dev/ino, try the name. */ |
825 | if (!foundone) for (p = plist; p; p = p->next) { | |
c5ae561e | 826 | if (prog[0] == '/') { |
aef3f4ec DWF |
827 | if (!p->pathname) { |
828 | if (root) | |
829 | continue; | |
830 | goto fallback; | |
831 | } | |
c5ae561e DWF |
832 | if (strcmp(prog, p->pathname)) { |
833 | int len = strlen(prog); | |
834 | if (strncmp(prog, p->pathname, len)) | |
aef3f4ec DWF |
835 | { |
836 | if (scripts_too) | |
837 | goto fallback; | |
c5ae561e | 838 | continue; |
aef3f4ec | 839 | } |
c5ae561e | 840 | if (strcmp(" (deleted)", p->pathname + len)) |
aef3f4ec DWF |
841 | { |
842 | if (scripts_too) | |
843 | goto fallback; | |
c5ae561e | 844 | continue; |
aef3f4ec | 845 | } |
c5ae561e DWF |
846 | } |
847 | add_pid_to_q(q, p); | |
848 | continue; | |
849 | } | |
850 | ||
aef3f4ec | 851 | fallback: |
a74aeac6 PR |
852 | ok = 0; |
853 | ||
854 | /* matching nonmatching | |
855 | * proc name prog name prog name | |
856 | * --- ----------- ------------ | |
857 | * b b, p/b, q/b | |
858 | * p/b b, p/b q/b | |
859 | * | |
860 | * Algorithm: Match if: | |
861 | * cmd = arg | |
862 | * or cmd = base(arg) | |
863 | * or base(cmd) = arg | |
864 | * | |
865 | * Specifically, do not match just because base(cmd) = base(arg) | |
866 | * as was done in earlier versions of this program, since this | |
867 | * allows /aaa/foo to match /bbb/foo . | |
868 | */ | |
869 | ok |= | |
870 | (p->argv0 && strcmp(p->argv0, prog) == 0) | |
871 | || (p->argv0 && s != prog && strcmp(p->argv0, s) == 0) | |
872 | || (p->argv0base && strcmp(p->argv0base, prog) == 0); | |
873 | ||
874 | /* For scripts, compare argv[1] as well. */ | |
875 | if ( | |
876 | scripts_too && p->statname && p->argv1base | |
877 | && !strncmp(p->statname, p->argv1base, STATNAMELEN) | |
878 | ) { | |
879 | ok |= | |
880 | (p->argv1 && strcmp(p->argv1, prog) == 0) | |
881 | || (p->argv1 && s != prog && strcmp(p->argv1, s) == 0) | |
882 | || (p->argv1base && strcmp(p->argv1base, prog) == 0); | |
883 | } | |
884 | ||
885 | /* | |
886 | * if we have a space in argv0, process probably | |
887 | * used setproctitle so try statname. | |
888 | */ | |
889 | if (strlen(s) <= STATNAMELEN && | |
890 | (p->argv0 == NULL || | |
891 | p->argv0[0] == 0 || | |
892 | strchr(p->argv0, ' '))) { | |
893 | ok |= (strcmp(p->statname, s) == 0); | |
894 | } | |
40fc1ac0 DWF |
895 | |
896 | /* | |
897 | * if we have a `-' as the first character, process | |
898 | * probably used as a login shell | |
899 | */ | |
900 | if (strlen(s) <= STATNAMELEN && | |
901 | p->argv1 == NULL && | |
902 | (p->argv0 != NULL && | |
903 | p->argv0[0] == '-')) { | |
904 | ok |= (strcmp(p->statname, s) == 0); | |
905 | } | |
906 | ||
a74aeac6 PR |
907 | if (ok) add_pid_to_q(q, p); |
908 | } | |
909 | ||
40fc1ac0 | 910 | return q; |
a74aeac6 PR |
911 | } |
912 | ||
913 | /* Give usage message and exit. */ | |
914 | void usage(void) | |
915 | { | |
916 | nsyslog(LOG_ERR, "only one argument, a signal number, allowed"); | |
917 | closelog(); | |
918 | exit(1); | |
919 | } | |
920 | ||
921 | /* write to syslog file if not open terminal */ | |
922 | #ifdef __GNUC__ | |
923 | __attribute__ ((format (printf, 2, 3))) | |
924 | #endif | |
925 | void nsyslog(int pri, char *fmt, ...) | |
926 | { | |
927 | va_list args; | |
928 | ||
929 | va_start(args, fmt); | |
930 | ||
931 | if (ttyname(0) == NULL) { | |
932 | vsyslog(pri, fmt, args); | |
933 | } else { | |
934 | fprintf(stderr, "%s: ",progname); | |
935 | vfprintf(stderr, fmt, args); | |
936 | fprintf(stderr, "\n"); | |
937 | } | |
938 | ||
939 | va_end(args); | |
940 | } | |
941 | ||
942 | #define PIDOF_SINGLE 0x01 | |
943 | #define PIDOF_OMIT 0x02 | |
40fc1ac0 | 944 | #define PIDOF_NETFS 0x04 |
a74aeac6 PR |
945 | |
946 | /* | |
947 | * Pidof functionality. | |
948 | */ | |
949 | int main_pidof(int argc, char **argv) | |
950 | { | |
951 | PIDQ_HEAD *q; | |
952 | PROC *p; | |
40fc1ac0 | 953 | char *token, *here; |
a74aeac6 PR |
954 | int f; |
955 | int first = 1; | |
40fc1ac0 | 956 | int opt, flags = 0; |
a74aeac6 PR |
957 | int chroot_check = 0; |
958 | struct stat st; | |
959 | char tmp[512]; | |
960 | ||
40fc1ac0 DWF |
961 | omit = (OMIT*)0; |
962 | nlist = (NFS*)0; | |
a74aeac6 PR |
963 | opterr = 0; |
964 | ||
40fc1ac0 DWF |
965 | if ((token = getenv("PIDOF_NETFS")) && (strcmp(token,"no") != 0)) |
966 | flags |= PIDOF_NETFS; | |
967 | ||
968 | while ((opt = getopt(argc,argv,"hco:sxn")) != EOF) switch (opt) { | |
a74aeac6 PR |
969 | case '?': |
970 | nsyslog(LOG_ERR,"invalid options on command line!\n"); | |
971 | closelog(); | |
972 | exit(1); | |
973 | case 'c': | |
974 | if (geteuid() == 0) chroot_check = 1; | |
975 | break; | |
976 | case 'o': | |
40fc1ac0 DWF |
977 | here = optarg; |
978 | while ((token = strsep(&here, ",;:"))) { | |
979 | OMIT *restrict optr; | |
980 | pid_t opid; | |
981 | ||
982 | if (strcmp("%PPID", token) == 0) | |
983 | opid = getppid(); | |
984 | else | |
985 | opid = (pid_t)atoi(token); | |
986 | ||
987 | if (opid < 1) { | |
988 | nsyslog(LOG_ERR, | |
989 | "illegal omit pid value " | |
990 | "(%s)!\n", token); | |
991 | continue; | |
992 | } | |
993 | xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); | |
994 | optr->next = omit; | |
995 | optr->prev = (OMIT*)0; | |
996 | optr->pid = opid; | |
997 | omit = optr; | |
a74aeac6 | 998 | } |
a74aeac6 PR |
999 | flags |= PIDOF_OMIT; |
1000 | break; | |
1001 | case 's': | |
1002 | flags |= PIDOF_SINGLE; | |
1003 | break; | |
1004 | case 'x': | |
1005 | scripts_too++; | |
1006 | break; | |
40fc1ac0 DWF |
1007 | case 'n': |
1008 | flags |= PIDOF_NETFS; | |
1009 | break; | |
a74aeac6 PR |
1010 | default: |
1011 | /* Nothing */ | |
1012 | break; | |
1013 | } | |
1014 | argc -= optind; | |
1015 | argv += optind; | |
1016 | ||
1017 | /* Check if we are in a chroot */ | |
1018 | if (chroot_check) { | |
1019 | snprintf(tmp, 512, "/proc/%d/root", getpid()); | |
1020 | if (stat(tmp, &st) < 0) { | |
1021 | nsyslog(LOG_ERR, "stat failed for %s!\n", tmp); | |
1022 | closelog(); | |
1023 | exit(1); | |
1024 | } | |
1025 | } | |
1026 | ||
40fc1ac0 DWF |
1027 | if (flags & PIDOF_NETFS) |
1028 | init_nfs(); /* Which network based FS are online? */ | |
1029 | ||
a74aeac6 | 1030 | /* Print out process-ID's one by one. */ |
40fc1ac0 DWF |
1031 | readproc((flags & PIDOF_NETFS) ? DO_NETFS : DO_STAT); |
1032 | ||
a74aeac6 PR |
1033 | for(f = 0; f < argc; f++) { |
1034 | if ((q = pidof(argv[f])) != NULL) { | |
40fc1ac0 | 1035 | pid_t spid = 0; |
a74aeac6 | 1036 | while ((p = get_next_from_pid_q(q))) { |
40fc1ac0 DWF |
1037 | if ((flags & PIDOF_OMIT) && omit) { |
1038 | OMIT * optr; | |
1039 | for (optr = omit; optr; optr = optr->next) { | |
1040 | if (optr->pid == p->pid) | |
a74aeac6 | 1041 | break; |
40fc1ac0 DWF |
1042 | } |
1043 | ||
a74aeac6 PR |
1044 | /* |
1045 | * On a match, continue with | |
1046 | * the for loop above. | |
1047 | */ | |
40fc1ac0 | 1048 | if (optr) |
a74aeac6 PR |
1049 | continue; |
1050 | } | |
1051 | if (flags & PIDOF_SINGLE) { | |
1052 | if (spid) | |
1053 | continue; | |
1054 | else | |
1055 | spid = 1; | |
1056 | } | |
1057 | if (chroot_check) { | |
1058 | struct stat st2; | |
1059 | snprintf(tmp, 512, "/proc/%d/root", | |
1060 | p->pid); | |
1061 | if (stat(tmp, &st2) < 0 || | |
1062 | st.st_dev != st2.st_dev || | |
1063 | st.st_ino != st2.st_ino) { | |
1064 | continue; | |
1065 | } | |
1066 | } | |
1067 | if (!first) | |
1068 | printf(" "); | |
1069 | printf("%d", p->pid); | |
1070 | first = 0; | |
1071 | } | |
1072 | } | |
1073 | } | |
1074 | if (!first) | |
1075 | printf("\n"); | |
40fc1ac0 DWF |
1076 | |
1077 | clear_mnt(); | |
1078 | ||
a74aeac6 PR |
1079 | closelog(); |
1080 | return(first ? 1 : 0); | |
1081 | } | |
1082 | ||
a74aeac6 PR |
1083 | /* Main for either killall or pidof. */ |
1084 | int main(int argc, char **argv) | |
1085 | { | |
1086 | PROC *p; | |
1087 | int pid, sid = -1; | |
a74aeac6 | 1088 | int sig = SIGKILL; |
40fc1ac0 | 1089 | int c; |
a74aeac6 PR |
1090 | |
1091 | /* return non-zero if no process was killed */ | |
1092 | int retval = 2; | |
1093 | ||
1094 | /* Get program name. */ | |
1095 | if ((progname = strrchr(argv[0], '/')) == NULL) | |
1096 | progname = argv[0]; | |
1097 | else | |
1098 | progname++; | |
1099 | ||
1100 | /* Now connect to syslog. */ | |
1101 | openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON); | |
1102 | ||
1103 | /* Were we called as 'pidof' ? */ | |
1104 | if (strcmp(progname, "pidof") == 0) | |
1105 | return main_pidof(argc, argv); | |
1106 | ||
1107 | /* Right, so we are "killall". */ | |
40fc1ac0 | 1108 | omit = (OMIT*)0; |
a74aeac6 PR |
1109 | |
1110 | if (argc > 1) { | |
40fc1ac0 DWF |
1111 | for (c = 1; c < argc; c++) { |
1112 | if (argv[c][0] == '-') (argv[c])++; | |
1113 | if (argv[c][0] == 'o') { | |
1114 | char * token, * here; | |
1115 | ||
1116 | if (++c >= argc) | |
1117 | usage(); | |
1118 | ||
1119 | here = argv[c]; | |
1120 | while ((token = strsep(&here, ",;:"))) { | |
1121 | OMIT *restrict optr; | |
1122 | pid_t opid = (pid_t)atoi(token); | |
1123 | ||
1124 | if (opid < 1) { | |
1125 | nsyslog(LOG_ERR, | |
1126 | "illegal omit pid value " | |
1127 | "(%s)!\n", token); | |
1128 | continue; | |
1129 | } | |
1130 | xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); | |
1131 | optr->next = omit; | |
1132 | optr->prev = (OMIT*)0; | |
1133 | optr->pid = opid; | |
1134 | omit = optr; | |
a74aeac6 | 1135 | } |
a74aeac6 PR |
1136 | } |
1137 | else if ((sig = atoi(argv[1])) <= 0 || sig > 31) | |
1138 | usage(); | |
1139 | } | |
1140 | } | |
1141 | ||
1142 | /* First get the /proc filesystem online. */ | |
1143 | mount_proc(); | |
1144 | ||
1145 | /* | |
1146 | * Ignoring SIGKILL and SIGSTOP do not make sense, but | |
1147 | * someday kill(-1, sig) might kill ourself if we don't | |
1148 | * do this. This certainly is a valid concern for SIGTERM- | |
1149 | * Linux 2.1 might send the calling process the signal too. | |
1150 | */ | |
1151 | signal(SIGTERM, SIG_IGN); | |
1152 | signal(SIGSTOP, SIG_IGN); | |
1153 | signal(SIGKILL, SIG_IGN); | |
1154 | ||
1155 | /* lock us into memory */ | |
1156 | mlockall(MCL_CURRENT | MCL_FUTURE); | |
1157 | ||
1158 | /* Now stop all processes. */ | |
1159 | kill(-1, SIGSTOP); | |
1160 | sent_sigstop = 1; | |
1161 | ||
1162 | /* Read /proc filesystem */ | |
1163 | if (readproc(NO_STAT) < 0) { | |
1164 | kill(-1, SIGCONT); | |
1165 | return(1); | |
1166 | } | |
1167 | ||
1168 | /* Now kill all processes except init (pid 1) and our session. */ | |
1169 | sid = (int)getsid(0); | |
1170 | pid = (int)getpid(); | |
1171 | for (p = plist; p; p = p->next) { | |
1172 | if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel) | |
1173 | continue; | |
40fc1ac0 | 1174 | |
a74aeac6 | 1175 | if (omit) { |
40fc1ac0 DWF |
1176 | OMIT * optr; |
1177 | for (optr = omit; optr; optr = optr->next) { | |
1178 | if (optr->pid == p->pid) | |
a74aeac6 | 1179 | break; |
40fc1ac0 DWF |
1180 | } |
1181 | ||
a74aeac6 | 1182 | /* On a match, continue with the for loop above. */ |
40fc1ac0 | 1183 | if (optr) |
a74aeac6 PR |
1184 | continue; |
1185 | } | |
40fc1ac0 | 1186 | |
a74aeac6 PR |
1187 | kill(p->pid, sig); |
1188 | retval = 0; | |
1189 | } | |
1190 | ||
1191 | /* And let them continue. */ | |
1192 | kill(-1, SIGCONT); | |
1193 | ||
1194 | /* Done. */ | |
1195 | closelog(); | |
1196 | ||
1197 | /* Force the kernel to run the scheduler */ | |
1198 | usleep(1); | |
1199 | ||
1200 | return retval; | |
1201 | } |