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