1 /* $OpenBSD: pkill.c,v 1.18 2009/10/27 23:59:41 deraadt 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 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <stdint.h> 43 #include <limits.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <signal.h> 47 #include <regex.h> 48 #include <ctype.h> 49 #include <kvm.h> 50 #include <err.h> 51 #include <pwd.h> 52 #include <grp.h> 53 #include <errno.h> 54 55 #define STATUS_MATCH 0 56 #define STATUS_NOMATCH 1 57 #define STATUS_BADUSAGE 2 58 #define STATUS_ERROR 3 59 60 enum listtype { 61 LT_GENERIC, 62 LT_USER, 63 LT_GROUP, 64 LT_TTY, 65 LT_PGRP, 66 LT_SID 67 }; 68 69 struct list { 70 SLIST_ENTRY(list) li_chain; 71 long li_number; 72 }; 73 74 SLIST_HEAD(listhead, list); 75 76 struct kinfo_proc2 *plist; 77 char *selected; 78 char *delim = "\n"; 79 int nproc; 80 int pgrep; 81 int signum = SIGTERM; 82 int newest; 83 int oldest; 84 int inverse; 85 int longfmt; 86 int matchargs; 87 int fullmatch; 88 kvm_t *kd; 89 pid_t mypid; 90 91 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 92 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 93 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 94 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 95 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 96 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 97 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 98 99 int main(int, char **); 100 void usage(void); 101 int killact(struct kinfo_proc2 *, int); 102 int grepact(struct kinfo_proc2 *, int); 103 void makelist(struct listhead *, enum listtype, char *); 104 105 extern char *__progname; 106 107 int 108 main(int argc, char **argv) 109 { 110 extern char *optarg; 111 extern int optind; 112 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 113 int i, j, ch, bestidx, rv, criteria; 114 int (*action)(struct kinfo_proc2 *, int); 115 struct kinfo_proc2 *kp; 116 struct list *li; 117 u_int32_t bestsec, bestusec; 118 regex_t reg; 119 regmatch_t regmatch; 120 121 if (strcmp(__progname, "pgrep") == 0) { 122 action = grepact; 123 pgrep = 1; 124 } else { 125 action = killact; 126 p = argv[1]; 127 128 if (argc > 1 && p[0] == '-') { 129 p++; 130 i = (int)strtol(p, &q, 10); 131 if (*q == '\0') { 132 signum = i; 133 argv++; 134 argc--; 135 } else { 136 if (strncasecmp(p, "sig", 3) == 0) 137 p += 3; 138 for (i = 1; i < NSIG; i++) 139 if (strcasecmp(sys_signame[i], p) == 0) 140 break; 141 if (i != NSIG) { 142 signum = i; 143 argv++; 144 argc--; 145 } 146 } 147 } 148 } 149 150 criteria = 0; 151 152 while ((ch = getopt(argc, argv, "G:P:U:d:fg:lnos:t:u:vx")) != -1) 153 switch (ch) { 154 case 'G': 155 makelist(&rgidlist, LT_GROUP, optarg); 156 criteria = 1; 157 break; 158 case 'P': 159 makelist(&ppidlist, LT_GENERIC, optarg); 160 criteria = 1; 161 break; 162 case 'U': 163 makelist(&ruidlist, LT_USER, optarg); 164 criteria = 1; 165 break; 166 case 'd': 167 if (!pgrep) 168 usage(); 169 delim = optarg; 170 break; 171 case 'f': 172 matchargs = 1; 173 break; 174 case 'g': 175 makelist(&pgrplist, LT_PGRP, optarg); 176 criteria = 1; 177 break; 178 case 'l': 179 if (!pgrep) 180 usage(); 181 longfmt = 1; 182 break; 183 case 'n': 184 newest = 1; 185 criteria = 1; 186 break; 187 case 'o': 188 oldest = 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 || (newest && oldest)) 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, "kvm_openfiles(): %s", buf); 229 230 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc); 231 if (plist == NULL) 232 errx(STATUS_ERROR, "kvm_getproc2() failed"); 233 234 /* 235 * Allocate memory which will be used to keep track of the 236 * selection. 237 */ 238 if ((selected = malloc(nproc)) == NULL) 239 errx(STATUS_ERROR, "memory allocation failure"); 240 memset(selected, 0, nproc); 241 242 /* 243 * Refine the selection. 244 */ 245 for (; *argv != NULL; argv++) { 246 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 247 regerror(rv, ®, buf, sizeof(buf)); 248 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 249 } 250 251 for (i = 0, kp = plist; i < nproc; i++, kp++) { 252 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 253 continue; 254 255 if (matchargs) { 256 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL) 257 continue; 258 259 j = 0; 260 while (j < sizeof(buf) && *pargv != NULL) { 261 int ret; 262 263 ret = snprintf(buf + j, sizeof(buf) - j, 264 pargv[1] != NULL ? "%s " : "%s", 265 pargv[0]); 266 if (ret >= sizeof(buf) - j) 267 j += sizeof(buf) - j - 1; 268 else if (ret > 0) 269 j += ret; 270 pargv++; 271 } 272 273 mstr = buf; 274 } else 275 mstr = kp->p_comm; 276 277 rv = regexec(®, mstr, 1, ®match, 0); 278 if (rv == 0) { 279 if (fullmatch) { 280 if (regmatch.rm_so == 0 && 281 regmatch.rm_eo == strlen(mstr)) 282 selected[i] = 1; 283 } else 284 selected[i] = 1; 285 } else if (rv != REG_NOMATCH) { 286 regerror(rv, ®, buf, sizeof(buf)); 287 errx(STATUS_ERROR, "regexec(): %s", buf); 288 } 289 } 290 291 regfree(®); 292 } 293 294 for (i = 0, kp = plist; i < nproc; i++, kp++) { 295 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 296 continue; 297 298 SLIST_FOREACH(li, &ruidlist, li_chain) 299 if (kp->p_ruid == (uid_t)li->li_number) 300 break; 301 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 302 selected[i] = 0; 303 continue; 304 } 305 306 SLIST_FOREACH(li, &rgidlist, li_chain) 307 if (kp->p_rgid == (gid_t)li->li_number) 308 break; 309 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 310 selected[i] = 0; 311 continue; 312 } 313 314 SLIST_FOREACH(li, &euidlist, li_chain) 315 if (kp->p_uid == (uid_t)li->li_number) 316 break; 317 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 318 selected[i] = 0; 319 continue; 320 } 321 322 SLIST_FOREACH(li, &ppidlist, li_chain) 323 if (kp->p_ppid == (uid_t)li->li_number) 324 break; 325 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 326 selected[i] = 0; 327 continue; 328 } 329 330 SLIST_FOREACH(li, &pgrplist, li_chain) 331 if (kp->p__pgid == (uid_t)li->li_number) 332 break; 333 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 334 selected[i] = 0; 335 continue; 336 } 337 338 SLIST_FOREACH(li, &tdevlist, li_chain) { 339 if (li->li_number == -1 && 340 (kp->p_flag & P_CONTROLT) == 0) 341 break; 342 if (kp->p_tdev == (uid_t)li->li_number) 343 break; 344 } 345 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 346 selected[i] = 0; 347 continue; 348 } 349 350 SLIST_FOREACH(li, &sidlist, li_chain) 351 if (kp->p_sid == (uid_t)li->li_number) 352 break; 353 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 354 selected[i] = 0; 355 continue; 356 } 357 358 if (argc == 0) 359 selected[i] = 1; 360 } 361 362 if (newest || oldest) { 363 bestidx = -1; 364 365 if (newest) 366 bestsec = bestusec = 0; 367 else 368 bestsec = bestusec = UINT32_MAX; 369 370 for (i = 0, kp = plist; i < nproc; i++, kp++) { 371 if (!selected[i]) 372 continue; 373 374 if ((newest && (kp->p_ustart_sec > bestsec || 375 (kp->p_ustart_sec == bestsec 376 && kp->p_ustart_usec > bestusec))) 377 || (oldest && (kp->p_ustart_sec < bestsec || 378 (kp->p_ustart_sec == bestsec 379 && kp->p_ustart_usec < bestusec)))) { 380 381 bestsec = kp->p_ustart_sec; 382 bestusec = kp->p_ustart_usec; 383 bestidx = i; 384 } 385 } 386 387 memset(selected, 0, nproc); 388 if (bestidx != -1) 389 selected[bestidx] = 1; 390 } 391 392 /* 393 * Take the appropriate action for each matched process, if any. 394 */ 395 rv = STATUS_NOMATCH; 396 for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) { 397 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid) 398 continue; 399 if (selected[i]) { 400 if (inverse) 401 continue; 402 } else if (!inverse) 403 continue; 404 405 if ((*action)(kp, j++) == -1) 406 rv = STATUS_ERROR; 407 else if (rv != STATUS_ERROR) 408 rv = STATUS_MATCH; 409 } 410 if (pgrep && j) 411 putchar('\n'); 412 413 exit(rv); 414 } 415 416 void 417 usage(void) 418 { 419 const char *ustr; 420 421 if (pgrep) 422 ustr = "[-flnovx] [-d delim]"; 423 else 424 ustr = "[-signal] [-fnovx]"; 425 426 fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] " 427 "[-t tty]\n\t[-U uid] [-u euid] [pattern ...]\n", __progname, ustr); 428 429 exit(STATUS_ERROR); 430 } 431 432 int 433 killact(struct kinfo_proc2 *kp, int dummy) 434 { 435 436 if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) { 437 warn("signalling pid %d", (int)kp->p_pid); 438 return (-1); 439 } 440 return (0); 441 } 442 443 int 444 grepact(struct kinfo_proc2 *kp, int printdelim) 445 { 446 char **argv; 447 448 if (printdelim) 449 fputs(delim, stdout); 450 if (longfmt && matchargs) { 451 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL) 452 return (-1); 453 454 printf("%d ", (int)kp->p_pid); 455 for (; *argv != NULL; argv++) { 456 printf("%s", *argv); 457 if (argv[1] != NULL) 458 putchar(' '); 459 } 460 } else if (longfmt) 461 printf("%d %s", (int)kp->p_pid, kp->p_comm); 462 else 463 printf("%d", (int)kp->p_pid); 464 465 return (0); 466 } 467 468 void 469 makelist(struct listhead *head, enum listtype type, char *src) 470 { 471 struct list *li; 472 struct passwd *pw; 473 struct group *gr; 474 struct stat st; 475 char *sp, *p, buf[MAXPATHLEN]; 476 int empty; 477 478 empty = 1; 479 480 while ((sp = strsep(&src, ",")) != NULL) { 481 if (*sp == '\0') 482 usage(); 483 484 if ((li = malloc(sizeof(*li))) == NULL) 485 errx(STATUS_ERROR, "memory allocation failure"); 486 SLIST_INSERT_HEAD(head, li, li_chain); 487 empty = 0; 488 489 li->li_number = (uid_t)strtol(sp, &p, 0); 490 if (*p == '\0') { 491 switch (type) { 492 case LT_PGRP: 493 if (li->li_number == 0) 494 li->li_number = getpgrp(); 495 break; 496 case LT_SID: 497 if (li->li_number == 0) 498 li->li_number = getsid(mypid); 499 break; 500 case LT_TTY: 501 usage(); 502 default: 503 break; 504 } 505 continue; 506 } 507 508 switch (type) { 509 case LT_USER: 510 if ((pw = getpwnam(sp)) == NULL) 511 errx(STATUS_BADUSAGE, "unknown user `%s'", sp); 512 li->li_number = pw->pw_uid; 513 break; 514 case LT_GROUP: 515 if ((gr = getgrnam(sp)) == NULL) 516 errx(STATUS_BADUSAGE, "unknown group `%s'", sp); 517 li->li_number = gr->gr_gid; 518 break; 519 case LT_TTY: 520 if (strcmp(sp, "-") == 0) { 521 li->li_number = -1; 522 break; 523 } else if (strcmp(sp, "co") == 0) 524 p = "console"; 525 else if (strncmp(sp, "tty", 3) == 0) 526 p = sp; 527 else 528 p = NULL; 529 530 if (p == NULL) 531 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 532 else 533 snprintf(buf, sizeof(buf), "/dev/%s", p); 534 535 if (stat(buf, &st) < 0) { 536 if (errno == ENOENT) 537 errx(STATUS_BADUSAGE, 538 "no such tty: `%s'", sp); 539 err(STATUS_ERROR, "stat(%s)", sp); 540 } 541 542 if (!S_ISCHR(st.st_mode)) 543 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 544 545 li->li_number = st.st_rdev; 546 break; 547 default: 548 usage(); 549 } 550 } 551 552 if (empty) 553 usage(); 554 } 555