1 /* $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $ */
2 /* $DragonFly: src/usr.bin/pkill/pkill.c,v 1.9 2007/02/01 10:33:26 corecode Exp $ */
5 * Copyright (c) 2002 The NetBSD Foundation, Inc.
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.
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/sysctl.h>
44 #include <sys/queue.h>
46 #include <sys/fcntl.h>
63 #define STATUS_MATCH 0
64 #define STATUS_NOMATCH 1
65 #define STATUS_BADUSAGE 2
66 #define STATUS_ERROR 3
69 LT_USER
, /* real or effective user: uid_t */
70 LT_GROUP
, /* group: gid_t */
71 LT_TTY
, /* tty: dev_t */
72 LT_PPID
, /* parent pid: pid_t */
73 LT_PGRP
, /* process group: pid_t */
74 LT_SID
/* session id: pid_t */
78 SLIST_ENTRY(list
) li_chain
;
87 SLIST_HEAD(listhead
, list
);
89 struct kinfo_proc
*plist
;
91 const char *delim
= "\n";
103 struct listhead euidlist
= SLIST_HEAD_INITIALIZER(list
);
104 struct listhead ruidlist
= SLIST_HEAD_INITIALIZER(list
);
105 struct listhead rgidlist
= SLIST_HEAD_INITIALIZER(list
);
106 struct listhead pgrplist
= SLIST_HEAD_INITIALIZER(list
);
107 struct listhead ppidlist
= SLIST_HEAD_INITIALIZER(list
);
108 struct listhead tdevlist
= SLIST_HEAD_INITIALIZER(list
);
109 struct listhead sidlist
= SLIST_HEAD_INITIALIZER(list
);
112 void killact(struct kinfo_proc
*, int);
113 void grepact(struct kinfo_proc
*, int);
114 int parse_pid(const char *, char **, struct list
*, pid_t
);
115 void makelist(struct listhead
*, enum listtype
, char *);
118 * pkill - list or signal selected processes based on regular expression.
121 main(int argc
, char **argv
)
123 char buf
[_POSIX2_LINE_MAX
], *mstr
, **pargv
, *p
, *q
;
124 int i
, ch
, bestidx
, rv
, criteria
;
126 void (*action
)(struct kinfo_proc
*, int);
127 struct kinfo_proc
*kp
;
132 const char *kvmf
= _PATH_DEVNULL
;
134 if (strcmp(getprogname(), "pgrep") == 0) {
142 * For pkill only: parse the signal (number or name) to send.
144 if (argc
> 1 && p
[0] == '-') {
146 i
= (int)strtol(p
, &q
, 10);
152 if (strncasecmp(p
, "sig", 3) == 0)
154 for (i
= 1; i
< NSIG
; i
++) {
155 if (strcasecmp(sys_signame
[i
], p
) == 0)
169 while ((ch
= getopt(argc
, argv
, "G:P:U:d:fg:lns:t:u:vx")) != -1) {
172 makelist(&rgidlist
, LT_GROUP
, optarg
);
176 makelist(&ppidlist
, LT_PPID
, optarg
);
180 makelist(&ruidlist
, LT_USER
, optarg
);
192 makelist(&pgrplist
, LT_PGRP
, optarg
);
205 makelist(&sidlist
, LT_SID
, optarg
);
209 makelist(&tdevlist
, LT_TTY
, optarg
);
213 makelist(&euidlist
, LT_USER
, optarg
);
238 * Retrieve the list of running processes from the kernel.
240 kd
= kvm_openfiles(kvmf
, kvmf
, NULL
, O_RDONLY
, buf
);
242 errx(STATUS_ERROR
, "kvm_openfiles(): %s", buf
);
244 plist
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nproc
);
246 errx(STATUS_ERROR
, "cannot list processes");
249 * Allocate memory which will be used to keep track of the
252 if ((selected
= malloc(nproc
)) == NULL
)
253 errx(STATUS_ERROR
, "memory allocation failure");
254 memset(selected
, 0, nproc
);
257 * Refine the selection.
259 for (; *argv
!= NULL
; argv
++) {
260 if ((rv
= regcomp(®
, *argv
, REG_EXTENDED
)) != 0) {
261 regerror(rv
, ®
, buf
, sizeof(buf
));
262 errx(STATUS_BADUSAGE
, "bad expression: %s", buf
);
265 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
266 if ((kp
->kp_flags
& P_SYSTEM
) != 0 || kp
->kp_pid
== mypid
)
270 if ((pargv
= kvm_getargv(kd
, kp
, 0)) == NULL
)
274 while (j
< sizeof(buf
) && *pargv
!= NULL
) {
275 j
+= snprintf(buf
+ j
, sizeof(buf
) - j
,
276 pargv
[1] != NULL
? "%s " : "%s",
285 rv
= regexec(®
, mstr
, 1, ®match
, 0);
288 if (regmatch
.rm_so
== 0 &&
289 regmatch
.rm_eo
== (regoff_t
)strlen(mstr
))
293 } else if (rv
!= REG_NOMATCH
) {
294 regerror(rv
, ®
, buf
, sizeof(buf
));
295 errx(STATUS_ERROR
, "regexec(): %s", buf
);
303 * Iterate through the list of processes, deselecting each one
304 * if it fails to meet the established criteria.
306 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
307 if ((kp
->kp_flags
& P_SYSTEM
) != 0)
310 SLIST_FOREACH(li
, &ruidlist
, li_chain
) {
311 if (kp
->kp_ruid
== li
->li_datum
.ld_uid
)
314 if (SLIST_FIRST(&ruidlist
) != NULL
&& li
== NULL
) {
319 SLIST_FOREACH(li
, &rgidlist
, li_chain
) {
320 if (kp
->kp_rgid
== li
->li_datum
.ld_gid
)
323 if (SLIST_FIRST(&rgidlist
) != NULL
&& li
== NULL
) {
328 SLIST_FOREACH(li
, &euidlist
, li_chain
) {
329 if (kp
->kp_uid
== li
->li_datum
.ld_uid
)
332 if (SLIST_FIRST(&euidlist
) != NULL
&& li
== NULL
) {
337 SLIST_FOREACH(li
, &ppidlist
, li_chain
) {
338 if (kp
->kp_ppid
== li
->li_datum
.ld_pid
)
341 if (SLIST_FIRST(&ppidlist
) != NULL
&& li
== NULL
) {
346 SLIST_FOREACH(li
, &pgrplist
, li_chain
) {
347 if (kp
->kp_pgid
== li
->li_datum
.ld_pid
)
350 if (SLIST_FIRST(&pgrplist
) != NULL
&& li
== NULL
) {
355 SLIST_FOREACH(li
, &tdevlist
, li_chain
) {
356 if (li
->li_datum
.ld_dev
== NODEV
&&
357 (kp
->kp_flags
& P_CONTROLT
) == 0)
359 if (kp
->kp_tdev
== li
->li_datum
.ld_dev
)
362 if (SLIST_FIRST(&tdevlist
) != NULL
&& li
== NULL
) {
367 SLIST_FOREACH(li
, &sidlist
, li_chain
) {
368 if (kp
->kp_sid
== li
->li_datum
.ld_pid
)
371 if (SLIST_FIRST(&sidlist
) != NULL
&& li
== NULL
) {
385 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
389 if (kp
->kp_start
.tv_sec
> best
.tv_sec
||
390 (kp
->kp_start
.tv_sec
== best
.tv_sec
391 && kp
->kp_start
.tv_usec
> best
.tv_usec
)) {
392 best
.tv_sec
= kp
->kp_start
.tv_sec
;
393 best
.tv_usec
= kp
->kp_start
.tv_usec
;
398 memset(selected
, 0, nproc
);
400 selected
[bestidx
] = 1;
404 * Take the appropriate action for each matched process, if any.
406 for (i
= 0, j
= 0, rv
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
407 if (kp
->kp_pid
== mypid
)
415 if ((kp
->kp_flags
& P_SYSTEM
) != 0)
425 exit(rv
? STATUS_MATCH
: STATUS_NOMATCH
);
434 ustr
= "[-flnvx] [-d delim]";
436 ustr
= "[-signal] [-fnvx]";
439 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n"
440 " [-t tty] [-u euid] pattern ...\n", getprogname(),
447 * Action callback to signal the given process (pkill).
450 killact(struct kinfo_proc
*kp
, int dummy __unused
)
452 if (kill(kp
->kp_pid
, signum
) == -1)
453 err(STATUS_ERROR
, "signalling pid %d", (int)kp
->kp_pid
);
457 * Action callback to print the pid of the given process (pgrep).
460 grepact(struct kinfo_proc
*kp
, int printdelim
)
465 fputs(delim
, stdout
);
467 if (longfmt
&& matchargs
) {
468 if ((argv
= kvm_getargv(kd
, kp
, 0)) == NULL
)
471 printf("%d ", (int)kp
->kp_pid
);
472 for (; *argv
!= NULL
; argv
++) {
478 printf("%d %s", (int)kp
->kp_pid
, kp
->kp_comm
);
480 printf("%d", (int)kp
->kp_pid
);
485 * Parse a pid from the given string. If zero, use a given default.
488 parse_pid(const char *string
, char **p
, struct list
*li
, pid_t default_pid
)
492 l
= strtol(string
, p
, 0);
493 li
->li_datum
.ld_pid
= (l
== 0 ? default_pid
: (pid_t
)l
);
498 * Populate a list from a comma-seperated string of items.
499 * The possible valid values for each item depends on the type of list.
502 makelist(struct listhead
*head
, enum listtype type
, char *src
)
508 const char *sp
, *tty_name
;
509 char *p
, buf
[MAXPATHLEN
];
514 while ((sp
= strsep(&src
, ",")) != NULL
) {
518 if ((li
= malloc(sizeof(*li
))) == NULL
)
519 errx(STATUS_ERROR
, "memory allocation failure");
520 SLIST_INSERT_HEAD(head
, li
, li_chain
);
525 if (!parse_pid(sp
, &p
, li
, (pid_t
)0))
529 if (!parse_pid(sp
, &p
, li
, getpgrp()))
533 if (!parse_pid(sp
, &p
, li
, getsid(mypid
)))
537 li
->li_datum
.ld_uid
= (uid_t
)strtol(sp
, &p
, 0);
539 if ((pw
= getpwnam(sp
)) == NULL
) {
540 errx(STATUS_BADUSAGE
,
541 "unknown user `%s'", optarg
);
543 li
->li_datum
.ld_uid
= pw
->pw_uid
;
547 li
->li_datum
.ld_gid
= (gid_t
)strtol(sp
, &p
, 0);
549 if ((gr
= getgrnam(sp
)) == NULL
) {
550 errx(STATUS_BADUSAGE
,
551 "unknown group `%s'", optarg
);
553 li
->li_datum
.ld_gid
= gr
->gr_gid
;
557 if (strcmp(sp
, "-") == 0) {
558 li
->li_datum
.ld_dev
= NODEV
;
560 } else if (strcmp(sp
, "co") == 0)
561 tty_name
= "console";
562 else if (strncmp(sp
, "tty", 3) == 0)
567 if (tty_name
== NULL
)
568 snprintf(buf
, sizeof(buf
), "/dev/tty%s", sp
);
570 snprintf(buf
, sizeof(buf
), "/dev/%s", tty_name
);
572 if (stat(buf
, &st
) < 0) {
574 errx(STATUS_BADUSAGE
,
575 "no such tty: `%s'", sp
);
576 err(STATUS_ERROR
, "stat(%s)", sp
);
579 if ((st
.st_mode
& S_IFCHR
) == 0)
580 errx(STATUS_BADUSAGE
, "not a tty: `%s'", sp
);
582 li
->li_datum
.ld_dev
= st
.st_rdev
;