1 /* $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $ */ 2 /* $DragonFly: src/usr.bin/pkill/pkill.c,v 1.6 2005/01/06 22:37:46 cpressey 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/types.h> 41 #include <sys/param.h> 42 #include <sys/sysctl.h> 43 #include <sys/user.h> 44 #include <sys/queue.h> 45 #include <sys/stat.h> 46 #include <sys/fcntl.h> 47 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <limits.h> 51 #include <paths.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <signal.h> 55 #include <regex.h> 56 #include <ctype.h> 57 #include <kvm.h> 58 #include <err.h> 59 #include <pwd.h> 60 #include <grp.h> 61 #include <errno.h> 62 63 #define STATUS_MATCH 0 64 #define STATUS_NOMATCH 1 65 #define STATUS_BADUSAGE 2 66 #define STATUS_ERROR 3 67 68 enum listtype { 69 LT_USER, 70 LT_GROUP, 71 LT_TTY, 72 LT_PPID, 73 LT_PGRP, 74 LT_SID 75 }; 76 77 struct list { 78 SLIST_ENTRY(list) li_chain; 79 union { 80 uid_t ld_uid; 81 gid_t ld_gid; 82 pid_t ld_pid; 83 dev_t ld_dev; 84 } li_datum; 85 }; 86 87 SLIST_HEAD(listhead, list); 88 89 struct kinfo_proc *plist; 90 char *selected; 91 const char *delim = "\n"; 92 int nproc; 93 int pgrep; 94 int signum = SIGTERM; 95 int newest; 96 int inverse; 97 int longfmt; 98 int matchargs; 99 int fullmatch; 100 kvm_t *kd; 101 pid_t mypid; 102 103 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 104 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 105 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 106 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 107 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 108 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 109 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 110 111 void usage(void); 112 void killact(struct kinfo_proc *); 113 void grepact(struct kinfo_proc *); 114 int parse_pid(const char *, char **, struct list *, pid_t); 115 void makelist(struct listhead *, enum listtype, char *); 116 117 int 118 main(int argc, char **argv) 119 { 120 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 121 int i, ch, bestidx, rv, criteria; 122 unsigned int j; 123 void (*action)(struct kinfo_proc *); 124 struct kinfo_proc *kp; 125 struct list *li; 126 struct timeval best; 127 regex_t reg; 128 regmatch_t regmatch; 129 const char *kvmf = _PATH_DEVNULL; 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 } 152 if (i != NSIG) { 153 signum = i; 154 argv++; 155 argc--; 156 } 157 } 158 } 159 } 160 161 criteria = 0; 162 163 while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1) { 164 switch (ch) { 165 case 'G': 166 makelist(&rgidlist, LT_GROUP, optarg); 167 criteria = 1; 168 break; 169 case 'P': 170 makelist(&ppidlist, LT_PPID, optarg); 171 criteria = 1; 172 break; 173 case 'U': 174 makelist(&ruidlist, LT_USER, optarg); 175 criteria = 1; 176 break; 177 case 'd': 178 if (!pgrep) 179 usage(); 180 delim = optarg; 181 break; 182 case 'f': 183 matchargs = 1; 184 break; 185 case 'g': 186 makelist(&pgrplist, LT_PGRP, optarg); 187 criteria = 1; 188 break; 189 case 'l': 190 if (!pgrep) 191 usage(); 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 222 argc -= optind; 223 argv += optind; 224 if (argc != 0) 225 criteria = 1; 226 if (!criteria) 227 usage(); 228 229 mypid = getpid(); 230 231 /* 232 * Retrieve the list of running processes from the kernel. 233 */ 234 kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf); 235 if (kd == NULL) 236 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 237 238 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 239 if (plist == NULL) 240 errx(STATUS_ERROR, "cannot list processes"); 241 242 /* 243 * Allocate memory which will be used to keep track of the 244 * selection. 245 */ 246 if ((selected = malloc(nproc)) == NULL) 247 errx(STATUS_ERROR, "memory allocation failure"); 248 memset(selected, 0, nproc); 249 250 /* 251 * Refine the selection. 252 */ 253 for (; *argv != NULL; argv++) { 254 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 255 regerror(rv, ®, buf, sizeof(buf)); 256 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 257 } 258 259 for (i = 0, kp = plist; i < nproc; i++, kp++) { 260 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0 || kp->kp_proc.p_pid == mypid) 261 continue; 262 263 if (matchargs) { 264 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 265 continue; 266 267 j = 0; 268 while (j < sizeof(buf) && *pargv != NULL) { 269 j += snprintf(buf + j, sizeof(buf) - j, 270 pargv[1] != NULL ? "%s " : "%s", 271 pargv[0]); 272 pargv++; 273 } 274 275 mstr = buf; 276 } else 277 mstr = kp->kp_thread.td_comm; 278 279 rv = regexec(®, mstr, 1, ®match, 0); 280 if (rv == 0) { 281 if (fullmatch) { 282 if (regmatch.rm_so == 0 && 283 regmatch.rm_eo == strlen(mstr)) 284 selected[i] = 1; 285 } else 286 selected[i] = 1; 287 } else if (rv != REG_NOMATCH) { 288 regerror(rv, ®, buf, sizeof(buf)); 289 errx(STATUS_ERROR, "regexec(): %s", buf); 290 } 291 } 292 293 regfree(®); 294 } 295 296 for (i = 0, kp = plist; i < nproc; i++, kp++) { 297 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 298 continue; 299 300 SLIST_FOREACH(li, &ruidlist, li_chain) { 301 if (kp->kp_eproc.e_ucred.cr_ruid == li->li_datum.ld_uid) 302 break; 303 } 304 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 305 selected[i] = 0; 306 continue; 307 } 308 309 SLIST_FOREACH(li, &rgidlist, li_chain) { 310 if (kp->kp_eproc.e_ucred.cr_rgid == li->li_datum.ld_gid) 311 break; 312 } 313 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 314 selected[i] = 0; 315 continue; 316 } 317 318 SLIST_FOREACH(li, &euidlist, li_chain) { 319 if (kp->kp_eproc.e_ucred.cr_uid == li->li_datum.ld_uid) 320 break; 321 } 322 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 323 selected[i] = 0; 324 continue; 325 } 326 327 SLIST_FOREACH(li, &ppidlist, li_chain) { 328 if (kp->kp_eproc.e_ppid == li->li_datum.ld_pid) 329 break; 330 } 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->kp_eproc.e_pgid == li->li_datum.ld_pid) 338 break; 339 } 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_datum.ld_dev == NODEV && 347 (kp->kp_proc.p_flag & P_CONTROLT) == 0) 348 break; 349 if (kp->kp_eproc.e_tdev == li->li_datum.ld_dev) 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->kp_eproc.e_sess->s_sid == li->li_datum.ld_pid) 359 break; 360 } 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 best.tv_sec = 0; 372 best.tv_usec = 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->kp_thread.td_start.tv_sec > best.tv_sec || 380 (kp->kp_thread.td_start.tv_sec == best.tv_sec 381 && kp->kp_thread.td_start.tv_usec > best.tv_usec)) { 382 best.tv_sec = kp->kp_thread.td_start.tv_sec; 383 best.tv_usec = kp->kp_thread.td_start.tv_usec; 384 bestidx = i; 385 } 386 } 387 388 memset(selected, 0, 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->kp_proc.p_pid == mypid) 398 continue; 399 if (selected[i]) { 400 if (inverse) 401 continue; 402 } else if (!inverse) 403 continue; 404 405 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 406 continue; 407 408 rv = 1; 409 (*action)(kp); 410 } 411 412 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 413 } 414 415 void 416 usage(void) 417 { 418 const char *ustr; 419 420 if (pgrep) 421 ustr = "[-flnvx] [-d delim]"; 422 else 423 ustr = "[-signal] [-fnvx]"; 424 425 fprintf(stderr, 426 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 427 " [-t tty] [-u euid] pattern ...\n", getprogname(), 428 ustr); 429 430 exit(STATUS_ERROR); 431 } 432 433 void 434 killact(struct kinfo_proc *kp) 435 { 436 if (kill(kp->kp_proc.p_pid, signum) == -1) 437 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_proc.p_pid); 438 } 439 440 void 441 grepact(struct kinfo_proc *kp) 442 { 443 char **argv; 444 445 if (longfmt && matchargs) { 446 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 447 return; 448 449 printf("%d ", (int)kp->kp_proc.p_pid); 450 for (; *argv != NULL; argv++) { 451 printf("%s", *argv); 452 if (argv[1] != NULL) 453 putchar(' '); 454 } 455 } else if (longfmt) 456 printf("%d %s", (int)kp->kp_proc.p_pid, kp->kp_thread.td_comm); 457 else 458 printf("%d", (int)kp->kp_proc.p_pid); 459 460 printf("%s", delim); 461 } 462 463 int 464 parse_pid(const char *string, char **p, struct list *li, pid_t default_pid) 465 { 466 long l; 467 468 l = strtol(string, p, 0); 469 li->li_datum.ld_pid = (l == 0 ? default_pid : (pid_t)l); 470 return(**p == '\0'); 471 } 472 473 void 474 makelist(struct listhead *head, enum listtype type, char *src) 475 { 476 struct list *li; 477 struct passwd *pw; 478 struct group *gr; 479 struct stat st; 480 const char *sp, *tty_name; 481 char *p, buf[MAXPATHLEN]; 482 int empty; 483 484 empty = 1; 485 486 while ((sp = strsep(&src, ",")) != NULL) { 487 if (*sp == '\0') 488 usage(); 489 490 if ((li = malloc(sizeof(*li))) == NULL) 491 errx(STATUS_ERROR, "memory allocation failure"); 492 SLIST_INSERT_HEAD(head, li, li_chain); 493 empty = 0; 494 495 switch (type) { 496 case LT_PPID: 497 if (!parse_pid(sp, &p, li, (pid_t)0)) 498 usage(); 499 break; 500 case LT_PGRP: 501 if (!parse_pid(sp, &p, li, getpgrp())) 502 usage(); 503 break; 504 case LT_SID: 505 if (!parse_pid(sp, &p, li, getsid(mypid))) 506 usage(); 507 break; 508 case LT_USER: 509 li->li_datum.ld_uid = (uid_t)strtol(sp, &p, 0); 510 if (*p != '\0') { 511 if ((pw = getpwnam(sp)) == NULL) { 512 errx(STATUS_BADUSAGE, 513 "unknown user `%s'", optarg); 514 } 515 li->li_datum.ld_uid = pw->pw_uid; 516 } 517 break; 518 case LT_GROUP: 519 li->li_datum.ld_gid = (gid_t)strtol(sp, &p, 0); 520 if (*p != '\0') { 521 if ((gr = getgrnam(sp)) == NULL) { 522 errx(STATUS_BADUSAGE, 523 "unknown group `%s'", optarg); 524 } 525 li->li_datum.ld_gid = gr->gr_gid; 526 } 527 break; 528 case LT_TTY: 529 if (strcmp(sp, "-") == 0) { 530 li->li_datum.ld_dev = NODEV; 531 break; 532 } else if (strcmp(sp, "co") == 0) 533 tty_name = "console"; 534 else if (strncmp(sp, "tty", 3) == 0) 535 tty_name = sp; 536 else 537 tty_name = NULL; 538 539 if (tty_name == NULL) 540 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 541 else 542 snprintf(buf, sizeof(buf), "/dev/%s", tty_name); 543 544 if (stat(buf, &st) < 0) { 545 if (errno == ENOENT) 546 errx(STATUS_BADUSAGE, 547 "no such tty: `%s'", sp); 548 err(STATUS_ERROR, "stat(%s)", sp); 549 } 550 551 if ((st.st_mode & S_IFCHR) == 0) 552 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 553 554 li->li_datum.ld_dev = st.st_rdev; 555 break; 556 default: 557 usage(); 558 } 559 } 560 561 if (empty) 562 usage(); 563 } 564