Initial import from FreeBSD RELENG_4:
[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  */
29
30 #include <sys/cdefs.h>
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/user.h>
34 #include <sys/sysctl.h>
35 #include <fcntl.h>
36 #include <dirent.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <pwd.h>
41 #include <signal.h>
42 #include <regex.h>
43 #include <ctype.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <unistd.h>
47
48 static char     *prog;
49
50 static void __dead2
51 usage(void)
52 {
53
54         fprintf(stderr, "usage: %s [-l] [-v] [-m] [-sig] [-u user] [-t tty] [-c cmd] [cmd]...\n", prog);
55         fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
56         exit(1);
57 }
58
59 static char *
60 upper(const char *str)
61 {
62         static char buf[80];
63         char *s;
64
65         strncpy(buf, str, sizeof(buf));
66         buf[sizeof(buf) - 1] = '\0';
67         for (s = buf; *s; s++)
68                 *s = toupper(*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             vflag = 0;
113         int             sflag = 0;
114         int             dflag = 0;
115         int             mflag = 0;
116         uid_t           uid = 0;
117         dev_t           tdev = 0;
118         char            thiscmd[MAXCOMLEN + 1];
119         pid_t           thispid;
120         uid_t           thisuid;
121         dev_t           thistdev;
122         int             sig = SIGTERM;
123         const char *const *p;
124         char            *ep;
125         int             errors = 0;
126         int             mib[4];
127         size_t          miblen;
128         int             st, nprocs;
129         size_t          size;
130         int             matched;
131         int             killed = 0;
132
133         prog = av[0];
134         av++;
135         ac--;
136
137         while (ac > 0) {
138                 if (strcmp(*av, "-l") == 0) {
139                         printsig(stdout);
140                         exit(0);
141                 }
142                 if (strcmp(*av, "-help") == 0)
143                         usage();
144                 if (**av == '-') {
145                         ++*av;
146                         switch (**av) {
147                         case 'u':
148                                 ++*av;
149                                 if (**av == '\0')
150                                         ++av;
151                                 --ac;
152                                 user = *av;
153                                 break;
154                         case 't':
155                                 ++*av;
156                                 if (**av == '\0')
157                                         ++av;
158                                 --ac;
159                                 tty = *av;
160                                 break;
161                         case 'c':
162                                 ++*av;
163                                 if (**av == '\0')
164                                         ++av;
165                                 --ac;
166                                 cmd = *av;
167                                 break;
168                         case 'v':
169                                 vflag++;
170                                 break;
171                         case 's':
172                                 sflag++;
173                                 break;
174                         case 'd':
175                                 dflag++;
176                                 break;
177                         case 'm':
178                                 mflag++;
179                                 break;
180                         default:
181                                 if (isalpha(**av)) {
182                                         if (strncasecmp(*av, "sig", 3) == 0)
183                                                 *av += 3;
184                                         for (sig = NSIG, p = sys_signame + 1;
185                                              --sig; ++p)
186                                                 if (strcasecmp(*p, *av) == 0) {
187                                                         sig = p - sys_signame;
188                                                         break;
189                                                 }
190                                         if (!sig)
191                                                 nosig(*av);
192                                 } else if (isdigit(**av)) {
193                                         sig = strtol(*av, &ep, 10);
194                                         if (!*av || *ep)
195                                                 errx(1, "illegal signal number: %s", *av);
196                                         if (sig < 0 || sig > NSIG)
197                                                 nosig(*av);
198                                 } else
199                                         nosig(*av);
200                         }
201                         ++av;
202                         --ac;
203                 } else {
204                         break;
205                 }
206         }
207
208         if (user == NULL && tty == NULL && cmd == NULL && ac == 0)
209                 usage();
210
211         if (tty) {
212                 if (strncmp(tty, "/dev/", 5) == 0)
213                         snprintf(buf, sizeof(buf), "%s", tty);
214                 else if (strncmp(tty, "tty", 3) == 0)
215                         snprintf(buf, sizeof(buf), "/dev/%s", tty);
216                 else
217                         snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
218                 if (stat(buf, &sb) < 0)
219                         err(1, "stat(%s)", buf);
220                 if (!S_ISCHR(sb.st_mode))
221                         errx(1, "%s: not a character device", buf);
222                 tdev = sb.st_rdev;
223                 if (dflag)
224                         printf("ttydev:0x%x\n", tdev);
225         }
226         if (user) {
227                 pw = getpwnam(user);
228                 if (pw == NULL)
229                         errx(1, "user %s does not exist", user);
230                 uid = pw->pw_uid;
231                 if (dflag)
232                         printf("uid:%d\n", uid);
233         } else {
234                 uid = getuid();
235                 if (uid != 0) {
236                         pw = getpwuid(uid);
237                         if (pw)
238                                 user = pw->pw_name;
239                         if (dflag)
240                                 printf("uid:%d\n", uid);
241                 }
242         }
243         size = 0;
244         mib[0] = CTL_KERN;
245         mib[1] = KERN_PROC;
246         mib[2] = KERN_PROC_ALL;
247         mib[3] = 0;
248         miblen = 3;
249
250         if (user && mib[2] == KERN_PROC_ALL) {
251                 mib[2] = KERN_PROC_RUID;
252                 mib[3] = uid;
253                 miblen = 4;
254         }
255         if (tty && mib[2] == KERN_PROC_ALL) {
256                 mib[2] = KERN_PROC_TTY;
257                 mib[3] = tdev;
258                 miblen = 4;
259         }
260
261         st = sysctl(mib, miblen, NULL, &size, NULL, 0);
262         do {
263                 size += size / 10;
264                 newprocs = realloc(procs, size);
265                 if (newprocs == 0) {
266                         if (procs)
267                                 free(procs);
268                         errx(1, "could not reallocate memory");
269                 }
270                 procs = newprocs;
271                 st = sysctl(mib, miblen, procs, &size, NULL, 0);
272         } while (st == -1 && errno == ENOMEM);
273         if (st == -1)
274                 err(1, "could not sysctl(KERN_PROC)");
275         if (size % sizeof(struct kinfo_proc) != 0) {
276                 fprintf(stderr, "proc size mismatch (%d total, %d chunks)\n",
277                         size, sizeof(struct kinfo_proc));
278                 fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n");
279                 exit(1);
280         }
281         nprocs = size / sizeof(struct kinfo_proc);
282         if (dflag)
283                 printf("nprocs %d\n", nprocs);
284
285         for (i = 0; i < nprocs; i++) {
286                 thispid = procs[i].kp_proc.p_pid;
287                 strncpy(thiscmd, procs[i].kp_proc.p_comm, MAXCOMLEN);
288                 thiscmd[MAXCOMLEN] = '\0';
289                 thistdev = procs[i].kp_eproc.e_tdev;
290                 thisuid = procs[i].kp_eproc.e_pcred.p_ruid;     /* real uid */
291
292                 matched = 1;
293                 if (user) {
294                         if (thisuid != uid)
295                                 matched = 0;
296                 }
297                 if (tty) {
298                         if (thistdev != tdev)
299                                 matched = 0;
300                 }
301                 if (cmd) {
302                         if (mflag) {
303                                 if (regcomp(&rgx, cmd,
304                                     REG_EXTENDED|REG_NOSUB) != 0) {
305                                         mflag = 0;
306                                         warnx("%s: illegal regexp", cmd);
307                                 }
308                         }
309                         if (mflag) {
310                                 pmatch.rm_so = 0;
311                                 pmatch.rm_eo = strlen(thiscmd);
312                                 if (regexec(&rgx, thiscmd, 0, &pmatch,
313                                     REG_STARTEND) != 0)
314                                         matched = 0;
315                                 regfree(&rgx);
316                         } else {
317                                 if (strcmp(thiscmd, cmd) != 0)
318                                         matched = 0;
319                         }
320                 }
321                 if (matched == 0)
322                         continue;
323                 matched = 0;
324                 for (j = 0; j < ac; j++) {
325                         if (mflag) {
326                                 if (regcomp(&rgx, av[j],
327                                     REG_EXTENDED|REG_NOSUB) != 0) {
328                                         mflag = 0;
329                                         warnx("%s: illegal regexp", av[j]);
330                                 }
331                         }
332                         if (mflag) {
333                                 pmatch.rm_so = 0;
334                                 pmatch.rm_eo = strlen(thiscmd);
335                                 if (regexec(&rgx, thiscmd, 0, &pmatch,
336                                     REG_STARTEND) == 0)
337                                         matched = 1;
338                                 regfree(&rgx);
339                         } else {
340                                 if (strcmp(thiscmd, av[j]) == 0)
341                                         matched = 1;
342                         }
343                         if (matched)
344                                 break;
345                 }
346                 if (matched == 0)
347                         continue;
348                 if (dflag)
349                         printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
350                             thiscmd, thispid, thistdev, thisuid);
351
352                 if (vflag || sflag)
353                         printf("kill -%s %d\n", upper(sys_signame[sig]),
354                             thispid);
355
356                 killed++;
357                 if (!dflag && !sflag) {
358                         if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
359                                 warn("kill -%s %d", upper(sys_signame[sig]),
360                                     thispid);
361                                 errors = 1;
362                         }
363                 }
364         }
365         if (killed == 0) {
366                 fprintf(stderr, "No matching processes %swere found\n",
367                     getuid() != 0 ? "belonging to you " : "");
368                 errors = 1;
369         }
370         exit(errors);
371 }