1 /* $NetBSD: pkill.c,v 1.22 2007/12/15 19:44:52 perry 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.22 2007/12/15 19:44:52 perry 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) __dead; 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 longfmt = 1; 193 break; 194 case 'n': 195 newest = 1; 196 criteria = 1; 197 break; 198 case 's': 199 makelist(&sidlist, LT_SID, optarg); 200 criteria = 1; 201 break; 202 case 't': 203 makelist(&tdevlist, LT_TTY, optarg); 204 criteria = 1; 205 break; 206 case 'u': 207 makelist(&euidlist, LT_USER, optarg); 208 criteria = 1; 209 break; 210 case 'v': 211 inverse = 1; 212 break; 213 case 'x': 214 fullmatch = 1; 215 break; 216 default: 217 usage(); 218 /* NOTREACHED */ 219 } 220 221 argc -= optind; 222 argv += optind; 223 if (argc != 0) 224 criteria = 1; 225 if (!criteria) 226 usage(); 227 228 mypid = getpid(); 229 230 /* 231 * Retrieve the list of running processes from the kernel. 232 */ 233 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf); 234 if (kd == NULL) 235 errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf); 236 237 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc); 238 if (plist == NULL) 239 errx(STATUS_ERROR, "Cannot get process list (%s)", 240 kvm_geterr(kd)); 241 242 /* 243 * Allocate memory which will be used to keep track of the 244 * selection. 245 */ 246 if ((selected = calloc((size_t)1, (size_t)nproc)) == NULL) 247 err(STATUS_ERROR, "Cannot allocate memory for %d processes", 248 nproc); 249 250 /* 251 * Refine the selection. 252 */ 253 for (; *argv != NULL; argv++) { 254 if ((rv = regcomp(®, *argv, cflags)) != 0) { 255 (void)regerror(rv, ®, buf, sizeof(buf)); 256 errx(STATUS_BADUSAGE, 257 "Cannot compile regular expression `%s' (%s)", 258 *argv, buf); 259 } 260 261 for (i = 0, kp = plist; i < nproc; i++, kp++) { 262 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 263 continue; 264 265 if (matchargs) { 266 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL) 267 continue; 268 269 j = 0; 270 while (j < sizeof(buf) && *pargv != NULL) { 271 j += snprintf(buf + j, sizeof(buf) - j, 272 pargv[1] != NULL ? "%s " : "%s", 273 pargv[0]); 274 pargv++; 275 } 276 277 mstr = buf; 278 } else 279 mstr = kp->p_comm; 280 281 rv = regexec(®, mstr, 1, ®match, 0); 282 if (rv == 0) { 283 if (fullmatch) { 284 if (regmatch.rm_so == 0 && 285 regmatch.rm_eo == strlen(mstr)) 286 selected[i] = 1; 287 } else 288 selected[i] = 1; 289 } else if (rv != REG_NOMATCH) { 290 (void)regerror(rv, ®, buf, sizeof(buf)); 291 errx(STATUS_ERROR, 292 "Regular expression evaluation error (%s)", 293 buf); 294 } 295 } 296 297 regfree(®); 298 } 299 300 for (i = 0, kp = plist; i < nproc; i++, kp++) { 301 if ((kp->p_flag & P_SYSTEM) != 0) 302 continue; 303 304 SLIST_FOREACH(li, &ruidlist, li_chain) 305 if (kp->p_ruid == (uid_t)li->li_number) 306 break; 307 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 308 selected[i] = 0; 309 continue; 310 } 311 312 SLIST_FOREACH(li, &rgidlist, li_chain) 313 if (kp->p_rgid == (gid_t)li->li_number) 314 break; 315 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 316 selected[i] = 0; 317 continue; 318 } 319 320 SLIST_FOREACH(li, &euidlist, li_chain) 321 if (kp->p_uid == (uid_t)li->li_number) 322 break; 323 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 324 selected[i] = 0; 325 continue; 326 } 327 328 SLIST_FOREACH(li, &ppidlist, li_chain) 329 if (kp->p_ppid == (uid_t)li->li_number) 330 break; 331 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 332 selected[i] = 0; 333 continue; 334 } 335 336 SLIST_FOREACH(li, &pgrplist, li_chain) 337 if (kp->p__pgid == (uid_t)li->li_number) 338 break; 339 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 340 selected[i] = 0; 341 continue; 342 } 343 344 SLIST_FOREACH(li, &tdevlist, li_chain) { 345 if (li->li_number == -1 && 346 (kp->p_flag & P_CONTROLT) == 0) 347 break; 348 if (kp->p_tdev == (uid_t)li->li_number) 349 break; 350 } 351 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 352 selected[i] = 0; 353 continue; 354 } 355 356 SLIST_FOREACH(li, &sidlist, li_chain) 357 if (kp->p_sid == (uid_t)li->li_number) 358 break; 359 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 360 selected[i] = 0; 361 continue; 362 } 363 364 if (argc == 0) 365 selected[i] = 1; 366 } 367 368 if (newest) { 369 bestsec = 0; 370 bestusec = 0; 371 bestidx = -1; 372 373 for (i = 0, kp = plist; i < nproc; i++, kp++) { 374 if (!selected[i]) 375 continue; 376 377 if (kp->p_ustart_sec > bestsec || 378 (kp->p_ustart_sec == bestsec 379 && kp->p_ustart_usec > bestusec)) { 380 bestsec = kp->p_ustart_sec; 381 bestusec = kp->p_ustart_usec; 382 bestidx = i; 383 } 384 } 385 386 (void)memset(selected, 0, (size_t)nproc); 387 if (bestidx != -1) 388 selected[bestidx] = 1; 389 } 390 391 /* 392 * Take the appropriate action for each matched process, if any. 393 */ 394 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 395 if (kp->p_pid == mypid) 396 continue; 397 if (selected[i]) { 398 if (inverse) 399 continue; 400 } else if (!inverse) 401 continue; 402 403 if ((kp->p_flag & P_SYSTEM) != 0) 404 continue; 405 406 rv |= (*action)(kp); 407 } 408 409 return rv ? STATUS_MATCH : STATUS_NOMATCH; 410 } 411 412 static void 413 usage(void) 414 { 415 const char *ustr; 416 417 if (pgrep) 418 ustr = "[-filnvx] [-d delim]"; 419 else 420 ustr = "[-signal] [-filnvx]"; 421 422 (void)fprintf(stderr, 423 "Usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] [-t tty]\n" 424 " [-U uid] [-u euid] pattern ...\n", getprogname(), 425 ustr); 426 427 exit(STATUS_BADUSAGE); 428 } 429 430 static int 431 killact(const struct kinfo_proc2 *kp) 432 { 433 if (longfmt) 434 grepact(kp); 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