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