Drop hurd specific dependency on libc0.3 (>= 2.3.2.ds1-12). It is
[sysvinit.git] / src / killall5.c
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 *
22 * This file is part of the sysvinit suite,
23 * Copyright (C) 1991-2004 Miquel van Smoorenburg.
24 *
25 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation; either version 2 of the License, or
28 * (at your option) any later version.
29 *
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
34 *
35 * You should have received a copy of the GNU General Public License
36 * along with this program; if not, write to the Free Software
37 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
38 */
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47 #include <signal.h>
48 #include <dirent.h>
49 #include <syslog.h>
50 #include <getopt.h>
51 #include <stdarg.h>
52 #include <sys/mman.h>
53
54 char *Version = "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl";
55
56 #define STATNAMELEN 15
57 #define DO_STAT 1
58 #define NO_STAT 0
59
60 /* Info about a process. */
61 typedef struct proc {
62 char *argv0; /* Name as found out from argv[0] */
63 char *argv0base; /* `basename argv[1]` */
64 char *argv1; /* Name as found out from argv[1] */
65 char *argv1base; /* `basename argv[1]` */
66 char *statname; /* the statname without braces */
67 ino_t ino; /* Inode number */
68 dev_t dev; /* Device it is on */
69 pid_t pid; /* Process ID. */
70 int sid; /* Session ID. */
71 int kernel; /* Kernel thread or zombie. */
72 struct proc *next; /* Pointer to next struct. */
73 } PROC;
74
75 /* pid queue */
76
77 typedef struct pidq {
78 PROC *proc;
79 struct pidq *next;
80 } PIDQ;
81
82 typedef struct {
83 PIDQ *head;
84 PIDQ *tail;
85 PIDQ *next;
86 } PIDQ_HEAD;
87
88 /* List of processes. */
89 PROC *plist;
90
91 /* Did we stop all processes ? */
92 int sent_sigstop;
93
94 int scripts_too = 0;
95
96 char *progname; /* the name of the running program */
97 #ifdef __GNUC__
98 __attribute__ ((format (printf, 2, 3)))
99 #endif
100 void nsyslog(int pri, char *fmt, ...);
101
102 /*
103 * Malloc space, barf if out of memory.
104 */
105 void *xmalloc(int bytes)
106 {
107 void *p;
108
109 if ((p = malloc(bytes)) == NULL) {
110 if (sent_sigstop) kill(-1, SIGCONT);
111 nsyslog(LOG_ERR, "out of memory");
112 exit(1);
113 }
114 return p;
115 }
116
117 /*
118 * See if the proc filesystem is there. Mount if needed.
119 */
120 int mount_proc(void)
121 {
122 struct stat st;
123 char *args[] = { "mount", "-t", "proc", "proc", "/proc", 0 };
124 pid_t pid, rc;
125 int wst;
126 int did_mount = 0;
127
128 /* Stat /proc/version to see if /proc is mounted. */
129 if (stat("/proc/version", &st) < 0 && errno == ENOENT) {
130
131 /* It's not there, so mount it. */
132 if ((pid = fork()) < 0) {
133 nsyslog(LOG_ERR, "cannot fork");
134 exit(1);
135 }
136 if (pid == 0) {
137 /* Try a few mount binaries. */
138 execv("/sbin/mount", args);
139 execv("/bin/mount", args);
140
141 /* Okay, I give up. */
142 nsyslog(LOG_ERR, "cannot execute mount");
143 exit(1);
144 }
145 /* Wait for child. */
146 while ((rc = wait(&wst)) != pid)
147 if (rc < 0 && errno == ECHILD)
148 break;
149 if (rc != pid || WEXITSTATUS(wst) != 0)
150 nsyslog(LOG_ERR, "mount returned non-zero exit status");
151
152 did_mount = 1;
153 }
154
155 /* See if mount succeeded. */
156 if (stat("/proc/version", &st) < 0) {
157 if (errno == ENOENT)
158 nsyslog(LOG_ERR, "/proc not mounted, failed to mount.");
159 else
160 nsyslog(LOG_ERR, "/proc unavailable.");
161 exit(1);
162 }
163
164 return did_mount;
165 }
166
167 int readarg(FILE *fp, char *buf, int sz)
168 {
169 int c = 0, f = 0;
170
171 while (f < (sz-1) && (c = fgetc(fp)) != EOF && c)
172 buf[f++] = c;
173 buf[f] = 0;
174
175 return (c == EOF && f == 0) ? c : f;
176 }
177
178 /*
179 * Read the proc filesystem.
180 * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse).
181 */
182 int readproc(int do_stat)
183 {
184 DIR *dir;
185 FILE *fp;
186 PROC *p, *n;
187 struct dirent *d;
188 struct stat st;
189 char path[256];
190 char buf[256];
191 char *s, *q;
192 unsigned long startcode, endcode;
193 int pid, f;
194
195 /* Open the /proc directory. */
196 if (chdir("/proc") == -1) {
197 nsyslog(LOG_ERR, "chdir /proc failed");
198 return -1;
199 }
200 if ((dir = opendir(".")) == NULL) {
201 nsyslog(LOG_ERR, "cannot opendir(/proc)");
202 return -1;
203 }
204
205 /* Free the already existing process list. */
206 n = plist;
207 for (p = plist; n; p = n) {
208 n = p->next;
209 if (p->argv0) free(p->argv0);
210 if (p->argv1) free(p->argv1);
211 free(p);
212 }
213 plist = NULL;
214
215 /* Walk through the directory. */
216 while ((d = readdir(dir)) != NULL) {
217
218 /* See if this is a process */
219 if ((pid = atoi(d->d_name)) == 0) continue;
220
221 /* Get a PROC struct . */
222 p = (PROC *)xmalloc(sizeof(PROC));
223 memset(p, 0, sizeof(PROC));
224
225 /* Open the status file. */
226 snprintf(path, sizeof(path), "%s/stat", d->d_name);
227
228 /* Read SID & statname from it. */
229 if ((fp = fopen(path, "r")) != NULL) {
230 buf[0] = 0;
231 fgets(buf, sizeof(buf), fp);
232
233 /* See if name starts with '(' */
234 s = buf;
235 while (*s != ' ') s++;
236 s++;
237 if (*s == '(') {
238 /* Read program name. */
239 q = strrchr(buf, ')');
240 if (q == NULL) {
241 p->sid = 0;
242 nsyslog(LOG_ERR,
243 "can't get program name from /proc/%s\n",
244 path);
245 free(p);
246 continue;
247 }
248 s++;
249 } else {
250 q = s;
251 while (*q != ' ') q++;
252 }
253 *q++ = 0;
254 while (*q == ' ') q++;
255 p->statname = (char *)xmalloc(strlen(s)+1);
256 strcpy(p->statname, s);
257
258 /* Get session, startcode, endcode. */
259 startcode = endcode = 0;
260 if (sscanf(q, "%*c %*d %*d %d %*d %*d %*u %*u "
261 "%*u %*u %*u %*u %*u %*d %*d "
262 "%*d %*d %*d %*d %*u %*u %*d "
263 "%*u %lu %lu",
264 &p->sid, &startcode, &endcode) != 3) {
265 p->sid = 0;
266 nsyslog(LOG_ERR, "can't read sid from %s\n",
267 path);
268 free(p);
269 continue;
270 }
271 if (startcode == 0 && endcode == 0)
272 p->kernel = 1;
273 fclose(fp);
274 } else {
275 /* Process disappeared.. */
276 free(p);
277 continue;
278 }
279
280 snprintf(path, sizeof(path), "%s/cmdline", d->d_name);
281 if ((fp = fopen(path, "r")) != NULL) {
282
283 /* Now read argv[0] */
284 f = readarg(fp, buf, sizeof(buf));
285
286 if (buf[0]) {
287 /* Store the name into malloced memory. */
288 p->argv0 = (char *)xmalloc(f + 1);
289 strcpy(p->argv0, buf);
290
291 /* Get a pointer to the basename. */
292 p->argv0base = strrchr(p->argv0, '/');
293 if (p->argv0base != NULL)
294 p->argv0base++;
295 else
296 p->argv0base = p->argv0;
297 }
298
299 /* And read argv[1] */
300 while ((f = readarg(fp, buf, sizeof(buf))) != EOF)
301 if (buf[0] != '-') break;
302
303 if (buf[0]) {
304 /* Store the name into malloced memory. */
305 p->argv1 = (char *)xmalloc(f + 1);
306 strcpy(p->argv1, buf);
307
308 /* Get a pointer to the basename. */
309 p->argv1base = strrchr(p->argv1, '/');
310 if (p->argv1base != NULL)
311 p->argv1base++;
312 else
313 p->argv1base = p->argv1;
314 }
315
316 fclose(fp);
317
318 } else {
319 /* Process disappeared.. */
320 free(p);
321 continue;
322 }
323
324 /* Try to stat the executable. */
325 snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name);
326 if (do_stat && stat(path, &st) == 0) {
327 p->dev = st.st_dev;
328 p->ino = st.st_ino;
329 }
330
331 /* Link it into the list. */
332 p->next = plist;
333 plist = p;
334 p->pid = pid;
335 }
336 closedir(dir);
337
338 /* Done. */
339 return 0;
340 }
341
342 PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q)
343 {
344 q->head = q->next = q->tail = NULL;
345 return q;
346 }
347
348 int empty_q(PIDQ_HEAD *q)
349 {
350 return (q->head == NULL);
351 }
352
353 int add_pid_to_q(PIDQ_HEAD *q, PROC *p)
354 {
355 PIDQ *tmp;
356
357 tmp = (PIDQ *)xmalloc(sizeof(PIDQ));
358
359 tmp->proc = p;
360 tmp->next = NULL;
361
362 if (empty_q(q)) {
363 q->head = tmp;
364 q->tail = tmp;
365 } else {
366 q->tail->next = tmp;
367 q->tail = tmp;
368 }
369 return 0;
370 }
371
372 PROC *get_next_from_pid_q(PIDQ_HEAD *q)
373 {
374 PROC *p;
375 PIDQ *tmp = q->head;
376
377 if (!empty_q(q)) {
378 p = q->head->proc;
379 q->head = tmp->next;
380 free(tmp);
381 return p;
382 }
383
384 return NULL;
385 }
386
387 /* Try to get the process ID of a given process. */
388 PIDQ_HEAD *pidof(char *prog)
389 {
390 PROC *p;
391 PIDQ_HEAD *q;
392 struct stat st;
393 char *s;
394 int dostat = 0;
395 int foundone = 0;
396 int ok = 0;
397
398 if (! prog)
399 return NULL;
400
401 /* Get basename of program. */
402 if ((s = strrchr(prog, '/')) == NULL)
403 s = prog;
404 else
405 s++;
406
407 if (! *s)
408 return NULL;
409
410 q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD));
411 q = init_pid_q(q);
412
413 /* Try to stat the executable. */
414 if (prog[0] == '/' && stat(prog, &st) == 0)
415 dostat++;
416
417 /* First try to find a match based on dev/ino pair. */
418 if (dostat) {
419 for (p = plist; p; p = p->next) {
420 if (p->dev == st.st_dev && p->ino == st.st_ino) {
421 add_pid_to_q(q, p);
422 foundone++;
423 }
424 }
425 }
426
427 /* If we didn't find a match based on dev/ino, try the name. */
428 if (!foundone) for (p = plist; p; p = p->next) {
429 ok = 0;
430
431 /* matching nonmatching
432 * proc name prog name prog name
433 * --- ----------- ------------
434 * b b, p/b, q/b
435 * p/b b, p/b q/b
436 *
437 * Algorithm: Match if:
438 * cmd = arg
439 * or cmd = base(arg)
440 * or base(cmd) = arg
441 *
442 * Specifically, do not match just because base(cmd) = base(arg)
443 * as was done in earlier versions of this program, since this
444 * allows /aaa/foo to match /bbb/foo .
445 */
446 ok |=
447 (p->argv0 && strcmp(p->argv0, prog) == 0)
448 || (p->argv0 && s != prog && strcmp(p->argv0, s) == 0)
449 || (p->argv0base && strcmp(p->argv0base, prog) == 0);
450
451 /* For scripts, compare argv[1] as well. */
452 if (
453 scripts_too && p->statname && p->argv1base
454 && !strncmp(p->statname, p->argv1base, STATNAMELEN)
455 ) {
456 ok |=
457 (p->argv1 && strcmp(p->argv1, prog) == 0)
458 || (p->argv1 && s != prog && strcmp(p->argv1, s) == 0)
459 || (p->argv1base && strcmp(p->argv1base, prog) == 0);
460 }
461
462 /*
463 * if we have a space in argv0, process probably
464 * used setproctitle so try statname.
465 */
466 if (strlen(s) <= STATNAMELEN &&
467 (p->argv0 == NULL ||
468 p->argv0[0] == 0 ||
469 strchr(p->argv0, ' '))) {
470 ok |= (strcmp(p->statname, s) == 0);
471 }
472 if (ok) add_pid_to_q(q, p);
473 }
474
475 return q;
476 }
477
478 /* Give usage message and exit. */
479 void usage(void)
480 {
481 nsyslog(LOG_ERR, "only one argument, a signal number, allowed");
482 closelog();
483 exit(1);
484 }
485
486 /* write to syslog file if not open terminal */
487 #ifdef __GNUC__
488 __attribute__ ((format (printf, 2, 3)))
489 #endif
490 void nsyslog(int pri, char *fmt, ...)
491 {
492 va_list args;
493
494 va_start(args, fmt);
495
496 if (ttyname(0) == NULL) {
497 vsyslog(pri, fmt, args);
498 } else {
499 fprintf(stderr, "%s: ",progname);
500 vfprintf(stderr, fmt, args);
501 fprintf(stderr, "\n");
502 }
503
504 va_end(args);
505 }
506
507 #define PIDOF_SINGLE 0x01
508 #define PIDOF_OMIT 0x02
509
510 #define PIDOF_OMITSZ 5
511
512 /*
513 * Pidof functionality.
514 */
515 int main_pidof(int argc, char **argv)
516 {
517 PIDQ_HEAD *q;
518 PROC *p;
519 pid_t opid[PIDOF_OMITSZ], spid;
520 int f;
521 int first = 1;
522 int i, oind, opt, flags = 0;
523 int chroot_check = 0;
524 struct stat st;
525 char tmp[512];
526
527 for (oind = PIDOF_OMITSZ-1; oind > 0; oind--)
528 opid[oind] = 0;
529 opterr = 0;
530
531 while ((opt = getopt(argc,argv,"hco:sx")) != EOF) switch (opt) {
532 case '?':
533 nsyslog(LOG_ERR,"invalid options on command line!\n");
534 closelog();
535 exit(1);
536 case 'c':
537 if (geteuid() == 0) chroot_check = 1;
538 break;
539 case 'o':
540 if (oind >= PIDOF_OMITSZ -1) {
541 nsyslog(LOG_ERR,"omit pid buffer size %d "
542 "exceeded!\n", PIDOF_OMITSZ);
543 closelog();
544 exit(1);
545 }
546 if (strcmp("%PPID",optarg) == 0)
547 opid[oind] = getppid();
548 else if ((opid[oind] = atoi(optarg)) < 1) {
549 nsyslog(LOG_ERR,
550 "illegal omit pid value (%s)!\n",
551 optarg);
552 closelog();
553 exit(1);
554 }
555 oind++;
556 flags |= PIDOF_OMIT;
557 break;
558 case 's':
559 flags |= PIDOF_SINGLE;
560 break;
561 case 'x':
562 scripts_too++;
563 break;
564 default:
565 /* Nothing */
566 break;
567 }
568 argc -= optind;
569 argv += optind;
570
571 /* Check if we are in a chroot */
572 if (chroot_check) {
573 snprintf(tmp, 512, "/proc/%d/root", getpid());
574 if (stat(tmp, &st) < 0) {
575 nsyslog(LOG_ERR, "stat failed for %s!\n", tmp);
576 closelog();
577 exit(1);
578 }
579 }
580
581 /* Print out process-ID's one by one. */
582 readproc(DO_STAT);
583 for(f = 0; f < argc; f++) {
584 if ((q = pidof(argv[f])) != NULL) {
585 spid = 0;
586 while ((p = get_next_from_pid_q(q))) {
587 if (flags & PIDOF_OMIT) {
588 for (i = 0; i < oind; i++)
589 if (opid[i] == p->pid)
590 break;
591 /*
592 * On a match, continue with
593 * the for loop above.
594 */
595 if (i < oind)
596 continue;
597 }
598 if (flags & PIDOF_SINGLE) {
599 if (spid)
600 continue;
601 else
602 spid = 1;
603 }
604 if (chroot_check) {
605 struct stat st2;
606 snprintf(tmp, 512, "/proc/%d/root",
607 p->pid);
608 if (stat(tmp, &st2) < 0 ||
609 st.st_dev != st2.st_dev ||
610 st.st_ino != st2.st_ino) {
611 continue;
612 }
613 }
614 if (!first)
615 printf(" ");
616 printf("%d", p->pid);
617 first = 0;
618 }
619 }
620 }
621 if (!first)
622 printf("\n");
623 closelog();
624 return(first ? 1 : 0);
625 }
626
627
628
629 #define KILLALL_OMITSZ 16
630
631 /* Main for either killall or pidof. */
632 int main(int argc, char **argv)
633 {
634 PROC *p;
635 int pid, sid = -1;
636 pid_t opid[KILLALL_OMITSZ];
637 int i, oind, omit = 0;
638 int sig = SIGKILL;
639
640 /* return non-zero if no process was killed */
641 int retval = 2;
642
643 /* Get program name. */
644 if ((progname = strrchr(argv[0], '/')) == NULL)
645 progname = argv[0];
646 else
647 progname++;
648
649 /* Now connect to syslog. */
650 openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON);
651
652 /* Were we called as 'pidof' ? */
653 if (strcmp(progname, "pidof") == 0)
654 return main_pidof(argc, argv);
655
656 /* Right, so we are "killall". */
657 for (oind = KILLALL_OMITSZ-1; oind > 0; oind--)
658 opid[oind] = 0;
659
660 if (argc > 1) {
661 for (i = 1; i < argc; i++) {
662 if (argv[i][0] == '-') (argv[i])++;
663 if (argv[i][0] == 'o') {
664 if (++i >= argc) usage();
665 if (oind >= KILLALL_OMITSZ -1) {
666 nsyslog(LOG_ERR,"omit pid buffer size "
667 "%d exceeded!\n",
668 KILLALL_OMITSZ);
669 closelog();
670 exit(1);
671 }
672 if ((opid[oind] = atoi(argv[i])) < 1) {
673 nsyslog(LOG_ERR,
674 "illegal omit pid value "
675 "(%s)!\n", argv[i]);
676 closelog();
677 exit(1);
678 }
679 oind++;
680 omit = 1;
681 }
682 else if ((sig = atoi(argv[1])) <= 0 || sig > 31)
683 usage();
684 }
685 }
686
687 /* First get the /proc filesystem online. */
688 mount_proc();
689
690 /*
691 * Ignoring SIGKILL and SIGSTOP do not make sense, but
692 * someday kill(-1, sig) might kill ourself if we don't
693 * do this. This certainly is a valid concern for SIGTERM-
694 * Linux 2.1 might send the calling process the signal too.
695 */
696 signal(SIGTERM, SIG_IGN);
697 signal(SIGSTOP, SIG_IGN);
698 signal(SIGKILL, SIG_IGN);
699
700 /* lock us into memory */
701 mlockall(MCL_CURRENT | MCL_FUTURE);
702
703 /* Now stop all processes. */
704 kill(-1, SIGSTOP);
705 sent_sigstop = 1;
706
707 /* Read /proc filesystem */
708 if (readproc(NO_STAT) < 0) {
709 kill(-1, SIGCONT);
710 return(1);
711 }
712
713 /* Now kill all processes except init (pid 1) and our session. */
714 sid = (int)getsid(0);
715 pid = (int)getpid();
716 for (p = plist; p; p = p->next) {
717 if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel)
718 continue;
719 if (omit) {
720 for (i = 0; i < oind; i++)
721 if (opid[i] == p->pid)
722 break;
723 /* On a match, continue with the for loop above. */
724 if (i < oind)
725 continue;
726 }
727 kill(p->pid, sig);
728 retval = 0;
729 }
730
731 /* And let them continue. */
732 kill(-1, SIGCONT);
733
734 /* Done. */
735 closelog();
736
737 /* Force the kernel to run the scheduler */
738 usleep(1);
739
740 return retval;
741 }