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