9583efb4a30e19c4347392ecf91c1ba00d101250
[dragonfly.git] / usr.bin / killall / killall.c
1 /*-
2  * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
3  * Copyright (c) 2000 Paul Saab <ps@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/usr.bin/killall/killall.c,v 1.5.2.4 2001/05/19 19:22:49 phk Exp $
28  * $DragonFly: src/usr.bin/killall/killall.c,v 1.6 2004/07/16 00:52:22 hmp Exp $
29  */
30
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/user.h>
35 #include <sys/sysctl.h>
36 #include <fcntl.h>
37 #include <dirent.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <pwd.h>
42 #include <signal.h>
43 #include <regex.h>
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <unistd.h>
48
49 static char     *prog;
50
51 static void __dead2
52 usage(void)
53 {
54
55         fprintf(stderr, "usage: %s [-l] [-v] [-m] [-sig] [-u user] [-t tty] [-c cmd] [cmd]...\n", prog);
56         fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
57         exit(1);
58 }
59
60 static char *
61 upper(const char *str)
62 {
63         static char buf[80];
64         char *s;
65
66         strlcpy(buf, str, sizeof(buf));
67         for (s = buf; *s; s++)
68                 *s = toupper((unsigned char)*s);
69         return buf;
70 }
71
72
73 static void
74 printsig(FILE *fp)
75 {
76         const char      *const * p;
77         int             cnt;
78         int             offset = 0;
79
80         for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
81                 offset += fprintf(fp, "%s ", upper(*p));
82                 if (offset >= 75 && cnt > 1) {
83                         offset = 0;
84                         fprintf(fp, "\n");
85                 }
86         }
87         fprintf(fp, "\n");
88 }
89
90 static void
91 nosig(char *name)
92 {
93
94         warnx("unknown signal %s; valid signals:", name);
95         printsig(stderr);
96         exit(1);
97 }
98
99 int
100 main(int ac, char **av)
101 {
102         struct kinfo_proc *procs = NULL, *newprocs;
103         struct stat     sb;
104         struct passwd   *pw;
105         regex_t         rgx;
106         regmatch_t      pmatch;
107         int             i, j;
108         char            buf[256];
109         char            *user = NULL;
110         char            *tty = NULL;
111         char            *cmd = NULL;
112         int             qflag = 0;
113         int             vflag = 0;
114         int             sflag = 0;
115         int             dflag = 0;
116         int             mflag = 0;
117         uid_t           uid = 0;
118         dev_t           tdev = 0;
119         pid_t           mypid;
120         char            thiscmd[MAXCOMLEN + 1];
121         pid_t           thispid;
122         uid_t           thisuid;
123         dev_t           thistdev;
124         int             sig = SIGTERM;
125         const char *const *p;
126         char            *ep;
127         int             errors = 0;
128         int             mib[4];
129         size_t          miblen;
130         int             st, nprocs;
131         size_t          size;
132         int             matched;
133         int             killed = 0;
134
135         prog = av[0];
136         av++;
137         ac--;
138
139         while (ac > 0) {
140                 if (strcmp(*av, "-l") == 0) {
141                         printsig(stdout);
142                         exit(0);
143                 }
144                 if (strcmp(*av, "-help") == 0)
145                         usage();
146                 if (**av == '-') {
147                         ++*av;
148                         switch (**av) {
149                         case 'u':
150                                 ++*av;
151                                 if (**av == '\0')
152                                         ++av;
153                                 --ac;
154                                 user = *av;
155                                 break;
156                         case 't':
157                                 ++*av;
158                                 if (**av == '\0')
159                                         ++av;
160                                 --ac;
161                                 tty = *av;
162                                 break;
163                         case 'c':
164                                 ++*av;
165                                 if (**av == '\0')
166                                         ++av;
167                                 --ac;
168                                 cmd = *av;
169                                 break;
170                         case 'q':
171                                 qflag++;
172                                 break;
173                         case 'v':
174                                 vflag++;
175                                 break;
176                         case 's':
177                                 sflag++;
178                                 break;
179                         case 'd':
180                                 dflag++;
181                                 break;
182                         case 'm':
183                                 mflag++;
184                                 break;
185                         default:
186                                 if (isalpha((unsigned char)**av)) {
187                                         if (strncasecmp(*av, "sig", 3) == 0)
188                                                 *av += 3;
189                                         for (sig = NSIG, p = sys_signame + 1;
190                                              --sig; ++p)
191                                                 if (strcasecmp(*p, *av) == 0) {
192                                                         sig = p - sys_signame;
193                                                         break;
194                                                 }
195                                         if (!sig)
196                                                 nosig(*av);
197                                 } else if (isdigit((unsigned char)**av)) {
198                                         sig = strtol(*av, &ep, 10);
199                                         if (!*av || *ep)
200                                                 errx(1, "illegal signal number: %s", *av);
201                                         if (sig < 0 || sig > NSIG)
202                                                 nosig(*av);
203                                 } else
204                                         nosig(*av);
205                         }
206                         ++av;
207                         --ac;
208                 } else {
209                         break;
210                 }
211         }
212
213         if (user == NULL && tty == NULL && cmd == NULL && ac == 0)
214                 usage();
215
216         if (tty) {
217                 if (strncmp(tty, "/dev/", 5) == 0)
218                         snprintf(buf, sizeof(buf), "%s", tty);
219                 else if (strncmp(tty, "tty", 3) == 0)
220                         snprintf(buf, sizeof(buf), "/dev/%s", tty);
221                 else
222                         snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
223                 if (stat(buf, &sb) < 0)
224                         err(1, "stat(%s)", buf);
225                 if (!S_ISCHR(sb.st_mode))
226                         errx(1, "%s: not a character device", buf);
227                 tdev = sb.st_rdev;
228                 if (dflag)
229                         printf("ttydev:0x%x\n", tdev);
230         }
231         if (user) {
232                 pw = getpwnam(user);
233                 if (pw == NULL)
234                         errx(1, "user %s does not exist", user);
235                 uid = pw->pw_uid;
236                 if (dflag)
237                         printf("uid:%d\n", uid);
238         } else {
239                 uid = getuid();
240                 if (uid != 0) {
241                         pw = getpwuid(uid);
242                         if (pw)
243                                 user = pw->pw_name;
244                         if (dflag)
245                                 printf("uid:%d\n", uid);
246                 }
247         }
248         size = 0;
249         mib[0] = CTL_KERN;
250         mib[1] = KERN_PROC;
251         mib[2] = KERN_PROC_ALL;
252         mib[3] = 0;
253         miblen = 3;
254
255         if (user && mib[2] == KERN_PROC_ALL) {
256                 mib[2] = KERN_PROC_RUID;
257                 mib[3] = uid;
258                 miblen = 4;
259         }
260         if (tty && mib[2] == KERN_PROC_ALL) {
261                 mib[2] = KERN_PROC_TTY;
262                 mib[3] = tdev;
263                 miblen = 4;
264         }
265
266         st = sysctl(mib, miblen, NULL, &size, NULL, 0);
267         do {
268                 size += size / 10;
269                 newprocs = realloc(procs, size);
270                 if (newprocs == 0) {
271                         if (procs)
272                                 free(procs);
273                         errx(1, "could not reallocate memory");
274                 }
275                 procs = newprocs;
276                 st = sysctl(mib, miblen, procs, &size, NULL, 0);
277         } while (st == -1 && errno == ENOMEM);
278         if (st == -1)
279                 err(1, "could not sysctl(KERN_PROC)");
280         if (size % sizeof(struct kinfo_proc) != 0) {
281                 fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
282                         size, sizeof(struct kinfo_proc));
283                 fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n");
284                 exit(1);
285         }
286         nprocs = size / sizeof(struct kinfo_proc);
287         if (dflag)
288                 printf("nprocs %d\n", nprocs);
289         mypid = getpid();
290
291         for (i = 0; i < nprocs; i++) {
292                 thispid = procs[i].kp_proc.p_pid;
293                 strncpy(thiscmd, procs[i].kp_thread.td_comm, MAXCOMLEN);
294                 thiscmd[MAXCOMLEN] = '\0';
295                 thistdev = procs[i].kp_eproc.e_tdev;
296                 thisuid = procs[i].kp_eproc.e_ucred.cr_ruid;    /* real uid */
297
298                 if (thispid == mypid)
299                         continue;
300                 matched = 1;
301                 if ((int)procs[i].kp_proc.p_pid < 0)
302                         matched = 0;
303                 if (user) {
304                         if (thisuid != uid)
305                                 matched = 0;
306                 }
307                 if (tty) {
308                         if (thistdev != tdev)
309                                 matched = 0;
310                 }
311                 if (cmd) {
312                         if (mflag) {
313                                 if (regcomp(&rgx, cmd,
314                                     REG_EXTENDED|REG_NOSUB) != 0) {
315                                         mflag = 0;
316                                         warnx("%s: illegal regexp", cmd);
317                                 }
318                         }
319                         if (mflag) {
320                                 pmatch.rm_so = 0;
321                                 pmatch.rm_eo = strlen(thiscmd);
322                                 if (regexec(&rgx, thiscmd, 0, &pmatch,
323                                     REG_STARTEND) != 0)
324                                         matched = 0;
325                                 regfree(&rgx);
326                         } else {
327                                 if (strcmp(thiscmd, cmd) != 0)
328                                         matched = 0;
329                         }
330                 }
331                 if (matched == 0)
332                         continue;
333                 matched = 0;
334                 for (j = 0; j < ac; j++) {
335                         if (mflag) {
336                                 if (regcomp(&rgx, av[j],
337                                     REG_EXTENDED|REG_NOSUB) != 0) {
338                                         mflag = 0;
339                                         warnx("%s: illegal regexp", av[j]);
340                                 }
341                         }
342                         if (mflag) {
343                                 pmatch.rm_so = 0;
344                                 pmatch.rm_eo = strlen(thiscmd);
345                                 if (regexec(&rgx, thiscmd, 0, &pmatch,
346                                     REG_STARTEND) == 0)
347                                         matched = 1;
348                                 regfree(&rgx);
349                         } else {
350                                 if (strcmp(thiscmd, av[j]) == 0)
351                                         matched = 1;
352                         }
353                         if (matched)
354                                 break;
355                 }
356                 if (matched == 0)
357                         continue;
358                 if (dflag)
359                         printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
360                             thiscmd, thispid, thistdev, thisuid);
361
362                 if (vflag || sflag)
363                         printf("kill -%s %d\n", upper(sys_signame[sig]),
364                             thispid);
365
366                 killed++;
367                 if (!dflag && !sflag) {
368                         if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
369                                 warn("kill -%s %d", upper(sys_signame[sig]),
370                                     thispid);
371                                 errors = 1;
372                         }
373                 }
374         }
375         if (!qflag && killed == 0) {
376                 fprintf(stderr, "No matching processes %swere found\n",
377                     getuid() != 0 ? "belonging to you " : "");
378                 errors = 1;
379         }
380         exit(errors);
381 }