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