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