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(®, *argv, cflags)) != 0) { 305 (void)regerror(rv, ®, 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(®, buf, 1, ®match, 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, ®, buf, sizeof(buf)); 342 errx(STATUS_ERROR, 343 "Regular expression evaluation error (%s)", 344 buf); 345 } 346 } 347 348 regfree(®); 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