1 /* $NetBSD: pkill.c,v 1.35 2026/01/12 02:58:18 mrg 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.35 2026/01/12 02:58:18 mrg 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 /* Eat the program name. */ 172 argv++; 173 argc--; 174 175 if (strcmp(argv[0], "-l") == 0) { 176 longfmt = 1; 177 argv++; 178 argc--; 179 } 180 181 if (argc < 1) 182 usage(); 183 184 p = argv[0]; 185 186 i = (int)strtol(p, &q, 10); 187 if (*q == '\0') { 188 nicenum = i; 189 argv++; 190 argc--; 191 } else 192 usage(); 193 } else { 194 while ((ch = getopt(argc, argv, "F:G:LP:U:d:fg:ilnqs:t:u:vx")) != -1) 195 switch (ch) { 196 case 'F': 197 pidfile = optarg; 198 criteria = 1; 199 break; 200 case 'G': 201 makelist(&rgidlist, LT_GROUP, optarg); 202 criteria = 1; 203 break; 204 case 'L': 205 pidfilelock = 1; 206 break; 207 case 'P': 208 makelist(&ppidlist, LT_GENERIC, optarg); 209 criteria = 1; 210 break; 211 case 'U': 212 makelist(&ruidlist, LT_USER, optarg); 213 criteria = 1; 214 break; 215 case 'd': 216 if (!pgrep) 217 usage(); 218 delim = optarg; 219 break; 220 case 'f': 221 matchargs = 1; 222 break; 223 case 'g': 224 makelist(&pgrplist, LT_PGRP, optarg); 225 criteria = 1; 226 break; 227 case 'i': 228 cflags |= REG_ICASE; 229 break; 230 case 'l': 231 longfmt = 1; 232 break; 233 case 'n': 234 newest = 1; 235 criteria = 1; 236 break; 237 case 'q': 238 if (!pgrep) 239 usage(); 240 quiet = 1; 241 break; 242 case 's': 243 makelist(&sidlist, LT_SID, optarg); 244 criteria = 1; 245 break; 246 case 't': 247 makelist(&tdevlist, LT_TTY, optarg); 248 criteria = 1; 249 break; 250 case 'u': 251 makelist(&euidlist, LT_USER, optarg); 252 criteria = 1; 253 break; 254 case 'v': 255 inverse = 1; 256 break; 257 case 'x': 258 fullmatch = 1; 259 break; 260 default: 261 usage(); 262 /* NOTREACHED */ 263 } 264 argc -= optind; 265 argv += optind; 266 } 267 268 if (argc != 0) 269 criteria = 1; 270 if (!criteria) 271 usage(); 272 if (pidfile != NULL) 273 pidfromfile = takepid(pidfile, pidfilelock); 274 else { 275 if (pidfilelock) { 276 errx(STATUS_ERROR, 277 "Option -L doesn't make sense without -F"); 278 } 279 pidfromfile = -1; 280 } 281 282 mypid = getpid(); 283 284 /* 285 * Retrieve the list of running processes from the kernel. 286 */ 287 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf); 288 if (kd == NULL) 289 errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf); 290 291 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc); 292 if (plist == NULL) 293 errx(STATUS_ERROR, "Cannot get process list (%s)", 294 kvm_geterr(kd)); 295 296 /* 297 * Allocate memory which will be used to keep track of the 298 * selection. 299 */ 300 if ((selected = calloc(sizeof(*selected), (size_t)nproc)) == NULL) 301 err(STATUS_ERROR, "Cannot allocate memory for %d processes", 302 nproc); 303 304 /* 305 * Refine the selection. 306 */ 307 for (; *argv != NULL; argv++) { 308 if ((rv = regcomp(®, *argv, cflags)) != 0) { 309 (void)regerror(rv, ®, buf, sizeof(buf)); 310 errx(STATUS_BADUSAGE, 311 "Cannot compile regular expression `%s' (%s)", 312 *argv, buf); 313 } 314 315 for (i = 0, kp = plist; i < nproc; i++, kp++) { 316 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 317 continue; 318 319 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL) 320 continue; 321 if (matchargs) { 322 323 j = 0; 324 while (j < (int)sizeof(buf) && *pargv != NULL) { 325 j += snprintf(buf + j, sizeof(buf) - j, 326 pargv[1] != NULL ? "%s " : "%s", 327 pargv[0]); 328 pargv++; 329 } 330 } else if (pargv[0] != NULL) 331 strlcpy(buf, pargv[0], sizeof(buf)); 332 else 333 strlcpy(buf, kp->p_comm, sizeof(buf)); 334 335 rv = regexec(®, buf, 1, ®match, 0); 336 if (rv == 0) { 337 if (fullmatch) { 338 if (regmatch.rm_so == 0 && 339 regmatch.rm_eo == 340 (regoff_t)strlen(buf)) 341 selected[i] = 1; 342 } else 343 selected[i] = 1; 344 } else if (rv != REG_NOMATCH) { 345 (void)regerror(rv, ®, buf, sizeof(buf)); 346 errx(STATUS_ERROR, 347 "Regular expression evaluation error (%s)", 348 buf); 349 } 350 } 351 352 regfree(®); 353 } 354 355 for (i = 0, kp = plist; i < nproc; i++, kp++) { 356 if ((kp->p_flag & P_SYSTEM) != 0) 357 continue; 358 359 if (pidfromfile >= 0 && kp->p_pid != pidfromfile) { 360 selected[i] = 0; 361 continue; 362 } 363 364 SLIST_FOREACH(li, &ruidlist, li_chain) 365 if (kp->p_ruid == (uid_t)li->li_number) 366 break; 367 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 368 selected[i] = 0; 369 continue; 370 } 371 372 SLIST_FOREACH(li, &rgidlist, li_chain) 373 if (kp->p_rgid == (gid_t)li->li_number) 374 break; 375 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 376 selected[i] = 0; 377 continue; 378 } 379 380 SLIST_FOREACH(li, &euidlist, li_chain) 381 if (kp->p_uid == (uid_t)li->li_number) 382 break; 383 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 384 selected[i] = 0; 385 continue; 386 } 387 388 SLIST_FOREACH(li, &ppidlist, li_chain) 389 if ((uid_t)kp->p_ppid == (uid_t)li->li_number) 390 break; 391 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 392 selected[i] = 0; 393 continue; 394 } 395 396 SLIST_FOREACH(li, &pgrplist, li_chain) 397 if (kp->p__pgid == (pid_t)li->li_number) 398 break; 399 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 400 selected[i] = 0; 401 continue; 402 } 403 404 SLIST_FOREACH(li, &tdevlist, li_chain) { 405 if (li->li_number == -1 && 406 (kp->p_flag & P_CONTROLT) == 0) 407 break; 408 if (kp->p_tdev == (uid_t)li->li_number) 409 break; 410 } 411 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 412 selected[i] = 0; 413 continue; 414 } 415 416 SLIST_FOREACH(li, &sidlist, li_chain) 417 if (kp->p_sid == (pid_t)li->li_number) 418 break; 419 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 420 selected[i] = 0; 421 continue; 422 } 423 424 if (argc == 0) 425 selected[i] = 1; 426 } 427 428 if (newest) { 429 bestsec = 0; 430 bestusec = 0; 431 bestidx = -1; 432 433 for (i = 0, kp = plist; i < nproc; i++, kp++) { 434 if (!selected[i]) 435 continue; 436 437 if (kp->p_ustart_sec > bestsec || 438 (kp->p_ustart_sec == bestsec 439 && kp->p_ustart_usec > bestusec)) { 440 bestsec = kp->p_ustart_sec; 441 bestusec = kp->p_ustart_usec; 442 bestidx = i; 443 } 444 } 445 446 (void)memset(selected, 0, (size_t)nproc); 447 if (bestidx != -1) 448 selected[bestidx] = 1; 449 } 450 451 /* 452 * Take the appropriate action for each matched process, if any. 453 */ 454 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 455 if (kp->p_pid == mypid) 456 continue; 457 if (selected[i]) { 458 if (inverse) 459 continue; 460 } else if (!inverse) 461 continue; 462 463 if ((kp->p_flag & P_SYSTEM) != 0) 464 continue; 465 466 rv |= (*action)(kp); 467 } 468 469 return rv ? STATUS_MATCH : STATUS_NOMATCH; 470 } 471 472 static void 473 usage(void) 474 { 475 const char *ustr; 476 477 if (prenice) 478 fprintf(stderr, "Usage: %s [-l] priority pattern ...\n", 479 getprogname()); 480 else { 481 if (pgrep) 482 ustr = "[-Lfilnqvx] [-d delim]"; 483 else 484 ustr = "[-signal] [-Lfilnvx]"; 485 486 (void)fprintf(stderr, 487 "Usage: %s %s [-F pidfile] [-G gid] [-g pgrp] [-P ppid] [-s sid] " 488 "[-t tty]\n" 489 " [-U uid] [-u euid] pattern ...\n", 490 getprogname(), ustr); 491 } 492 493 exit(STATUS_BADUSAGE); 494 } 495 496 static int 497 killact(const struct kinfo_proc2 *kp) 498 { 499 if (longfmt) 500 grepact(kp); 501 if (kill(kp->p_pid, signum) == -1) { 502 503 /* 504 * Check for ESRCH, which indicates that the process 505 * disappeared between us matching it and us 506 * signalling it; don't issue a warning about it. 507 */ 508 if (errno != ESRCH) 509 warn("signalling pid %d", (int)kp->p_pid); 510 511 /* 512 * Return 0 to indicate that the process should not be 513 * considered a match, since we didn't actually get to 514 * signal it. 515 */ 516 return 0; 517 } 518 519 return 1; 520 } 521 522 static int 523 reniceact(const struct kinfo_proc2 *kp) 524 { 525 int oldprio; 526 527 if (longfmt) 528 grepact(kp); 529 530 errno = 0; 531 if ((oldprio = getpriority(PRIO_PROCESS, kp->p_pid)) == -1 && 532 errno != 0) { 533 warn("%d: getpriority", kp->p_pid); 534 return 0; 535 } 536 537 if (setpriority(PRIO_PROCESS, kp->p_pid, nicenum) == -1) { 538 warn("%d: setpriority", kp->p_pid); 539 return 0; 540 } 541 542 (void)printf("%d: old priority %d, new priority %d\n", 543 kp->p_pid, oldprio, nicenum); 544 545 return 1; 546 } 547 548 static int 549 grepact(const struct kinfo_proc2 *kp) 550 { 551 char **argv; 552 553 if (quiet) 554 return 1; 555 556 if (longfmt && matchargs) { 557 558 /* 559 * If kvm_getargv2() failed the process has probably 560 * disappeared. Return 0 to indicate that the process 561 * should not be considered a match, since we are no 562 * longer in a position to output it as a match. 563 */ 564 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL) 565 return 0; 566 567 (void)printf("%d ", (int)kp->p_pid); 568 for (; *argv != NULL; argv++) { 569 (void)printf("%s", *argv); 570 if (argv[1] != NULL) 571 (void)putchar(' '); 572 } 573 } else if (longfmt) 574 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm); 575 else 576 (void)printf("%d", (int)kp->p_pid); 577 578 (void)printf("%s", delim); 579 580 return 1; 581 } 582 583 static void 584 makelist(struct listhead *head, enum listtype type, char *src) 585 { 586 struct list *li; 587 struct passwd *pw; 588 struct group *gr; 589 struct stat st; 590 char *sp, *ep, buf[MAXPATHLEN]; 591 const char *p; 592 int empty; 593 const char *prefix = _PATH_DEV; 594 595 empty = 1; 596 597 while ((sp = strsep(&src, ",")) != NULL) { 598 if (*sp == '\0') 599 usage(); 600 601 if ((li = malloc(sizeof(*li))) == NULL) 602 err(STATUS_ERROR, "Cannot allocate %zu bytes", 603 sizeof(*li)); 604 SLIST_INSERT_HEAD(head, li, li_chain); 605 empty = 0; 606 607 li->li_number = (uid_t)strtol(sp, &ep, 0); 608 if (*ep == '\0' && type != LT_TTY) { 609 switch (type) { 610 case LT_PGRP: 611 if (li->li_number == 0) 612 li->li_number = getpgrp(); 613 break; 614 case LT_SID: 615 if (li->li_number == 0) 616 li->li_number = getsid(mypid); 617 break; 618 default: 619 break; 620 } 621 continue; 622 } 623 624 switch (type) { 625 case LT_USER: 626 if ((pw = getpwnam(sp)) == NULL) 627 errx(STATUS_BADUSAGE, "Unknown user `%s'", 628 sp); 629 li->li_number = pw->pw_uid; 630 break; 631 case LT_GROUP: 632 if ((gr = getgrnam(sp)) == NULL) 633 errx(STATUS_BADUSAGE, "Unknown group `%s'", 634 sp); 635 li->li_number = gr->gr_gid; 636 break; 637 case LT_TTY: 638 p = sp; 639 if (*sp == '/') 640 prefix = ""; 641 else if (strcmp(sp, "-") == 0) { 642 li->li_number = -1; 643 break; 644 } else if (strcmp(sp, "co") == 0) 645 p = "console"; 646 else if (strncmp(sp, "tty", 3) == 0) 647 /* all set */; 648 else if (strncmp(sp, "pts/", 4) == 0) 649 /* all set */; 650 else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0')) 651 prefix = _PATH_TTY; 652 else 653 prefix = _PATH_DEV_PTS; 654 655 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p); 656 657 if (stat(buf, &st) == -1) { 658 if (errno == ENOENT) 659 errx(STATUS_BADUSAGE, 660 "No such tty: `%s'", buf); 661 err(STATUS_ERROR, "Cannot access `%s'", buf); 662 } 663 664 if ((st.st_mode & S_IFCHR) == 0) 665 errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf); 666 667 li->li_number = st.st_rdev; 668 break; 669 default: 670 usage(); 671 } 672 } 673 674 if (empty) 675 usage(); 676 } 677 678 static int 679 takepid(const char *pidfile, int pidfilelock) 680 { 681 char *endp, line[BUFSIZ]; 682 FILE *fh; 683 long rval; 684 685 fh = fopen(pidfile, "r"); 686 if (fh == NULL) 687 err(STATUS_ERROR, "Cannot open pidfile `%s'", pidfile); 688 689 if (pidfilelock) { 690 /* 691 * If we can lock pidfile, this means that daemon is not 692 * running, so would be better not to kill some random process. 693 */ 694 if (flock(fileno(fh), LOCK_EX | LOCK_NB) == 0) { 695 (void)fclose(fh); 696 errx(STATUS_ERROR, "File '%s' can be locked", pidfile); 697 } else { 698 if (errno != EWOULDBLOCK) { 699 errx(STATUS_ERROR, 700 "Error while locking file '%s'", pidfile); 701 } 702 } 703 } 704 705 if (fgets(line, sizeof(line), fh) == NULL) { 706 if (feof(fh)) { 707 (void)fclose(fh); 708 errx(STATUS_ERROR, "Pidfile `%s' is empty", pidfile); 709 } 710 (void)fclose(fh); 711 err(STATUS_ERROR, "Cannot read from pid file `%s'", pidfile); 712 } 713 (void)fclose(fh); 714 715 rval = strtol(line, &endp, 10); 716 if (*endp != '\0' && !isspace((unsigned char)*endp)) 717 errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile); 718 else if (rval < MIN_PID || rval > MAX_PID) 719 errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile); 720 return (rval); 721 } 722