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