1 /*	$OpenBSD: pkill.c,v 1.14 2005/07/16 11:48:46 jmc 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  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #ifndef lint
41 static const char rcsid[] = "$OpenBSD: pkill.c,v 1.14 2005/07/16 11:48:46 jmc Exp $";
42 #endif /* !lint */
43 
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/sysctl.h>
47 #include <sys/proc.h>
48 #include <sys/queue.h>
49 #include <sys/stat.h>
50 
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <limits.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <signal.h>
57 #include <regex.h>
58 #include <ctype.h>
59 #include <kvm.h>
60 #include <err.h>
61 #include <pwd.h>
62 #include <grp.h>
63 #include <errno.h>
64 
65 #define	STATUS_MATCH	0
66 #define	STATUS_NOMATCH	1
67 #define	STATUS_BADUSAGE	2
68 #define	STATUS_ERROR	3
69 
70 enum listtype {
71 	LT_GENERIC,
72 	LT_USER,
73 	LT_GROUP,
74 	LT_TTY,
75 	LT_PGRP,
76 	LT_SID
77 };
78 
79 struct list {
80 	SLIST_ENTRY(list) li_chain;
81 	long	li_number;
82 };
83 
84 SLIST_HEAD(listhead, list);
85 
86 struct kinfo_proc2	*plist;
87 char	*selected;
88 char	*delim = "\n";
89 int	nproc;
90 int	pgrep;
91 int	signum = SIGTERM;
92 int	newest;
93 int	inverse;
94 int	longfmt;
95 int	matchargs;
96 int	fullmatch;
97 kvm_t	*kd;
98 pid_t	mypid;
99 
100 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
101 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
102 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
103 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
104 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
105 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
106 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
107 
108 int	main(int, char **);
109 void	usage(void);
110 int	killact(struct kinfo_proc2 *, int);
111 int	grepact(struct kinfo_proc2 *, int);
112 void	makelist(struct listhead *, enum listtype, char *);
113 
114 extern char *__progname;
115 
116 int
main(int argc,char ** argv)117 main(int argc, char **argv)
118 {
119 	extern char *optarg;
120 	extern int optind;
121 	char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q;
122 	int i, j, ch, bestidx, rv, criteria;
123 	int (*action)(struct kinfo_proc2 *, int);
124 	struct kinfo_proc2 *kp;
125 	struct list *li;
126 	u_int32_t bestsec, bestusec;
127 	regex_t reg;
128 	regmatch_t regmatch;
129 
130 	if (strcmp(__progname, "pgrep") == 0) {
131 		action = grepact;
132 		pgrep = 1;
133 	} else {
134 		action = killact;
135 		p = argv[1];
136 
137 		if (argc > 1 && p[0] == '-') {
138 			p++;
139 			i = (int)strtol(p, &q, 10);
140 			if (*q == '\0') {
141 				signum = i;
142 				argv++;
143 				argc--;
144 			} else {
145 				if (strncasecmp(p, "sig", 3) == 0)
146 					p += 3;
147 				for (i = 1; i < NSIG; i++)
148 					if (strcasecmp(sys_signame[i], p) == 0)
149 						break;
150 				if (i != NSIG) {
151 					signum = i;
152 					argv++;
153 					argc--;
154 				}
155 			}
156 		}
157 	}
158 
159 	criteria = 0;
160 
161 	while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1)
162 		switch (ch) {
163 		case 'G':
164 			makelist(&rgidlist, LT_GROUP, optarg);
165 			criteria = 1;
166 			break;
167 		case 'P':
168 			makelist(&ppidlist, LT_GENERIC, optarg);
169 			criteria = 1;
170 			break;
171 		case 'U':
172 			makelist(&ruidlist, LT_USER, optarg);
173 			criteria = 1;
174 			break;
175 		case 'd':
176 			if (!pgrep)
177 				usage();
178 			delim = optarg;
179 			break;
180 		case 'f':
181 			matchargs = 1;
182 			break;
183 		case 'g':
184 			makelist(&pgrplist, LT_PGRP, optarg);
185 			criteria = 1;
186 			break;
187 		case 'l':
188 			if (!pgrep)
189 				usage();
190 			longfmt = 1;
191 			break;
192 		case 'n':
193 			newest = 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)
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_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
236 	if (plist == NULL)
237 		errx(STATUS_ERROR, "kvm_getproc2() 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) != 0 || kp->p_pid == mypid)
258 				continue;
259 
260 			if (matchargs) {
261 				if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
262 					continue;
263 
264 				j = 0;
265 				while (j < sizeof(buf) && *pargv != NULL) {
266 					int ret;
267 
268 					ret = snprintf(buf + j, sizeof(buf) - j,
269 					    pargv[1] != NULL ? "%s " : "%s",
270 					    pargv[0]);
271 					if (ret >= sizeof(buf) - j)
272 						j += sizeof(buf) - j - 1;
273 					else if (ret > 0)
274 						j += ret;
275 					pargv++;
276 				}
277 
278 				mstr = buf;
279 			} else
280 				mstr = kp->p_comm;
281 
282 			rv = regexec(&reg, mstr, 1, &regmatch, 0);
283 			if (rv == 0) {
284 				if (fullmatch) {
285 					if (regmatch.rm_so == 0 &&
286 					    regmatch.rm_eo == strlen(mstr))
287 						selected[i] = 1;
288 				} else
289 					selected[i] = 1;
290 			} else if (rv != REG_NOMATCH) {
291 				regerror(rv, &reg, buf, sizeof(buf));
292 				errx(STATUS_ERROR, "regexec(): %s", buf);
293 			}
294 		}
295 
296 		regfree(&reg);
297 	}
298 
299 	for (i = 0, kp = plist; i < nproc; i++, kp++) {
300 		if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
301 			continue;
302 
303 		SLIST_FOREACH(li, &ruidlist, li_chain)
304 			if (kp->p_ruid == (uid_t)li->li_number)
305 				break;
306 		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
307 			selected[i] = 0;
308 			continue;
309 		}
310 
311 		SLIST_FOREACH(li, &rgidlist, li_chain)
312 			if (kp->p_rgid == (gid_t)li->li_number)
313 				break;
314 		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
315 			selected[i] = 0;
316 			continue;
317 		}
318 
319 		SLIST_FOREACH(li, &euidlist, li_chain)
320 			if (kp->p_uid == (uid_t)li->li_number)
321 				break;
322 		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
323 			selected[i] = 0;
324 			continue;
325 		}
326 
327 		SLIST_FOREACH(li, &ppidlist, li_chain)
328 			if (kp->p_ppid == (uid_t)li->li_number)
329 				break;
330 		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
331 			selected[i] = 0;
332 			continue;
333 		}
334 
335 		SLIST_FOREACH(li, &pgrplist, li_chain)
336 			if (kp->p__pgid == (uid_t)li->li_number)
337 				break;
338 		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
339 			selected[i] = 0;
340 			continue;
341 		}
342 
343 		SLIST_FOREACH(li, &tdevlist, li_chain) {
344 			if (li->li_number == -1 &&
345 			    (kp->p_flag & P_CONTROLT) == 0)
346 				break;
347 			if (kp->p_tdev == (uid_t)li->li_number)
348 				break;
349 		}
350 		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
351 			selected[i] = 0;
352 			continue;
353 		}
354 
355 		SLIST_FOREACH(li, &sidlist, li_chain)
356 			if (kp->p_sid == (uid_t)li->li_number)
357 				break;
358 		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
359 			selected[i] = 0;
360 			continue;
361 		}
362 
363 		if (argc == 0)
364 			selected[i] = 1;
365 	}
366 
367 	if (newest) {
368 		bestsec = 0;
369 		bestusec = 0;
370 		bestidx = -1;
371 
372 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
373 			if (!selected[i])
374 				continue;
375 
376 			if (kp->p_ustart_sec > bestsec ||
377 			    (kp->p_ustart_sec == bestsec
378 			    && kp->p_ustart_usec > bestusec)) {
379 				bestsec = kp->p_ustart_sec;
380 				bestusec = kp->p_ustart_usec;
381 				bestidx = i;
382 			}
383 		}
384 
385 		memset(selected, 0, nproc);
386 		if (bestidx != -1)
387 			selected[bestidx] = 1;
388 	}
389 
390 	/*
391 	 * Take the appropriate action for each matched process, if any.
392 	 */
393 	rv = STATUS_NOMATCH;
394 	for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) {
395 		if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
396 			continue;
397 		if (selected[i]) {
398 			if (inverse)
399 				continue;
400 		} else if (!inverse)
401 			continue;
402 
403 		if ((*action)(kp, j++) == -1)
404 			rv = STATUS_ERROR;
405 		else if (rv != STATUS_ERROR)
406 			rv = STATUS_MATCH;
407 	}
408 	if (pgrep && j)
409 		putchar('\n');
410 
411 	exit(rv);
412 }
413 
414 void
usage(void)415 usage(void)
416 {
417 	const char *ustr;
418 
419 	if (pgrep)
420 		ustr = "[-flnvx] [-d delim]";
421 	else
422 		ustr = "[-signal] [-fnvx]";
423 
424 	fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] "
425 	    "[-t tty]\n\t[-U uid] [-u euid] [pattern ...]\n", __progname, ustr);
426 
427 	exit(STATUS_ERROR);
428 }
429 
430 int
killact(struct kinfo_proc2 * kp,int dummy)431 killact(struct kinfo_proc2 *kp, int dummy)
432 {
433 
434 	if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) {
435 		warn("signalling pid %d", (int)kp->p_pid);
436 		return (-1);
437 	}
438 	return (0);
439 }
440 
441 int
grepact(struct kinfo_proc2 * kp,int printdelim)442 grepact(struct kinfo_proc2 *kp, int printdelim)
443 {
444 	char **argv;
445 
446 	if (printdelim)
447 		fputs(delim, stdout);
448 	if (longfmt && matchargs) {
449 		if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
450 			return (-1);
451 
452 		printf("%d ", (int)kp->p_pid);
453 		for (; *argv != NULL; argv++) {
454 			printf("%s", *argv);
455 			if (argv[1] != NULL)
456 				putchar(' ');
457 		}
458 	} else if (longfmt)
459 		printf("%d %s", (int)kp->p_pid, kp->p_comm);
460 	else
461 		printf("%d", (int)kp->p_pid);
462 
463 	return (0);
464 }
465 
466 void
makelist(struct listhead * head,enum listtype type,char * src)467 makelist(struct listhead *head, enum listtype type, char *src)
468 {
469 	struct list *li;
470 	struct passwd *pw;
471 	struct group *gr;
472 	struct stat st;
473 	char *sp, *p, buf[MAXPATHLEN];
474 	int empty;
475 
476 	empty = 1;
477 
478 	while ((sp = strsep(&src, ",")) != NULL) {
479 		if (*sp == '\0')
480 			usage();
481 
482 		if ((li = malloc(sizeof(*li))) == NULL)
483 			errx(STATUS_ERROR, "memory allocation failure");
484 		SLIST_INSERT_HEAD(head, li, li_chain);
485 		empty = 0;
486 
487 		li->li_number = (uid_t)strtol(sp, &p, 0);
488 		if (*p == '\0') {
489 			switch (type) {
490 			case LT_PGRP:
491 				if (li->li_number == 0)
492 					li->li_number = getpgrp();
493 				break;
494 			case LT_SID:
495 				if (li->li_number == 0)
496 					li->li_number = getsid(mypid);
497 				break;
498 			case LT_TTY:
499 				usage();
500 			default:
501 				break;
502 			}
503 			continue;
504 		}
505 
506 		switch (type) {
507 		case LT_USER:
508 			if ((pw = getpwnam(sp)) == NULL)
509 				errx(STATUS_BADUSAGE, "unknown user `%s'", sp);
510 			li->li_number = pw->pw_uid;
511 			break;
512 		case LT_GROUP:
513 			if ((gr = getgrnam(sp)) == NULL)
514 				errx(STATUS_BADUSAGE, "unknown group `%s'", sp);
515 			li->li_number = gr->gr_gid;
516 			break;
517 		case LT_TTY:
518 			if (strcmp(sp, "-") == 0) {
519 				li->li_number = -1;
520 				break;
521 			} else if (strcmp(sp, "co") == 0)
522 				p = "console";
523 			else if (strncmp(sp, "tty", 3) == 0)
524 				p = sp;
525 			else
526 				p = NULL;
527 
528 			if (p == NULL)
529 				snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
530 			else
531 				snprintf(buf, sizeof(buf), "/dev/%s", p);
532 
533 			if (stat(buf, &st) < 0) {
534 				if (errno == ENOENT)
535 					errx(STATUS_BADUSAGE,
536 					    "no such tty: `%s'", sp);
537 				err(STATUS_ERROR, "stat(%s)", sp);
538 			}
539 
540 			if ((st.st_mode & S_IFCHR) == 0)
541 				errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
542 
543 			li->li_number = st.st_rdev;
544 			break;
545 		default:
546 			usage();
547 		}
548 	}
549 
550 	if (empty)
551 		usage();
552 }
553