diff options
author | Roy Marples <roy@marples.name> | 2007-04-05 11:18:42 +0000 |
---|---|---|
committer | Roy Marples <roy@marples.name> | 2007-04-05 11:18:42 +0000 |
commit | 5af58b45146ab5253ca964738f4e45287bf963d4 (patch) | |
tree | 68d3a9a61fa55dd7fe273db776c375f797edaa5b /src/runscript.c |
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.
Diffstat (limited to 'src/runscript.c')
-rw-r--r-- | src/runscript.c | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/src/runscript.c b/src/runscript.c new file mode 100644 index 00000000..bca1195b --- /dev/null +++ b/src/runscript.c @@ -0,0 +1,1097 @@ +/* + * runscript.c + * Handle launching of Gentoo init scripts. + * + * Copyright 1999-2007 Gentoo Foundation + * Distributed under the terms of the GNU General Public License v2 + */ + +#include <sys/types.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <dlfcn.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef __linux__ +#include <libgen.h> +#endif + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-plugin.h" +#include "strlist.h" + +#define RCSCRIPT_HELP RC_LIBDIR "/sh/rc-help.sh" +#define SELINUX_LIB RC_LIBDIR "/runscript_selinux.so" + +static char *applet = NULL; +static char *exclusive = NULL; +static char *mtime_test = NULL; +static rc_depinfo_t *deptree = NULL; +static char **services = NULL; +static char **svclist = NULL; +static char **tmplist = NULL; +static char **providelist = NULL; +static char **types = NULL; +static char **restart_services = NULL; +static char **need_services = NULL; +static char **env = NULL; +static char *mycmd = NULL; +static char *myarg1 = NULL; +static char *myarg2 = 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; + +extern char **environ; + +#ifdef __linux__ +static void (*selinux_run_init_old) (void); +static void (*selinux_run_init_new) (int argc, char **argv); + +void setup_selinux (int argc, char **argv); +#endif + +#ifdef __linux__ +void setup_selinux (int argc, char **argv) +{ + void *lib_handle = NULL; + + lib_handle = dlopen (SELINUX_LIB, RTLD_NOW | RTLD_GLOBAL); + if (lib_handle) + { + /* FIXME: the below code generates the warning + ISO C forbids assignment between function pointer and 'void *' + which sucks ass + http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html */ + selinux_run_init_old = dlsym (lib_handle, "selinux_runscript"); + selinux_run_init_new = dlsym (lib_handle, "selinux_runscript2"); + + /* Use new run_init if it rc_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!"); + } +} +#endif + +static void handle_signal (int sig) +{ + pid_t pid; + int status; + int serrno = errno; + + switch (sig) + { + case SIGHUP: + sighup = true; + break; + + case SIGCHLD: + do + { + pid = waitpid (-1, &status, WNOHANG); + if (pid < 0) + { + if (errno != ECHILD) + eerror ("waitpid: %s", strerror (errno)); + return; + } + } while (! WIFEXITED (status) && ! WIFSIGNALED (status)); + break; + + case SIGINT: + case SIGTERM: + case SIGQUIT: + eerrorx ("%s: caught signal %d, aborting", applet, sig); + + 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 == 0) + 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 == NULL || ! rc_exists (mtime_test)) + return (false); + + if (rc_service_state (applet, rc_service_stopped)) + return (false); + + if ((mtime = get_mtime (mtime_test, false)) == 0) + return (false); + + while (tests[i]) + { + path = rc_strcatpaths (RC_SVCDIR, tests[i], applet, NULL); + if (rc_exists (path)) + { + int m = get_mtime (path, false); + if (mtime < m && m != 0) + { + free (path); + return (false); + } + } + free (path); + i++; + } + + return (true); +} + +static void uncoldplug (char *service) +{ + char *cold = rc_strcatpaths (RC_SVCDIR "coldplugged", basename (service), NULL); + if (rc_exists (cold) && unlink (cold) != 0) + eerror ("%s: unlink `%s': %s", applet, cold, strerror (errno)); + free (cold); +} + +static void cleanup (void) +{ + /* Flush our buffered output if any */ + eflush (); + + if (hook_out) + rc_plugin_run (hook_out, applet); + rc_plugin_unload (); + + if (deptree) + rc_free_deptree (deptree); + if (services) + rc_strlist_free (services); + if (types) + rc_strlist_free (types); + if (svclist) + rc_strlist_free (svclist); + if (providelist) + rc_strlist_free (providelist); + if (restart_services) + rc_strlist_free (restart_services); + if (need_services) + rc_strlist_free (need_services); + if (mycmd) + free (mycmd); + if (myarg1) + free (myarg1); + if (myarg2) + free (myarg2); + if (ibsave) + free (ibsave); + + if (in_control ()) + { + if (rc_service_state (applet, rc_service_starting)) + { + if (rc_service_state (applet, rc_service_wasinactive)) + rc_mark_service (applet, rc_service_inactive); + else + rc_mark_service (applet, rc_service_stopped); + } + else if (rc_service_state (applet, rc_service_stopping)) + { + /* If the we're shutting down, do it cleanly */ + if ((softlevel && rc_runlevel_stopping () && + (strcmp (softlevel, RC_LEVEL_SHUTDOWN) == 0 || + strcmp (softlevel, RC_LEVEL_REBOOT) == 0)) || + ! rc_service_state (applet, rc_service_wasinactive)) + rc_mark_service (applet, rc_service_stopped); + else + rc_mark_service (applet, rc_service_inactive); + } + if (exclusive && rc_exists (exclusive)) + unlink (exclusive); + } + + if (env) + rc_strlist_free (env); + + if (mtime_test) + { + unlink (mtime_test); + free (mtime_test); + } + if (exclusive) + free (exclusive); + + if (applet) + free (applet); +} + +static bool svc_exec (const char *service, const char *arg1, const char *arg2) +{ + int status = 0; + pid_t pid; + + /* We need to disable our child signal handler now so we block + until our script returns. */ + signal (SIGCHLD, NULL); + + pid = fork(); + + if (pid == -1) + eerrorx ("%s: fork: %s", service, strerror (errno)); + if (pid == 0) + { + mycmd = rc_xstrdup (service); + myarg1 = rc_xstrdup (arg1); + if (arg2) + myarg2 = rc_xstrdup (arg2); + + if (rc_exists (RC_SVCDIR "runscript.sh")) + { + execl (RC_SVCDIR "runscript.sh", mycmd, mycmd, myarg1, myarg2, NULL); + eerrorx ("%s: exec `" RC_SVCDIR "runscript.sh': %s", + service, strerror (errno)); + } + else + { + execl (RC_LIBDIR "sh/runscript.sh", mycmd, mycmd, myarg1, myarg2, NULL); + eerrorx ("%s: exec `" RC_LIBDIR "sh/runscript.sh': %s", + service, strerror (errno)); + } + } + + do + { + if (waitpid (pid, &status, 0) < 0) + { + if (errno != ECHILD) + eerror ("waitpid: %s", strerror (errno)); + break; + } + } while (! WIFEXITED (status) && ! WIFSIGNALED (status)); + + /* Done, so restore the signal handler */ + signal (SIGCHLD, handle_signal); + + if (WIFEXITED (status)) + return (WEXITSTATUS (status) == 0 ? true : false); + + return (false); +} + +static rc_service_state_t svc_status (const char *service) +{ + char status[10]; + int (*e) (const char *fmt, ...) = &einfo; + + rc_service_state_t retval = rc_service_stopped; + + if (rc_service_state (service, rc_service_stopping)) + { + snprintf (status, sizeof (status), "stopping"); + e = &ewarn; + retval = rc_service_stopping; + } + else if (rc_service_state (service, rc_service_starting)) + { + snprintf (status, sizeof (status), "starting"); + e = &ewarn; + retval = rc_service_starting; + } + else if (rc_service_state (service, rc_service_inactive)) + { + snprintf (status, sizeof (status), "inactive"); + e = &ewarn; + retval = rc_service_inactive; + } + else if (rc_service_state (service, rc_service_crashed)) + { + snprintf (status, sizeof (status), "crashed"); + e = &eerror; + retval = rc_service_crashed; + } + else if (rc_service_state (service, rc_service_started)) + { + snprintf (status, sizeof (status), "started"); + retval = rc_service_started; + } + else + snprintf (status, sizeof (status), "stopped"); + + e ("status: %s", status); + return (retval); +} + +static void make_exclusive (const char *service) +{ + 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, 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, NULL); + i = strlen (path) + 16; + mtime_test = rc_xmalloc (sizeof (char *) * i); + snprintf (mtime_test, i, "%s.%d", path, getpid ()); + free (path); + + if (rc_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 () +{ + char *service; + int i; + + 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); + + STRLIST_FOREACH (tmplist, service, i) + restart_services = rc_strlist_addsort (restart_services, service); + + rc_strlist_free (tmplist); + tmplist = NULL; +} + +static void svc_start (const char *service, bool deps) +{ + bool started; + bool background = false; + char *svc; + char *svc2; + int i; + int j; + int depoptions = RC_DEP_TRACE; + + if (rc_is_env ("RC_STRICT_DEPEND", "yes")) + depoptions |= RC_DEP_STRICT; + + if (rc_is_env ("IN_HOTPLUG", "1") || in_background) + { + if (! rc_service_state (service, rc_service_inactive)) + exit (EXIT_FAILURE); + background = true; + } + + if (rc_service_state (service, rc_service_started)) + ewarnx ("WARNING: %s has already been started", applet); + else if (rc_service_state (service, rc_service_starting)) + ewarnx ("WARNING: %s is already starting", applet); + else if (rc_service_state (service, rc_service_stopping)) + ewarnx ("WARNING: %s is stopping", applet); + else if (rc_service_state (service, rc_service_inactive) && ! background) + ewarnx ("WARNING: %s has already started, but is inactive", applet); + + if (! rc_mark_service (service, rc_service_starting)) + eerrorx ("ERROR: %s has been started by something else", applet); + + make_exclusive (service); + + if (deps) + { + if (! deptree && ((deptree = rc_load_deptree ()) == NULL)) + eerrorx ("failed to load deptree"); + + rc_strlist_free (types); + types = rc_strlist_add (NULL, "broken"); + rc_strlist_free (svclist); + svclist = rc_strlist_add (NULL, applet); + rc_strlist_free (services); + services = rc_get_depends (deptree, types, svclist, 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 (types); + types = rc_strlist_add (NULL, "ineed"); + rc_strlist_free (need_services); + need_services = rc_get_depends (deptree, types, svclist, + softlevel, depoptions); + types = rc_strlist_add (types, "iuse"); + if (! rc_runlevel_starting ()) + { + services = rc_get_depends (deptree, types, svclist, + softlevel, depoptions); + STRLIST_FOREACH (services, svc, i) + if (rc_service_state (svc, rc_service_stopped)) + rc_start_service (svc); + + rc_strlist_free (services); + } + + /* Now wait for them to start */ + types = rc_strlist_add (types, "iafter"); + services = rc_get_depends (deptree, types, svclist, + softlevel, depoptions); + + /* We use tmplist to hold our scheduled by list */ + rc_strlist_free (tmplist); + tmplist = NULL; + + STRLIST_FOREACH (services, svc, i) + { + if (rc_service_state (svc, rc_service_started)) + continue; + if (! rc_wait_service (svc)) + { eerror ("%s: timed out waiting for %s", applet, svc); + system ("ps ax > /tmp/$SVCNAME.waiting"); } + if (rc_service_state (svc, rc_service_started)) + continue; + + STRLIST_FOREACH (need_services, svc2, j) + if (strcmp (svc, svc2) == 0) + { + if (rc_service_state (svc, rc_service_inactive) || + rc_service_state (svc, rc_service_wasinactive)) + tmplist = 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_mark_service (service, rc_service_stopped); + unlink_mtime_test (); + + rc_strlist_free (types); + types = rc_strlist_add (NULL, "iprovide"); + STRLIST_FOREACH (tmplist, svc, i) + { + rc_schedule_start_service (svc, service); + + rc_strlist_free (svclist); + svclist = rc_strlist_add (NULL, svc); + rc_strlist_free (providelist); + providelist = rc_get_depends (deptree, types, svclist, + softlevel, depoptions); + STRLIST_FOREACH (providelist, svc2, j) + rc_schedule_start_service (svc2, service); + + len += strlen (svc) + 2; + n++; + } + + tmp = rc_xmalloc (sizeof (char *) * len + 5); + p = tmp; + STRLIST_FOREACH (tmplist, svc, i) + { + if (i > 1) + { + if (i == n - 1) + p += sprintf (p, " or "); + else + p += sprintf (p, ", "); + } + p += sprintf (p, "%s", svc); + } + ewarnx ("WARNING: %s is scheduled to start when %s has started", + applet, tmp); + } + + rc_strlist_free (services); + services = NULL; + rc_strlist_free (types); + types = NULL; + rc_strlist_free (svclist); + svclist = NULL; + } + + if (ibsave) + setenv ("IN_BACKGROUND", ibsave, 1); + rc_plugin_run (rc_hook_service_start_in, applet); + hook_out = rc_hook_service_start_out; + started = svc_exec (service, "start", NULL); + if (ibsave) + unsetenv ("IN_BACKGROUND"); + + if (in_control ()) + { + if (! started) + { + if (rc_service_state (service, rc_service_wasinactive)) + rc_mark_service (service, rc_service_inactive); + else + { + rc_mark_service (service, rc_service_stopped); + if (rc_runlevel_starting ()) + rc_mark_service (service, rc_service_failed); + } + eerrorx ("ERROR: %s failed to start", applet); + } + + rc_mark_service (service, rc_service_started); + unlink_mtime_test (); + + hook_out = 0; + rc_plugin_run (rc_hook_service_start_out, applet); + } + else + { + if (rc_service_state (service, rc_service_inactive)) + ewarn ("WARNING: %s has started, but is inactive", applet); + else + ewarn ("WARNING: %s not under our control, aborting", applet); + } + + /* 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_start_service (svc); + rc_strlist_free (services); + services = NULL; + + /* Do the same for any services we provide */ + rc_strlist_free (types); + types = rc_strlist_add (NULL, "iprovide"); + rc_strlist_free (svclist); + svclist = rc_strlist_add (NULL, applet); + rc_strlist_free (tmplist); + tmplist = rc_get_depends (deptree, types, svclist, softlevel, depoptions); + + 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_start_service (svc); + } +} + +static void svc_stop (const char *service, bool deps) +{ + bool stopped; + + if (rc_runlevel_stopping () && + rc_service_state (service, rc_service_failed)) + exit (EXIT_FAILURE); + + if (rc_is_env ("IN_HOTPLUG", "1") || in_background) + if (! rc_service_state (service, rc_service_started)) + exit (EXIT_FAILURE); + + if (rc_service_state (service, rc_service_stopped)) + ewarnx ("WARNING: %s is already stopped", applet); + else if (rc_service_state (service, rc_service_stopping)) + ewarnx ("WARNING: %s is already stopping", applet); + + if (! rc_mark_service (service, rc_service_stopping)) + eerrorx ("ERROR: %s has been stopped by something else", applet); + + make_exclusive (service); + + if (! rc_runlevel_stopping () && + rc_service_in_runlevel (service, RC_LEVEL_BOOT)) + ewarn ("WARNING: you are stopping a boot service"); + + if (deps || ! rc_service_state (service, rc_service_wasinactive)) + { + int depoptions = RC_DEP_TRACE; + char *svc; + int i; + + if (rc_is_env ("RC_STRICT_DEPEND", "yes")) + depoptions |= RC_DEP_STRICT; + + if (! deptree && ((deptree = rc_load_deptree ()) == NULL)) + eerrorx ("failed to load deptree"); + + rc_strlist_free (types); + types = rc_strlist_add (NULL, "needsme"); + rc_strlist_free (svclist); + svclist = rc_strlist_add (NULL, applet); + rc_strlist_free (tmplist); + tmplist = NULL; + rc_strlist_free (services); + services = rc_get_depends (deptree, types, svclist, + softlevel, depoptions); + rc_strlist_reverse (services); + STRLIST_FOREACH (services, svc, i) + { + if (rc_service_state (svc, rc_service_started) || + rc_service_state (svc, rc_service_inactive)) + { + rc_wait_service (svc); + if (rc_service_state (svc, rc_service_started) || + rc_service_state (svc, rc_service_inactive)) + { + rc_stop_service (svc); + tmplist = 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 */ + rc_wait_service (svc); + if (! rc_service_state (svc, rc_service_stopped)) + { + if (rc_runlevel_stopping ()) + rc_mark_service (svc, 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 */ + types = rc_strlist_add (types, "usesme"); + types = rc_strlist_add (types, "ibefore"); + services = rc_get_depends (deptree, types, svclist, + softlevel, depoptions); + STRLIST_FOREACH (services, svc, i) + { + if (rc_service_state (svc, rc_service_stopped)) + continue; + rc_wait_service (svc); + } + + rc_strlist_free (services); + services = NULL; + rc_strlist_free (types); + types = NULL; + } + + if (ibsave) + setenv ("IN_BACKGROUND", ibsave, 1); + rc_plugin_run (rc_hook_service_stop_in, applet); + hook_out = rc_hook_service_stop_out; + stopped = svc_exec (service, "stop", NULL); + if (ibsave) + unsetenv ("IN_BACKGROUND"); + + if (! in_control ()) + ewarnx ("WARNING: %s not under our control, aborting", applet); + + if (! stopped) + { + if (rc_service_state (service, rc_service_wasinactive)) + rc_mark_service (service, rc_service_inactive); + else + rc_mark_service (service, rc_service_stopped); + eerrorx ("ERROR: %s failed to stop", applet); + } + + if (in_background) + rc_mark_service (service, rc_service_inactive); + else + rc_mark_service (service, rc_service_stopped); + + unlink_mtime_test (); + hook_out = 0; + rc_plugin_run (rc_hook_service_stop_out, applet); +} + +static void svc_restart (const char *service, bool deps) +{ + char *svc; + int i; + bool inactive = false; + + /* 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) + { + if (rc_service_state (service, rc_service_started) || + rc_service_state (service, rc_service_inactive)) + svc_exec (service, "stop", "start"); + else + svc_exec (service, "start", NULL); + return; + } + + if (! rc_service_state (service, rc_service_stopped)) + { + get_started_services (); + svc_stop (service, deps); + + /* Flush our buffered output if any */ + eflush (); + } + + svc_start (service, deps); + + inactive = rc_service_state (service, rc_service_inactive); + if (! inactive) + inactive = rc_service_state (service, rc_service_wasinactive); + + if (inactive || + rc_service_state (service, rc_service_starting) || + rc_service_state (service, rc_service_started)) + { + STRLIST_FOREACH (restart_services, svc, i) + { + if (rc_service_state (svc, rc_service_stopped)) + { + if (inactive) + { + rc_schedule_start_service (service, svc); + ewarn ("WARNING: %s is scheduled to started when %s has started", + svc, basename (service)); + } + else + rc_start_service (svc); + } + } + } +} + +int main (int argc, char **argv) +{ + const char *service = argv[1]; + int i; + bool deps = true; + bool doneone = false; + char pid[16]; + int retval; + bool ifstarted = false; + + applet = strdup (basename (service)); + atexit (cleanup); + + /* Show help if insufficient args */ + if (argc < 3) + { + execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, NULL); + eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s", + applet, strerror (errno)); + } + +#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 (rc_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, NULL); + symlink (service, tmp); + exit (EXIT_FAILURE); + } +#endif + + if ((softlevel = getenv ("RC_SOFTLEVEL")) == NULL) + { + /* Ensure our environment is pure + Also, add our configuration to it */ + env = rc_filter_env (); + env = rc_config_env (env); + + 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 = rc_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_get_runlevel (); + + /* If not called from RC or another service then don't be parallel */ + unsetenv ("RC_PARALLEL_STARTUP"); + } + + setenv ("RC_ELOG", 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); + + if (rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + { + char ebname[PATH_MAX]; + char *eb; + + snprintf (ebname, sizeof (ebname), "%s.%s", applet, pid); + eb = rc_strcatpaths (RC_SVCDIR "ebuffer", ebname, NULL); + setenv ("RC_EBUFFER", eb, 1); + free (eb); + } + + /* 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")) + { + in_background = rc_is_env ("IN_BACKGROUND", "true"); + ibsave = strdup (getenv ("IN_BACKGROUND")); + unsetenv ("IN_BACKGROUND"); + + /* Don't hang around */ + if (in_background) + setenv ("RC_PARALLEL_STARTUP", "yes", 1); + } + +#ifdef __linux__ + /* Ok, we are ready to go, so setup selinux if applicable */ + setup_selinux (argc, argv); +#endif + + /* Right then, parse any options there may be */ + for (i = 2; i < argc; i++) + { + if (strlen (argv[i]) < 2 || argv[i][0] != '-' || argv[i][1] != '-') + continue; + + if (strcmp (argv[i], "--debug") == 0) + setenv ("RC_DEBUG", "yes", 1); + else if (strcmp (argv[i], "--help") == 0) + { + execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, NULL); + eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s", + applet, strerror (errno)); + } + else if (strcmp (argv[i],"--ifstarted") == 0) + ifstarted = true; + else if (strcmp (argv[i], "--nocolour") == 0 || + strcmp (argv[i], "--nocolor") == 0) + setenv ("RC_NOCOLOR", "yes", 1); + else if (strcmp (argv[i], "--nodeps") == 0) + deps = false; + else if (strcmp (argv[i], "--quiet") == 0) + setenv ("RC_QUIET", "yes", 1); + else if (strcmp (argv[i], "--verbose") == 0) + setenv ("RC_VERBOSE", "yes", 1); + else if (strcmp (argv[i], "--version") == 0) + printf ("version me\n"); + else + eerror ("%s: unknown option `%s'", applet, argv[i]); + } + + if (ifstarted && ! rc_service_state (applet, rc_service_started)) + { + if (! rc_is_env("RC_QUIET", "yes")) + eerror ("ERROR: %s is not started", applet); + exit (EXIT_FAILURE); + } + + if (rc_is_env ("IN_HOTPLUG", "1")) + { + if (! rc_is_env ("RC_HOTPLUG", "yes") || ! rc_allow_plug (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; + for (i = 2; i < argc; i++) + { + /* Abort on a sighup here */ + if (sighup) + exit (EXIT_FAILURE); + + if (strlen (argv[i]) < 2 || + (argv[i][0] == '-' && argv[i][1] == '-')) + continue; + + /* 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", argv[i], 1); + + doneone = true; + if (strcmp (argv[i], "conditionalrestart") == 0 || + strcmp (argv[i], "condrestart") == 0) + { + if (rc_service_state (service, rc_service_started)) + svc_restart (service, deps); + } + else if (strcmp (argv[i], "restart") == 0) + svc_restart (service, deps); + else if (strcmp (argv[i], "start") == 0) + svc_start (service, deps); + else if (strcmp (argv[i], "status") == 0) + { + rc_service_state_t r = svc_status (service); + retval = (int) r; + } + else if (strcmp (argv[i], "stop") == 0) + { + if (in_background) + get_started_services (); + else if (! rc_runlevel_stopping ()) + uncoldplug (applet); + + svc_stop (service, deps); + + if (in_background && + rc_service_state (service, rc_service_inactive)) + { + char *svc; + int j; + STRLIST_FOREACH (restart_services, svc, j) + if (rc_service_state (svc, rc_service_stopped)) + rc_schedule_start_service (service, svc); + } + } + else if (strcmp (argv[i], "zap") == 0) + { + einfo ("Manually resetting %s to stopped state", applet); + rc_mark_service (applet, rc_service_stopped); + uncoldplug (applet); + } + else if (strcmp (argv[i], "help") == 0) + { + execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", NULL); + eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s", + applet, strerror (errno)); + } + else + svc_exec (service, argv[i], NULL); + + /* Flush our buffered output if any */ + eflush (); + + /* We should ensure this list is empty after an action is done */ + rc_strlist_free (restart_services); + restart_services = NULL; + } + + if (! doneone) + { + execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, NULL); + eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s", + applet, strerror (errno)); + } + + return (retval); +} |