xref: /openbsd-src/usr.bin/pkill/pkill.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: pkill.c,v 1.39 2016/10/10 02:22:59 gsoares 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 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 *
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
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_flag & (P_SYSTEM | P_THREAD)) != 0 ||
301 			     kp->p_pid == mypid)
302 				continue;
303 
304 			if (matchargs)
305 				mstr = getargv(kp);
306 			else
307 				mstr = kp->p_comm;
308 
309 			rv = regexec(&reg, mstr, 1, &regmatch, 0);
310 			if (rv == 0) {
311 				if (fullmatch) {
312 					if (regmatch.rm_so == 0 &&
313 					    regmatch.rm_eo == strlen(mstr))
314 						selected[i] = 1;
315 				} else
316 					selected[i] = 1;
317 			} else if (rv != REG_NOMATCH) {
318 				regerror(rv, &reg, buf, sizeof(buf));
319 				errx(STATUS_ERROR, "regexec(): %s", buf);
320 			}
321 		}
322 
323 		regfree(&reg);
324 	}
325 
326 	for (i = 0, kp = plist; i < nproc; i++, kp++) {
327 		if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 ||
328 		     kp->p_pid == mypid)
329 			continue;
330 
331 		SLIST_FOREACH(li, &ruidlist, li_chain)
332 			if (kp->p_ruid == (uid_t)li->li_number)
333 				break;
334 		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
335 			selected[i] = 0;
336 			continue;
337 		}
338 
339 		SLIST_FOREACH(li, &rgidlist, li_chain)
340 			if (kp->p_rgid == (gid_t)li->li_number)
341 				break;
342 		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
343 			selected[i] = 0;
344 			continue;
345 		}
346 
347 		SLIST_FOREACH(li, &euidlist, li_chain)
348 			if (kp->p_uid == (uid_t)li->li_number)
349 				break;
350 		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
351 			selected[i] = 0;
352 			continue;
353 		}
354 
355 		SLIST_FOREACH(li, &ppidlist, li_chain)
356 			if (kp->p_ppid == (uid_t)li->li_number)
357 				break;
358 		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
359 			selected[i] = 0;
360 			continue;
361 		}
362 
363 		SLIST_FOREACH(li, &pgrplist, li_chain)
364 			if (kp->p__pgid == (uid_t)li->li_number)
365 				break;
366 		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
367 			selected[i] = 0;
368 			continue;
369 		}
370 
371 		SLIST_FOREACH(li, &tdevlist, li_chain) {
372 			if (li->li_number == -1 &&
373 			    (kp->p_psflags & PS_CONTROLT) == 0)
374 				break;
375 			if (kp->p_tdev == (uid_t)li->li_number)
376 				break;
377 		}
378 		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
379 			selected[i] = 0;
380 			continue;
381 		}
382 
383 		SLIST_FOREACH(li, &sidlist, li_chain)
384 			if (kp->p_sid == (uid_t)li->li_number)
385 				break;
386 		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
387 			selected[i] = 0;
388 			continue;
389 		}
390 
391 		SLIST_FOREACH(li, &rtablist, li_chain)
392 			if (kp->p_rtableid == (u_int32_t)li->li_number)
393 				break;
394 		if (SLIST_FIRST(&rtablist) != NULL && li == NULL) {
395 			selected[i] = 0;
396 			continue;
397 		}
398 
399 		if (argc == 0)
400 			selected[i] = 1;
401 	}
402 
403 	if (newest || oldest) {
404 		bestidx = -1;
405 
406 		if (newest)
407 			bestsec = bestusec = 0;
408 		else
409 			bestsec = bestusec = UINT32_MAX;
410 
411 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
412 			if (!selected[i])
413 				continue;
414 
415 			if ((newest && (kp->p_ustart_sec > bestsec ||
416 			    (kp->p_ustart_sec == bestsec
417 			    && kp->p_ustart_usec > bestusec)))
418 			|| (oldest && (kp->p_ustart_sec < bestsec ||
419                             (kp->p_ustart_sec == bestsec
420                             && kp->p_ustart_usec < bestusec)))) {
421 
422 				bestsec = kp->p_ustart_sec;
423 				bestusec = kp->p_ustart_usec;
424 				bestidx = i;
425 			}
426 		}
427 
428 		memset(selected, 0, nproc);
429 		if (bestidx != -1)
430 			selected[bestidx] = 1;
431 	}
432 
433 	/*
434 	 * Take the appropriate action for each matched process, if any.
435 	 */
436 	rv = STATUS_NOMATCH;
437 	for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) {
438 		if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 ||
439 		     kp->p_pid == mypid)
440 			continue;
441 		if (selected[i] == inverse)
442 			continue;
443 
444 		switch ((*action)(kp, j++)) {
445 		case STATUS_MATCH:
446 			if (rv != STATUS_ERROR)
447 				rv = STATUS_MATCH;
448 			break;
449 		case STATUS_NOMATCH:
450 			j--;
451 			break;
452 		case STATUS_ERROR:
453 			rv = STATUS_ERROR;
454 			break;
455 		}
456 	}
457 	if (pgrep && j && !quiet)
458 		putchar('\n');
459 
460 	return(rv);
461 }
462 
463 static void __dead
464 usage(void)
465 {
466 	const char *ustr;
467 
468 	if (pgrep)
469 		ustr = "[-flnoqvx] [-d delim]";
470 	else
471 		ustr = "[-signal] [-fIlnoqvx]";
472 
473 	fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid]"
474 	    "\n\t[-T rtable] [-t tty] [-U uid] [-u euid] [pattern ...]\n",
475 	    __progname, ustr);
476 
477 	exit(STATUS_BADUSAGE);
478 }
479 
480 static int
481 askyn(struct kinfo_proc *kp)
482 {
483 	int first, ch;
484 
485 	printf("kill %d %.60s? ", (int)kp->p_pid, getargv(kp));
486 	fflush(stdout);
487 
488 	first = ch = getchar();
489 	while (ch != '\n' && ch != EOF)
490 		ch = getchar();
491 	return (first == 'y' || first == 'Y');
492 }
493 
494 static int
495 killact(struct kinfo_proc *kp, int dummy)
496 {
497 	int doit;
498 
499 	if (confirmkill) {
500 		doit = askyn(kp);
501 	} else {
502 		if (longfmt && !quiet)
503 			printf("%d %s\n", (int)kp->p_pid, kp->p_comm);
504 		doit = 1;
505 	}
506 
507 	if (doit && kill(kp->p_pid, signum) == -1) {
508 		if (errno == ESRCH)
509 			return (STATUS_NOMATCH);
510 		warn("signalling pid %d", (int)kp->p_pid);
511 		return (STATUS_ERROR);
512 	}
513 	return (STATUS_MATCH);
514 }
515 
516 static int
517 grepact(struct kinfo_proc *kp, int printdelim)
518 {
519 	char **argv;
520 
521 	if (quiet)
522 		return (STATUS_MATCH);
523 	if (longfmt && matchargs)
524 		if ((argv = kvm_getargv(kd, kp, 0)) == NULL)
525 			return (errno == ESRCH ? STATUS_NOMATCH : STATUS_ERROR);
526 	if (printdelim)
527 		fputs(delim, stdout);
528 	if (longfmt && matchargs) {
529 		printf("%d ", (int)kp->p_pid);
530 		for (; *argv != NULL; argv++) {
531 			printf("%s", *argv);
532 			if (argv[1] != NULL)
533 				putchar(' ');
534 		}
535 	} else if (longfmt)
536 		printf("%d %s", (int)kp->p_pid, kp->p_comm);
537 	else
538 		printf("%d", (int)kp->p_pid);
539 
540 	return (STATUS_MATCH);
541 }
542 
543 static void
544 makelist(struct listhead *head, enum listtype type, char *src)
545 {
546 	struct list *li;
547 	struct passwd *pw;
548 	struct group *gr;
549 	struct stat st;
550 	char *sp, *p, buf[PATH_MAX];
551 	int empty;
552 
553 	empty = 1;
554 
555 	while ((sp = strsep(&src, ",")) != NULL) {
556 		if (*sp == '\0')
557 			usage();
558 
559 		if ((li = malloc(sizeof(*li))) == NULL)
560 			errx(STATUS_ERROR, "memory allocation failure");
561 		SLIST_INSERT_HEAD(head, li, li_chain);
562 		empty = 0;
563 
564 		li->li_number = strtol(sp, &p, 0);
565 		if (*p == '\0') {
566 			switch (type) {
567 			case LT_PGRP:
568 				if (li->li_number == 0)
569 					li->li_number = getpgrp();
570 				break;
571 			case LT_SID:
572 				if (li->li_number == 0)
573 					li->li_number = getsid(mypid);
574 				break;
575 			case LT_RTABLE:
576 				if (li->li_number < 0 ||
577 				    li->li_number > RT_TABLEID_MAX)
578 					errx(STATUS_BADUSAGE,
579 					    "rtable out of range");
580 				break;
581 			case LT_TTY:
582 				usage();
583 			default:
584 				break;
585 			}
586 			continue;
587 		}
588 
589 		switch (type) {
590 		case LT_USER:
591 			if ((pw = getpwnam(sp)) == NULL)
592 				errx(STATUS_BADUSAGE, "unknown user `%s'", sp);
593 			li->li_number = pw->pw_uid;
594 			break;
595 		case LT_GROUP:
596 			if ((gr = getgrnam(sp)) == NULL)
597 				errx(STATUS_BADUSAGE, "unknown group `%s'", sp);
598 			li->li_number = gr->gr_gid;
599 			break;
600 		case LT_TTY:
601 			if (strcmp(sp, "-") == 0) {
602 				li->li_number = -1;
603 				break;
604 			} else if (strcmp(sp, "co") == 0)
605 				p = "console";
606 			else if (strncmp(sp, "tty", 3) == 0)
607 				p = sp;
608 			else
609 				p = NULL;
610 
611 			if (p == NULL)
612 				snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
613 			else
614 				snprintf(buf, sizeof(buf), "/dev/%s", p);
615 
616 			if (stat(buf, &st) < 0) {
617 				if (errno == ENOENT)
618 					errx(STATUS_BADUSAGE,
619 					    "no such tty: `%s'", sp);
620 				err(STATUS_ERROR, "stat(%s)", sp);
621 			}
622 
623 			if (!S_ISCHR(st.st_mode))
624 				errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
625 
626 			li->li_number = st.st_rdev;
627 			break;
628 		default:
629 			usage();
630 		}
631 	}
632 
633 	if (empty)
634 		usage();
635 }
636