xref: /openbsd-src/usr.bin/pkill/pkill.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /*	$OpenBSD: pkill.c,v 1.18 2009/10/27 23:59:41 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/param.h>
35 #include <sys/sysctl.h>
36 #include <sys/proc.h>
37 #include <sys/queue.h>
38 #include <sys/stat.h>
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdint.h>
43 #include <limits.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <signal.h>
47 #include <regex.h>
48 #include <ctype.h>
49 #include <kvm.h>
50 #include <err.h>
51 #include <pwd.h>
52 #include <grp.h>
53 #include <errno.h>
54 
55 #define	STATUS_MATCH	0
56 #define	STATUS_NOMATCH	1
57 #define	STATUS_BADUSAGE	2
58 #define	STATUS_ERROR	3
59 
60 enum listtype {
61 	LT_GENERIC,
62 	LT_USER,
63 	LT_GROUP,
64 	LT_TTY,
65 	LT_PGRP,
66 	LT_SID
67 };
68 
69 struct list {
70 	SLIST_ENTRY(list) li_chain;
71 	long	li_number;
72 };
73 
74 SLIST_HEAD(listhead, list);
75 
76 struct kinfo_proc2	*plist;
77 char	*selected;
78 char	*delim = "\n";
79 int	nproc;
80 int	pgrep;
81 int	signum = SIGTERM;
82 int	newest;
83 int	oldest;
84 int	inverse;
85 int	longfmt;
86 int	matchargs;
87 int	fullmatch;
88 kvm_t	*kd;
89 pid_t	mypid;
90 
91 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
92 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
93 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
94 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
95 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
96 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
97 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
98 
99 int	main(int, char **);
100 void	usage(void);
101 int	killact(struct kinfo_proc2 *, int);
102 int	grepact(struct kinfo_proc2 *, int);
103 void	makelist(struct listhead *, enum listtype, char *);
104 
105 extern char *__progname;
106 
107 int
108 main(int argc, char **argv)
109 {
110 	extern char *optarg;
111 	extern int optind;
112 	char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q;
113 	int i, j, ch, bestidx, rv, criteria;
114 	int (*action)(struct kinfo_proc2 *, int);
115 	struct kinfo_proc2 *kp;
116 	struct list *li;
117 	u_int32_t bestsec, bestusec;
118 	regex_t reg;
119 	regmatch_t regmatch;
120 
121 	if (strcmp(__progname, "pgrep") == 0) {
122 		action = grepact;
123 		pgrep = 1;
124 	} else {
125 		action = killact;
126 		p = argv[1];
127 
128 		if (argc > 1 && p[0] == '-') {
129 			p++;
130 			i = (int)strtol(p, &q, 10);
131 			if (*q == '\0') {
132 				signum = i;
133 				argv++;
134 				argc--;
135 			} else {
136 				if (strncasecmp(p, "sig", 3) == 0)
137 					p += 3;
138 				for (i = 1; i < NSIG; i++)
139 					if (strcasecmp(sys_signame[i], p) == 0)
140 						break;
141 				if (i != NSIG) {
142 					signum = i;
143 					argv++;
144 					argc--;
145 				}
146 			}
147 		}
148 	}
149 
150 	criteria = 0;
151 
152 	while ((ch = getopt(argc, argv, "G:P:U:d:fg:lnos:t:u:vx")) != -1)
153 		switch (ch) {
154 		case 'G':
155 			makelist(&rgidlist, LT_GROUP, optarg);
156 			criteria = 1;
157 			break;
158 		case 'P':
159 			makelist(&ppidlist, LT_GENERIC, optarg);
160 			criteria = 1;
161 			break;
162 		case 'U':
163 			makelist(&ruidlist, LT_USER, optarg);
164 			criteria = 1;
165 			break;
166 		case 'd':
167 			if (!pgrep)
168 				usage();
169 			delim = optarg;
170 			break;
171 		case 'f':
172 			matchargs = 1;
173 			break;
174 		case 'g':
175 			makelist(&pgrplist, LT_PGRP, optarg);
176 			criteria = 1;
177 			break;
178 		case 'l':
179 			if (!pgrep)
180 				usage();
181 			longfmt = 1;
182 			break;
183 		case 'n':
184 			newest = 1;
185 			criteria = 1;
186 			break;
187 		case 'o':
188 			oldest = 1;
189 			criteria = 1;
190 			break;
191 		case 's':
192 			makelist(&sidlist, LT_SID, optarg);
193 			criteria = 1;
194 			break;
195 		case 't':
196 			makelist(&tdevlist, LT_TTY, optarg);
197 			criteria = 1;
198 			break;
199 		case 'u':
200 			makelist(&euidlist, LT_USER, optarg);
201 			criteria = 1;
202 			break;
203 		case 'v':
204 			inverse = 1;
205 			break;
206 		case 'x':
207 			fullmatch = 1;
208 			break;
209 		default:
210 			usage();
211 			/* NOTREACHED */
212 		}
213 
214 	argc -= optind;
215 	argv += optind;
216 	if (argc != 0)
217 		criteria = 1;
218 	if (!criteria || (newest && oldest))
219 		usage();
220 
221 	mypid = getpid();
222 
223 	/*
224 	 * Retrieve the list of running processes from the kernel.
225 	 */
226 	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
227 	if (kd == NULL)
228 		errx(STATUS_ERROR, "kvm_openfiles(): %s", buf);
229 
230 	plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
231 	if (plist == NULL)
232 		errx(STATUS_ERROR, "kvm_getproc2() failed");
233 
234 	/*
235 	 * Allocate memory which will be used to keep track of the
236 	 * selection.
237 	 */
238 	if ((selected = malloc(nproc)) == NULL)
239 		errx(STATUS_ERROR, "memory allocation failure");
240 	memset(selected, 0, nproc);
241 
242 	/*
243 	 * Refine the selection.
244 	 */
245 	for (; *argv != NULL; argv++) {
246 		if ((rv = regcomp(&reg, *argv, REG_EXTENDED)) != 0) {
247 			regerror(rv, &reg, buf, sizeof(buf));
248 			errx(STATUS_BADUSAGE, "bad expression: %s", buf);
249 		}
250 
251 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
252 			if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
253 				continue;
254 
255 			if (matchargs) {
256 				if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
257 					continue;
258 
259 				j = 0;
260 				while (j < sizeof(buf) && *pargv != NULL) {
261 					int ret;
262 
263 					ret = snprintf(buf + j, sizeof(buf) - j,
264 					    pargv[1] != NULL ? "%s " : "%s",
265 					    pargv[0]);
266 					if (ret >= sizeof(buf) - j)
267 						j += sizeof(buf) - j - 1;
268 					else if (ret > 0)
269 						j += ret;
270 					pargv++;
271 				}
272 
273 				mstr = buf;
274 			} else
275 				mstr = kp->p_comm;
276 
277 			rv = regexec(&reg, mstr, 1, &regmatch, 0);
278 			if (rv == 0) {
279 				if (fullmatch) {
280 					if (regmatch.rm_so == 0 &&
281 					    regmatch.rm_eo == strlen(mstr))
282 						selected[i] = 1;
283 				} else
284 					selected[i] = 1;
285 			} else if (rv != REG_NOMATCH) {
286 				regerror(rv, &reg, buf, sizeof(buf));
287 				errx(STATUS_ERROR, "regexec(): %s", buf);
288 			}
289 		}
290 
291 		regfree(&reg);
292 	}
293 
294 	for (i = 0, kp = plist; i < nproc; i++, kp++) {
295 		if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
296 			continue;
297 
298 		SLIST_FOREACH(li, &ruidlist, li_chain)
299 			if (kp->p_ruid == (uid_t)li->li_number)
300 				break;
301 		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
302 			selected[i] = 0;
303 			continue;
304 		}
305 
306 		SLIST_FOREACH(li, &rgidlist, li_chain)
307 			if (kp->p_rgid == (gid_t)li->li_number)
308 				break;
309 		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
310 			selected[i] = 0;
311 			continue;
312 		}
313 
314 		SLIST_FOREACH(li, &euidlist, li_chain)
315 			if (kp->p_uid == (uid_t)li->li_number)
316 				break;
317 		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
318 			selected[i] = 0;
319 			continue;
320 		}
321 
322 		SLIST_FOREACH(li, &ppidlist, li_chain)
323 			if (kp->p_ppid == (uid_t)li->li_number)
324 				break;
325 		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
326 			selected[i] = 0;
327 			continue;
328 		}
329 
330 		SLIST_FOREACH(li, &pgrplist, li_chain)
331 			if (kp->p__pgid == (uid_t)li->li_number)
332 				break;
333 		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
334 			selected[i] = 0;
335 			continue;
336 		}
337 
338 		SLIST_FOREACH(li, &tdevlist, li_chain) {
339 			if (li->li_number == -1 &&
340 			    (kp->p_flag & P_CONTROLT) == 0)
341 				break;
342 			if (kp->p_tdev == (uid_t)li->li_number)
343 				break;
344 		}
345 		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
346 			selected[i] = 0;
347 			continue;
348 		}
349 
350 		SLIST_FOREACH(li, &sidlist, li_chain)
351 			if (kp->p_sid == (uid_t)li->li_number)
352 				break;
353 		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
354 			selected[i] = 0;
355 			continue;
356 		}
357 
358 		if (argc == 0)
359 			selected[i] = 1;
360 	}
361 
362 	if (newest || oldest) {
363 		bestidx = -1;
364 
365 		if (newest)
366 			bestsec = bestusec = 0;
367 		else
368 			bestsec = bestusec = UINT32_MAX;
369 
370 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
371 			if (!selected[i])
372 				continue;
373 
374 			if ((newest && (kp->p_ustart_sec > bestsec ||
375 			    (kp->p_ustart_sec == bestsec
376 			    && kp->p_ustart_usec > bestusec)))
377 			|| (oldest && (kp->p_ustart_sec < bestsec ||
378                             (kp->p_ustart_sec == bestsec
379                             && kp->p_ustart_usec < bestusec)))) {
380 
381 				bestsec = kp->p_ustart_sec;
382 				bestusec = kp->p_ustart_usec;
383 				bestidx = i;
384 			}
385 		}
386 
387 		memset(selected, 0, nproc);
388 		if (bestidx != -1)
389 			selected[bestidx] = 1;
390 	}
391 
392 	/*
393 	 * Take the appropriate action for each matched process, if any.
394 	 */
395 	rv = STATUS_NOMATCH;
396 	for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) {
397 		if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
398 			continue;
399 		if (selected[i]) {
400 			if (inverse)
401 				continue;
402 		} else if (!inverse)
403 			continue;
404 
405 		if ((*action)(kp, j++) == -1)
406 			rv = STATUS_ERROR;
407 		else if (rv != STATUS_ERROR)
408 			rv = STATUS_MATCH;
409 	}
410 	if (pgrep && j)
411 		putchar('\n');
412 
413 	exit(rv);
414 }
415 
416 void
417 usage(void)
418 {
419 	const char *ustr;
420 
421 	if (pgrep)
422 		ustr = "[-flnovx] [-d delim]";
423 	else
424 		ustr = "[-signal] [-fnovx]";
425 
426 	fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] "
427 	    "[-t tty]\n\t[-U uid] [-u euid] [pattern ...]\n", __progname, ustr);
428 
429 	exit(STATUS_ERROR);
430 }
431 
432 int
433 killact(struct kinfo_proc2 *kp, int dummy)
434 {
435 
436 	if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) {
437 		warn("signalling pid %d", (int)kp->p_pid);
438 		return (-1);
439 	}
440 	return (0);
441 }
442 
443 int
444 grepact(struct kinfo_proc2 *kp, int printdelim)
445 {
446 	char **argv;
447 
448 	if (printdelim)
449 		fputs(delim, stdout);
450 	if (longfmt && matchargs) {
451 		if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
452 			return (-1);
453 
454 		printf("%d ", (int)kp->p_pid);
455 		for (; *argv != NULL; argv++) {
456 			printf("%s", *argv);
457 			if (argv[1] != NULL)
458 				putchar(' ');
459 		}
460 	} else if (longfmt)
461 		printf("%d %s", (int)kp->p_pid, kp->p_comm);
462 	else
463 		printf("%d", (int)kp->p_pid);
464 
465 	return (0);
466 }
467 
468 void
469 makelist(struct listhead *head, enum listtype type, char *src)
470 {
471 	struct list *li;
472 	struct passwd *pw;
473 	struct group *gr;
474 	struct stat st;
475 	char *sp, *p, buf[MAXPATHLEN];
476 	int empty;
477 
478 	empty = 1;
479 
480 	while ((sp = strsep(&src, ",")) != NULL) {
481 		if (*sp == '\0')
482 			usage();
483 
484 		if ((li = malloc(sizeof(*li))) == NULL)
485 			errx(STATUS_ERROR, "memory allocation failure");
486 		SLIST_INSERT_HEAD(head, li, li_chain);
487 		empty = 0;
488 
489 		li->li_number = (uid_t)strtol(sp, &p, 0);
490 		if (*p == '\0') {
491 			switch (type) {
492 			case LT_PGRP:
493 				if (li->li_number == 0)
494 					li->li_number = getpgrp();
495 				break;
496 			case LT_SID:
497 				if (li->li_number == 0)
498 					li->li_number = getsid(mypid);
499 				break;
500 			case LT_TTY:
501 				usage();
502 			default:
503 				break;
504 			}
505 			continue;
506 		}
507 
508 		switch (type) {
509 		case LT_USER:
510 			if ((pw = getpwnam(sp)) == NULL)
511 				errx(STATUS_BADUSAGE, "unknown user `%s'", sp);
512 			li->li_number = pw->pw_uid;
513 			break;
514 		case LT_GROUP:
515 			if ((gr = getgrnam(sp)) == NULL)
516 				errx(STATUS_BADUSAGE, "unknown group `%s'", sp);
517 			li->li_number = gr->gr_gid;
518 			break;
519 		case LT_TTY:
520 			if (strcmp(sp, "-") == 0) {
521 				li->li_number = -1;
522 				break;
523 			} else if (strcmp(sp, "co") == 0)
524 				p = "console";
525 			else if (strncmp(sp, "tty", 3) == 0)
526 				p = sp;
527 			else
528 				p = NULL;
529 
530 			if (p == NULL)
531 				snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
532 			else
533 				snprintf(buf, sizeof(buf), "/dev/%s", p);
534 
535 			if (stat(buf, &st) < 0) {
536 				if (errno == ENOENT)
537 					errx(STATUS_BADUSAGE,
538 					    "no such tty: `%s'", sp);
539 				err(STATUS_ERROR, "stat(%s)", sp);
540 			}
541 
542 			if (!S_ISCHR(st.st_mode))
543 				errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
544 
545 			li->li_number = st.st_rdev;
546 			break;
547 		default:
548 			usage();
549 		}
550 	}
551 
552 	if (empty)
553 		usage();
554 }
555