pkill.c revision 143878
1/* $NetBSD: pkill.c,v 1.8 2005/03/02 15:31:44 abs 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include <sys/cdefs.h> 40__FBSDID("$FreeBSD: head/usr.bin/pkill/pkill.c 143878 2005-03-20 11:42:26Z pjd $"); 41 42#include <sys/types.h> 43#include <sys/param.h> 44#include <sys/sysctl.h> 45#include <sys/proc.h> 46#include <sys/queue.h> 47#include <sys/stat.h> 48#include <sys/time.h> 49#include <sys/user.h> 50 51#include <stdio.h> 52#include <stdlib.h> 53#include <limits.h> 54#include <paths.h> 55#include <string.h> 56#include <unistd.h> 57#include <signal.h> 58#include <regex.h> 59#include <ctype.h> 60#include <fcntl.h> 61#include <kvm.h> 62#include <err.h> 63#include <pwd.h> 64#include <grp.h> 65#include <errno.h> 66#include <locale.h> 67 68#define STATUS_MATCH 0 69#define STATUS_NOMATCH 1 70#define STATUS_BADUSAGE 2 71#define STATUS_ERROR 3 72 73#define MIN_PID 5 74#define MAX_PID 99999 75 76/* Ignore system-processes (if '-S' flag is not specified) and myself. */ 77#define PSKIP(kp) ((kp)->ki_pid == mypid || \ 78 (!kthreads && ((kp)->ki_flag & P_KTHREAD) != 0)) 79 80enum listtype { 81 LT_GENERIC, 82 LT_USER, 83 LT_GROUP, 84 LT_TTY, 85 LT_PGRP, 86 LT_SID 87}; 88 89struct list { 90 SLIST_ENTRY(list) li_chain; 91 long li_number; 92}; 93 94SLIST_HEAD(listhead, list); 95 96struct kinfo_proc *plist; 97char *selected; 98const char *delim = "\n"; 99int nproc; 100int pgrep; 101int signum = SIGTERM; 102int newest; 103int oldest; 104int inverse; 105int longfmt; 106int matchargs; 107int fullmatch; 108int kthreads; 109int cflags = REG_EXTENDED; 110kvm_t *kd; 111pid_t mypid; 112 113struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 114struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 115struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 116struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 117struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 118struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 119struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 120struct listhead jidlist = SLIST_HEAD_INITIALIZER(list); 121 122int main(int, char **); 123void usage(void); 124void killact(struct kinfo_proc *); 125void grepact(struct kinfo_proc *); 126void makelist(struct listhead *, enum listtype, char *); 127int takepid(const char *); 128 129int 130main(int argc, char **argv) 131{ 132 extern char *optarg; 133 extern int optind; 134 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 135 const char *execf, *coref; 136 int debug_opt; 137 int i, ch, bestidx, rv, criteria, pidfromfile; 138 size_t jsz; 139 void (*action)(struct kinfo_proc *); 140 struct kinfo_proc *kp; 141 struct list *li; 142 struct timeval best_tval; 143 regex_t reg; 144 regmatch_t regmatch; 145 146 setlocale(LC_ALL, ""); 147 148 if (strcmp(getprogname(), "pgrep") == 0) { 149 action = grepact; 150 pgrep = 1; 151 } else { 152 action = killact; 153 p = argv[1]; 154 155 if (argc > 1 && p[0] == '-') { 156 p++; 157 i = (int)strtol(p, &q, 10); 158 if (*q == '\0') { 159 signum = i; 160 argv++; 161 argc--; 162 } else { 163 if (strncasecmp(p, "sig", 3) == 0) 164 p += 3; 165 for (i = 1; i < NSIG; i++) 166 if (strcasecmp(sys_signame[i], p) == 0) 167 break; 168 if (i != NSIG) { 169 signum = i; 170 argv++; 171 argc--; 172 } 173 } 174 } 175 } 176 177 criteria = 0; 178 debug_opt = 0; 179 pidfromfile = -1; 180 execf = coref = _PATH_DEVNULL; 181 182 while ((ch = getopt(argc, argv, "DF:G:M:N:P:SU:d:fg:ij:lnos:t:u:vx")) != -1) 183 switch (ch) { 184 case 'D': 185 debug_opt++; 186 break; 187 case 'F': 188 pidfromfile = takepid(optarg); 189 criteria = 1; 190 break; 191 case 'G': 192 makelist(&rgidlist, LT_GROUP, optarg); 193 criteria = 1; 194 break; 195 case 'M': 196 coref = optarg; 197 break; 198 case 'N': 199 execf = optarg; 200 break; 201 case 'P': 202 makelist(&ppidlist, LT_GENERIC, optarg); 203 criteria = 1; 204 break; 205 case 'S': 206 if (!pgrep) 207 usage(); 208 kthreads = 1; 209 break; 210 case 'U': 211 makelist(&ruidlist, LT_USER, optarg); 212 criteria = 1; 213 break; 214 case 'd': 215 if (!pgrep) 216 usage(); 217 delim = optarg; 218 break; 219 case 'f': 220 matchargs = 1; 221 break; 222 case 'g': 223 makelist(&pgrplist, LT_PGRP, optarg); 224 criteria = 1; 225 break; 226 case 'i': 227 cflags |= REG_ICASE; 228 break; 229 case 'j': 230 makelist(&jidlist, LT_GENERIC, optarg); 231 criteria = 1; 232 break; 233 case 'l': 234 if (!pgrep) 235 usage(); 236 longfmt = 1; 237 break; 238 case 'n': 239 newest = 1; 240 criteria = 1; 241 break; 242 case 'o': 243 oldest = 1; 244 criteria = 1; 245 break; 246 case 's': 247 makelist(&sidlist, LT_SID, optarg); 248 criteria = 1; 249 break; 250 case 't': 251 makelist(&tdevlist, LT_TTY, optarg); 252 criteria = 1; 253 break; 254 case 'u': 255 makelist(&euidlist, LT_USER, optarg); 256 criteria = 1; 257 break; 258 case 'v': 259 inverse = 1; 260 break; 261 case 'x': 262 fullmatch = 1; 263 break; 264 default: 265 usage(); 266 /* NOTREACHED */ 267 } 268 269 argc -= optind; 270 argv += optind; 271 if (argc != 0) 272 criteria = 1; 273 if (!criteria) 274 usage(); 275 if (newest && oldest) 276 errx(STATUS_ERROR, "-n and -o are mutually exclusive"); 277 278 mypid = getpid(); 279 280 /* 281 * Retrieve the list of running processes from the kernel. 282 */ 283 kd = kvm_openfiles(execf, coref, NULL, O_RDONLY, buf); 284 if (kd == NULL) 285 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 286 287 /* 288 * Use KERN_PROC_PROC instead of KERN_PROC_ALL, since we 289 * just want processes and not individual kernel threads. 290 */ 291 plist = kvm_getprocs(kd, KERN_PROC_PROC, 0, &nproc); 292 if (plist == NULL) 293 errx(STATUS_ERROR, "kvm_getprocs() failed"); 294 295 /* 296 * Allocate memory which will be used to keep track of the 297 * selection. 298 */ 299 if ((selected = malloc(nproc)) == NULL) 300 errx(STATUS_ERROR, "memory allocation failure"); 301 memset(selected, 0, nproc); 302 303 /* 304 * Refine the selection. 305 */ 306 for (; *argv != NULL; argv++) { 307 if ((rv = regcomp(®, *argv, cflags)) != 0) { 308 regerror(rv, ®, buf, sizeof(buf)); 309 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 310 } 311 312 for (i = 0, kp = plist; i < nproc; i++, kp++) { 313 if (PSKIP(kp)) { 314 if (debug_opt > 0) 315 fprintf(stderr, "* Skipped %5d %3d %s\n", 316 kp->ki_pid, kp->ki_uid, kp->ki_comm); 317 continue; 318 } 319 320 if (matchargs && 321 (pargv = kvm_getargv(kd, kp, 0)) != NULL) { 322 jsz = 0; 323 while (jsz < sizeof(buf) && *pargv != NULL) { 324 jsz += snprintf(buf + jsz, 325 sizeof(buf) - jsz, 326 pargv[1] != NULL ? "%s " : "%s", 327 pargv[0]); 328 pargv++; 329 } 330 mstr = buf; 331 } else 332 mstr = kp->ki_comm; 333 334 rv = regexec(®, mstr, 1, ®match, 0); 335 if (rv == 0) { 336 if (fullmatch) { 337 if (regmatch.rm_so == 0 && 338 regmatch.rm_eo == 339 (off_t)strlen(mstr)) 340 selected[i] = 1; 341 } else 342 selected[i] = 1; 343 } else if (rv != REG_NOMATCH) { 344 regerror(rv, ®, buf, sizeof(buf)); 345 errx(STATUS_ERROR, "regexec(): %s", buf); 346 } 347 if (debug_opt > 1) { 348 const char *rv_res = "NoMatch"; 349 if (selected[i]) 350 rv_res = "Matched"; 351 fprintf(stderr, "* %s %5d %3d %s\n", rv_res, 352 kp->ki_pid, kp->ki_uid, mstr); 353 } 354 } 355 356 regfree(®); 357 } 358 359 for (i = 0, kp = plist; i < nproc; i++, kp++) { 360 if (PSKIP(kp)) 361 continue; 362 363 if (pidfromfile >= 0 && kp->ki_pid != pidfromfile) { 364 selected[i] = 0; 365 continue; 366 } 367 368 SLIST_FOREACH(li, &ruidlist, li_chain) 369 if (kp->ki_ruid == (uid_t)li->li_number) 370 break; 371 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 372 selected[i] = 0; 373 continue; 374 } 375 376 SLIST_FOREACH(li, &rgidlist, li_chain) 377 if (kp->ki_rgid == (gid_t)li->li_number) 378 break; 379 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 380 selected[i] = 0; 381 continue; 382 } 383 384 SLIST_FOREACH(li, &euidlist, li_chain) 385 if (kp->ki_uid == (uid_t)li->li_number) 386 break; 387 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 388 selected[i] = 0; 389 continue; 390 } 391 392 SLIST_FOREACH(li, &ppidlist, li_chain) 393 if (kp->ki_ppid == (pid_t)li->li_number) 394 break; 395 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 396 selected[i] = 0; 397 continue; 398 } 399 400 SLIST_FOREACH(li, &pgrplist, li_chain) 401 if (kp->ki_pgid == (pid_t)li->li_number) 402 break; 403 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 404 selected[i] = 0; 405 continue; 406 } 407 408 SLIST_FOREACH(li, &tdevlist, li_chain) { 409 if (li->li_number == -1 && 410 (kp->ki_flag & P_CONTROLT) == 0) 411 break; 412 if (kp->ki_tdev == (dev_t)li->li_number) 413 break; 414 } 415 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 416 selected[i] = 0; 417 continue; 418 } 419 420 SLIST_FOREACH(li, &sidlist, li_chain) 421 if (kp->ki_sid == (pid_t)li->li_number) 422 break; 423 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 424 selected[i] = 0; 425 continue; 426 } 427 428 SLIST_FOREACH(li, &jidlist, li_chain) { 429 if (kp->ki_jid > 0) { 430 if (li->li_number == 0) 431 break; 432 if (kp->ki_jid == (int)li->li_number) 433 break; 434 } 435 } 436 if (SLIST_FIRST(&jidlist) != NULL && li == NULL) { 437 selected[i] = 0; 438 continue; 439 } 440 441 if (argc == 0) 442 selected[i] = 1; 443 } 444 445 if (newest || oldest) { 446 best_tval.tv_sec = 0; 447 best_tval.tv_usec = 0; 448 bestidx = -1; 449 450 for (i = 0, kp = plist; i < nproc; i++, kp++) { 451 if (!selected[i]) 452 continue; 453 454 if (bestidx == -1) { 455 /* The first entry of the list which matched. */ 456 ; 457 } else if (timercmp(&kp->ki_start, &best_tval, >)) { 458 /* This entry is newer than previous "best". */ 459 if (oldest) /* but we want the oldest */ 460 continue; 461 } else { 462 /* This entry is older than previous "best". */ 463 if (newest) /* but we want the newest */ 464 continue; 465 } 466 /* This entry is better than previous "best" entry. */ 467 best_tval.tv_sec = kp->ki_start.tv_sec; 468 best_tval.tv_usec = kp->ki_start.tv_usec; 469 bestidx = i; 470 } 471 472 memset(selected, 0, nproc); 473 if (bestidx != -1) 474 selected[bestidx] = 1; 475 } 476 477 /* 478 * Take the appropriate action for each matched process, if any. 479 */ 480 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 481 if (PSKIP(kp)) 482 continue; 483 if (selected[i]) { 484 if (inverse) 485 continue; 486 } else if (!inverse) 487 continue; 488 rv = 1; 489 (*action)(kp); 490 } 491 492 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 493} 494 495void 496usage(void) 497{ 498 const char *ustr; 499 500 if (pgrep) 501 ustr = "[-Sfilnovx] [-d delim]"; 502 else 503 ustr = "[-signal] [-finovx]"; 504 505 fprintf(stderr, 506 "usage: %s %s [-F pidfile] [-G gid] [-M core] [-N system]\n" 507 " [-P ppid] [-U uid] [-g pgrp] [-j jid] [-s sid]\n" 508 " [-t tty] [-u euid] pattern ...\n", getprogname(), 509 ustr); 510 511 exit(STATUS_ERROR); 512} 513 514void 515killact(struct kinfo_proc *kp) 516{ 517 518 if (kill(kp->ki_pid, signum) == -1) 519 err(STATUS_ERROR, "signalling pid %d", (int)kp->ki_pid); 520} 521 522void 523grepact(struct kinfo_proc *kp) 524{ 525 char **argv; 526 527 if (longfmt && matchargs && 528 (argv = kvm_getargv(kd, kp, 0)) != NULL) { 529 printf("%d ", (int)kp->ki_pid); 530 for (; *argv != NULL; argv++) { 531 printf("%s", *argv); 532 if (argv[1] != NULL) 533 putchar(' '); 534 } 535 } else if (longfmt) 536 printf("%d %s", (int)kp->ki_pid, kp->ki_comm); 537 else 538 printf("%d", (int)kp->ki_pid); 539 540 printf("%s", delim); 541} 542 543void 544makelist(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 const char *cp; 551 char *sp, *p, buf[MAXPATHLEN]; 552 int empty; 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 errx(STATUS_ERROR, "memory allocation failure"); 562 SLIST_INSERT_HEAD(head, li, li_chain); 563 empty = 0; 564 565 li->li_number = (uid_t)strtol(sp, &p, 0); 566 if (*p == '\0') { 567 switch (type) { 568 case LT_PGRP: 569 if (li->li_number == 0) 570 li->li_number = getpgrp(); 571 break; 572 case LT_SID: 573 if (li->li_number == 0) 574 li->li_number = getsid(mypid); 575 break; 576 case LT_TTY: 577 usage(); 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 if (strcmp(sp, "-") == 0) { 599 li->li_number = -1; 600 break; 601 } else if (strcmp(sp, "co") == 0) 602 cp = "console"; 603 else if (strncmp(sp, "tty", 3) == 0) 604 cp = sp; 605 else 606 cp = NULL; 607 608 if (cp == NULL) 609 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 610 else 611 snprintf(buf, sizeof(buf), "/dev/%s", cp); 612 613 if (stat(buf, &st) < 0) { 614 if (errno == ENOENT) 615 errx(STATUS_BADUSAGE, 616 "no such tty: `%s'", sp); 617 err(STATUS_ERROR, "stat(%s)", sp); 618 } 619 620 if ((st.st_mode & S_IFCHR) == 0) 621 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 622 623 li->li_number = st.st_rdev; 624 break; 625 default: 626 usage(); 627 }; 628 } 629 630 if (empty) 631 usage(); 632} 633 634int 635takepid(const char *pidfile) 636{ 637 char *endp, line[BUFSIZ]; 638 FILE *fh; 639 long rval; 640 641 fh = fopen(pidfile, "r"); 642 if (fh == NULL) 643 err(STATUS_ERROR, "can't open pid file `%s'", pidfile); 644 645 if (fgets(line, sizeof(line), fh) == NULL) { 646 if (feof(fh)) { 647 (void)fclose(fh); 648 errx(STATUS_ERROR, "pid file `%s' is empty", pidfile); 649 } 650 (void)fclose(fh); 651 err(STATUS_ERROR, "can't read from pid file `%s'", pidfile); 652 } 653 (void)fclose(fh); 654 655 rval = strtol(line, &endp, 10); 656 if (*endp != '\0' && !isspace((unsigned char)*endp)) 657 errx(STATUS_ERROR, "invalid pid in file `%s'", pidfile); 658 else if (rval < MIN_PID || rval > MAX_PID) 659 errx(STATUS_ERROR, "invalid pid in file `%s'", pidfile); 660 return (rval); 661} 662