aboutsummaryrefslogtreecommitdiff
path: root/src/runscript.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/runscript.c')
-rw-r--r--src/runscript.c1311
1 files changed, 0 insertions, 1311 deletions
diff --git a/src/runscript.c b/src/runscript.c
deleted file mode 100644
index 1385bb02..00000000
--- a/src/runscript.c
+++ /dev/null
@@ -1,1311 +0,0 @@
-/*
- * runscript.c
- * Handle launching of init scripts.
- */
-
-/*
- * Copyright 2007 Roy Marples
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/select.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <termios.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifdef __linux__
-# include <pty.h>
-#else
-# include <libutil.h>
-#endif
-
-#include "builtins.h"
-#include "einfo.h"
-#include "rc.h"
-#include "rc-misc.h"
-#include "rc-plugin.h"
-#include "strlist.h"
-
-#define SELINUX_LIB RC_LIBDIR "/runscript_selinux.so"
-
-#define PREFIX_LOCK RC_SVCDIR "/prefix.lock"
-
-/* usecs to wait while we poll the fifo */
-#define WAIT_INTERVAL 20000000
-
-/* max secs to wait until a service comes up */
-#define WAIT_MAX 300
-
-#define ONE_SECOND 1000000000
-
-static const char *applet = NULL;
-static char *service = NULL;
-static char *exclusive = NULL;
-static char *mtime_test = NULL;
-static rc_depinfo_t *deptree = NULL;
-static char **services = NULL;
-static char **tmplist = NULL;
-static char **providelist = NULL;
-static char **restart_services = NULL;
-static char **need_services = NULL;
-static char **use_services = NULL;
-static char **env = NULL;
-static char *tmp = NULL;
-static char *softlevel = NULL;
-static bool sighup = false;
-static char *ibsave = NULL;
-static bool in_background = false;
-static rc_hook_t hook_out = 0;
-static pid_t service_pid = 0;
-static char *prefix = NULL;
-static bool prefix_locked = false;
-static int signal_pipe[2] = { -1, -1 };
-static int master_tty = -1;
-
-extern char **environ;
-
-static const char *const types_b[] = { "broken", NULL };
-static const char *const types_n[] = { "ineed", NULL };
-static const char *const types_nu[] = { "ineed", "iuse", NULL };
-static const char *const types_nua[] = { "ineed", "iuse", "iafter", NULL };
-
-static const char *const types_m[] = { "needsme", NULL };
-static const char *const types_mua[] = { "needsme", "usesme", "beforeme", NULL };
-
-#ifdef __linux__
-static void (*selinux_run_init_old) (void);
-static void (*selinux_run_init_new) (int argc, char **argv);
-
-static void setup_selinux (int argc, char **argv);
-
-static void setup_selinux (int argc, char **argv)
-{
- void *lib_handle = NULL;
-
- if (! exists (SELINUX_LIB))
- return;
-
- lib_handle = dlopen (SELINUX_LIB, RTLD_NOW | RTLD_GLOBAL);
- if (! lib_handle) {
- eerror ("dlopen: %s", dlerror ());
- return;
- }
-
- selinux_run_init_old = (void (*)(void))
- dlfunc (lib_handle, "selinux_runscript");
- selinux_run_init_new = (void (*)(int, char **))
- dlfunc (lib_handle, "selinux_runscript2");
-
- /* Use new run_init if it exists, else fall back to old */
- if (selinux_run_init_new)
- selinux_run_init_new (argc, argv);
- else if (selinux_run_init_old)
- selinux_run_init_old ();
- else
- /* This shouldnt happen... probably corrupt lib */
- eerrorx ("run_init is missing from runscript_selinux.so!");
-
- dlclose (lib_handle);
-}
-#endif
-
-static void handle_signal (int sig)
-{
- int serrno = errno;
- char signame[10] = { '\0' };
- struct winsize ws;
-
- switch (sig) {
- case SIGHUP:
- sighup = true;
- break;
-
- case SIGCHLD:
- if (signal_pipe[1] > -1) {
- if (write (signal_pipe[1], &sig, sizeof (sig)) == -1)
- eerror ("%s: send: %s", service, strerror (errno));
- } else
- rc_waitpid (-1);
- break;
-
- case SIGWINCH:
- if (master_tty >= 0) {
- ioctl (fileno (stdout), TIOCGWINSZ, &ws);
- ioctl (master_tty, TIOCSWINSZ, &ws);
- }
- break;
-
- case SIGINT:
- if (! signame[0])
- snprintf (signame, sizeof (signame), "SIGINT");
- case SIGTERM:
- if (! signame[0])
- snprintf (signame, sizeof (signame), "SIGTERM");
- case SIGQUIT:
- if (! signame[0])
- snprintf (signame, sizeof (signame), "SIGQUIT");
- /* Send the signal to our children too */
- if (service_pid > 0)
- kill (service_pid, sig);
- eerrorx ("%s: caught %s, aborting", applet, signame);
-
- default:
- eerror ("%s: caught unknown signal %d", applet, sig);
- }
-
- /* Restore errno */
- errno = serrno;
-}
-
-static time_t get_mtime (const char *pathname, bool follow_link)
-{
- struct stat buf;
- int retval;
-
- if (! pathname)
- return (0);
-
- retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
- if (! retval)
- return (buf.st_mtime);
-
- errno = 0;
- return (0);
-}
-
-static bool in_control ()
-{
- char *path;
- time_t mtime;
- const char *tests[] = { "starting", "started", "stopping",
- "inactive", "wasinactive", NULL };
- int i = 0;
-
- if (sighup)
- return (false);
-
- if (! mtime_test || ! exists (mtime_test))
- return (false);
-
- if (rc_service_state (applet) & RC_SERVICE_STOPPED)
- return (false);
-
- if (! (mtime = get_mtime (mtime_test, false)))
- return (false);
-
- while (tests[i]) {
- path = rc_strcatpaths (RC_SVCDIR, tests[i], applet, (char *) NULL);
- if (exists (path)) {
- time_t m = get_mtime (path, false);
- if (mtime < m && m != 0) {
- free (path);
- return (false);
- }
- }
- free (path);
- i++;
- }
-
- return (true);
-}
-
-static void uncoldplug ()
-{
- char *cold = rc_strcatpaths (RC_SVCDIR, "coldplugged", applet, (char *) NULL);
- if (exists (cold) && unlink (cold) != 0)
- eerror ("%s: unlink `%s': %s", applet, cold, strerror (errno));
- free (cold);
-}
-
-static void start_services (char **list) {
- char *svc;
- int i;
- rc_service_state_t state = rc_service_state (service);
-
- if (! list)
- return;
-
- if (state & RC_SERVICE_INACTIVE ||
- state & RC_SERVICE_WASINACTIVE ||
- state & RC_SERVICE_STARTING ||
- state & RC_SERVICE_STARTED)
- {
- STRLIST_FOREACH (list, svc, i) {
- if (rc_service_state (svc) & RC_SERVICE_STOPPED) {
- if (state & RC_SERVICE_INACTIVE ||
- state & RC_SERVICE_WASINACTIVE)
- {
- rc_service_schedule_start (service, svc);
- ewarn ("WARNING: %s is scheduled to started when %s has started",
- svc, applet);
- } else
- rc_service_start (svc);
- }
- }
- }
-}
-
-static void restore_state (void)
-{
- rc_service_state_t state;
-
- if (rc_in_plugin || ! in_control ())
- return;
-
- state = rc_service_state (applet);
- if (state & RC_SERVICE_STOPPING) {
-
- if (state & RC_SERVICE_WASINACTIVE)
- rc_service_mark (applet, RC_SERVICE_INACTIVE);
- else
- rc_service_mark (applet, RC_SERVICE_STARTED);
- if (rc_runlevel_stopping ())
- rc_service_mark (applet, RC_SERVICE_FAILED);
- } else if (state & RC_SERVICE_STARTING) {
- if (state & RC_SERVICE_WASINACTIVE)
- rc_service_mark (applet, RC_SERVICE_INACTIVE);
- else
- rc_service_mark (applet, RC_SERVICE_STOPPED);
- if (rc_runlevel_starting ())
- rc_service_mark (applet, RC_SERVICE_FAILED);
- }
-
- if (exclusive)
- unlink (exclusive);
- free (exclusive);
- exclusive = NULL;
-}
-
-static void cleanup (void)
-{
- restore_state ();
-
- if (! rc_in_plugin) {
- if (prefix_locked)
- unlink (PREFIX_LOCK);
- if (hook_out) {
- rc_plugin_run (hook_out, applet);
- if (hook_out == RC_HOOK_SERVICE_START_DONE)
- rc_plugin_run (RC_HOOK_SERVICE_START_OUT, applet);
- else if (hook_out == RC_HOOK_SERVICE_STOP_DONE)
- rc_plugin_run (RC_HOOK_SERVICE_STOP_OUT, applet);
- }
-
- if (restart_services)
- start_services (restart_services);
- }
-
- rc_plugin_unload ();
- rc_deptree_free (deptree);
- rc_strlist_free (services);
- rc_strlist_free (providelist);
- rc_strlist_free (need_services);
- rc_strlist_free (use_services);
- rc_strlist_free (restart_services);
- rc_strlist_free (tmplist);
- free (ibsave);
-
- rc_strlist_free (env);
-
- if (mtime_test)
- {
- if (! rc_in_plugin)
- unlink (mtime_test);
- free (mtime_test);
- }
- free (exclusive);
- free (service);
- free (prefix);
- free (softlevel);
-}
-
-static int write_prefix (const char *buffer, size_t bytes, bool *prefixed) {
- unsigned int i;
- const char *ec = ecolor (ECOLOR_HILITE);
- const char *ec_normal = ecolor (ECOLOR_NORMAL);
- ssize_t ret = 0;
- int fd = fileno (stdout);
-
- for (i = 0; i < bytes; i++) {
- /* We don't prefix escape codes, like eend */
- if (buffer[i] == '\033')
- *prefixed = true;
-
- if (! *prefixed) {
- ret += write (fd, ec, strlen (ec));
- ret += write (fd, prefix, strlen (prefix));
- ret += write (fd, ec_normal, strlen (ec_normal));
- ret += write (fd, "|", 1);
- *prefixed = true;
- }
-
- if (buffer[i] == '\n')
- *prefixed = false;
- ret += write (fd, buffer + i, 1);
- }
-
- return (ret);
-}
-
-static bool svc_exec (const char *arg1, const char *arg2)
-{
- bool execok;
- int fdout = fileno (stdout);
- struct termios tt;
- struct winsize ws;
- int i;
- int flags = 0;
- fd_set rset;
- int s;
- char *buffer;
- size_t bytes;
- bool prefixed = false;
- int selfd;
- int slave_tty;
-
- /* Setup our signal pipe */
- if (pipe (signal_pipe) == -1)
- eerrorx ("%s: pipe: %s", service, applet);
- for (i = 0; i < 2; i++)
- if ((flags = fcntl (signal_pipe[i], F_GETFD, 0) == -1 ||
- fcntl (signal_pipe[i], F_SETFD, flags | FD_CLOEXEC) == -1))
- eerrorx ("%s: fcntl: %s", service, strerror (errno));
-
- /* Open a pty for our prefixed output
- * We do this instead of mapping pipes to stdout, stderr so that
- * programs can tell if they're attached to a tty or not.
- * The only loss is that we can no longer tell the difference
- * between the childs stdout or stderr */
- master_tty = slave_tty = -1;
- if (prefix && isatty (fdout)) {
- tcgetattr (fdout, &tt);
- ioctl (fdout, TIOCGWINSZ, &ws);
-
- /* If the below call fails due to not enough ptys then we don't
- * prefix the output, but we still work */
- openpty (&master_tty, &slave_tty, NULL, &tt, &ws);
- }
-
- service_pid = vfork();
- if (service_pid == -1)
- eerrorx ("%s: vfork: %s", service, strerror (errno));
- if (service_pid == 0) {
- if (slave_tty >= 0) {
- /* Hmmm, this shouldn't work in a vfork, but it does which is
- * good for us */
- close (master_tty);
-
- dup2 (slave_tty, 1);
- dup2 (slave_tty, 2);
- if (slave_tty > 2)
- close (slave_tty);
- }
-
- if (exists (RC_SVCDIR "/runscript.sh")) {
- execl (RC_SVCDIR "/runscript.sh", RC_SVCDIR "/runscript.sh",
- service, arg1, arg2, (char *) NULL);
- eerror ("%s: exec `" RC_SVCDIR "/runscript.sh': %s",
- service, strerror (errno));
- _exit (EXIT_FAILURE);
- } else {
- execl (RC_LIBDIR "/sh/runscript.sh", RC_LIBDIR "/sh/runscript.sh",
- service, arg1, arg2, (char *) NULL);
- eerror ("%s: exec `" RC_LIBDIR "/sh/runscript.sh': %s",
- service, strerror (errno));
- _exit (EXIT_FAILURE);
- }
- }
-
- selfd = MAX (master_tty, signal_pipe[0]) + 1;
- buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
- while (1) {
- FD_ZERO (&rset);
- FD_SET (signal_pipe[0], &rset);
- if (master_tty >= 0)
- FD_SET (master_tty, &rset);
-
- if ((s = select (selfd, &rset, NULL, NULL, NULL)) == -1) {
- if (errno != EINTR) {
- eerror ("%s: select: %s", service, strerror (errno));
- break;
- }
- }
-
- if (s > 0) {
- if (master_tty >= 0 && FD_ISSET (master_tty, &rset)) {
- bytes = read (master_tty, buffer, RC_LINEBUFFER);
- write_prefix (buffer, bytes, &prefixed);
- }
-
- /* Only SIGCHLD signals come down this pipe */
- if (FD_ISSET (signal_pipe[0], &rset))
- break;
- }
- }
-
- free (buffer);
- close (signal_pipe[0]);
- close (signal_pipe[1]);
- signal_pipe[0] = signal_pipe[1] = -1;
-
- if (master_tty >= 0) {
- signal (SIGWINCH, SIG_IGN);
- close (master_tty);
- master_tty = -1;
- }
-
- execok = rc_waitpid (service_pid) == 0 ? true : false;
- service_pid = 0;
-
- return (execok);
-}
-
-static bool svc_wait (rc_depinfo_t *depinfo, const char *svc)
-{
- char *s;
- char *fifo;
- struct timespec ts;
- int nloops = WAIT_MAX * (ONE_SECOND / WAIT_INTERVAL);
- bool retval = false;
- bool forever = false;
- char **keywords = NULL;
- int i;
-
- if (! service)
- return (false);
-
- /* Some services don't have a timeout, like checkroot and checkfs */
- keywords = rc_deptree_depend (depinfo, svc, "keywords");
- STRLIST_FOREACH (keywords, s, i) {
- if (strcmp (s, "notimeout") == 0) {
- forever = true;
- break;
- }
- }
- rc_strlist_free (keywords);
-
- fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename_c (svc), (char *) NULL);
- ts.tv_sec = 0;
- ts.tv_nsec = WAIT_INTERVAL;
-
- while (nloops) {
- if (! exists (fifo)) {
- retval = true;
- break;
- }
-
- if (nanosleep (&ts, NULL) == -1) {
- if (errno != EINTR)
- break;
- }
-
- if (! forever)
- nloops --;
- }
-
- if (! exists (fifo))
- retval = true;
- free (fifo);
- return (retval);
-}
-
-static rc_service_state_t svc_status ()
-{
- char status[10];
- int (*e) (const char *fmt, ...) = &einfo;
-
- rc_service_state_t state = rc_service_state (service);
-
- if (state & RC_SERVICE_STOPPING) {
- snprintf (status, sizeof (status), "stopping");
- e = &ewarn;
- } else if (state & RC_SERVICE_STARTING) {
- snprintf (status, sizeof (status), "starting");
- e = &ewarn;
- } else if (state & RC_SERVICE_INACTIVE) {
- snprintf (status, sizeof (status), "inactive");
- e = &ewarn;
- } else if (state & RC_SERVICE_STARTED) {
- if (geteuid () == 0 && rc_service_daemons_crashed (service)) {
- snprintf (status, sizeof (status), "crashed");
- e = &eerror;
- } else
- snprintf (status, sizeof (status), "started");
- } else
- snprintf (status, sizeof (status), "stopped");
-
- e ("status: %s", status);
- return (state);
-}
-
-static void make_exclusive ()
-{
- char *path;
- int i;
-
- /* We create a fifo so that other services can wait until we complete */
- if (! exclusive)
- exclusive = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
-
- if (mkfifo (exclusive, 0600) != 0 && errno != EEXIST &&
- (errno != EACCES || geteuid () == 0))
- eerrorx ("%s: unable to create fifo `%s': %s",
- applet, exclusive, strerror (errno));
-
- path = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
- i = strlen (path) + 16;
- mtime_test = xmalloc (sizeof (char) * i);
- snprintf (mtime_test, i, "%s.%d", path, getpid ());
- free (path);
-
- if (exists (mtime_test) && unlink (mtime_test) != 0) {
- eerror ("%s: unlink `%s': %s",
- applet, mtime_test, strerror (errno));
- free (mtime_test);
- mtime_test = NULL;
- return;
- }
-
- if (symlink (service, mtime_test) != 0) {
- eerror ("%s: symlink `%s' to `%s': %s",
- applet, service, mtime_test, strerror (errno));
- free (mtime_test);
- mtime_test = NULL;
- }
-}
-
-static void unlink_mtime_test ()
-{
- if (unlink (mtime_test) != 0)
- eerror ("%s: unlink `%s': %s", applet, mtime_test, strerror (errno));
- free (mtime_test);
- mtime_test = NULL;
-}
-
-static void get_started_services ()
-{
- rc_strlist_free (tmplist);
- tmplist = rc_services_in_state (RC_SERVICE_INACTIVE);
- rc_strlist_free (restart_services);
- restart_services = rc_services_in_state (RC_SERVICE_STARTED);
- rc_strlist_join (&restart_services, tmplist);
- rc_strlist_free (tmplist);
- tmplist = NULL;
-}
-
-static void svc_start (bool deps)
-{
- bool started;
- bool background = false;
- char *svc;
- char *svc2;
- int i;
- int j;
- int depoptions = RC_DEP_TRACE;
- const char *const svcl[] = { applet, NULL };
- rc_service_state_t state;
-
- state = rc_service_state (service);
-
- if (rc_yesno (getenv ("IN_HOTPLUG")) || in_background) {
- if (! state & RC_SERVICE_INACTIVE &&
- ! state & RC_SERVICE_STOPPED)
- exit (EXIT_FAILURE);
- background = true;
- }
-
- if (state & RC_SERVICE_STARTED) {
- ewarn ("WARNING: %s has already been started", applet);
- return;
- } else if (state & RC_SERVICE_STARTING)
- ewarnx ("WARNING: %s is already starting", applet);
- else if (state & RC_SERVICE_STOPPING)
- ewarnx ("WARNING: %s is stopping", applet);
- else if (state & RC_SERVICE_INACTIVE && ! background)
- ewarnx ("WARNING: %s has already started, but is inactive", applet);
-
- if (! rc_service_mark (service, RC_SERVICE_STARTING))
- eerrorx ("ERROR: %s has been started by something else", applet);
-
- make_exclusive (service);
-
- hook_out = RC_HOOK_SERVICE_START_OUT;
- rc_plugin_run (RC_HOOK_SERVICE_START_IN, applet);
-
- if (rc_conf_yesno ("rc_depend_strict"))
- depoptions |= RC_DEP_STRICT;
-
- if (rc_runlevel_starting ())
- depoptions |= RC_DEP_START;
-
- if (deps) {
- if (! deptree && ((deptree = _rc_deptree_load (NULL)) == NULL))
- eerrorx ("failed to load deptree");
-
- rc_strlist_free (services);
- services = rc_deptree_depends (deptree, types_b, svcl, softlevel, 0);
- if (services) {
- eerrorn ("ERROR: `%s' needs ", applet);
- STRLIST_FOREACH (services, svc, i) {
- if (i > 0)
- fprintf (stderr, ", ");
- fprintf (stderr, "%s", svc);
- }
- exit (EXIT_FAILURE);
- }
- rc_strlist_free (services);
- services = NULL;
-
- rc_strlist_free (need_services);
- need_services = rc_deptree_depends (deptree, types_n, svcl,
- softlevel, depoptions);
-
- rc_strlist_free (use_services);
- use_services = rc_deptree_depends (deptree, types_nu, svcl,
- softlevel, depoptions);
-
- if (! rc_runlevel_starting ()) {
- STRLIST_FOREACH (use_services, svc, i)
- if (rc_service_state (svc) & RC_SERVICE_STOPPED) {
- pid_t pid = rc_service_start (svc);
- if (! rc_conf_yesno ("rc_parallel"))
- rc_waitpid (pid);
- }
- }
-
- /* Now wait for them to start */
- services = rc_deptree_depends (deptree, types_nua, svcl,
- softlevel, depoptions);
-
- /* We use tmplist to hold our scheduled by list */
- rc_strlist_free (tmplist);
- tmplist = NULL;
-
- STRLIST_FOREACH (services, svc, i) {
- rc_service_state_t svcs = rc_service_state (svc);
- if (svcs & RC_SERVICE_STARTED)
- continue;
-
- /* Don't wait for services which went inactive but are now in
- * starting state which we are after */
- if (svcs & RC_SERVICE_STARTING &&
- svcs & RC_SERVICE_WASINACTIVE) {
- bool use = false;
- STRLIST_FOREACH (use_services, svc2, j)
- if (strcmp (svc, svc2) == 0) {
- use = true;
- break;
- }
- if (! use)
- continue;
- }
-
- if (! svc_wait (deptree, svc))
- eerror ("%s: timed out waiting for %s", applet, svc);
- if ((svcs = rc_service_state (svc)) & RC_SERVICE_STARTED)
- continue;
-
- STRLIST_FOREACH (need_services, svc2, j)
- if (strcmp (svc, svc2) == 0) {
- if (svcs & RC_SERVICE_INACTIVE ||
- svcs & RC_SERVICE_WASINACTIVE)
- rc_strlist_add (&tmplist, svc);
- else
- eerrorx ("ERROR: cannot start %s as %s would not start",
- applet, svc);
- }
- }
-
- if (tmplist) {
- int n = 0;
- int len = 0;
- char *p;
-
- /* Set the state now, then unlink our exclusive so that
- our scheduled list is preserved */
- rc_service_mark (service, RC_SERVICE_STOPPED);
- unlink_mtime_test ();
-
- STRLIST_FOREACH (tmplist, svc, i) {
- rc_service_schedule_start (svc, service);
- rc_strlist_free (providelist);
- providelist = rc_deptree_depend (deptree, "iprovide", svc);
- STRLIST_FOREACH (providelist, svc2, j)
- rc_service_schedule_start (svc2, service);
-
- len += strlen (svc) + 2;
- n++;
- }
-
- len += 5;
- tmp = xmalloc (sizeof (char) * len);
- p = tmp;
- STRLIST_FOREACH (tmplist, svc, i) {
- if (i > 1) {
- if (i == n)
- p += snprintf (p, len, " or ");
- else
- p += snprintf (p, len, ", ");
- }
- p += snprintf (p, len, "%s", svc);
- }
- ewarnx ("WARNING: %s is scheduled to start when %s has started",
- applet, tmp);
- }
-
- rc_strlist_free (services);
- services = NULL;
- }
-
- if (ibsave)
- setenv ("IN_BACKGROUND", ibsave, 1);
- hook_out = RC_HOOK_SERVICE_START_DONE;
- rc_plugin_run (RC_HOOK_SERVICE_START_NOW, applet);
- started = svc_exec ("start", NULL);
- if (ibsave)
- unsetenv ("IN_BACKGROUND");
-
- if (in_control ()) {
- if (! started)
- eerrorx ("ERROR: %s failed to start", applet);
- } else {
- if (rc_service_state (service) & RC_SERVICE_INACTIVE)
- ewarnx ("WARNING: %s has started, but is inactive", applet);
- else
- ewarnx ("WARNING: %s not under our control, aborting", applet);
- }
-
- rc_service_mark (service, RC_SERVICE_STARTED);
- unlink_mtime_test ();
- hook_out = RC_HOOK_SERVICE_START_OUT;
- rc_plugin_run (RC_HOOK_SERVICE_START_DONE, applet);
-
- if (exclusive)
- unlink (exclusive);
-
- /* Now start any scheduled services */
- rc_strlist_free (services);
- services = rc_services_scheduled (service);
- STRLIST_FOREACH (services, svc, i)
- if (rc_service_state (svc) & RC_SERVICE_STOPPED)
- rc_service_start (svc);
- rc_strlist_free (services);
- services = NULL;
-
- /* Do the same for any services we provide */
- rc_strlist_free (tmplist);
- tmplist = rc_deptree_depend (deptree, "iprovide", applet);
-
- STRLIST_FOREACH (tmplist, svc2, j) {
- rc_strlist_free (services);
- services = rc_services_scheduled (svc2);
- STRLIST_FOREACH (services, svc, i)
- if (rc_service_state (svc) & RC_SERVICE_STOPPED)
- rc_service_start (svc);
- }
-
- hook_out = 0;
- rc_plugin_run (RC_HOOK_SERVICE_START_OUT, applet);
-}
-
-static void svc_stop (bool deps)
-{
- bool stopped;
- const char *const svcl[] = { applet, NULL };
-
- rc_service_state_t state = rc_service_state (service);
-
- if (rc_runlevel_stopping () &&
- state & RC_SERVICE_FAILED)
- exit (EXIT_FAILURE);
-
- if (rc_yesno (getenv ("IN_HOTPLUG")) || in_background)
- if (! (state & RC_SERVICE_STARTED) &&
- ! (state & RC_SERVICE_INACTIVE))
- exit (EXIT_FAILURE);
-
- if (state & RC_SERVICE_STOPPED) {
- ewarn ("WARNING: %s is already stopped", applet);
- return;
- } else if (state & RC_SERVICE_STOPPING)
- ewarnx ("WARNING: %s is already stopping", applet);
-
- if (! rc_service_mark (service, RC_SERVICE_STOPPING))
- eerrorx ("ERROR: %s has been stopped by something else", applet);
-
- make_exclusive (service);
-
- hook_out = RC_HOOK_SERVICE_STOP_OUT;
- rc_plugin_run (RC_HOOK_SERVICE_STOP_IN, applet);
-
- if (! rc_runlevel_stopping () &&
- rc_service_in_runlevel (service, RC_LEVEL_BOOT))
- ewarn ("WARNING: you are stopping a boot service");
-
- if (deps && ! (state & RC_SERVICE_WASINACTIVE)) {
- int depoptions = RC_DEP_TRACE;
- char *svc;
- int i;
-
- if (rc_conf_yesno ("rc_depend_strict"))
- depoptions |= RC_DEP_STRICT;
-
- if (rc_runlevel_stopping ())
- depoptions |= RC_DEP_STOP;
-
- if (! deptree && ((deptree = _rc_deptree_load (NULL)) == NULL))
- eerrorx ("failed to load deptree");
-
- rc_strlist_free (tmplist);
- tmplist = NULL;
- rc_strlist_free (services);
- services = rc_deptree_depends (deptree, types_m, svcl,
- softlevel, depoptions);
- rc_strlist_reverse (services);
- STRLIST_FOREACH (services, svc, i) {
- rc_service_state_t svcs = rc_service_state (svc);
- if (svcs & RC_SERVICE_STARTED ||
- svcs & RC_SERVICE_INACTIVE)
- {
- svc_wait (deptree, svc);
- svcs = rc_service_state (svc);
- if (svcs & RC_SERVICE_STARTED ||
- svcs & RC_SERVICE_INACTIVE)
- {
- pid_t pid = rc_service_stop (svc);
- if (! rc_conf_yesno ("rc_parallel"))
- rc_waitpid (pid);
- rc_strlist_add (&tmplist, svc);
- }
- }
- }
- rc_strlist_free (services);
- services = NULL;
-
- STRLIST_FOREACH (tmplist, svc, i) {
- if (rc_service_state (svc) & RC_SERVICE_STOPPED)
- continue;
-
- /* We used to loop 3 times here - maybe re-do this if needed */
- svc_wait (deptree, svc);
- if (! (rc_service_state (svc) & RC_SERVICE_STOPPED)) {
- if (rc_runlevel_stopping ()) {
- /* If shutting down, we should stop even if a dependant failed */
- if (softlevel &&
- (strcmp (softlevel, RC_LEVEL_SHUTDOWN) == 0 ||
- strcmp (softlevel, RC_LEVEL_REBOOT) == 0 ||
- strcmp (softlevel, RC_LEVEL_SINGLE) == 0))
- continue;
- rc_service_mark (service, RC_SERVICE_FAILED);
- }
-
- eerrorx ("ERROR: cannot stop %s as %s is still up",
- applet, svc);
- }
- }
- rc_strlist_free (tmplist);
- tmplist = NULL;
-
- /* We now wait for other services that may use us and are stopping
- This is important when a runlevel stops */
- services = rc_deptree_depends (deptree, types_mua, svcl,
- softlevel, depoptions);
- STRLIST_FOREACH (services, svc, i) {
- if (rc_service_state (svc) & RC_SERVICE_STOPPED)
- continue;
- svc_wait (deptree, svc);
- }
-
- rc_strlist_free (services);
- services = NULL;
- }
-
- if (ibsave)
- setenv ("IN_BACKGROUND", ibsave, 1);
- hook_out = RC_HOOK_SERVICE_STOP_DONE;
- rc_plugin_run (RC_HOOK_SERVICE_STOP_NOW, applet);
- stopped = svc_exec ("stop", NULL);
- if (ibsave)
- unsetenv ("IN_BACKGROUND");
-
- if (! in_control ())
- ewarnx ("WARNING: %s not under our control, aborting", applet);
-
- if (! stopped)
- eerrorx ("ERROR: %s failed to stop", applet);
-
- if (in_background)
- rc_service_mark (service, RC_SERVICE_INACTIVE);
- else
- rc_service_mark (service, RC_SERVICE_STOPPED);
-
- unlink_mtime_test ();
- hook_out = RC_HOOK_SERVICE_STOP_OUT;
- rc_plugin_run (RC_HOOK_SERVICE_STOP_DONE, applet);
- if (exclusive)
- unlink (exclusive);
- hook_out = 0;
- rc_plugin_run (RC_HOOK_SERVICE_STOP_OUT, applet);
-}
-
-static void svc_restart (bool deps)
-{
- /* This is hairly and a better way needs to be found I think!
- The issue is this - openvpn need net and dns. net can restart
- dns via resolvconf, so you could have openvpn trying to restart dnsmasq
- which in turn is waiting on net which in turn is waiting on dnsmasq.
- The work around is for resolvconf to restart it's services with --nodeps
- which means just that. The downside is that there is a small window when
- our status is invalid.
- One workaround would be to introduce a new status, or status locking. */
- if (! deps) {
- rc_service_state_t state = rc_service_state (service);
- if (state & RC_SERVICE_STARTED || state & RC_SERVICE_INACTIVE)
- svc_exec ("stop", "start");
- else
- svc_exec ("start", NULL);
- return;
- }
-
- if (! (rc_service_state (service) & RC_SERVICE_STOPPED)) {
- get_started_services ();
- svc_stop (deps);
- }
-
- svc_start (deps);
- start_services (restart_services);
- rc_strlist_free (restart_services);
- restart_services = NULL;
-}
-
-#include "_usage.h"
-#define getoptstring "dDsv" getoptstring_COMMON
-#define extraopts "stop | start | restart | describe | zap"
-static struct option longopts[] = {
- { "debug", 0, NULL, 'd'},
- { "ifstarted", 0, NULL, 's'},
- { "nodeps", 0, NULL, 'D'},
- longopts_COMMON
-};
-static const char * const longopts_help[] = {
- "set xtrace when running the script",
- "only run commands when started",
- "ignore dependencies",
- longopts_help_COMMON
-};
-#include "_usage.c"
-
-int runscript (int argc, char **argv)
-{
- int i;
- bool deps = true;
- bool doneone = false;
- char pid[16];
- int retval;
- int opt;
- char *svc;
-
- /* Show help if insufficient args */
- if (argc < 2 || ! exists (argv[1])) {
- fprintf (stderr, "runscript is not meant to be to run directly\n");
- exit (EXIT_FAILURE);
- }
-
- applet = basename_c (argv[1]);
- if (argc < 3)
- usage (EXIT_FAILURE);
-
- if (*argv[1] == '/')
- service = xstrdup (argv[1]);
- else {
- char d[PATH_MAX];
- getcwd (d, sizeof (d));
- i = strlen (d) + strlen (argv[1]) + 2;
- service = xmalloc (sizeof (char) * i);
- snprintf (service, i, "%s/%s", d, argv[1]);
- }
-
- atexit (cleanup);
-
- /* Change dir to / to ensure all init scripts don't use stuff in pwd */
- chdir ("/");
-
-#ifdef __linux__
- /* coldplug events can trigger init scripts, but we don't want to run them
- until after rc sysinit has completed so we punt them to the boot runlevel */
- if (exists ("/dev/.rcsysinit")) {
- eerror ("%s: cannot run until sysvinit completes", applet);
- if (mkdir ("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
- eerrorx ("%s: mkdir `/dev/.rcboot': %s", applet, strerror (errno));
- tmp = rc_strcatpaths ("/dev/.rcboot", applet, (char *) NULL);
- symlink (service, tmp);
- exit (EXIT_FAILURE);
- }
-#endif
-
- if ((softlevel = xstrdup (getenv ("RC_SOFTLEVEL"))) == NULL) {
- /* Ensure our environment is pure
- Also, add our configuration to it */
- env = env_filter ();
- tmplist = env_config ();
- rc_strlist_join (&env, tmplist);
- rc_strlist_free (tmplist);
- tmplist = NULL;
-
- if (env) {
- char *p;
-
-#ifdef __linux__
- /* clearenv isn't portable, but there's no harm in using it
- if we have it */
- clearenv ();
-#else
- char *var;
- /* No clearenv present here then.
- We could manipulate environ directly ourselves, but it seems that
- some kernels bitch about this according to the environ man pages
- so we walk though environ and call unsetenv for each value. */
- while (environ[0]) {
- tmp = xstrdup (environ[0]);
- p = tmp;
- var = strsep (&p, "=");
- unsetenv (var);
- free (tmp);
- }
- tmp = NULL;
-#endif
-
- STRLIST_FOREACH (env, p, i)
- putenv (p);
- /* We don't free our list as that would be null in environ */
- }
-
- softlevel = rc_runlevel_get ();
- }
-
- setenv ("EINFO_LOG", service, 1);
- setenv ("SVCNAME", applet, 1);
-
- /* Set an env var so that we always know our pid regardless of any
- subshells the init script may create so that our mark_service_*
- functions can always instruct us of this change */
- snprintf (pid, sizeof (pid), "%d", (int) getpid ());
- setenv ("RC_RUNSCRIPT_PID", pid, 1);
-
- /* eprefix is kinda klunky, but it works for our purposes */
- if (rc_conf_yesno ("rc_parallel")) {
- int l = 0;
- int ll;
-
- /* Get the longest service name */
- services = rc_services_in_runlevel (NULL);
- STRLIST_FOREACH (services, svc, i) {
- ll = strlen (svc);
- if (ll > l)
- l = ll;
- }
-
- /* Make our prefix string */
- prefix = xmalloc (sizeof (char) * l);
- ll = strlen (applet);
- memcpy (prefix, applet, ll);
- memset (prefix + ll, ' ', l - ll);
- memset (prefix + l, 0, 1);
- eprefix (prefix);
- }
-
-#ifdef __linux__
- /* Ok, we are ready to go, so setup selinux if applicable */
- setup_selinux (argc, argv);
-#endif
-
- /* Punt the first arg as it's our service name */
- argc--;
- argv++;
-
- /* Right then, parse any options there may be */
- while ((opt = getopt_long (argc, argv, getoptstring,
- longopts, (int *) 0)) != -1)
- switch (opt) {
- case 'd':
- setenv ("RC_DEBUG", "yes", 1);
- break;
- case 's':
- if (! (rc_service_state (service) & RC_SERVICE_STARTED))
- exit (EXIT_FAILURE);
- break;
- case 'D':
- deps = false;
- break;
- case_RC_COMMON_GETOPT
- }
-
- /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service
- that is being called and not any dependents */
- if (getenv ("IN_BACKGROUND")) {
- ibsave = xstrdup (getenv ("IN_BACKGROUND"));
- in_background = rc_yesno (ibsave);
- unsetenv ("IN_BACKGROUND");
- }
-
- if (rc_yesno (getenv ("IN_HOTPLUG"))) {
- if (! rc_conf_yesno ("rc_hotplug") || ! rc_service_plugable (applet))
- eerrorx ("%s: not allowed to be hotplugged", applet);
- }
-
- /* Setup a signal handler */
- signal (SIGHUP, handle_signal);
- signal (SIGINT, handle_signal);
- signal (SIGQUIT, handle_signal);
- signal (SIGTERM, handle_signal);
- signal (SIGCHLD, handle_signal);
-
- /* Load our plugins */
- rc_plugin_load ();
-
- /* Now run each option */
- retval = EXIT_SUCCESS;
- while (optind < argc) {
- optarg = argv[optind++];
-
- /* Abort on a sighup here */
- if (sighup)
- exit (EXIT_FAILURE);
-
- if (strcmp (optarg, "status") != 0 &&
- strcmp (optarg, "help") != 0) {
- /* Only root should be able to run us */
- }
-
- /* Export the command we're running.
- This is important as we stamp on the restart function now but
- some start/stop routines still need to behave differently if
- restarting. */
- unsetenv ("RC_CMD");
- setenv ("RC_CMD", optarg, 1);
-
- doneone = true;
-
- if (strcmp (optarg, "describe") == 0 ||
- strcmp (optarg, "help") == 0)
- {
- char *save = prefix;
-
- eprefix (NULL);
- prefix = NULL;
- svc_exec (optarg, NULL);
- eprefix (save);
- } else if (strcmp (optarg, "ineed") == 0 ||
- strcmp (optarg, "iuse") == 0 ||
- strcmp (optarg, "needsme") == 0 ||
- strcmp (optarg, "usesme") == 0 ||
- strcmp (optarg, "iafter") == 0 ||
- strcmp (optarg, "ibefore") == 0 ||
- strcmp (optarg, "iprovide") == 0) {
- int depoptions = RC_DEP_TRACE;
- const char *t[] = { optarg, NULL };
- const char *s[] = { applet, NULL };
-
- if (rc_conf_yesno ("rc_depend_strict"))
- depoptions |= RC_DEP_STRICT;
-
- if (! deptree && ((deptree = _rc_deptree_load (NULL)) == NULL))
- eerrorx ("failed to load deptree");
-
- rc_strlist_free (services);
- services = rc_deptree_depends (deptree, t, s, softlevel, depoptions);
- STRLIST_FOREACH (services, svc, i)
- printf ("%s%s", i == 1 ? "" : " ", svc);
- if (services)
- printf ("\n");
- } else if (strcmp (optarg, "status") == 0) {
- rc_service_state_t r = svc_status (service);
- retval = (int) r;
- if (retval & RC_SERVICE_STARTED)
- retval = 0;
- } else {
- if (geteuid () != 0)
- eerrorx ("%s: root access required", applet);
-
- if (strcmp (optarg, "conditionalrestart") == 0 ||
- strcmp (optarg, "condrestart") == 0)
- {
- if (rc_service_state (service) & RC_SERVICE_STARTED)
- svc_restart (deps);
- } else if (strcmp (optarg, "restart") == 0) {
- svc_restart (deps);
- } else if (strcmp (optarg, "start") == 0) {
- svc_start (deps);
- } else if (strcmp (optarg, "stop") == 0) {
- if (deps && in_background)
- get_started_services ();
-
- svc_stop (deps);
-
- if (deps) {
- if (! in_background &&
- ! rc_runlevel_stopping () &&
- rc_service_state (service) & RC_SERVICE_STOPPED)
- uncoldplug ();
-
- if (in_background &&
- rc_service_state (service) & RC_SERVICE_INACTIVE)
- {
- int j;
- STRLIST_FOREACH (restart_services, svc, j)
- if (rc_service_state (svc) & RC_SERVICE_STOPPED)
- rc_service_schedule_start (service, svc);
- }
- }
- } else if (strcmp (optarg, "zap") == 0) {
- einfo ("Manually resetting %s to stopped state", applet);
- rc_service_mark (applet, RC_SERVICE_STOPPED);
- uncoldplug ();
- } else
- svc_exec (optarg, NULL);
-
- /* We should ensure this list is empty after an action is done */
- rc_strlist_free (restart_services);
- restart_services = NULL;
- }
-
- if (! doneone)
- usage (EXIT_FAILURE);
- }
-
- return (retval);
-}