pkill.c revision 127472
1/* $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren 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 127472 2004-03-27 06:49:57Z gad $"); 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/user.h> 49 50#include <stdio.h> 51#include <stdlib.h> 52#include <limits.h> 53#include <paths.h> 54#include <string.h> 55#include <unistd.h> 56#include <signal.h> 57#include <regex.h> 58#include <ctype.h> 59#include <fcntl.h> 60#include <kvm.h> 61#include <err.h> 62#include <pwd.h> 63#include <grp.h> 64#include <errno.h> 65 66#define STATUS_MATCH 0 67#define STATUS_NOMATCH 1 68#define STATUS_BADUSAGE 2 69#define STATUS_ERROR 3 70 71/* Check for system-processes which should always be ignored. */ 72#define IS_KERNPROC(kp) ((kp)->ki_flag & P_KTHREAD) 73 74enum listtype { 75 LT_GENERIC, 76 LT_USER, 77 LT_GROUP, 78 LT_TTY, 79 LT_PGRP, 80 LT_SID 81}; 82 83struct list { 84 SLIST_ENTRY(list) li_chain; 85 long li_number; 86}; 87 88SLIST_HEAD(listhead, list); 89 90struct kinfo_proc *plist; 91char *selected; 92const char *delim = "\n"; 93int nproc; 94int pgrep; 95int signum = SIGTERM; 96int newest; 97int inverse; 98int longfmt; 99int matchargs; 100int fullmatch; 101kvm_t *kd; 102pid_t mypid; 103 104struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 105struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 106struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 107struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 108struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 109struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 110struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 111 112int main(int, char **); 113void usage(void); 114void killact(struct kinfo_proc *); 115void grepact(struct kinfo_proc *); 116void makelist(struct listhead *, enum listtype, char *); 117 118int 119main(int argc, char **argv) 120{ 121 extern char *optarg; 122 extern int optind; 123 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 124 const char *execf, *coref; 125 int debug_opt; 126 int i, ch, bestidx, rv, criteria, drop_privs; 127 size_t jsz; 128 void (*action)(struct kinfo_proc *); 129 struct kinfo_proc *kp; 130 struct list *li; 131 struct timeval best_tval; 132 regex_t reg; 133 regmatch_t regmatch; 134 135 if (strcmp(getprogname(), "pgrep") == 0) { 136 action = grepact; 137 pgrep = 1; 138 } else { 139 action = killact; 140 p = argv[1]; 141 142 if (argc > 1 && p[0] == '-') { 143 p++; 144 i = (int)strtol(p, &q, 10); 145 if (*q == '\0') { 146 signum = i; 147 argv++; 148 argc--; 149 } else { 150 if (strncasecmp(p, "sig", 3) == 0) 151 p += 3; 152 for (i = 1; i < NSIG; i++) 153 if (strcasecmp(sys_signame[i], p) == 0) 154 break; 155 if (i != NSIG) { 156 signum = i; 157 argv++; 158 argc--; 159 } 160 } 161 } 162 } 163 164 criteria = 0; 165 debug_opt = 0; 166 drop_privs = 0; 167 execf = coref = _PATH_DEVNULL; 168 169 while ((ch = getopt(argc, argv, "DG:M:N:P:U:d:fg:lns:t:u:vx")) != -1) 170 switch (ch) { 171 case 'D': 172 debug_opt++; 173 break; 174 case 'G': 175 makelist(&rgidlist, LT_GROUP, optarg); 176 criteria = 1; 177 break; 178 case 'M': 179 coref = optarg; 180 drop_privs = 1; 181 break; 182 case 'N': 183 execf = optarg; 184 drop_privs = 1; 185 break; 186 case 'P': 187 makelist(&ppidlist, LT_GENERIC, optarg); 188 criteria = 1; 189 break; 190 case 'U': 191 makelist(&ruidlist, LT_USER, optarg); 192 criteria = 1; 193 break; 194 case 'd': 195 if (!pgrep) 196 usage(); 197 delim = optarg; 198 break; 199 case 'f': 200 matchargs = 1; 201 break; 202 case 'g': 203 makelist(&pgrplist, LT_PGRP, optarg); 204 criteria = 1; 205 break; 206 case 'l': 207 if (!pgrep) 208 usage(); 209 longfmt = 1; 210 break; 211 case 'n': 212 newest = 1; 213 criteria = 1; 214 break; 215 case 's': 216 makelist(&sidlist, LT_SID, optarg); 217 criteria = 1; 218 break; 219 case 't': 220 makelist(&tdevlist, LT_TTY, optarg); 221 criteria = 1; 222 break; 223 case 'u': 224 makelist(&euidlist, LT_USER, optarg); 225 criteria = 1; 226 break; 227 case 'v': 228 inverse = 1; 229 break; 230 case 'x': 231 fullmatch = 1; 232 break; 233 default: 234 usage(); 235 /* NOTREACHED */ 236 } 237 238 argc -= optind; 239 argv += optind; 240 if (argc != 0) 241 criteria = 1; 242 if (!criteria) 243 usage(); 244 245 /* 246 * Discard privileges if not the running kernel so that bad 247 * guys can't print interesting stuff from kernel memory. 248 */ 249 if (drop_privs) { 250 setgid(getgid()); 251 setuid(getuid()); 252 } 253 254 mypid = getpid(); 255 256 /* 257 * Retrieve the list of running processes from the kernel. 258 */ 259 kd = kvm_openfiles(execf, coref, NULL, O_RDONLY, buf); 260 if (kd == NULL) 261 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 262 263 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 264 if (plist == NULL) 265 errx(STATUS_ERROR, "kvm_getprocs() failed"); 266 267 /* 268 * Allocate memory which will be used to keep track of the 269 * selection. 270 */ 271 if ((selected = malloc(nproc)) == NULL) 272 errx(STATUS_ERROR, "memory allocation failure"); 273 memset(selected, 0, nproc); 274 275 /* 276 * Refine the selection. 277 */ 278 for (; *argv != NULL; argv++) { 279 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 280 regerror(rv, ®, buf, sizeof(buf)); 281 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 282 } 283 284 for (i = 0, kp = plist; i < nproc; i++, kp++) { 285 if (IS_KERNPROC(kp) != 0) { 286 if (debug_opt > 0) 287 fprintf(stderr, "* Skipped %5d %3d %s\n", 288 kp->ki_pid, kp->ki_uid, kp->ki_comm); 289 continue; 290 } 291 292 if (matchargs) { 293 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 294 continue; 295 296 jsz = 0; 297 while (jsz < sizeof(buf) && *pargv != NULL) { 298 jsz += snprintf(buf + jsz, 299 sizeof(buf) - jsz, 300 pargv[1] != NULL ? "%s " : "%s", 301 pargv[0]); 302 pargv++; 303 } 304 305 mstr = buf; 306 } else 307 mstr = kp->ki_comm; 308 309 rv = regexec(®, mstr, 1, ®match, 0); 310 if (rv == 0) { 311 if (fullmatch) { 312 if (regmatch.rm_so == 0 && 313 regmatch.rm_eo == 314 (off_t)strlen(mstr)) 315 selected[i] = 1; 316 } else 317 selected[i] = 1; 318 } else if (rv != REG_NOMATCH) { 319 regerror(rv, ®, buf, sizeof(buf)); 320 errx(STATUS_ERROR, "regexec(): %s", buf); 321 } 322 if (debug_opt > 1) { 323 const char *rv_res = "NoMatch"; 324 if (selected[i]) 325 rv_res = "Matched"; 326 fprintf(stderr, "* %s %5d %3d %s\n", rv_res, 327 kp->ki_pid, kp->ki_uid, mstr); 328 } 329 } 330 331 regfree(®); 332 } 333 334 for (i = 0, kp = plist; i < nproc; i++, kp++) { 335 if (IS_KERNPROC(kp) != 0) 336 continue; 337 338 SLIST_FOREACH(li, &ruidlist, li_chain) 339 if (kp->ki_ruid == (uid_t)li->li_number) 340 break; 341 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 342 selected[i] = 0; 343 continue; 344 } 345 346 SLIST_FOREACH(li, &rgidlist, li_chain) 347 if (kp->ki_rgid == (gid_t)li->li_number) 348 break; 349 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 350 selected[i] = 0; 351 continue; 352 } 353 354 SLIST_FOREACH(li, &euidlist, li_chain) 355 if (kp->ki_uid == (uid_t)li->li_number) 356 break; 357 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 358 selected[i] = 0; 359 continue; 360 } 361 362 SLIST_FOREACH(li, &ppidlist, li_chain) 363 if (kp->ki_ppid == (pid_t)li->li_number) 364 break; 365 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 366 selected[i] = 0; 367 continue; 368 } 369 370 SLIST_FOREACH(li, &pgrplist, li_chain) 371 if (kp->ki_pgid == (pid_t)li->li_number) 372 break; 373 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 374 selected[i] = 0; 375 continue; 376 } 377 378 SLIST_FOREACH(li, &tdevlist, li_chain) { 379 if (li->li_number == -1 && 380 (kp->ki_flag & P_CONTROLT) == 0) 381 break; 382 if (kp->ki_tdev == (udev_t)li->li_number) 383 break; 384 } 385 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 386 selected[i] = 0; 387 continue; 388 } 389 390 SLIST_FOREACH(li, &sidlist, li_chain) 391 if (kp->ki_sid == (pid_t)li->li_number) 392 break; 393 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 394 selected[i] = 0; 395 continue; 396 } 397 398 if (argc == 0) 399 selected[i] = 1; 400 } 401 402 if (newest) { 403 best_tval.tv_sec = 0; 404 best_tval.tv_usec = 0; 405 bestidx = -1; 406 407 for (i = 0, kp = plist; i < nproc; i++, kp++) { 408 if (!selected[i]) 409 continue; 410 411 if (kp->ki_start.tv_sec > best_tval.tv_sec || 412 (kp->ki_start.tv_sec == best_tval.tv_sec 413 && kp->ki_start.tv_usec > best_tval.tv_usec)) { 414 best_tval.tv_sec = kp->ki_start.tv_sec; 415 best_tval.tv_usec = kp->ki_start.tv_usec; 416 bestidx = i; 417 } 418 } 419 420 memset(selected, 0, nproc); 421 if (bestidx != -1) 422 selected[bestidx] = 1; 423 } 424 425 /* 426 * Take the appropriate action for each matched process, if any. 427 */ 428 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 429 if (kp->ki_pid == mypid) 430 continue; 431 if (selected[i]) { 432 if (inverse) 433 continue; 434 } else if (!inverse) 435 continue; 436 437 if (IS_KERNPROC(kp) != 0) 438 continue; 439 440 rv = 1; 441 (*action)(kp); 442 } 443 444 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 445} 446 447void 448usage(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] [-M core] [-N system]\n" 459 " [-P ppid] [-U uid] [-g pgrp] [-s sid] [-t tty]\n" 460 " [-u euid] pattern ...\n", getprogname(), ustr); 461 462 exit(STATUS_ERROR); 463} 464 465void 466killact(struct kinfo_proc *kp) 467{ 468 469 if (kill(kp->ki_pid, signum) == -1) 470 err(STATUS_ERROR, "signalling pid %d", (int)kp->ki_pid); 471} 472 473void 474grepact(struct kinfo_proc *kp) 475{ 476 char **argv; 477 478 if (longfmt && matchargs) { 479 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 480 return; 481 482 printf("%d ", (int)kp->ki_pid); 483 for (; *argv != NULL; argv++) { 484 printf("%s", *argv); 485 if (argv[1] != NULL) 486 putchar(' '); 487 } 488 } else if (longfmt) 489 printf("%d %s", (int)kp->ki_pid, kp->ki_comm); 490 else 491 printf("%d", (int)kp->ki_pid); 492 493 printf("%s", delim); 494} 495 496void 497makelist(struct listhead *head, enum listtype type, char *src) 498{ 499 struct list *li; 500 struct passwd *pw; 501 struct group *gr; 502 struct stat st; 503 const char *cp; 504 char *sp, *p, buf[MAXPATHLEN]; 505 int empty; 506 507 empty = 1; 508 509 while ((sp = strsep(&src, ",")) != NULL) { 510 if (*sp == '\0') 511 usage(); 512 513 if ((li = malloc(sizeof(*li))) == NULL) 514 errx(STATUS_ERROR, "memory allocation failure"); 515 SLIST_INSERT_HEAD(head, li, li_chain); 516 empty = 0; 517 518 li->li_number = (uid_t)strtol(sp, &p, 0); 519 if (*p == '\0') { 520 switch (type) { 521 case LT_PGRP: 522 if (li->li_number == 0) 523 li->li_number = getpgrp(); 524 break; 525 case LT_SID: 526 if (li->li_number == 0) 527 li->li_number = getsid(mypid); 528 break; 529 case LT_TTY: 530 usage(); 531 default: 532 break; 533 } 534 continue; 535 } 536 537 switch (type) { 538 case LT_USER: 539 if ((pw = getpwnam(sp)) == NULL) 540 errx(STATUS_BADUSAGE, "unknown user `%s'", 541 optarg); 542 li->li_number = pw->pw_uid; 543 break; 544 case LT_GROUP: 545 if ((gr = getgrnam(sp)) == NULL) 546 errx(STATUS_BADUSAGE, "unknown group `%s'", 547 optarg); 548 li->li_number = gr->gr_gid; 549 break; 550 case LT_TTY: 551 if (strcmp(sp, "-") == 0) { 552 li->li_number = -1; 553 break; 554 } else if (strcmp(sp, "co") == 0) 555 cp = "console"; 556 else if (strncmp(sp, "tty", 3) == 0) 557 cp = sp; 558 else 559 cp = NULL; 560 561 if (cp == NULL) 562 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 563 else 564 snprintf(buf, sizeof(buf), "/dev/%s", cp); 565 566 if (stat(buf, &st) < 0) { 567 if (errno == ENOENT) 568 errx(STATUS_BADUSAGE, 569 "no such tty: `%s'", sp); 570 err(STATUS_ERROR, "stat(%s)", sp); 571 } 572 573 if ((st.st_mode & S_IFCHR) == 0) 574 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 575 576 li->li_number = st.st_rdev; 577 break; 578 default: 579 usage(); 580 }; 581 } 582 583 if (empty) 584 usage(); 585} 586