openrc/src/start-stop-daemon.c
Roy Marples 5af58b4514 Rewrite the core parts in C. We now provide librc so other programs can
query runlevels, services and state without using bash. We also provide
libeinfo so other programs can easily use our informational functions.

As such, we have dropped the requirement of using bash as the init script
shell. We now use /bin/sh and have strived to make the scripts as portable
as possible. Shells that work are bash and dash. busybox works provided
you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
should disable find too.
zsh and ksh do not work at this time.

Networking support is currently being re-vamped also as it was heavily bash
array based. As such, a new config format is available like so
config_eth0="1.2.3.4/24 5.6.7.8/16"
or like so
config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

We will still support the old bash array format provided that /bin/sh IS
a link it bash.

ChangeLog for baselayout-1 can be found in our SVN repo.
2007-04-05 11:18:42 +00:00

1048 lines
23 KiB
C

/*
start-stop-daemon
Starts, stops, tests and signals daemons
Copyright 2007 Gentoo Foundation
Released under the GPLv2
This is essentially a ground up re-write of Debians
start-stop-daemon for cleaner code and to integrate into our RC
system so we can monitor daemons a little.
*/
#define POLL_INTERVAL 20000
#define START_WAIT 100000
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/termios.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
/* We are not supporting authentication conversations */
static struct pam_conv conv = { NULL, NULL} ;
#endif
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
typedef struct schedulelist
{
enum
{
schedule_timeout,
schedule_signal,
schedule_goto,
schedule_forever
} type;
int value;
struct schedulelist *gotolist;
struct schedulelist *next;
} schedulelist_t;
static schedulelist_t *schedule;
static char *progname;
static char *changeuser;
static char **newenv;
extern char **environ;
static void free_schedulelist (schedulelist_t **list)
{
schedulelist_t *here;
schedulelist_t *next;
for (here = *list; here; here = next)
{
next = here->next;
free (here);
}
*list = NULL;
}
static void cleanup (void)
{
if (changeuser)
free (changeuser);
if (schedule)
free_schedulelist (&schedule);
if (newenv)
rc_strlist_free (newenv);
}
static int parse_signal (const char *sig)
{
typedef struct signalpair
{
const char *name;
int signal;
} signalpair_t;
static const signalpair_t signallist[] = {
{ "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 }
};
unsigned int i = 0;
char *s;
if (! sig || strlen (sig) == 0)
return (-1);
if (sscanf (sig, "%u", &i) == 1)
{
if (i > 0 && i < sizeof (signallist) / sizeof (signallist[0]))
return (i);
eerrorx ("%s: `%s' is not a valid signal", progname, sig);
}
if (strncmp (sig, "SIG", 3) == 0)
s = (char *) sig + 3;
else
s = NULL;
for (i = 0; i < sizeof (signallist) / sizeof (signallist[0]); i++)
if (strcmp (sig, signallist[i].name) == 0 ||
(s && strcmp (s, signallist[i].name) == 0))
return (signallist[i].signal);
eerrorx ("%s: `%s' is not a valid signal", progname, sig);
}
static void parse_schedule_item (schedulelist_t *item, const char *string)
{
const char *after_hyph;
int sig;
if (strcmp (string,"forever") == 0)
item->type = schedule_forever;
else if (isdigit (string[0]))
{
item->type = schedule_timeout;
errno = 0;
if (sscanf (string, "%d", &item->value) != 1)
eerrorx ("%s: invalid timeout value in schedule `%s'", progname,
string);
}
else if ((after_hyph = string + (string[0] == '-')) &&
((sig = parse_signal (after_hyph)) != -1))
{
item->type = schedule_signal;
item->value = (int) sig;
}
else
eerrorx ("%s: invalid schedule item `%s'", progname, string);
}
static void parse_schedule (const char *string, int default_signal)
{
char buffer[20];
const char *slash;
int count = 0;
schedulelist_t *repeatat = NULL;
ptrdiff_t len;
schedulelist_t *next;
if (string)
for (slash = string; *slash; slash++)
if (*slash == '/')
count++;
if (schedule)
free_schedulelist (&schedule);
schedule = rc_xmalloc (sizeof (schedulelist_t));
schedule->gotolist = NULL;
if (count == 0)
{
schedule->type = schedule_signal;
schedule->value = default_signal;
schedule->next = rc_xmalloc (sizeof (schedulelist_t));
next = schedule->next;
next->type = schedule_timeout;
next->gotolist = NULL;
if (string)
{
if (sscanf (string, "%d", &next->value) != 1)
eerrorx ("%s: invalid timeout value in schedule", progname);
}
else
next->value = 5;
next->next = NULL;
return;
}
next = schedule;
while (string != NULL)
{
if ((slash = strchr (string, '/')))
len = slash - string;
else
len = strlen (string);
if (len >= (ptrdiff_t) sizeof (buffer))
eerrorx ("%s: invalid schedule item, far too long", progname);
memcpy (buffer, string, len);
buffer[len] = 0;
string = slash ? slash + 1 : NULL;
parse_schedule_item (next, buffer);
if (next->type == schedule_forever)
{
if (repeatat)
eerrorx ("%s: invalid schedule, `forever' appears more than once",
progname);
repeatat = next;
continue;
}
if (string)
{
next->next = rc_xmalloc (sizeof (schedulelist_t));
next = next->next;
next->gotolist = NULL;
}
}
if (repeatat)
{
next->next = rc_xmalloc (sizeof (schedulelist_t));
next = next->next;
next->type = schedule_goto;
next->value = 0;
next->gotolist = repeatat;
}
next->next = NULL;
return;
}
static pid_t get_pid (const char *pidfile, bool quiet)
{
FILE *fp;
pid_t pid;
if (! pidfile)
return (-1);
if ((fp = fopen (pidfile, "r")) == NULL)
{
if (! quiet)
eerror ("%s: fopen `%s': %s", progname, pidfile, strerror (errno));
return (-1);
}
if (fscanf (fp, "%d", &pid) != 1)
{
if (! quiet)
eerror ("%s: no pid found in `%s'", progname, pidfile);
fclose (fp);
return (-1);
}
fclose (fp);
return (pid);
}
/* return number of processed killed, -1 on error */
static int do_stop (const char *exec, const char *cmd,
const char *pidfile, uid_t uid,int sig,
bool quiet, bool verbose, bool test)
{
pid_t *pids;
bool killed;
int nkilled = 0;
pid_t pid = 0;
int i;
if (pidfile)
if ((pid = get_pid (pidfile, quiet)) == -1)
return (quiet ? 0 : -1);
if ((pids = rc_find_pids (exec, cmd, uid, pid)) == NULL)
return (0);
for (i = 0; pids[i]; i++)
{
if (test)
{
if (! quiet)
einfo ("Would send signal %d to PID %d", sig, pids[i]);
nkilled++;
continue;
}
if (verbose)
ebegin ("Sending signal %d to PID %d", sig, pids[i]);
errno = 0;
killed = (kill (pids[i], sig) == 0 || errno == ESRCH ? true : false);
if (! killed)
{
if (! quiet)
eerror ("%s: failed to send signal %d to PID %d: %s",
progname, sig, pids[i], strerror (errno));
if (verbose)
eend (1, NULL);
nkilled = -1;
}
else
{
if (verbose)
eend (0, NULL);
if (nkilled != -1)
nkilled++;
}
}
free (pids);
return (nkilled);
}
static int run_stop_schedule (const char *exec, const char *cmd,
const char *pidfile, uid_t uid,
bool quiet, bool verbose, bool test)
{
schedulelist_t *item = schedule;
int nkilled = 0;
int tkilled = 0;
int nrunning = 0;
struct timeval tv;
struct timeval now;
struct timeval stopat;
if (verbose)
{
if (pidfile)
einfo ("Will stop PID in pidfile `%s'", pidfile);
if (uid)
einfo ("Will stop processes owned by UID %d", uid);
if (exec)
einfo ("Will stop processes of `%s'", exec);
if (cmd)
einfo ("Will stop processes called `%s'", cmd);
}
while (item)
{
switch (item->type)
{
case schedule_goto:
item = item->gotolist;
continue;
case schedule_signal:
nrunning = 0;
nkilled = do_stop (exec, cmd, pidfile, uid, item->value,
quiet, verbose, test);
if (nkilled == 0)
{
if (tkilled == 0)
{
if (! quiet)
eerror ("%s: no matching processes found", progname);
}
return (tkilled);
}
else if (nkilled == -1)
return (0);
tkilled += nkilled;
break;
case schedule_timeout:
if (item->value < 1)
{
item = NULL;
break;
}
if (gettimeofday (&stopat, NULL) != 0)
{
eerror ("%s: gettimeofday: %s", progname, strerror (errno));
return (0);
}
stopat.tv_sec += item->value;
while (1)
{
if ((nrunning = do_stop (exec, cmd, pidfile,
uid, 0, true, false, true)) == 0)
return (true);
tv.tv_sec = 0;
tv.tv_usec = POLL_INTERVAL;
if (select (0, 0, 0, 0, &tv) < 0)
{
if (errno == EINTR)
eerror ("%s: caught an interupt", progname);
else
eerror ("%s: select: %s", progname, strerror (errno));
return (0);
}
if (gettimeofday (&now, NULL) != 0)
{
eerror ("%s: gettimeofday: %s", progname, strerror (errno));
return (0);
}
if (timercmp (&now, &stopat, >))
break;
}
break;
default:
eerror ("%s: invalid schedule item `%d'", progname, item->type);
return (0);
}
if (item)
item = item->next;
}
if (test || (tkilled > 0 && nrunning == 0))
return (nkilled);
if (! quiet)
{
if (nrunning == 1)
eerror ("%s: %d process refused to stop", progname, nrunning);
else
eerror ("%s: %d process(es) refused to stop", progname, nrunning);
}
return (-nrunning);
}
static void handle_signal (int sig)
{
int pid;
int status;
int serrno = errno;
switch (sig)
{
case SIGINT:
case SIGTERM:
case SIGQUIT:
eerrorx ("%s: caught signal %d, aborting", progname, sig);
case SIGCHLD:
while (1)
{
if ((pid = waitpid (-1, &status, WNOHANG)) < 0)
{
if (errno != ECHILD)
eerror ("%s: waitpid: %s", progname, strerror (errno));
break;
}
}
break;
default:
eerror ("%s: caught unknown signal %d", progname, sig);
}
/* Restore errno */
errno = serrno;
}
int main (int argc, char **argv)
{
int devnull_fd = -1;
#ifdef TIOCNOTTY
int tty_fd = -1;
#endif
#ifdef HAVE_PAM
pam_handle_t *pamh = NULL;
int pamr;
#endif
static struct option longopts[] = {
{ "stop", 0, NULL, 'K'},
{ "nicelevel", 1, NULL, 'N'},
{ "retry", 1, NULL, 'R'},
{ "start", 0, NULL, 'S'},
{ "background", 0, NULL, 'b'},
{ "chuid", 1, NULL, 'c'},
{ "chdir", 1, NULL, 'd'},
{ "group", 1, NULL, 'g'},
{ "make-pidfile", 0, NULL, 'm'},
{ "name", 1, NULL, 'n'},
{ "oknodo", 0, NULL, 'o'},
{ "pidfile", 1, NULL, 'p'},
{ "quiet", 0, NULL, 'q'},
{ "signal", 1, NULL, 's'},
{ "test", 0, NULL, 't'},
{ "user", 1, NULL, 'u'},
{ "chroot", 1, NULL, 'r'},
{ "verbose", 0, NULL, 'v'},
{ "exec", 1, NULL, 'x'},
{ "stdout", 1, NULL, '1'},
{ "stderr", 1, NULL, '2'},
{ NULL, 0, NULL, 0}
};
int c;
bool start = false;
bool stop = false;
bool oknodo = false;
bool test = false;
bool quiet = false;
bool verbose = false;
char *exec = NULL;
char *cmd = NULL;
char *pidfile = NULL;
int sig = SIGTERM;
uid_t uid = 0;
int nicelevel = 0;
bool background = false;
bool makepidfile = false;
uid_t ch_uid = 0;
gid_t ch_gid = 0;
char *ch_root = NULL;
char *ch_dir = NULL;
int tid = 0;
char *redirect_stderr = NULL;
char *redirect_stdout = NULL;
int stdout_fd;
int stderr_fd;
pid_t pid;
struct timeval tv;
int i;
char *svcname = getenv ("SVCNAME");
char *env;
progname = argv[0];
atexit (cleanup);
signal (SIGINT, handle_signal);
signal (SIGQUIT, handle_signal);
signal (SIGTERM, handle_signal);
while ((c = getopt_long (argc, argv,
"KN:R:Sbc:d:g:mn:op:qs:tu:r:vx:1:2:",
longopts, (int *) 0)) != -1)
switch (c)
{
case 'K': /* --stop */
stop = true;
break;
case 'N': /* --nice */
if (sscanf (optarg, "%d", &nicelevel) != 1)
eerrorx ("%s: invalid nice level `%s'", progname, optarg);
break;
case 'R': /* --retry <schedule>|<timeout> */
parse_schedule (optarg, sig);
break;
case 'S': /* --start */
start = true;
break;
case 'b': /* --background */
background = true;
break;
case 'c': /* --chuid <username>|<uid> */
/* we copy the string just in case we need the
* argument later. */
{
char *p = optarg;
char *cu = strsep (&p, ":");
changeuser = strdup (cu);
if (sscanf (cu, "%d", &tid) != 1)
{
struct passwd *pw = getpwnam (cu);
if (! pw)
eerrorx ("%s: user `%s' not found", progname, cu);
ch_uid = pw->pw_uid;
}
else
ch_uid = tid;
if (p)
{
char *cg = strsep (&p, ":");
if (sscanf (cg, "%d", &tid) != 1)
{
struct group *gr = getgrnam (cg);
if (! gr)
eerrorx ("%s: group `%s' not found", progname, cg);
ch_gid = gr->gr_gid;
}
else
ch_gid = tid;
}
}
break;
case 'd': /* --chdir /new/dir */
ch_dir = optarg;
break;
case 'g': /* --group <group>|<gid> */
if (sscanf (optarg, "%d", &tid) != 1)
{
struct group *gr = getgrnam (optarg);
if (! gr)
eerrorx ("%s: group `%s' not found", progname, optarg);
ch_gid = gr->gr_gid;
}
else
ch_gid = tid;
break;
case 'm': /* --make-pidfile */
makepidfile = true;
break;
case 'n': /* --name <process-name> */
cmd = optarg;
break;
case 'o': /* --oknodo */
oknodo = true;
break;
case 'p': /* --pidfile <pid-file> */
pidfile = optarg;
break;
case 'q': /* --quiet */
quiet = true;
break;
case 's': /* --signal <signal> */
sig = parse_signal (optarg);
break;
case 't': /* --test */
test = true;
break;
case 'u': /* --user <username>|<uid> */
if (sscanf (optarg, "%d", &tid) != 1)
{
struct passwd *pw = getpwnam (optarg);
if (! pw)
eerrorx ("%s: user `%s' not found", progname, optarg);
uid = pw->pw_uid;
}
else
uid = tid;
break;
case 'r': /* --chroot /new/root */
ch_root = optarg;
break;
case 'v': /* --verbose */
verbose = true;
break;
case 'x': /* --exec <executable> */
exec = optarg;
break;
case '1': /* --stdout /path/to/stdout.lgfile */
redirect_stdout = optarg;
break;
case '2': /* --stderr /path/to/stderr.logfile */
redirect_stderr = optarg;
break;
default:
exit (EXIT_FAILURE);
}
/* Respect RC as well as how we are called */
if (rc_is_env ("RC_QUIET", "yes") && ! verbose)
quiet = true;
if (start == stop)
eerrorx ("%s: need one of --start or --stop", progname);
if (start && ! exec)
eerrorx ("%s: --start needs --exec", progname);
if (stop && ! exec && ! pidfile && ! cmd && ! uid)
eerrorx ("%s: --stop needs --exec, --pidfile, --name or --user", progname);
if (makepidfile && ! pidfile)
eerrorx ("%s: --make-pidfile is only relevant with --pidfile", progname);
if (background && ! start)
eerrorx ("%s: --background is only relevant with --start", progname);
if ((redirect_stdout || redirect_stderr) && ! background)
eerrorx ("%s: --stdout and --stderr are only relevant with --background",
progname);
argc -= optind;
argv += optind;
/* Validate that the binary rc_exists if we are starting */
if (exec && start)
{
char *tmp;
if (ch_root)
tmp = rc_strcatpaths (ch_root, exec, NULL);
else
tmp = exec;
if (! rc_is_file (tmp))
{
eerror ("%s: %s does not exist", progname, tmp);
if (ch_root)
free (tmp);
exit (EXIT_FAILURE);
}
if (ch_root)
free (tmp);
}
if (stop)
{
int result;
if (! schedule)
{
if (test || oknodo)
parse_schedule ("0", sig);
else
parse_schedule (NULL, sig);
}
result = run_stop_schedule (exec, cmd, pidfile, uid, quiet, verbose, test);
if (test || oknodo)
return (result > 0 ? EXIT_SUCCESS : EXIT_FAILURE);
if (result < 1)
exit (result == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
if (pidfile && rc_is_file (pidfile))
unlink (pidfile);
if (svcname)
rc_set_service_daemon (svcname, exec, cmd, pidfile, false);
exit (EXIT_SUCCESS);
}
if (do_stop (exec, cmd, pidfile, uid, 0, true, false, true) > 0)
eerrorx ("%s: %s is already running", progname, exec);
if (test)
{
if (quiet)
exit (EXIT_SUCCESS);
einfon ("Would start %s", exec);
while (argc-- > 0)
printf("%s ", *argv++);
printf ("\n");
eindent ();
if (ch_uid != 0)
einfo ("as user %d", ch_uid);
if (ch_gid != 0)
einfo ("as group %d", ch_gid);
if (ch_root)
einfo ("in root `%s'", ch_root);
if (ch_dir)
einfo ("in dir `%s'", ch_dir);
if (nicelevel != 0)
einfo ("with a priority of %d", nicelevel);
eoutdent ();
exit (EXIT_SUCCESS);
}
/* Ensure this is unset, so if the daemon does /etc/init.d/foo
Then we filter the environment accordingly */
unsetenv ("RC_SOFTLEVEL");
if (verbose)
{
ebegin ("Detaching to start `%s'", exec);
eindent ();
}
if (background)
signal (SIGCHLD, handle_signal);
*--argv = exec;
if ((pid = fork ()) == -1)
eerrorx ("%s: fork: %s", progname, strerror (errno));
/* Child process - lets go! */
if (pid == 0)
{
pid_t mypid = getpid ();
#ifdef TIOCNOTTY
tty_fd = open("/dev/tty", O_RDWR);
#endif
devnull_fd = open("/dev/null", O_RDWR);
if (nicelevel)
{
if (setpriority (PRIO_PROCESS, mypid, nicelevel) == -1)
eerrorx ("%s: setpritory %d: %s", progname, nicelevel,
strerror(errno));
}
if (ch_root && chroot (ch_root) < 0)
eerrorx ("%s: chroot `%s': %s", progname, ch_root, strerror (errno));
if (ch_dir && chdir (ch_dir) < 0)
eerrorx ("%s: chdir `%s': %s", progname, ch_dir, strerror (errno));
if (makepidfile && pidfile)
{
FILE *fp = fopen (pidfile, "w");
if (! fp)
eerrorx ("%s: fopen `%s': %s", progname, pidfile, strerror
(errno));
fprintf (fp, "%d\n", mypid);
fclose (fp);
}
#ifdef HAVE_PAM
if (changeuser != NULL)
pamr = pam_start ("start-stop-daemon", changeuser, &conv, &pamh);
else
pamr = pam_start ("start-stop-daemon", "nobody", &conv, &pamh);
if (pamr == PAM_SUCCESS)
pamr = pam_authenticate (pamh, PAM_SILENT);
if (pamr == PAM_SUCCESS)
pamr = pam_acct_mgmt (pamh, PAM_SILENT);
if (pamr == PAM_SUCCESS)
pamr = pam_open_session (pamh, PAM_SILENT);
if (pamr != PAM_SUCCESS)
eerrorx ("%s: pam error: %s", progname, pam_strerror(pamh, pamr));
#endif
if ((ch_gid) && setgid(ch_gid))
eerrorx ("%s: unable to set groupid to %d", progname, ch_gid);
if (changeuser && ch_gid)
if (initgroups (changeuser, ch_gid))
eerrorx ("%s: initgroups (%s, %d)", progname, changeuser, ch_gid);
if (ch_uid && setuid (ch_uid))
eerrorx ("%s: unable to set userid to %d", progname, ch_uid);
else
{
struct passwd *passwd = getpwuid (ch_uid);
if (passwd)
{
unsetenv ("HOME");
if (passwd->pw_dir)
setenv ("HOME", passwd->pw_dir, 1);
unsetenv ("USER");
if (passwd->pw_name)
setenv ("USER", passwd->pw_name, 1);
}
}
/* Close any fd's to the passwd database */
endpwent ();
#ifdef TIOCNOTTY
ioctl(tty_fd, TIOCNOTTY, 0);
close(tty_fd);
#endif
/* Clean the environment of any RC_ variables */
STRLIST_FOREACH (environ, env, i)
if (env && strncmp (env, "RC_", 3) != 0)
{
/* For the path character, remove the rcscript bin dir from it */
if (strncmp (env, "PATH=" RC_LIBDIR "bin:",
strlen ("PATH=" RC_LIBDIR "bin:")) == 0)
{
char *path = env;
char *newpath;
int len;
path += strlen ("PATH=" RC_LIBDIR "bin:");
len = sizeof (char *) * strlen (path) + 6;
newpath = rc_xmalloc (len);
snprintf (newpath, len, "PATH=%s", path);
newenv = rc_strlist_add (newenv, newpath);
free (newpath);
}
else
newenv = rc_strlist_add (newenv, env);
}
umask (022);
stdout_fd = devnull_fd;
stderr_fd = devnull_fd;
if (redirect_stdout)
{
if ((stdout_fd = open (redirect_stdout, O_WRONLY | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR)) == -1)
eerrorx ("%s: unable to open the logfile for stdout `%s': %s",
progname, redirect_stdout, strerror (errno));
}
if (redirect_stderr)
{
if ((stderr_fd = open (redirect_stderr, O_WRONLY | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR)) == -1)
eerrorx ("%s: unable to open the logfile for stderr `%s': %s",
progname, redirect_stderr, strerror (errno));
}
dup2 (devnull_fd, STDIN_FILENO);
if (background)
{
dup2 (stdout_fd, STDOUT_FILENO);
dup2 (stderr_fd, STDERR_FILENO);
}
for (i = getdtablesize () - 1; i >= 3; --i)
close(i);
setsid ();
execve (exec, argv, newenv);
#ifdef HAVE_PAM
if (pamr == PAM_SUCCESS)
pam_close_session (pamh, PAM_SILENT);
#endif
eerrorx ("%s: failed to exec `%s': %s", progname, exec, strerror (errno));
}
/* Parent process */
if (! background)
{
/* As we're not backgrounding the process, wait for our pid to return */
int status = 0;
int savepid = pid;
errno = 0;
do
{
pid = waitpid (savepid, &status, 0);
if (pid < 1)
{
eerror ("waitpid %d: %s", savepid, strerror (errno));
return (-1);
}
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
{
if (! quiet)
eerrorx ("%s: failed to started `%s'", progname, exec);
exit (EXIT_FAILURE);
}
pid = savepid;
}
/* Wait a little bit and check that process is still running
We do this as some badly written daemons fork and then barf */
if (START_WAIT > 0)
{
struct timeval stopat;
struct timeval now;
if (gettimeofday (&stopat, NULL) != 0)
eerrorx ("%s: gettimeofday: %s", progname, strerror (errno));
stopat.tv_usec += START_WAIT;
while (1)
{
bool alive = false;
tv.tv_sec = 0;
tv.tv_usec = POLL_INTERVAL;
if (select (0, 0, 0, 0, &tv) < 0)
{
/* Let our signal handler handle the interupt */
if (errno != EINTR)
eerrorx ("%s: select: %s", progname, strerror (errno));
}
if (gettimeofday (&now, NULL) != 0)
eerrorx ("%s: gettimeofday: %s", progname, strerror (errno));
/* This is knarly.
If we backgrounded then we know the exact pid.
Otherwise if we have a pidfile then it *may* know the exact pid.
Failing that, we'll have to query processes.
We sleep first as some programs like ntp like to fork, and write
their pidfile a LONG time later. */
if (background)
{
if (kill (pid, 0) == 0)
alive = true;
}
else
{
if (pidfile && rc_exists (pidfile))
{
if (do_stop (NULL, NULL, pidfile, uid, 0, true, false, true) > 0)
alive = true;
}
else
{
if (do_stop (exec, cmd, NULL, uid, 0, true, false, true) > 0)
alive = true;
}
}
if (! alive)
eerrorx ("%s: %s died", progname, exec);
if (timercmp (&now, &stopat, >))
break;
}
}
if (svcname)
rc_set_service_daemon (svcname, exec, cmd, pidfile, true);
exit (EXIT_SUCCESS);
}