CRUX-ARM : Home

Home :: Documentation :: Download :: Development :: Community :: Ports :: Packages :: Bugs :: Links :: About :: Donors
start-stop-daemon: added new port
authorVictor Martinez <pitillo@ono.com>
Fri, 13 Nov 2015 13:42:30 +0000 (13:42 +0000)
committerVictor Martinez <pitillo@ono.com>
Fri, 13 Nov 2015 13:42:30 +0000 (13:42 +0000)
start-stop-daemon/.footprint [new file with mode: 0644]
start-stop-daemon/.md5sum [new file with mode: 0644]
start-stop-daemon/Pkgfile [new file with mode: 0644]
start-stop-daemon/crux-patch.diff [new file with mode: 0644]
start-stop-daemon/makefile [new file with mode: 0644]
start-stop-daemon/start-stop-daemon.8 [new file with mode: 0644]
start-stop-daemon/start-stop-daemon.c [new file with mode: 0644]

diff --git a/start-stop-daemon/.footprint b/start-stop-daemon/.footprint
new file mode 100644 (file)
index 0000000..ad56180
--- /dev/null
@@ -0,0 +1,7 @@
+drwxr-xr-x     root/root       sbin/
+-rwxr-xr-x     root/root       sbin/start-stop-daemon
+drwxr-xr-x     root/root       usr/
+drwxr-xr-x     root/root       usr/share/
+drwxr-xr-x     root/root       usr/share/man/
+drwxr-xr-x     root/root       usr/share/man/man8/
+-rw-r--r--     root/root       usr/share/man/man8/start-stop-daemon.8.gz
diff --git a/start-stop-daemon/.md5sum b/start-stop-daemon/.md5sum
new file mode 100644 (file)
index 0000000..76fcb21
--- /dev/null
@@ -0,0 +1,4 @@
+d69ea09c844389f3e4d0cda696d0d968  crux-patch.diff
+707efd334e4ba1d5f65f366a3c03c794  makefile
+c021c418059b2afcb2f501927239beca  start-stop-daemon.8
+61ba9ca1003221e1af632714c1cfbd11  start-stop-daemon.c
diff --git a/start-stop-daemon/Pkgfile b/start-stop-daemon/Pkgfile
new file mode 100644 (file)
index 0000000..311d22d
--- /dev/null
@@ -0,0 +1,19 @@
+# Description: Control the creation and termination of system-level processes
+# URL: http://man7.org/linux/man-pages/man8/start-stop-daemon.8.html
+# Maintainer: CRUX System Team, core-ports at crux dot nu
+# Arch Maintainer: CRUX-ARM System Team, devel at crux-arm dot nu
+
+name=start-stop-daemon
+version=20150921
+release=1
+source=(start-stop-daemon.c start-stop-daemon.8 crux-patch.diff makefile)
+
+build () {
+    patch -p2 -i crux-patch.diff
+
+    make CC="$CC"
+
+    install -d $PKG/{sbin,usr/share/man/man8}
+    install -m 755 $name $PKG/sbin/
+    install -m 644 $name.8 $PKG/usr/share/man/man8/
+}
diff --git a/start-stop-daemon/crux-patch.diff b/start-stop-daemon/crux-patch.diff
new file mode 100644 (file)
index 0000000..7c0498b
--- /dev/null
@@ -0,0 +1,86 @@
+diff --git a/start-stop-daemon/start-stop-daemon.8 b/start-stop-daemon/start-stop-daemon.8
+index deae6c6..28d2de8 100644
+--- a/start-stop-daemon/start-stop-daemon.8
++++ b/start-stop-daemon/start-stop-daemon.8
+@@ -20,7 +20,7 @@
+ .\" You should have received a copy of the GNU General Public License
+ .\" along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ .
+-.TH start\-stop\-daemon 8 "2014-03-26" "Debian Project" "dpkg utilities"
++.TH start\-stop\-daemon 8 "2015-09-21" "CRUX 3.2" "core services"
+ .SH NAME
+ start\-stop\-daemon \- start and stop system daemon programs
+ .
+diff --git a/start-stop-daemon/start-stop-daemon.c b/start-stop-daemon/start-stop-daemon.c
+index c844f2e..67cd043 100644
+--- a/start-stop-daemon/start-stop-daemon.c
++++ b/start-stop-daemon/start-stop-daemon.c
+@@ -20,10 +20,34 @@
+  * Changes by Ian Jackson: added --retry (and associated rearrangements).
+  */
++#if 0
+ #include <config.h>
+ #include <compat.h>
+ #include <dpkg/macros.h>
++#else
++# define VERSION              "20150921"
++# define CRUX                         "CRUX 3.2"
++
++# define HAVE_SYS_PARAM_H
++# define HAVE_SYS_SYSCALL_H
++# define HAVE_SYS_SYSCTL_H
++# define HAVE_SYS_USER_H
++# define HAVE_STDDEF_H
++# define HAVE_ERROR_H
++# define HAVE_ERR_H
++
++# define HAVE_CLOCK_MONOTONIC
++# define HAVE_GETDTABLESIZE
++# define HAVE_IOPRIO_SET
++# define HAVE_SETSID
++
++# define DPKG_ATTR_NORET      __attribute__((noreturn))
++# define DPKG_ATTR_PRINTF(X)
++
++# define _GNU_SOURCE
++# include <unistd.h>
++#endif
+ #if defined(linux)
+ #  define OSLinux
+@@ -142,6 +166,10 @@
+ #define HAVE_IOPRIO_SET
+ #endif
++#ifndef array_count
++# define array_count(x)       (sizeof(x) / sizeof((x)[0]))
++#endif
++
+ #define IOPRIO_CLASS_SHIFT 13
+ #define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | (prio))
+ #define IO_SCHED_PRIO_MIN 0
+@@ -310,8 +338,7 @@ xstrndup(const char *str, size_t n)
+ static void
+ timespec_gettime(struct timespec *ts)
+ {
+-#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \
+-    defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0
++#ifdef HAVE_CLOCK_MONOTONIC
+       if (clock_gettime(CLOCK_MONOTONIC, ts) < 0)
+               fatal("clock_gettime failed");
+ #else
+@@ -615,9 +642,9 @@ usage(void)
+ static void
+ do_version(void)
+ {
+-      printf("start-stop-daemon %s for Debian\n\n", VERSION);
+-
+-      printf("Written by Marek Michalkiewicz, public domain.\n");
++      printf("start-stop-daemon " VERSION " for " CRUX "\n\n"
++              "Written by Marek Michalkiewicz, public domain.\n"
++              "Adjusted for " CRUX ".\n");
+ }
+ static void DPKG_ATTR_NORET
diff --git a/start-stop-daemon/makefile b/start-stop-daemon/makefile
new file mode 100644 (file)
index 0000000..976a01a
--- /dev/null
@@ -0,0 +1,12 @@
+CC = cc
+CFLAGS += -g -Wall
+PROGRAM = start-stop-daemon
+SOURCES = start-stop-daemon.c
+
+$(PROGRAM): $(SOURCES)
+       $(CC) $(CFLAGS) -o $(@) $(SOURCES)
+
+
+all:   $(PROGRAM)
+clean: ; rm -f $(PROGRAM)
+force: clean all
diff --git a/start-stop-daemon/start-stop-daemon.8 b/start-stop-daemon/start-stop-daemon.8
new file mode 100644 (file)
index 0000000..deae6c6
--- /dev/null
@@ -0,0 +1,381 @@
+.\" dpkg manual page - start-stop-daemon(8)
+.\"
+.\" Copyright © 1999 Klee Dienes <klee@mit.edu>
+.\" Copyright © 1999 Ben Collins <bcollins@debian.org>
+.\" Copyright © 2000-2001 Wichert Akkerman <wakkerma@debian.org>
+.\" Copyright © 2002-2003 Adam Heath <doogie@debian.org>
+.\" Copyright © 2004 Scott James Remnant <keybuk@debian.org>
+.\" Copyright © 2008-2015 Guillem Jover <guillem@debian.org>
+.\"
+.\" This is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program.  If not, see <https://www.gnu.org/licenses/>.
+.
+.TH start\-stop\-daemon 8 "2014-03-26" "Debian Project" "dpkg utilities"
+.SH NAME
+start\-stop\-daemon \- start and stop system daemon programs
+.
+.SH SYNOPSIS
+.B start\-stop\-daemon
+.RI [ option "...] " command
+.
+.SH DESCRIPTION
+.B start\-stop\-daemon
+is used to control the creation and termination of system-level processes.
+Using one of the matching options, \fBstart\-stop\-daemon\fP
+can be configured to find existing instances of a running process.
+.PP
+Note: unless
+.B \-\-pid
+or
+.B \-\-pidfile
+are specified,
+.B start\-stop\-daemon
+behaves similar to
+.BR killall (1).
+.B start\-stop\-daemon
+will scan the process table looking for any processes which
+match the process name, parent pid, uid, and/or gid (if specified). Any
+matching process will prevent
+.BR \-\-start
+from starting the daemon. All matching processes will be sent the TERM
+signal (or the one specified via \fB\-\-signal\fP or \fB\-\-retry\fP) if
+.BR \-\-stop
+is specified. For daemons which have long-lived children
+which need to live through a
+.BR \-\-stop ,
+you must specify a pidfile.
+.
+.SH COMMANDS
+.TP
+.BR \-S ", " \-\-start " [" \-\- "] \fIarguments\fP"
+Check for the existence of a specified process.
+If such a process exists,
+.B start\-stop\-daemon
+does nothing, and exits with error status 1 (0 if
+.BR \-\-oknodo
+is specified).
+If such a process does not exist, it starts an
+instance, using either the executable specified by
+.B \-\-exec
+or, if specified, by
+.BR \-\-startas .
+Any arguments given after
+.BR \-\-
+on the command line are passed unmodified to the program being
+started.
+.TP
+.BR \-K ", " \-\-stop
+Checks for the existence of a specified process.
+If such a process exists,
+.B start\-stop\-daemon
+sends it the signal specified by
+.BR \-\-signal ,
+and exits with error status 0.
+If such a process does not exist,
+.B start\-stop\-daemon
+exits with error status 1
+(0 if
+.BR \-\-oknodo
+is specified). If
+.B \-\-retry
+is specified, then
+.B start\-stop\-daemon
+will check that the process(es) have terminated.
+.TP
+.BR \-T ", " \-\-status
+Check for the existence of a specified process, and returns an exit status
+code, according to the LSB Init Script Actions (since version 1.16.1).
+.TP
+.BR \-H ", " \-\-help
+Show usage information and exit.
+.TP
+.BR \-V ", " \-\-version
+Show the program version and exit.
+.
+.SH OPTIONS
+.SS Matching options
+.TP
+.BR \-\-pid " \fIpid\fP"
+Check for a process with the specified \fIpid\fP (since version 1.17.6).
+The \fIpid\fP must be a number greater than 0.
+.TP
+.BR \-\-ppid " \fIppid\fP"
+Check for a process with the specified parent pid \fIppid\fP
+(since version 1.17.7).
+The \fIppid\fP must be a number greater than 0.
+.TP
+.BR \-p ", " \-\-pidfile " \fIpid-file\fP"
+Check whether a process has created the file \fIpid-file\fP. Note: using this
+matching option alone might cause unintended processes to be acted on, if the
+old process terminated without being able to remove the \fIpid-file\fP.
+.TP
+.BR \-x ", " \-\-exec " \fIexecutable\fP"
+Check for processes that are instances of this \fIexecutable\fP. The
+\fIexecutable\fP argument should be an absolute pathname. Note: this might
+not work as intended with interpreted scripts, as the executable will point
+to the interpreter. Take into account processes running from inside a chroot
+will also be matched, so other match restrictions might be needed.
+.TP
+.BR \-n ", " \-\-name " \fIprocess-name\fP"
+Check for processes with the name \fIprocess-name\fP. The \fIprocess-name\fP
+is usually the process filename, but it could have been changed by the
+process itself. Note: on most systems this information is retrieved from
+the process comm name from the kernel, which tends to have a relatively
+short length limit (assuming more than 15 characters is non-portable).
+.TP
+.BR \-u ", " \-\-user " \fIusername\fP|\fIuid\fP
+Check for processes owned by the user specified by \fIusername\fP or
+\fIuid\fP. Note: using this matching option alone will cause all processes
+matching the user to be acted on.
+.
+.SS Generic options
+.TP
+.BR \-g ", " \-\-group " \fIgroup\fP|\fIgid\fP"
+Change to \fIgroup\fP or \fIgid\fP when starting the process.
+.TP
+.BR \-s ", " \-\-signal " \fIsignal\fP"
+With
+.BR \-\-stop ,
+specifies the signal to send to processes being stopped (default TERM).
+.TP
+.BR \-R ", " \-\-retry " \fItimeout\fP|\fIschedule\fP"
+With
+.BR \-\-stop ,
+specifies that
+.B start\-stop\-daemon
+is to check whether the process(es)
+do finish. It will check repeatedly whether any matching processes
+are running, until none are. If the processes do not exit it will
+then take further action as determined by the schedule.
+
+If
+.I timeout
+is specified instead of
+.IR schedule ,
+then the schedule
+.IB signal / timeout /KILL/ timeout
+is used, where
+.I signal
+is the signal specified with
+.BR \-\-signal .
+
+.I schedule
+is a list of at least two items separated by slashes
+.RB ( / );
+each item may be
+.BI \- signal-number
+or [\fB\-\fP]\fIsignal-name\fP,
+which means to send that signal,
+or
+.IR timeout ,
+which means to wait that many seconds for processes to
+exit,
+or
+.BR forever ,
+which means to repeat the rest of the schedule forever if
+necessary.
+
+If the end of the schedule is reached and
+.BR forever
+is not specified, then
+.B start\-stop\-daemon
+exits with error status 2.
+If a schedule is specified, then any signal specified
+with
+.B \-\-signal
+is ignored.
+.TP
+.BR \-a ", " \-\-startas " \fIpathname\fP"
+With
+.BR \-\-start ,
+start the process specified by
+.IR pathname .
+If not specified, defaults to the argument given to
+.BR \-\-exec .
+.TP
+.BR \-t ", " \-\-test
+Print actions that would be taken and set appropriate return value,
+but take no action.
+.TP
+.BR \-o ", " \-\-oknodo
+Return exit status 0 instead of 1 if no actions are (would be) taken.
+.TP
+.BR \-q ", " \-\-quiet
+Do not print informational messages; only display error messages.
+.TP
+.BR \-c ", " \-\-chuid " \fIusername\fR|\fIuid\fP[\fB:\fP\fIgroup\fR|\fIgid\fP]"
+Change to this username/uid before starting the process. You can also
+specify a group by appending a
+.BR : ,
+then the group or gid in the same way
+as you would for the \fBchown\fP(1) command (\fIuser\fP\fB:\fP\fIgroup\fP).
+If a user is specified without a group, the primary GID for that user is used.
+When using this option
+you must realize that the primary and supplemental groups are set as well,
+even if the
+.B \-\-group
+option is not specified. The
+.B \-\-group
+option is only for
+groups that the user isn't normally a member of (like adding per process
+group membership for generic users like
+.BR nobody ).
+.TP
+.BR \-r ", " \-\-chroot " \fIroot\fP"
+Chdir and chroot to
+.I root
+before starting the process. Please note that the pidfile is also written
+after the chroot.
+.TP
+.BR \-d ", " \-\-chdir " \fIpath\fP"
+Chdir to
+.I path
+before starting the process. This is done after the chroot if the
+\fB\-r\fP|\fB\-\-chroot\fP option is set. When not specified,
+.B start\-stop\-daemon
+will chdir to the root directory before starting the process.
+.TP
+.BR \-b ", " \-\-background
+Typically used with programs that don't detach on their own. This option
+will force
+.B start\-stop\-daemon
+to fork before starting the process, and force it into the background.
+.B Warning: start\-stop\-daemon
+cannot check the exit status if the process fails to execute for
+.B any
+reason. This is a last resort, and is only meant for programs that either
+make no sense forking on their own, or where it's not feasible to add the
+code for them to do this themselves.
+.TP
+.BR \-C ", " \-\-no\-close
+Do not close any file descriptor when forcing the daemon into the background
+(since version 1.16.5).
+Used for debugging purposes to see the process output, or to redirect file
+descriptors to log the process output.
+Only relevant when using \fB\-\-background\fP.
+.TP
+.BR \-N ", " \-\-nicelevel " \fIint\fP"
+This alters the priority of the process before starting it.
+.TP
+.BR \-P ", " \-\-procsched " \fIpolicy\fP\fB:\fP\fIpriority\fP"
+This alters the process scheduler policy and priority of the process before
+starting it (since version 1.15.0).
+The priority can be optionally specified by appending a \fB:\fP
+followed by the value. The default \fIpriority\fP is 0. The currently
+supported policy values are \fBother\fP, \fBfifo\fP and \fBrr\fP.
+.TP
+.BR \-I ", " \-\-iosched " \fIclass\fP\fB:\fP\fIpriority\fP"
+This alters the IO scheduler class and priority of the process before starting
+it (since version 1.15.0).
+The priority can be optionally specified by appending a \fB:\fP followed
+by the value. The default \fIpriority\fP is 4, unless \fIclass\fP is \fBidle\fP,
+then \fIpriority\fP will always be 7. The currently supported values for
+\fIclass\fP are \fBidle\fP, \fBbest-effort\fP and \fBreal-time\fP.
+.TP
+.BR \-k ", " \-\-umask " \fImask\fP"
+This sets the umask of the process before starting it (since version 1.13.22).
+.TP
+.BR \-m ", " \-\-make\-pidfile
+Used when starting a program that does not create its own pid file. This
+option will make
+.B start\-stop\-daemon
+create the file referenced with
+.B \-\-pidfile
+and place the pid into it just before executing the process. Note, the
+file will only be removed when stopping the program if
+\fB\-\-remove\-pidfile\fP is used.
+.B Note:
+This feature may not work in all cases. Most notably when the program
+being executed forks from its main process. Because of this, it is usually
+only useful when combined with the
+.B \-\-background
+option.
+.TP
+.B \-\-remove\-pidfile
+Used when stopping a program that does not remove its own pid file
+(since version 1.17.19).
+This option will make
+.B start\-stop\-daemon
+remove the file referenced with
+.B \-\-pidfile
+after terminating the process.
+.TP
+.BR \-v ", " \-\-verbose
+Print verbose informational messages.
+.
+.SH EXIT STATUS
+.TP
+.B 0
+The requested action was performed. If
+.B \-\-oknodo
+was specified, it's also possible that nothing had to be done.
+This can happen when
+.B \-\-start
+was specified and a matching process was already running, or when
+.B \-\-stop
+was specified and there were no matching processes.
+.TP
+.B 1
+If
+.B \-\-oknodo
+was not specified and nothing was done.
+.TP
+.B 2
+If
+.B \-\-stop
+and
+.B \-\-retry
+were specified, but the end of the schedule was reached and the processes were
+still running.
+.TP
+.B 3
+Any other error.
+.PP
+When using the \fB\-\-status\fP command, the following status codes are
+returned:
+.TP
+.B 0
+Program is running.
+.TP
+.B 1
+Program is not running and the pid file exists.
+.TP
+.B 3
+Program is not running.
+.TP
+.B 4
+Unable to determine program status.
+.
+.SH EXAMPLE
+Start the \fBfood\fP daemon, unless one is already running (a process named
+food, running as user food, with pid in food.pid):
+.IP
+.nf
+start\-stop\-daemon \-\-start \-\-oknodo \-\-user food \-\-name food \\
+       \-\-pidfile /run/food.pid \-\-startas /usr/sbin/food \\
+       \-\-chuid food \-\- \-\-daemon
+.fi
+.PP
+Send \fBSIGTERM\fP to \fBfood\fP and wait up to 5 seconds for it to stop:
+.IP
+.nf
+start\-stop\-daemon \-\-stop \-\-oknodo \-\-user food \-\-name food \\
+       \-\-pidfile /run/food.pid \-\-retry 5
+.fi
+.PP
+Demonstration of a custom schedule for stopping \fBfood\fP:
+.IP
+.nf
+start\-stop\-daemon \-\-stop \-\-oknodo \-\-user food \-\-name food \\
+       \-\-pidfile /run/food.pid \-\-retry=TERM/30/KILL/5
+.fi
diff --git a/start-stop-daemon/start-stop-daemon.c b/start-stop-daemon/start-stop-daemon.c
new file mode 100644 (file)
index 0000000..c844f2e
--- /dev/null
@@ -0,0 +1,2236 @@
+/*
+ * A rewrite of the original Debian's start-stop-daemon Perl script
+ * in C (faster - it is executed many times during system startup).
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * public domain.  Based conceptually on start-stop-daemon.pl, by Ian
+ * Jackson <ijackson@gnu.ai.mit.edu>.  May be used and distributed
+ * freely for any purpose.  Changes by Christian Schwarz
+ * <schwarz@monet.m.isar.de>, to make output conform to the Debian
+ * Console Message Standard, also placed in public domain.  Minor
+ * changes by Klee Dienes <klee@debian.org>, also placed in the Public
+ * Domain.
+ *
+ * Changes by Ben Collins <bcollins@debian.org>, added --chuid, --background
+ * and --make-pidfile options, placed in public domain as well.
+ *
+ * Port to OpenBSD by Sontri Tomo Huynh <huynh.29@osu.edu>
+ *                 and Andreas Schuldei <andreas@schuldei.org>
+ *
+ * Changes by Ian Jackson: added --retry (and associated rearrangements).
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/macros.h>
+
+#if defined(linux)
+#  define OSLinux
+#elif defined(__GNU__)
+#  define OSHurd
+#elif defined(__sun)
+#  define OSsunos
+#elif defined(OPENBSD) || defined(__OpenBSD__)
+#  define OSOpenBSD
+#elif defined(hpux)
+#  define OShpux
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#  define OSFreeBSD
+#elif defined(__NetBSD__)
+#  define OSNetBSD
+#elif defined(__DragonFly__)
+#  define OSDragonFlyBSD
+#else
+#  error Unknown architecture - cannot build start-stop-daemon
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_SYS_PROC_H
+#include <sys/proc.h>
+#endif
+#ifdef HAVE_SYS_USER_H
+#include <sys/user.h>
+#endif
+#ifdef HAVE_SYS_PSTAT_H
+#include <sys/pstat.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+#include <termios.h>
+#include <unistd.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#ifdef HAVE_ERROR_H
+#include <error.h>
+#endif
+#ifdef HAVE_ERR_H
+#include <err.h>
+#endif
+
+#if defined(OSHurd)
+#include <hurd.h>
+#include <ps.h>
+#endif
+
+#ifdef HAVE_KVM_H
+#include <kvm.h>
+#if defined(OSFreeBSD)
+#define KVM_MEMFILE "/dev/null"
+#else
+#define KVM_MEMFILE NULL
+#endif
+#endif
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+#include <sched.h>
+#else
+#define SCHED_OTHER -1
+#define SCHED_FIFO -1
+#define SCHED_RR -1
+#endif
+
+#if defined(OSLinux)
+/* This comes from TASK_COMM_LEN defined in Linux' include/linux/sched.h. */
+#define PROCESS_NAME_SIZE 15
+#elif defined(OSsunos)
+#define PROCESS_NAME_SIZE 15
+#elif defined(OSDarwin)
+#define PROCESS_NAME_SIZE 16
+#elif defined(OSNetBSD)
+#define PROCESS_NAME_SIZE 16
+#elif defined(OSOpenBSD)
+#define PROCESS_NAME_SIZE 16
+#elif defined(OSFreeBSD)
+#define PROCESS_NAME_SIZE 19
+#elif defined(OSDragonFlyBSD)
+/* On DragonFlyBSD MAXCOMLEN expands to 16. */
+#define PROCESS_NAME_SIZE MAXCOMLEN
+#endif
+
+#if defined(SYS_ioprio_set) && defined(linux)
+#define HAVE_IOPRIO_SET
+#endif
+
+#define IOPRIO_CLASS_SHIFT 13
+#define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | (prio))
+#define IO_SCHED_PRIO_MIN 0
+#define IO_SCHED_PRIO_MAX 7
+
+enum {
+       IOPRIO_WHO_PROCESS = 1,
+       IOPRIO_WHO_PGRP,
+       IOPRIO_WHO_USER,
+};
+
+enum {
+       IOPRIO_CLASS_NONE,
+       IOPRIO_CLASS_RT,
+       IOPRIO_CLASS_BE,
+       IOPRIO_CLASS_IDLE,
+};
+
+enum action_code {
+       ACTION_NONE,
+       ACTION_START,
+       ACTION_STOP,
+       ACTION_STATUS,
+};
+
+/* Time conversion constants. */
+enum {
+       NANOSEC_IN_SEC      = 1000000000L,
+       NANOSEC_IN_MILLISEC = 1000000L,
+       NANOSEC_IN_MICROSEC = 1000L,
+};
+
+/* The minimum polling interval, 20ms. */
+static const long MIN_POLL_INTERVAL = 20 * NANOSEC_IN_MILLISEC;
+
+static enum action_code action;
+static bool testmode = false;
+static int quietmode = 0;
+static int exitnodo = 1;
+static bool background = false;
+static bool close_io = true;
+static bool mpidfile = false;
+static bool rpidfile = false;
+static int signal_nr = SIGTERM;
+static int user_id = -1;
+static int runas_uid = -1;
+static int runas_gid = -1;
+static const char *userspec = NULL;
+static char *changeuser = NULL;
+static const char *changegroup = NULL;
+static char *changeroot = NULL;
+static const char *changedir = "/";
+static const char *cmdname = NULL;
+static char *execname = NULL;
+static char *startas = NULL;
+static pid_t match_pid = -1;
+static pid_t match_ppid = -1;
+static const char *pidfile = NULL;
+static char what_stop[1024];
+static const char *progname = "";
+static int nicelevel = 0;
+static int umask_value = -1;
+
+static struct stat exec_stat;
+#if defined(OSHurd)
+static struct proc_stat_list *procset = NULL;
+#endif
+
+/* LSB Init Script process status exit codes. */
+enum status_code {
+       STATUS_OK = 0,
+       STATUS_DEAD_PIDFILE = 1,
+       STATUS_DEAD_LOCKFILE = 2,
+       STATUS_DEAD = 3,
+       STATUS_UNKNOWN = 4,
+};
+
+struct pid_list {
+       struct pid_list *next;
+       pid_t pid;
+};
+
+static struct pid_list *found = NULL;
+static struct pid_list *killed = NULL;
+
+/* Resource scheduling policy. */
+struct res_schedule {
+       const char *policy_name;
+       int policy;
+       int priority;
+};
+
+struct schedule_item {
+       enum {
+               sched_timeout,
+               sched_signal,
+               sched_goto,
+               /* Only seen within parse_schedule and callees. */
+               sched_forever,
+       } type;
+       /* Seconds, signal no., or index into array. */
+       int value;
+};
+
+static struct res_schedule *proc_sched = NULL;
+static struct res_schedule *io_sched = NULL;
+
+static int schedule_length;
+static struct schedule_item *schedule = NULL;
+
+
+static void DPKG_ATTR_PRINTF(1)
+warning(const char *format, ...)
+{
+       va_list arglist;
+
+       fprintf(stderr, "%s: warning: ", progname);
+       va_start(arglist, format);
+       vfprintf(stderr, format, arglist);
+       va_end(arglist);
+}
+
+static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
+fatal(const char *format, ...)
+{
+       va_list arglist;
+       int errno_fatal = errno;
+
+       fprintf(stderr, "%s: ", progname);
+       va_start(arglist, format);
+       vfprintf(stderr, format, arglist);
+       va_end(arglist);
+       if (errno_fatal)
+               fprintf(stderr, " (%s)\n", strerror(errno_fatal));
+       else
+               fprintf(stderr, "\n");
+
+       if (action == ACTION_STATUS)
+               exit(STATUS_UNKNOWN);
+       else
+               exit(2);
+}
+
+static void *
+xmalloc(int size)
+{
+       void *ptr;
+
+       ptr = malloc(size);
+       if (ptr)
+               return ptr;
+       fatal("malloc(%d) failed", size);
+}
+
+static char *
+xstrndup(const char *str, size_t n)
+{
+       char *new_str;
+
+       new_str = strndup(str, n);
+       if (new_str)
+               return new_str;
+       fatal("strndup(%s, %zu) failed", str, n);
+}
+
+static void
+timespec_gettime(struct timespec *ts)
+{
+#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \
+    defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0
+       if (clock_gettime(CLOCK_MONOTONIC, ts) < 0)
+               fatal("clock_gettime failed");
+#else
+       struct timeval tv;
+
+       if (gettimeofday(&tv, NULL) != 0)
+               fatal("gettimeofday failed");
+
+       ts->tv_sec = tv.tv_sec;
+       ts->tv_nsec = tv.tv_usec * NANOSEC_IN_MICROSEC;
+#endif
+}
+
+#define timespec_cmp(a, b, OP) \
+       (((a)->tv_sec == (b)->tv_sec) ? \
+        ((a)->tv_nsec OP (b)->tv_nsec) : \
+        ((a)->tv_sec OP (b)->tv_sec))
+
+static void
+timespec_sub(struct timespec *a, struct timespec *b, struct timespec *res)
+{
+       res->tv_sec = a->tv_sec - b->tv_sec;
+       res->tv_nsec = a->tv_nsec - b->tv_nsec;
+       if (res->tv_nsec < 0) {
+               res->tv_sec--;
+               res->tv_nsec += NANOSEC_IN_SEC;
+       }
+}
+
+static void
+timespec_mul(struct timespec *a, int b)
+{
+       long nsec = a->tv_nsec * b;
+
+       a->tv_sec *= b;
+       a->tv_sec += nsec / NANOSEC_IN_SEC;
+       a->tv_nsec = nsec % NANOSEC_IN_SEC;
+}
+
+static char *
+newpath(const char *dirname, const char *filename)
+{
+       char *path;
+       size_t path_len;
+
+       path_len = strlen(dirname) + 1 + strlen(filename) + 1;
+       path = xmalloc(path_len);
+       snprintf(path, path_len, "%s/%s", dirname, filename);
+
+       return path;
+}
+
+static long
+get_open_fd_max(void)
+{
+#ifdef HAVE_GETDTABLESIZE
+       return getdtablesize();
+#else
+       return sysconf(_SC_OPEN_MAX);
+#endif
+}
+
+#ifndef HAVE_SETSID
+static void
+detach_controlling_tty(void)
+{
+#ifdef HAVE_TIOCNOTTY
+       int tty_fd;
+
+       tty_fd = open("/dev/tty", O_RDWR);
+
+       /* The current process does not have a controlling tty. */
+       if (tty_fd < 0)
+               return;
+
+       if (ioctl(tty_fd, TIOCNOTTY, 0) != 0)
+               fatal("unable to detach controlling tty");
+
+       close(tty_fd);
+#endif
+}
+
+static pid_t
+setsid(void)
+{
+       if (setpgid(0, 0) < 0)
+               return -1:
+
+       detach_controlling_tty();
+
+       return 0;
+}
+#endif
+
+static void
+wait_for_child(pid_t pid)
+{
+       pid_t child;
+       int status;
+
+       do {
+               child = waitpid(pid, &status, 0);
+       } while (child == -1 && errno == EINTR);
+
+       if (child != pid)
+               fatal("error waiting for child");
+
+       if (WIFEXITED(status)) {
+               int err = WEXITSTATUS(status);
+
+               if (err != 0)
+                       fatal("child returned error exit status %d", err);
+       } else if (WIFSIGNALED(status)) {
+               int signo = WTERMSIG(status);
+
+               fatal("child was killed by signal %d", signo);
+       } else {
+               fatal("unexpected status %d waiting for child", status);
+       }
+}
+
+static void
+write_pidfile(const char *filename, pid_t pid)
+{
+       FILE *fp;
+       int fd;
+
+       fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666);
+       if (fd < 0)
+               fp = NULL;
+       else
+               fp = fdopen(fd, "w");
+
+       if (fp == NULL)
+               fatal("unable to open pidfile '%s' for writing", filename);
+
+       fprintf(fp, "%d\n", pid);
+
+       if (fclose(fp))
+               fatal("unable to close pidfile '%s'", filename);
+}
+
+static void
+remove_pidfile(const char *filename)
+{
+       if (unlink(filename) < 0 && errno != ENOENT)
+               fatal("cannot remove pidfile '%s'", filename);
+}
+
+static void
+daemonize(void)
+{
+       pid_t pid;
+       sigset_t mask;
+       sigset_t oldmask;
+
+       if (quietmode < 0)
+               printf("Detaching to start %s...", startas);
+
+       /* Block SIGCHLD to allow waiting for the child process while it is
+        * performing actions, such as creating a pidfile. */
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGCHLD);
+       if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1)
+               fatal("cannot block SIGCHLD");
+
+       pid = fork();
+       if (pid < 0)
+               fatal("unable to do first fork");
+       else if (pid) { /* First Parent. */
+               /* Wait for the second parent to exit, so that if we need to
+                * perform any actions there, like creating a pidfile, we do
+                * not suffer from race conditions on return. */
+               wait_for_child(pid);
+
+               _exit(0);
+       }
+
+       /* Create a new session. */
+       if (setsid() < 0)
+               fatal("cannot set session ID");
+
+       pid = fork();
+       if (pid < 0)
+               fatal("unable to do second fork");
+       else if (pid) { /* Second parent. */
+               /* Set a default umask for dumb programs, which might get
+                * overridden by the --umask option later on, so that we get
+                * a defined umask when creating the pidfille. */
+               umask(022);
+
+               if (mpidfile && pidfile != NULL)
+                       /* User wants _us_ to make the pidfile. */
+                       write_pidfile(pidfile, pid);
+
+               _exit(0);
+       }
+
+       if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
+               fatal("cannot restore signal mask");
+
+       if (quietmode < 0)
+               printf("done.\n");
+}
+
+static void
+pid_list_push(struct pid_list **list, pid_t pid)
+{
+       struct pid_list *p;
+
+       p = xmalloc(sizeof(*p));
+       p->next = *list;
+       p->pid = pid;
+       *list = p;
+}
+
+static void
+pid_list_free(struct pid_list **list)
+{
+       struct pid_list *here, *next;
+
+       for (here = *list; here != NULL; here = next) {
+               next = here->next;
+               free(here);
+       }
+
+       *list = NULL;
+}
+
+static void
+usage(void)
+{
+       printf(
+"Usage: start-stop-daemon [<option> ...] <command>\n"
+"\n"
+"Commands:\n"
+"  -S|--start -- <argument> ...  start a program and pass <arguments> to it\n"
+"  -K|--stop                     stop a program\n"
+"  -T|--status                   get the program status\n"
+"  -H|--help                     print help information\n"
+"  -V|--version                  print version\n"
+"\n"
+"Matching options (at least one is required):\n"
+"     --pid <pid>                pid to check\n"
+"     --ppid <ppid>              parent pid to check\n"
+"  -p|--pidfile <pid-file>       pid file to check\n"
+"  -x|--exec <executable>        program to start/check if it is running\n"
+"  -n|--name <process-name>      process name to check\n"
+"  -u|--user <username|uid>      process owner to check\n"
+"\n"
+"Options:\n"
+"  -g|--group <group|gid>        run process as this group\n"
+"  -c|--chuid <name|uid[:group|gid]>\n"
+"                                change to this user/group before starting\n"
+"                                  process\n"
+"  -s|--signal <signal>          signal to send (default TERM)\n"
+"  -a|--startas <pathname>       program to start (default is <executable>)\n"
+"  -r|--chroot <directory>       chroot to <directory> before starting\n"
+"  -d|--chdir <directory>        change to <directory> (default is /)\n"
+"  -N|--nicelevel <incr>         add incr to the process' nice level\n"
+"  -P|--procsched <policy[:prio]>\n"
+"                                use <policy> with <prio> for the kernel\n"
+"                                  process scheduler (default prio is 0)\n"
+"  -I|--iosched <class[:prio]>   use <class> with <prio> to set the IO\n"
+"                                  scheduler (default prio is 4)\n"
+"  -k|--umask <mask>             change the umask to <mask> before starting\n"
+"  -b|--background               force the process to detach\n"
+"  -C|--no-close                 do not close any file descriptor\n"
+"  -m|--make-pidfile             create the pidfile before starting\n"
+"    |--remove-pidfile           delete the pidfile after stopping\n"
+"  -R|--retry <schedule>         check whether processes die, and retry\n"
+"  -t|--test                     test mode, don't do anything\n"
+"  -o|--oknodo                   exit status 0 (not 1) if nothing done\n"
+"  -q|--quiet                    be more quiet\n"
+"  -v|--verbose                  be more verbose\n"
+"\n"
+"Retry <schedule> is <item>|/<item>/... where <item> is one of\n"
+" -<signal-num>|[-]<signal-name>  send that signal\n"
+" <timeout>                       wait that many seconds\n"
+" forever                         repeat remainder forever\n"
+"or <schedule> may be just <timeout>, meaning <signal>/<timeout>/KILL/<timeout>\n"
+"\n"
+"The process scheduler <policy> can be one of:\n"
+"  other, fifo or rr\n"
+"\n"
+"The IO scheduler <class> can be one of:\n"
+"  real-time, best-effort or idle\n"
+"\n"
+"Exit status:\n"
+"  0 = done\n"
+"  1 = nothing done (=> 0 if --oknodo)\n"
+"  2 = with --retry, processes would not die\n"
+"  3 = trouble\n"
+"Exit status with --status:\n"
+"  0 = program is running\n"
+"  1 = program is not running and the pid file exists\n"
+"  3 = program is not running\n"
+"  4 = unable to determine status\n");
+}
+
+static void
+do_version(void)
+{
+       printf("start-stop-daemon %s for Debian\n\n", VERSION);
+
+       printf("Written by Marek Michalkiewicz, public domain.\n");
+}
+
+static void DPKG_ATTR_NORET
+badusage(const char *msg)
+{
+       if (msg)
+               fprintf(stderr, "%s: %s\n", progname, msg);
+       fprintf(stderr, "Try '%s --help' for more information.\n", progname);
+
+       if (action == ACTION_STATUS)
+               exit(STATUS_UNKNOWN);
+       else
+               exit(3);
+}
+
+struct sigpair {
+       const char *name;
+       int signal;
+};
+
+static const struct sigpair siglist[] = {
+       { "ABRT",       SIGABRT },
+       { "ALRM",       SIGALRM },
+       { "FPE",        SIGFPE  },
+       { "HUP",        SIGHUP  },
+       { "ILL",        SIGILL  },
+       { "INT",        SIGINT  },
+       { "KILL",       SIGKILL },
+       { "PIPE",       SIGPIPE },
+       { "QUIT",       SIGQUIT },
+       { "SEGV",       SIGSEGV },
+       { "TERM",       SIGTERM },
+       { "USR1",       SIGUSR1 },
+       { "USR2",       SIGUSR2 },
+       { "CHLD",       SIGCHLD },
+       { "CONT",       SIGCONT },
+       { "STOP",       SIGSTOP },
+       { "TSTP",       SIGTSTP },
+       { "TTIN",       SIGTTIN },
+       { "TTOU",       SIGTTOU }
+};
+
+static int
+parse_unsigned(const char *string, int base, int *value_r)
+{
+       long value;
+       char *endptr;
+
+       if (!string[0])
+               return -1;
+
+       errno = 0;
+       value = strtol(string, &endptr, base);
+       if (string == endptr || *endptr != '\0' || errno != 0)
+               return -1;
+       if (value < 0 || value > INT_MAX)
+               return -1;
+
+       *value_r = value;
+       return 0;
+}
+
+static int
+parse_pid(const char *pid_str, int *pid_num)
+{
+       if (parse_unsigned(pid_str, 10, pid_num) != 0)
+               return -1;
+       if (*pid_num == 0)
+               return -1;
+
+       return 0;
+}
+
+static int
+parse_signal(const char *sig_str, int *sig_num)
+{
+       unsigned int i;
+
+       if (parse_unsigned(sig_str, 10, sig_num) == 0)
+               return 0;
+
+       for (i = 0; i < array_count(siglist); i++) {
+               if (strcmp(sig_str, siglist[i].name) == 0) {
+                       *sig_num = siglist[i].signal;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int
+parse_umask(const char *string, int *value_r)
+{
+       return parse_unsigned(string, 0, value_r);
+}
+
+static void
+validate_proc_schedule(void)
+{
+#ifdef _POSIX_PRIORITY_SCHEDULING
+       int prio_min, prio_max;
+
+       prio_min = sched_get_priority_min(proc_sched->policy);
+       prio_max = sched_get_priority_max(proc_sched->policy);
+
+       if (proc_sched->priority < prio_min)
+               badusage("process scheduler priority less than min");
+       if (proc_sched->priority > prio_max)
+               badusage("process scheduler priority greater than max");
+#endif
+}
+
+static void
+parse_proc_schedule(const char *string)
+{
+       char *policy_str;
+       size_t policy_len;
+       int prio = 0;
+
+       policy_len = strcspn(string, ":");
+       policy_str = xstrndup(string, policy_len);
+
+       if (string[policy_len] == ':' &&
+           parse_unsigned(string + policy_len + 1, 10, &prio) != 0)
+               fatal("invalid process scheduler priority");
+
+       proc_sched = xmalloc(sizeof(*proc_sched));
+       proc_sched->policy_name = policy_str;
+
+       if (strcmp(policy_str, "other") == 0) {
+               proc_sched->policy = SCHED_OTHER;
+               proc_sched->priority = 0;
+       } else if (strcmp(policy_str, "fifo") == 0) {
+               proc_sched->policy = SCHED_FIFO;
+               proc_sched->priority = prio;
+       } else if (strcmp(policy_str, "rr") == 0) {
+               proc_sched->policy = SCHED_RR;
+               proc_sched->priority = prio;
+       } else
+               badusage("invalid process scheduler policy");
+
+       validate_proc_schedule();
+}
+
+static void
+parse_io_schedule(const char *string)
+{
+       char *class_str;
+       size_t class_len;
+       int prio = 4;
+
+       class_len = strcspn(string, ":");
+       class_str = xstrndup(string, class_len);
+
+       if (string[class_len] == ':' &&
+           parse_unsigned(string + class_len + 1, 10, &prio) != 0)
+               fatal("invalid IO scheduler priority");
+
+       io_sched = xmalloc(sizeof(*io_sched));
+       io_sched->policy_name = class_str;
+
+       if (strcmp(class_str, "real-time") == 0) {
+               io_sched->policy = IOPRIO_CLASS_RT;
+               io_sched->priority = prio;
+       } else if (strcmp(class_str, "best-effort") == 0) {
+               io_sched->policy = IOPRIO_CLASS_BE;
+               io_sched->priority = prio;
+       } else if (strcmp(class_str, "idle") == 0) {
+               io_sched->policy = IOPRIO_CLASS_IDLE;
+               io_sched->priority = 7;
+       } else
+               badusage("invalid IO scheduler policy");
+
+       if (io_sched->priority < IO_SCHED_PRIO_MIN)
+               badusage("IO scheduler priority less than min");
+       if (io_sched->priority > IO_SCHED_PRIO_MAX)
+               badusage("IO scheduler priority greater than max");
+}
+
+static void
+set_proc_schedule(struct res_schedule *sched)
+{
+#ifdef _POSIX_PRIORITY_SCHEDULING
+       struct sched_param param;
+
+       param.sched_priority = sched->priority;
+
+       if (sched_setscheduler(getpid(), sched->policy, &param) == -1)
+               fatal("unable to set process scheduler");
+#endif
+}
+
+#ifdef HAVE_IOPRIO_SET
+static inline int
+ioprio_set(int which, int who, int ioprio)
+{
+       return syscall(SYS_ioprio_set, which, who, ioprio);
+}
+#endif
+
+static void
+set_io_schedule(struct res_schedule *sched)
+{
+#ifdef HAVE_IOPRIO_SET
+       int io_sched_mask;
+
+       io_sched_mask = IOPRIO_PRIO_VALUE(sched->policy, sched->priority);
+       if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), io_sched_mask) == -1)
+               warning("unable to alter IO priority to mask %i (%s)\n",
+                       io_sched_mask, strerror(errno));
+#endif
+}
+
+static void
+parse_schedule_item(const char *string, struct schedule_item *item)
+{
+       const char *after_hyph;
+
+       if (strcmp(string, "forever") == 0) {
+               item->type = sched_forever;
+       } else if (isdigit(string[0])) {
+               item->type = sched_timeout;
+               if (parse_unsigned(string, 10, &item->value) != 0)
+                       badusage("invalid timeout value in schedule");
+       } else if ((after_hyph = string + (string[0] == '-')) &&
+                  parse_signal(after_hyph, &item->value) == 0) {
+               item->type = sched_signal;
+       } else {
+               badusage("invalid schedule item (must be [-]<signal-name>, "
+                        "-<signal-number>, <timeout> or 'forever'");
+       }
+}
+
+static void
+parse_schedule(const char *schedule_str)
+{
+       char item_buf[20];
+       const char *slash;
+       int count, repeatat;
+       size_t str_len;
+
+       count = 0;
+       for (slash = schedule_str; *slash; slash++)
+               if (*slash == '/')
+                       count++;
+
+       schedule_length = (count == 0) ? 4 : count + 1;
+       schedule = xmalloc(sizeof(*schedule) * schedule_length);
+
+       if (count == 0) {
+               schedule[0].type = sched_signal;
+               schedule[0].value = signal_nr;
+               parse_schedule_item(schedule_str, &schedule[1]);
+               if (schedule[1].type != sched_timeout) {
+                       badusage("--retry takes timeout, or schedule list"
+                                " of at least two items");
+               }
+               schedule[2].type = sched_signal;
+               schedule[2].value = SIGKILL;
+               schedule[3] = schedule[1];
+       } else {
+               count = 0;
+               repeatat = -1;
+               while (schedule_str != NULL) {
+                       slash = strchr(schedule_str, '/');
+                       str_len = slash ? (size_t)(slash - schedule_str) : strlen(schedule_str);
+                       if (str_len >= sizeof(item_buf))
+                               badusage("invalid schedule item: far too long"
+                                        " (you must delimit items with slashes)");
+                       memcpy(item_buf, schedule_str, str_len);
+                       item_buf[str_len] = '\0';
+                       schedule_str = slash ? slash + 1 : NULL;
+
+                       parse_schedule_item(item_buf, &schedule[count]);
+                       if (schedule[count].type == sched_forever) {
+                               if (repeatat >= 0)
+                                       badusage("invalid schedule: 'forever'"
+                                                " appears more than once");
+                               repeatat = count;
+                               continue;
+                       }
+                       count++;
+               }
+               if (repeatat == count)
+                       badusage("invalid schedule: 'forever' appears last, "
+                                "nothing to repeat");
+               if (repeatat >= 0) {
+                       schedule[count].type = sched_goto;
+                       schedule[count].value = repeatat;
+                       count++;
+               }
+               assert(count == schedule_length);
+       }
+}
+
+static void
+set_action(enum action_code new_action)
+{
+       if (action == new_action)
+               return;
+
+       if (action != ACTION_NONE)
+               badusage("only one command can be specified");
+
+       action = new_action;
+}
+
+#define OPT_PID                500
+#define OPT_PPID       501
+#define OPT_RM_PIDFILE 502
+
+static void
+parse_options(int argc, char * const *argv)
+{
+       static struct option longopts[] = {
+               { "help",         0, NULL, 'H'},
+               { "stop",         0, NULL, 'K'},
+               { "start",        0, NULL, 'S'},
+               { "status",       0, NULL, 'T'},
+               { "version",      0, NULL, 'V'},
+               { "startas",      1, NULL, 'a'},
+               { "name",         1, NULL, 'n'},
+               { "oknodo",       0, NULL, 'o'},
+               { "pid",          1, NULL, OPT_PID},
+               { "ppid",         1, NULL, OPT_PPID},
+               { "pidfile",      1, NULL, 'p'},
+               { "quiet",        0, NULL, 'q'},
+               { "signal",       1, NULL, 's'},
+               { "test",         0, NULL, 't'},
+               { "user",         1, NULL, 'u'},
+               { "group",        1, NULL, 'g'},
+               { "chroot",       1, NULL, 'r'},
+               { "verbose",      0, NULL, 'v'},
+               { "exec",         1, NULL, 'x'},
+               { "chuid",        1, NULL, 'c'},
+               { "nicelevel",    1, NULL, 'N'},
+               { "procsched",    1, NULL, 'P'},
+               { "iosched",      1, NULL, 'I'},
+               { "umask",        1, NULL, 'k'},
+               { "background",   0, NULL, 'b'},
+               { "no-close",     0, NULL, 'C'},
+               { "make-pidfile", 0, NULL, 'm'},
+               { "remove-pidfile", 0, NULL, OPT_RM_PIDFILE},
+               { "retry",        1, NULL, 'R'},
+               { "chdir",        1, NULL, 'd'},
+               { NULL,           0, NULL, 0  }
+       };
+       const char *pid_str = NULL;
+       const char *ppid_str = NULL;
+       const char *umask_str = NULL;
+       const char *signal_str = NULL;
+       const char *schedule_str = NULL;
+       const char *proc_schedule_str = NULL;
+       const char *io_schedule_str = NULL;
+       size_t changeuser_len;
+       int c;
+
+       for (;;) {
+               c = getopt_long(argc, argv,
+                               "HKSVTa:n:op:qr:s:tu:vx:c:N:P:I:k:bCmR:g:d:",
+                               longopts, NULL);
+               if (c == -1)
+                       break;
+               switch (c) {
+               case 'H':  /* --help */
+                       usage();
+                       exit(0);
+               case 'K':  /* --stop */
+                       set_action(ACTION_STOP);
+                       break;
+               case 'S':  /* --start */
+                       set_action(ACTION_START);
+                       break;
+               case 'T':  /* --status */
+                       set_action(ACTION_STATUS);
+                       break;
+               case 'V':  /* --version */
+                       do_version();
+                       exit(0);
+               case 'a':  /* --startas <pathname> */
+                       startas = optarg;
+                       break;
+               case 'n':  /* --name <process-name> */
+                       cmdname = optarg;
+                       break;
+               case 'o':  /* --oknodo */
+                       exitnodo = 0;
+                       break;
+               case OPT_PID: /* --pid <pid> */
+                       pid_str = optarg;
+                       break;
+               case OPT_PPID: /* --ppid <ppid> */
+                       ppid_str = optarg;
+                       break;
+               case 'p':  /* --pidfile <pid-file> */
+                       pidfile = optarg;
+                       break;
+               case 'q':  /* --quiet */
+                       quietmode = true;
+                       break;
+               case 's':  /* --signal <signal> */
+                       signal_str = optarg;
+                       break;
+               case 't':  /* --test */
+                       testmode = true;
+                       break;
+               case 'u':  /* --user <username>|<uid> */
+                       userspec = optarg;
+                       break;
+               case 'v':  /* --verbose */
+                       quietmode = -1;
+                       break;
+               case 'x':  /* --exec <executable> */
+                       execname = optarg;
+                       break;
+               case 'c':  /* --chuid <username>|<uid> */
+                       /* We copy the string just in case we need the
+                        * argument later. */
+                       changeuser_len = strcspn(optarg, ":");
+                       changeuser = xstrndup(optarg, changeuser_len);
+                       if (optarg[changeuser_len] == ':') {
+                               if (optarg[changeuser_len + 1] == '\0')
+                                       fatal("missing group name");
+                               changegroup = optarg + changeuser_len + 1;
+                       }
+                       break;
+               case 'g':  /* --group <group>|<gid> */
+                       changegroup = optarg;
+                       break;
+               case 'r':  /* --chroot /new/root */
+                       changeroot = optarg;
+                       break;
+               case 'N':  /* --nice */
+                       nicelevel = atoi(optarg);
+                       break;
+               case 'P':  /* --procsched */
+                       proc_schedule_str = optarg;
+                       break;
+               case 'I':  /* --iosched */
+                       io_schedule_str = optarg;
+                       break;
+               case 'k':  /* --umask <mask> */
+                       umask_str = optarg;
+                       break;
+               case 'b':  /* --background */
+                       background = true;
+                       break;
+               case 'C': /* --no-close */
+                       close_io = false;
+                       break;
+               case 'm':  /* --make-pidfile */
+                       mpidfile = true;
+                       break;
+               case OPT_RM_PIDFILE: /* --remove-pidfile */
+                       rpidfile = true;
+                       break;
+               case 'R':  /* --retry <schedule>|<timeout> */
+                       schedule_str = optarg;
+                       break;
+               case 'd':  /* --chdir /new/dir */
+                       changedir = optarg;
+                       break;
+               default:
+                       /* Message printed by getopt. */
+                       badusage(NULL);
+               }
+       }
+
+       if (pid_str != NULL) {
+               if (parse_pid(pid_str, &match_pid) != 0)
+                       badusage("pid value must be a number greater than 0");
+       }
+
+       if (ppid_str != NULL) {
+               if (parse_pid(ppid_str, &match_ppid) != 0)
+                       badusage("ppid value must be a number greater than 0");
+       }
+
+       if (signal_str != NULL) {
+               if (parse_signal(signal_str, &signal_nr) != 0)
+                       badusage("signal value must be numeric or name"
+                                " of signal (KILL, INT, ...)");
+       }
+
+       if (schedule_str != NULL) {
+               parse_schedule(schedule_str);
+       }
+
+       if (proc_schedule_str != NULL)
+               parse_proc_schedule(proc_schedule_str);
+
+       if (io_schedule_str != NULL)
+               parse_io_schedule(io_schedule_str);
+
+       if (umask_str != NULL) {
+               if (parse_umask(umask_str, &umask_value) != 0)
+                       badusage("umask value must be a positive number");
+       }
+
+       if (action == ACTION_NONE)
+               badusage("need one of --start or --stop or --status");
+
+       if (!execname && !pid_str && !ppid_str && !pidfile && !userspec &&
+           !cmdname)
+               badusage("need at least one of --exec, --pid, --ppid, --pidfile, --user or --name");
+
+#ifdef PROCESS_NAME_SIZE
+       if (cmdname && strlen(cmdname) > PROCESS_NAME_SIZE)
+               warning("this system is not able to track process names\n"
+                       "longer than %d characters, please use --exec "
+                       "instead of --name.\n", PROCESS_NAME_SIZE);
+#endif
+
+       if (!startas)
+               startas = execname;
+
+       if (action == ACTION_START && !startas)
+               badusage("--start needs --exec or --startas");
+
+       if (mpidfile && pidfile == NULL)
+               badusage("--make-pidfile requires --pidfile");
+       if (rpidfile && pidfile == NULL)
+               badusage("--remove-pidfile requires --pidfile");
+
+       if (pid_str && pidfile)
+               badusage("need either --pid of --pidfile, not both");
+
+       if (background && action != ACTION_START)
+               badusage("--background is only relevant with --start");
+
+       if (!close_io && !background)
+               badusage("--no-close is only relevant with --background");
+}
+
+static void
+setup_options(void)
+{
+       if (execname) {
+               char *fullexecname;
+
+               /* If it's a relative path, normalize it. */
+               if (execname[0] != '/')
+                       execname = newpath(changedir, execname);
+
+               if (changeroot)
+                       fullexecname = newpath(changeroot, execname);
+               else
+                       fullexecname = execname;
+
+               if (stat(fullexecname, &exec_stat))
+                       fatal("unable to stat %s", fullexecname);
+
+               if (fullexecname != execname)
+                       free(fullexecname);
+       }
+
+       if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
+               struct passwd *pw;
+
+               pw = getpwnam(userspec);
+               if (!pw)
+                       fatal("user '%s' not found", userspec);
+
+               user_id = pw->pw_uid;
+       }
+
+       if (changegroup && sscanf(changegroup, "%d", &runas_gid) != 1) {
+               struct group *gr;
+
+               gr = getgrnam(changegroup);
+               if (!gr)
+                       fatal("group '%s' not found", changegroup);
+               changegroup = gr->gr_name;
+               runas_gid = gr->gr_gid;
+       }
+       if (changeuser) {
+               struct passwd *pw;
+               struct stat st;
+
+               if (sscanf(changeuser, "%d", &runas_uid) == 1)
+                       pw = getpwuid(runas_uid);
+               else
+                       pw = getpwnam(changeuser);
+               if (!pw)
+                       fatal("user '%s' not found", changeuser);
+               changeuser = pw->pw_name;
+               runas_uid = pw->pw_uid;
+               if (changegroup == NULL) {
+                       /* Pass the default group of this user. */
+                       changegroup = ""; /* Just empty. */
+                       runas_gid = pw->pw_gid;
+               }
+               if (stat(pw->pw_dir, &st) == 0)
+                       setenv("HOME", pw->pw_dir, 1);
+       }
+}
+
+#if defined(OSLinux)
+static const char *
+proc_status_field(pid_t pid, const char *field)
+{
+       static char *line = NULL;
+       static size_t line_size = 0;
+
+       FILE *fp;
+       char filename[32];
+       char *value = NULL;
+       ssize_t line_len;
+       size_t field_len = strlen(field);
+
+       sprintf(filename, "/proc/%d/status", pid);
+       fp = fopen(filename, "r");
+       if (!fp)
+               return NULL;
+       while ((line_len = getline(&line, &line_size, fp)) >= 0) {
+               if (strncasecmp(line, field, field_len) == 0) {
+                       line[line_len - 1] = '\0';
+
+                       value = line + field_len;
+                       while (isspace(*value))
+                               value++;
+
+                       break;
+               }
+       }
+       fclose(fp);
+
+       return value;
+}
+#endif
+
+#if defined(OSHurd)
+static void
+init_procset(void)
+{
+       struct ps_context *context;
+       error_t err;
+
+       err = ps_context_create(getproc(), &context);
+       if (err)
+               error(1, err, "ps_context_create");
+
+       err = proc_stat_list_create(context, &procset);
+       if (err)
+               error(1, err, "proc_stat_list_create");
+
+       err = proc_stat_list_add_all(procset, 0, 0);
+       if (err)
+               error(1, err, "proc_stat_list_add_all");
+}
+
+static struct proc_stat *
+get_proc_stat(pid_t pid, ps_flags_t flags)
+{
+       struct proc_stat *ps;
+       ps_flags_t wanted_flags = PSTAT_PID | flags;
+
+       if (!procset)
+               init_procset();
+
+       ps = proc_stat_list_pid_proc_stat(procset, pid);
+       if (!ps)
+               return NULL;
+       if (proc_stat_set_flags(ps, wanted_flags))
+               return NULL;
+       if ((proc_stat_flags(ps) & wanted_flags) != wanted_flags)
+               return NULL;
+
+       return ps;
+}
+#elif defined(HAVE_KVM_H)
+static kvm_t *
+ssd_kvm_open(void)
+{
+       kvm_t *kd;
+       char errbuf[_POSIX2_LINE_MAX];
+
+       kd = kvm_openfiles(NULL, KVM_MEMFILE, NULL, O_RDONLY, errbuf);
+       if (kd == NULL)
+               errx(1, "%s", errbuf);
+
+       return kd;
+}
+
+static struct kinfo_proc *
+ssd_kvm_get_procs(kvm_t *kd, int op, int arg, int *count)
+{
+       struct kinfo_proc *kp;
+       int lcount;
+
+       if (count == NULL)
+               count = &lcount;
+       *count = 0;
+
+       kp = kvm_getprocs(kd, op, arg, count);
+       if (kp == NULL && errno != ESRCH)
+               errx(1, "%s", kvm_geterr(kd));
+
+       return kp;
+}
+#endif
+
+#if defined(OSLinux)
+static bool
+pid_is_exec(pid_t pid, const struct stat *esb)
+{
+       char lname[32];
+       char lcontents[_POSIX_PATH_MAX + 1];
+       char *filename;
+       const char deleted[] = " (deleted)";
+       int nread;
+       struct stat sb;
+
+       sprintf(lname, "/proc/%d/exe", pid);
+       nread = readlink(lname, lcontents, sizeof(lcontents) - 1);
+       if (nread == -1)
+               return false;
+
+       filename = lcontents;
+       filename[nread] = '\0';
+
+       /* OpenVZ kernels contain a bogus patch that instead of appending,
+        * prepends the deleted marker. Workaround those. Otherwise handle
+        * the normal appended marker. */
+       if (strncmp(filename, deleted, strlen(deleted)) == 0)
+               filename += strlen(deleted);
+       else if (strcmp(filename + nread - strlen(deleted), deleted) == 0)
+               filename[nread - strlen(deleted)] = '\0';
+
+       if (stat(filename, &sb) != 0)
+               return false;
+
+       return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
+}
+#elif defined(OSHurd)
+static bool
+pid_is_exec(pid_t pid, const struct stat *esb)
+{
+       struct proc_stat *ps;
+       struct stat sb;
+       const char *filename;
+
+       ps = get_proc_stat(pid, PSTAT_ARGS);
+       if (ps == NULL)
+               return false;
+
+       filename = proc_stat_args(ps);
+
+       if (stat(filename, &sb) != 0)
+               return false;
+
+       return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
+}
+#elif defined(OShpux)
+static bool
+pid_is_exec(pid_t pid, const struct stat *esb)
+{
+       struct pst_status pst;
+
+       if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0)
+               return false;
+       return ((dev_t)pst.pst_text.psf_fsid.psfs_id == esb->st_dev &&
+               (ino_t)pst.pst_text.psf_fileid == esb->st_ino);
+}
+#elif defined(OSFreeBSD)
+static bool
+pid_is_exec(pid_t pid, const struct stat *esb)
+{
+       struct stat sb;
+       int error, name[4];
+       size_t len;
+       char pathname[PATH_MAX];
+
+       name[0] = CTL_KERN;
+       name[1] = KERN_PROC;
+       name[2] = KERN_PROC_PATHNAME;
+       name[3] = pid;
+       len = sizeof(pathname);
+
+       error = sysctl(name, 4, pathname, &len, NULL, 0);
+       if (error != 0 && errno != ESRCH)
+               return false;
+       if (len == 0)
+               pathname[0] = '\0';
+
+       if (stat(pathname, &sb) != 0)
+               return false;
+
+       return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
+}
+#elif defined(HAVE_KVM_H)
+static bool
+pid_is_exec(pid_t pid, const struct stat *esb)
+{
+       kvm_t *kd;
+       int argv_len = 0;
+       struct kinfo_proc *kp;
+       struct stat sb;
+       char buf[_POSIX2_LINE_MAX];
+       char **pid_argv_p;
+       char *start_argv_0_p, *end_argv_0_p;
+       bool res = false;
+
+       kd = ssd_kvm_open();
+       kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL);
+       if (kp == NULL)
+               goto cleanup;
+
+       pid_argv_p = kvm_getargv(kd, kp, argv_len);
+       if (pid_argv_p == NULL)
+               errx(1, "%s", kvm_geterr(kd));
+
+       /* Find and compare string. */
+       start_argv_0_p = *pid_argv_p;
+
+       /* Find end of argv[0] then copy and cut of str there. */
+       end_argv_0_p = strchr(*pid_argv_p, ' ');
+       if (end_argv_0_p == NULL)
+               /* There seems to be no space, so we have the command
+                * already in its desired form. */
+               start_argv_0_p = *pid_argv_p;
+       else {
+               /* Tests indicate that this never happens, since
+                * kvm_getargv itself cuts of tailing stuff. This is
+                * not what the manpage says, however. */
+               strncpy(buf, *pid_argv_p, (end_argv_0_p - start_argv_0_p));
+               buf[(end_argv_0_p - start_argv_0_p) + 1] = '\0';
+               start_argv_0_p = buf;
+       }
+
+       if (stat(start_argv_0_p, &sb) != 0)
+               goto cleanup;
+
+       res = (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
+
+cleanup:
+       kvm_close(kd);
+
+       return res;
+}
+#endif
+
+#if defined(OSLinux)
+static bool
+pid_is_child(pid_t pid, pid_t ppid)
+{
+       const char *ppid_str;
+       pid_t proc_ppid;
+       int rc;
+
+       ppid_str = proc_status_field(pid, "PPid:");
+       if (ppid_str == NULL)
+               return false;
+
+       rc = parse_pid(ppid_str, &proc_ppid);
+       if (rc < 0)
+               return false;
+
+       return proc_ppid == ppid;
+}
+#elif defined(OSHurd)
+static bool
+pid_is_child(pid_t pid, pid_t ppid)
+{
+       struct proc_stat *ps;
+       struct procinfo *pi;
+
+       ps = get_proc_stat(pid, PSTAT_PROC_INFO);
+       if (ps == NULL)
+               return false;
+
+       pi = proc_stat_proc_info(ps);
+
+       return pi->ppid == ppid;
+}
+#elif defined(OShpux)
+static bool
+pid_is_child(pid_t pid, pid_t ppid)
+{
+       struct pst_status pst;
+
+       if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0)
+               return false;
+
+       return pst.pst_ppid == ppid;
+}
+#elif defined(HAVE_KVM_H)
+static bool
+pid_is_child(pid_t pid, pid_t ppid)
+{
+       kvm_t *kd;
+       struct kinfo_proc *kp;
+       pid_t proc_ppid;
+       bool res = false;
+
+       kd = ssd_kvm_open();
+       kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL);
+       if (kp == NULL)
+               goto cleanup;
+
+#if defined(OSFreeBSD)
+       proc_ppid = kp->ki_ppid;
+#elif defined(OSOpenBSD)
+       proc_ppid = kp->p_ppid;
+#elif defined(OSDragonFlyBSD)
+       proc_ppid = kp->kp_ppid;
+#else
+       proc_ppid = kp->kp_proc.p_ppid;
+#endif
+
+       res = (proc_ppid == ppid);
+
+cleanup:
+       kvm_close(kd);
+
+       return res;
+}
+#endif
+
+#if defined(OSLinux)
+static bool
+pid_is_user(pid_t pid, uid_t uid)
+{
+       struct stat sb;
+       char buf[32];
+
+       sprintf(buf, "/proc/%d", pid);
+       if (stat(buf, &sb) != 0)
+               return false;
+       return (sb.st_uid == uid);
+}
+#elif defined(OSHurd)
+static bool
+pid_is_user(pid_t pid, uid_t uid)
+{
+       struct proc_stat *ps;
+
+       ps = get_proc_stat(pid, PSTAT_OWNER_UID);
+       return ps && (uid_t)proc_stat_owner_uid(ps) == uid;
+}
+#elif defined(OShpux)
+static bool
+pid_is_user(pid_t pid, uid_t uid)
+{
+       struct pst_status pst;
+
+       if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0)
+               return false;
+       return ((uid_t)pst.pst_uid == uid);
+}
+#elif defined(HAVE_KVM_H)
+static bool
+pid_is_user(pid_t pid, uid_t uid)
+{
+       kvm_t *kd;
+       uid_t proc_uid;
+       struct kinfo_proc *kp;
+       bool res = false;
+
+       kd = ssd_kvm_open();
+       kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL);
+       if (kp == NULL)
+               goto cleanup;
+
+#if defined(OSFreeBSD)
+       proc_uid = kp->ki_ruid;
+#elif defined(OSOpenBSD)
+       proc_uid = kp->p_ruid;
+#elif defined(OSDragonFlyBSD)
+       proc_uid = kp->kp_ruid;
+#else
+       if (kp->kp_proc.p_cred)
+               kvm_read(kd, (u_long)&(kp->kp_proc.p_cred->p_ruid),
+                        &proc_uid, sizeof(uid_t));
+       else
+               goto cleanup;
+#endif
+
+       res = (proc_uid == (uid_t)uid);
+
+cleanup:
+       kvm_close(kd);
+
+       return res;
+}
+#endif
+
+#if defined(OSLinux)
+static bool
+pid_is_cmd(pid_t pid, const char *name)
+{
+       const char *comm;
+
+       comm = proc_status_field(pid, "Name:");
+       if (comm == NULL)
+               return false;
+
+       return strcmp(comm, name) == 0;
+}
+#elif defined(OSHurd)
+static bool
+pid_is_cmd(pid_t pid, const char *name)
+{
+       struct proc_stat *ps;
+       size_t argv0_len;
+       const char *argv0;
+       const char *binary_name;
+
+       ps = get_proc_stat(pid, PSTAT_ARGS);
+       if (ps == NULL)
+               return false;
+
+       argv0 = proc_stat_args(ps);
+       argv0_len = strlen(argv0) + 1;
+
+       binary_name = basename(argv0);
+       if (strcmp(binary_name, name) == 0)
+               return true;
+
+       /* XXX: This is all kinds of ugly, but on the Hurd there's no way to
+        * know the command name of a process, so we have to try to match
+        * also on argv[1] for the case of an interpreted script. */
+       if (proc_stat_args_len(ps) > argv0_len) {
+               const char *script_name = basename(argv0 + argv0_len);
+
+               return strcmp(script_name, name) == 0;
+       }
+
+       return false;
+}
+#elif defined(OShpux)
+static bool
+pid_is_cmd(pid_t pid, const char *name)
+{
+       struct pst_status pst;
+
+       if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0)
+               return false;
+       return (strcmp(pst.pst_ucomm, name) == 0);
+}
+#elif defined(HAVE_KVM_H)
+static bool
+pid_is_cmd(pid_t pid, const char *name)
+{
+       kvm_t *kd;
+       struct kinfo_proc *kp;
+       char *process_name;
+       bool res = false;
+
+       kd = ssd_kvm_open();
+       kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL);
+       if (kp == NULL)
+               goto cleanup;
+
+#if defined(OSFreeBSD)
+       process_name = kp->ki_comm;
+#elif defined(OSOpenBSD)
+       process_name = kp->p_comm;
+#elif defined(OSDragonFlyBSD)
+       process_name = kp->kp_comm;
+#else
+       process_name = kp->kp_proc.p_comm;
+#endif
+
+       res = (strcmp(name, process_name) == 0);
+
+cleanup:
+       kvm_close(kd);
+
+       return res;
+}
+#endif
+
+#if defined(OSHurd)
+static bool
+pid_is_running(pid_t pid)
+{
+       return get_proc_stat(pid, 0) != NULL;
+}
+#else /* !OSHurd */
+static bool
+pid_is_running(pid_t pid)
+{
+       if (kill(pid, 0) == 0 || errno == EPERM)
+               return true;
+       else if (errno == ESRCH)
+               return false;
+       else
+               fatal("error checking pid %u status", pid);
+}
+#endif
+
+static enum status_code
+pid_check(pid_t pid)
+{
+       if (execname && !pid_is_exec(pid, &exec_stat))
+               return STATUS_DEAD;
+       if (match_ppid > 0 && !pid_is_child(pid, match_ppid))
+               return STATUS_DEAD;
+       if (userspec && !pid_is_user(pid, user_id))
+               return STATUS_DEAD;
+       if (cmdname && !pid_is_cmd(pid, cmdname))
+               return STATUS_DEAD;
+       if (action != ACTION_STOP && !pid_is_running(pid))
+               return STATUS_DEAD;
+
+       pid_list_push(&found, pid);
+
+       return STATUS_OK;
+}
+
+static enum status_code
+do_pidfile(const char *name)
+{
+       FILE *f;
+       static pid_t pid = 0;
+
+       if (pid)
+               return pid_check(pid);
+
+       f = fopen(name, "r");
+       if (f) {
+               enum status_code pid_status;
+
+               if (fscanf(f, "%d", &pid) == 1)
+                       pid_status = pid_check(pid);
+               else
+                       pid_status = STATUS_UNKNOWN;
+               fclose(f);
+
+               if (pid_status == STATUS_DEAD)
+                       return STATUS_DEAD_PIDFILE;
+               else
+                       return pid_status;
+       } else if (errno == ENOENT)
+               return STATUS_DEAD;
+       else
+               fatal("unable to open pidfile %s", name);
+}
+
+#if defined(OSLinux) || defined (OSsunos)
+static enum status_code
+do_procinit(void)
+{
+       DIR *procdir;
+       struct dirent *entry;
+       int foundany;
+       pid_t pid;
+       enum status_code prog_status = STATUS_DEAD;
+
+       procdir = opendir("/proc");
+       if (!procdir)
+               fatal("unable to opendir /proc");
+
+       foundany = 0;
+       while ((entry = readdir(procdir)) != NULL) {
+               enum status_code pid_status;
+
+               if (sscanf(entry->d_name, "%d", &pid) != 1)
+                       continue;
+               foundany++;
+
+               pid_status = pid_check(pid);
+               if (pid_status < prog_status)
+                       prog_status = pid_status;
+       }
+       closedir(procdir);
+       if (!foundany)
+               fatal("nothing in /proc - not mounted?");
+
+       return prog_status;
+}
+#elif defined(OSHurd)
+static int
+check_proc_stat(struct proc_stat *ps)
+{
+       pid_check(ps->pid);
+       return 0;
+}
+
+static enum status_code
+do_procinit(void)
+{
+       if (!procset)
+               init_procset();
+
+       proc_stat_list_for_each(procset, check_proc_stat);
+
+       if (found)
+               return STATUS_OK;
+       else
+               return STATUS_DEAD;
+}
+#elif defined(OShpux)
+static enum status_code
+do_procinit(void)
+{
+       struct pst_status pst[10];
+       int i, count;
+       int idx = 0;
+       enum status_code prog_status = STATUS_DEAD;
+
+       while ((count = pstat_getproc(pst, sizeof(pst[0]), 10, idx)) > 0) {
+               enum status_code pid_status;
+
+               for (i = 0; i < count; i++) {
+                       pid_status = pid_check(pst[i].pst_pid);
+                       if (pid_status < prog_status)
+                               prog_status = pid_status;
+               }
+               idx = pst[count - 1].pst_idx + 1;
+       }
+
+       return prog_status;
+}
+#elif defined(HAVE_KVM_H)
+static enum status_code
+do_procinit(void)
+{
+       kvm_t *kd;
+       int nentries, i;
+       struct kinfo_proc *kp;
+       enum status_code prog_status = STATUS_DEAD;
+
+       kd = ssd_kvm_open();
+       kp = ssd_kvm_get_procs(kd, KERN_PROC_ALL, 0, &nentries);
+
+       for (i = 0; i < nentries; i++) {
+               enum status_code pid_status;
+               pid_t pid;
+
+#if defined(OSFreeBSD)
+               pid = kp[i].ki_pid;
+#elif defined(OSOpenBSD)
+               pid = kp[i].p_pid;
+#elif defined(OSDragonFlyBSD)
+               pid = kp[i].kp_pid;
+#else
+               pid = kp[i].kp_proc.p_pid;
+#endif
+
+               pid_status = pid_check(pid);
+               if (pid_status < prog_status)
+                       prog_status = pid_status;
+       }
+
+       kvm_close(kd);
+
+       return prog_status;
+}
+#endif
+
+static enum status_code
+do_findprocs(void)
+{
+       pid_list_free(&found);
+
+       if (match_pid > 0)
+               return pid_check(match_pid);
+       else if (pidfile)
+               return do_pidfile(pidfile);
+       else
+               return do_procinit();
+}
+
+static void
+do_start(int argc, char **argv)
+{
+       int devnull_fd = -1;
+       gid_t rgid;
+       uid_t ruid;
+
+       do_findprocs();
+
+       if (found) {
+               if (quietmode <= 0)
+                       printf("%s already running.\n", execname ? execname : "process");
+               exit(exitnodo);
+       }
+       if (testmode && quietmode <= 0) {
+               printf("Would start %s ", startas);
+               while (argc-- > 0)
+                       printf("%s ", *argv++);
+               if (changeuser != NULL) {
+                       printf(" (as user %s[%d]", changeuser, runas_uid);
+                       if (changegroup != NULL)
+                               printf(", and group %s[%d])", changegroup, runas_gid);
+                       else
+                               printf(")");
+               }
+               if (changeroot != NULL)
+                       printf(" in directory %s", changeroot);
+               if (nicelevel)
+                       printf(", and add %i to the priority", nicelevel);
+               if (proc_sched)
+                       printf(", with scheduling policy %s with priority %i",
+                              proc_sched->policy_name, proc_sched->priority);
+               if (io_sched)
+                       printf(", with IO scheduling class %s with priority %i",
+                              io_sched->policy_name, io_sched->priority);
+               printf(".\n");
+       }
+       if (testmode)
+               exit(0);
+       if (quietmode < 0)
+               printf("Starting %s...\n", startas);
+       *--argv = startas;
+       if (background)
+               /* Ok, we need to detach this process. */
+               daemonize();
+       else if (mpidfile && pidfile != NULL)
+               /* User wants _us_ to make the pidfile, but detach themself! */
+               write_pidfile(pidfile, getpid());
+       if (background && close_io) {
+               devnull_fd = open("/dev/null", O_RDWR);
+               if (devnull_fd < 0)
+                       fatal("unable to open '%s'", "/dev/null");
+       }
+       if (nicelevel) {
+               errno = 0;
+               if ((nice(nicelevel) == -1) && (errno != 0))
+                       fatal("unable to alter nice level by %i", nicelevel);
+       }
+       if (proc_sched)
+               set_proc_schedule(proc_sched);
+       if (io_sched)
+               set_io_schedule(io_sched);
+       if (umask_value >= 0)
+               umask(umask_value);
+       if (changeroot != NULL) {
+               if (chdir(changeroot) < 0)
+                       fatal("unable to chdir() to %s", changeroot);
+               if (chroot(changeroot) < 0)
+                       fatal("unable to chroot() to %s", changeroot);
+       }
+       if (chdir(changedir) < 0)
+               fatal("unable to chdir() to %s", changedir);
+
+       rgid = getgid();
+       ruid = getuid();
+       if (changegroup != NULL) {
+               if (rgid != (gid_t)runas_gid)
+                       if (setgid(runas_gid))
+                               fatal("unable to set gid to %d", runas_gid);
+       }
+       if (changeuser != NULL) {
+               /* We assume that if our real user and group are the same as
+                * the ones we should switch to, the supplementary groups
+                * will be already in place. */
+               if (rgid != (gid_t)runas_gid || ruid != (uid_t)runas_uid)
+                       if (initgroups(changeuser, runas_gid))
+                               fatal("unable to set initgroups() with gid %d",
+                                     runas_gid);
+
+               if (ruid != (uid_t)runas_uid)
+                       if (setuid(runas_uid))
+                               fatal("unable to set uid to %s", changeuser);
+       }
+
+       if (background && close_io) {
+               int i;
+
+               dup2(devnull_fd, 0); /* stdin */
+               dup2(devnull_fd, 1); /* stdout */
+               dup2(devnull_fd, 2); /* stderr */
+
+                /* Now close all extra fds. */
+               for (i = get_open_fd_max() - 1; i >= 3; --i)
+                       close(i);
+       }
+       execv(startas, argv);
+       fatal("unable to start %s", startas);
+}
+
+static void
+do_stop(int sig_num, int *n_killed, int *n_notkilled)
+{
+       struct pid_list *p;
+
+       do_findprocs();
+
+       *n_killed = 0;
+       *n_notkilled = 0;
+
+       if (!found)
+               return;
+
+       pid_list_free(&killed);
+
+       for (p = found; p; p = p->next) {
+               if (testmode) {
+                       if (quietmode <= 0)
+                               printf("Would send signal %d to %d.\n",
+                                      sig_num, p->pid);
+                       (*n_killed)++;
+               } else if (kill(p->pid, sig_num) == 0) {
+                       pid_list_push(&killed, p->pid);
+                       (*n_killed)++;
+               } else {
+                       if (sig_num)
+                               warning("failed to kill %d: %s\n",
+                                       p->pid, strerror(errno));
+                       (*n_notkilled)++;
+               }
+       }
+}
+
+static void
+do_stop_summary(int retry_nr)
+{
+       struct pid_list *p;
+
+       if (quietmode >= 0 || !killed)
+               return;
+
+       printf("Stopped %s (pid", what_stop);
+       for (p = killed; p; p = p->next)
+               printf(" %d", p->pid);
+       putchar(')');
+       if (retry_nr > 0)
+               printf(", retry #%d", retry_nr);
+       printf(".\n");
+}
+
+static void
+set_what_stop(const char *str)
+{
+       strncpy(what_stop, str, sizeof(what_stop));
+       what_stop[sizeof(what_stop) - 1] = '\0';
+}
+
+/*
+ * We want to keep polling for the processes, to see if they've exited, or
+ * until the timeout expires.
+ *
+ * This is a somewhat complicated algorithm to try to ensure that we notice
+ * reasonably quickly when all the processes have exited, but don't spend
+ * too much CPU time polling. In particular, on a fast machine with
+ * quick-exiting daemons we don't want to delay system shutdown too much,
+ * whereas on a slow one, or where processes are taking some time to exit,
+ * we want to increase the polling interval.
+ *
+ * The algorithm is as follows: we measure the elapsed time it takes to do
+ * one poll(), and wait a multiple of this time for the next poll. However,
+ * if that would put us past the end of the timeout period we wait only as
+ * long as the timeout period, but in any case we always wait at least
+ * MIN_POLL_INTERVAL (20ms). The multiple (‘ratio’) starts out as 2, and
+ * increases by 1 for each poll to a maximum of 10; so we use up to between
+ * 30% and 10% of the machine's resources (assuming a few reasonable things
+ * about system performance).
+ */
+static bool
+do_stop_timeout(int timeout, int *n_killed, int *n_notkilled)
+{
+       struct timespec stopat, before, after, interval, maxinterval;
+       int rc, ratio;
+
+       timespec_gettime(&stopat);
+       stopat.tv_sec += timeout;
+       ratio = 1;
+       for (;;) {
+               timespec_gettime(&before);
+               if (timespec_cmp(&before, &stopat, >))
+                       return false;
+
+               do_stop(0, n_killed, n_notkilled);
+               if (!*n_killed)
+                       return true;
+
+               timespec_gettime(&after);
+
+               if (!timespec_cmp(&after, &stopat, <))
+                       return false;
+
+               if (ratio < 10)
+                       ratio++;
+
+               timespec_sub(&stopat, &after, &maxinterval);
+               timespec_sub(&after, &before, &interval);
+               timespec_mul(&interval, ratio);
+
+               if (interval.tv_sec < 0 || interval.tv_nsec < 0)
+                       interval.tv_sec = interval.tv_nsec = 0;
+
+               if (timespec_cmp(&interval, &maxinterval, >))
+                       interval = maxinterval;
+
+               if (interval.tv_sec == 0 &&
+                   interval.tv_nsec <= MIN_POLL_INTERVAL)
+                       interval.tv_nsec = MIN_POLL_INTERVAL;
+
+               rc = pselect(0, NULL, NULL, NULL, &interval, NULL);
+               if (rc < 0 && errno != EINTR)
+                       fatal("select() failed for pause");
+       }
+}
+
+static int
+finish_stop_schedule(bool anykilled)
+{
+       if (rpidfile && pidfile && !testmode)
+               remove_pidfile(pidfile);
+
+       if (anykilled)
+               return 0;
+
+       if (quietmode <= 0)
+               printf("No %s found running; none killed.\n", what_stop);
+
+       return exitnodo;
+}
+
+static int
+run_stop_schedule(void)
+{
+       int position, n_killed, n_notkilled, value, retry_nr;
+       bool anykilled;
+
+       if (testmode) {
+               if (schedule != NULL) {
+                       if (quietmode <= 0)
+                               printf("Ignoring --retry in test mode\n");
+                       schedule = NULL;
+               }
+       }
+
+       if (cmdname)
+               set_what_stop(cmdname);
+       else if (execname)
+               set_what_stop(execname);
+       else if (pidfile)
+               sprintf(what_stop, "process in pidfile '%.200s'", pidfile);
+       else if (match_pid > 0)
+               sprintf(what_stop, "process with pid %d", match_pid);
+       else if (match_ppid > 0)
+               sprintf(what_stop, "process(es) with parent pid %d", match_ppid);
+       else if (userspec)
+               sprintf(what_stop, "process(es) owned by '%.200s'", userspec);
+       else
+               fatal("internal error, no match option, please report");
+
+       anykilled = false;
+       retry_nr = 0;
+
+       if (schedule == NULL) {
+               do_stop(signal_nr, &n_killed, &n_notkilled);
+               do_stop_summary(0);
+               if (n_notkilled > 0 && quietmode <= 0)
+                       printf("%d pids were not killed\n", n_notkilled);
+               if (n_killed)
+                       anykilled = true;
+               return finish_stop_schedule(anykilled);
+       }
+
+       for (position = 0; position < schedule_length; position++) {
+       reposition:
+               value = schedule[position].value;
+               n_notkilled = 0;
+
+               switch (schedule[position].type) {
+               case sched_goto:
+                       position = value;
+                       goto reposition;
+               case sched_signal:
+                       do_stop(value, &n_killed, &n_notkilled);
+                       do_stop_summary(retry_nr++);
+                       if (!n_killed)
+                               return finish_stop_schedule(anykilled);
+                       else
+                               anykilled = true;
+                       continue;
+               case sched_timeout:
+                       if (do_stop_timeout(value, &n_killed, &n_notkilled))
+                               return finish_stop_schedule(anykilled);
+                       else
+                               continue;
+               default:
+                       assert(!"schedule[].type value must be valid");
+               }
+       }
+
+       if (quietmode <= 0)
+               printf("Program %s, %d process(es), refused to die.\n",
+                      what_stop, n_killed);
+
+       return 2;
+}
+
+int
+main(int argc, char **argv)
+{
+       progname = argv[0];
+
+       parse_options(argc, argv);
+       setup_options();
+
+       argc -= optind;
+       argv += optind;
+
+       if (action == ACTION_START)
+               do_start(argc, argv);
+
+       if (action == ACTION_STOP) {
+               int i = run_stop_schedule();
+               exit(i);
+       }
+
+       if (action == ACTION_STATUS) {
+               enum status_code prog_status;
+
+               prog_status = do_findprocs();
+               exit(prog_status);
+       }
+
+       return 0;
+}