Fix markup.
[netbsd-mini2440.git] / usr.bin / pkill / pkill.c
blob714ce35e8a5c82761442fa9013b7769844eaca1b
1 /* $NetBSD: pkill.c,v 1.24 2009/02/28 18:16:11 christos Exp $ */
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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>
33 #ifndef lint
34 __RCSID("$NetBSD: pkill.c,v 1.24 2009/02/28 18:16:11 christos Exp $");
35 #endif /* !lint */
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysctl.h>
40 #include <sys/proc.h>
41 #include <sys/queue.h>
42 #include <sys/stat.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <limits.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <signal.h>
50 #include <regex.h>
51 #include <ctype.h>
52 #include <kvm.h>
53 #include <err.h>
54 #include <pwd.h>
55 #include <grp.h>
56 #include <errno.h>
57 #include <paths.h>
59 #define STATUS_MATCH 0
60 #define STATUS_NOMATCH 1
61 #define STATUS_BADUSAGE 2
62 #define STATUS_ERROR 3
64 enum listtype {
65 LT_GENERIC,
66 LT_USER,
67 LT_GROUP,
68 LT_TTY,
69 LT_PGRP,
70 LT_SID
73 struct list {
74 SLIST_ENTRY(list) li_chain;
75 long li_number;
78 SLIST_HEAD(listhead, list);
80 static struct kinfo_proc2 *plist;
81 static char *selected;
82 static const char *delim = "\n";
83 static int nproc;
84 static int pgrep;
85 static int signum = SIGTERM;
86 static int newest;
87 static int inverse;
88 static int longfmt;
89 static int matchargs;
90 static int fullmatch;
91 static int cflags = REG_EXTENDED;
92 static kvm_t *kd;
93 static pid_t mypid;
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;
116 struct list *li;
117 const char *mstr, *p;
118 u_int32_t bestsec, bestusec;
119 regex_t reg;
120 regmatch_t regmatch;
122 setprogname(argv[0]);
124 if (strcmp(getprogname(), "pgrep") == 0) {
125 action = grepact;
126 pgrep = 1;
127 } else {
128 action = killact;
129 p = argv[1];
131 if (argc > 1 && p[0] == '-') {
132 p++;
133 i = (int)strtol(p, &q, 10);
134 if (*q == '\0') {
135 signum = i;
136 argv++;
137 argc--;
138 } else {
139 if (strncasecmp(p, "sig", 3) == 0)
140 p += 3;
141 for (i = 1; i < NSIG; i++)
142 if (strcasecmp(sys_signame[i], p) == 0)
143 break;
144 if (i != NSIG) {
145 signum = i;
146 argv++;
147 argc--;
153 criteria = 0;
155 while ((ch = getopt(argc, argv, "G:P:U:d:fg:ilns:t:u:vx")) != -1)
156 switch (ch) {
157 case 'G':
158 makelist(&rgidlist, LT_GROUP, optarg);
159 criteria = 1;
160 break;
161 case 'P':
162 makelist(&ppidlist, LT_GENERIC, optarg);
163 criteria = 1;
164 break;
165 case 'U':
166 makelist(&ruidlist, LT_USER, optarg);
167 criteria = 1;
168 break;
169 case 'd':
170 if (!pgrep)
171 usage();
172 delim = optarg;
173 break;
174 case 'f':
175 matchargs = 1;
176 break;
177 case 'g':
178 makelist(&pgrplist, LT_PGRP, optarg);
179 criteria = 1;
180 break;
181 case 'i':
182 cflags |= REG_ICASE;
183 break;
184 case 'l':
185 longfmt = 1;
186 break;
187 case 'n':
188 newest = 1;
189 criteria = 1;
190 break;
191 case 's':
192 makelist(&sidlist, LT_SID, optarg);
193 criteria = 1;
194 break;
195 case 't':
196 makelist(&tdevlist, LT_TTY, optarg);
197 criteria = 1;
198 break;
199 case 'u':
200 makelist(&euidlist, LT_USER, optarg);
201 criteria = 1;
202 break;
203 case 'v':
204 inverse = 1;
205 break;
206 case 'x':
207 fullmatch = 1;
208 break;
209 default:
210 usage();
211 /* NOTREACHED */
214 argc -= optind;
215 argv += optind;
216 if (argc != 0)
217 criteria = 1;
218 if (!criteria)
219 usage();
221 mypid = getpid();
224 * Retrieve the list of running processes from the kernel.
226 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
227 if (kd == NULL)
228 errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf);
230 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
231 if (plist == NULL)
232 errx(STATUS_ERROR, "Cannot get process list (%s)",
233 kvm_geterr(kd));
236 * Allocate memory which will be used to keep track of the
237 * selection.
239 if ((selected = calloc((size_t)1, (size_t)nproc)) == NULL)
240 err(STATUS_ERROR, "Cannot allocate memory for %d processes",
241 nproc);
244 * Refine the selection.
246 for (; *argv != NULL; argv++) {
247 if ((rv = regcomp(&reg, *argv, cflags)) != 0) {
248 (void)regerror(rv, &reg, buf, sizeof(buf));
249 errx(STATUS_BADUSAGE,
250 "Cannot compile regular expression `%s' (%s)",
251 *argv, buf);
254 for (i = 0, kp = plist; i < nproc; i++, kp++) {
255 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
256 continue;
258 if (matchargs) {
259 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
260 continue;
262 j = 0;
263 while (j < (int)sizeof(buf) && *pargv != NULL) {
264 j += snprintf(buf + j, sizeof(buf) - j,
265 pargv[1] != NULL ? "%s " : "%s",
266 pargv[0]);
267 pargv++;
270 mstr = buf;
271 } else
272 mstr = kp->p_comm;
274 rv = regexec(&reg, mstr, 1, &regmatch, 0);
275 if (rv == 0) {
276 if (fullmatch) {
277 if (regmatch.rm_so == 0 &&
278 regmatch.rm_eo == (regoff_t)strlen(mstr))
279 selected[i] = 1;
280 } else
281 selected[i] = 1;
282 } else if (rv != REG_NOMATCH) {
283 (void)regerror(rv, &reg, buf, sizeof(buf));
284 errx(STATUS_ERROR,
285 "Regular expression evaluation error (%s)",
286 buf);
290 regfree(&reg);
293 for (i = 0, kp = plist; i < nproc; i++, kp++) {
294 if ((kp->p_flag & P_SYSTEM) != 0)
295 continue;
297 SLIST_FOREACH(li, &ruidlist, li_chain)
298 if (kp->p_ruid == (uid_t)li->li_number)
299 break;
300 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
301 selected[i] = 0;
302 continue;
305 SLIST_FOREACH(li, &rgidlist, li_chain)
306 if (kp->p_rgid == (gid_t)li->li_number)
307 break;
308 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
309 selected[i] = 0;
310 continue;
313 SLIST_FOREACH(li, &euidlist, li_chain)
314 if (kp->p_uid == (uid_t)li->li_number)
315 break;
316 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
317 selected[i] = 0;
318 continue;
321 SLIST_FOREACH(li, &ppidlist, li_chain)
322 if ((uid_t)kp->p_ppid == (uid_t)li->li_number)
323 break;
324 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
325 selected[i] = 0;
326 continue;
329 SLIST_FOREACH(li, &pgrplist, li_chain)
330 if (kp->p__pgid == (pid_t)li->li_number)
331 break;
332 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
333 selected[i] = 0;
334 continue;
337 SLIST_FOREACH(li, &tdevlist, li_chain) {
338 if (li->li_number == -1 &&
339 (kp->p_flag & P_CONTROLT) == 0)
340 break;
341 if (kp->p_tdev == (uid_t)li->li_number)
342 break;
344 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
345 selected[i] = 0;
346 continue;
349 SLIST_FOREACH(li, &sidlist, li_chain)
350 if (kp->p_sid == (pid_t)li->li_number)
351 break;
352 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
353 selected[i] = 0;
354 continue;
357 if (argc == 0)
358 selected[i] = 1;
361 if (newest) {
362 bestsec = 0;
363 bestusec = 0;
364 bestidx = -1;
366 for (i = 0, kp = plist; i < nproc; i++, kp++) {
367 if (!selected[i])
368 continue;
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;
375 bestidx = i;
379 (void)memset(selected, 0, (size_t)nproc);
380 if (bestidx != -1)
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)
389 continue;
390 if (selected[i]) {
391 if (inverse)
392 continue;
393 } else if (!inverse)
394 continue;
396 if ((kp->p_flag & P_SYSTEM) != 0)
397 continue;
399 rv |= (*action)(kp);
402 return rv ? STATUS_MATCH : STATUS_NOMATCH;
405 static void
406 usage(void)
408 const char *ustr;
410 if (pgrep)
411 ustr = "[-filnvx] [-d delim]";
412 else
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(),
418 ustr);
420 exit(STATUS_BADUSAGE);
423 static int
424 killact(const struct kinfo_proc2 *kp)
426 if (longfmt)
427 grepact(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.
435 if (errno != ESRCH)
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
441 * signal it.
443 return 0;
446 return 1;
449 static int
450 grepact(const struct kinfo_proc2 *kp)
452 char **argv;
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)
463 return 0;
465 (void)printf("%d ", (int)kp->p_pid);
466 for (; *argv != NULL; argv++) {
467 (void)printf("%s", *argv);
468 if (argv[1] != NULL)
469 (void)putchar(' ');
471 } else if (longfmt)
472 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm);
473 else
474 (void)printf("%d", (int)kp->p_pid);
476 (void)printf("%s", delim);
478 return 1;
481 static void
482 makelist(struct listhead *head, enum listtype type, char *src)
484 struct list *li;
485 struct passwd *pw;
486 struct group *gr;
487 struct stat st;
488 char *sp, *ep, buf[MAXPATHLEN];
489 const char *p;
490 int empty;
491 const char *prefix = _PATH_DEV;
493 empty = 1;
495 while ((sp = strsep(&src, ",")) != NULL) {
496 if (*sp == '\0')
497 usage();
499 if ((li = malloc(sizeof(*li))) == NULL)
500 err(STATUS_ERROR, "Cannot allocate %zd bytes",
501 sizeof(*li));
502 SLIST_INSERT_HEAD(head, li, li_chain);
503 empty = 0;
505 li->li_number = (uid_t)strtol(sp, &ep, 0);
506 if (*ep == '\0' && type != LT_TTY) {
507 switch (type) {
508 case LT_PGRP:
509 if (li->li_number == 0)
510 li->li_number = getpgrp();
511 break;
512 case LT_SID:
513 if (li->li_number == 0)
514 li->li_number = getsid(mypid);
515 break;
516 default:
517 break;
519 continue;
522 switch (type) {
523 case LT_USER:
524 if ((pw = getpwnam(sp)) == NULL)
525 errx(STATUS_BADUSAGE, "Unknown user `%s'",
526 sp);
527 li->li_number = pw->pw_uid;
528 break;
529 case LT_GROUP:
530 if ((gr = getgrnam(sp)) == NULL)
531 errx(STATUS_BADUSAGE, "Unknown group `%s'",
532 sp);
533 li->li_number = gr->gr_gid;
534 break;
535 case LT_TTY:
536 p = sp;
537 if (*sp == '/')
538 prefix = "";
539 else if (strcmp(sp, "-") == 0) {
540 li->li_number = -1;
541 break;
542 } else if (strcmp(sp, "co") == 0)
543 p = "console";
544 else if (strncmp(sp, "tty", 3) == 0)
545 /* all set */;
546 else if (strncmp(sp, "pts/", 4) == 0)
547 /* all set */;
548 else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0'))
549 prefix = _PATH_TTY;
550 else
551 prefix = _PATH_DEV_PTS;
553 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p);
555 if (stat(buf, &st) == -1) {
556 if (errno == ENOENT)
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;
566 break;
567 default:
568 usage();
572 if (empty)
573 usage();