]>
git.wh0rd.org - sysvinit.git/blob - src/killall5.c
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 (C) 1991-2004 Miquel van Smoorenburg.
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.
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.
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
39 #include <sys/types.h>
54 char *Version
= "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl";
56 #define STATNAMELEN 15
60 /* Info about a process. */
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. */
88 /* List of processes. */
91 /* Did we stop all processes ? */
96 char *progname
; /* the name of the running program */
98 __attribute__ ((format (printf
, 2, 3)))
100 void nsyslog(int pri
, char *fmt
, ...);
103 * Malloc space, barf if out of memory.
105 void *xmalloc(int bytes
)
109 if ((p
= malloc(bytes
)) == NULL
) {
110 if (sent_sigstop
) kill(-1, SIGCONT
);
111 nsyslog(LOG_ERR
, "out of memory");
118 * See if the proc filesystem is there. Mount if needed.
123 char *args
[] = { "mount", "-t", "proc", "proc", "/proc", 0 };
128 /* Stat /proc/version to see if /proc is mounted. */
129 if (stat("/proc/version", &st
) < 0 && errno
== ENOENT
) {
131 /* It's not there, so mount it. */
132 if ((pid
= fork()) < 0) {
133 nsyslog(LOG_ERR
, "cannot fork");
137 /* Try a few mount binaries. */
138 execv("/sbin/mount", args
);
139 execv("/bin/mount", args
);
141 /* Okay, I give up. */
142 nsyslog(LOG_ERR
, "cannot execute mount");
145 /* Wait for child. */
146 while ((rc
= wait(&wst
)) != pid
)
147 if (rc
< 0 && errno
== ECHILD
)
149 if (rc
!= pid
|| WEXITSTATUS(wst
) != 0)
150 nsyslog(LOG_ERR
, "mount returned non-zero exit status");
155 /* See if mount succeeded. */
156 if (stat("/proc/version", &st
) < 0) {
158 nsyslog(LOG_ERR
, "/proc not mounted, failed to mount.");
160 nsyslog(LOG_ERR
, "/proc unavailable.");
167 int readarg(FILE *fp
, char *buf
, int sz
)
171 while (f
< (sz
-1) && (c
= fgetc(fp
)) != EOF
&& c
)
175 return (c
== EOF
&& f
== 0) ? c
: f
;
179 * Read the proc filesystem.
180 * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse).
182 int readproc(int do_stat
)
192 unsigned long startcode
, endcode
;
195 /* Open the /proc directory. */
196 if (chdir("/proc") == -1) {
197 nsyslog(LOG_ERR
, "chdir /proc failed");
200 if ((dir
= opendir(".")) == NULL
) {
201 nsyslog(LOG_ERR
, "cannot opendir(/proc)");
205 /* Free the already existing process list. */
207 for (p
= plist
; n
; p
= n
) {
209 if (p
->argv0
) free(p
->argv0
);
210 if (p
->argv1
) free(p
->argv1
);
215 /* Walk through the directory. */
216 while ((d
= readdir(dir
)) != NULL
) {
218 /* See if this is a process */
219 if ((pid
= atoi(d
->d_name
)) == 0) continue;
221 /* Get a PROC struct . */
222 p
= (PROC
*)xmalloc(sizeof(PROC
));
223 memset(p
, 0, sizeof(PROC
));
225 /* Open the status file. */
226 snprintf(path
, sizeof(path
), "%s/stat", d
->d_name
);
228 /* Read SID & statname from it. */
229 if ((fp
= fopen(path
, "r")) != NULL
) {
231 fgets(buf
, sizeof(buf
), fp
);
233 /* See if name starts with '(' */
235 while (*s
!= ' ') s
++;
238 /* Read program name. */
239 q
= strrchr(buf
, ')');
243 "can't get program name from /proc/%s\n",
251 while (*q
!= ' ') q
++;
254 while (*q
== ' ') q
++;
255 p
->statname
= (char *)xmalloc(strlen(s
)+1);
256 strcpy(p
->statname
, s
);
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 "
264 &p
->sid
, &startcode
, &endcode
) != 3) {
266 nsyslog(LOG_ERR
, "can't read sid from %s\n",
271 if (startcode
== 0 && endcode
== 0)
275 /* Process disappeared.. */
280 snprintf(path
, sizeof(path
), "%s/cmdline", d
->d_name
);
281 if ((fp
= fopen(path
, "r")) != NULL
) {
283 /* Now read argv[0] */
284 f
= readarg(fp
, buf
, sizeof(buf
));
287 /* Store the name into malloced memory. */
288 p
->argv0
= (char *)xmalloc(f
+ 1);
289 strcpy(p
->argv0
, buf
);
291 /* Get a pointer to the basename. */
292 p
->argv0base
= strrchr(p
->argv0
, '/');
293 if (p
->argv0base
!= NULL
)
296 p
->argv0base
= p
->argv0
;
299 /* And read argv[1] */
300 while ((f
= readarg(fp
, buf
, sizeof(buf
))) != EOF
)
301 if (buf
[0] != '-') break;
304 /* Store the name into malloced memory. */
305 p
->argv1
= (char *)xmalloc(f
+ 1);
306 strcpy(p
->argv1
, buf
);
308 /* Get a pointer to the basename. */
309 p
->argv1base
= strrchr(p
->argv1
, '/');
310 if (p
->argv1base
!= NULL
)
313 p
->argv1base
= p
->argv1
;
319 /* Process disappeared.. */
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) {
331 /* Link it into the list. */
342 PIDQ_HEAD
*init_pid_q(PIDQ_HEAD
*q
)
344 q
->head
= q
->next
= q
->tail
= NULL
;
348 int empty_q(PIDQ_HEAD
*q
)
350 return (q
->head
== NULL
);
353 int add_pid_to_q(PIDQ_HEAD
*q
, PROC
*p
)
357 tmp
= (PIDQ
*)xmalloc(sizeof(PIDQ
));
372 PROC
*get_next_from_pid_q(PIDQ_HEAD
*q
)
387 /* Try to get the process ID of a given process. */
388 PIDQ_HEAD
*pidof(char *prog
)
401 /* Get basename of program. */
402 if ((s
= strrchr(prog
, '/')) == NULL
)
410 q
= (PIDQ_HEAD
*)xmalloc(sizeof(PIDQ_HEAD
));
413 /* Try to stat the executable. */
414 if (prog
[0] == '/' && stat(prog
, &st
) == 0)
417 /* First try to find a match based on dev/ino pair. */
419 for (p
= plist
; p
; p
= p
->next
) {
420 if (p
->dev
== st
.st_dev
&& p
->ino
== st
.st_ino
) {
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
) {
431 /* matching nonmatching
432 * proc name prog name prog name
433 * --- ----------- ------------
437 * Algorithm: Match if:
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 .
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);
451 /* For scripts, compare argv[1] as well. */
453 scripts_too
&& p
->statname
&& p
->argv1base
454 && !strncmp(p
->statname
, p
->argv1base
, STATNAMELEN
)
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);
463 * if we have a space in argv0, process probably
464 * used setproctitle so try statname.
466 if (strlen(s
) <= STATNAMELEN
&&
469 strchr(p
->argv0
, ' '))) {
470 ok
|= (strcmp(p
->statname
, s
) == 0);
472 if (ok
) add_pid_to_q(q
, p
);
478 /* Give usage message and exit. */
481 nsyslog(LOG_ERR
, "only one argument, a signal number, allowed");
486 /* write to syslog file if not open terminal */
488 __attribute__ ((format (printf
, 2, 3)))
490 void nsyslog(int pri
, char *fmt
, ...)
496 if (ttyname(0) == NULL
) {
497 vsyslog(pri
, fmt
, args
);
499 fprintf(stderr
, "%s: ",progname
);
500 vfprintf(stderr
, fmt
, args
);
501 fprintf(stderr
, "\n");
507 #define PIDOF_SINGLE 0x01
508 #define PIDOF_OMIT 0x02
510 #define PIDOF_OMITSZ 5
513 * Pidof functionality.
515 int main_pidof(int argc
, char **argv
)
519 pid_t opid
[PIDOF_OMITSZ
], spid
;
522 int i
, oind
, opt
, flags
= 0;
523 int chroot_check
= 0;
527 for (oind
= PIDOF_OMITSZ
-1; oind
> 0; oind
--)
531 while ((opt
= getopt(argc
,argv
,"hco:sx")) != EOF
) switch (opt
) {
533 nsyslog(LOG_ERR
,"invalid options on command line!\n");
537 if (geteuid() == 0) chroot_check
= 1;
540 if (oind
>= PIDOF_OMITSZ
-1) {
541 nsyslog(LOG_ERR
,"omit pid buffer size %d "
542 "exceeded!\n", PIDOF_OMITSZ
);
546 if (strcmp("%PPID",optarg
) == 0)
547 opid
[oind
] = getppid();
548 else if ((opid
[oind
] = atoi(optarg
)) < 1) {
550 "illegal omit pid value (%s)!\n",
559 flags
|= PIDOF_SINGLE
;
571 /* Check if we are in a chroot */
573 snprintf(tmp
, 512, "/proc/%d/root", getpid());
574 if (stat(tmp
, &st
) < 0) {
575 nsyslog(LOG_ERR
, "stat failed for %s!\n", tmp
);
581 /* Print out process-ID's one by one. */
583 for(f
= 0; f
< argc
; f
++) {
584 if ((q
= pidof(argv
[f
])) != NULL
) {
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
)
592 * On a match, continue with
593 * the for loop above.
598 if (flags
& PIDOF_SINGLE
) {
606 snprintf(tmp
, 512, "/proc/%d/root",
608 if (stat(tmp
, &st2
) < 0 ||
609 st
.st_dev
!= st2
.st_dev
||
610 st
.st_ino
!= st2
.st_ino
) {
616 printf("%d", p
->pid
);
624 return(first
? 1 : 0);
629 #define KILLALL_OMITSZ 16
631 /* Main for either killall or pidof. */
632 int main(int argc
, char **argv
)
636 pid_t opid
[KILLALL_OMITSZ
];
637 int i
, oind
, omit
= 0;
640 /* return non-zero if no process was killed */
643 /* Get program name. */
644 if ((progname
= strrchr(argv
[0], '/')) == NULL
)
649 /* Now connect to syslog. */
650 openlog(progname
, LOG_CONS
|LOG_PID
, LOG_DAEMON
);
652 /* Were we called as 'pidof' ? */
653 if (strcmp(progname
, "pidof") == 0)
654 return main_pidof(argc
, argv
);
656 /* Right, so we are "killall". */
657 for (oind
= KILLALL_OMITSZ
-1; oind
> 0; oind
--)
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 "
672 if ((opid
[oind
] = atoi(argv
[i
])) < 1) {
674 "illegal omit pid value "
682 else if ((sig
= atoi(argv
[1])) <= 0 || sig
> 31)
687 /* First get the /proc filesystem online. */
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.
696 signal(SIGTERM
, SIG_IGN
);
697 signal(SIGSTOP
, SIG_IGN
);
698 signal(SIGKILL
, SIG_IGN
);
700 /* lock us into memory */
701 mlockall(MCL_CURRENT
| MCL_FUTURE
);
703 /* Now stop all processes. */
707 /* Read /proc filesystem */
708 if (readproc(NO_STAT
) < 0) {
713 /* Now kill all processes except init (pid 1) and our session. */
714 sid
= (int)getsid(0);
716 for (p
= plist
; p
; p
= p
->next
) {
717 if (p
->pid
== 1 || p
->pid
== pid
|| p
->sid
== sid
|| p
->kernel
)
720 for (i
= 0; i
< oind
; i
++)
721 if (opid
[i
] == p
->pid
)
723 /* On a match, continue with the for loop above. */
731 /* And let them continue. */
737 /* Force the kernel to run the scheduler */