killall.c revision 256281
1254219Scy/*- 2254219Scy * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org> 3254219Scy * Copyright (c) 2000 Paul Saab <ps@FreeBSD.org> 4254219Scy * All rights reserved. 5254219Scy * 6254219Scy * Redistribution and use in source and binary forms, with or without 7254219Scy * modification, are permitted provided that the following conditions 8254219Scy * are met: 9254219Scy * 1. Redistributions of source code must retain the above copyright 10254219Scy * notice, this list of conditions and the following disclaimer. 11254219Scy * 2. Redistributions in binary form must reproduce the above copyright 12254219Scy * notice, this list of conditions and the following disclaimer in the 13254219Scy * documentation and/or other materials provided with the distribution. 14254219Scy * 15254219Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16254219Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17254219Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18254219Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19254219Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20254219Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21254219Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22254219Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23254219Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24254219Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25254219Scy * SUCH DAMAGE. 26254219Scy */ 27254219Scy 28254219Scy#include <sys/cdefs.h> 29254219Scy__FBSDID("$FreeBSD: stable/10/usr.bin/killall/killall.c 252428 2013-06-30 20:27:31Z mjg $"); 30254219Scy 31254219Scy#include <sys/param.h> 32254219Scy#include <sys/jail.h> 33254219Scy#include <sys/stat.h> 34254219Scy#include <sys/uio.h> 35254219Scy#include <sys/user.h> 36254219Scy#include <sys/sysctl.h> 37254219Scy#include <fcntl.h> 38254219Scy#include <dirent.h> 39254219Scy#include <jail.h> 40254219Scy#include <stdio.h> 41254219Scy#include <stdlib.h> 42254219Scy#include <string.h> 43254219Scy#include <pwd.h> 44254219Scy#include <signal.h> 45254219Scy#include <regex.h> 46254219Scy#include <ctype.h> 47254219Scy#include <err.h> 48254219Scy#include <errno.h> 49254219Scy#include <unistd.h> 50254219Scy#include <locale.h> 51254219Scy 52254219Scystatic void __dead2 53254219Scyusage(void) 54254219Scy{ 55254219Scy 56254219Scy fprintf(stderr, "usage: killall [-delmsqvz] [-help] [-I] [-j jail]\n"); 57254219Scy fprintf(stderr, 58254219Scy " [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n"); 59254219Scy fprintf(stderr, "At least one option or argument to specify processes must be given.\n"); 60254219Scy exit(1); 61254219Scy} 62254219Scy 63254219Scy 64254219Scystatic void 65254219Scyprintsig(FILE *fp) 66254219Scy{ 67254219Scy const char *const * p; 68254219Scy int cnt; 69254219Scy int offset = 0; 70254219Scy 71254219Scy for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) { 72254219Scy offset += fprintf(fp, "%s ", *p); 73254219Scy if (offset >= 75 && cnt > 1) { 74254219Scy offset = 0; 75254219Scy fprintf(fp, "\n"); 76254219Scy } 77254219Scy } 78254219Scy fprintf(fp, "\n"); 79254219Scy} 80254219Scy 81254219Scystatic void 82254219Scynosig(char *name) 83254219Scy{ 84254219Scy 85254219Scy warnx("unknown signal %s; valid signals:", name); 86254219Scy printsig(stderr); 87254219Scy exit(1); 88254219Scy} 89254219Scy 90254219Scyint 91254219Scymain(int ac, char **av) 92254219Scy{ 93254219Scy struct kinfo_proc *procs, *newprocs; 94254219Scy struct stat sb; 95254219Scy struct passwd *pw; 96254219Scy regex_t rgx; 97254219Scy regmatch_t pmatch; 98254219Scy int i, j, ch; 99254219Scy char buf[256]; 100254219Scy char first; 101254219Scy char *user = NULL; 102254219Scy char *tty = NULL; 103254219Scy char *cmd = NULL; 104254219Scy int qflag = 0; 105254219Scy int vflag = 0; 106254219Scy int sflag = 0; 107254219Scy int dflag = 0; 108254219Scy int eflag = 0; 109254219Scy int Iflag = 0; 110254219Scy int jflag = 0; 111254219Scy int mflag = 0; 112254219Scy int zflag = 0; 113254219Scy uid_t uid = 0; 114254219Scy dev_t tdev = 0; 115254219Scy pid_t mypid; 116254219Scy char thiscmd[MAXCOMLEN + 1]; 117254219Scy pid_t thispid; 118254219Scy uid_t thisuid; 119254219Scy dev_t thistdev; 120254219Scy int sig = SIGTERM; 121254219Scy const char *const *p; 122254219Scy char *ep; 123254219Scy int errors = 0; 124254219Scy int jid; 125254219Scy int mib[4]; 126254219Scy size_t miblen; 127254219Scy int st, nprocs; 128254219Scy size_t size; 129254219Scy int matched; 130254219Scy int killed = 0; 131254219Scy 132254219Scy setlocale(LC_ALL, ""); 133254219Scy 134254219Scy av++; 135254219Scy ac--; 136254219Scy 137254219Scy while (ac > 0) { 138254219Scy if (strcmp(*av, "-l") == 0) { 139254219Scy printsig(stdout); 140254219Scy exit(0); 141254219Scy } 142254219Scy if (strcmp(*av, "-help") == 0) 143254219Scy usage(); 144254219Scy if (**av == '-') { 145254219Scy ++*av; 146254219Scy switch (**av) { 147254219Scy case 'I': 148254219Scy Iflag = 1; 149254219Scy break; 150254219Scy case 'j': 151254219Scy ++*av; 152254219Scy if (**av == '\0') { 153254219Scy ++av; 154254219Scy --ac; 155254219Scy } 156254219Scy jflag++; 157254219Scy if (*av == NULL) 158254219Scy errx(1, "must specify jail"); 159254219Scy jid = jail_getid(*av); 160254219Scy if (jid < 0) 161254219Scy errx(1, "%s", jail_errmsg); 162254219Scy if (jail_attach(jid) == -1) 163254219Scy err(1, "jail_attach(%d)", jid); 164254219Scy break; 165254219Scy case 'u': 166254219Scy ++*av; 167254219Scy if (**av == '\0') { 168254219Scy ++av; 169254219Scy --ac; 170254219Scy } 171254219Scy if (*av == NULL) 172254219Scy errx(1, "must specify user"); 173254219Scy user = *av; 174254219Scy break; 175254219Scy case 't': 176254219Scy ++*av; 177254219Scy if (**av == '\0') { 178254219Scy ++av; 179254219Scy --ac; 180254219Scy } 181254219Scy if (*av == NULL) 182254219Scy errx(1, "must specify tty"); 183254219Scy tty = *av; 184254219Scy break; 185254219Scy case 'c': 186254219Scy ++*av; 187254219Scy if (**av == '\0') { 188254219Scy ++av; 189254219Scy --ac; 190254219Scy } 191254219Scy if (*av == NULL) 192254219Scy errx(1, "must specify procname"); 193254219Scy cmd = *av; 194254219Scy break; 195254219Scy case 'q': 196254219Scy qflag++; 197254219Scy break; 198254219Scy case 'v': 199254219Scy vflag++; 200254219Scy break; 201254219Scy case 's': 202254219Scy sflag++; 203254219Scy break; 204254219Scy case 'd': 205254219Scy dflag++; 206254219Scy break; 207254219Scy case 'e': 208254219Scy eflag++; 209254219Scy break; 210254219Scy case 'm': 211254219Scy mflag++; 212254219Scy break; 213254219Scy case 'z': 214254219Scy zflag++; 215254219Scy break; 216254219Scy default: 217254219Scy if (isalpha((unsigned char)**av)) { 218254219Scy if (strncasecmp(*av, "SIG", 3) == 0) 219254219Scy *av += 3; 220254219Scy for (sig = NSIG, p = sys_signame + 1; 221254219Scy --sig; ++p) 222254219Scy if (strcasecmp(*p, *av) == 0) { 223254219Scy sig = p - sys_signame; 224254219Scy break; 225254219Scy } 226254219Scy if (!sig) 227254219Scy nosig(*av); 228254219Scy } else if (isdigit((unsigned char)**av)) { 229254219Scy sig = strtol(*av, &ep, 10); 230254219Scy if (!*av || *ep) 231254219Scy errx(1, "illegal signal number: %s", *av); 232254219Scy if (sig < 0 || sig >= NSIG) 233254219Scy nosig(*av); 234254219Scy } else 235254219Scy nosig(*av); 236254219Scy } 237254219Scy ++av; 238254219Scy --ac; 239254219Scy } else { 240254219Scy break; 241254219Scy } 242254219Scy } 243254219Scy 244254219Scy if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0) 245254219Scy usage(); 246254219Scy 247254219Scy if (tty) { 248254219Scy if (strncmp(tty, "/dev/", 5) == 0) 249254219Scy snprintf(buf, sizeof(buf), "%s", tty); 250254219Scy else if (strncmp(tty, "tty", 3) == 0) 251254219Scy snprintf(buf, sizeof(buf), "/dev/%s", tty); 252254219Scy else 253254219Scy snprintf(buf, sizeof(buf), "/dev/tty%s", tty); 254254219Scy if (stat(buf, &sb) < 0) 255254219Scy err(1, "stat(%s)", buf); 256254219Scy if (!S_ISCHR(sb.st_mode)) 257254219Scy errx(1, "%s: not a character device", buf); 258254219Scy tdev = sb.st_rdev; 259254219Scy if (dflag) 260254219Scy printf("ttydev:0x%x\n", tdev); 261254219Scy } 262254219Scy if (user) { 263254219Scy uid = strtol(user, &ep, 10); 264254219Scy if (*user == '\0' || *ep != '\0') { /* was it a number? */ 265254219Scy pw = getpwnam(user); 266254219Scy if (pw == NULL) 267254219Scy errx(1, "user %s does not exist", user); 268254219Scy uid = pw->pw_uid; 269254219Scy if (dflag) 270254219Scy printf("uid:%d\n", uid); 271254219Scy } 272254219Scy } else { 273254219Scy uid = getuid(); 274254219Scy if (uid != 0) { 275254219Scy pw = getpwuid(uid); 276254219Scy if (pw) 277254219Scy user = pw->pw_name; 278254219Scy if (dflag) 279254219Scy printf("uid:%d\n", uid); 280254219Scy } 281254219Scy } 282254219Scy size = 0; 283254219Scy mib[0] = CTL_KERN; 284254219Scy mib[1] = KERN_PROC; 285254219Scy 286254219Scy if (user) { 287254219Scy mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID; 288254219Scy mib[3] = uid; 289254219Scy miblen = 4; 290254219Scy } else if (tty) { 291254219Scy mib[2] = KERN_PROC_TTY; 292254219Scy mib[3] = tdev; 293254219Scy miblen = 4; 294254219Scy } else { 295254219Scy mib[2] = KERN_PROC_PROC; 296254219Scy mib[3] = 0; 297254219Scy miblen = 3; 298254219Scy } 299254219Scy 300254219Scy procs = NULL; 301254219Scy st = sysctl(mib, miblen, NULL, &size, NULL, 0); 302254219Scy do { 303254219Scy size += size / 10; 304254219Scy newprocs = realloc(procs, size); 305254219Scy if (newprocs == NULL) { 306254219Scy free(procs); 307254219Scy err(1, "could not reallocate memory"); 308254219Scy } 309254219Scy procs = newprocs; 310254219Scy st = sysctl(mib, miblen, procs, &size, NULL, 0); 311254219Scy } while (st == -1 && errno == ENOMEM); 312254219Scy if (st == -1) 313254219Scy err(1, "could not sysctl(KERN_PROC)"); 314254219Scy if (size % sizeof(struct kinfo_proc) != 0) { 315254219Scy fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n", 316254219Scy size, sizeof(struct kinfo_proc)); 317254219Scy fprintf(stderr, "userland out of sync with kernel\n"); 318254219Scy exit(1); 319254219Scy } 320254219Scy nprocs = size / sizeof(struct kinfo_proc); 321254219Scy if (dflag) 322254219Scy printf("nprocs %d\n", nprocs); 323254219Scy mypid = getpid(); 324254219Scy 325254219Scy for (i = 0; i < nprocs; i++) { 326254219Scy if (procs[i].ki_stat == SZOMB && !zflag) 327254219Scy continue; 328254219Scy thispid = procs[i].ki_pid; 329254219Scy strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd)); 330254219Scy thistdev = procs[i].ki_tdev; 331254219Scy if (eflag) 332254219Scy thisuid = procs[i].ki_uid; /* effective uid */ 333254219Scy else 334254219Scy thisuid = procs[i].ki_ruid; /* real uid */ 335254219Scy 336254219Scy if (thispid == mypid) 337254219Scy continue; 338254219Scy matched = 1; 339254219Scy if (user) { 340254219Scy if (thisuid != uid) 341254219Scy matched = 0; 342254219Scy } 343254219Scy if (tty) { 344254219Scy if (thistdev != tdev) 345254219Scy matched = 0; 346254219Scy } 347254219Scy if (cmd) { 348254219Scy if (mflag) { 349254219Scy if (regcomp(&rgx, cmd, 350254219Scy REG_EXTENDED|REG_NOSUB) != 0) { 351254219Scy mflag = 0; 352254219Scy warnx("%s: illegal regexp", cmd); 353254219Scy } 354254219Scy } 355254219Scy if (mflag) { 356254219Scy pmatch.rm_so = 0; 357254219Scy pmatch.rm_eo = strlen(thiscmd); 358254219Scy if (regexec(&rgx, thiscmd, 0, &pmatch, 359254219Scy REG_STARTEND) != 0) 360254219Scy matched = 0; 361254219Scy regfree(&rgx); 362254219Scy } else { 363254219Scy if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0) 364254219Scy matched = 0; 365254219Scy } 366254219Scy } 367254219Scy if (jflag && thispid == getpid()) 368254219Scy matched = 0; 369254219Scy if (matched == 0) 370254219Scy continue; 371254219Scy if (ac > 0) 372254219Scy matched = 0; 373254219Scy for (j = 0; j < ac; j++) { 374254219Scy if (mflag) { 375254219Scy if (regcomp(&rgx, av[j], 376254219Scy REG_EXTENDED|REG_NOSUB) != 0) { 377254219Scy mflag = 0; 378254219Scy warnx("%s: illegal regexp", av[j]); 379254219Scy } 380254219Scy } 381254219Scy if (mflag) { 382254219Scy pmatch.rm_so = 0; 383254219Scy pmatch.rm_eo = strlen(thiscmd); 384254219Scy if (regexec(&rgx, thiscmd, 0, &pmatch, 385254219Scy REG_STARTEND) == 0) 386254219Scy matched = 1; 387254219Scy regfree(&rgx); 388254219Scy } else { 389254219Scy if (strcmp(thiscmd, av[j]) == 0) 390254219Scy matched = 1; 391254219Scy } 392254219Scy if (matched) 393254219Scy break; 394254219Scy } 395254219Scy if (matched != 0 && Iflag) { 396254219Scy printf("Send signal %d to %s (pid %d uid %d)? ", 397254219Scy sig, thiscmd, thispid, thisuid); 398254219Scy fflush(stdout); 399254219Scy first = ch = getchar(); 400254219Scy while (ch != '\n' && ch != EOF) 401254219Scy ch = getchar(); 402254219Scy if (first != 'y' && first != 'Y') 403254219Scy matched = 0; 404254219Scy } 405254219Scy if (matched == 0) 406254219Scy continue; 407254219Scy if (dflag) 408254219Scy printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig, 409254219Scy thiscmd, thispid, thistdev, thisuid); 410254219Scy 411254219Scy if (vflag || sflag) 412254219Scy printf("kill -%s %d\n", sys_signame[sig], thispid); 413254219Scy 414254219Scy killed++; 415254219Scy if (!dflag && !sflag) { 416254219Scy if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) { 417254219Scy warn("warning: kill -%s %d", 418254219Scy sys_signame[sig], thispid); 419254219Scy errors = 1; 420254219Scy } 421254219Scy } 422254219Scy } 423254219Scy if (killed == 0) { 424254219Scy if (!qflag) 425254219Scy fprintf(stderr, "No matching processes %swere found\n", 426254219Scy getuid() != 0 ? "belonging to you " : ""); 427254219Scy errors = 1; 428254219Scy } 429254219Scy exit(errors); 430254219Scy} 431254219Scy