xref: /netbsd-src/usr.bin/pkill/pkill.c (revision 9d92ab4fc272b18d6e70508f516aa05f638eddd5)
1 /*	$NetBSD: pkill.c,v 1.34 2024/10/07 06:14:05 roy Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002, 2022 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: pkill.c,v 1.34 2024/10/07 06:14:05 roy Exp $");
35 #endif /* !lint */
36 
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysctl.h>
40 #include <sys/proc.h>
41 #include <sys/queue.h>
42 #include <sys/resource.h>
43 #include <sys/stat.h>
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <limits.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <signal.h>
51 #include <regex.h>
52 #include <ctype.h>
53 #include <kvm.h>
54 #include <err.h>
55 #include <fcntl.h>
56 #include <pwd.h>
57 #include <grp.h>
58 #include <errno.h>
59 #include <paths.h>
60 
61 #define	STATUS_MATCH	0
62 #define	STATUS_NOMATCH	1
63 #define	STATUS_BADUSAGE	2
64 #define	STATUS_ERROR	3
65 
66 #define	MIN_PID		5
67 #define	MAX_PID		30000 // XXX PID_MAX from sys/proc.h?
68 
69 enum listtype {
70 	LT_GENERIC,
71 	LT_USER,
72 	LT_GROUP,
73 	LT_TTY,
74 	LT_PGRP,
75 	LT_SID
76 };
77 
78 struct list {
79 	SLIST_ENTRY(list) li_chain;
80 	long	li_number;
81 };
82 
83 SLIST_HEAD(listhead, list);
84 
85 static struct kinfo_proc2	*plist;
86 static char	*selected;
87 static const char *delim = "\n";
88 static int	nproc;
89 static int	pgrep;
90 static int	prenice;
91 static int	signum = SIGTERM;
92 static int	nicenum;
93 static int	newest;
94 static int	quiet;
95 static int	inverse;
96 static int	longfmt;
97 static int	matchargs;
98 static int	fullmatch;
99 static int	cflags = REG_EXTENDED;
100 static kvm_t	*kd;
101 static pid_t	mypid;
102 
103 static struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
104 static struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
105 static struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
106 static struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
107 static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
108 static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
109 static struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
110 
111 static void	usage(void) __dead;
112 static int	killact(const struct kinfo_proc2 *);
113 static int	reniceact(const struct kinfo_proc2 *);
114 static int	grepact(const struct kinfo_proc2 *);
115 static void	makelist(struct listhead *, enum listtype, char *);
116 static int	takepid(const char *, int);
117 
118 int
119 main(int argc, char **argv)
120 {
121   	char buf[_POSIX2_LINE_MAX], **pargv, *q, *pidfile = NULL;
122 	int i, j, ch, bestidx, rv, criteria, pidfromfile, pidfilelock = 0;
123 	int (*action)(const struct kinfo_proc2 *);
124 	const struct kinfo_proc2 *kp;
125 	struct list *li;
126 	const char *p;
127 	u_int32_t bestsec, bestusec;
128 	regex_t reg;
129 	regmatch_t regmatch;
130 
131 	setprogname(argv[0]);
132 
133 	if (strcmp(getprogname(), "pgrep") == 0) {
134 		action = grepact;
135 		pgrep = 1;
136 	} else if (strcmp(getprogname(), "prenice") == 0) {
137 		action = reniceact;
138 		prenice = 1;
139 	} else {
140 		action = killact;
141 		p = argv[1];
142 
143 		if (argc > 1 && p[0] == '-') {
144 			p++;
145 			i = (int)strtol(p, &q, 10);
146 			if (*q == '\0') {
147 				signum = i;
148 				argv++;
149 				argc--;
150 			} else {
151 				if (strncasecmp(p, "sig", 3) == 0)
152 					p += 3;
153 				for (i = 1; i < NSIG; i++)
154 					if (strcasecmp(sys_signame[i], p) == 0)
155 						break;
156 				if (i != NSIG) {
157 					signum = i;
158 					argv++;
159 					argc--;
160 				}
161 			}
162 		}
163 	}
164 
165 	criteria = 0;
166 
167 	if (prenice) {
168 		if (argc < 2)
169 			usage();
170 
171 		if (strcmp(argv[1], "-l") == 0) {
172 			longfmt = 1;
173 			argv++;
174 			argc--;
175 		}
176 
177 		if (argc < 2)
178 			usage();
179 
180 		p = argv[1];
181 
182 		i = (int)strtol(p, &q, 10);
183 		if (*q == '\0') {
184 			nicenum = i;
185 			argv++;
186 			argc--;
187 		} else
188 			usage();
189 	} else {
190 		while ((ch = getopt(argc, argv, "F:G:LP:U:d:fg:ilnqs:t:u:vx")) != -1)
191 			switch (ch) {
192 			case 'F':
193 				pidfile = optarg;
194 				criteria = 1;
195 				break;
196 			case 'G':
197 				makelist(&rgidlist, LT_GROUP, optarg);
198 				criteria = 1;
199 				break;
200 			case 'L':
201 				pidfilelock = 1;
202 				break;
203 			case 'P':
204 				makelist(&ppidlist, LT_GENERIC, optarg);
205 				criteria = 1;
206 				break;
207 			case 'U':
208 				makelist(&ruidlist, LT_USER, optarg);
209 				criteria = 1;
210 				break;
211 			case 'd':
212 				if (!pgrep)
213 					usage();
214 				delim = optarg;
215 				break;
216 			case 'f':
217 				matchargs = 1;
218 				break;
219 			case 'g':
220 				makelist(&pgrplist, LT_PGRP, optarg);
221 				criteria = 1;
222 				break;
223 			case 'i':
224 				cflags |= REG_ICASE;
225 				break;
226 			case 'l':
227 				longfmt = 1;
228 				break;
229 			case 'n':
230 				newest = 1;
231 				criteria = 1;
232 				break;
233 			case 'q':
234 				if (!pgrep)
235 					usage();
236 				quiet = 1;
237 				break;
238 			case 's':
239 				makelist(&sidlist, LT_SID, optarg);
240 				criteria = 1;
241 				break;
242 			case 't':
243 				makelist(&tdevlist, LT_TTY, optarg);
244 				criteria = 1;
245 				break;
246 			case 'u':
247 				makelist(&euidlist, LT_USER, optarg);
248 				criteria = 1;
249 				break;
250 			case 'v':
251 				inverse = 1;
252 				break;
253 			case 'x':
254 				fullmatch = 1;
255 				break;
256 			default:
257 				usage();
258 				/* NOTREACHED */
259 			}
260 		argc -= optind;
261 		argv += optind;
262 	}
263 
264 	if (argc != 0)
265 		criteria = 1;
266 	if (!criteria)
267 		usage();
268 	if (pidfile != NULL)
269 		pidfromfile = takepid(pidfile, pidfilelock);
270 	else {
271 		if (pidfilelock) {
272 			errx(STATUS_ERROR,
273 			    "Option -L doesn't make sense without -F");
274 		}
275 		pidfromfile = -1;
276 	}
277 
278 	mypid = getpid();
279 
280 	/*
281 	 * Retrieve the list of running processes from the kernel.
282 	 */
283 	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
284 	if (kd == NULL)
285 		errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf);
286 
287 	plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
288 	if (plist == NULL)
289 		errx(STATUS_ERROR, "Cannot get process list (%s)",
290 		    kvm_geterr(kd));
291 
292 	/*
293 	 * Allocate memory which will be used to keep track of the
294 	 * selection.
295 	 */
296 	if ((selected = calloc(sizeof(*selected), (size_t)nproc)) == NULL)
297 		err(STATUS_ERROR, "Cannot allocate memory for %d processes",
298 		    nproc);
299 
300 	/*
301 	 * Refine the selection.
302 	 */
303 	for (; *argv != NULL; argv++) {
304 		if ((rv = regcomp(&reg, *argv, cflags)) != 0) {
305 			(void)regerror(rv, &reg, buf, sizeof(buf));
306 			errx(STATUS_BADUSAGE,
307 			    "Cannot compile regular expression `%s' (%s)",
308 			    *argv, buf);
309 		}
310 
311 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
312 			if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
313 				continue;
314 
315 			if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
316 				continue;
317 			if (matchargs) {
318 
319 				j = 0;
320 				while (j < (int)sizeof(buf) && *pargv != NULL) {
321 					j += snprintf(buf + j, sizeof(buf) - j,
322 					    pargv[1] != NULL ? "%s " : "%s",
323 					    pargv[0]);
324 					pargv++;
325 				}
326 			} else if (pargv[0] != NULL)
327 				strlcpy(buf, pargv[0], sizeof(buf));
328 			else
329 				strlcpy(buf, kp->p_comm, sizeof(buf));
330 
331 			rv = regexec(&reg, buf, 1, &regmatch, 0);
332 			if (rv == 0) {
333 				if (fullmatch) {
334 					if (regmatch.rm_so == 0 &&
335 					    regmatch.rm_eo ==
336 					    (regoff_t)strlen(buf))
337 						selected[i] = 1;
338 				} else
339 					selected[i] = 1;
340 			} else if (rv != REG_NOMATCH) {
341 				(void)regerror(rv, &reg, buf, sizeof(buf));
342 				errx(STATUS_ERROR,
343 				    "Regular expression evaluation error (%s)",
344 				    buf);
345 			}
346 		}
347 
348 		regfree(&reg);
349 	}
350 
351 	for (i = 0, kp = plist; i < nproc; i++, kp++) {
352 		if ((kp->p_flag & P_SYSTEM) != 0)
353 			continue;
354 
355 		if (pidfromfile >= 0 && kp->p_pid != pidfromfile) {
356 			selected[i] = 0;
357 			continue;
358 		}
359 
360 		SLIST_FOREACH(li, &ruidlist, li_chain)
361 			if (kp->p_ruid == (uid_t)li->li_number)
362 				break;
363 		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
364 			selected[i] = 0;
365 			continue;
366 		}
367 
368 		SLIST_FOREACH(li, &rgidlist, li_chain)
369 			if (kp->p_rgid == (gid_t)li->li_number)
370 				break;
371 		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
372 			selected[i] = 0;
373 			continue;
374 		}
375 
376 		SLIST_FOREACH(li, &euidlist, li_chain)
377 			if (kp->p_uid == (uid_t)li->li_number)
378 				break;
379 		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
380 			selected[i] = 0;
381 			continue;
382 		}
383 
384 		SLIST_FOREACH(li, &ppidlist, li_chain)
385 			if ((uid_t)kp->p_ppid == (uid_t)li->li_number)
386 				break;
387 		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
388 			selected[i] = 0;
389 			continue;
390 		}
391 
392 		SLIST_FOREACH(li, &pgrplist, li_chain)
393 			if (kp->p__pgid == (pid_t)li->li_number)
394 				break;
395 		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
396 			selected[i] = 0;
397 			continue;
398 		}
399 
400 		SLIST_FOREACH(li, &tdevlist, li_chain) {
401 			if (li->li_number == -1 &&
402 			    (kp->p_flag & P_CONTROLT) == 0)
403 				break;
404 			if (kp->p_tdev == (uid_t)li->li_number)
405 				break;
406 		}
407 		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
408 			selected[i] = 0;
409 			continue;
410 		}
411 
412 		SLIST_FOREACH(li, &sidlist, li_chain)
413 			if (kp->p_sid == (pid_t)li->li_number)
414 				break;
415 		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
416 			selected[i] = 0;
417 			continue;
418 		}
419 
420 		if (argc == 0)
421 			selected[i] = 1;
422 	}
423 
424 	if (newest) {
425 		bestsec = 0;
426 		bestusec = 0;
427 		bestidx = -1;
428 
429 		for (i = 0, kp = plist; i < nproc; i++, kp++) {
430 			if (!selected[i])
431 				continue;
432 
433 			if (kp->p_ustart_sec > bestsec ||
434 			    (kp->p_ustart_sec == bestsec
435 			    && kp->p_ustart_usec > bestusec)) {
436 			    	bestsec = kp->p_ustart_sec;
437 			    	bestusec = kp->p_ustart_usec;
438 				bestidx = i;
439 			}
440 		}
441 
442 		(void)memset(selected, 0, (size_t)nproc);
443 		if (bestidx != -1)
444 			selected[bestidx] = 1;
445 	}
446 
447 	/*
448 	 * Take the appropriate action for each matched process, if any.
449 	 */
450 	for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) {
451 		if (kp->p_pid == mypid)
452 			continue;
453 		if (selected[i]) {
454 			if (inverse)
455 				continue;
456 		} else if (!inverse)
457 			continue;
458 
459 		if ((kp->p_flag & P_SYSTEM) != 0)
460 			continue;
461 
462 		rv |= (*action)(kp);
463 	}
464 
465 	return rv ? STATUS_MATCH : STATUS_NOMATCH;
466 }
467 
468 static void
469 usage(void)
470 {
471 	const char *ustr;
472 
473 	if (prenice)
474 		fprintf(stderr, "Usage: %s [-l] priority pattern ...\n",
475 		    getprogname());
476 	else {
477 		if (pgrep)
478 			ustr = "[-Lfilnqvx] [-d delim]";
479 		else
480 			ustr = "[-signal] [-Lfilnvx]";
481 
482 		(void)fprintf(stderr,
483 		    "Usage: %s %s [-F pidfile] [-G gid] [-g pgrp] [-P ppid] [-s sid] "
484 			   "[-t tty]\n"
485 		    "             [-U uid] [-u euid] pattern ...\n",
486 			      getprogname(), ustr);
487 	}
488 
489 	exit(STATUS_BADUSAGE);
490 }
491 
492 static int
493 killact(const struct kinfo_proc2 *kp)
494 {
495 	if (longfmt)
496 		grepact(kp);
497 	if (kill(kp->p_pid, signum) == -1) {
498 
499 		/*
500 		 * Check for ESRCH, which indicates that the process
501 		 * disappeared between us matching it and us
502 		 * signalling it; don't issue a warning about it.
503 		 */
504 		if (errno != ESRCH)
505 			warn("signalling pid %d", (int)kp->p_pid);
506 
507 		/*
508 		 * Return 0 to indicate that the process should not be
509 		 * considered a match, since we didn't actually get to
510 		 * signal it.
511 		 */
512 		return 0;
513 	}
514 
515 	return 1;
516 }
517 
518 static int
519 reniceact(const struct kinfo_proc2 *kp)
520 {
521 	int oldprio;
522 
523 	if (longfmt)
524 		grepact(kp);
525 
526 	errno = 0;
527 	if ((oldprio = getpriority(PRIO_PROCESS, kp->p_pid)) == -1 &&
528 	    errno != 0) {
529 		warn("%d: getpriority", kp->p_pid);
530 		return 0;
531 	}
532 
533 	if (setpriority(PRIO_PROCESS, kp->p_pid, nicenum) == -1) {
534 		warn("%d: setpriority", kp->p_pid);
535 		return 0;
536 	}
537 
538 	(void)printf("%d: old priority %d, new priority %d\n",
539 	    kp->p_pid, oldprio, nicenum);
540 
541 	return 1;
542 }
543 
544 static int
545 grepact(const struct kinfo_proc2 *kp)
546 {
547 	char **argv;
548 
549 	if (quiet)
550 		return 1;
551 
552 	if (longfmt && matchargs) {
553 
554 		/*
555 		 * If kvm_getargv2() failed the process has probably
556 		 * disappeared.  Return 0 to indicate that the process
557 		 * should not be considered a match, since we are no
558 		 * longer in a position to output it as a match.
559 		 */
560 		if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
561 			return 0;
562 
563 		(void)printf("%d ", (int)kp->p_pid);
564 		for (; *argv != NULL; argv++) {
565 			(void)printf("%s", *argv);
566 			if (argv[1] != NULL)
567 				(void)putchar(' ');
568 		}
569 	} else if (longfmt)
570 		(void)printf("%d %s", (int)kp->p_pid, kp->p_comm);
571 	else
572 		(void)printf("%d", (int)kp->p_pid);
573 
574 	(void)printf("%s", delim);
575 
576 	return 1;
577 }
578 
579 static void
580 makelist(struct listhead *head, enum listtype type, char *src)
581 {
582 	struct list *li;
583 	struct passwd *pw;
584 	struct group *gr;
585 	struct stat st;
586 	char *sp, *ep, buf[MAXPATHLEN];
587 	const char *p;
588 	int empty;
589 	const char *prefix = _PATH_DEV;
590 
591 	empty = 1;
592 
593 	while ((sp = strsep(&src, ",")) != NULL) {
594 		if (*sp == '\0')
595 			usage();
596 
597 		if ((li = malloc(sizeof(*li))) == NULL)
598 			err(STATUS_ERROR, "Cannot allocate %zu bytes",
599 			    sizeof(*li));
600 		SLIST_INSERT_HEAD(head, li, li_chain);
601 		empty = 0;
602 
603 		li->li_number = (uid_t)strtol(sp, &ep, 0);
604 		if (*ep == '\0' && type != LT_TTY) {
605 			switch (type) {
606 			case LT_PGRP:
607 				if (li->li_number == 0)
608 					li->li_number = getpgrp();
609 				break;
610 			case LT_SID:
611 				if (li->li_number == 0)
612 					li->li_number = getsid(mypid);
613 				break;
614 			default:
615 				break;
616 			}
617 			continue;
618 		}
619 
620 		switch (type) {
621 		case LT_USER:
622 			if ((pw = getpwnam(sp)) == NULL)
623 				errx(STATUS_BADUSAGE, "Unknown user `%s'",
624 				    sp);
625 			li->li_number = pw->pw_uid;
626 			break;
627 		case LT_GROUP:
628 			if ((gr = getgrnam(sp)) == NULL)
629 				errx(STATUS_BADUSAGE, "Unknown group `%s'",
630 				    sp);
631 			li->li_number = gr->gr_gid;
632 			break;
633 		case LT_TTY:
634 			p = sp;
635 			if (*sp == '/')
636 				prefix = "";
637 			else if (strcmp(sp, "-") == 0) {
638 				li->li_number = -1;
639 				break;
640 			} else if (strcmp(sp, "co") == 0)
641 				p = "console";
642 			else if (strncmp(sp, "tty", 3) == 0)
643 				/* all set */;
644 			else if (strncmp(sp, "pts/", 4) == 0)
645 				/* all set */;
646 			else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0'))
647 				prefix = _PATH_TTY;
648 			else
649 				prefix = _PATH_DEV_PTS;
650 
651 			(void)snprintf(buf, sizeof(buf), "%s%s", prefix, p);
652 
653 			if (stat(buf, &st) == -1) {
654 				if (errno == ENOENT)
655 					errx(STATUS_BADUSAGE,
656 					    "No such tty: `%s'", buf);
657 				err(STATUS_ERROR, "Cannot access `%s'", buf);
658 			}
659 
660 			if ((st.st_mode & S_IFCHR) == 0)
661 				errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf);
662 
663 			li->li_number = st.st_rdev;
664 			break;
665 		default:
666 			usage();
667 		}
668 	}
669 
670 	if (empty)
671 		usage();
672 }
673 
674 static int
675 takepid(const char *pidfile, int pidfilelock)
676 {
677 	char *endp, line[BUFSIZ];
678 	FILE *fh;
679 	long rval;
680 
681 	fh = fopen(pidfile, "r");
682 	if (fh == NULL)
683 		err(STATUS_ERROR, "Cannot open pidfile `%s'", pidfile);
684 
685 	if (pidfilelock) {
686 		/*
687 		 * If we can lock pidfile, this means that daemon is not
688 		 * running, so would be better not to kill some random process.
689 		 */
690 		if (flock(fileno(fh), LOCK_EX | LOCK_NB) == 0) {
691 			(void)fclose(fh);
692 			errx(STATUS_ERROR, "File '%s' can be locked", pidfile);
693 		} else {
694 			if (errno != EWOULDBLOCK) {
695 				errx(STATUS_ERROR,
696 				    "Error while locking file '%s'", pidfile);
697 			}
698 		}
699 	}
700 
701 	if (fgets(line, sizeof(line), fh) == NULL) {
702 		if (feof(fh)) {
703 			(void)fclose(fh);
704 			errx(STATUS_ERROR, "Pidfile `%s' is empty", pidfile);
705 		}
706 		(void)fclose(fh);
707 		err(STATUS_ERROR, "Cannot read from pid file `%s'", pidfile);
708 	}
709 	(void)fclose(fh);
710 
711 	rval = strtol(line, &endp, 10);
712 	if (*endp != '\0' && !isspace((unsigned char)*endp))
713 		errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile);
714 	else if (rval < MIN_PID || rval > MAX_PID)
715 		errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile);
716 	return (rval);
717 }
718