1 /* $NetBSD: pkill.c,v 1.16 2005/10/10 22:13:20 kleink 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.16 2005/10/10 22:13:20 kleink 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_BADUSAGE); 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; don't issue a warning about it. 441 */ 442 if (errno != ESRCH) 443 warn("signalling pid %d", (int)kp->p_pid); 444 445 /* 446 * Return 0 to indicate that the process should not be 447 * considered a match, since we didn't actually get to 448 * signal it. 449 */ 450 return 0; 451 } 452 453 return 1; 454 } 455 456 static int 457 grepact(const struct kinfo_proc2 *kp) 458 { 459 char **argv; 460 461 if (longfmt && matchargs) { 462 463 /* 464 * If kvm_getargv2() failed the process has probably 465 * disappeared. Return 0 to indicate that the process 466 * should not be considered a match, since we are no 467 * longer in a position to output it as a match. 468 */ 469 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL) 470 return 0; 471 472 (void)printf("%d ", (int)kp->p_pid); 473 for (; *argv != NULL; argv++) { 474 (void)printf("%s", *argv); 475 if (argv[1] != NULL) 476 (void)putchar(' '); 477 } 478 } else if (longfmt) 479 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm); 480 else 481 (void)printf("%d", (int)kp->p_pid); 482 483 (void)printf("%s", delim); 484 485 return 1; 486 } 487 488 static void 489 makelist(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 char *sp, *ep, buf[MAXPATHLEN]; 496 const char *p; 497 int empty; 498 const char *prefix = _PATH_DEV; 499 500 empty = 1; 501 502 while ((sp = strsep(&src, ",")) != NULL) { 503 if (*sp == '\0') 504 usage(); 505 506 if ((li = malloc(sizeof(*li))) == NULL) 507 err(STATUS_ERROR, "Cannot allocate %zd bytes", 508 sizeof(*li)); 509 SLIST_INSERT_HEAD(head, li, li_chain); 510 empty = 0; 511 512 li->li_number = (uid_t)strtol(sp, &ep, 0); 513 if (*ep == '\0') { 514 switch (type) { 515 case LT_PGRP: 516 if (li->li_number == 0) 517 li->li_number = getpgrp(); 518 break; 519 case LT_SID: 520 if (li->li_number == 0) 521 li->li_number = getsid(mypid); 522 break; 523 case LT_TTY: 524 usage(); 525 /*NOTREACHED*/ 526 default: 527 break; 528 } 529 continue; 530 } 531 532 switch (type) { 533 case LT_USER: 534 if ((pw = getpwnam(sp)) == NULL) 535 errx(STATUS_BADUSAGE, "Unknown user `%s'", 536 sp); 537 li->li_number = pw->pw_uid; 538 break; 539 case LT_GROUP: 540 if ((gr = getgrnam(sp)) == NULL) 541 errx(STATUS_BADUSAGE, "Unknown group `%s'", 542 sp); 543 li->li_number = gr->gr_gid; 544 break; 545 case LT_TTY: 546 if (strcmp(sp, "-") == 0) { 547 li->li_number = -1; 548 break; 549 } else if (strcmp(sp, "co") == 0) 550 p = "console"; 551 else if (strncmp(sp, "tty", 3) == 0) 552 p = sp; 553 else { 554 p = sp; 555 prefix = _PATH_TTY; 556 } 557 558 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p); 559 560 if (stat(buf, &st) == -1) { 561 if (errno == ENOENT) 562 errx(STATUS_BADUSAGE, 563 "No such tty: `%s'", sp); 564 err(STATUS_ERROR, "Cannot access `%s'", sp); 565 } 566 567 if ((st.st_mode & S_IFCHR) == 0) 568 errx(STATUS_BADUSAGE, "Not a tty: `%s'", sp); 569 570 li->li_number = st.st_rdev; 571 break; 572 default: 573 usage(); 574 } 575 } 576 577 if (empty) 578 usage(); 579 } 580