Merge from vendor branch OPENSSH:
[dragonfly.git] / usr.bin / pkill / pkill.c
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.7 2005/02/15 20:31:29 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 *, int);
113 void    grepact(struct kinfo_proc *, int);
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 *, int);
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(&reg, *argv, REG_EXTENDED)) != 0) {
255                         regerror(rv, &reg, 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(&reg, mstr, 1, &regmatch, 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, &reg, buf, sizeof(buf));
289                                 errx(STATUS_ERROR, "regexec(): %s", buf);
290                         }
291                 }
292
293                 regfree(&reg);
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, j = 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, j++);
410         }
411         
412         if (pgrep)
413                 putchar('\n');
414         
415         exit(rv ? STATUS_MATCH : STATUS_NOMATCH);
416 }
417
418 void
419 usage(void)
420 {
421         const char *ustr;
422
423         if (pgrep)
424                 ustr = "[-flnvx] [-d delim]";
425         else
426                 ustr = "[-signal] [-fnvx]";
427
428         fprintf(stderr,
429                 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n"
430                 "             [-t tty] [-u euid] pattern ...\n", getprogname(),
431                 ustr);
432
433         exit(STATUS_ERROR);
434 }
435
436 void
437 killact(struct kinfo_proc *kp, int dummy)
438 {
439         dummy = 0; /* unused */
440
441         if (kill(kp->kp_proc.p_pid, signum) == -1)
442                 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_proc.p_pid);
443 }
444
445 void
446 grepact(struct kinfo_proc *kp, int printdelim)
447 {
448         char **argv;
449
450         if (printdelim)
451                 fputs(delim, stdout);
452
453         if (longfmt && matchargs) {
454                 if ((argv = kvm_getargv(kd, kp, 0)) == NULL)
455                         return;
456
457                 printf("%d ", (int)kp->kp_proc.p_pid);
458                 for (; *argv != NULL; argv++) {
459                         printf("%s", *argv);
460                         if (argv[1] != NULL)
461                                 putchar(' ');
462                 }
463         } else if (longfmt)
464                 printf("%d %s", (int)kp->kp_proc.p_pid, kp->kp_thread.td_comm);
465         else
466                 printf("%d", (int)kp->kp_proc.p_pid);
467
468 }
469
470 int
471 parse_pid(const char *string, char **p, struct list *li, pid_t default_pid)
472 {
473         long l;
474
475         l = strtol(string, p, 0);
476         li->li_datum.ld_pid = (l == 0 ? default_pid : (pid_t)l);
477         return(**p == '\0');
478 }
479
480 void
481 makelist(struct listhead *head, enum listtype type, char *src)
482 {
483         struct list *li;
484         struct passwd *pw;
485         struct group *gr;
486         struct stat st;
487         const char *sp, *tty_name;
488         char *p, buf[MAXPATHLEN];
489         int empty;
490
491         empty = 1;
492
493         while ((sp = strsep(&src, ",")) != NULL) {
494                 if (*sp == '\0')
495                         usage();
496
497                 if ((li = malloc(sizeof(*li))) == NULL)
498                         errx(STATUS_ERROR, "memory allocation failure");
499                 SLIST_INSERT_HEAD(head, li, li_chain);
500                 empty = 0;
501
502                 switch (type) {
503                 case LT_PPID:
504                         if (!parse_pid(sp, &p, li, (pid_t)0))
505                                 usage();
506                         break;
507                 case LT_PGRP:
508                         if (!parse_pid(sp, &p, li, getpgrp()))
509                                 usage();
510                         break;
511                 case LT_SID:
512                         if (!parse_pid(sp, &p, li, getsid(mypid)))
513                                 usage();
514                         break;
515                 case LT_USER:
516                         li->li_datum.ld_uid = (uid_t)strtol(sp, &p, 0);
517                         if (*p != '\0') {
518                                 if ((pw = getpwnam(sp)) == NULL) {
519                                         errx(STATUS_BADUSAGE,
520                                              "unknown user `%s'", optarg);
521                                 }
522                                 li->li_datum.ld_uid = pw->pw_uid;
523                         }
524                         break;
525                 case LT_GROUP:
526                         li->li_datum.ld_gid = (gid_t)strtol(sp, &p, 0);
527                         if (*p != '\0') {
528                                 if ((gr = getgrnam(sp)) == NULL) {
529                                         errx(STATUS_BADUSAGE,
530                                              "unknown group `%s'", optarg);
531                                 }
532                                 li->li_datum.ld_gid = gr->gr_gid;
533                         }
534                         break;
535                 case LT_TTY:
536                         if (strcmp(sp, "-") == 0) {
537                                 li->li_datum.ld_dev = NODEV;
538                                 break;
539                         } else if (strcmp(sp, "co") == 0)
540                                 tty_name = "console";
541                         else if (strncmp(sp, "tty", 3) == 0)
542                                 tty_name = sp;
543                         else
544                                 tty_name = NULL;
545
546                         if (tty_name == NULL)
547                                 snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
548                         else
549                                 snprintf(buf, sizeof(buf), "/dev/%s", tty_name);
550
551                         if (stat(buf, &st) < 0) {
552                                 if (errno == ENOENT)
553                                         errx(STATUS_BADUSAGE,
554                                             "no such tty: `%s'", sp);
555                                 err(STATUS_ERROR, "stat(%s)", sp);
556                         }
557
558                         if ((st.st_mode & S_IFCHR) == 0)
559                                 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
560
561                         li->li_datum.ld_dev = st.st_rdev;
562                         break;
563                 default:
564                         usage();
565                 }
566         }
567
568         if (empty)
569                 usage();
570 }