1 /* $NetBSD: pkill.c,v 1.24 2009/02/28 18:16:11 christos Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: pkill.c,v 1.24 2009/02/28 18:16:11 christos Exp $");
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysctl.h>
41 #include <sys/queue.h>
59 #define STATUS_MATCH 0
60 #define STATUS_NOMATCH 1
61 #define STATUS_BADUSAGE 2
62 #define STATUS_ERROR 3
74 SLIST_ENTRY(list
) li_chain
;
78 SLIST_HEAD(listhead
, list
);
80 static struct kinfo_proc2
*plist
;
81 static char *selected
;
82 static const char *delim
= "\n";
85 static int signum
= SIGTERM
;
91 static int cflags
= REG_EXTENDED
;
95 static struct listhead euidlist
= SLIST_HEAD_INITIALIZER(list
);
96 static struct listhead ruidlist
= SLIST_HEAD_INITIALIZER(list
);
97 static struct listhead rgidlist
= SLIST_HEAD_INITIALIZER(list
);
98 static struct listhead pgrplist
= SLIST_HEAD_INITIALIZER(list
);
99 static struct listhead ppidlist
= SLIST_HEAD_INITIALIZER(list
);
100 static struct listhead tdevlist
= SLIST_HEAD_INITIALIZER(list
);
101 static struct listhead sidlist
= SLIST_HEAD_INITIALIZER(list
);
103 int main(int, char **);
104 static void usage(void) __dead
;
105 static int killact(const struct kinfo_proc2
*);
106 static int grepact(const struct kinfo_proc2
*);
107 static void makelist(struct listhead
*, enum listtype
, char *);
110 main(int argc
, char **argv
)
112 char buf
[_POSIX2_LINE_MAX
], **pargv
, *q
;
113 int i
, j
, ch
, bestidx
, rv
, criteria
;
114 int (*action
)(const struct kinfo_proc2
*);
115 const struct kinfo_proc2
*kp
;
117 const char *mstr
, *p
;
118 u_int32_t bestsec
, bestusec
;
122 setprogname(argv
[0]);
124 if (strcmp(getprogname(), "pgrep") == 0) {
131 if (argc
> 1 && p
[0] == '-') {
133 i
= (int)strtol(p
, &q
, 10);
139 if (strncasecmp(p
, "sig", 3) == 0)
141 for (i
= 1; i
< NSIG
; i
++)
142 if (strcasecmp(sys_signame
[i
], p
) == 0)
155 while ((ch
= getopt(argc
, argv
, "G:P:U:d:fg:ilns:t:u:vx")) != -1)
158 makelist(&rgidlist
, LT_GROUP
, optarg
);
162 makelist(&ppidlist
, LT_GENERIC
, optarg
);
166 makelist(&ruidlist
, LT_USER
, optarg
);
178 makelist(&pgrplist
, LT_PGRP
, optarg
);
192 makelist(&sidlist
, LT_SID
, optarg
);
196 makelist(&tdevlist
, LT_TTY
, optarg
);
200 makelist(&euidlist
, LT_USER
, optarg
);
224 * Retrieve the list of running processes from the kernel.
226 kd
= kvm_openfiles(NULL
, NULL
, NULL
, KVM_NO_FILES
, buf
);
228 errx(STATUS_ERROR
, "Cannot open kernel files (%s)", buf
);
230 plist
= kvm_getproc2(kd
, KERN_PROC_ALL
, 0, sizeof(*plist
), &nproc
);
232 errx(STATUS_ERROR
, "Cannot get process list (%s)",
236 * Allocate memory which will be used to keep track of the
239 if ((selected
= calloc((size_t)1, (size_t)nproc
)) == NULL
)
240 err(STATUS_ERROR
, "Cannot allocate memory for %d processes",
244 * Refine the selection.
246 for (; *argv
!= NULL
; argv
++) {
247 if ((rv
= regcomp(®
, *argv
, cflags
)) != 0) {
248 (void)regerror(rv
, ®
, buf
, sizeof(buf
));
249 errx(STATUS_BADUSAGE
,
250 "Cannot compile regular expression `%s' (%s)",
254 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
255 if ((kp
->p_flag
& P_SYSTEM
) != 0 || kp
->p_pid
== mypid
)
259 if ((pargv
= kvm_getargv2(kd
, kp
, 0)) == NULL
)
263 while (j
< (int)sizeof(buf
) && *pargv
!= NULL
) {
264 j
+= snprintf(buf
+ j
, sizeof(buf
) - j
,
265 pargv
[1] != NULL
? "%s " : "%s",
274 rv
= regexec(®
, mstr
, 1, ®match
, 0);
277 if (regmatch
.rm_so
== 0 &&
278 regmatch
.rm_eo
== (regoff_t
)strlen(mstr
))
282 } else if (rv
!= REG_NOMATCH
) {
283 (void)regerror(rv
, ®
, buf
, sizeof(buf
));
285 "Regular expression evaluation error (%s)",
293 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
294 if ((kp
->p_flag
& P_SYSTEM
) != 0)
297 SLIST_FOREACH(li
, &ruidlist
, li_chain
)
298 if (kp
->p_ruid
== (uid_t
)li
->li_number
)
300 if (SLIST_FIRST(&ruidlist
) != NULL
&& li
== NULL
) {
305 SLIST_FOREACH(li
, &rgidlist
, li_chain
)
306 if (kp
->p_rgid
== (gid_t
)li
->li_number
)
308 if (SLIST_FIRST(&rgidlist
) != NULL
&& li
== NULL
) {
313 SLIST_FOREACH(li
, &euidlist
, li_chain
)
314 if (kp
->p_uid
== (uid_t
)li
->li_number
)
316 if (SLIST_FIRST(&euidlist
) != NULL
&& li
== NULL
) {
321 SLIST_FOREACH(li
, &ppidlist
, li_chain
)
322 if ((uid_t
)kp
->p_ppid
== (uid_t
)li
->li_number
)
324 if (SLIST_FIRST(&ppidlist
) != NULL
&& li
== NULL
) {
329 SLIST_FOREACH(li
, &pgrplist
, li_chain
)
330 if (kp
->p__pgid
== (pid_t
)li
->li_number
)
332 if (SLIST_FIRST(&pgrplist
) != NULL
&& li
== NULL
) {
337 SLIST_FOREACH(li
, &tdevlist
, li_chain
) {
338 if (li
->li_number
== -1 &&
339 (kp
->p_flag
& P_CONTROLT
) == 0)
341 if (kp
->p_tdev
== (uid_t
)li
->li_number
)
344 if (SLIST_FIRST(&tdevlist
) != NULL
&& li
== NULL
) {
349 SLIST_FOREACH(li
, &sidlist
, li_chain
)
350 if (kp
->p_sid
== (pid_t
)li
->li_number
)
352 if (SLIST_FIRST(&sidlist
) != NULL
&& li
== NULL
) {
366 for (i
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
370 if (kp
->p_ustart_sec
> bestsec
||
371 (kp
->p_ustart_sec
== bestsec
372 && kp
->p_ustart_usec
> bestusec
)) {
373 bestsec
= kp
->p_ustart_sec
;
374 bestusec
= kp
->p_ustart_usec
;
379 (void)memset(selected
, 0, (size_t)nproc
);
381 selected
[bestidx
] = 1;
385 * Take the appropriate action for each matched process, if any.
387 for (i
= 0, rv
= 0, kp
= plist
; i
< nproc
; i
++, kp
++) {
388 if (kp
->p_pid
== mypid
)
396 if ((kp
->p_flag
& P_SYSTEM
) != 0)
402 return rv
? STATUS_MATCH
: STATUS_NOMATCH
;
411 ustr
= "[-filnvx] [-d delim]";
413 ustr
= "[-signal] [-filnvx]";
415 (void)fprintf(stderr
,
416 "Usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] [-t tty]\n"
417 " [-U uid] [-u euid] pattern ...\n", getprogname(),
420 exit(STATUS_BADUSAGE
);
424 killact(const struct kinfo_proc2
*kp
)
428 if (kill(kp
->p_pid
, signum
) == -1) {
431 * Check for ESRCH, which indicates that the process
432 * disappeared between us matching it and us
433 * signalling it; don't issue a warning about it.
436 warn("signalling pid %d", (int)kp
->p_pid
);
439 * Return 0 to indicate that the process should not be
440 * considered a match, since we didn't actually get to
450 grepact(const struct kinfo_proc2
*kp
)
454 if (longfmt
&& matchargs
) {
457 * If kvm_getargv2() failed the process has probably
458 * disappeared. Return 0 to indicate that the process
459 * should not be considered a match, since we are no
460 * longer in a position to output it as a match.
462 if ((argv
= kvm_getargv2(kd
, kp
, 0)) == NULL
)
465 (void)printf("%d ", (int)kp
->p_pid
);
466 for (; *argv
!= NULL
; argv
++) {
467 (void)printf("%s", *argv
);
472 (void)printf("%d %s", (int)kp
->p_pid
, kp
->p_comm
);
474 (void)printf("%d", (int)kp
->p_pid
);
476 (void)printf("%s", delim
);
482 makelist(struct listhead
*head
, enum listtype type
, char *src
)
488 char *sp
, *ep
, buf
[MAXPATHLEN
];
491 const char *prefix
= _PATH_DEV
;
495 while ((sp
= strsep(&src
, ",")) != NULL
) {
499 if ((li
= malloc(sizeof(*li
))) == NULL
)
500 err(STATUS_ERROR
, "Cannot allocate %zd bytes",
502 SLIST_INSERT_HEAD(head
, li
, li_chain
);
505 li
->li_number
= (uid_t
)strtol(sp
, &ep
, 0);
506 if (*ep
== '\0' && type
!= LT_TTY
) {
509 if (li
->li_number
== 0)
510 li
->li_number
= getpgrp();
513 if (li
->li_number
== 0)
514 li
->li_number
= getsid(mypid
);
524 if ((pw
= getpwnam(sp
)) == NULL
)
525 errx(STATUS_BADUSAGE
, "Unknown user `%s'",
527 li
->li_number
= pw
->pw_uid
;
530 if ((gr
= getgrnam(sp
)) == NULL
)
531 errx(STATUS_BADUSAGE
, "Unknown group `%s'",
533 li
->li_number
= gr
->gr_gid
;
539 else if (strcmp(sp
, "-") == 0) {
542 } else if (strcmp(sp
, "co") == 0)
544 else if (strncmp(sp
, "tty", 3) == 0)
546 else if (strncmp(sp
, "pts/", 4) == 0)
548 else if (*ep
!= '\0' || (strlen(sp
) == 2 && *sp
== '0'))
551 prefix
= _PATH_DEV_PTS
;
553 (void)snprintf(buf
, sizeof(buf
), "%s%s", prefix
, p
);
555 if (stat(buf
, &st
) == -1) {
557 errx(STATUS_BADUSAGE
,
558 "No such tty: `%s'", buf
);
559 err(STATUS_ERROR
, "Cannot access `%s'", buf
);
562 if ((st
.st_mode
& S_IFCHR
) == 0)
563 errx(STATUS_BADUSAGE
, "Not a tty: `%s'", buf
);
565 li
->li_number
= st
.st_rdev
;