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