1 /* $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $ */ 2 /* $DragonFly: src/usr.bin/pkill/pkill.c,v 1.9 2007/02/01 10:33:26 corecode Exp $ */ 3 4 /*- 5 * Copyright (c) 2002 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Andrew Doran. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/user.h> 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/sysctl.h> 44 #include <sys/queue.h> 45 #include <sys/stat.h> 46 #include <sys/fcntl.h> 47 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <limits.h> 51 #include <paths.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <signal.h> 55 #include <regex.h> 56 #include <ctype.h> 57 #include <kvm.h> 58 #include <err.h> 59 #include <pwd.h> 60 #include <grp.h> 61 #include <errno.h> 62 63 #define STATUS_MATCH 0 64 #define STATUS_NOMATCH 1 65 #define STATUS_BADUSAGE 2 66 #define STATUS_ERROR 3 67 68 enum listtype { 69 LT_USER, /* real or effective user: uid_t */ 70 LT_GROUP, /* group: gid_t */ 71 LT_TTY, /* tty: dev_t */ 72 LT_PPID, /* parent pid: pid_t */ 73 LT_PGRP, /* process group: pid_t */ 74 LT_SID, /* session id: pid_t */ 75 LT_JID /* jail id: pid_t */ 76 }; 77 78 struct list { 79 SLIST_ENTRY(list) li_chain; 80 union { 81 uid_t ld_uid; 82 gid_t ld_gid; 83 pid_t ld_pid; 84 dev_t ld_dev; 85 } li_datum; 86 }; 87 88 SLIST_HEAD(listhead, list); 89 90 struct kinfo_proc *plist; 91 char *selected; 92 const char *delim = "\n"; 93 int nproc; 94 int pgrep; 95 int signum = SIGTERM; 96 int newest; 97 int inverse; 98 int longfmt; 99 int matchargs; 100 int fullmatch; 101 kvm_t *kd; 102 pid_t mypid; 103 104 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 105 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 106 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 107 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 108 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 109 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 110 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 111 struct listhead jidlist = SLIST_HEAD_INITIALIZER(list); 112 113 void usage(void); 114 void killact(struct kinfo_proc *, int); 115 void grepact(struct kinfo_proc *, int); 116 int parse_pid(const char *, char **, struct list *, pid_t); 117 void makelist(struct listhead *, enum listtype, char *); 118 119 /* 120 * pkill - list or signal selected processes based on regular expression. 121 */ 122 int 123 main(int argc, char **argv) 124 { 125 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 126 int i, ch, bestidx, rv, criteria; 127 unsigned int j; 128 void (*action)(struct kinfo_proc *, int); 129 struct kinfo_proc *kp; 130 struct list *li; 131 struct timeval best; 132 regex_t reg; 133 regmatch_t regmatch; 134 const char *kvmf = _PATH_DEVNULL; 135 136 if (strcmp(getprogname(), "pgrep") == 0) { 137 action = grepact; 138 pgrep = 1; 139 } else { 140 action = killact; 141 p = argv[1]; 142 143 /* 144 * For pkill only: parse the signal (number or name) to send. 145 */ 146 if (argc > 1 && p[0] == '-') { 147 p++; 148 i = (int)strtol(p, &q, 10); 149 if (*q == '\0') { 150 signum = i; 151 argv++; 152 argc--; 153 } else { 154 if (strncasecmp(p, "sig", 3) == 0) 155 p += 3; 156 for (i = 1; i < NSIG; i++) { 157 if (strcasecmp(sys_signame[i], p) == 0) 158 break; 159 } 160 if (i != NSIG) { 161 signum = i; 162 argv++; 163 argc--; 164 } 165 } 166 } 167 } 168 169 criteria = 0; 170 171 while ((ch = getopt(argc, argv, "G:P:U:d:fg:j:lns:t:u:vx")) != -1) { 172 switch (ch) { 173 case 'G': 174 makelist(&rgidlist, LT_GROUP, optarg); 175 criteria = 1; 176 break; 177 case 'P': 178 makelist(&ppidlist, LT_PPID, optarg); 179 criteria = 1; 180 break; 181 case 'U': 182 makelist(&ruidlist, LT_USER, optarg); 183 criteria = 1; 184 break; 185 case 'd': 186 if (!pgrep) 187 usage(); 188 delim = optarg; 189 break; 190 case 'f': 191 matchargs = 1; 192 break; 193 case 'g': 194 makelist(&pgrplist, LT_PGRP, optarg); 195 criteria = 1; 196 break; 197 case 'j': 198 makelist(&jidlist, LT_JID, optarg); 199 criteria = 1; 200 break; 201 case 'l': 202 if (!pgrep) 203 usage(); 204 longfmt = 1; 205 break; 206 case 'n': 207 newest = 1; 208 criteria = 1; 209 break; 210 case 's': 211 makelist(&sidlist, LT_SID, optarg); 212 criteria = 1; 213 break; 214 case 't': 215 makelist(&tdevlist, LT_TTY, optarg); 216 criteria = 1; 217 break; 218 case 'u': 219 makelist(&euidlist, LT_USER, optarg); 220 criteria = 1; 221 break; 222 case 'v': 223 inverse = 1; 224 break; 225 case 'x': 226 fullmatch = 1; 227 break; 228 default: 229 usage(); 230 /* NOTREACHED */ 231 } 232 } 233 234 argc -= optind; 235 argv += optind; 236 if (argc != 0) 237 criteria = 1; 238 if (!criteria) 239 usage(); 240 241 mypid = getpid(); 242 243 /* 244 * Retrieve the list of running processes from the kernel. 245 */ 246 kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf); 247 if (kd == NULL) 248 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 249 250 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 251 if (plist == NULL) 252 errx(STATUS_ERROR, "cannot list processes"); 253 254 /* 255 * Allocate memory which will be used to keep track of the 256 * selection. 257 */ 258 if ((selected = malloc(nproc)) == NULL) 259 errx(STATUS_ERROR, "memory allocation failure"); 260 memset(selected, 0, nproc); 261 262 /* 263 * Refine the selection. 264 */ 265 for (; *argv != NULL; argv++) { 266 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 267 regerror(rv, ®, buf, sizeof(buf)); 268 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 269 } 270 271 for (i = 0, kp = plist; i < nproc; i++, kp++) { 272 if ((kp->kp_flags & P_SYSTEM) != 0 || kp->kp_pid == mypid) 273 continue; 274 275 if (matchargs) { 276 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 277 continue; 278 279 j = 0; 280 while (j < sizeof(buf) && *pargv != NULL) { 281 j += snprintf(buf + j, sizeof(buf) - j, 282 pargv[1] != NULL ? "%s " : "%s", 283 pargv[0]); 284 pargv++; 285 } 286 287 mstr = buf; 288 } else 289 mstr = kp->kp_comm; 290 291 rv = regexec(®, mstr, 1, ®match, 0); 292 if (rv == 0) { 293 if (fullmatch) { 294 if (regmatch.rm_so == 0 && 295 regmatch.rm_eo == (regoff_t)strlen(mstr)) 296 selected[i] = 1; 297 } else 298 selected[i] = 1; 299 } else if (rv != REG_NOMATCH) { 300 regerror(rv, ®, buf, sizeof(buf)); 301 errx(STATUS_ERROR, "regexec(): %s", buf); 302 } 303 } 304 305 regfree(®); 306 } 307 308 /* 309 * Iterate through the list of processes, deselecting each one 310 * if it fails to meet the established criteria. 311 */ 312 for (i = 0, kp = plist; i < nproc; i++, kp++) { 313 if ((kp->kp_flags & P_SYSTEM) != 0) 314 continue; 315 316 SLIST_FOREACH(li, &ruidlist, li_chain) { 317 if (kp->kp_ruid == li->li_datum.ld_uid) 318 break; 319 } 320 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 321 selected[i] = 0; 322 continue; 323 } 324 325 SLIST_FOREACH(li, &rgidlist, li_chain) { 326 if (kp->kp_rgid == li->li_datum.ld_gid) 327 break; 328 } 329 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 330 selected[i] = 0; 331 continue; 332 } 333 334 SLIST_FOREACH(li, &euidlist, li_chain) { 335 if (kp->kp_uid == li->li_datum.ld_uid) 336 break; 337 } 338 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 339 selected[i] = 0; 340 continue; 341 } 342 343 SLIST_FOREACH(li, &ppidlist, li_chain) { 344 if (kp->kp_ppid == li->li_datum.ld_pid) 345 break; 346 } 347 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 348 selected[i] = 0; 349 continue; 350 } 351 352 SLIST_FOREACH(li, &pgrplist, li_chain) { 353 if (kp->kp_pgid == li->li_datum.ld_pid) 354 break; 355 } 356 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 357 selected[i] = 0; 358 continue; 359 } 360 361 SLIST_FOREACH(li, &tdevlist, li_chain) { 362 if (li->li_datum.ld_dev == NODEV && 363 (kp->kp_flags & P_CONTROLT) == 0) 364 break; 365 if (kp->kp_tdev == li->li_datum.ld_dev) 366 break; 367 } 368 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 369 selected[i] = 0; 370 continue; 371 } 372 373 SLIST_FOREACH(li, &sidlist, li_chain) { 374 if (kp->kp_sid == li->li_datum.ld_pid) 375 break; 376 } 377 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 378 selected[i] = 0; 379 continue; 380 } 381 382 SLIST_FOREACH(li, &jidlist, li_chain) { 383 /* A particular jail ID, including 0 (not in jail) */ 384 if (kp->kp_jailid == li->li_datum.ld_pid) 385 break; 386 /* Any jail */ 387 if (kp->kp_jailid > 0 && li->li_datum.ld_pid < 0) 388 break; 389 } 390 if (SLIST_FIRST(&jidlist) != NULL && li == NULL) { 391 selected[i] = 0; 392 continue; 393 } 394 395 if (argc == 0) 396 selected[i] = 1; 397 } 398 399 if (newest) { 400 best.tv_sec = 0; 401 best.tv_usec = 0; 402 bestidx = -1; 403 404 for (i = 0, kp = plist; i < nproc; i++, kp++) { 405 if (!selected[i]) 406 continue; 407 408 if (kp->kp_start.tv_sec > best.tv_sec || 409 (kp->kp_start.tv_sec == best.tv_sec 410 && kp->kp_start.tv_usec > best.tv_usec)) { 411 best.tv_sec = kp->kp_start.tv_sec; 412 best.tv_usec = kp->kp_start.tv_usec; 413 bestidx = i; 414 } 415 } 416 417 memset(selected, 0, nproc); 418 if (bestidx != -1) 419 selected[bestidx] = 1; 420 } 421 422 /* 423 * Take the appropriate action for each matched process, if any. 424 */ 425 for (i = 0, j = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 426 if (kp->kp_pid == mypid) 427 continue; 428 if (selected[i]) { 429 if (inverse) 430 continue; 431 } else if (!inverse) 432 continue; 433 434 if ((kp->kp_flags & P_SYSTEM) != 0) 435 continue; 436 437 rv = 1; 438 (*action)(kp, j++); 439 } 440 441 if (pgrep) 442 putchar('\n'); 443 444 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 445 } 446 447 void 448 usage(void) 449 { 450 const char *ustr; 451 452 if (pgrep) 453 ustr = "[-flnvx] [-d delim]"; 454 else 455 ustr = "[-signal] [-fnvx]"; 456 457 fprintf(stderr, 458 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 459 " [-t tty] [-u euid] [-j jid] pattern ...\n", 460 getprogname(), ustr); 461 462 exit(STATUS_ERROR); 463 } 464 465 /* 466 * Action callback to signal the given process (pkill). 467 */ 468 void 469 killact(struct kinfo_proc *kp, int dummy __unused) 470 { 471 if (kill(kp->kp_pid, signum) == -1) 472 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_pid); 473 } 474 475 /* 476 * Action callback to print the pid of the given process (pgrep). 477 */ 478 void 479 grepact(struct kinfo_proc *kp, int printdelim) 480 { 481 char **argv; 482 483 if (printdelim) 484 fputs(delim, stdout); 485 486 if (longfmt && matchargs) { 487 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 488 return; 489 490 printf("%d ", (int)kp->kp_pid); 491 for (; *argv != NULL; argv++) { 492 printf("%s", *argv); 493 if (argv[1] != NULL) 494 putchar(' '); 495 } 496 } else if (longfmt) 497 printf("%d %s", (int)kp->kp_pid, kp->kp_comm); 498 else 499 printf("%d", (int)kp->kp_pid); 500 501 } 502 503 /* 504 * Parse a pid from the given string. If zero, use a given default. 505 */ 506 int 507 parse_pid(const char *string, char **p, struct list *li, pid_t default_pid) 508 { 509 long l; 510 511 l = strtol(string, p, 0); 512 li->li_datum.ld_pid = (l == 0 ? default_pid : (pid_t)l); 513 return(**p == '\0'); 514 } 515 516 /* 517 * Populate a list from a comma-seperated string of items. 518 * The possible valid values for each item depends on the type of list. 519 */ 520 void 521 makelist(struct listhead *head, enum listtype type, char *src) 522 { 523 struct list *li; 524 struct passwd *pw; 525 struct group *gr; 526 struct stat st; 527 const char *sp, *tty_name; 528 char *p, buf[MAXPATHLEN]; 529 int empty; 530 531 empty = 1; 532 533 while ((sp = strsep(&src, ",")) != NULL) { 534 if (*sp == '\0') 535 usage(); 536 537 if ((li = malloc(sizeof(*li))) == NULL) 538 errx(STATUS_ERROR, "memory allocation failure"); 539 SLIST_INSERT_HEAD(head, li, li_chain); 540 empty = 0; 541 542 switch (type) { 543 case LT_PPID: 544 if (!parse_pid(sp, &p, li, (pid_t)0)) 545 usage(); 546 break; 547 case LT_PGRP: 548 if (!parse_pid(sp, &p, li, getpgrp())) 549 usage(); 550 break; 551 case LT_SID: 552 if (!parse_pid(sp, &p, li, getsid(mypid))) 553 usage(); 554 break; 555 case LT_JID: 556 /* default to no jail */ 557 if (!parse_pid(sp, &p, li, 0)) 558 usage(); 559 if (li->li_datum.ld_pid < -1) { 560 errx(STATUS_BADUSAGE, 561 "Negative jail ID `%s'", sp); 562 } 563 break; 564 case LT_USER: 565 li->li_datum.ld_uid = (uid_t)strtol(sp, &p, 0); 566 if (*p != '\0') { 567 if ((pw = getpwnam(sp)) == NULL) { 568 errx(STATUS_BADUSAGE, 569 "unknown user `%s'", optarg); 570 } 571 li->li_datum.ld_uid = pw->pw_uid; 572 } 573 break; 574 case LT_GROUP: 575 li->li_datum.ld_gid = (gid_t)strtol(sp, &p, 0); 576 if (*p != '\0') { 577 if ((gr = getgrnam(sp)) == NULL) { 578 errx(STATUS_BADUSAGE, 579 "unknown group `%s'", optarg); 580 } 581 li->li_datum.ld_gid = gr->gr_gid; 582 } 583 break; 584 case LT_TTY: 585 if (strcmp(sp, "-") == 0) { 586 li->li_datum.ld_dev = NODEV; 587 break; 588 } else if (strcmp(sp, "co") == 0) 589 tty_name = "console"; 590 else if (strncmp(sp, "tty", 3) == 0) 591 tty_name = sp; 592 else 593 tty_name = NULL; 594 595 if (tty_name == NULL) 596 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 597 else 598 snprintf(buf, sizeof(buf), "/dev/%s", tty_name); 599 600 if (stat(buf, &st) < 0) { 601 if (errno == ENOENT) 602 errx(STATUS_BADUSAGE, 603 "no such tty: `%s'", sp); 604 err(STATUS_ERROR, "stat(%s)", sp); 605 } 606 607 if ((st.st_mode & S_IFCHR) == 0) 608 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 609 610 li->li_datum.ld_dev = st.st_rdev; 611 break; 612 default: 613 usage(); 614 } 615 } 616 617 if (empty) 618 usage(); 619 } 620