Annotation of src/usr.bin/pkill/pkill.c, Revision 1.16
1.16 ! millert 1: /* $OpenBSD: pkill.c,v 1.15 2006/09/19 05:52:23 otto Exp $ */
1.1 millert 2: /* $NetBSD: pkill.c,v 1.5 2002/10/27 11:49:34 kleink Exp $ */
3:
4: /*-
5: * Copyright (c) 2002 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Andrew Doran.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
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.
26: *
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.
38: */
39:
40: #ifndef lint
1.16 ! millert 41: static const char rcsid[] = "$OpenBSD: pkill.c,v 1.15 2006/09/19 05:52:23 otto Exp $";
1.1 millert 42: #endif /* !lint */
43:
44: #include <sys/types.h>
45: #include <sys/param.h>
46: #include <sys/sysctl.h>
47: #include <sys/proc.h>
48: #include <sys/queue.h>
49: #include <sys/stat.h>
50:
51: #include <stdio.h>
52: #include <stdlib.h>
1.16 ! millert 53: #include <stdint.h>
1.1 millert 54: #include <limits.h>
55: #include <string.h>
56: #include <unistd.h>
57: #include <signal.h>
58: #include <regex.h>
59: #include <ctype.h>
60: #include <kvm.h>
61: #include <err.h>
62: #include <pwd.h>
63: #include <grp.h>
64: #include <errno.h>
65:
66: #define STATUS_MATCH 0
67: #define STATUS_NOMATCH 1
68: #define STATUS_BADUSAGE 2
69: #define STATUS_ERROR 3
70:
71: enum listtype {
72: LT_GENERIC,
73: LT_USER,
74: LT_GROUP,
75: LT_TTY,
76: LT_PGRP,
77: LT_SID
78: };
79:
80: struct list {
81: SLIST_ENTRY(list) li_chain;
82: long li_number;
83: };
84:
85: SLIST_HEAD(listhead, list);
86:
1.2 millert 87: struct kinfo_proc2 *plist;
1.1 millert 88: char *selected;
89: char *delim = "\n";
90: int nproc;
91: int pgrep;
92: int signum = SIGTERM;
93: int newest;
1.16 ! millert 94: int oldest;
1.1 millert 95: int inverse;
96: int longfmt;
97: int matchargs;
98: int fullmatch;
99: kvm_t *kd;
100: pid_t mypid;
101:
102: struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
103: struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
104: struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
105: struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
106: struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
107: struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
108: struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
109:
110: int main(int, char **);
111: void usage(void);
1.8 millert 112: int killact(struct kinfo_proc2 *, int);
113: int grepact(struct kinfo_proc2 *, int);
1.1 millert 114: void makelist(struct listhead *, enum listtype, char *);
115:
116: extern char *__progname;
117:
118: int
119: main(int argc, char **argv)
120: {
121: extern char *optarg;
122: extern int optind;
1.2 millert 123: char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q;
1.1 millert 124: int i, j, ch, bestidx, rv, criteria;
1.8 millert 125: int (*action)(struct kinfo_proc2 *, int);
1.2 millert 126: struct kinfo_proc2 *kp;
1.1 millert 127: struct list *li;
1.2 millert 128: u_int32_t bestsec, bestusec;
1.1 millert 129: regex_t reg;
130: regmatch_t regmatch;
131:
132: if (strcmp(__progname, "pgrep") == 0) {
133: action = grepact;
134: pgrep = 1;
135: } else {
136: action = killact;
1.2 millert 137: p = argv[1];
1.1 millert 138:
1.2 millert 139: if (argc > 1 && p[0] == '-') {
140: p++;
141: i = (int)strtol(p, &q, 10);
1.1 millert 142: if (*q == '\0') {
143: signum = i;
144: argv++;
145: argc--;
146: } else {
1.2 millert 147: if (strncasecmp(p, "sig", 3) == 0)
148: p += 3;
1.1 millert 149: for (i = 1; i < NSIG; i++)
1.2 millert 150: if (strcasecmp(sys_signame[i], p) == 0)
1.1 millert 151: break;
152: if (i != NSIG) {
153: signum = i;
154: argv++;
155: argc--;
156: }
157: }
158: }
159: }
160:
161: criteria = 0;
162:
1.16 ! millert 163: while ((ch = getopt(argc, argv, "G:P:U:d:fg:lnos:t:u:vx")) != -1)
1.1 millert 164: switch (ch) {
165: case 'G':
166: makelist(&rgidlist, LT_GROUP, optarg);
167: criteria = 1;
168: break;
169: case 'P':
170: makelist(&ppidlist, LT_GENERIC, optarg);
171: criteria = 1;
172: break;
173: case 'U':
174: makelist(&ruidlist, LT_USER, optarg);
175: criteria = 1;
176: break;
177: case 'd':
178: if (!pgrep)
179: usage();
180: delim = optarg;
181: break;
182: case 'f':
183: matchargs = 1;
184: break;
185: case 'g':
186: makelist(&pgrplist, LT_PGRP, optarg);
187: criteria = 1;
188: break;
189: case 'l':
190: if (!pgrep)
191: usage();
192: longfmt = 1;
193: break;
194: case 'n':
195: newest = 1;
196: criteria = 1;
197: break;
1.16 ! millert 198: case 'o':
! 199: oldest = 1;
! 200: criteria = 1;
! 201: break;
1.1 millert 202: case 's':
203: makelist(&sidlist, LT_SID, optarg);
204: criteria = 1;
205: break;
206: case 't':
207: makelist(&tdevlist, LT_TTY, optarg);
208: criteria = 1;
209: break;
210: case 'u':
211: makelist(&euidlist, LT_USER, optarg);
212: criteria = 1;
213: break;
214: case 'v':
215: inverse = 1;
216: break;
217: case 'x':
218: fullmatch = 1;
219: break;
220: default:
221: usage();
222: /* NOTREACHED */
223: }
224:
225: argc -= optind;
226: argv += optind;
227: if (argc != 0)
228: criteria = 1;
1.16 ! millert 229: if (!criteria || (newest && oldest))
1.1 millert 230: usage();
231:
232: mypid = getpid();
233:
234: /*
235: * Retrieve the list of running processes from the kernel.
236: */
237: kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
238: if (kd == NULL)
239: errx(STATUS_ERROR, "kvm_openfiles(): %s", buf);
240:
1.2 millert 241: plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
1.1 millert 242: if (plist == NULL)
1.2 millert 243: errx(STATUS_ERROR, "kvm_getproc2() failed");
1.1 millert 244:
245: /*
246: * Allocate memory which will be used to keep track of the
247: * selection.
248: */
249: if ((selected = malloc(nproc)) == NULL)
250: errx(STATUS_ERROR, "memory allocation failure");
251: memset(selected, 0, nproc);
252:
253: /*
254: * Refine the selection.
255: */
256: for (; *argv != NULL; argv++) {
1.11 robert 257: if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) {
1.1 millert 258: regerror(rv, ®, buf, sizeof(buf));
259: errx(STATUS_BADUSAGE, "bad expression: %s", buf);
260: }
261:
262: for (i = 0, kp = plist; i < nproc; i++, kp++) {
1.2 millert 263: if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
1.1 millert 264: continue;
265:
266: if (matchargs) {
1.2 millert 267: if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
1.1 millert 268: continue;
269:
270: j = 0;
271: while (j < sizeof(buf) && *pargv != NULL) {
1.7 deraadt 272: int ret;
273:
274: ret = snprintf(buf + j, sizeof(buf) - j,
1.1 millert 275: pargv[1] != NULL ? "%s " : "%s",
276: pargv[0]);
1.12 deraadt 277: if (ret >= sizeof(buf) - j)
278: j += sizeof(buf) - j - 1;
279: else if (ret > 0)
1.7 deraadt 280: j += ret;
1.1 millert 281: pargv++;
282: }
283:
284: mstr = buf;
285: } else
1.2 millert 286: mstr = kp->p_comm;
1.1 millert 287:
288: rv = regexec(®, mstr, 1, ®match, 0);
289: if (rv == 0) {
290: if (fullmatch) {
291: if (regmatch.rm_so == 0 &&
292: regmatch.rm_eo == strlen(mstr))
293: selected[i] = 1;
294: } else
295: selected[i] = 1;
296: } else if (rv != REG_NOMATCH) {
297: regerror(rv, ®, buf, sizeof(buf));
298: errx(STATUS_ERROR, "regexec(): %s", buf);
299: }
300: }
301:
302: regfree(®);
303: }
304:
305: for (i = 0, kp = plist; i < nproc; i++, kp++) {
1.5 mpech 306: if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
1.1 millert 307: continue;
308:
309: SLIST_FOREACH(li, &ruidlist, li_chain)
1.2 millert 310: if (kp->p_ruid == (uid_t)li->li_number)
1.1 millert 311: break;
312: if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
313: selected[i] = 0;
314: continue;
315: }
1.3 deraadt 316:
1.1 millert 317: SLIST_FOREACH(li, &rgidlist, li_chain)
1.2 millert 318: if (kp->p_rgid == (gid_t)li->li_number)
1.1 millert 319: break;
320: if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
321: selected[i] = 0;
322: continue;
323: }
324:
325: SLIST_FOREACH(li, &euidlist, li_chain)
1.2 millert 326: if (kp->p_uid == (uid_t)li->li_number)
1.1 millert 327: break;
328: if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
329: selected[i] = 0;
330: continue;
331: }
332:
333: SLIST_FOREACH(li, &ppidlist, li_chain)
1.2 millert 334: if (kp->p_ppid == (uid_t)li->li_number)
1.1 millert 335: break;
336: if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
337: selected[i] = 0;
338: continue;
339: }
340:
341: SLIST_FOREACH(li, &pgrplist, li_chain)
1.2 millert 342: if (kp->p__pgid == (uid_t)li->li_number)
1.1 millert 343: break;
344: if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
345: selected[i] = 0;
346: continue;
347: }
348:
349: SLIST_FOREACH(li, &tdevlist, li_chain) {
350: if (li->li_number == -1 &&
1.2 millert 351: (kp->p_flag & P_CONTROLT) == 0)
1.1 millert 352: break;
1.2 millert 353: if (kp->p_tdev == (uid_t)li->li_number)
1.1 millert 354: break;
355: }
356: if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
357: selected[i] = 0;
358: continue;
359: }
360:
361: SLIST_FOREACH(li, &sidlist, li_chain)
1.2 millert 362: if (kp->p_sid == (uid_t)li->li_number)
1.1 millert 363: break;
364: if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
365: selected[i] = 0;
366: continue;
367: }
368:
369: if (argc == 0)
370: selected[i] = 1;
371: }
372:
1.16 ! millert 373: if (newest || oldest) {
1.1 millert 374: bestidx = -1;
375:
1.16 ! millert 376: if (newest)
! 377: bestsec = bestusec = 0;
! 378: else
! 379: bestsec = bestusec = UINT32_MAX;
! 380:
1.1 millert 381: for (i = 0, kp = plist; i < nproc; i++, kp++) {
382: if (!selected[i])
383: continue;
384:
1.16 ! millert 385: if ((newest && (kp->p_ustart_sec > bestsec ||
1.2 millert 386: (kp->p_ustart_sec == bestsec
1.16 ! millert 387: && kp->p_ustart_usec > bestusec)))
! 388: || (oldest && (kp->p_ustart_sec < bestsec ||
! 389: (kp->p_ustart_sec == bestsec
! 390: && kp->p_ustart_usec < bestusec)))) {
! 391:
1.3 deraadt 392: bestsec = kp->p_ustart_sec;
393: bestusec = kp->p_ustart_usec;
1.1 millert 394: bestidx = i;
395: }
396: }
397:
398: memset(selected, 0, nproc);
399: if (bestidx != -1)
400: selected[bestidx] = 1;
401: }
402:
403: /*
404: * Take the appropriate action for each matched process, if any.
405: */
1.8 millert 406: rv = STATUS_NOMATCH;
407: for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) {
1.5 mpech 408: if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
1.1 millert 409: continue;
410: if (selected[i]) {
411: if (inverse)
412: continue;
413: } else if (!inverse)
414: continue;
415:
1.8 millert 416: if ((*action)(kp, j++) == -1)
1.4 millert 417: rv = STATUS_ERROR;
418: else if (rv != STATUS_ERROR)
419: rv = STATUS_MATCH;
1.1 millert 420: }
1.13 otto 421: if (pgrep && j)
1.8 millert 422: putchar('\n');
1.1 millert 423:
1.4 millert 424: exit(rv);
1.1 millert 425: }
426:
427: void
428: usage(void)
429: {
430: const char *ustr;
431:
432: if (pgrep)
1.16 ! millert 433: ustr = "[-flnovx] [-d delim]";
1.1 millert 434: else
1.16 ! millert 435: ustr = "[-signal] [-fnovx]";
1.1 millert 436:
1.14 jmc 437: fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] "
438: "[-t tty]\n\t[-U uid] [-u euid] [pattern ...]\n", __progname, ustr);
1.1 millert 439:
440: exit(STATUS_ERROR);
441: }
442:
1.4 millert 443: int
1.8 millert 444: killact(struct kinfo_proc2 *kp, int dummy)
1.1 millert 445: {
446:
1.6 millert 447: if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) {
1.4 millert 448: warn("signalling pid %d", (int)kp->p_pid);
449: return (-1);
450: }
451: return (0);
1.1 millert 452: }
453:
1.4 millert 454: int
1.8 millert 455: grepact(struct kinfo_proc2 *kp, int printdelim)
1.1 millert 456: {
457: char **argv;
458:
1.8 millert 459: if (printdelim)
460: fputs(delim, stdout);
1.1 millert 461: if (longfmt && matchargs) {
1.2 millert 462: if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
1.4 millert 463: return (-1);
1.1 millert 464:
1.2 millert 465: printf("%d ", (int)kp->p_pid);
1.1 millert 466: for (; *argv != NULL; argv++) {
467: printf("%s", *argv);
468: if (argv[1] != NULL)
469: putchar(' ');
470: }
471: } else if (longfmt)
1.2 millert 472: printf("%d %s", (int)kp->p_pid, kp->p_comm);
1.1 millert 473: else
1.2 millert 474: printf("%d", (int)kp->p_pid);
1.1 millert 475:
1.4 millert 476: return (0);
1.1 millert 477: }
478:
479: void
480: makelist(struct listhead *head, enum listtype type, char *src)
481: {
482: struct list *li;
483: struct passwd *pw;
484: struct group *gr;
485: struct stat st;
486: char *sp, *p, buf[MAXPATHLEN];
487: int empty;
488:
489: empty = 1;
490:
491: while ((sp = strsep(&src, ",")) != NULL) {
492: if (*sp == '\0')
493: usage();
494:
495: if ((li = malloc(sizeof(*li))) == NULL)
496: errx(STATUS_ERROR, "memory allocation failure");
497: SLIST_INSERT_HEAD(head, li, li_chain);
498: empty = 0;
499:
500: li->li_number = (uid_t)strtol(sp, &p, 0);
501: if (*p == '\0') {
502: switch (type) {
503: case LT_PGRP:
504: if (li->li_number == 0)
505: li->li_number = getpgrp();
506: break;
507: case LT_SID:
508: if (li->li_number == 0)
509: li->li_number = getsid(mypid);
510: break;
511: case LT_TTY:
512: usage();
513: default:
514: break;
515: }
516: continue;
517: }
518:
519: switch (type) {
520: case LT_USER:
521: if ((pw = getpwnam(sp)) == NULL)
1.9 otto 522: errx(STATUS_BADUSAGE, "unknown user `%s'", sp);
1.1 millert 523: li->li_number = pw->pw_uid;
524: break;
525: case LT_GROUP:
526: if ((gr = getgrnam(sp)) == NULL)
1.9 otto 527: errx(STATUS_BADUSAGE, "unknown group `%s'", sp);
1.1 millert 528: li->li_number = gr->gr_gid;
529: break;
530: case LT_TTY:
531: if (strcmp(sp, "-") == 0) {
532: li->li_number = -1;
533: break;
534: } else if (strcmp(sp, "co") == 0)
535: p = "console";
536: else if (strncmp(sp, "tty", 3) == 0)
537: p = sp;
538: else
539: p = NULL;
540:
541: if (p == NULL)
542: snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
543: else
544: snprintf(buf, sizeof(buf), "/dev/%s", p);
545:
546: if (stat(buf, &st) < 0) {
547: if (errno == ENOENT)
548: errx(STATUS_BADUSAGE,
549: "no such tty: `%s'", sp);
550: err(STATUS_ERROR, "stat(%s)", sp);
551: }
552:
1.15 otto 553: if (!S_ISCHR(st.st_mode))
1.1 millert 554: errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
555:
556: li->li_number = st.st_rdev;
557: break;
558: default:
559: usage();
1.3 deraadt 560: }
1.1 millert 561: }
562:
563: if (empty)
564: usage();
565: }