1 /* $OpenBSD: pkill.c,v 1.28 2012/07/10 12:48:08 halex Exp $ */ 2 /* $NetBSD: pkill.c,v 1.5 2002/10/27 11:49:34 kleink Exp $ */ 3 4 /*- 5 * Copyright (c) 2002 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Andrew Doran. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/sysctl.h> 36 #include <sys/proc.h> 37 #include <sys/queue.h> 38 #include <sys/stat.h> 39 #include <sys/socket.h> 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <stdint.h> 44 #include <limits.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <signal.h> 48 #include <regex.h> 49 #include <ctype.h> 50 #include <kvm.h> 51 #include <err.h> 52 #include <pwd.h> 53 #include <grp.h> 54 #include <errno.h> 55 56 #define STATUS_MATCH 0 57 #define STATUS_NOMATCH 1 58 #define STATUS_BADUSAGE 2 59 #define STATUS_ERROR 3 60 61 enum listtype { 62 LT_GENERIC, 63 LT_USER, 64 LT_GROUP, 65 LT_TTY, 66 LT_PGRP, 67 LT_SID, 68 LT_RTABLE 69 }; 70 71 struct list { 72 SLIST_ENTRY(list) li_chain; 73 long li_number; 74 }; 75 76 SLIST_HEAD(listhead, list); 77 78 struct kinfo_proc *plist; 79 char *selected; 80 char *delim = "\n"; 81 int nproc; 82 int pgrep; 83 int signum = SIGTERM; 84 int newest; 85 int oldest; 86 int inverse; 87 int longfmt; 88 int matchargs; 89 int fullmatch; 90 kvm_t *kd; 91 pid_t mypid; 92 93 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 94 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 95 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 96 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 97 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 98 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 99 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 100 struct listhead rtablist = SLIST_HEAD_INITIALIZER(list); 101 102 int main(int, char **); 103 void usage(void); 104 int killact(struct kinfo_proc *, int); 105 int grepact(struct kinfo_proc *, int); 106 void makelist(struct listhead *, enum listtype, char *); 107 108 extern char *__progname; 109 110 int 111 main(int argc, char **argv) 112 { 113 extern char *optarg; 114 extern int optind; 115 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 116 int i, j, ch, bestidx, rv, criteria; 117 int (*action)(struct kinfo_proc *, int); 118 struct kinfo_proc *kp; 119 struct list *li; 120 u_int32_t bestsec, bestusec; 121 regex_t reg; 122 regmatch_t regmatch; 123 124 if (strcmp(__progname, "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:T:U:d:fg:lnos: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 'T': 166 makelist(&rtablist, LT_RTABLE, optarg); 167 criteria = 1; 168 break; 169 case 'U': 170 makelist(&ruidlist, LT_USER, optarg); 171 criteria = 1; 172 break; 173 case 'd': 174 if (!pgrep) 175 usage(); 176 delim = optarg; 177 break; 178 case 'f': 179 matchargs = 1; 180 break; 181 case 'g': 182 makelist(&pgrplist, LT_PGRP, optarg); 183 criteria = 1; 184 break; 185 case 'l': 186 longfmt = 1; 187 break; 188 case 'n': 189 newest = 1; 190 criteria = 1; 191 break; 192 case 'o': 193 oldest = 1; 194 criteria = 1; 195 break; 196 case 's': 197 makelist(&sidlist, LT_SID, optarg); 198 criteria = 1; 199 break; 200 case 't': 201 makelist(&tdevlist, LT_TTY, optarg); 202 criteria = 1; 203 break; 204 case 'u': 205 makelist(&euidlist, LT_USER, optarg); 206 criteria = 1; 207 break; 208 case 'v': 209 inverse = 1; 210 break; 211 case 'x': 212 fullmatch = 1; 213 break; 214 default: 215 usage(); 216 /* NOTREACHED */ 217 } 218 219 argc -= optind; 220 argv += optind; 221 if (argc != 0) 222 criteria = 1; 223 if (!criteria || (newest && oldest)) 224 usage(); 225 226 mypid = getpid(); 227 228 /* 229 * Retrieve the list of running processes from the kernel. 230 */ 231 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf); 232 if (kd == NULL) 233 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 234 235 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc); 236 if (plist == NULL) 237 errx(STATUS_ERROR, "kvm_getprocs() failed"); 238 239 /* 240 * Allocate memory which will be used to keep track of the 241 * selection. 242 */ 243 if ((selected = malloc(nproc)) == NULL) 244 errx(STATUS_ERROR, "memory allocation failure"); 245 memset(selected, 0, nproc); 246 247 /* 248 * Refine the selection. 249 */ 250 for (; *argv != NULL; argv++) { 251 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 252 regerror(rv, ®, buf, sizeof(buf)); 253 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 254 } 255 256 for (i = 0, kp = plist; i < nproc; i++, kp++) { 257 if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 || 258 kp->p_pid == mypid) 259 continue; 260 261 if (matchargs) { 262 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 263 continue; 264 265 j = 0; 266 while (j < sizeof(buf) && *pargv != NULL) { 267 int ret; 268 269 ret = snprintf(buf + j, sizeof(buf) - j, 270 pargv[1] != NULL ? "%s " : "%s", 271 pargv[0]); 272 if (ret >= sizeof(buf) - j) 273 j += sizeof(buf) - j - 1; 274 else if (ret > 0) 275 j += ret; 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 regerror(rv, ®, buf, sizeof(buf)); 293 errx(STATUS_ERROR, "regexec(): %s", 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 | P_THREAD)) != 0 || 302 kp->p_pid == mypid) 303 continue; 304 305 SLIST_FOREACH(li, &ruidlist, li_chain) 306 if (kp->p_ruid == (uid_t)li->li_number) 307 break; 308 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 309 selected[i] = 0; 310 continue; 311 } 312 313 SLIST_FOREACH(li, &rgidlist, li_chain) 314 if (kp->p_rgid == (gid_t)li->li_number) 315 break; 316 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 317 selected[i] = 0; 318 continue; 319 } 320 321 SLIST_FOREACH(li, &euidlist, li_chain) 322 if (kp->p_uid == (uid_t)li->li_number) 323 break; 324 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 325 selected[i] = 0; 326 continue; 327 } 328 329 SLIST_FOREACH(li, &ppidlist, li_chain) 330 if (kp->p_ppid == (uid_t)li->li_number) 331 break; 332 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 333 selected[i] = 0; 334 continue; 335 } 336 337 SLIST_FOREACH(li, &pgrplist, li_chain) 338 if (kp->p__pgid == (uid_t)li->li_number) 339 break; 340 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 341 selected[i] = 0; 342 continue; 343 } 344 345 SLIST_FOREACH(li, &tdevlist, li_chain) { 346 if (li->li_number == -1 && 347 (kp->p_flag & P_CONTROLT) == 0) 348 break; 349 if (kp->p_tdev == (uid_t)li->li_number) 350 break; 351 } 352 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 353 selected[i] = 0; 354 continue; 355 } 356 357 SLIST_FOREACH(li, &sidlist, li_chain) 358 if (kp->p_sid == (uid_t)li->li_number) 359 break; 360 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 361 selected[i] = 0; 362 continue; 363 } 364 365 SLIST_FOREACH(li, &rtablist, li_chain) 366 if (kp->p_rtableid == (u_int32_t)li->li_number) 367 break; 368 if (SLIST_FIRST(&rtablist) != NULL && li == NULL) { 369 selected[i] = 0; 370 continue; 371 } 372 373 if (argc == 0) 374 selected[i] = 1; 375 } 376 377 if (newest || oldest) { 378 bestidx = -1; 379 380 if (newest) 381 bestsec = bestusec = 0; 382 else 383 bestsec = bestusec = UINT32_MAX; 384 385 for (i = 0, kp = plist; i < nproc; i++, kp++) { 386 if (!selected[i]) 387 continue; 388 389 if ((newest && (kp->p_ustart_sec > bestsec || 390 (kp->p_ustart_sec == bestsec 391 && kp->p_ustart_usec > bestusec))) 392 || (oldest && (kp->p_ustart_sec < bestsec || 393 (kp->p_ustart_sec == bestsec 394 && kp->p_ustart_usec < bestusec)))) { 395 396 bestsec = kp->p_ustart_sec; 397 bestusec = kp->p_ustart_usec; 398 bestidx = i; 399 } 400 } 401 402 memset(selected, 0, nproc); 403 if (bestidx != -1) 404 selected[bestidx] = 1; 405 } 406 407 /* 408 * Take the appropriate action for each matched process, if any. 409 */ 410 rv = STATUS_NOMATCH; 411 for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) { 412 if ((kp->p_flag & (P_SYSTEM | P_THREAD)) != 0 || 413 kp->p_pid == mypid) 414 continue; 415 if (selected[i] == inverse) 416 continue; 417 418 if ((*action)(kp, j++) == -1) 419 rv = STATUS_ERROR; 420 else if (rv != STATUS_ERROR) 421 rv = STATUS_MATCH; 422 } 423 if (pgrep && j) 424 putchar('\n'); 425 426 exit(rv); 427 } 428 429 void 430 usage(void) 431 { 432 const char *ustr; 433 434 if (pgrep) 435 ustr = "[-flnovx] [-d delim]"; 436 else 437 ustr = "[-signal] [-flnovx]"; 438 439 fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid]" 440 "\n\t[-T rtable] [-t tty] [-U uid] [-u euid] [pattern ...]\n", 441 __progname, ustr); 442 443 exit(STATUS_BADUSAGE); 444 } 445 446 int 447 killact(struct kinfo_proc *kp, int dummy) 448 { 449 if (longfmt) 450 printf("%d %s\n", (int)kp->p_pid, kp->p_comm); 451 452 if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) { 453 warn("signalling pid %d", (int)kp->p_pid); 454 return (-1); 455 } 456 return (0); 457 } 458 459 int 460 grepact(struct kinfo_proc *kp, int printdelim) 461 { 462 char **argv; 463 464 if (printdelim) 465 fputs(delim, stdout); 466 if (longfmt && matchargs) { 467 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 468 return (-1); 469 470 printf("%d ", (int)kp->p_pid); 471 for (; *argv != NULL; argv++) { 472 printf("%s", *argv); 473 if (argv[1] != NULL) 474 putchar(' '); 475 } 476 } else if (longfmt) 477 printf("%d %s", (int)kp->p_pid, kp->p_comm); 478 else 479 printf("%d", (int)kp->p_pid); 480 481 return (0); 482 } 483 484 void 485 makelist(struct listhead *head, enum listtype type, char *src) 486 { 487 struct list *li; 488 struct passwd *pw; 489 struct group *gr; 490 struct stat st; 491 char *sp, *p, buf[MAXPATHLEN]; 492 int empty; 493 494 empty = 1; 495 496 while ((sp = strsep(&src, ",")) != NULL) { 497 if (*sp == '\0') 498 usage(); 499 500 if ((li = malloc(sizeof(*li))) == NULL) 501 errx(STATUS_ERROR, "memory allocation failure"); 502 SLIST_INSERT_HEAD(head, li, li_chain); 503 empty = 0; 504 505 li->li_number = strtol(sp, &p, 0); 506 if (*p == '\0') { 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 case LT_RTABLE: 517 if (li->li_number < 0 || 518 li->li_number > RT_TABLEID_MAX) 519 errx(STATUS_BADUSAGE, 520 "rtable out of range"); 521 break; 522 case LT_TTY: 523 usage(); 524 default: 525 break; 526 } 527 continue; 528 } 529 530 switch (type) { 531 case LT_USER: 532 if ((pw = getpwnam(sp)) == NULL) 533 errx(STATUS_BADUSAGE, "unknown user `%s'", 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'", sp); 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 p = "console"; 547 else if (strncmp(sp, "tty", 3) == 0) 548 p = sp; 549 else 550 p = NULL; 551 552 if (p == NULL) 553 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 554 else 555 snprintf(buf, sizeof(buf), "/dev/%s", p); 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 (!S_ISCHR(st.st_mode)) 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