1 /* $NetBSD: pkill.c,v 1.30 2015/01/09 12:45:32 prlw1 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.30 2015/01/09 12:45:32 prlw1 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 300 strlcpy(buf, pargv[0], sizeof(buf)); 301 302 rv = regexec(®, buf, 1, ®match, 0); 303 if (rv == 0) { 304 if (fullmatch) { 305 if (regmatch.rm_so == 0 && 306 regmatch.rm_eo == 307 (regoff_t)strlen(buf)) 308 selected[i] = 1; 309 } else 310 selected[i] = 1; 311 } else if (rv != REG_NOMATCH) { 312 (void)regerror(rv, ®, buf, sizeof(buf)); 313 errx(STATUS_ERROR, 314 "Regular expression evaluation error (%s)", 315 buf); 316 } 317 } 318 319 regfree(®); 320 } 321 322 for (i = 0, kp = plist; i < nproc; i++, kp++) { 323 if ((kp->p_flag & P_SYSTEM) != 0) 324 continue; 325 326 SLIST_FOREACH(li, &ruidlist, li_chain) 327 if (kp->p_ruid == (uid_t)li->li_number) 328 break; 329 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 330 selected[i] = 0; 331 continue; 332 } 333 334 SLIST_FOREACH(li, &rgidlist, li_chain) 335 if (kp->p_rgid == (gid_t)li->li_number) 336 break; 337 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 338 selected[i] = 0; 339 continue; 340 } 341 342 SLIST_FOREACH(li, &euidlist, li_chain) 343 if (kp->p_uid == (uid_t)li->li_number) 344 break; 345 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 346 selected[i] = 0; 347 continue; 348 } 349 350 SLIST_FOREACH(li, &ppidlist, li_chain) 351 if ((uid_t)kp->p_ppid == (uid_t)li->li_number) 352 break; 353 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 354 selected[i] = 0; 355 continue; 356 } 357 358 SLIST_FOREACH(li, &pgrplist, li_chain) 359 if (kp->p__pgid == (pid_t)li->li_number) 360 break; 361 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 362 selected[i] = 0; 363 continue; 364 } 365 366 SLIST_FOREACH(li, &tdevlist, li_chain) { 367 if (li->li_number == -1 && 368 (kp->p_flag & P_CONTROLT) == 0) 369 break; 370 if (kp->p_tdev == (uid_t)li->li_number) 371 break; 372 } 373 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 374 selected[i] = 0; 375 continue; 376 } 377 378 SLIST_FOREACH(li, &sidlist, li_chain) 379 if (kp->p_sid == (pid_t)li->li_number) 380 break; 381 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 382 selected[i] = 0; 383 continue; 384 } 385 386 if (argc == 0) 387 selected[i] = 1; 388 } 389 390 if (newest) { 391 bestsec = 0; 392 bestusec = 0; 393 bestidx = -1; 394 395 for (i = 0, kp = plist; i < nproc; i++, kp++) { 396 if (!selected[i]) 397 continue; 398 399 if (kp->p_ustart_sec > bestsec || 400 (kp->p_ustart_sec == bestsec 401 && kp->p_ustart_usec > bestusec)) { 402 bestsec = kp->p_ustart_sec; 403 bestusec = kp->p_ustart_usec; 404 bestidx = i; 405 } 406 } 407 408 (void)memset(selected, 0, (size_t)nproc); 409 if (bestidx != -1) 410 selected[bestidx] = 1; 411 } 412 413 /* 414 * Take the appropriate action for each matched process, if any. 415 */ 416 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 417 if (kp->p_pid == mypid) 418 continue; 419 if (selected[i]) { 420 if (inverse) 421 continue; 422 } else if (!inverse) 423 continue; 424 425 if ((kp->p_flag & P_SYSTEM) != 0) 426 continue; 427 428 rv |= (*action)(kp); 429 } 430 431 return rv ? STATUS_MATCH : STATUS_NOMATCH; 432 } 433 434 static void 435 usage(void) 436 { 437 const char *ustr; 438 439 if (prenice) 440 fprintf(stderr, "Usage: %s [-l] priority pattern ...\n", 441 getprogname()); 442 else { 443 if (pgrep) 444 ustr = "[-filnvx] [-d delim]"; 445 else 446 ustr = "[-signal] [-filnvx]"; 447 448 (void)fprintf(stderr, 449 "Usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] " 450 "[-t tty]\n" 451 " [-U uid] [-u euid] pattern ...\n", 452 getprogname(), ustr); 453 } 454 455 exit(STATUS_BADUSAGE); 456 } 457 458 static int 459 killact(const struct kinfo_proc2 *kp) 460 { 461 if (longfmt) 462 grepact(kp); 463 if (kill(kp->p_pid, signum) == -1) { 464 465 /* 466 * Check for ESRCH, which indicates that the process 467 * disappeared between us matching it and us 468 * signalling it; don't issue a warning about it. 469 */ 470 if (errno != ESRCH) 471 warn("signalling pid %d", (int)kp->p_pid); 472 473 /* 474 * Return 0 to indicate that the process should not be 475 * considered a match, since we didn't actually get to 476 * signal it. 477 */ 478 return 0; 479 } 480 481 return 1; 482 } 483 484 static int 485 reniceact(const struct kinfo_proc2 *kp) 486 { 487 int oldprio; 488 489 if (longfmt) 490 grepact(kp); 491 492 errno = 0; 493 if ((oldprio = getpriority(PRIO_PROCESS, kp->p_pid)) == -1 && 494 errno != 0) { 495 warn("%d: getpriority", kp->p_pid); 496 return 0; 497 } 498 499 if (setpriority(PRIO_PROCESS, kp->p_pid, nicenum) == -1) { 500 warn("%d: setpriority", kp->p_pid); 501 return 0; 502 } 503 504 (void)printf("%d: old priority %d, new priority %d\n", 505 kp->p_pid, oldprio, nicenum); 506 507 return 1; 508 } 509 510 static int 511 grepact(const struct kinfo_proc2 *kp) 512 { 513 char **argv; 514 515 if (longfmt && matchargs) { 516 517 /* 518 * If kvm_getargv2() failed the process has probably 519 * disappeared. Return 0 to indicate that the process 520 * should not be considered a match, since we are no 521 * longer in a position to output it as a match. 522 */ 523 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL) 524 return 0; 525 526 (void)printf("%d ", (int)kp->p_pid); 527 for (; *argv != NULL; argv++) { 528 (void)printf("%s", *argv); 529 if (argv[1] != NULL) 530 (void)putchar(' '); 531 } 532 } else if (longfmt) 533 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm); 534 else 535 (void)printf("%d", (int)kp->p_pid); 536 537 (void)printf("%s", delim); 538 539 return 1; 540 } 541 542 static void 543 makelist(struct listhead *head, enum listtype type, char *src) 544 { 545 struct list *li; 546 struct passwd *pw; 547 struct group *gr; 548 struct stat st; 549 char *sp, *ep, buf[MAXPATHLEN]; 550 const char *p; 551 int empty; 552 const char *prefix = _PATH_DEV; 553 554 empty = 1; 555 556 while ((sp = strsep(&src, ",")) != NULL) { 557 if (*sp == '\0') 558 usage(); 559 560 if ((li = malloc(sizeof(*li))) == NULL) 561 err(STATUS_ERROR, "Cannot allocate %zu bytes", 562 sizeof(*li)); 563 SLIST_INSERT_HEAD(head, li, li_chain); 564 empty = 0; 565 566 li->li_number = (uid_t)strtol(sp, &ep, 0); 567 if (*ep == '\0' && type != LT_TTY) { 568 switch (type) { 569 case LT_PGRP: 570 if (li->li_number == 0) 571 li->li_number = getpgrp(); 572 break; 573 case LT_SID: 574 if (li->li_number == 0) 575 li->li_number = getsid(mypid); 576 break; 577 default: 578 break; 579 } 580 continue; 581 } 582 583 switch (type) { 584 case LT_USER: 585 if ((pw = getpwnam(sp)) == NULL) 586 errx(STATUS_BADUSAGE, "Unknown user `%s'", 587 sp); 588 li->li_number = pw->pw_uid; 589 break; 590 case LT_GROUP: 591 if ((gr = getgrnam(sp)) == NULL) 592 errx(STATUS_BADUSAGE, "Unknown group `%s'", 593 sp); 594 li->li_number = gr->gr_gid; 595 break; 596 case LT_TTY: 597 p = sp; 598 if (*sp == '/') 599 prefix = ""; 600 else if (strcmp(sp, "-") == 0) { 601 li->li_number = -1; 602 break; 603 } else if (strcmp(sp, "co") == 0) 604 p = "console"; 605 else if (strncmp(sp, "tty", 3) == 0) 606 /* all set */; 607 else if (strncmp(sp, "pts/", 4) == 0) 608 /* all set */; 609 else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0')) 610 prefix = _PATH_TTY; 611 else 612 prefix = _PATH_DEV_PTS; 613 614 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p); 615 616 if (stat(buf, &st) == -1) { 617 if (errno == ENOENT) 618 errx(STATUS_BADUSAGE, 619 "No such tty: `%s'", buf); 620 err(STATUS_ERROR, "Cannot access `%s'", buf); 621 } 622 623 if ((st.st_mode & S_IFCHR) == 0) 624 errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf); 625 626 li->li_number = st.st_rdev; 627 break; 628 default: 629 usage(); 630 } 631 } 632 633 if (empty) 634 usage(); 635 } 636