1 /* $NetBSD: pkill.c,v 1.16 2005/10/10 22:13:20 kleink Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
8 * This code is derived from software contributed to The NetBSD Foundation
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/sysctl.h>
47 #include <sys/queue.h>
69 #define STATUS_MATCH 0
70 #define STATUS_NOMATCH 1
71 #define STATUS_BADUSAGE 2
72 #define STATUS_ERROR 3
77 /* Ignore system-processes (if '-S' flag is not specified) and myself. */
78 #define PSKIP(kp) ((kp)->ki_pid == mypid || \
79 (!kthreads && ((kp)->ki_flag & P_KTHREAD) != 0))
92 SLIST_ENTRY(list
) li_chain
;
96 SLIST_HEAD(listhead
, list
);
98 static struct kinfo_proc
*plist
;
99 static char *selected
;
100 static const char *delim
= "\n";
103 static int signum
= SIGTERM
;
106 static int interactive
;
109 static int matchargs
;
110 static int fullmatch
;
112 static int cflags
= REG_EXTENDED
;
116 static struct listhead euidlist
= SLIST_HEAD_INITIALIZER(list
);
117 static struct listhead ruidlist
= SLIST_HEAD_INITIALIZER(list
);
118 static struct listhead rgidlist
= SLIST_HEAD_INITIALIZER(list
);
119 static struct listhead pgrplist
= SLIST_HEAD_INITIALIZER(list
);
120 static struct listhead ppidlist
= SLIST_HEAD_INITIALIZER(list
);
121 static struct listhead tdevlist
= SLIST_HEAD_INITIALIZER(list
);
122 static struct listhead sidlist
= SLIST_HEAD_INITIALIZER(list
);
123 static struct listhead jidlist
= SLIST_HEAD_INITIALIZER(list
);
125 static void usage(void) __attribute__((__noreturn__
));
126 static int killact(const struct kinfo_proc
*);
127 static int grepact(const struct kinfo_proc
*);
128 static void makelist(struct listhead
*, enum listtype
, char *);
129 static int takepid(const char *, int);
132 main(int argc
, char **argv
)
134 char buf
[_POSIX2_LINE_MAX
], *mstr
, **pargv
, *p
, *q
, *pidfile
;
135 const char *execf
, *coref
;
137 int i
, ch
, bestidx
, rv
, criteria
, pidfromfile
, pidfilelock
;
139 int (*action
)(const struct kinfo_proc
*);
140 struct kinfo_proc
*kp
;
142 struct timeval best_tval
;
146 setlocale(LC_ALL
, "");
148 if (strcmp(getprogname(), "pgrep") == 0) {
155 if (argc
> 1 && p
[0] == '-') {
157 i
= (int)strtol(p
, &q
, 10);
163 if (strncasecmp(p
, "sig", 3) == 0)
165 for (i
= 1; i
< NSIG
; i
++)
166 if (strcasecmp(sys_signame
[i
], p
) == 0)
181 execf
= coref
= _PATH_DEVNULL
;
183 while ((ch
= getopt(argc
, argv
, "DF:G:ILM:N:P:SU:d:fg:ij:lnos:t:u:vx")) != -1)
193 makelist(&rgidlist
, LT_GROUP
, optarg
);
211 makelist(&ppidlist
, LT_GENERIC
, optarg
);
220 makelist(&ruidlist
, LT_USER
, optarg
);
232 makelist(&pgrplist
, LT_PGRP
, optarg
);
239 makelist(&jidlist
, LT_JID
, optarg
);
256 makelist(&sidlist
, LT_SID
, optarg
);
260 makelist(&tdevlist
, LT_TTY
, optarg
);
264 makelist(&euidlist
, LT_USER
, optarg
);
284 if (newest
&& oldest
)
285 errx(STATUS_ERROR
, "Options -n and -o are mutually exclusive");
287 pidfromfile
= takepid(pidfile
, pidfilelock
);
291 "Option -L doesn't make sense without -F");
299 * Retrieve the list of running processes from the kernel.
301 kd
= kvm_openfiles(execf
, coref
, NULL
, O_RDONLY
, buf
);
303 errx(STATUS_ERROR
, "Cannot open kernel files (%s)", buf
);
306 * Use KERN_PROC_PROC instead of KERN_PROC_ALL, since we
307 * just want processes and not individual kernel threads.
309 plist
= kvm_getprocs(kd
, KERN_PROC_PROC
, 0, &nproc
);
311 errx(STATUS_ERROR
, "Cannot get process list (%s)",
316 * Allocate memory which will be used to keep track of the
319 if ((selected
= malloc(nproc
)) == NULL
) {
320 err(STATUS_ERROR
, "Cannot allocate memory for %d processes",
323 memset(selected
, 0, nproc
);
326 * Refine the selection.
328 for (; *argv
!= NULL
; argv
++) {
329 if ((rv
= regcomp(®
, *argv
, cflags
)) != 0) {
330 regerror(rv
, ®
, buf
, sizeof(buf
));
331 errx(STATUS_BADUSAGE
,
332 "Cannot compile regular expression `%s' (%s)",
336 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
339 fprintf(stderr
, "* Skipped %5d %3d %s\n",
340 kp
->ki_pid
, kp
->ki_uid
, kp
->ki_comm
);
345 (pargv
= kvm_getargv(kd
, kp
, 0)) != NULL
) {
347 while (jsz
< sizeof(buf
) && *pargv
!= NULL
) {
348 jsz
+= snprintf(buf
+ jsz
,
350 pargv
[1] != NULL
? "%s " : "%s",
358 rv
= regexec(®
, mstr
, 1, ®match
, 0);
361 if (regmatch
.rm_so
== 0 &&
367 } else if (rv
!= REG_NOMATCH
) {
368 regerror(rv
, ®
, buf
, sizeof(buf
));
370 "Regular expression evaluation error (%s)",
374 const char *rv_res
= "NoMatch";
377 fprintf(stderr
, "* %s %5d %3d %s\n", rv_res
,
378 kp
->ki_pid
, kp
->ki_uid
, mstr
);
385 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
389 if (pidfromfile
>= 0 && kp
->ki_pid
!= pidfromfile
) {
394 SLIST_FOREACH(li
, &ruidlist
, li_chain
)
395 if (kp
->ki_ruid
== (uid_t
)li
->li_number
)
397 if (SLIST_FIRST(&ruidlist
) != NULL
&& li
== NULL
) {
402 SLIST_FOREACH(li
, &rgidlist
, li_chain
)
403 if (kp
->ki_rgid
== (gid_t
)li
->li_number
)
405 if (SLIST_FIRST(&rgidlist
) != NULL
&& li
== NULL
) {
410 SLIST_FOREACH(li
, &euidlist
, li_chain
)
411 if (kp
->ki_uid
== (uid_t
)li
->li_number
)
413 if (SLIST_FIRST(&euidlist
) != NULL
&& li
== NULL
) {
418 SLIST_FOREACH(li
, &ppidlist
, li_chain
)
419 if (kp
->ki_ppid
== (pid_t
)li
->li_number
)
421 if (SLIST_FIRST(&ppidlist
) != NULL
&& li
== NULL
) {
426 SLIST_FOREACH(li
, &pgrplist
, li_chain
)
427 if (kp
->ki_pgid
== (pid_t
)li
->li_number
)
429 if (SLIST_FIRST(&pgrplist
) != NULL
&& li
== NULL
) {
434 SLIST_FOREACH(li
, &tdevlist
, li_chain
) {
435 if (li
->li_number
== -1 &&
436 (kp
->ki_flag
& P_CONTROLT
) == 0)
438 if (kp
->ki_tdev
== (dev_t
)li
->li_number
)
441 if (SLIST_FIRST(&tdevlist
) != NULL
&& li
== NULL
) {
446 SLIST_FOREACH(li
, &sidlist
, li_chain
)
447 if (kp
->ki_sid
== (pid_t
)li
->li_number
)
449 if (SLIST_FIRST(&sidlist
) != NULL
&& li
== NULL
) {
454 SLIST_FOREACH(li
, &jidlist
, li_chain
) {
455 /* A particular jail ID, including 0 (not in jail) */
456 if (kp
->ki_jid
== (int)li
->li_number
)
459 if (kp
->ki_jid
> 0 && li
->li_number
== -1)
462 if (SLIST_FIRST(&jidlist
) != NULL
&& li
== NULL
) {
471 if (newest
|| oldest
) {
472 best_tval
.tv_sec
= 0;
473 best_tval
.tv_usec
= 0;
476 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
480 /* The first entry of the list which matched. */
482 } else if (timercmp(&kp
->ki_start
, &best_tval
, >)) {
483 /* This entry is newer than previous "best". */
484 if (oldest
) /* but we want the oldest */
487 /* This entry is older than previous "best". */
488 if (newest
) /* but we want the newest */
491 /* This entry is better than previous "best" entry. */
492 best_tval
.tv_sec
= kp
->ki_start
.tv_sec
;
493 best_tval
.tv_usec
= kp
->ki_start
.tv_usec
;
497 memset(selected
, 0, nproc
);
499 selected
[bestidx
] = 1;
503 * Take the appropriate action for each matched process, if any.
505 for (i
= 0, rv
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
516 exit(rv
? STATUS_MATCH
: STATUS_NOMATCH
);
525 ustr
= "[-LSfilnovx] [-d delim]";
527 ustr
= "[-signal] [-ILfinovx]";
530 "usage: %s %s [-F pidfile] [-G gid] [-M core] [-N system]\n"
531 " [-P ppid] [-U uid] [-g pgrp] [-j jid] [-s sid]\n"
532 " [-t tty] [-u euid] pattern ...\n", getprogname(),
535 exit(STATUS_BADUSAGE
);
539 show_process(const struct kinfo_proc
*kp
)
543 if ((longfmt
|| !pgrep
) && matchargs
&&
544 (argv
= kvm_getargv(kd
, kp
, 0)) != NULL
) {
545 printf("%d ", (int)kp
->ki_pid
);
546 for (; *argv
!= NULL
; argv
++) {
551 } else if (longfmt
|| !pgrep
)
552 printf("%d %s", (int)kp
->ki_pid
, kp
->ki_comm
);
554 printf("%d", (int)kp
->ki_pid
);
558 killact(const struct kinfo_proc
*kp
)
564 * Be careful, ask before killing.
570 first
= ch
= getchar();
571 while (ch
!= '\n' && ch
!= EOF
)
573 if (first
!= 'y' && first
!= 'Y')
576 if (kill(kp
->ki_pid
, signum
) == -1) {
578 * Check for ESRCH, which indicates that the process
579 * disappeared between us matching it and us
580 * signalling it; don't issue a warning about it.
583 warn("signalling pid %d", (int)kp
->ki_pid
);
585 * Return 0 to indicate that the process should not be
586 * considered a match, since we didn't actually get to
595 grepact(const struct kinfo_proc
*kp
)
604 makelist(struct listhead
*head
, enum listtype type
, char *src
)
610 const char *cp
, *prefix
;
611 char *sp
, *ep
, buf
[MAXPATHLEN
];
617 while ((sp
= strsep(&src
, ",")) != NULL
) {
621 if ((li
= malloc(sizeof(*li
))) == NULL
) {
622 err(STATUS_ERROR
, "Cannot allocate %zu bytes",
626 SLIST_INSERT_HEAD(head
, li
, li_chain
);
629 li
->li_number
= (uid_t
)strtol(sp
, &ep
, 0);
633 if (li
->li_number
== 0)
634 li
->li_number
= getpgrp();
637 if (li
->li_number
== 0)
638 li
->li_number
= getsid(mypid
);
641 if (li
->li_number
< 0)
642 errx(STATUS_BADUSAGE
,
643 "Negative jail ID `%s'", sp
);
644 /* For compatibility with old -j */
645 if (li
->li_number
== 0)
646 li
->li_number
= -1; /* any jail */
659 if ((pw
= getpwnam(sp
)) == NULL
)
660 errx(STATUS_BADUSAGE
, "Unknown user `%s'", sp
);
661 li
->li_number
= pw
->pw_uid
;
664 if ((gr
= getgrnam(sp
)) == NULL
)
665 errx(STATUS_BADUSAGE
, "Unknown group `%s'", sp
);
666 li
->li_number
= gr
->gr_gid
;
669 if (strcmp(sp
, "-") == 0) {
672 } else if (strcmp(sp
, "co") == 0) {
676 if (strncmp(sp
, "tty", 3) != 0)
680 snprintf(buf
, sizeof(buf
), "%s%s", prefix
, cp
);
682 if (stat(buf
, &st
) == -1) {
683 if (errno
== ENOENT
) {
684 errx(STATUS_BADUSAGE
,
685 "No such tty: `%s'", sp
);
687 err(STATUS_ERROR
, "Cannot access `%s'", sp
);
690 if ((st
.st_mode
& S_IFCHR
) == 0)
691 errx(STATUS_BADUSAGE
, "Not a tty: `%s'", sp
);
693 li
->li_number
= st
.st_rdev
;
696 if (strcmp(sp
, "none") == 0)
698 else if (strcmp(sp
, "any") == 0)
700 else if (*ep
!= '\0')
701 errx(STATUS_BADUSAGE
,
702 "Invalid jail ID `%s'", sp
);
714 takepid(const char *pidfile
, int pidfilelock
)
716 char *endp
, line
[BUFSIZ
];
720 fh
= fopen(pidfile
, "r");
722 err(STATUS_ERROR
, "Cannot open pidfile `%s'", pidfile
);
726 * If we can lock pidfile, this means that daemon is not
727 * running, so would be better not to kill some random process.
729 if (flock(fileno(fh
), LOCK_EX
| LOCK_NB
) == 0) {
731 errx(STATUS_ERROR
, "File '%s' can be locked", pidfile
);
733 if (errno
!= EWOULDBLOCK
) {
735 "Error while locking file '%s'", pidfile
);
740 if (fgets(line
, sizeof(line
), fh
) == NULL
) {
743 errx(STATUS_ERROR
, "Pidfile `%s' is empty", pidfile
);
746 err(STATUS_ERROR
, "Cannot read from pid file `%s'", pidfile
);
750 rval
= strtol(line
, &endp
, 10);
751 if (*endp
!= '\0' && !isspace((unsigned char)*endp
))
752 errx(STATUS_ERROR
, "Invalid pid in file `%s'", pidfile
);
753 else if (rval
< MIN_PID
|| rval
> MAX_PID
)
754 errx(STATUS_ERROR
, "Invalid pid in file `%s'", pidfile
);