1 /* $NetBSD: pkill.c,v 1.25 2009/04/13 00:12:16 lukem 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: pkill.c,v 1.25 2009/04/13 00:12:16 lukem Exp $"); 35 #endif /* !lint */ 36 37 #include <sys/types.h> 38 #include <sys/param.h> 39 #include <sys/sysctl.h> 40 #include <sys/proc.h> 41 #include <sys/queue.h> 42 #include <sys/stat.h> 43 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <limits.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <signal.h> 50 #include <regex.h> 51 #include <ctype.h> 52 #include <kvm.h> 53 #include <err.h> 54 #include <pwd.h> 55 #include <grp.h> 56 #include <errno.h> 57 #include <paths.h> 58 59 #define STATUS_MATCH 0 60 #define STATUS_NOMATCH 1 61 #define STATUS_BADUSAGE 2 62 #define STATUS_ERROR 3 63 64 enum listtype { 65 LT_GENERIC, 66 LT_USER, 67 LT_GROUP, 68 LT_TTY, 69 LT_PGRP, 70 LT_SID 71 }; 72 73 struct list { 74 SLIST_ENTRY(list) li_chain; 75 long li_number; 76 }; 77 78 SLIST_HEAD(listhead, list); 79 80 static struct kinfo_proc2 *plist; 81 static char *selected; 82 static const char *delim = "\n"; 83 static int nproc; 84 static int pgrep; 85 static int signum = SIGTERM; 86 static int newest; 87 static int inverse; 88 static int longfmt; 89 static int matchargs; 90 static int fullmatch; 91 static int cflags = REG_EXTENDED; 92 static kvm_t *kd; 93 static pid_t mypid; 94 95 static struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 96 static struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 97 static struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 98 static struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 99 static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 100 static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 101 static struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 102 103 int main(int, char **); 104 static void usage(void) __dead; 105 static int killact(const struct kinfo_proc2 *); 106 static int grepact(const struct kinfo_proc2 *); 107 static void makelist(struct listhead *, enum listtype, char *); 108 109 int 110 main(int argc, char **argv) 111 { 112 char buf[_POSIX2_LINE_MAX], **pargv, *q; 113 int i, j, ch, bestidx, rv, criteria; 114 int (*action)(const struct kinfo_proc2 *); 115 const struct kinfo_proc2 *kp; 116 struct list *li; 117 const char *mstr, *p; 118 u_int32_t bestsec, bestusec; 119 regex_t reg; 120 regmatch_t regmatch; 121 122 setprogname(argv[0]); 123 124 if (strcmp(getprogname(), "pgrep") == 0) { 125 action = grepact; 126 pgrep = 1; 127 } else { 128 action = killact; 129 p = argv[1]; 130 131 if (argc > 1 && p[0] == '-') { 132 p++; 133 i = (int)strtol(p, &q, 10); 134 if (*q == '\0') { 135 signum = i; 136 argv++; 137 argc--; 138 } else { 139 if (strncasecmp(p, "sig", 3) == 0) 140 p += 3; 141 for (i = 1; i < NSIG; i++) 142 if (strcasecmp(sys_signame[i], p) == 0) 143 break; 144 if (i != NSIG) { 145 signum = i; 146 argv++; 147 argc--; 148 } 149 } 150 } 151 } 152 153 criteria = 0; 154 155 while ((ch = getopt(argc, argv, "G:P:U:d:fg:ilns:t:u:vx")) != -1) 156 switch (ch) { 157 case 'G': 158 makelist(&rgidlist, LT_GROUP, optarg); 159 criteria = 1; 160 break; 161 case 'P': 162 makelist(&ppidlist, LT_GENERIC, optarg); 163 criteria = 1; 164 break; 165 case 'U': 166 makelist(&ruidlist, LT_USER, optarg); 167 criteria = 1; 168 break; 169 case 'd': 170 if (!pgrep) 171 usage(); 172 delim = optarg; 173 break; 174 case 'f': 175 matchargs = 1; 176 break; 177 case 'g': 178 makelist(&pgrplist, LT_PGRP, optarg); 179 criteria = 1; 180 break; 181 case 'i': 182 cflags |= REG_ICASE; 183 break; 184 case 'l': 185 longfmt = 1; 186 break; 187 case 'n': 188 newest = 1; 189 criteria = 1; 190 break; 191 case 's': 192 makelist(&sidlist, LT_SID, optarg); 193 criteria = 1; 194 break; 195 case 't': 196 makelist(&tdevlist, LT_TTY, optarg); 197 criteria = 1; 198 break; 199 case 'u': 200 makelist(&euidlist, LT_USER, optarg); 201 criteria = 1; 202 break; 203 case 'v': 204 inverse = 1; 205 break; 206 case 'x': 207 fullmatch = 1; 208 break; 209 default: 210 usage(); 211 /* NOTREACHED */ 212 } 213 214 argc -= optind; 215 argv += optind; 216 if (argc != 0) 217 criteria = 1; 218 if (!criteria) 219 usage(); 220 221 mypid = getpid(); 222 223 /* 224 * Retrieve the list of running processes from the kernel. 225 */ 226 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf); 227 if (kd == NULL) 228 errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf); 229 230 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc); 231 if (plist == NULL) 232 errx(STATUS_ERROR, "Cannot get process list (%s)", 233 kvm_geterr(kd)); 234 235 /* 236 * Allocate memory which will be used to keep track of the 237 * selection. 238 */ 239 if ((selected = calloc((size_t)1, (size_t)nproc)) == NULL) 240 err(STATUS_ERROR, "Cannot allocate memory for %d processes", 241 nproc); 242 243 /* 244 * Refine the selection. 245 */ 246 for (; *argv != NULL; argv++) { 247 if ((rv = regcomp(®, *argv, cflags)) != 0) { 248 (void)regerror(rv, ®, buf, sizeof(buf)); 249 errx(STATUS_BADUSAGE, 250 "Cannot compile regular expression `%s' (%s)", 251 *argv, buf); 252 } 253 254 for (i = 0, kp = plist; i < nproc; i++, kp++) { 255 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 256 continue; 257 258 if (matchargs) { 259 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL) 260 continue; 261 262 j = 0; 263 while (j < (int)sizeof(buf) && *pargv != NULL) { 264 j += snprintf(buf + j, sizeof(buf) - j, 265 pargv[1] != NULL ? "%s " : "%s", 266 pargv[0]); 267 pargv++; 268 } 269 270 mstr = buf; 271 } else 272 mstr = kp->p_comm; 273 274 rv = regexec(®, mstr, 1, ®match, 0); 275 if (rv == 0) { 276 if (fullmatch) { 277 if (regmatch.rm_so == 0 && 278 regmatch.rm_eo == (regoff_t)strlen(mstr)) 279 selected[i] = 1; 280 } else 281 selected[i] = 1; 282 } else if (rv != REG_NOMATCH) { 283 (void)regerror(rv, ®, buf, sizeof(buf)); 284 errx(STATUS_ERROR, 285 "Regular expression evaluation error (%s)", 286 buf); 287 } 288 } 289 290 regfree(®); 291 } 292 293 for (i = 0, kp = plist; i < nproc; i++, kp++) { 294 if ((kp->p_flag & P_SYSTEM) != 0) 295 continue; 296 297 SLIST_FOREACH(li, &ruidlist, li_chain) 298 if (kp->p_ruid == (uid_t)li->li_number) 299 break; 300 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 301 selected[i] = 0; 302 continue; 303 } 304 305 SLIST_FOREACH(li, &rgidlist, li_chain) 306 if (kp->p_rgid == (gid_t)li->li_number) 307 break; 308 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 309 selected[i] = 0; 310 continue; 311 } 312 313 SLIST_FOREACH(li, &euidlist, li_chain) 314 if (kp->p_uid == (uid_t)li->li_number) 315 break; 316 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 317 selected[i] = 0; 318 continue; 319 } 320 321 SLIST_FOREACH(li, &ppidlist, li_chain) 322 if ((uid_t)kp->p_ppid == (uid_t)li->li_number) 323 break; 324 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 325 selected[i] = 0; 326 continue; 327 } 328 329 SLIST_FOREACH(li, &pgrplist, li_chain) 330 if (kp->p__pgid == (pid_t)li->li_number) 331 break; 332 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 333 selected[i] = 0; 334 continue; 335 } 336 337 SLIST_FOREACH(li, &tdevlist, li_chain) { 338 if (li->li_number == -1 && 339 (kp->p_flag & P_CONTROLT) == 0) 340 break; 341 if (kp->p_tdev == (uid_t)li->li_number) 342 break; 343 } 344 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 345 selected[i] = 0; 346 continue; 347 } 348 349 SLIST_FOREACH(li, &sidlist, li_chain) 350 if (kp->p_sid == (pid_t)li->li_number) 351 break; 352 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 353 selected[i] = 0; 354 continue; 355 } 356 357 if (argc == 0) 358 selected[i] = 1; 359 } 360 361 if (newest) { 362 bestsec = 0; 363 bestusec = 0; 364 bestidx = -1; 365 366 for (i = 0, kp = plist; i < nproc; i++, kp++) { 367 if (!selected[i]) 368 continue; 369 370 if (kp->p_ustart_sec > bestsec || 371 (kp->p_ustart_sec == bestsec 372 && kp->p_ustart_usec > bestusec)) { 373 bestsec = kp->p_ustart_sec; 374 bestusec = kp->p_ustart_usec; 375 bestidx = i; 376 } 377 } 378 379 (void)memset(selected, 0, (size_t)nproc); 380 if (bestidx != -1) 381 selected[bestidx] = 1; 382 } 383 384 /* 385 * Take the appropriate action for each matched process, if any. 386 */ 387 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 388 if (kp->p_pid == mypid) 389 continue; 390 if (selected[i]) { 391 if (inverse) 392 continue; 393 } else if (!inverse) 394 continue; 395 396 if ((kp->p_flag & P_SYSTEM) != 0) 397 continue; 398 399 rv |= (*action)(kp); 400 } 401 402 return rv ? STATUS_MATCH : STATUS_NOMATCH; 403 } 404 405 static void 406 usage(void) 407 { 408 const char *ustr; 409 410 if (pgrep) 411 ustr = "[-filnvx] [-d delim]"; 412 else 413 ustr = "[-signal] [-filnvx]"; 414 415 (void)fprintf(stderr, 416 "Usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] [-t tty]\n" 417 " [-U uid] [-u euid] pattern ...\n", getprogname(), 418 ustr); 419 420 exit(STATUS_BADUSAGE); 421 } 422 423 static int 424 killact(const struct kinfo_proc2 *kp) 425 { 426 if (longfmt) 427 grepact(kp); 428 if (kill(kp->p_pid, signum) == -1) { 429 430 /* 431 * Check for ESRCH, which indicates that the process 432 * disappeared between us matching it and us 433 * signalling it; don't issue a warning about it. 434 */ 435 if (errno != ESRCH) 436 warn("signalling pid %d", (int)kp->p_pid); 437 438 /* 439 * Return 0 to indicate that the process should not be 440 * considered a match, since we didn't actually get to 441 * signal it. 442 */ 443 return 0; 444 } 445 446 return 1; 447 } 448 449 static int 450 grepact(const struct kinfo_proc2 *kp) 451 { 452 char **argv; 453 454 if (longfmt && matchargs) { 455 456 /* 457 * If kvm_getargv2() failed the process has probably 458 * disappeared. Return 0 to indicate that the process 459 * should not be considered a match, since we are no 460 * longer in a position to output it as a match. 461 */ 462 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL) 463 return 0; 464 465 (void)printf("%d ", (int)kp->p_pid); 466 for (; *argv != NULL; argv++) { 467 (void)printf("%s", *argv); 468 if (argv[1] != NULL) 469 (void)putchar(' '); 470 } 471 } else if (longfmt) 472 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm); 473 else 474 (void)printf("%d", (int)kp->p_pid); 475 476 (void)printf("%s", delim); 477 478 return 1; 479 } 480 481 static void 482 makelist(struct listhead *head, enum listtype type, char *src) 483 { 484 struct list *li; 485 struct passwd *pw; 486 struct group *gr; 487 struct stat st; 488 char *sp, *ep, buf[MAXPATHLEN]; 489 const char *p; 490 int empty; 491 const char *prefix = _PATH_DEV; 492 493 empty = 1; 494 495 while ((sp = strsep(&src, ",")) != NULL) { 496 if (*sp == '\0') 497 usage(); 498 499 if ((li = malloc(sizeof(*li))) == NULL) 500 err(STATUS_ERROR, "Cannot allocate %zd bytes", 501 sizeof(*li)); 502 SLIST_INSERT_HEAD(head, li, li_chain); 503 empty = 0; 504 505 li->li_number = (uid_t)strtol(sp, &ep, 0); 506 if (*ep == '\0' && type != LT_TTY) { 507 switch (type) { 508 case LT_PGRP: 509 if (li->li_number == 0) 510 li->li_number = getpgrp(); 511 break; 512 case LT_SID: 513 if (li->li_number == 0) 514 li->li_number = getsid(mypid); 515 break; 516 default: 517 break; 518 } 519 continue; 520 } 521 522 switch (type) { 523 case LT_USER: 524 if ((pw = getpwnam(sp)) == NULL) 525 errx(STATUS_BADUSAGE, "Unknown user `%s'", 526 sp); 527 li->li_number = pw->pw_uid; 528 break; 529 case LT_GROUP: 530 if ((gr = getgrnam(sp)) == NULL) 531 errx(STATUS_BADUSAGE, "Unknown group `%s'", 532 sp); 533 li->li_number = gr->gr_gid; 534 break; 535 case LT_TTY: 536 p = sp; 537 if (*sp == '/') 538 prefix = ""; 539 else if (strcmp(sp, "-") == 0) { 540 li->li_number = -1; 541 break; 542 } else if (strcmp(sp, "co") == 0) 543 p = "console"; 544 else if (strncmp(sp, "tty", 3) == 0) 545 /* all set */; 546 else if (strncmp(sp, "pts/", 4) == 0) 547 /* all set */; 548 else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0')) 549 prefix = _PATH_TTY; 550 else 551 prefix = _PATH_DEV_PTS; 552 553 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p); 554 555 if (stat(buf, &st) == -1) { 556 if (errno == ENOENT) 557 errx(STATUS_BADUSAGE, 558 "No such tty: `%s'", buf); 559 err(STATUS_ERROR, "Cannot access `%s'", buf); 560 } 561 562 if ((st.st_mode & S_IFCHR) == 0) 563 errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf); 564 565 li->li_number = st.st_rdev; 566 break; 567 default: 568 usage(); 569 } 570 } 571 572 if (empty) 573 usage(); 574 } 575