Annotation of src/usr.bin/pkill/pkill.c, Revision 1.7
1.7 ! deraadt 1: /* $OpenBSD: pkill.c,v 1.6 2004/07/15 17:15:33 millert 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.7 ! deraadt 41: static const char rcsid[] = "$OpenBSD: pkill.c,v 1.6 2004/07/15 17:15:33 millert 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) {
1.7 ! deraadt 266: int ret;
! 267:
! 268: ret = snprintf(buf + j, sizeof(buf) - j,
1.1 millert 269: pargv[1] != NULL ? "%s " : "%s",
270: pargv[0]);
1.7 ! deraadt 271: if (ret > 0)
! 272: j += ret;
1.1 millert 273: pargv++;
274: }
275:
276: mstr = buf;
277: } else
1.2 millert 278: mstr = kp->p_comm;
1.1 millert 279:
280: rv = regexec(®, mstr, 1, ®match, 0);
281: if (rv == 0) {
282: if (fullmatch) {
283: if (regmatch.rm_so == 0 &&
284: regmatch.rm_eo == strlen(mstr))
285: selected[i] = 1;
286: } else
287: selected[i] = 1;
288: } else if (rv != REG_NOMATCH) {
289: regerror(rv, ®, buf, sizeof(buf));
290: errx(STATUS_ERROR, "regexec(): %s", buf);
291: }
292: }
293:
294: regfree(®);
295: }
296:
297: for (i = 0, kp = plist; i < nproc; i++, kp++) {
1.5 mpech 298: if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
1.1 millert 299: continue;
300:
301: SLIST_FOREACH(li, &ruidlist, li_chain)
1.2 millert 302: if (kp->p_ruid == (uid_t)li->li_number)
1.1 millert 303: break;
304: if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
305: selected[i] = 0;
306: continue;
307: }
1.3 deraadt 308:
1.1 millert 309: SLIST_FOREACH(li, &rgidlist, li_chain)
1.2 millert 310: if (kp->p_rgid == (gid_t)li->li_number)
1.1 millert 311: break;
312: if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
313: selected[i] = 0;
314: continue;
315: }
316:
317: SLIST_FOREACH(li, &euidlist, li_chain)
1.2 millert 318: if (kp->p_uid == (uid_t)li->li_number)
1.1 millert 319: break;
320: if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
321: selected[i] = 0;
322: continue;
323: }
324:
325: SLIST_FOREACH(li, &ppidlist, li_chain)
1.2 millert 326: if (kp->p_ppid == (uid_t)li->li_number)
1.1 millert 327: break;
328: if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
329: selected[i] = 0;
330: continue;
331: }
332:
333: SLIST_FOREACH(li, &pgrplist, li_chain)
1.2 millert 334: if (kp->p__pgid == (uid_t)li->li_number)
1.1 millert 335: break;
336: if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
337: selected[i] = 0;
338: continue;
339: }
340:
341: SLIST_FOREACH(li, &tdevlist, li_chain) {
342: if (li->li_number == -1 &&
1.2 millert 343: (kp->p_flag & P_CONTROLT) == 0)
1.1 millert 344: break;
1.2 millert 345: if (kp->p_tdev == (uid_t)li->li_number)
1.1 millert 346: break;
347: }
348: if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
349: selected[i] = 0;
350: continue;
351: }
352:
353: SLIST_FOREACH(li, &sidlist, li_chain)
1.2 millert 354: if (kp->p_sid == (uid_t)li->li_number)
1.1 millert 355: break;
356: if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
357: selected[i] = 0;
358: continue;
359: }
360:
361: if (argc == 0)
362: selected[i] = 1;
363: }
364:
365: if (newest) {
1.2 millert 366: bestsec = 0;
367: bestusec = 0;
1.1 millert 368: bestidx = -1;
369:
370: for (i = 0, kp = plist; i < nproc; i++, kp++) {
371: if (!selected[i])
372: continue;
373:
1.2 millert 374: if (kp->p_ustart_sec > bestsec ||
375: (kp->p_ustart_sec == bestsec
376: && kp->p_ustart_usec > bestusec)) {
1.3 deraadt 377: bestsec = kp->p_ustart_sec;
378: bestusec = kp->p_ustart_usec;
1.1 millert 379: bestidx = i;
380: }
381: }
382:
383: memset(selected, 0, nproc);
384: if (bestidx != -1)
385: selected[bestidx] = 1;
386: }
387:
388: /*
389: * Take the appropriate action for each matched process, if any.
390: */
1.4 millert 391: for (i = 0, rv = STATUS_NOMATCH, kp = plist; i < nproc; i++, kp++) {
1.5 mpech 392: if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
1.1 millert 393: continue;
394: if (selected[i]) {
395: if (inverse)
396: continue;
397: } else if (!inverse)
398: continue;
399:
1.4 millert 400: if ((*action)(kp) == -1)
401: rv = STATUS_ERROR;
402: else if (rv != STATUS_ERROR)
403: rv = STATUS_MATCH;
1.1 millert 404: }
405:
1.4 millert 406: exit(rv);
1.1 millert 407: }
408:
409: void
410: usage(void)
411: {
412: const char *ustr;
413:
414: if (pgrep)
415: ustr = "[-flnvx] [-d delim]";
416: else
417: ustr = "[-signal] [-fnvx]";
418:
419: fprintf(stderr, "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] "
420: "[-s sid] [-t tty] [-u euid] pattern ...\n", __progname, ustr);
421:
422: exit(STATUS_ERROR);
423: }
424:
1.4 millert 425: int
1.2 millert 426: killact(struct kinfo_proc2 *kp)
1.1 millert 427: {
428:
1.6 millert 429: if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) {
1.4 millert 430: warn("signalling pid %d", (int)kp->p_pid);
431: return (-1);
432: }
433: return (0);
1.1 millert 434: }
435:
1.4 millert 436: int
1.2 millert 437: grepact(struct kinfo_proc2 *kp)
1.1 millert 438: {
439: char **argv;
440:
441: if (longfmt && matchargs) {
1.2 millert 442: if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
1.4 millert 443: return (-1);
1.1 millert 444:
1.2 millert 445: printf("%d ", (int)kp->p_pid);
1.1 millert 446: for (; *argv != NULL; argv++) {
447: printf("%s", *argv);
448: if (argv[1] != NULL)
449: putchar(' ');
450: }
451: } else if (longfmt)
1.2 millert 452: printf("%d %s", (int)kp->p_pid, kp->p_comm);
1.1 millert 453: else
1.2 millert 454: printf("%d", (int)kp->p_pid);
1.1 millert 455:
456: printf("%s", delim);
1.4 millert 457: return (0);
1.1 millert 458: }
459:
460: void
461: makelist(struct listhead *head, enum listtype type, char *src)
462: {
463: struct list *li;
464: struct passwd *pw;
465: struct group *gr;
466: struct stat st;
467: char *sp, *p, buf[MAXPATHLEN];
468: int empty;
469:
470: empty = 1;
471:
472: while ((sp = strsep(&src, ",")) != NULL) {
473: if (*sp == '\0')
474: usage();
475:
476: if ((li = malloc(sizeof(*li))) == NULL)
477: errx(STATUS_ERROR, "memory allocation failure");
478: SLIST_INSERT_HEAD(head, li, li_chain);
479: empty = 0;
480:
481: li->li_number = (uid_t)strtol(sp, &p, 0);
482: if (*p == '\0') {
483: switch (type) {
484: case LT_PGRP:
485: if (li->li_number == 0)
486: li->li_number = getpgrp();
487: break;
488: case LT_SID:
489: if (li->li_number == 0)
490: li->li_number = getsid(mypid);
491: break;
492: case LT_TTY:
493: usage();
494: default:
495: break;
496: }
497: continue;
498: }
499:
500: switch (type) {
501: case LT_USER:
502: if ((pw = getpwnam(sp)) == NULL)
503: errx(STATUS_BADUSAGE, "unknown user `%s'",
504: optarg);
505: li->li_number = pw->pw_uid;
506: break;
507: case LT_GROUP:
508: if ((gr = getgrnam(sp)) == NULL)
509: errx(STATUS_BADUSAGE, "unknown group `%s'",
510: optarg);
511: li->li_number = gr->gr_gid;
512: break;
513: case LT_TTY:
514: if (strcmp(sp, "-") == 0) {
515: li->li_number = -1;
516: break;
517: } else if (strcmp(sp, "co") == 0)
518: p = "console";
519: else if (strncmp(sp, "tty", 3) == 0)
520: p = sp;
521: else
522: p = NULL;
523:
524: if (p == NULL)
525: snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
526: else
527: snprintf(buf, sizeof(buf), "/dev/%s", p);
528:
529: if (stat(buf, &st) < 0) {
530: if (errno == ENOENT)
531: errx(STATUS_BADUSAGE,
532: "no such tty: `%s'", sp);
533: err(STATUS_ERROR, "stat(%s)", sp);
534: }
535:
536: if ((st.st_mode & S_IFCHR) == 0)
537: errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
538:
539: li->li_number = st.st_rdev;
540: break;
541: default:
542: usage();
1.3 deraadt 543: }
1.1 millert 544: }
545:
546: if (empty)
547: usage();
548: }