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