xref: /netbsd-src/usr.bin/pkill/pkill.c (revision 9d92ab4fc272b18d6e70508f516aa05f638eddd5)
1*9d92ab4fSroy /*	$NetBSD: pkill.c,v 1.34 2024/10/07 06:14:05 roy Exp $	*/
23b8a0d4fSad 
33b8a0d4fSad /*-
46b00e6e9Sad  * Copyright (c) 2002, 2022 The NetBSD Foundation, Inc.
53b8a0d4fSad  * All rights reserved.
63b8a0d4fSad  *
73b8a0d4fSad  * This code is derived from software contributed to The NetBSD Foundation
83b8a0d4fSad  * by Andrew Doran.
93b8a0d4fSad  *
103b8a0d4fSad  * Redistribution and use in source and binary forms, with or without
113b8a0d4fSad  * modification, are permitted provided that the following conditions
123b8a0d4fSad  * are met:
133b8a0d4fSad  * 1. Redistributions of source code must retain the above copyright
143b8a0d4fSad  *    notice, this list of conditions and the following disclaimer.
153b8a0d4fSad  * 2. Redistributions in binary form must reproduce the above copyright
163b8a0d4fSad  *    notice, this list of conditions and the following disclaimer in the
173b8a0d4fSad  *    documentation and/or other materials provided with the distribution.
183b8a0d4fSad  *
193b8a0d4fSad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
203b8a0d4fSad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
213b8a0d4fSad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
223b8a0d4fSad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
233b8a0d4fSad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
243b8a0d4fSad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
253b8a0d4fSad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
263b8a0d4fSad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
273b8a0d4fSad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
283b8a0d4fSad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
293b8a0d4fSad  * POSSIBILITY OF SUCH DAMAGE.
303b8a0d4fSad  */
313b8a0d4fSad 
323b8a0d4fSad #include <sys/cdefs.h>
3338b296bfSad #ifndef lint
34*9d92ab4fSroy __RCSID("$NetBSD: pkill.c,v 1.34 2024/10/07 06:14:05 roy Exp $");
3538b296bfSad #endif /* !lint */
363b8a0d4fSad 
373b8a0d4fSad #include <sys/types.h>
383b8a0d4fSad #include <sys/param.h>
393b8a0d4fSad #include <sys/sysctl.h>
403b8a0d4fSad #include <sys/proc.h>
413b8a0d4fSad #include <sys/queue.h>
42b374a568Sdsl #include <sys/resource.h>
433b8a0d4fSad #include <sys/stat.h>
443b8a0d4fSad 
453b8a0d4fSad #include <stdio.h>
463b8a0d4fSad #include <stdlib.h>
473b8a0d4fSad #include <limits.h>
483b8a0d4fSad #include <string.h>
493b8a0d4fSad #include <unistd.h>
503b8a0d4fSad #include <signal.h>
513b8a0d4fSad #include <regex.h>
523b8a0d4fSad #include <ctype.h>
533b8a0d4fSad #include <kvm.h>
543b8a0d4fSad #include <err.h>
55*9d92ab4fSroy #include <fcntl.h>
563b8a0d4fSad #include <pwd.h>
573b8a0d4fSad #include <grp.h>
583b8a0d4fSad #include <errno.h>
59140eaa6eSchristos #include <paths.h>
603b8a0d4fSad 
613b8a0d4fSad #define	STATUS_MATCH	0
623b8a0d4fSad #define	STATUS_NOMATCH	1
633b8a0d4fSad #define	STATUS_BADUSAGE	2
643b8a0d4fSad #define	STATUS_ERROR	3
653b8a0d4fSad 
66*9d92ab4fSroy #define	MIN_PID		5
67*9d92ab4fSroy #define	MAX_PID		30000 // XXX PID_MAX from sys/proc.h?
68*9d92ab4fSroy 
693b8a0d4fSad enum listtype {
703b8a0d4fSad 	LT_GENERIC,
713b8a0d4fSad 	LT_USER,
723b8a0d4fSad 	LT_GROUP,
733b8a0d4fSad 	LT_TTY,
743b8a0d4fSad 	LT_PGRP,
753b8a0d4fSad 	LT_SID
763b8a0d4fSad };
773b8a0d4fSad 
783b8a0d4fSad struct list {
793b8a0d4fSad 	SLIST_ENTRY(list) li_chain;
803b8a0d4fSad 	long	li_number;
813b8a0d4fSad };
823b8a0d4fSad 
833b8a0d4fSad SLIST_HEAD(listhead, list);
843b8a0d4fSad 
85140eaa6eSchristos static struct kinfo_proc2	*plist;
86140eaa6eSchristos static char	*selected;
87140eaa6eSchristos static const char *delim = "\n";
88140eaa6eSchristos static int	nproc;
89140eaa6eSchristos static int	pgrep;
9079ffd9c9Smrg static int	prenice;
91140eaa6eSchristos static int	signum = SIGTERM;
9279ffd9c9Smrg static int	nicenum;
93140eaa6eSchristos static int	newest;
9449ef36ceSsimonb static int	quiet;
95140eaa6eSchristos static int	inverse;
96140eaa6eSchristos static int	longfmt;
97140eaa6eSchristos static int	matchargs;
98140eaa6eSchristos static int	fullmatch;
99140eaa6eSchristos static int	cflags = REG_EXTENDED;
100140eaa6eSchristos static kvm_t	*kd;
101140eaa6eSchristos static pid_t	mypid;
1023b8a0d4fSad 
103140eaa6eSchristos static struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
104140eaa6eSchristos static struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
105140eaa6eSchristos static struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
106140eaa6eSchristos static struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
107140eaa6eSchristos static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
108140eaa6eSchristos static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
109140eaa6eSchristos static struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
1103b8a0d4fSad 
1118b0f9554Sperry static void	usage(void) __dead;
112de094613Sdsainty static int	killact(const struct kinfo_proc2 *);
11379ffd9c9Smrg static int	reniceact(const struct kinfo_proc2 *);
114de094613Sdsainty static int	grepact(const struct kinfo_proc2 *);
115140eaa6eSchristos static void	makelist(struct listhead *, enum listtype, char *);
116*9d92ab4fSroy static int	takepid(const char *, int);
1173b8a0d4fSad 
1183b8a0d4fSad int
1193b8a0d4fSad main(int argc, char **argv)
1203b8a0d4fSad {
121*9d92ab4fSroy   	char buf[_POSIX2_LINE_MAX], **pargv, *q, *pidfile = NULL;
122*9d92ab4fSroy 	int i, j, ch, bestidx, rv, criteria, pidfromfile, pidfilelock = 0;
123de094613Sdsainty 	int (*action)(const struct kinfo_proc2 *);
124de094613Sdsainty 	const struct kinfo_proc2 *kp;
1253b8a0d4fSad 	struct list *li;
1261524d388Schristos 	const char *p;
1273b8a0d4fSad 	u_int32_t bestsec, bestusec;
1283b8a0d4fSad 	regex_t reg;
1293b8a0d4fSad 	regmatch_t regmatch;
1303b8a0d4fSad 
131140eaa6eSchristos 	setprogname(argv[0]);
132140eaa6eSchristos 
1333b8a0d4fSad 	if (strcmp(getprogname(), "pgrep") == 0) {
1343b8a0d4fSad 		action = grepact;
1353b8a0d4fSad 		pgrep = 1;
13679ffd9c9Smrg 	} else if (strcmp(getprogname(), "prenice") == 0) {
1378707c3a3Sprlw1 		action = reniceact;
13879ffd9c9Smrg 		prenice = 1;
1393b8a0d4fSad 	} else {
1403b8a0d4fSad 		action = killact;
1413b8a0d4fSad 		p = argv[1];
1423b8a0d4fSad 
143ee19c521Saugustss 		if (argc > 1 && p[0] == '-') {
1443b8a0d4fSad 			p++;
145ee19c521Saugustss 			i = (int)strtol(p, &q, 10);
146ee19c521Saugustss 			if (*q == '\0') {
1473b8a0d4fSad 				signum = i;
1483b8a0d4fSad 				argv++;
149ee19c521Saugustss 				argc--;
1503b8a0d4fSad 			} else {
1513b8a0d4fSad 				if (strncasecmp(p, "sig", 3) == 0)
1523b8a0d4fSad 					p += 3;
1533b8a0d4fSad 				for (i = 1; i < NSIG; i++)
1543b8a0d4fSad 					if (strcasecmp(sys_signame[i], p) == 0)
1553b8a0d4fSad 						break;
1563b8a0d4fSad 				if (i != NSIG) {
1573b8a0d4fSad 					signum = i;
1583b8a0d4fSad 					argv++;
159ee19c521Saugustss 					argc--;
1603b8a0d4fSad 				}
1613b8a0d4fSad 			}
1623b8a0d4fSad 		}
1633b8a0d4fSad 	}
1643b8a0d4fSad 
1653b8a0d4fSad 	criteria = 0;
1663b8a0d4fSad 
167f2e89618Smrg 	if (prenice) {
168f2e89618Smrg 		if (argc < 2)
169f2e89618Smrg 			usage();
170f2e89618Smrg 
171f2e89618Smrg 		if (strcmp(argv[1], "-l") == 0) {
172f2e89618Smrg 			longfmt = 1;
173f2e89618Smrg 			argv++;
174f2e89618Smrg 			argc--;
175f2e89618Smrg 		}
176f2e89618Smrg 
177f2e89618Smrg 		if (argc < 2)
178f2e89618Smrg 			usage();
179f2e89618Smrg 
180f2e89618Smrg 		p = argv[1];
181f2e89618Smrg 
182f2e89618Smrg 		i = (int)strtol(p, &q, 10);
183f2e89618Smrg 		if (*q == '\0') {
184f2e89618Smrg 			nicenum = i;
185f2e89618Smrg 			argv++;
186f2e89618Smrg 			argc--;
187f2e89618Smrg 		} else
188f2e89618Smrg 			usage();
189f2e89618Smrg 	} else {
190*9d92ab4fSroy 		while ((ch = getopt(argc, argv, "F:G:LP:U:d:fg:ilnqs:t:u:vx")) != -1)
1913b8a0d4fSad 			switch (ch) {
192*9d92ab4fSroy 			case 'F':
193*9d92ab4fSroy 				pidfile = optarg;
194*9d92ab4fSroy 				criteria = 1;
195*9d92ab4fSroy 				break;
1963b8a0d4fSad 			case 'G':
1973b8a0d4fSad 				makelist(&rgidlist, LT_GROUP, optarg);
1983b8a0d4fSad 				criteria = 1;
1993b8a0d4fSad 				break;
200*9d92ab4fSroy 			case 'L':
201*9d92ab4fSroy 				pidfilelock = 1;
202*9d92ab4fSroy 				break;
2033b8a0d4fSad 			case 'P':
2043b8a0d4fSad 				makelist(&ppidlist, LT_GENERIC, optarg);
2053b8a0d4fSad 				criteria = 1;
2063b8a0d4fSad 				break;
2073b8a0d4fSad 			case 'U':
2083b8a0d4fSad 				makelist(&ruidlist, LT_USER, optarg);
2093b8a0d4fSad 				criteria = 1;
2103b8a0d4fSad 				break;
2113b8a0d4fSad 			case 'd':
2123b8a0d4fSad 				if (!pgrep)
2133b8a0d4fSad 					usage();
2143b8a0d4fSad 				delim = optarg;
2153b8a0d4fSad 				break;
2163b8a0d4fSad 			case 'f':
2173b8a0d4fSad 				matchargs = 1;
2183b8a0d4fSad 				break;
2193b8a0d4fSad 			case 'g':
2203b8a0d4fSad 				makelist(&pgrplist, LT_PGRP, optarg);
2213b8a0d4fSad 				criteria = 1;
2223b8a0d4fSad 				break;
2238e587b4cSsketch 			case 'i':
2248e587b4cSsketch 				cflags |= REG_ICASE;
2258e587b4cSsketch 				break;
2263b8a0d4fSad 			case 'l':
2273b8a0d4fSad 				longfmt = 1;
2283b8a0d4fSad 				break;
2293b8a0d4fSad 			case 'n':
2303b8a0d4fSad 				newest = 1;
2313b8a0d4fSad 				criteria = 1;
2323b8a0d4fSad 				break;
23349ef36ceSsimonb 			case 'q':
23449ef36ceSsimonb 				if (!pgrep)
23549ef36ceSsimonb 					usage();
23649ef36ceSsimonb 				quiet = 1;
23749ef36ceSsimonb 				break;
2383b8a0d4fSad 			case 's':
2393b8a0d4fSad 				makelist(&sidlist, LT_SID, optarg);
2403b8a0d4fSad 				criteria = 1;
2413b8a0d4fSad 				break;
2423b8a0d4fSad 			case 't':
2433b8a0d4fSad 				makelist(&tdevlist, LT_TTY, optarg);
2443b8a0d4fSad 				criteria = 1;
2453b8a0d4fSad 				break;
2463b8a0d4fSad 			case 'u':
2473b8a0d4fSad 				makelist(&euidlist, LT_USER, optarg);
2483b8a0d4fSad 				criteria = 1;
2493b8a0d4fSad 				break;
2503b8a0d4fSad 			case 'v':
2513b8a0d4fSad 				inverse = 1;
2523b8a0d4fSad 				break;
2533b8a0d4fSad 			case 'x':
2543b8a0d4fSad 				fullmatch = 1;
2553b8a0d4fSad 				break;
2563b8a0d4fSad 			default:
2573b8a0d4fSad 				usage();
2583b8a0d4fSad 				/* NOTREACHED */
2593b8a0d4fSad 			}
2603b8a0d4fSad 		argc -= optind;
2613b8a0d4fSad 		argv += optind;
262f2e89618Smrg 	}
263f2e89618Smrg 
2643b8a0d4fSad 	if (argc != 0)
2653b8a0d4fSad 		criteria = 1;
2663b8a0d4fSad 	if (!criteria)
2673b8a0d4fSad 		usage();
268*9d92ab4fSroy 	if (pidfile != NULL)
269*9d92ab4fSroy 		pidfromfile = takepid(pidfile, pidfilelock);
270*9d92ab4fSroy 	else {
271*9d92ab4fSroy 		if (pidfilelock) {
272*9d92ab4fSroy 			errx(STATUS_ERROR,
273*9d92ab4fSroy 			    "Option -L doesn't make sense without -F");
274*9d92ab4fSroy 		}
275*9d92ab4fSroy 		pidfromfile = -1;
276*9d92ab4fSroy 	}
2773b8a0d4fSad 
2783b8a0d4fSad 	mypid = getpid();
2793b8a0d4fSad 
2803b8a0d4fSad 	/*
2813b8a0d4fSad 	 * Retrieve the list of running processes from the kernel.
2823b8a0d4fSad 	 */
2833b8a0d4fSad 	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
2843b8a0d4fSad 	if (kd == NULL)
285140eaa6eSchristos 		errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf);
2863b8a0d4fSad 
2873b8a0d4fSad 	plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
2883b8a0d4fSad 	if (plist == NULL)
289140eaa6eSchristos 		errx(STATUS_ERROR, "Cannot get process list (%s)",
290140eaa6eSchristos 		    kvm_geterr(kd));
2913b8a0d4fSad 
2923b8a0d4fSad 	/*
2933b8a0d4fSad 	 * Allocate memory which will be used to keep track of the
2943b8a0d4fSad 	 * selection.
2953b8a0d4fSad 	 */
2966b00e6e9Sad 	if ((selected = calloc(sizeof(*selected), (size_t)nproc)) == NULL)
297140eaa6eSchristos 		err(STATUS_ERROR, "Cannot allocate memory for %d processes",
298140eaa6eSchristos 		    nproc);
2993b8a0d4fSad 
3003b8a0d4fSad 	/*
3013b8a0d4fSad 	 * Refine the selection.
3023b8a0d4fSad 	 */
3033b8a0d4fSad 	for (; *argv != NULL; argv++) {
3048e587b4cSsketch 		if ((rv = regcomp(&reg, *argv, cflags)) != 0) {
30541d3ce98Schristos 			(void)regerror(rv, &reg, buf, sizeof(buf));
306140eaa6eSchristos 			errx(STATUS_BADUSAGE,
307140eaa6eSchristos 			    "Cannot compile regular expression `%s' (%s)",
308140eaa6eSchristos 			    *argv, buf);
3093b8a0d4fSad 		}
3103b8a0d4fSad 
3113b8a0d4fSad 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
312f5e7ca24Spavel 			if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
3133b8a0d4fSad 				continue;
3143b8a0d4fSad 
3153b8a0d4fSad 			if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
3163b8a0d4fSad 				continue;
3171524d388Schristos 			if (matchargs) {
3183b8a0d4fSad 
3193b8a0d4fSad 				j = 0;
320ba2ed01cSlukem 				while (j < (int)sizeof(buf) && *pargv != NULL) {
3213b8a0d4fSad 					j += snprintf(buf + j, sizeof(buf) - j,
3223b8a0d4fSad 					    pargv[1] != NULL ? "%s " : "%s",
3233b8a0d4fSad 					    pargv[0]);
3243b8a0d4fSad 					pargv++;
3253b8a0d4fSad 				}
326d7b1a047Skre 			} else if (pargv[0] != NULL)
3271524d388Schristos 				strlcpy(buf, pargv[0], sizeof(buf));
328d7b1a047Skre 			else
329d7b1a047Skre 				strlcpy(buf, kp->p_comm, sizeof(buf));
3303b8a0d4fSad 
3311524d388Schristos 			rv = regexec(&reg, buf, 1, &regmatch, 0);
3323b8a0d4fSad 			if (rv == 0) {
3333b8a0d4fSad 				if (fullmatch) {
3343b8a0d4fSad 					if (regmatch.rm_so == 0 &&
3351524d388Schristos 					    regmatch.rm_eo ==
3361524d388Schristos 					    (regoff_t)strlen(buf))
3373b8a0d4fSad 						selected[i] = 1;
3383b8a0d4fSad 				} else
3393b8a0d4fSad 					selected[i] = 1;
3403b8a0d4fSad 			} else if (rv != REG_NOMATCH) {
34141d3ce98Schristos 				(void)regerror(rv, &reg, buf, sizeof(buf));
342140eaa6eSchristos 				errx(STATUS_ERROR,
343140eaa6eSchristos 				    "Regular expression evaluation error (%s)",
344140eaa6eSchristos 				    buf);
3453b8a0d4fSad 			}
3463b8a0d4fSad 		}
3473b8a0d4fSad 
3483b8a0d4fSad 		regfree(&reg);
3493b8a0d4fSad 	}
3503b8a0d4fSad 
3513b8a0d4fSad 	for (i = 0, kp = plist; i < nproc; i++, kp++) {
352f5e7ca24Spavel 		if ((kp->p_flag & P_SYSTEM) != 0)
3533b8a0d4fSad 			continue;
3543b8a0d4fSad 
355*9d92ab4fSroy 		if (pidfromfile >= 0 && kp->p_pid != pidfromfile) {
356*9d92ab4fSroy 			selected[i] = 0;
357*9d92ab4fSroy 			continue;
358*9d92ab4fSroy 		}
359*9d92ab4fSroy 
3603b8a0d4fSad 		SLIST_FOREACH(li, &ruidlist, li_chain)
3613b8a0d4fSad 			if (kp->p_ruid == (uid_t)li->li_number)
3623b8a0d4fSad 				break;
3633b8a0d4fSad 		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
3643b8a0d4fSad 			selected[i] = 0;
3653b8a0d4fSad 			continue;
3663b8a0d4fSad 		}
3673b8a0d4fSad 
3683b8a0d4fSad 		SLIST_FOREACH(li, &rgidlist, li_chain)
3693b8a0d4fSad 			if (kp->p_rgid == (gid_t)li->li_number)
3703b8a0d4fSad 				break;
3713b8a0d4fSad 		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
3723b8a0d4fSad 			selected[i] = 0;
3733b8a0d4fSad 			continue;
3743b8a0d4fSad 		}
3753b8a0d4fSad 
3763b8a0d4fSad 		SLIST_FOREACH(li, &euidlist, li_chain)
3773b8a0d4fSad 			if (kp->p_uid == (uid_t)li->li_number)
3783b8a0d4fSad 				break;
3793b8a0d4fSad 		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
3803b8a0d4fSad 			selected[i] = 0;
3813b8a0d4fSad 			continue;
3823b8a0d4fSad 		}
3833b8a0d4fSad 
3843b8a0d4fSad 		SLIST_FOREACH(li, &ppidlist, li_chain)
385ba2ed01cSlukem 			if ((uid_t)kp->p_ppid == (uid_t)li->li_number)
3863b8a0d4fSad 				break;
3873b8a0d4fSad 		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
3883b8a0d4fSad 			selected[i] = 0;
3893b8a0d4fSad 			continue;
3903b8a0d4fSad 		}
3913b8a0d4fSad 
3923b8a0d4fSad 		SLIST_FOREACH(li, &pgrplist, li_chain)
393ba2ed01cSlukem 			if (kp->p__pgid == (pid_t)li->li_number)
3943b8a0d4fSad 				break;
3953b8a0d4fSad 		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
3963b8a0d4fSad 			selected[i] = 0;
3973b8a0d4fSad 			continue;
3983b8a0d4fSad 		}
3993b8a0d4fSad 
4003b8a0d4fSad 		SLIST_FOREACH(li, &tdevlist, li_chain) {
4013b8a0d4fSad 			if (li->li_number == -1 &&
402f5e7ca24Spavel 			    (kp->p_flag & P_CONTROLT) == 0)
4033b8a0d4fSad 				break;
4043b8a0d4fSad 			if (kp->p_tdev == (uid_t)li->li_number)
4053b8a0d4fSad 				break;
4063b8a0d4fSad 		}
4073b8a0d4fSad 		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
4083b8a0d4fSad 			selected[i] = 0;
4093b8a0d4fSad 			continue;
4103b8a0d4fSad 		}
4113b8a0d4fSad 
4123b8a0d4fSad 		SLIST_FOREACH(li, &sidlist, li_chain)
413ba2ed01cSlukem 			if (kp->p_sid == (pid_t)li->li_number)
4143b8a0d4fSad 				break;
4153b8a0d4fSad 		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
4163b8a0d4fSad 			selected[i] = 0;
4173b8a0d4fSad 			continue;
4183b8a0d4fSad 		}
4193b8a0d4fSad 
4203b8a0d4fSad 		if (argc == 0)
4213b8a0d4fSad 			selected[i] = 1;
4223b8a0d4fSad 	}
4233b8a0d4fSad 
4243b8a0d4fSad 	if (newest) {
4253b8a0d4fSad 		bestsec = 0;
4263b8a0d4fSad 		bestusec = 0;
4273b8a0d4fSad 		bestidx = -1;
4283b8a0d4fSad 
4293b8a0d4fSad 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
4303b8a0d4fSad 			if (!selected[i])
4313b8a0d4fSad 				continue;
4323b8a0d4fSad 
4333b8a0d4fSad 			if (kp->p_ustart_sec > bestsec ||
4343b8a0d4fSad 			    (kp->p_ustart_sec == bestsec
4353b8a0d4fSad 			    && kp->p_ustart_usec > bestusec)) {
4363b8a0d4fSad 			    	bestsec = kp->p_ustart_sec;
4373b8a0d4fSad 			    	bestusec = kp->p_ustart_usec;
4383b8a0d4fSad 				bestidx = i;
4393b8a0d4fSad 			}
4403b8a0d4fSad 		}
4413b8a0d4fSad 
44241d3ce98Schristos 		(void)memset(selected, 0, (size_t)nproc);
4433b8a0d4fSad 		if (bestidx != -1)
4443b8a0d4fSad 			selected[bestidx] = 1;
4453b8a0d4fSad 	}
4463b8a0d4fSad 
4473b8a0d4fSad 	/*
4483b8a0d4fSad 	 * Take the appropriate action for each matched process, if any.
4493b8a0d4fSad 	 */
4503b8a0d4fSad 	for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) {
4513b8a0d4fSad 		if (kp->p_pid == mypid)
4523b8a0d4fSad 			continue;
4533b8a0d4fSad 		if (selected[i]) {
4543b8a0d4fSad 			if (inverse)
4553b8a0d4fSad 				continue;
4563b8a0d4fSad 		} else if (!inverse)
4573b8a0d4fSad 			continue;
4583b8a0d4fSad 
459f5e7ca24Spavel 		if ((kp->p_flag & P_SYSTEM) != 0)
460eabe280bSad 			continue;
461eabe280bSad 
462078c5c1aSchristos 		rv |= (*action)(kp);
4633b8a0d4fSad 	}
4643b8a0d4fSad 
46541d3ce98Schristos 	return rv ? STATUS_MATCH : STATUS_NOMATCH;
4663b8a0d4fSad }
4673b8a0d4fSad 
468140eaa6eSchristos static void
4693b8a0d4fSad usage(void)
4703b8a0d4fSad {
4713b8a0d4fSad 	const char *ustr;
4723b8a0d4fSad 
47379ffd9c9Smrg 	if (prenice)
474f2e89618Smrg 		fprintf(stderr, "Usage: %s [-l] priority pattern ...\n",
47579ffd9c9Smrg 		    getprogname());
47679ffd9c9Smrg 	else {
4773b8a0d4fSad 		if (pgrep)
478*9d92ab4fSroy 			ustr = "[-Lfilnqvx] [-d delim]";
4793b8a0d4fSad 		else
480*9d92ab4fSroy 			ustr = "[-signal] [-Lfilnvx]";
4813b8a0d4fSad 
482140eaa6eSchristos 		(void)fprintf(stderr,
483*9d92ab4fSroy 		    "Usage: %s %s [-F pidfile] [-G gid] [-g pgrp] [-P ppid] [-s sid] "
48479ffd9c9Smrg 			   "[-t tty]\n"
48579ffd9c9Smrg 		    "             [-U uid] [-u euid] pattern ...\n",
48679ffd9c9Smrg 			      getprogname(), ustr);
48779ffd9c9Smrg 	}
4883b8a0d4fSad 
48970316522Skleink 	exit(STATUS_BADUSAGE);
4903b8a0d4fSad }
4913b8a0d4fSad 
492140eaa6eSchristos static int
493de094613Sdsainty killact(const struct kinfo_proc2 *kp)
4943b8a0d4fSad {
495de96a26eSerh 	if (longfmt)
496de96a26eSerh 		grepact(kp);
497078c5c1aSchristos 	if (kill(kp->p_pid, signum) == -1) {
4980de40530Sdsainty 
499078c5c1aSchristos 		/*
5000de40530Sdsainty 		 * Check for ESRCH, which indicates that the process
5010de40530Sdsainty 		 * disappeared between us matching it and us
502a050a273Skleink 		 * signalling it; don't issue a warning about it.
503078c5c1aSchristos 		 */
504a050a273Skleink 		if (errno != ESRCH)
505a050a273Skleink 			warn("signalling pid %d", (int)kp->p_pid);
5063b8a0d4fSad 
507a050a273Skleink 		/*
508a050a273Skleink 		 * Return 0 to indicate that the process should not be
509a050a273Skleink 		 * considered a match, since we didn't actually get to
510a050a273Skleink 		 * signal it.
511a050a273Skleink 		 */
512a050a273Skleink 		return 0;
5133b8a0d4fSad 	}
5143b8a0d4fSad 
515078c5c1aSchristos 	return 1;
516078c5c1aSchristos }
517078c5c1aSchristos 
518140eaa6eSchristos static int
51979ffd9c9Smrg reniceact(const struct kinfo_proc2 *kp)
52079ffd9c9Smrg {
52179ffd9c9Smrg 	int oldprio;
52279ffd9c9Smrg 
52379ffd9c9Smrg 	if (longfmt)
52479ffd9c9Smrg 		grepact(kp);
52579ffd9c9Smrg 
52679ffd9c9Smrg 	errno = 0;
52779ffd9c9Smrg 	if ((oldprio = getpriority(PRIO_PROCESS, kp->p_pid)) == -1 &&
52879ffd9c9Smrg 	    errno != 0) {
52979ffd9c9Smrg 		warn("%d: getpriority", kp->p_pid);
53079ffd9c9Smrg 		return 0;
53179ffd9c9Smrg 	}
53279ffd9c9Smrg 
53379ffd9c9Smrg 	if (setpriority(PRIO_PROCESS, kp->p_pid, nicenum) == -1) {
53479ffd9c9Smrg 		warn("%d: setpriority", kp->p_pid);
53579ffd9c9Smrg 		return 0;
53679ffd9c9Smrg 	}
53779ffd9c9Smrg 
53879ffd9c9Smrg 	(void)printf("%d: old priority %d, new priority %d\n",
53979ffd9c9Smrg 	    kp->p_pid, oldprio, nicenum);
54079ffd9c9Smrg 
54179ffd9c9Smrg 	return 1;
54279ffd9c9Smrg }
54379ffd9c9Smrg 
54479ffd9c9Smrg static int
545de094613Sdsainty grepact(const struct kinfo_proc2 *kp)
5463b8a0d4fSad {
5473b8a0d4fSad 	char **argv;
5483b8a0d4fSad 
54949ef36ceSsimonb 	if (quiet)
55049ef36ceSsimonb 		return 1;
55149ef36ceSsimonb 
5523b8a0d4fSad 	if (longfmt && matchargs) {
5530de40530Sdsainty 
554078c5c1aSchristos 		/*
5550de40530Sdsainty 		 * If kvm_getargv2() failed the process has probably
5560de40530Sdsainty 		 * disappeared.  Return 0 to indicate that the process
5570de40530Sdsainty 		 * should not be considered a match, since we are no
5580de40530Sdsainty 		 * longer in a position to output it as a match.
559078c5c1aSchristos 		 */
5600de40530Sdsainty 		if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
561078c5c1aSchristos 			return 0;
5623b8a0d4fSad 
563140eaa6eSchristos 		(void)printf("%d ", (int)kp->p_pid);
5643b8a0d4fSad 		for (; *argv != NULL; argv++) {
565140eaa6eSchristos 			(void)printf("%s", *argv);
5663b8a0d4fSad 			if (argv[1] != NULL)
567140eaa6eSchristos 				(void)putchar(' ');
5683b8a0d4fSad 		}
5693b8a0d4fSad 	} else if (longfmt)
570140eaa6eSchristos 		(void)printf("%d %s", (int)kp->p_pid, kp->p_comm);
5713b8a0d4fSad 	else
572140eaa6eSchristos 		(void)printf("%d", (int)kp->p_pid);
5733b8a0d4fSad 
574140eaa6eSchristos 	(void)printf("%s", delim);
575078c5c1aSchristos 
576078c5c1aSchristos 	return 1;
5773b8a0d4fSad }
5783b8a0d4fSad 
579140eaa6eSchristos static void
5803b8a0d4fSad makelist(struct listhead *head, enum listtype type, char *src)
5813b8a0d4fSad {
5823b8a0d4fSad 	struct list *li;
5833b8a0d4fSad 	struct passwd *pw;
5843b8a0d4fSad 	struct group *gr;
5853b8a0d4fSad 	struct stat st;
586140eaa6eSchristos 	char *sp, *ep, buf[MAXPATHLEN];
587140eaa6eSchristos 	const char *p;
5883b8a0d4fSad 	int empty;
589140eaa6eSchristos 	const char *prefix = _PATH_DEV;
5903b8a0d4fSad 
5913b8a0d4fSad 	empty = 1;
5923b8a0d4fSad 
5933b8a0d4fSad 	while ((sp = strsep(&src, ",")) != NULL) {
5943b8a0d4fSad 		if (*sp == '\0')
5953b8a0d4fSad 			usage();
5963b8a0d4fSad 
5973b8a0d4fSad 		if ((li = malloc(sizeof(*li))) == NULL)
5981524d388Schristos 			err(STATUS_ERROR, "Cannot allocate %zu bytes",
599140eaa6eSchristos 			    sizeof(*li));
6003b8a0d4fSad 		SLIST_INSERT_HEAD(head, li, li_chain);
6013b8a0d4fSad 		empty = 0;
6023b8a0d4fSad 
603140eaa6eSchristos 		li->li_number = (uid_t)strtol(sp, &ep, 0);
604b90f233bSchristos 		if (*ep == '\0' && type != LT_TTY) {
6053b8a0d4fSad 			switch (type) {
6063b8a0d4fSad 			case LT_PGRP:
6073b8a0d4fSad 				if (li->li_number == 0)
6083b8a0d4fSad 					li->li_number = getpgrp();
6093b8a0d4fSad 				break;
6103b8a0d4fSad 			case LT_SID:
6113b8a0d4fSad 				if (li->li_number == 0)
6123b8a0d4fSad 					li->li_number = getsid(mypid);
6133b8a0d4fSad 				break;
6143b8a0d4fSad 			default:
6153b8a0d4fSad 				break;
6163b8a0d4fSad 			}
6173b8a0d4fSad 			continue;
6183b8a0d4fSad 		}
6193b8a0d4fSad 
6203b8a0d4fSad 		switch (type) {
6213b8a0d4fSad 		case LT_USER:
6223b8a0d4fSad 			if ((pw = getpwnam(sp)) == NULL)
623140eaa6eSchristos 				errx(STATUS_BADUSAGE, "Unknown user `%s'",
62424cba1acSabs 				    sp);
6253b8a0d4fSad 			li->li_number = pw->pw_uid;
6263b8a0d4fSad 			break;
6273b8a0d4fSad 		case LT_GROUP:
6283b8a0d4fSad 			if ((gr = getgrnam(sp)) == NULL)
629140eaa6eSchristos 				errx(STATUS_BADUSAGE, "Unknown group `%s'",
63024cba1acSabs 				    sp);
6313b8a0d4fSad 			li->li_number = gr->gr_gid;
6323b8a0d4fSad 			break;
6333b8a0d4fSad 		case LT_TTY:
634b90f233bSchristos 			p = sp;
635b90f233bSchristos 			if (*sp == '/')
636b90f233bSchristos 				prefix = "";
637b90f233bSchristos 			else if (strcmp(sp, "-") == 0) {
6383b8a0d4fSad 				li->li_number = -1;
6393b8a0d4fSad 				break;
6403b8a0d4fSad 			} else if (strcmp(sp, "co") == 0)
6413b8a0d4fSad 				p = "console";
6423b8a0d4fSad 			else if (strncmp(sp, "tty", 3) == 0)
643b90f233bSchristos 				/* all set */;
644b90f233bSchristos 			else if (strncmp(sp, "pts/", 4) == 0)
645b90f233bSchristos 				/* all set */;
646b90f233bSchristos 			else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0'))
647140eaa6eSchristos 				prefix = _PATH_TTY;
648b90f233bSchristos 			else
649b90f233bSchristos 				prefix = _PATH_DEV_PTS;
6503b8a0d4fSad 
651140eaa6eSchristos 			(void)snprintf(buf, sizeof(buf), "%s%s", prefix, p);
6523b8a0d4fSad 
653140eaa6eSchristos 			if (stat(buf, &st) == -1) {
6543b8a0d4fSad 				if (errno == ENOENT)
6553b8a0d4fSad 					errx(STATUS_BADUSAGE,
656b90f233bSchristos 					    "No such tty: `%s'", buf);
657b90f233bSchristos 				err(STATUS_ERROR, "Cannot access `%s'", buf);
6583b8a0d4fSad 			}
6593b8a0d4fSad 
6603b8a0d4fSad 			if ((st.st_mode & S_IFCHR) == 0)
661b90f233bSchristos 				errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf);
6623b8a0d4fSad 
6633b8a0d4fSad 			li->li_number = st.st_rdev;
6643b8a0d4fSad 			break;
6653b8a0d4fSad 		default:
6663b8a0d4fSad 			usage();
667140eaa6eSchristos 		}
6683b8a0d4fSad 	}
6693b8a0d4fSad 
6703b8a0d4fSad 	if (empty)
6713b8a0d4fSad 		usage();
6723b8a0d4fSad }
673*9d92ab4fSroy 
674*9d92ab4fSroy static int
675*9d92ab4fSroy takepid(const char *pidfile, int pidfilelock)
676*9d92ab4fSroy {
677*9d92ab4fSroy 	char *endp, line[BUFSIZ];
678*9d92ab4fSroy 	FILE *fh;
679*9d92ab4fSroy 	long rval;
680*9d92ab4fSroy 
681*9d92ab4fSroy 	fh = fopen(pidfile, "r");
682*9d92ab4fSroy 	if (fh == NULL)
683*9d92ab4fSroy 		err(STATUS_ERROR, "Cannot open pidfile `%s'", pidfile);
684*9d92ab4fSroy 
685*9d92ab4fSroy 	if (pidfilelock) {
686*9d92ab4fSroy 		/*
687*9d92ab4fSroy 		 * If we can lock pidfile, this means that daemon is not
688*9d92ab4fSroy 		 * running, so would be better not to kill some random process.
689*9d92ab4fSroy 		 */
690*9d92ab4fSroy 		if (flock(fileno(fh), LOCK_EX | LOCK_NB) == 0) {
691*9d92ab4fSroy 			(void)fclose(fh);
692*9d92ab4fSroy 			errx(STATUS_ERROR, "File '%s' can be locked", pidfile);
693*9d92ab4fSroy 		} else {
694*9d92ab4fSroy 			if (errno != EWOULDBLOCK) {
695*9d92ab4fSroy 				errx(STATUS_ERROR,
696*9d92ab4fSroy 				    "Error while locking file '%s'", pidfile);
697*9d92ab4fSroy 			}
698*9d92ab4fSroy 		}
699*9d92ab4fSroy 	}
700*9d92ab4fSroy 
701*9d92ab4fSroy 	if (fgets(line, sizeof(line), fh) == NULL) {
702*9d92ab4fSroy 		if (feof(fh)) {
703*9d92ab4fSroy 			(void)fclose(fh);
704*9d92ab4fSroy 			errx(STATUS_ERROR, "Pidfile `%s' is empty", pidfile);
705*9d92ab4fSroy 		}
706*9d92ab4fSroy 		(void)fclose(fh);
707*9d92ab4fSroy 		err(STATUS_ERROR, "Cannot read from pid file `%s'", pidfile);
708*9d92ab4fSroy 	}
709*9d92ab4fSroy 	(void)fclose(fh);
710*9d92ab4fSroy 
711*9d92ab4fSroy 	rval = strtol(line, &endp, 10);
712*9d92ab4fSroy 	if (*endp != '\0' && !isspace((unsigned char)*endp))
713*9d92ab4fSroy 		errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile);
714*9d92ab4fSroy 	else if (rval < MIN_PID || rval > MAX_PID)
715*9d92ab4fSroy 		errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile);
716*9d92ab4fSroy 	return (rval);
717*9d92ab4fSroy }
718