xref: /openbsd-src/usr.bin/pkill/pkill.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: pkill.c,v 1.38 2015/10/11 03:08:20 deraadt Exp $	*/
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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>	/* MAXCOMLEN */
34 #include <sys/types.h>
35 #include <sys/sysctl.h>
36 #include <sys/proc.h>
37 #include <sys/queue.h>
38 #include <sys/stat.h>
39 #include <sys/socket.h>
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <stdint.h>
44 #include <limits.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <signal.h>
48 #include <regex.h>
49 #include <ctype.h>
50 #include <kvm.h>
51 #include <err.h>
52 #include <pwd.h>
53 #include <grp.h>
54 #include <errno.h>
55 
56 #define	STATUS_MATCH	0
57 #define	STATUS_NOMATCH	1
58 #define	STATUS_BADUSAGE	2
59 #define	STATUS_ERROR	3
60 
61 enum listtype {
62 	LT_GENERIC,
63 	LT_USER,
64 	LT_GROUP,
65 	LT_TTY,
66 	LT_PGRP,
67 	LT_SID,
68 	LT_RTABLE
69 };
70 
71 struct list {
72 	SLIST_ENTRY(list) li_chain;
73 	long	li_number;
74 };
75 
76 SLIST_HEAD(listhead, list);
77 
78 struct kinfo_proc	*plist;
79 char	*selected;
80 char	*delim = "\n";
81 int	nproc;
82 int	pgrep;
83 int	signum = SIGTERM;
84 int	newest;
85 int	oldest;
86 int 	quiet;
87 int	inverse;
88 int	longfmt;
89 int	matchargs;
90 int	fullmatch;
91 int	confirmkill;
92 kvm_t	*kd;
93 pid_t	mypid;
94 
95 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
96 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
97 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
98 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
99 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
100 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
101 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
102 struct listhead rtablist = SLIST_HEAD_INITIALIZER(list);
103 
104 int	main(int, char **);
105 void	usage(void);
106 int	killact(struct kinfo_proc *, int);
107 int	grepact(struct kinfo_proc *, int);
108 void	makelist(struct listhead *, enum listtype, char *);
109 char	*getargv(struct kinfo_proc *);
110 int	askyn(struct kinfo_proc *);
111 
112 extern char *__progname;
113 
114 char *
115 getargv(struct kinfo_proc *kp)
116 {
117 	static char buf[_POSIX2_LINE_MAX];
118 	char **pargv;
119 	size_t j;
120 
121 	if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) {
122 		strlcpy(buf, kp->p_comm, sizeof(buf));
123 		return buf;
124 	}
125 
126 	j = 0;
127 	while (j < sizeof(buf) && *pargv != NULL) {
128 		int ret;
129 
130 		ret = snprintf(buf + j, sizeof(buf) - j,
131 		    pargv[1] != NULL ? "%s " : "%s", pargv[0]);
132 		if (ret >= sizeof(buf) - j)
133 			j += sizeof(buf) - j - 1;
134 		else if (ret > 0)
135 			j += ret;
136 		pargv++;
137 	}
138 	return buf;
139 }
140 
141 int
142 main(int argc, char **argv)
143 {
144 	extern char *optarg;
145 	extern int optind;
146 	char buf[_POSIX2_LINE_MAX], *mstr, *p, *q;
147 	int i, j, ch, bestidx, rv, criteria;
148 	int (*action)(struct kinfo_proc *, int);
149 	struct kinfo_proc *kp;
150 	struct list *li;
151 	u_int32_t bestsec, bestusec;
152 	regex_t reg;
153 	regmatch_t regmatch;
154 
155 	if (strcmp(__progname, "pgrep") == 0) {
156 		action = grepact;
157 		pgrep = 1;
158 	} else {
159 		action = killact;
160 		p = argv[1];
161 
162 		if (argc > 1 && p[0] == '-') {
163 			p++;
164 			i = (int)strtol(p, &q, 10);
165 			if (*q == '\0') {
166 				signum = i;
167 				argv++;
168 				argc--;
169 			} else {
170 				if (strncasecmp(p, "sig", 3) == 0)
171 					p += 3;
172 				for (i = 1; i < NSIG; i++)
173 					if (strcasecmp(sys_signame[i], p) == 0)
174 						break;
175 				if (i != NSIG) {
176 					signum = i;
177 					argv++;
178 					argc--;
179 				}
180 			}
181 		}
182 	}
183 
184 	criteria = 0;
185 
186 	while ((ch = getopt(argc, argv, "G:P:T:U:d:fg:Ilnoqs:t:u:vx")) != -1)
187 		switch (ch) {
188 		case 'G':
189 			makelist(&rgidlist, LT_GROUP, optarg);
190 			criteria = 1;
191 			break;
192 		case 'P':
193 			makelist(&ppidlist, LT_GENERIC, optarg);
194 			criteria = 1;
195 			break;
196 		case 'T':
197 			makelist(&rtablist, LT_RTABLE, optarg);
198 			criteria = 1;
199 			break;
200 		case 'U':
201 			makelist(&ruidlist, LT_USER, optarg);
202 			criteria = 1;
203 			break;
204 		case 'd':
205 			if (!pgrep)
206 				usage();
207 			delim = optarg;
208 			break;
209 		case 'f':
210 			matchargs = 1;
211 			break;
212 		case 'g':
213 			makelist(&pgrplist, LT_PGRP, optarg);
214 			criteria = 1;
215 			break;
216 		case 'I':
217 			confirmkill = 1;
218 			break;
219 		case 'l':
220 			longfmt = 1;
221 			break;
222 		case 'n':
223 			newest = 1;
224 			criteria = 1;
225 			break;
226 		case 'o':
227 			oldest = 1;
228 			criteria = 1;
229 			break;
230 		case 'q':
231 			quiet = 1;
232 			break;
233 		case 's':
234 			makelist(&sidlist, LT_SID, optarg);
235 			criteria = 1;
236 			break;
237 		case 't':
238 			makelist(&tdevlist, LT_TTY, optarg);
239 			criteria = 1;
240 			break;
241 		case 'u':
242 			makelist(&euidlist, LT_USER, optarg);
243 			criteria = 1;
244 			break;
245 		case 'v':
246 			inverse = 1;
247 			break;
248 		case 'x':
249 			fullmatch = 1;
250 			break;
251 		default:
252 			usage();
253 			/* NOTREACHED */
254 		}
255 
256 	argc -= optind;
257 	argv += optind;
258 	if (argc != 0)
259 		criteria = 1;
260 	if (!criteria || (newest && oldest))
261 		usage();
262 
263 	mypid = getpid();
264 
265 	/*
266 	 * Retrieve the list of running processes from the kernel.
267 	 */
268 	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
269 	if (kd == NULL)
270 		errx(STATUS_ERROR, "kvm_openfiles(): %s", buf);
271 
272 	plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
273 	if (plist == NULL)
274 		errx(STATUS_ERROR, "kvm_getprocs() failed");
275 
276 	if (matchargs == 0 && confirmkill == 0) {
277 		if (action == killact) {
278 			if (pledge("stdio proc", NULL) == -1)
279 				err(1, "pledge");
280 		} else if (action == grepact) {
281 			if (pledge("stdio", NULL) == -1)
282 				err(1, "pledge");
283 		}
284 	}
285 
286 	/*
287 	 * Allocate memory which will be used to keep track of the
288 	 * selection.
289 	 */
290 	if ((selected = calloc(nproc, 1)) == NULL)
291 		errx(STATUS_ERROR, "memory allocation failure");
292 
293 	/*
294 	 * Refine the selection.
295 	 */
296 	for (; *argv != NULL; argv++) {
297 		if ((rv = regcomp(&reg, *argv, REG_EXTENDED)) != 0) {
298 			regerror(rv, &reg, buf, sizeof(buf));
299 			errx(STATUS_BADUSAGE, "bad expression: %s", buf);
300 		}
301 
302 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
303 			if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 ||
304 			     kp->p_pid == mypid)
305 				continue;
306 
307 			if (matchargs)
308 				mstr = getargv(kp);
309 			else
310 				mstr = kp->p_comm;
311 
312 			rv = regexec(&reg, mstr, 1, &regmatch, 0);
313 			if (rv == 0) {
314 				if (fullmatch) {
315 					if (regmatch.rm_so == 0 &&
316 					    regmatch.rm_eo == strlen(mstr))
317 						selected[i] = 1;
318 				} else
319 					selected[i] = 1;
320 			} else if (rv != REG_NOMATCH) {
321 				regerror(rv, &reg, buf, sizeof(buf));
322 				errx(STATUS_ERROR, "regexec(): %s", buf);
323 			}
324 		}
325 
326 		regfree(&reg);
327 	}
328 
329 	for (i = 0, kp = plist; i < nproc; i++, kp++) {
330 		if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 ||
331 		     kp->p_pid == mypid)
332 			continue;
333 
334 		SLIST_FOREACH(li, &ruidlist, li_chain)
335 			if (kp->p_ruid == (uid_t)li->li_number)
336 				break;
337 		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
338 			selected[i] = 0;
339 			continue;
340 		}
341 
342 		SLIST_FOREACH(li, &rgidlist, li_chain)
343 			if (kp->p_rgid == (gid_t)li->li_number)
344 				break;
345 		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
346 			selected[i] = 0;
347 			continue;
348 		}
349 
350 		SLIST_FOREACH(li, &euidlist, li_chain)
351 			if (kp->p_uid == (uid_t)li->li_number)
352 				break;
353 		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
354 			selected[i] = 0;
355 			continue;
356 		}
357 
358 		SLIST_FOREACH(li, &ppidlist, li_chain)
359 			if (kp->p_ppid == (uid_t)li->li_number)
360 				break;
361 		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
362 			selected[i] = 0;
363 			continue;
364 		}
365 
366 		SLIST_FOREACH(li, &pgrplist, li_chain)
367 			if (kp->p__pgid == (uid_t)li->li_number)
368 				break;
369 		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
370 			selected[i] = 0;
371 			continue;
372 		}
373 
374 		SLIST_FOREACH(li, &tdevlist, li_chain) {
375 			if (li->li_number == -1 &&
376 			    (kp->p_psflags & PS_CONTROLT) == 0)
377 				break;
378 			if (kp->p_tdev == (uid_t)li->li_number)
379 				break;
380 		}
381 		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
382 			selected[i] = 0;
383 			continue;
384 		}
385 
386 		SLIST_FOREACH(li, &sidlist, li_chain)
387 			if (kp->p_sid == (uid_t)li->li_number)
388 				break;
389 		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
390 			selected[i] = 0;
391 			continue;
392 		}
393 
394 		SLIST_FOREACH(li, &rtablist, li_chain)
395 			if (kp->p_rtableid == (u_int32_t)li->li_number)
396 				break;
397 		if (SLIST_FIRST(&rtablist) != NULL && li == NULL) {
398 			selected[i] = 0;
399 			continue;
400 		}
401 
402 		if (argc == 0)
403 			selected[i] = 1;
404 	}
405 
406 	if (newest || oldest) {
407 		bestidx = -1;
408 
409 		if (newest)
410 			bestsec = bestusec = 0;
411 		else
412 			bestsec = bestusec = UINT32_MAX;
413 
414 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
415 			if (!selected[i])
416 				continue;
417 
418 			if ((newest && (kp->p_ustart_sec > bestsec ||
419 			    (kp->p_ustart_sec == bestsec
420 			    && kp->p_ustart_usec > bestusec)))
421 			|| (oldest && (kp->p_ustart_sec < bestsec ||
422                             (kp->p_ustart_sec == bestsec
423                             && kp->p_ustart_usec < bestusec)))) {
424 
425 				bestsec = kp->p_ustart_sec;
426 				bestusec = kp->p_ustart_usec;
427 				bestidx = i;
428 			}
429 		}
430 
431 		memset(selected, 0, nproc);
432 		if (bestidx != -1)
433 			selected[bestidx] = 1;
434 	}
435 
436 	/*
437 	 * Take the appropriate action for each matched process, if any.
438 	 */
439 	rv = STATUS_NOMATCH;
440 	for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) {
441 		if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 ||
442 		     kp->p_pid == mypid)
443 			continue;
444 		if (selected[i] == inverse)
445 			continue;
446 
447 		switch ((*action)(kp, j++)) {
448 		case STATUS_MATCH:
449 			if (rv != STATUS_ERROR)
450 				rv = STATUS_MATCH;
451 			break;
452 		case STATUS_NOMATCH:
453 			j--;
454 			break;
455 		case STATUS_ERROR:
456 			rv = STATUS_ERROR;
457 			break;
458 		}
459 	}
460 	if (pgrep && j && !quiet)
461 		putchar('\n');
462 
463 	exit(rv);
464 }
465 
466 void
467 usage(void)
468 {
469 	const char *ustr;
470 
471 	if (pgrep)
472 		ustr = "[-flnoqvx] [-d delim]";
473 	else
474 		ustr = "[-signal] [-fIlnoqvx]";
475 
476 	fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid]"
477 	    "\n\t[-T rtable] [-t tty] [-U uid] [-u euid] [pattern ...]\n",
478 	    __progname, ustr);
479 
480 	exit(STATUS_BADUSAGE);
481 }
482 
483 int
484 askyn(struct kinfo_proc *kp)
485 {
486 	int first, ch;
487 
488 	printf("kill %d %.60s? ", (int)kp->p_pid, getargv(kp));
489 	fflush(stdout);
490 
491 	first = ch = getchar();
492 	while (ch != '\n' && ch != EOF)
493 		ch = getchar();
494 	return (first == 'y' || first == 'Y');
495 }
496 
497 int
498 killact(struct kinfo_proc *kp, int dummy)
499 {
500 	int doit;
501 
502 	if (confirmkill) {
503 		doit = askyn(kp);
504 	} else {
505 		if (longfmt && !quiet)
506 			printf("%d %s\n", (int)kp->p_pid, kp->p_comm);
507 		doit = 1;
508 	}
509 
510 	if (doit && kill(kp->p_pid, signum) == -1) {
511 		if (errno == ESRCH)
512 			return (STATUS_NOMATCH);
513 		warn("signalling pid %d", (int)kp->p_pid);
514 		return (STATUS_ERROR);
515 	}
516 	return (STATUS_MATCH);
517 }
518 
519 int
520 grepact(struct kinfo_proc *kp, int printdelim)
521 {
522 	char **argv;
523 
524 	if (quiet)
525 		return (STATUS_MATCH);
526 	if (longfmt && matchargs)
527 		if ((argv = kvm_getargv(kd, kp, 0)) == NULL)
528 			return (errno == ESRCH ? STATUS_NOMATCH : STATUS_ERROR);
529 	if (printdelim)
530 		fputs(delim, stdout);
531 	if (longfmt && matchargs) {
532 		printf("%d ", (int)kp->p_pid);
533 		for (; *argv != NULL; argv++) {
534 			printf("%s", *argv);
535 			if (argv[1] != NULL)
536 				putchar(' ');
537 		}
538 	} else if (longfmt)
539 		printf("%d %s", (int)kp->p_pid, kp->p_comm);
540 	else
541 		printf("%d", (int)kp->p_pid);
542 
543 	return (STATUS_MATCH);
544 }
545 
546 void
547 makelist(struct listhead *head, enum listtype type, char *src)
548 {
549 	struct list *li;
550 	struct passwd *pw;
551 	struct group *gr;
552 	struct stat st;
553 	char *sp, *p, buf[PATH_MAX];
554 	int empty;
555 
556 	empty = 1;
557 
558 	while ((sp = strsep(&src, ",")) != NULL) {
559 		if (*sp == '\0')
560 			usage();
561 
562 		if ((li = malloc(sizeof(*li))) == NULL)
563 			errx(STATUS_ERROR, "memory allocation failure");
564 		SLIST_INSERT_HEAD(head, li, li_chain);
565 		empty = 0;
566 
567 		li->li_number = strtol(sp, &p, 0);
568 		if (*p == '\0') {
569 			switch (type) {
570 			case LT_PGRP:
571 				if (li->li_number == 0)
572 					li->li_number = getpgrp();
573 				break;
574 			case LT_SID:
575 				if (li->li_number == 0)
576 					li->li_number = getsid(mypid);
577 				break;
578 			case LT_RTABLE:
579 				if (li->li_number < 0 ||
580 				    li->li_number > RT_TABLEID_MAX)
581 					errx(STATUS_BADUSAGE,
582 					    "rtable out of range");
583 				break;
584 			case LT_TTY:
585 				usage();
586 			default:
587 				break;
588 			}
589 			continue;
590 		}
591 
592 		switch (type) {
593 		case LT_USER:
594 			if ((pw = getpwnam(sp)) == NULL)
595 				errx(STATUS_BADUSAGE, "unknown user `%s'", sp);
596 			li->li_number = pw->pw_uid;
597 			break;
598 		case LT_GROUP:
599 			if ((gr = getgrnam(sp)) == NULL)
600 				errx(STATUS_BADUSAGE, "unknown group `%s'", sp);
601 			li->li_number = gr->gr_gid;
602 			break;
603 		case LT_TTY:
604 			if (strcmp(sp, "-") == 0) {
605 				li->li_number = -1;
606 				break;
607 			} else if (strcmp(sp, "co") == 0)
608 				p = "console";
609 			else if (strncmp(sp, "tty", 3) == 0)
610 				p = sp;
611 			else
612 				p = NULL;
613 
614 			if (p == NULL)
615 				snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
616 			else
617 				snprintf(buf, sizeof(buf), "/dev/%s", p);
618 
619 			if (stat(buf, &st) < 0) {
620 				if (errno == ENOENT)
621 					errx(STATUS_BADUSAGE,
622 					    "no such tty: `%s'", sp);
623 				err(STATUS_ERROR, "stat(%s)", sp);
624 			}
625 
626 			if (!S_ISCHR(st.st_mode))
627 				errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
628 
629 			li->li_number = st.st_rdev;
630 			break;
631 		default:
632 			usage();
633 		}
634 	}
635 
636 	if (empty)
637 		usage();
638 }
639