1 /* $NetBSD: pkill.c,v 1.31 2017/02/21 13:09:56 kre Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 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.31 2017/02/21 13:09:56 kre 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 <pwd.h> 56 #include <grp.h> 57 #include <errno.h> 58 #include <paths.h> 59 60 #define STATUS_MATCH 0 61 #define STATUS_NOMATCH 1 62 #define STATUS_BADUSAGE 2 63 #define STATUS_ERROR 3 64 65 enum listtype { 66 LT_GENERIC, 67 LT_USER, 68 LT_GROUP, 69 LT_TTY, 70 LT_PGRP, 71 LT_SID 72 }; 73 74 struct list { 75 SLIST_ENTRY(list) li_chain; 76 long li_number; 77 }; 78 79 SLIST_HEAD(listhead, list); 80 81 static struct kinfo_proc2 *plist; 82 static char *selected; 83 static const char *delim = "\n"; 84 static int nproc; 85 static int pgrep; 86 static int prenice; 87 static int signum = SIGTERM; 88 static int nicenum; 89 static int newest; 90 static int inverse; 91 static int longfmt; 92 static int matchargs; 93 static int fullmatch; 94 static int cflags = REG_EXTENDED; 95 static kvm_t *kd; 96 static pid_t mypid; 97 98 static struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 99 static struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 100 static struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 101 static struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 102 static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 103 static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 104 static struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 105 106 static void usage(void) __dead; 107 static int killact(const struct kinfo_proc2 *); 108 static int reniceact(const struct kinfo_proc2 *); 109 static int grepact(const struct kinfo_proc2 *); 110 static void makelist(struct listhead *, enum listtype, char *); 111 112 int 113 main(int argc, char **argv) 114 { 115 char buf[_POSIX2_LINE_MAX], **pargv, *q; 116 int i, j, ch, bestidx, rv, criteria; 117 int (*action)(const struct kinfo_proc2 *); 118 const struct kinfo_proc2 *kp; 119 struct list *li; 120 const char *p; 121 u_int32_t bestsec, bestusec; 122 regex_t reg; 123 regmatch_t regmatch; 124 125 setprogname(argv[0]); 126 127 if (strcmp(getprogname(), "pgrep") == 0) { 128 action = grepact; 129 pgrep = 1; 130 } else if (strcmp(getprogname(), "prenice") == 0) { 131 action = reniceact; 132 prenice = 1; 133 } else { 134 action = killact; 135 p = argv[1]; 136 137 if (argc > 1 && p[0] == '-') { 138 p++; 139 i = (int)strtol(p, &q, 10); 140 if (*q == '\0') { 141 signum = i; 142 argv++; 143 argc--; 144 } else { 145 if (strncasecmp(p, "sig", 3) == 0) 146 p += 3; 147 for (i = 1; i < NSIG; i++) 148 if (strcasecmp(sys_signame[i], p) == 0) 149 break; 150 if (i != NSIG) { 151 signum = i; 152 argv++; 153 argc--; 154 } 155 } 156 } 157 } 158 159 criteria = 0; 160 161 if (prenice) { 162 if (argc < 2) 163 usage(); 164 165 if (strcmp(argv[1], "-l") == 0) { 166 longfmt = 1; 167 argv++; 168 argc--; 169 } 170 171 if (argc < 2) 172 usage(); 173 174 p = argv[1]; 175 176 i = (int)strtol(p, &q, 10); 177 if (*q == '\0') { 178 nicenum = i; 179 argv++; 180 argc--; 181 } else 182 usage(); 183 } else { 184 while ((ch = getopt(argc, argv, "G:P:U:d:fg:ilns:t:u:vx")) != -1) 185 switch (ch) { 186 case 'G': 187 makelist(&rgidlist, LT_GROUP, optarg); 188 criteria = 1; 189 break; 190 case 'P': 191 makelist(&ppidlist, LT_GENERIC, optarg); 192 criteria = 1; 193 break; 194 case 'U': 195 makelist(&ruidlist, LT_USER, optarg); 196 criteria = 1; 197 break; 198 case 'd': 199 if (!pgrep) 200 usage(); 201 delim = optarg; 202 break; 203 case 'f': 204 matchargs = 1; 205 break; 206 case 'g': 207 makelist(&pgrplist, LT_PGRP, optarg); 208 criteria = 1; 209 break; 210 case 'i': 211 cflags |= REG_ICASE; 212 break; 213 case 'l': 214 longfmt = 1; 215 break; 216 case 'n': 217 newest = 1; 218 criteria = 1; 219 break; 220 case 's': 221 makelist(&sidlist, LT_SID, optarg); 222 criteria = 1; 223 break; 224 case 't': 225 makelist(&tdevlist, LT_TTY, optarg); 226 criteria = 1; 227 break; 228 case 'u': 229 makelist(&euidlist, LT_USER, optarg); 230 criteria = 1; 231 break; 232 case 'v': 233 inverse = 1; 234 break; 235 case 'x': 236 fullmatch = 1; 237 break; 238 default: 239 usage(); 240 /* NOTREACHED */ 241 } 242 argc -= optind; 243 argv += optind; 244 } 245 246 if (argc != 0) 247 criteria = 1; 248 if (!criteria) 249 usage(); 250 251 mypid = getpid(); 252 253 /* 254 * Retrieve the list of running processes from the kernel. 255 */ 256 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf); 257 if (kd == NULL) 258 errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf); 259 260 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc); 261 if (plist == NULL) 262 errx(STATUS_ERROR, "Cannot get process list (%s)", 263 kvm_geterr(kd)); 264 265 /* 266 * Allocate memory which will be used to keep track of the 267 * selection. 268 */ 269 if ((selected = calloc((size_t)1, (size_t)nproc)) == NULL) 270 err(STATUS_ERROR, "Cannot allocate memory for %d processes", 271 nproc); 272 273 /* 274 * Refine the selection. 275 */ 276 for (; *argv != NULL; argv++) { 277 if ((rv = regcomp(®, *argv, cflags)) != 0) { 278 (void)regerror(rv, ®, buf, sizeof(buf)); 279 errx(STATUS_BADUSAGE, 280 "Cannot compile regular expression `%s' (%s)", 281 *argv, buf); 282 } 283 284 for (i = 0, kp = plist; i < nproc; i++, kp++) { 285 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 286 continue; 287 288 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL) 289 continue; 290 if (matchargs) { 291 292 j = 0; 293 while (j < (int)sizeof(buf) && *pargv != NULL) { 294 j += snprintf(buf + j, sizeof(buf) - j, 295 pargv[1] != NULL ? "%s " : "%s", 296 pargv[0]); 297 pargv++; 298 } 299 } else if (pargv[0] != NULL) 300 strlcpy(buf, pargv[0], sizeof(buf)); 301 else 302 strlcpy(buf, kp->p_comm, sizeof(buf)); 303 304 rv = regexec(®, buf, 1, ®match, 0); 305 if (rv == 0) { 306 if (fullmatch) { 307 if (regmatch.rm_so == 0 && 308 regmatch.rm_eo == 309 (regoff_t)strlen(buf)) 310 selected[i] = 1; 311 } else 312 selected[i] = 1; 313 } else if (rv != REG_NOMATCH) { 314 (void)regerror(rv, ®, buf, sizeof(buf)); 315 errx(STATUS_ERROR, 316 "Regular expression evaluation error (%s)", 317 buf); 318 } 319 } 320 321 regfree(®); 322 } 323 324 for (i = 0, kp = plist; i < nproc; i++, kp++) { 325 if ((kp->p_flag & P_SYSTEM) != 0) 326 continue; 327 328 SLIST_FOREACH(li, &ruidlist, li_chain) 329 if (kp->p_ruid == (uid_t)li->li_number) 330 break; 331 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 332 selected[i] = 0; 333 continue; 334 } 335 336 SLIST_FOREACH(li, &rgidlist, li_chain) 337 if (kp->p_rgid == (gid_t)li->li_number) 338 break; 339 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 340 selected[i] = 0; 341 continue; 342 } 343 344 SLIST_FOREACH(li, &euidlist, li_chain) 345 if (kp->p_uid == (uid_t)li->li_number) 346 break; 347 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 348 selected[i] = 0; 349 continue; 350 } 351 352 SLIST_FOREACH(li, &ppidlist, li_chain) 353 if ((uid_t)kp->p_ppid == (uid_t)li->li_number) 354 break; 355 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 356 selected[i] = 0; 357 continue; 358 } 359 360 SLIST_FOREACH(li, &pgrplist, li_chain) 361 if (kp->p__pgid == (pid_t)li->li_number) 362 break; 363 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 364 selected[i] = 0; 365 continue; 366 } 367 368 SLIST_FOREACH(li, &tdevlist, li_chain) { 369 if (li->li_number == -1 && 370 (kp->p_flag & P_CONTROLT) == 0) 371 break; 372 if (kp->p_tdev == (uid_t)li->li_number) 373 break; 374 } 375 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 376 selected[i] = 0; 377 continue; 378 } 379 380 SLIST_FOREACH(li, &sidlist, li_chain) 381 if (kp->p_sid == (pid_t)li->li_number) 382 break; 383 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 384 selected[i] = 0; 385 continue; 386 } 387 388 if (argc == 0) 389 selected[i] = 1; 390 } 391 392 if (newest) { 393 bestsec = 0; 394 bestusec = 0; 395 bestidx = -1; 396 397 for (i = 0, kp = plist; i < nproc; i++, kp++) { 398 if (!selected[i]) 399 continue; 400 401 if (kp->p_ustart_sec > bestsec || 402 (kp->p_ustart_sec == bestsec 403 && kp->p_ustart_usec > bestusec)) { 404 bestsec = kp->p_ustart_sec; 405 bestusec = kp->p_ustart_usec; 406 bestidx = i; 407 } 408 } 409 410 (void)memset(selected, 0, (size_t)nproc); 411 if (bestidx != -1) 412 selected[bestidx] = 1; 413 } 414 415 /* 416 * Take the appropriate action for each matched process, if any. 417 */ 418 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 419 if (kp->p_pid == mypid) 420 continue; 421 if (selected[i]) { 422 if (inverse) 423 continue; 424 } else if (!inverse) 425 continue; 426 427 if ((kp->p_flag & P_SYSTEM) != 0) 428 continue; 429 430 rv |= (*action)(kp); 431 } 432 433 return rv ? STATUS_MATCH : STATUS_NOMATCH; 434 } 435 436 static void 437 usage(void) 438 { 439 const char *ustr; 440 441 if (prenice) 442 fprintf(stderr, "Usage: %s [-l] priority pattern ...\n", 443 getprogname()); 444 else { 445 if (pgrep) 446 ustr = "[-filnvx] [-d delim]"; 447 else 448 ustr = "[-signal] [-filnvx]"; 449 450 (void)fprintf(stderr, 451 "Usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] " 452 "[-t tty]\n" 453 " [-U uid] [-u euid] pattern ...\n", 454 getprogname(), ustr); 455 } 456 457 exit(STATUS_BADUSAGE); 458 } 459 460 static int 461 killact(const struct kinfo_proc2 *kp) 462 { 463 if (longfmt) 464 grepact(kp); 465 if (kill(kp->p_pid, signum) == -1) { 466 467 /* 468 * Check for ESRCH, which indicates that the process 469 * disappeared between us matching it and us 470 * signalling it; don't issue a warning about it. 471 */ 472 if (errno != ESRCH) 473 warn("signalling pid %d", (int)kp->p_pid); 474 475 /* 476 * Return 0 to indicate that the process should not be 477 * considered a match, since we didn't actually get to 478 * signal it. 479 */ 480 return 0; 481 } 482 483 return 1; 484 } 485 486 static int 487 reniceact(const struct kinfo_proc2 *kp) 488 { 489 int oldprio; 490 491 if (longfmt) 492 grepact(kp); 493 494 errno = 0; 495 if ((oldprio = getpriority(PRIO_PROCESS, kp->p_pid)) == -1 && 496 errno != 0) { 497 warn("%d: getpriority", kp->p_pid); 498 return 0; 499 } 500 501 if (setpriority(PRIO_PROCESS, kp->p_pid, nicenum) == -1) { 502 warn("%d: setpriority", kp->p_pid); 503 return 0; 504 } 505 506 (void)printf("%d: old priority %d, new priority %d\n", 507 kp->p_pid, oldprio, nicenum); 508 509 return 1; 510 } 511 512 static int 513 grepact(const struct kinfo_proc2 *kp) 514 { 515 char **argv; 516 517 if (longfmt && matchargs) { 518 519 /* 520 * If kvm_getargv2() failed the process has probably 521 * disappeared. Return 0 to indicate that the process 522 * should not be considered a match, since we are no 523 * longer in a position to output it as a match. 524 */ 525 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL) 526 return 0; 527 528 (void)printf("%d ", (int)kp->p_pid); 529 for (; *argv != NULL; argv++) { 530 (void)printf("%s", *argv); 531 if (argv[1] != NULL) 532 (void)putchar(' '); 533 } 534 } else if (longfmt) 535 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm); 536 else 537 (void)printf("%d", (int)kp->p_pid); 538 539 (void)printf("%s", delim); 540 541 return 1; 542 } 543 544 static void 545 makelist(struct listhead *head, enum listtype type, char *src) 546 { 547 struct list *li; 548 struct passwd *pw; 549 struct group *gr; 550 struct stat st; 551 char *sp, *ep, buf[MAXPATHLEN]; 552 const char *p; 553 int empty; 554 const char *prefix = _PATH_DEV; 555 556 empty = 1; 557 558 while ((sp = strsep(&src, ",")) != NULL) { 559 if (*sp == '\0') 560 usage(); 561 562 if ((li = malloc(sizeof(*li))) == NULL) 563 err(STATUS_ERROR, "Cannot allocate %zu bytes", 564 sizeof(*li)); 565 SLIST_INSERT_HEAD(head, li, li_chain); 566 empty = 0; 567 568 li->li_number = (uid_t)strtol(sp, &ep, 0); 569 if (*ep == '\0' && type != LT_TTY) { 570 switch (type) { 571 case LT_PGRP: 572 if (li->li_number == 0) 573 li->li_number = getpgrp(); 574 break; 575 case LT_SID: 576 if (li->li_number == 0) 577 li->li_number = getsid(mypid); 578 break; 579 default: 580 break; 581 } 582 continue; 583 } 584 585 switch (type) { 586 case LT_USER: 587 if ((pw = getpwnam(sp)) == NULL) 588 errx(STATUS_BADUSAGE, "Unknown user `%s'", 589 sp); 590 li->li_number = pw->pw_uid; 591 break; 592 case LT_GROUP: 593 if ((gr = getgrnam(sp)) == NULL) 594 errx(STATUS_BADUSAGE, "Unknown group `%s'", 595 sp); 596 li->li_number = gr->gr_gid; 597 break; 598 case LT_TTY: 599 p = sp; 600 if (*sp == '/') 601 prefix = ""; 602 else if (strcmp(sp, "-") == 0) { 603 li->li_number = -1; 604 break; 605 } else if (strcmp(sp, "co") == 0) 606 p = "console"; 607 else if (strncmp(sp, "tty", 3) == 0) 608 /* all set */; 609 else if (strncmp(sp, "pts/", 4) == 0) 610 /* all set */; 611 else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0')) 612 prefix = _PATH_TTY; 613 else 614 prefix = _PATH_DEV_PTS; 615 616 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p); 617 618 if (stat(buf, &st) == -1) { 619 if (errno == ENOENT) 620 errx(STATUS_BADUSAGE, 621 "No such tty: `%s'", buf); 622 err(STATUS_ERROR, "Cannot access `%s'", buf); 623 } 624 625 if ((st.st_mode & S_IFCHR) == 0) 626 errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf); 627 628 li->li_number = st.st_rdev; 629 break; 630 default: 631 usage(); 632 } 633 } 634 635 if (empty) 636 usage(); 637 } 638