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.2 2004/07/28 02:51:06 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_GENERIC, 70 LT_USER, 71 LT_GROUP, 72 LT_TTY, 73 LT_PGRP, 74 LT_SID 75 }; 76 77 struct list { 78 SLIST_ENTRY(list) li_chain; 79 long li_number; 80 }; 81 82 SLIST_HEAD(listhead, list); 83 84 struct kinfo_proc *plist; 85 char *selected; 86 char *delim = "\n"; 87 int nproc; 88 int pgrep; 89 int signum = SIGTERM; 90 int newest; 91 int inverse; 92 int longfmt; 93 int matchargs; 94 int fullmatch; 95 kvm_t *kd; 96 pid_t mypid; 97 98 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 99 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 100 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 101 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 102 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 103 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 104 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 105 106 int main(int, char **); 107 void usage(void); 108 void killact(struct kinfo_proc *); 109 void grepact(struct kinfo_proc *); 110 void makelist(struct listhead *, enum listtype, char *); 111 112 int 113 main(int argc, char **argv) 114 { 115 extern char *optarg; 116 extern int optind; 117 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 118 int i, j, ch, bestidx, rv, criteria; 119 void (*action)(struct kinfo_proc *); 120 struct kinfo_proc *kp; 121 struct list *li; 122 u_int32_t bestsec, bestusec; 123 regex_t reg; 124 regmatch_t regmatch; 125 const char *kvmf = _PATH_DEVNULL; 126 127 if (strcmp(getprogname(), "pgrep") == 0) { 128 action = grepact; 129 pgrep = 1; 130 } else { 131 action = killact; 132 p = argv[1]; 133 134 if (argc > 1 && p[0] == '-') { 135 p++; 136 i = (int)strtol(p, &q, 10); 137 if (*q == '\0') { 138 signum = i; 139 argv++; 140 argc--; 141 } else { 142 if (strncasecmp(p, "sig", 3) == 0) 143 p += 3; 144 for (i = 1; i < NSIG; i++) 145 if (strcasecmp(sys_signame[i], p) == 0) 146 break; 147 if (i != NSIG) { 148 signum = i; 149 argv++; 150 argc--; 151 } 152 } 153 } 154 } 155 156 criteria = 0; 157 158 while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1) 159 switch (ch) { 160 case 'G': 161 makelist(&rgidlist, LT_GROUP, optarg); 162 criteria = 1; 163 break; 164 case 'P': 165 makelist(&ppidlist, LT_GENERIC, optarg); 166 criteria = 1; 167 break; 168 case 'U': 169 makelist(&ruidlist, LT_USER, optarg); 170 criteria = 1; 171 break; 172 case 'd': 173 if (!pgrep) 174 usage(); 175 delim = optarg; 176 break; 177 case 'f': 178 matchargs = 1; 179 break; 180 case 'g': 181 makelist(&pgrplist, LT_PGRP, optarg); 182 criteria = 1; 183 break; 184 case 'l': 185 if (!pgrep) 186 usage(); 187 longfmt = 1; 188 break; 189 case 'n': 190 newest = 1; 191 criteria = 1; 192 break; 193 case 's': 194 makelist(&sidlist, LT_SID, optarg); 195 criteria = 1; 196 break; 197 case 't': 198 makelist(&tdevlist, LT_TTY, optarg); 199 criteria = 1; 200 break; 201 case 'u': 202 makelist(&euidlist, LT_USER, optarg); 203 criteria = 1; 204 break; 205 case 'v': 206 inverse = 1; 207 break; 208 case 'x': 209 fullmatch = 1; 210 break; 211 default: 212 usage(); 213 /* NOTREACHED */ 214 } 215 216 argc -= optind; 217 argv += optind; 218 if (argc != 0) 219 criteria = 1; 220 if (!criteria) 221 usage(); 222 223 mypid = getpid(); 224 225 /* 226 * Retrieve the list of running processes from the kernel. 227 */ 228 kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf); 229 if (kd == NULL) 230 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 231 232 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 233 if (plist == NULL) 234 errx(STATUS_ERROR, "cannot list processes"); 235 236 /* 237 * Allocate memory which will be used to keep track of the 238 * selection. 239 */ 240 if ((selected = malloc(nproc)) == NULL) 241 errx(STATUS_ERROR, "memory allocation failure"); 242 memset(selected, 0, nproc); 243 244 /* 245 * Refine the selection. 246 */ 247 for (; *argv != NULL; argv++) { 248 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 249 regerror(rv, ®, buf, sizeof(buf)); 250 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 251 } 252 253 for (i = 0, kp = plist; i < nproc; i++, kp++) { 254 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0 || kp->kp_proc.p_pid == mypid) 255 continue; 256 257 if (matchargs) { 258 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 259 continue; 260 261 j = 0; 262 while (j < sizeof(buf) && *pargv != NULL) { 263 j += snprintf(buf + j, sizeof(buf) - j, 264 pargv[1] != NULL ? "%s " : "%s", 265 pargv[0]); 266 pargv++; 267 } 268 269 mstr = buf; 270 } else 271 mstr = kp->kp_thread.td_comm; 272 273 rv = regexec(®, mstr, 1, ®match, 0); 274 if (rv == 0) { 275 if (fullmatch) { 276 if (regmatch.rm_so == 0 && 277 regmatch.rm_eo == strlen(mstr)) 278 selected[i] = 1; 279 } else 280 selected[i] = 1; 281 } else if (rv != REG_NOMATCH) { 282 regerror(rv, ®, buf, sizeof(buf)); 283 errx(STATUS_ERROR, "regexec(): %s", buf); 284 } 285 } 286 287 regfree(®); 288 } 289 290 for (i = 0, kp = plist; i < nproc; i++, kp++) { 291 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 292 continue; 293 294 SLIST_FOREACH(li, &ruidlist, li_chain) 295 if (kp->kp_eproc.e_ucred.cr_ruid == (uid_t)li->li_number) 296 break; 297 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 298 selected[i] = 0; 299 continue; 300 } 301 302 SLIST_FOREACH(li, &rgidlist, li_chain) 303 if (kp->kp_eproc.e_ucred.cr_rgid == (gid_t)li->li_number) 304 break; 305 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 306 selected[i] = 0; 307 continue; 308 } 309 310 SLIST_FOREACH(li, &euidlist, li_chain) 311 if (kp->kp_eproc.e_ucred.cr_uid == (uid_t)li->li_number) 312 break; 313 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 314 selected[i] = 0; 315 continue; 316 } 317 318 SLIST_FOREACH(li, &ppidlist, li_chain) 319 if (kp->kp_eproc.e_ppid == (uid_t)li->li_number) 320 break; 321 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 322 selected[i] = 0; 323 continue; 324 } 325 326 SLIST_FOREACH(li, &pgrplist, li_chain) 327 if (kp->kp_eproc.e_pgid == (uid_t)li->li_number) 328 break; 329 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 330 selected[i] = 0; 331 continue; 332 } 333 334 SLIST_FOREACH(li, &tdevlist, li_chain) { 335 if (li->li_number == -1 && 336 (kp->kp_proc.p_flag & P_CONTROLT) == 0) 337 break; 338 if (kp->kp_eproc.e_tdev == (uid_t)li->li_number) 339 break; 340 } 341 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 342 selected[i] = 0; 343 continue; 344 } 345 346 SLIST_FOREACH(li, &sidlist, li_chain) 347 if (kp->kp_eproc.e_sess->s_sid == (uid_t)li->li_number) 348 break; 349 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 350 selected[i] = 0; 351 continue; 352 } 353 354 if (argc == 0) 355 selected[i] = 1; 356 } 357 358 if (newest) { 359 bestsec = 0; 360 bestusec = 0; 361 bestidx = -1; 362 363 for (i = 0, kp = plist; i < nproc; i++, kp++) { 364 if (!selected[i]) 365 continue; 366 367 if (kp->kp_thread.td_start.tv_sec > bestsec || 368 (kp->kp_thread.td_start.tv_sec == bestsec 369 && kp->kp_thread.td_start.tv_usec > bestusec)) { 370 bestsec = kp->kp_thread.td_start.tv_sec; 371 bestusec = kp->kp_thread.td_start.tv_usec; 372 bestidx = i; 373 } 374 } 375 376 memset(selected, 0, nproc); 377 if (bestidx != -1) 378 selected[bestidx] = 1; 379 } 380 381 /* 382 * Take the appropriate action for each matched process, if any. 383 */ 384 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 385 if (kp->kp_proc.p_pid == mypid) 386 continue; 387 if (selected[i]) { 388 if (inverse) 389 continue; 390 } else if (!inverse) 391 continue; 392 393 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 394 continue; 395 396 rv = 1; 397 (*action)(kp); 398 } 399 400 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 401 } 402 403 void 404 usage(void) 405 { 406 const char *ustr; 407 408 if (pgrep) 409 ustr = "[-flnvx] [-d delim]"; 410 else 411 ustr = "[-signal] [-fnvx]"; 412 413 fprintf(stderr, 414 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 415 " [-t tty] [-u euid] pattern ...\n", getprogname(), 416 ustr); 417 418 exit(STATUS_ERROR); 419 } 420 421 void 422 killact(struct kinfo_proc *kp) 423 { 424 425 if (kill(kp->kp_proc.p_pid, signum) == -1) 426 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_proc.p_pid); 427 } 428 429 void 430 grepact(struct kinfo_proc *kp) 431 { 432 char **argv; 433 434 if (longfmt && matchargs) { 435 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 436 return; 437 438 printf("%d ", (int)kp->kp_proc.p_pid); 439 for (; *argv != NULL; argv++) { 440 printf("%s", *argv); 441 if (argv[1] != NULL) 442 putchar(' '); 443 } 444 } else if (longfmt) 445 printf("%d %s", (int)kp->kp_proc.p_pid, kp->kp_thread.td_comm); 446 else 447 printf("%d", (int)kp->kp_proc.p_pid); 448 449 printf("%s", delim); 450 } 451 452 void 453 makelist(struct listhead *head, enum listtype type, char *src) 454 { 455 struct list *li; 456 struct passwd *pw; 457 struct group *gr; 458 struct stat st; 459 char *sp, *p, buf[MAXPATHLEN]; 460 int empty; 461 462 empty = 1; 463 464 while ((sp = strsep(&src, ",")) != NULL) { 465 if (*sp == '\0') 466 usage(); 467 468 if ((li = malloc(sizeof(*li))) == NULL) 469 errx(STATUS_ERROR, "memory allocation failure"); 470 SLIST_INSERT_HEAD(head, li, li_chain); 471 empty = 0; 472 473 li->li_number = (uid_t)strtol(sp, &p, 0); 474 if (*p == '\0') { 475 switch (type) { 476 case LT_PGRP: 477 if (li->li_number == 0) 478 li->li_number = getpgrp(); 479 break; 480 case LT_SID: 481 if (li->li_number == 0) 482 li->li_number = getsid(mypid); 483 break; 484 case LT_TTY: 485 usage(); 486 default: 487 break; 488 } 489 continue; 490 } 491 492 switch (type) { 493 case LT_USER: 494 if ((pw = getpwnam(sp)) == NULL) 495 errx(STATUS_BADUSAGE, "unknown user `%s'", 496 optarg); 497 li->li_number = pw->pw_uid; 498 break; 499 case LT_GROUP: 500 if ((gr = getgrnam(sp)) == NULL) 501 errx(STATUS_BADUSAGE, "unknown group `%s'", 502 optarg); 503 li->li_number = gr->gr_gid; 504 break; 505 case LT_TTY: 506 if (strcmp(sp, "-") == 0) { 507 li->li_number = -1; 508 break; 509 } else if (strcmp(sp, "co") == 0) 510 p = "console"; 511 else if (strncmp(sp, "tty", 3) == 0) 512 p = sp; 513 else 514 p = NULL; 515 516 if (p == NULL) 517 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 518 else 519 snprintf(buf, sizeof(buf), "/dev/%s", p); 520 521 if (stat(buf, &st) < 0) { 522 if (errno == ENOENT) 523 errx(STATUS_BADUSAGE, 524 "no such tty: `%s'", sp); 525 err(STATUS_ERROR, "stat(%s)", sp); 526 } 527 528 if ((st.st_mode & S_IFCHR) == 0) 529 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 530 531 li->li_number = st.st_rdev; 532 break; 533 default: 534 usage(); 535 }; 536 } 537 538 if (empty) 539 usage(); 540 } 541