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