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