pkill.c revision 127622
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 127622 2004-03-30 15:10:50Z 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 /* 264 * Use KERN_PROC_PROC instead of KERN_PROC_ALL, since we 265 * just want processes and not individual kernel threads. 266 */ 267 plist = kvm_getprocs(kd, KERN_PROC_PROC, 0, &nproc); 268 if (plist == NULL) 269 errx(STATUS_ERROR, "kvm_getprocs() failed"); 270 271 /* 272 * Allocate memory which will be used to keep track of the 273 * selection. 274 */ 275 if ((selected = malloc(nproc)) == NULL) 276 errx(STATUS_ERROR, "memory allocation failure"); 277 memset(selected, 0, nproc); 278 279 /* 280 * Refine the selection. 281 */ 282 for (; *argv != NULL; argv++) { 283 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 284 regerror(rv, ®, buf, sizeof(buf)); 285 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 286 } 287 288 for (i = 0, kp = plist; i < nproc; i++, kp++) { 289 if (IS_KERNPROC(kp) != 0) { 290 if (debug_opt > 0) 291 fprintf(stderr, "* Skipped %5d %3d %s\n", 292 kp->ki_pid, kp->ki_uid, kp->ki_comm); 293 continue; 294 } 295 296 if (matchargs) { 297 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 298 continue; 299 300 jsz = 0; 301 while (jsz < sizeof(buf) && *pargv != NULL) { 302 jsz += snprintf(buf + jsz, 303 sizeof(buf) - jsz, 304 pargv[1] != NULL ? "%s " : "%s", 305 pargv[0]); 306 pargv++; 307 } 308 309 mstr = buf; 310 } else 311 mstr = kp->ki_comm; 312 313 rv = regexec(®, mstr, 1, ®match, 0); 314 if (rv == 0) { 315 if (fullmatch) { 316 if (regmatch.rm_so == 0 && 317 regmatch.rm_eo == 318 (off_t)strlen(mstr)) 319 selected[i] = 1; 320 } else 321 selected[i] = 1; 322 } else if (rv != REG_NOMATCH) { 323 regerror(rv, ®, buf, sizeof(buf)); 324 errx(STATUS_ERROR, "regexec(): %s", buf); 325 } 326 if (debug_opt > 1) { 327 const char *rv_res = "NoMatch"; 328 if (selected[i]) 329 rv_res = "Matched"; 330 fprintf(stderr, "* %s %5d %3d %s\n", rv_res, 331 kp->ki_pid, kp->ki_uid, mstr); 332 } 333 } 334 335 regfree(®); 336 } 337 338 for (i = 0, kp = plist; i < nproc; i++, kp++) { 339 if (IS_KERNPROC(kp) != 0) 340 continue; 341 342 SLIST_FOREACH(li, &ruidlist, li_chain) 343 if (kp->ki_ruid == (uid_t)li->li_number) 344 break; 345 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 346 selected[i] = 0; 347 continue; 348 } 349 350 SLIST_FOREACH(li, &rgidlist, li_chain) 351 if (kp->ki_rgid == (gid_t)li->li_number) 352 break; 353 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 354 selected[i] = 0; 355 continue; 356 } 357 358 SLIST_FOREACH(li, &euidlist, li_chain) 359 if (kp->ki_uid == (uid_t)li->li_number) 360 break; 361 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 362 selected[i] = 0; 363 continue; 364 } 365 366 SLIST_FOREACH(li, &ppidlist, li_chain) 367 if (kp->ki_ppid == (pid_t)li->li_number) 368 break; 369 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 370 selected[i] = 0; 371 continue; 372 } 373 374 SLIST_FOREACH(li, &pgrplist, li_chain) 375 if (kp->ki_pgid == (pid_t)li->li_number) 376 break; 377 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 378 selected[i] = 0; 379 continue; 380 } 381 382 SLIST_FOREACH(li, &tdevlist, li_chain) { 383 if (li->li_number == -1 && 384 (kp->ki_flag & P_CONTROLT) == 0) 385 break; 386 if (kp->ki_tdev == (udev_t)li->li_number) 387 break; 388 } 389 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 390 selected[i] = 0; 391 continue; 392 } 393 394 SLIST_FOREACH(li, &sidlist, li_chain) 395 if (kp->ki_sid == (pid_t)li->li_number) 396 break; 397 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 398 selected[i] = 0; 399 continue; 400 } 401 402 if (argc == 0) 403 selected[i] = 1; 404 } 405 406 if (newest) { 407 best_tval.tv_sec = 0; 408 best_tval.tv_usec = 0; 409 bestidx = -1; 410 411 for (i = 0, kp = plist; i < nproc; i++, kp++) { 412 if (!selected[i]) 413 continue; 414 415 if (kp->ki_start.tv_sec > best_tval.tv_sec || 416 (kp->ki_start.tv_sec == best_tval.tv_sec 417 && kp->ki_start.tv_usec > best_tval.tv_usec)) { 418 best_tval.tv_sec = kp->ki_start.tv_sec; 419 best_tval.tv_usec = kp->ki_start.tv_usec; 420 bestidx = i; 421 } 422 } 423 424 memset(selected, 0, nproc); 425 if (bestidx != -1) 426 selected[bestidx] = 1; 427 } 428 429 /* 430 * Take the appropriate action for each matched process, if any. 431 */ 432 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 433 if (kp->ki_pid == mypid) 434 continue; 435 if (selected[i]) { 436 if (inverse) 437 continue; 438 } else if (!inverse) 439 continue; 440 441 if (IS_KERNPROC(kp) != 0) 442 continue; 443 444 rv = 1; 445 (*action)(kp); 446 } 447 448 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 449} 450 451void 452usage(void) 453{ 454 const char *ustr; 455 456 if (pgrep) 457 ustr = "[-flnvx] [-d delim]"; 458 else 459 ustr = "[-signal] [-fnvx]"; 460 461 fprintf(stderr, 462 "usage: %s %s [-G gid] [-M core] [-N system]\n" 463 " [-P ppid] [-U uid] [-g pgrp] [-s sid] [-t tty]\n" 464 " [-u euid] pattern ...\n", getprogname(), ustr); 465 466 exit(STATUS_ERROR); 467} 468 469void 470killact(struct kinfo_proc *kp) 471{ 472 473 if (kill(kp->ki_pid, signum) == -1) 474 err(STATUS_ERROR, "signalling pid %d", (int)kp->ki_pid); 475} 476 477void 478grepact(struct kinfo_proc *kp) 479{ 480 char **argv; 481 482 if (longfmt && matchargs) { 483 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 484 return; 485 486 printf("%d ", (int)kp->ki_pid); 487 for (; *argv != NULL; argv++) { 488 printf("%s", *argv); 489 if (argv[1] != NULL) 490 putchar(' '); 491 } 492 } else if (longfmt) 493 printf("%d %s", (int)kp->ki_pid, kp->ki_comm); 494 else 495 printf("%d", (int)kp->ki_pid); 496 497 printf("%s", delim); 498} 499 500void 501makelist(struct listhead *head, enum listtype type, char *src) 502{ 503 struct list *li; 504 struct passwd *pw; 505 struct group *gr; 506 struct stat st; 507 const char *cp; 508 char *sp, *p, buf[MAXPATHLEN]; 509 int empty; 510 511 empty = 1; 512 513 while ((sp = strsep(&src, ",")) != NULL) { 514 if (*sp == '\0') 515 usage(); 516 517 if ((li = malloc(sizeof(*li))) == NULL) 518 errx(STATUS_ERROR, "memory allocation failure"); 519 SLIST_INSERT_HEAD(head, li, li_chain); 520 empty = 0; 521 522 li->li_number = (uid_t)strtol(sp, &p, 0); 523 if (*p == '\0') { 524 switch (type) { 525 case LT_PGRP: 526 if (li->li_number == 0) 527 li->li_number = getpgrp(); 528 break; 529 case LT_SID: 530 if (li->li_number == 0) 531 li->li_number = getsid(mypid); 532 break; 533 case LT_TTY: 534 usage(); 535 default: 536 break; 537 } 538 continue; 539 } 540 541 switch (type) { 542 case LT_USER: 543 if ((pw = getpwnam(sp)) == NULL) 544 errx(STATUS_BADUSAGE, "unknown user `%s'", 545 optarg); 546 li->li_number = pw->pw_uid; 547 break; 548 case LT_GROUP: 549 if ((gr = getgrnam(sp)) == NULL) 550 errx(STATUS_BADUSAGE, "unknown group `%s'", 551 optarg); 552 li->li_number = gr->gr_gid; 553 break; 554 case LT_TTY: 555 if (strcmp(sp, "-") == 0) { 556 li->li_number = -1; 557 break; 558 } else if (strcmp(sp, "co") == 0) 559 cp = "console"; 560 else if (strncmp(sp, "tty", 3) == 0) 561 cp = sp; 562 else 563 cp = NULL; 564 565 if (cp == NULL) 566 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 567 else 568 snprintf(buf, sizeof(buf), "/dev/%s", cp); 569 570 if (stat(buf, &st) < 0) { 571 if (errno == ENOENT) 572 errx(STATUS_BADUSAGE, 573 "no such tty: `%s'", sp); 574 err(STATUS_ERROR, "stat(%s)", sp); 575 } 576 577 if ((st.st_mode & S_IFCHR) == 0) 578 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 579 580 li->li_number = st.st_rdev; 581 break; 582 default: 583 usage(); 584 }; 585 } 586 587 if (empty) 588 usage(); 589} 590