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