work work werk
[mikesnafu-overlay.git] / sysvinit-2.86 / src / killall5.c
blob2775777ee4649e60b27ddefa01d9593bb1c19745
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.
6 * pidof.c Tries to get the pid of the process[es] named.
8 * Version: 2.86 30-Jul-2004 MvS
10 * Usage: killall5 [-][signal]
11 * pidof [-s] [-o omitpid [-o omitpid]] program [program..]
13 * Authors: Miquel van Smoorenburg, miquels@cistron.nl
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
22 * This file is part of the sysvinit suite,
23 * Copyright 1991-2004 Miquel van Smoorenburg.
25 * This program is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU General Public License
27 * as published by the Free Software Foundation; either version
28 * 2 of the License, or (at your option) any later version.
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <sys/wait.h>
37 #include <sys/stat.h>
38 #include <signal.h>
39 #include <dirent.h>
40 #include <syslog.h>
41 #include <getopt.h>
42 #include <stdarg.h>
44 char *Version = "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl";
46 #define STATNAMELEN 15
48 /* Info about a process. */
49 typedef struct proc {
50 char *argv0; /* Name as found out from argv[0] */
51 char *argv0base; /* `basename argv[1]` */
52 char *argv1; /* Name as found out from argv[1] */
53 char *argv1base; /* `basename argv[1]` */
54 char *statname; /* the statname without braces */
55 ino_t ino; /* Inode number */
56 dev_t dev; /* Device it is on */
57 pid_t pid; /* Process ID. */
58 int sid; /* Session ID. */
59 int kernel; /* Kernel thread or zombie. */
60 struct proc *next; /* Pointer to next struct. */
61 } PROC;
63 /* pid queue */
65 typedef struct pidq {
66 PROC *proc;
67 struct pidq *next;
68 } PIDQ;
70 typedef struct {
71 PIDQ *head;
72 PIDQ *tail;
73 PIDQ *next;
74 } PIDQ_HEAD;
76 /* List of processes. */
77 PROC *plist;
79 /* Did we stop all processes ? */
80 int sent_sigstop;
82 int scripts_too = 0;
84 char *progname; /* the name of the running program */
85 #ifdef __GNUC__
86 __attribute__ ((format (printf, 2, 3)))
87 #endif
88 void nsyslog(int pri, char *fmt, ...);
91 * Malloc space, barf if out of memory.
93 void *xmalloc(int bytes)
95 void *p;
97 if ((p = malloc(bytes)) == NULL) {
98 if (sent_sigstop) kill(-1, SIGCONT);
99 nsyslog(LOG_ERR, "out of memory");
100 exit(1);
102 return p;
106 * See if the proc filesystem is there. Mount if needed.
108 int mount_proc(void)
110 struct stat st;
111 char *args[] = { "mount", "-t", "proc", "proc", "/proc", 0 };
112 pid_t pid, rc;
113 int wst;
114 int did_mount = 0;
116 /* Stat /proc/version to see if /proc is mounted. */
117 if (stat("/proc/version", &st) < 0 && errno == ENOENT) {
119 /* It's not there, so mount it. */
120 if ((pid = fork()) < 0) {
121 nsyslog(LOG_ERR, "cannot fork");
122 exit(1);
124 if (pid == 0) {
125 /* Try a few mount binaries. */
126 execv("/sbin/mount", args);
127 execv("/bin/mount", args);
129 /* Okay, I give up. */
130 nsyslog(LOG_ERR, "cannot execute mount");
131 exit(1);
133 /* Wait for child. */
134 while ((rc = wait(&wst)) != pid)
135 if (rc < 0 && errno == ECHILD)
136 break;
137 if (rc != pid || WEXITSTATUS(wst) != 0)
138 nsyslog(LOG_ERR, "mount returned non-zero exit status");
140 did_mount = 1;
143 /* See if mount succeeded. */
144 if (stat("/proc/version", &st) < 0) {
145 if (errno == ENOENT)
146 nsyslog(LOG_ERR, "/proc not mounted, failed to mount.");
147 else
148 nsyslog(LOG_ERR, "/proc unavailable.");
149 exit(1);
152 return did_mount;
155 int readarg(FILE *fp, char *buf, int sz)
157 int c = 0, f = 0;
159 while (f < (sz-1) && (c = fgetc(fp)) != EOF && c)
160 buf[f++] = c;
161 buf[f] = 0;
163 return (c == EOF && f == 0) ? c : f;
167 * Read the proc filesystem.
169 int readproc()
171 DIR *dir;
172 FILE *fp;
173 PROC *p, *n;
174 struct dirent *d;
175 struct stat st;
176 char path[256];
177 char buf[256];
178 char *s, *q;
179 unsigned long startcode, endcode;
180 int pid, f;
182 /* Open the /proc directory. */
183 if ((dir = opendir("/proc")) == NULL) {
184 nsyslog(LOG_ERR, "cannot opendir(/proc)");
185 return -1;
188 /* Free the already existing process list. */
189 n = plist;
190 for (p = plist; n; p = n) {
191 n = p->next;
192 if (p->argv0) free(p->argv0);
193 if (p->argv1) free(p->argv1);
194 free(p);
196 plist = NULL;
198 /* Walk through the directory. */
199 while ((d = readdir(dir)) != NULL) {
201 /* See if this is a process */
202 if ((pid = atoi(d->d_name)) == 0) continue;
204 /* Get a PROC struct . */
205 p = (PROC *)xmalloc(sizeof(PROC));
206 memset(p, 0, sizeof(PROC));
208 /* Open the status file. */
209 snprintf(path, sizeof(path), "/proc/%s/stat", d->d_name);
211 /* Read SID & statname from it. */
212 if ((fp = fopen(path, "r")) != NULL) {
213 buf[0] = 0;
214 fgets(buf, sizeof(buf), fp);
216 /* See if name starts with '(' */
217 s = buf;
218 while (*s != ' ') s++;
219 s++;
220 if (*s == '(') {
221 /* Read program name. */
222 q = strrchr(buf, ')');
223 if (q == NULL) {
224 p->sid = 0;
225 nsyslog(LOG_ERR,
226 "can't get program name from %s\n",
227 path);
228 free(p);
229 continue;
231 s++;
232 } else {
233 q = s;
234 while (*q != ' ') q++;
236 *q++ = 0;
237 while (*q == ' ') q++;
238 p->statname = (char *)xmalloc(strlen(s)+1);
239 strcpy(p->statname, s);
241 /* Get session, startcode, endcode. */
242 startcode = endcode = 0;
243 if (sscanf(q, "%*c %*d %*d %d %*d %*d %*u %*u "
244 "%*u %*u %*u %*u %*u %*d %*d "
245 "%*d %*d %*d %*d %*u %*u %*d "
246 "%*u %lu %lu",
247 &p->sid, &startcode, &endcode) != 3) {
248 p->sid = 0;
249 nsyslog(LOG_ERR, "can't read sid from %s\n",
250 path);
251 free(p);
252 continue;
254 if (startcode == 0 && endcode == 0)
255 p->kernel = 1;
256 fclose(fp);
257 } else {
258 /* Process disappeared.. */
259 free(p);
260 continue;
263 snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
264 if ((fp = fopen(path, "r")) != NULL) {
266 /* Now read argv[0] */
267 f = readarg(fp, buf, sizeof(buf));
269 if (buf[0]) {
270 /* Store the name into malloced memory. */
271 p->argv0 = (char *)xmalloc(f + 1);
272 strcpy(p->argv0, buf);
274 /* Get a pointer to the basename. */
275 p->argv0base = strrchr(p->argv0, '/');
276 if (p->argv0base != NULL)
277 p->argv0base++;
278 else
279 p->argv0base = p->argv0;
282 /* And read argv[1] */
283 while ((f = readarg(fp, buf, sizeof(buf))) != EOF)
284 if (buf[0] != '-') break;
286 if (buf[0]) {
287 /* Store the name into malloced memory. */
288 p->argv1 = (char *)xmalloc(f + 1);
289 strcpy(p->argv1, buf);
291 /* Get a pointer to the basename. */
292 p->argv1base = strrchr(p->argv1, '/');
293 if (p->argv1base != NULL)
294 p->argv1base++;
295 else
296 p->argv1base = p->argv1;
299 fclose(fp);
301 } else {
302 /* Process disappeared.. */
303 free(p);
304 continue;
307 /* Try to stat the executable. */
308 snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name);
309 if (stat(path, &st) == 0) {
310 p->dev = st.st_dev;
311 p->ino = st.st_ino;
314 /* Link it into the list. */
315 p->next = plist;
316 plist = p;
317 p->pid = pid;
319 closedir(dir);
321 /* Done. */
322 return 0;
325 PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q)
327 q->head = q->next = q->tail = NULL;
328 return q;
331 int empty_q(PIDQ_HEAD *q)
333 return (q->head == NULL);
336 int add_pid_to_q(PIDQ_HEAD *q, PROC *p)
338 PIDQ *tmp;
340 tmp = (PIDQ *)xmalloc(sizeof(PIDQ));
342 tmp->proc = p;
343 tmp->next = NULL;
345 if (empty_q(q)) {
346 q->head = tmp;
347 q->tail = tmp;
348 } else {
349 q->tail->next = tmp;
350 q->tail = tmp;
352 return 0;
355 PROC *get_next_from_pid_q(PIDQ_HEAD *q)
357 PROC *p;
358 PIDQ *tmp = q->head;
360 if (!empty_q(q)) {
361 p = q->head->proc;
362 q->head = tmp->next;
363 free(tmp);
364 return p;
367 return NULL;
370 /* Try to get the process ID of a given process. */
371 PIDQ_HEAD *pidof(char *prog)
373 PROC *p;
374 PIDQ_HEAD *q;
375 struct stat st;
376 char *s;
377 int dostat = 0;
378 int foundone = 0;
379 int ok = 0;
381 /* Try to stat the executable. */
382 if (prog[0] == '/' && stat(prog, &st) == 0) dostat++;
384 /* Get basename of program. */
385 if ((s = strrchr(prog, '/')) == NULL)
386 s = prog;
387 else
388 s++;
390 q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD));
391 q = init_pid_q(q);
393 /* First try to find a match based on dev/ino pair. */
394 if (dostat) {
395 for (p = plist; p; p = p->next) {
396 if (p->dev == st.st_dev && p->ino == st.st_ino) {
397 add_pid_to_q(q, p);
398 foundone++;
403 /* If we didn't find a match based on dev/ino, try the name. */
404 if (!foundone) for (p = plist; p; p = p->next) {
405 ok = 0;
407 /* Compare name (both basename and full path) */
408 ok += (p->argv0 && strcmp(p->argv0, prog) == 0);
409 ok += (p->argv0 && strcmp(p->argv0base, s) == 0);
411 /* For scripts, compare argv[1] as well. */
412 if (scripts_too && p->argv1 &&
413 !strncmp(p->statname, p->argv1base, STATNAMELEN)) {
414 ok += (strcmp(p->argv1, prog) == 0);
415 ok += (strcmp(p->argv1base, s) == 0);
419 * if we have a space in argv0, process probably
420 * used setproctitle so try statname.
422 if (strlen(s) <= STATNAMELEN &&
423 (p->argv0 == NULL ||
424 p->argv0[0] == 0 ||
425 strchr(p->argv0, ' '))) {
426 ok += (strcmp(p->statname, s) == 0);
428 if (ok) add_pid_to_q(q, p);
431 return q;
434 /* Give usage message and exit. */
435 void usage(void)
437 nsyslog(LOG_ERR, "only one argument, a signal number, allowed");
438 closelog();
439 exit(1);
442 /* write to syslog file if not open terminal */
443 #ifdef __GNUC__
444 __attribute__ ((format (printf, 2, 3)))
445 #endif
446 void nsyslog(int pri, char *fmt, ...)
448 va_list args;
450 va_start(args, fmt);
452 if (ttyname(0) == NULL) {
453 vsyslog(pri, fmt, args);
454 } else {
455 fprintf(stderr, "%s: ",progname);
456 vfprintf(stderr, fmt, args);
457 fprintf(stderr, "\n");
460 va_end(args);
463 #define PIDOF_SINGLE 0x01
464 #define PIDOF_OMIT 0x02
466 #define PIDOF_OMITSZ 5
469 * Pidof functionality.
471 int main_pidof(int argc, char **argv)
473 PIDQ_HEAD *q;
474 PROC *p;
475 pid_t opid[PIDOF_OMITSZ], spid;
476 int f;
477 int first = 1;
478 int i, oind, opt, flags = 0;
480 for (oind = PIDOF_OMITSZ-1; oind > 0; oind--)
481 opid[oind] = 0;
482 opterr = 0;
484 while ((opt = getopt(argc,argv,"ho:sx")) != EOF) switch (opt) {
485 case '?':
486 nsyslog(LOG_ERR,"invalid options on command line!\n");
487 closelog();
488 exit(1);
489 case 'o':
490 if (oind >= PIDOF_OMITSZ -1) {
491 nsyslog(LOG_ERR,"omit pid buffer size %d "
492 "exceeded!\n", PIDOF_OMITSZ);
493 closelog();
494 exit(1);
496 if (strcmp("%PPID",optarg) == 0)
497 opid[oind] = getppid();
498 else if ((opid[oind] = atoi(optarg)) < 1) {
499 nsyslog(LOG_ERR,
500 "illegal omit pid value (%s)!\n",
501 optarg);
502 closelog();
503 exit(1);
505 oind++;
506 flags |= PIDOF_OMIT;
507 break;
508 case 's':
509 flags |= PIDOF_SINGLE;
510 break;
511 case 'x':
512 scripts_too++;
513 break;
514 default:
515 /* Nothing */
516 break;
518 argc -= optind;
519 argv += optind;
521 /* Print out process-ID's one by one. */
522 readproc();
523 for(f = 0; f < argc; f++) {
524 if ((q = pidof(argv[f])) != NULL) {
525 spid = 0;
526 while ((p = get_next_from_pid_q(q))) {
527 if (flags & PIDOF_OMIT) {
528 for (i = 0; i < oind; i++)
529 if (opid[i] == p->pid)
530 break;
532 * On a match, continue with
533 * the for loop above.
535 if (i < oind)
536 continue;
538 if (flags & PIDOF_SINGLE) {
539 if (spid)
540 continue;
541 else
542 spid = 1;
544 if (!first)
545 printf(" ");
546 printf("%d", p->pid);
547 first = 0;
551 printf("\n");
552 closelog();
553 return(first ? 1 : 0);
558 /* Main for either killall or pidof. */
559 int main(int argc, char **argv)
561 PROC *p;
562 int pid, sid = -1;
563 int sig = SIGKILL;
565 /* Get program name. */
566 if ((progname = strrchr(argv[0], '/')) == NULL)
567 progname = argv[0];
568 else
569 progname++;
571 /* Now connect to syslog. */
572 openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON);
574 /* Were we called as 'pidof' ? */
575 if (strcmp(progname, "pidof") == 0)
576 return main_pidof(argc, argv);
578 /* Right, so we are "killall". */
579 if (argc > 1) {
580 if (argc != 2) usage();
581 if (argv[1][0] == '-') (argv[1])++;
582 if ((sig = atoi(argv[1])) <= 0 || sig > 31) usage();
585 /* First get the /proc filesystem online. */
586 mount_proc();
589 * Ignoring SIGKILL and SIGSTOP do not make sense, but
590 * someday kill(-1, sig) might kill ourself if we don't
591 * do this. This certainly is a valid concern for SIGTERM-
592 * Linux 2.1 might send the calling process the signal too.
594 signal(SIGTERM, SIG_IGN);
595 signal(SIGSTOP, SIG_IGN);
596 signal(SIGKILL, SIG_IGN);
598 /* Now stop all processes. */
599 kill(-1, SIGSTOP);
600 sent_sigstop = 1;
602 /* Read /proc filesystem */
603 if (readproc() < 0) {
604 kill(-1, SIGCONT);
605 exit(1);
608 /* Now kill all processes except our session. */
609 sid = (int)getsid(0);
610 pid = (int)getpid();
611 for (p = plist; p; p = p->next)
612 if (p->pid != pid && p->sid != sid && !p->kernel)
613 kill(p->pid, sig);
615 /* And let them continue. */
616 kill(-1, SIGCONT);
618 /* Done. */
619 closelog();
621 return 0;