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