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