pgrep(1), pkill(1): Sync with FreeBSD to get the -F options
authorJohn Marino <draco@marino.st>
Fri, 11 Oct 2013 23:04:54 +0000 (01:04 +0200)
committerJohn Marino <draco@marino.st>
Fri, 11 Oct 2013 23:29:28 +0000 (01:29 +0200)
These functions haven't been touched since DragonFly 1.1 (2004), other
than build tweaks.  It was claimed that it would be "trivial" to add
the -F options (pid file) but I wouldn't classify it as such.  There
is a pretty big diff between the FreeBSD and DragonFly 1.1 version.  I
had make some modifications, but the functions appear to work in the
very short tests that I performed.

usr.bin/pkill/pkill.1
usr.bin/pkill/pkill.c

index 2cece0d..df5e2c5 100644 (file)
@@ -1,5 +1,7 @@
 .\"    $NetBSD: pkill.1,v 1.8 2003/02/14 15:59:18 grant Exp $
 .\"
+.\" $FreeBSD: head/bin/pkill/pkill.1 254134 2013-08-09 08:38:51Z trasz $
+.\"
 .\" Copyright (c) 2002 The NetBSD Foundation, Inc.
 .\" All rights reserved.
 .\"
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"        This product includes software developed by the NetBSD
-.\"        Foundation, Inc. and its contributors.
-.\" 4. Neither the name of The NetBSD Foundation nor the names of its
-.\"    contributors may be used to endorse or promote products derived
-.\"    from this software without specific prior written permission.
 .\"
 .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd July 27, 2004
+.Dd August 9, 2013
 .Dt PKILL 1
 .Os
 .Sh NAME
-.Nm pgrep ,
-.Nm pkill
+.Nm pgrep , pkill
 .Nd find or signal processes by name
 .Sh SYNOPSIS
 .Nm pgrep
-.Op Fl flnvx
+.Op Fl LSafilnoqvx
+.Op Fl F Ar pidfile
 .Op Fl G Ar gid
+.Op Fl M Ar core
+.Op Fl N Ar system
 .Op Fl P Ar ppid
 .Op Fl U Ar uid
+.Op Fl c Ar class
 .Op Fl d Ar delim
 .Op Fl g Ar pgrp
+.Op Fl j Ar jid
 .Op Fl s Ar sid
 .Op Fl t Ar tty
 .Op Fl u Ar euid
-.Op Fl j Ar jid
-.Op Ar pattern Op ...
+.Ar pattern ...
 .Nm pkill
-.Op Fl signal
-.Op Fl fnvx
+.Op Fl Ar signal
+.Op Fl ILafilnovx
+.Op Fl F Ar pidfile
 .Op Fl G Ar gid
+.Op Fl M Ar core
+.Op Fl N Ar system
 .Op Fl P Ar ppid
 .Op Fl U Ar uid
+.Op Fl c Ar class
 .Op Fl g Ar pgrp
+.Op Fl j Ar jid
 .Op Fl s Ar sid
 .Op Fl t Ar tty
 .Op Fl u Ar euid
-.Op Fl j Ar jid
-.Op Ar pattern Op ...
+.Ar pattern ...
 .Sh DESCRIPTION
 The
 .Nm pgrep
@@ -79,15 +81,38 @@ command searches the process table on the running system and signals all
 processes that match the criteria given on the command line.
 .Pp
 The following options are available:
-.Bl -tag -width xxxxxxxx
+.Bl -tag -width ".Fl F Ar pidfile"
+.It Fl F Ar pidfile
+Restrict matches to a process whose PID is stored in the
+.Ar pidfile
+file.
 .It Fl G Ar gid
 Restrict matches to processes with a real group ID in the comma-separated
 list
 .Ar gid .
+.It Fl I
+Request confirmation before attempting to signal each process.
+.It Fl L
+The
+.Ar pidfile
+file given for the
+.Fl F
+option must be locked with the
+.Xr flock 2
+syscall or created with
+.Xr pidfile 3 .
+.It Fl M Ar core
+Extract values associated with the name list from the specified core
+instead of the currently running system.
+.It Fl N Ar system
+Extract the name list from the specified system instead of the default,
+which is the kernel image the system has booted from.
 .It Fl P Ar ppid
 Restrict matches to processes with a parent process ID in the
 comma-separated list
 .Ar ppid .
+.It Fl S
+Search also in system processes (kernel threads).
 .It Fl U Ar uid
 Restrict matches to processes with a real user ID in the comma-separated
 list
@@ -98,6 +123,18 @@ The default is a newline.
 This option can only be used with the
 .Nm pgrep
 command.
+.It Fl a
+Include process ancestors in the match list.
+By default, the current
+.Nm pgrep
+or
+.Nm pkill
+process and all of its ancestors are excluded (unless
+.Fl v
+is used).
+.It Fl c Ar class
+Restrict matches to processes running with specified login class
+.Ar class .
 .It Fl f
 Match against full argument lists.
 The default is to match against process names.
@@ -110,28 +147,36 @@ The value zero is taken to mean the process group ID of the running
 or
 .Nm pkill
 command.
+.It Fl i
+Ignore case distinctions in both the process table and the supplied pattern.
 .It Fl j Ar jid
 Restrict matches to processes inside jails with a jail ID in the comma-separated
 list
 .Ar jid .
 The value
-.Dq Li -1
+.Dq Li any
 matches processes in any jail.
 The value
-.Dq Li 0
-matches processes not in jail.
+.Dq Li none
+matches processes not in jail.
 .It Fl l
 Long output.
-Print the process name in addition to the process ID for each matching
+For
+.Nm pgrep ,
+print the process name in addition to the process ID for each matching
 process.
 If used in conjunction with
 .Fl f ,
 print the process ID and the full argument list for each matching process.
-This option can only be used with the
-.Nm pgrep
-command.
+For
+.Nm pkill ,
+display the kill command used for each process killed.
 .It Fl n
-Match only the most recently created process, if any.
+Select only the newest (most recently started) of the matching processes.
+.It Fl o
+Select only the oldest (least recently started) of the matching processes.
+.It Fl q
+Do not write anything to standard output.
 .It Fl s Ar sid
 Restrict matches to processes with a session ID in the comma-separated
 list
@@ -146,10 +191,12 @@ Restrict matches to processes associated with a terminal in the
 comma-separated list
 .Ar tty .
 Terminal names may be of the form
-.Sq ttyxx
+.Pa tty Ns Ar xx
 or the shortened form
-.Sq xx .
-A single dash (`-') matches processes not associated with a terminal.
+.Ar xx .
+A single dash
+.Pq Ql -
+matches processes not associated with a terminal.
 .It Fl u Ar euid
 Restrict matches to processes with an effective user ID in the
 comma-separated list
@@ -162,25 +209,44 @@ Require an exact match of the process name, or argument list if
 .Fl f
 is given.
 The default is to match any substring.
-.It Fl signal
+.It Fl Ns Ar signal
 A non-negative decimal number or symbolic signal name specifying the signal
-to be sent instead of the default TERM.
+to be sent instead of the default
+.Dv TERM .
 This option is valid only when given as the first argument to
 .Nm pkill .
 .El
 .Pp
+If any
+.Ar pattern
+operands are specified, they are used as regular expressions to match
+the command name or full argument list of each process.
+If the
+.Fl f
+option is not specified, then the
+.Ar pattern
+will attempt to match the command name.
+However, presently
+.Fx
+will only keep track of the first 19 characters of the command
+name for each process.
+Attempts to match any characters after the first 19 of a command name
+will quietly fail.
+.Pp
 Note that a running
 .Nm pgrep
 or
 .Nm pkill
 process will never consider itself nor system processes (kernel threads) as
 a potential match.
-.Sh DIAGNOSTICS
+.Sh EXIT STATUS
+The
 .Nm pgrep
 and
 .Nm pkill
+utilities
 return one of the following values upon exit:
-.Bl -tag -width foo
+.Bl -tag -width indent
 .It 0
 One or more processes were matched.
 .It 1
@@ -190,23 +256,39 @@ Invalid options were specified on the command line.
 .It 3
 An internal error occurred.
 .El
+.Sh COMPATIBILITY
+Historically the option
+.Dq Fl j Li 0
+means any jail, although in other utilities such as
+.Xr ps 1
+jail ID
+.Li 0
+has the opposite meaning, not in jail.
+Therefore
+.Dq Fl j Li 0
+is deprecated, and its use is discouraged in favor of
+.Dq Fl j Li any .
 .Sh SEE ALSO
 .Xr kill 1 ,
+.Xr killall 1 ,
 .Xr ps 1 ,
+.Xr flock 2 ,
 .Xr kill 2 ,
 .Xr sigaction 2 ,
-.Xr signal 3 ,
+.Xr pidfile 3 ,
 .Xr re_format 7
+.\" Xr signal 7
 .Sh HISTORY
+The
 .Nm pkill
 and
 .Nm pgrep
-originated in
+utilities
+first appeared in
 .Nx 1.6 .
-They are modeled after utilities of the same name that appeared in Sun
+They are modelled after utilities of the same name that appeared in Sun
 Solaris 7.
 They first appeared in
-.Dx
-in version 1.1.
+.Dx 1.1.
 .Sh AUTHORS
 .An Andrew Doran Aq Mt ad@NetBSD.org
index 4ef27e7..f8ac2c7 100644 (file)
@@ -1,8 +1,8 @@
-/*     $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $  */
-/*     $DragonFly: src/usr.bin/pkill/pkill.c,v 1.9 2007/02/01 10:33:26 corecode Exp $ */
+/*     $NetBSD: pkill.c,v 1.16 2005/10/10 22:13:20 kleink Exp $        */
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the NetBSD
- *     Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -35,6 +28,8 @@
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/bin/pkill/pkill.c 256050 2013-10-04 16:08:44Z trasz $
  */
 
 #include <sys/user.h>
@@ -44,7 +39,9 @@
 #include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/fcntl.h>
+#include <sys/time.h>
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <pwd.h>
 #include <grp.h>
 #include <errno.h>
+#include <locale.h>
 
 #define        STATUS_MATCH    0
 #define        STATUS_NOMATCH  1
 #define        STATUS_BADUSAGE 2
 #define        STATUS_ERROR    3
 
+#define        MIN_PID 5
+#define        MAX_PID 99999
+
+/* Ignore system-processes (if '-S' flag is not specified) and myself. */
+#define        PSKIP(kp)       ((kp)->kp_pid == mypid ||                       \
+                        (!kthreads && ((kp)->kp_flags & P_KTHREADP) != 0))
+
 enum listtype {
-       LT_USER,                /* real or effective user:      uid_t */
-       LT_GROUP,               /* group:                       gid_t */
-       LT_TTY,                 /* tty:                         dev_t */
-       LT_PPID,                /* parent pid:                  pid_t */
-       LT_PGRP,                /* process group:               pid_t */
-       LT_SID,                 /* session id:                  pid_t */
-       LT_JID                  /* jail id:                     pid_t */
+       LT_GENERIC,
+       LT_USER,
+       LT_GROUP,
+       LT_TTY,
+       LT_PGRP,
+       LT_JID,
+       LT_SID,
+       LT_CLASS
 };
 
 struct list {
        SLIST_ENTRY(list) li_chain;
-       union {
-               uid_t   ld_uid;
-               gid_t   ld_gid;
-               pid_t   ld_pid;
-               dev_t   ld_dev;
-       } li_datum;
+       long    li_number;
+       char    *li_name;
 };
 
 SLIST_HEAD(listhead, list);
 
-struct kinfo_proc      *plist;
-char   *selected;
-const char *delim = "\n";
-int    nproc;
-int    pgrep;
-int    signum = SIGTERM;
-int    newest;
-int    inverse;
-int    longfmt;
-int    matchargs;
-int    fullmatch;
-kvm_t  *kd;
-pid_t  mypid;
-
-struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
-struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
-struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
-struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
-struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
-struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
-struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
-struct listhead jidlist = SLIST_HEAD_INITIALIZER(list);
-
-void   usage(void);
-void   killact(struct kinfo_proc *, int);
-void   grepact(struct kinfo_proc *, int);
-int    parse_pid(const char *, char **, struct list *, pid_t);
-void   makelist(struct listhead *, enum listtype, char *);
-
-/*
- * pkill - list or signal selected processes based on regular expression.
- */
+static struct kinfo_proc *plist;
+static char    *selected;
+static const char *delim = "\n";
+static int     nproc;
+static int     pgrep;
+static int     signum = SIGTERM;
+static int     newest;
+static int     oldest;
+static int     interactive;
+static int     inverse;
+static int     longfmt;
+static int     matchargs;
+static int     fullmatch;
+static int     kthreads;
+static int     cflags = REG_EXTENDED;
+static int     quiet;
+static kvm_t   *kd;
+static pid_t   mypid;
+
+static struct listhead euidlist = SLIST_HEAD_INITIALIZER(euidlist);
+static struct listhead ruidlist = SLIST_HEAD_INITIALIZER(ruidlist);
+static struct listhead rgidlist = SLIST_HEAD_INITIALIZER(rgidlist);
+static struct listhead pgrplist = SLIST_HEAD_INITIALIZER(pgrplist);
+static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(ppidlist);
+static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(tdevlist);
+static struct listhead sidlist = SLIST_HEAD_INITIALIZER(sidlist);
+static struct listhead jidlist = SLIST_HEAD_INITIALIZER(jidlist);
+static struct listhead classlist = SLIST_HEAD_INITIALIZER(classlist);
+
+static void    usage(void) __attribute__((__noreturn__));
+static int     killact(const struct kinfo_proc *);
+static int     grepact(const struct kinfo_proc *);
+static void    makelist(struct listhead *, enum listtype, char *);
+static int     takepid(const char *, int);
+
 int
 main(int argc, char **argv)
 {
-       char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q;
-       int i, ch, bestidx, rv, criteria;
-       unsigned int j;
-       void (*action)(struct kinfo_proc *, int);
+       char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q, *pidfile;
+       const char *execf, *coref;
+       int ancestors, debug_opt, did_action;
+       int i, ch, bestidx, rv, criteria, pidfromfile, pidfilelock;
+       size_t jsz;
+       int (*action)(const struct kinfo_proc *);
        struct kinfo_proc *kp;
        struct list *li;
-       struct timeval best;
+       struct timeval best_tval;
        regex_t reg;
        regmatch_t regmatch;
-       const char *kvmf = _PATH_DEVNULL;
+       pid_t pid;
+
+       setlocale(LC_ALL, "");
 
        if (strcmp(getprogname(), "pgrep") == 0) {
                action = grepact;
@@ -140,9 +149,6 @@ main(int argc, char **argv)
                action = killact;
                p = argv[1];
 
-               /*
-                * For pkill only: parse the signal (number or name) to send.
-                */
                if (argc > 1 && p[0] == '-') {
                        p++;
                        i = (int)strtol(p, &q, 10);
@@ -151,12 +157,11 @@ main(int argc, char **argv)
                                argv++;
                                argc--;
                        } else {
-                               if (strncasecmp(p, "sig", 3) == 0)
+                               if (strncasecmp(p, "SIG", 3) == 0)
                                        p += 3;
-                               for (i = 1; i < NSIG; i++) {
+                               for (i = 1; i < NSIG; i++)
                                        if (strcasecmp(sys_signame[i], p) == 0)
                                                break;
-                               }
                                if (i != NSIG) {
                                        signum = i;
                                        argv++;
@@ -166,22 +171,62 @@ main(int argc, char **argv)
                }
        }
 
+       ancestors = 0;
        criteria = 0;
-
-       while ((ch = getopt(argc, argv, "G:P:U:d:fg:j:lns:t:u:vx")) != -1) {
+       debug_opt = 0;
+       pidfile = NULL;
+       pidfilelock = 0;
+       quiet = 0;
+       execf = NULL;
+       coref = _PATH_DEVNULL;
+
+       while ((ch = getopt(argc, argv, "DF:G:ILM:N:P:SU:ac:d:fg:ij:lnoqs:t:u:vx")) != -1)
                switch (ch) {
+               case 'D':
+                       debug_opt++;
+                       break;
+               case 'F':
+                       pidfile = optarg;
+                       criteria = 1;
+                       break;
                case 'G':
                        makelist(&rgidlist, LT_GROUP, optarg);
                        criteria = 1;
                        break;
+               case 'I':
+                       if (pgrep)
+                               usage();
+                       interactive = 1;
+                       break;
+               case 'L':
+                       pidfilelock = 1;
+                       break;
+               case 'M':
+                       coref = optarg;
+                       break;
+               case 'N':
+                       execf = optarg;
+                       break;
                case 'P':
-                       makelist(&ppidlist, LT_PPID, optarg);
+                       makelist(&ppidlist, LT_GENERIC, optarg);
                        criteria = 1;
                        break;
+               case 'S':
+                       if (!pgrep)
+                               usage();
+                       kthreads = 1;
+                       break;
                case 'U':
                        makelist(&ruidlist, LT_USER, optarg);
                        criteria = 1;
                        break;
+               case 'a':
+                       ancestors++;
+                       break;
+               case 'c':
+                       makelist(&classlist, LT_CLASS, optarg);
+                       criteria = 1;
+                       break;
                case 'd':
                        if (!pgrep)
                                usage();
@@ -194,19 +239,29 @@ main(int argc, char **argv)
                        makelist(&pgrplist, LT_PGRP, optarg);
                        criteria = 1;
                        break;
+               case 'i':
+                       cflags |= REG_ICASE;
+                       break;
                case 'j':
                        makelist(&jidlist, LT_JID, optarg);
                        criteria = 1;
                        break;
                case 'l':
-                       if (!pgrep)
-                               usage();
                        longfmt = 1;
                        break;
                case 'n':
                        newest = 1;
                        criteria = 1;
                        break;
+               case 'o':
+                       oldest = 1;
+                       criteria = 1;
+                       break;
+               case 'q':
+                       if (!pgrep)
+                               usage();
+                       quiet = 1;
+                       break;
                case 's':
                        makelist(&sidlist, LT_SID, optarg);
                        criteria = 1;
@@ -229,7 +284,6 @@ main(int argc, char **argv)
                        usage();
                        /* NOTREACHED */
                }
-       }
 
        argc -= optind;
        argv += optind;
@@ -237,53 +291,72 @@ main(int argc, char **argv)
                criteria = 1;
        if (!criteria)
                usage();
+       if (newest && oldest)
+               errx(STATUS_ERROR, "Options -n and -o are mutually exclusive");
+       if (pidfile != NULL)
+               pidfromfile = takepid(pidfile, pidfilelock);
+       else {
+               if (pidfilelock) {
+                       errx(STATUS_ERROR,
+                           "Option -L doesn't make sense without -F");
+               }
+               pidfromfile = -1;
+       }
 
        mypid = getpid();
 
        /*
         * Retrieve the list of running processes from the kernel.
         */
-       kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf);
+       kd = kvm_openfiles(execf, coref, NULL, O_RDONLY, buf);
        if (kd == NULL)
-               errx(STATUS_ERROR, "kvm_openfiles(): %s", buf);
+               errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf);
 
        plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
-       if (plist == NULL)
-               errx(STATUS_ERROR, "cannot list processes");
+       if (plist == NULL) {
+               errx(STATUS_ERROR, "Cannot get process list (%s)",
+                   kvm_geterr(kd));
+       }
 
        /*
         * Allocate memory which will be used to keep track of the
         * selection.
         */
-       if ((selected = malloc(nproc)) == NULL)
-               errx(STATUS_ERROR, "memory allocation failure");
+       if ((selected = malloc(nproc)) == NULL) {
+               err(STATUS_ERROR, "Cannot allocate memory for %d processes",
+                   nproc);
+       }
        memset(selected, 0, nproc);
 
        /*
         * Refine the selection.
         */
        for (; *argv != NULL; argv++) {
-               if ((rv = regcomp(&reg, *argv, REG_EXTENDED)) != 0) {
+               if ((rv = regcomp(&reg, *argv, cflags)) != 0) {
                        regerror(rv, &reg, buf, sizeof(buf));
-                       errx(STATUS_BADUSAGE, "bad expression: %s", buf);
+                       errx(STATUS_BADUSAGE,
+                           "Cannot compile regular expression `%s' (%s)",
+                           *argv, buf);
                }
 
                for (i = 0, kp = plist; i < nproc; i++, kp++) {
-                       if ((kp->kp_flags & P_SYSTEM) != 0 || kp->kp_pid == mypid)
+                       if (PSKIP(kp)) {
+                               if (debug_opt > 0)
+                                   fprintf(stderr, "* Skipped %5d %3d %s\n",
+                                       kp->kp_pid, kp->kp_uid, kp->kp_comm);
                                continue;
+                       }
 
-                       if (matchargs) {
-                               if ((pargv = kvm_getargv(kd, kp, 0)) == NULL)
-                                       continue;
-
-                               j = 0;
-                               while (j < sizeof(buf) && *pargv != NULL) {
-                                       j += snprintf(buf + j, sizeof(buf) - j,
+                       if (matchargs &&
+                           (pargv = kvm_getargv(kd, kp, 0)) != NULL) {
+                               jsz = 0;
+                               while (jsz < sizeof(buf) && *pargv != NULL) {
+                                       jsz += snprintf(buf + jsz,
+                                           sizeof(buf) - jsz,
                                            pargv[1] != NULL ? "%s " : "%s",
                                            pargv[0]);
                                        pargv++;
                                }
-
                                mstr = buf;
                        } else
                                mstr = kp->kp_comm;
@@ -292,77 +365,83 @@ main(int argc, char **argv)
                        if (rv == 0) {
                                if (fullmatch) {
                                        if (regmatch.rm_so == 0 &&
-                                           regmatch.rm_eo == (regoff_t)strlen(mstr))
+                                           regmatch.rm_eo ==
+                                           (off_t)strlen(mstr))
                                                selected[i] = 1;
                                } else
                                        selected[i] = 1;
                        } else if (rv != REG_NOMATCH) {
                                regerror(rv, &reg, buf, sizeof(buf));
-                               errx(STATUS_ERROR, "regexec(): %s", buf);
+                               errx(STATUS_ERROR,
+                                   "Regular expression evaluation error (%s)",
+                                   buf);
+                       }
+                       if (debug_opt > 1) {
+                               const char *rv_res = "NoMatch";
+                               if (selected[i])
+                                       rv_res = "Matched";
+                               fprintf(stderr, "* %s %5d %3d %s\n", rv_res,
+                                   kp->kp_pid, kp->kp_uid, mstr);
                        }
                }
 
                regfree(&reg);
        }
 
-       /*
-        * Iterate through the list of processes, deselecting each one
-        * if it fails to meet the established criteria.
-        */
        for (i = 0, kp = plist; i < nproc; i++, kp++) {
-               if ((kp->kp_flags & P_SYSTEM) != 0)
+               if (PSKIP(kp))
                        continue;
 
-               SLIST_FOREACH(li, &ruidlist, li_chain) {
-                       if (kp->kp_ruid == li->li_datum.ld_uid)
-                               break;
+               if (pidfromfile >= 0 && kp->kp_pid != pidfromfile) {
+                       selected[i] = 0;
+                       continue;
                }
+
+               SLIST_FOREACH(li, &ruidlist, li_chain)
+                       if (kp->kp_ruid == (uid_t)li->li_number)
+                               break;
                if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
                        selected[i] = 0;
                        continue;
                }
-       
-               SLIST_FOREACH(li, &rgidlist, li_chain) {
-                       if (kp->kp_rgid == li->li_datum.ld_gid)
+
+               SLIST_FOREACH(li, &rgidlist, li_chain)
+                       if (kp->kp_rgid == (gid_t)li->li_number)
                                break;
-               }
                if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
                        selected[i] = 0;
                        continue;
                }
 
-               SLIST_FOREACH(li, &euidlist, li_chain) {
-                       if (kp->kp_uid == li->li_datum.ld_uid)
+               SLIST_FOREACH(li, &euidlist, li_chain)
+                       if (kp->kp_uid == (uid_t)li->li_number)
                                break;
-               }
                if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
                        selected[i] = 0;
                        continue;
                }
 
-               SLIST_FOREACH(li, &ppidlist, li_chain) {
-                       if (kp->kp_ppid == li->li_datum.ld_pid)
+               SLIST_FOREACH(li, &ppidlist, li_chain)
+                       if (kp->kp_ppid == (pid_t)li->li_number)
                                break;
-               }
                if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
                        selected[i] = 0;
                        continue;
                }
 
-               SLIST_FOREACH(li, &pgrplist, li_chain) {
-                       if (kp->kp_pgid == li->li_datum.ld_pid)
+               SLIST_FOREACH(li, &pgrplist, li_chain)
+                       if (kp->kp_pgid == (pid_t)li->li_number)
                                break;
-               }
                if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
                        selected[i] = 0;
                        continue;
                }
 
                SLIST_FOREACH(li, &tdevlist, li_chain) {
-                       if (li->li_datum.ld_dev == NODEV &&
+                       if (li->li_number == -1 &&
                            (kp->kp_flags & P_CONTROLT) == 0)
                                break;
-                       if (kp->kp_tdev == li->li_datum.ld_dev)
+                       if (kp->kp_tdev == (dev_t)li->li_number)
                                break;
                }
                if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
@@ -370,10 +449,9 @@ main(int argc, char **argv)
                        continue;
                }
 
-               SLIST_FOREACH(li, &sidlist, li_chain) {
-                       if (kp->kp_sid == li->li_datum.ld_pid)
+               SLIST_FOREACH(li, &sidlist, li_chain)
+                       if (kp->kp_sid == (pid_t)li->li_number)
                                break;
-               }
                if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
                        selected[i] = 0;
                        continue;
@@ -381,10 +459,10 @@ main(int argc, char **argv)
 
                SLIST_FOREACH(li, &jidlist, li_chain) {
                        /* A particular jail ID, including 0 (not in jail) */
-                       if (kp->kp_jailid == li->li_datum.ld_pid)
+                       if (kp->kp_jailid == (int)li->li_number)
                                break;
                        /* Any jail */
-                       if (kp->kp_jailid > 0 && li->li_datum.ld_pid < 0)
+                       if (kp->kp_jailid > 0 && li->li_number == -1)
                                break;
                }
                if (SLIST_FIRST(&jidlist) != NULL && li == NULL) {
@@ -392,26 +470,67 @@ main(int argc, char **argv)
                        continue;
                }
 
+               SLIST_FOREACH(li, &classlist, li_chain) {
+                       /*
+                        * We skip P_SYSTEM processes to match ps(1) output.
+                        */
+                       if ((kp->kp_flags & P_SYSTEM) == 0)
+                               break;
+               }
+               if (SLIST_FIRST(&classlist) != NULL && li == NULL) {
+                       selected[i] = 0;
+                       continue;
+               }
+
                if (argc == 0)
                        selected[i] = 1;
        }
 
-       if (newest) {
-               best.tv_sec = 0;
-               best.tv_usec = 0;
+       if (!ancestors) {
+               pid = mypid;
+               while (pid) {
+                       for (i = 0, kp = plist; i < nproc; i++, kp++) {
+                               if (PSKIP(kp))
+                                       continue;
+                               if (kp->kp_pid == pid) {
+                                       selected[i] = 0;
+                                       pid = kp->kp_ppid;
+                                       break;
+                               }
+                       }
+                       if (i == nproc) {
+                               if (pid == mypid)
+                                       pid = getppid();
+                               else
+                                       break;  /* Maybe we're in a jail ? */
+                       }
+               }
+       }
+
+       if (newest || oldest) {
+               best_tval.tv_sec = 0;
+               best_tval.tv_usec = 0;
                bestidx = -1;
 
                for (i = 0, kp = plist; i < nproc; i++, kp++) {
                        if (!selected[i])
                                continue;
-
-                       if (kp->kp_start.tv_sec > best.tv_sec ||
-                           (kp->kp_start.tv_sec == best.tv_sec
-                           && kp->kp_start.tv_usec > best.tv_usec)) {
-                               best.tv_sec = kp->kp_start.tv_sec;
-                               best.tv_usec = kp->kp_start.tv_usec;
-                               bestidx = i;
+                       if (bestidx == -1) {
+                               /* The first entry of the list which matched. */
+                               ;
+                       } else if (timercmp(&kp->kp_start, &best_tval, >)) {
+                               /* This entry is newer than previous "best". */
+                               if (oldest)     /* but we want the oldest */
+                                       continue;
+                       } else {
+                               /* This entry is older than previous "best". */
+                               if (newest)     /* but we want the newest */
+                                       continue;
                        }
+                       /* This entry is better than previous "best" entry. */
+                       best_tval.tv_sec = kp->kp_start.tv_sec;
+                       best_tval.tv_usec = kp->kp_start.tv_usec;
+                       bestidx = i;
                }
 
                memset(selected, 0, nproc);
@@ -422,110 +541,126 @@ main(int argc, char **argv)
        /*
         * Take the appropriate action for each matched process, if any.
         */
-       for (i = 0, j = 0, rv = 0, kp = plist; i < nproc; i++, kp++) {
-               if (kp->kp_pid == mypid)
+       did_action = 0;
+       for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) {
+               if (PSKIP(kp))
                        continue;
                if (selected[i]) {
+                       if (longfmt && !pgrep) {
+                               did_action = 1;
+                               printf("kill -%d %d\n", signum, kp->kp_pid);
+                       }
                        if (inverse)
                                continue;
                } else if (!inverse)
                        continue;
-
-               if ((kp->kp_flags & P_SYSTEM) != 0)
-                       continue;
-
-               rv = 1;
-               (*action)(kp, j++);
+               rv |= (*action)(kp);
        }
-       
-       if (pgrep)
-               putchar('\n');
-       
+       if (!did_action && !pgrep && longfmt)
+               fprintf(stderr,
+                   "No matching processes belonging to you were found\n");
+
        exit(rv ? STATUS_MATCH : STATUS_NOMATCH);
 }
 
-void
+static void
 usage(void)
 {
        const char *ustr;
 
        if (pgrep)
-               ustr = "[-flnvx] [-d delim]";
+               ustr = "[-LSfilnoqvx] [-d delim]";
        else
-               ustr = "[-signal] [-fnvx]";
+               ustr = "[-signal] [-ILfilnovx]";
 
        fprintf(stderr,
-               "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n"
-               "             [-t tty] [-u euid] [-j jid] pattern ...\n",
+               "usage: %s %s [-F pidfile] [-G gid] [-M core] [-N system]\n"
+               "             [-P ppid] [-U uid] [-c class] [-g pgrp] [-j jid]\n"
+               "             [-s sid] [-t tty] [-u euid] pattern ...\n",
                getprogname(), ustr);
 
-       exit(STATUS_ERROR);
+       exit(STATUS_BADUSAGE);
 }
 
-/*
- * Action callback to signal the given process (pkill).
- */
-void
-killact(struct kinfo_proc *kp, int dummy __unused)
-{
-       if (kill(kp->kp_pid, signum) == -1)
-               err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_pid);
-}
-
-/*
- * Action callback to print the pid of the given process (pgrep).
- */
-void
-grepact(struct kinfo_proc *kp, int printdelim)
+static void
+show_process(const struct kinfo_proc *kp)
 {
        char **argv;
 
-       if (printdelim)
-               fputs(delim, stdout);
-
-       if (longfmt && matchargs) {
-               if ((argv = kvm_getargv(kd, kp, 0)) == NULL)
-                       return;
-
+       if (quiet) {
+               assert(pgrep);
+               return;
+       }
+       if ((longfmt || !pgrep) && matchargs &&
+           (argv = kvm_getargv(kd, kp, 0)) != NULL) {
                printf("%d ", (int)kp->kp_pid);
                for (; *argv != NULL; argv++) {
                        printf("%s", *argv);
                        if (argv[1] != NULL)
                                putchar(' ');
                }
-       } else if (longfmt)
+       } else if (longfmt || !pgrep)
                printf("%d %s", (int)kp->kp_pid, kp->kp_comm);
        else
                printf("%d", (int)kp->kp_pid);
+}
+
+static int
+killact(const struct kinfo_proc *kp)
+{
+       int ch, first;
 
+       if (interactive) {
+               /*
+                * Be careful, ask before killing.
+                */
+               printf("kill ");
+               show_process(kp);
+               printf("? ");
+               fflush(stdout);
+               first = ch = getchar();
+               while (ch != '\n' && ch != EOF)
+                       ch = getchar();
+               if (first != 'y' && first != 'Y')
+                       return (1);
+       }
+       if (kill(kp->kp_pid, signum) == -1) {
+               /*
+                * Check for ESRCH, which indicates that the process
+                * disappeared between us matching it and us
+                * signalling it; don't issue a warning about it.
+                */
+               if (errno != ESRCH)
+                       warn("signalling pid %d", (int)kp->kp_pid);
+               /*
+                * Return 0 to indicate that the process should not be
+                * considered a match, since we didn't actually get to
+                * signal it.
+                */
+               return (0);
+       }
+       return (1);
 }
 
-/*
- * Parse a pid from the given string.  If zero, use a given default.
- */
-int
-parse_pid(const char *string, char **p, struct list *li, pid_t default_pid)
+static int
+grepact(const struct kinfo_proc *kp)
 {
-       long l;
 
-       l = strtol(string, p, 0);
-       li->li_datum.ld_pid = (l == 0 ? default_pid : (pid_t)l);
-       return(**p == '\0');
+       show_process(kp);
+       if (!quiet)
+               printf("%s", delim);
+       return (1);
 }
 
-/*
- * Populate a list from a comma-seperated string of items.
- * The possible valid values for each item depends on the type of list.
- */
-void
+static void
 makelist(struct listhead *head, enum listtype type, char *src)
 {
        struct list *li;
        struct passwd *pw;
        struct group *gr;
        struct stat st;
-       const char *sp, *tty_name;
-       char *p, buf[MAXPATHLEN];
+       const char *cp;
+       char *sp, *ep, buf[MAXPATHLEN];
        int empty;
 
        empty = 1;
@@ -534,80 +669,107 @@ makelist(struct listhead *head, enum listtype type, char *src)
                if (*sp == '\0')
                        usage();
 
-               if ((li = malloc(sizeof(*li))) == NULL)
-                       errx(STATUS_ERROR, "memory allocation failure");
+               if ((li = malloc(sizeof(*li))) == NULL) {
+                       err(STATUS_ERROR, "Cannot allocate %zu bytes",
+                           sizeof(*li));
+               }
+
                SLIST_INSERT_HEAD(head, li, li_chain);
                empty = 0;
 
-               switch (type) {
-               case LT_PPID:
-                       if (!parse_pid(sp, &p, li, (pid_t)0))
-                               usage();
-                       break;
-               case LT_PGRP:
-                       if (!parse_pid(sp, &p, li, getpgrp()))
-                               usage();
-                       break;
-               case LT_SID:
-                       if (!parse_pid(sp, &p, li, getsid(mypid)))
-                               usage();
-                       break;
-               case LT_JID:
-                       /* default to no jail */
-                       if (!parse_pid(sp, &p, li, 0))
-                               usage();
-                       if (li->li_datum.ld_pid < -1) {
-                               errx(STATUS_BADUSAGE,
-                                    "Negative jail ID `%s'", sp);
-                       }
-                       break;
-               case LT_USER:
-                       li->li_datum.ld_uid = (uid_t)strtol(sp, &p, 0);
-                       if (*p != '\0') {
-                               if ((pw = getpwnam(sp)) == NULL) {
+               if (type != LT_CLASS)
+                       li->li_number = (uid_t)strtol(sp, &ep, 0);
+
+               if (type != LT_CLASS && *ep == '\0') {
+                       switch (type) {
+                       case LT_PGRP:
+                               if (li->li_number == 0)
+                                       li->li_number = getpgrp();
+                               break;
+                       case LT_SID:
+                               if (li->li_number == 0)
+                                       li->li_number = getsid(mypid);
+                               break;
+                       case LT_JID:
+                               if (li->li_number < 0)
                                        errx(STATUS_BADUSAGE,
-                                            "unknown user `%s'", optarg);
-                               }
-                               li->li_datum.ld_uid = pw->pw_uid;
+                                            "Negative jail ID `%s'", sp);
+                               /* For compatibility with old -j */
+                               if (li->li_number == 0)
+                                       li->li_number = -1;     /* any jail */
+                               break;
+                       case LT_TTY:
+                               if (li->li_number < 0)
+                                       errx(STATUS_BADUSAGE,
+                                            "Negative /dev/pts tty `%s'", sp);
+                               snprintf(buf, sizeof(buf), _PATH_DEV "pts/%s",
+                                   sp);
+                               if (stat(buf, &st) != -1)
+                                       goto foundtty;
+                               if (errno == ENOENT)
+                                       errx(STATUS_BADUSAGE, "No such tty: `"
+                                           _PATH_DEV "pts/%s'", sp);
+                               err(STATUS_ERROR, "Cannot access `"
+                                   _PATH_DEV "pts/%s'", sp);
+                               break;
+                       default:
+                               break;
                        }
+                       continue;
+               }
+
+               switch (type) {
+               case LT_USER:
+                       if ((pw = getpwnam(sp)) == NULL)
+                               errx(STATUS_BADUSAGE, "Unknown user `%s'", sp);
+                       li->li_number = pw->pw_uid;
                        break;
                case LT_GROUP:
-                       li->li_datum.ld_gid = (gid_t)strtol(sp, &p, 0);
-                       if (*p != '\0') {
-                               if ((gr = getgrnam(sp)) == NULL) {
-                                       errx(STATUS_BADUSAGE,
-                                            "unknown group `%s'", optarg);
-                               }
-                               li->li_datum.ld_gid = gr->gr_gid;
-                       }
+                       if ((gr = getgrnam(sp)) == NULL)
+                               errx(STATUS_BADUSAGE, "Unknown group `%s'", sp);
+                       li->li_number = gr->gr_gid;
                        break;
                case LT_TTY:
                        if (strcmp(sp, "-") == 0) {
-                               li->li_datum.ld_dev = NODEV;
+                               li->li_number = -1;
                                break;
-                       } else if (strcmp(sp, "co") == 0)
-                               tty_name = "console";
-                       else if (strncmp(sp, "tty", 3) == 0)
-                               tty_name = sp;
-                       else
-                               tty_name = NULL;
-
-                       if (tty_name == NULL)
-                               snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
-                       else
-                               snprintf(buf, sizeof(buf), "/dev/%s", tty_name);
-
-                       if (stat(buf, &st) < 0) {
-                               if (errno == ENOENT)
-                                       errx(STATUS_BADUSAGE,
-                                           "no such tty: `%s'", sp);
-                               err(STATUS_ERROR, "stat(%s)", sp);
+                       } else if (strcmp(sp, "co") == 0) {
+                               cp = "console";
+                       } else {
+                               cp = sp;
                        }
 
-                       if ((st.st_mode & S_IFCHR) == 0)
-                               errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
+                       snprintf(buf, sizeof(buf), _PATH_DEV "%s", cp);
+                       if (stat(buf, &st) != -1)
+                               goto foundtty;
+
+                       snprintf(buf, sizeof(buf), _PATH_DEV "tty%s", cp);
+                       if (stat(buf, &st) != -1)
+                               goto foundtty;
 
-                       li->li_datum.ld_dev = st.st_rdev;
+                       if (errno == ENOENT)
+                               errx(STATUS_BADUSAGE, "No such tty: `%s'", sp);
+                       err(STATUS_ERROR, "Cannot access `%s'", sp);
+
+foundtty:              if ((st.st_mode & S_IFCHR) == 0)
+                               errx(STATUS_BADUSAGE, "Not a tty: `%s'", sp);
+
+                       li->li_number = st.st_rdev;
+                       break;
+               case LT_JID:
+                       if (strcmp(sp, "none") == 0)
+                               li->li_number = 0;
+                       else if (strcmp(sp, "any") == 0)
+                               li->li_number = -1;
+                       else if (*ep != '\0')
+                               errx(STATUS_BADUSAGE,
+                                    "Invalid jail ID `%s'", sp);
+                       break;
+               case LT_CLASS:
+                       li->li_number = -1;
+                       li->li_name = strdup(sp);
+                       if (li->li_name == NULL)
+                               err(STATUS_ERROR, "Cannot allocate memory");
                        break;
                default:
                        usage();
@@ -617,3 +779,48 @@ makelist(struct listhead *head, enum listtype type, char *src)
        if (empty)
                usage();
 }
+
+static int
+takepid(const char *pidfile, int pidfilelock)
+{
+       char *endp, line[BUFSIZ];
+       FILE *fh;
+       long rval;
+
+       fh = fopen(pidfile, "r");
+       if (fh == NULL)
+               err(STATUS_ERROR, "Cannot open pidfile `%s'", pidfile);
+
+       if (pidfilelock) {
+               /*
+                * If we can lock pidfile, this means that daemon is not
+                * running, so would be better not to kill some random process.
+                */
+               if (flock(fileno(fh), LOCK_EX | LOCK_NB) == 0) {
+                       (void)fclose(fh);
+                       errx(STATUS_ERROR, "File '%s' can be locked", pidfile);
+               } else {
+                       if (errno != EWOULDBLOCK) {
+                               errx(STATUS_ERROR,
+                                   "Error while locking file '%s'", pidfile);
+                       }
+               }
+       }
+
+       if (fgets(line, sizeof(line), fh) == NULL) {
+               if (feof(fh)) {
+                       (void)fclose(fh);
+                       errx(STATUS_ERROR, "Pidfile `%s' is empty", pidfile);
+               }
+               (void)fclose(fh);
+               err(STATUS_ERROR, "Cannot read from pid file `%s'", pidfile);
+       }
+       (void)fclose(fh);
+
+       rval = strtol(line, &endp, 10);
+       if (*endp != '\0' && !isspace((unsigned char)*endp))
+               errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile);
+       else if (rval < MIN_PID || rval > MAX_PID)
+               errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile);
+       return (rval);
+}