From c52f2164502544c17811e47fc3236fe87aa7fc5b Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Fri, 20 Apr 2007 13:12:21 +0000 Subject: [PATCH] Added the rc-abort command, #175106 thanks to Daniel Drake. --- ChangeLog | 2 + init.d/checkfs | 9 +-- init.d/checkroot | 4 +- src/Makefile | 5 +- src/librc.c | 12 ++-- src/rc.c | 175 ++++++++++++++++++++++++++--------------------- src/runscript.c | 38 ++++------ 7 files changed, 128 insertions(+), 117 deletions(-) diff --git a/ChangeLog b/ChangeLog index 63a36328..d1860c12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,8 @@ 20 Apr 2007; Roy Marples : + Added the rc-abort command, #175106 thanks to Daniel Drake. + Plugins now run in a forked process for extra resliance. 17 Apr 2007; Roy Marples : diff --git a/init.d/checkfs b/init.d/checkfs index 2f9ead02..da3dc39f 100755 --- a/init.d/checkfs +++ b/init.d/checkfs @@ -51,13 +51,8 @@ do_checkfs() { if [ ${retval} -gt 3 ] ; then eend 2 "Fsck could not correct all errors, manual repair needed" - if [ "${RC_SYS}" = "VPS" ] ; then - halt -f - elif [ -x /sbin/sulogin ] ; then - sulogin "${CONSOLE}" - else - return 1 - fi + rc-abort + exit 1 fi fi diff --git a/init.d/checkroot b/init.d/checkroot index 2fa22ec5..03741ae8 100755 --- a/init.d/checkroot +++ b/init.d/checkroot @@ -104,8 +104,8 @@ start() { retval=$? else eend 2 "Filesystem couldn't be fixed :(" - [ "${RC_UNAME}" = "Linux" ] || return 1 - sulogin "${CONSOLE}" + rc-abort + exit 1 fi if [ ${retval} != "0" ] ; then einfo "Unmounting filesystems" diff --git a/src/Makefile b/src/Makefile index e255f193..4b39bbe3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,6 +5,9 @@ CC ?= gcc CFLAGS ?= -Wall -O2 -pipe +check_gcc=$(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; \ + then echo "$(1)"; else echo "$(2)"; fi) + # Loads of nice flags to ensure our code is good CFLAGS += -pedantic -std=c99 \ -Wall -Wunused -Wimplicit -Wshadow -Wformat=2 \ @@ -48,7 +51,7 @@ RCLINKS = einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ mark_service_stopping mark_service_stopped \ mark_service_inactive mark_service_wasinactive \ mark_service_coldplugged \ - get_options save_options \ + get_options save_options rc-abort \ is_runlevel_start is_runlevel_stop service_started_daemon # Quick hack to make my life easier on BSD and Linux diff --git a/src/librc.c b/src/librc.c index 9172ee7c..2a240a83 100644 --- a/src/librc.c +++ b/src/librc.c @@ -469,22 +469,22 @@ static pid_t _exec_service (const char *service, const char *arg) return (-1); } - if ((pid = fork ()) == 0) { - char *myarg = rc_xstrdup (arg); + if ((pid = vfork ()) == 0) { int e = 0; - execl (file, file, myarg, (char *) NULL); + execl (file, file, arg, (char *) NULL); e = errno; - free (myarg); unlink (fifo); free (fifo); - eerrorx ("unable to exec `%s': %s", file, strerror (errno)); + eerror ("unable to exec `%s': %s", file, strerror (errno)); + free (file); + _exit (EXIT_FAILURE); } free (fifo); free (file); if (pid == -1) { - eerror ("unable to fork: %s", strerror (errno)); + eerror ("vfork: %s", strerror (errno)); return (pid); } diff --git a/src/rc.c b/src/rc.c index d5482d15..375800af 100644 --- a/src/rc.c +++ b/src/rc.c @@ -56,16 +56,14 @@ extern char **environ; static char *applet = NULL; static char **env = NULL; static char **newenv = NULL; -static char **coldplugged_services; +static char **coldplugged_services = NULL; static char **stop_services = NULL; static char **start_services = NULL; static rc_depinfo_t *deptree = NULL; static char **types = NULL; -static char *mycmd = NULL; -static char *myarg = NULL; static char *tmp = NULL; -struct termios *termios_orig; +struct termios *termios_orig = NULL; static void cleanup (void) { @@ -76,24 +74,13 @@ static void cleanup (void) free (termios_orig); } - if (env) - rc_strlist_free (env); - if (newenv) - rc_strlist_free (newenv); - if (coldplugged_services) - rc_strlist_free (coldplugged_services); - if (stop_services) - rc_strlist_free (stop_services); - if (start_services) - rc_strlist_free (start_services); - if (deptree) - rc_free_deptree (deptree); - if (types) - rc_strlist_free (types); - if (mycmd) - free (mycmd); - if (myarg) - free (myarg); + rc_strlist_free (env); + rc_strlist_free (newenv); + rc_strlist_free (coldplugged_services); + rc_strlist_free (stop_services); + rc_strlist_free (start_services); + rc_free_deptree (deptree); + rc_strlist_free (types); /* Clean runlevel start, stop markers */ if (rc_is_dir (RC_SVCDIR "softscripts.new")) @@ -101,8 +88,7 @@ static void cleanup (void) if (rc_is_dir (RC_SVCDIR "softscripts.old")) rc_rm_dir (RC_SVCDIR "softscripts.old", true); - if (applet) - free (applet); + free (applet); } static int do_e (int argc, char **argv) @@ -335,6 +321,10 @@ static char read_key (bool block) struct termios termios; char c = 0; + /* This locks up rc for some reason! + * Why? it used to work fine... */ + return 0; + if (! isatty (STDIN_FILENO)) return (false); @@ -378,33 +368,54 @@ static void mark_interactive (void) static void sulogin (bool cont) { #ifdef __linux__ + char *e = getenv ("RC_SYS"); + + /* VPS systems cannot do an sulogin */ + if (e && strcmp (e, "VPS") == 0) { + execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL); + eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno)); + } + if (cont) { int status = 0; - pid_t pid = fork(); + pid_t pid = vfork(); if (pid == -1) - eerrorx ("%s: fork: %s", applet, strerror (errno)); + eerrorx ("%s: vfork: %s", applet, strerror (errno)); if (pid == 0) { newenv = rc_filter_env (); - mycmd = rc_xstrdup ("/sbin/sulogin"); - myarg = rc_xstrdup (getenv ("CONSOLE")); - execle (mycmd, mycmd, myarg, (char *) NULL, newenv); - eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); + execl ("/sbin/sulogin", "/sbin/sulogin", + getenv ("CONSOLE"), (char *) NULL, newenv); + eerror ("%s: unable to exec `/sbin/sulogin': %s", applet, + strerror (errno)); + _exit (EXIT_FAILURE); } waitpid (pid, &status, 0); } else { newenv = rc_filter_env (); - mycmd = rc_xstrdup ("/sbin/sulogin"); - myarg = rc_xstrdup (getenv ("CONSOLE")); - execle (mycmd, mycmd, myarg, (char *) NULL, newenv); + execl ("/sbin/sulogin", "/sbin/sulogin", + getenv ("CONSOLE"), (char *) NULL, newenv); eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); } #else - /* Appease gcc */ exit (cont ? EXIT_FAILURE : EXIT_SUCCESS); #endif } +static void single_user (void) +{ +#ifdef __linux__ + execl ("/sbin/telinit", "/sbin/telinit", "S", (char *) NULL); + eerrorx ("%s: unable to exec `/sbin/telinit': %s", + applet, strerror (errno)); +#else + if (kill (1, SIGTERM) != 0) + eerrorx ("%s: unable to send SIGTERM to init (pid 1): %s", + applet, strerror (errno)); + exit (EXIT_SUCCESS); +#endif +} + static void set_ksoftlevel (const char *runlevel) { FILE *fp; @@ -457,6 +468,18 @@ static void handle_signal (int sig) if (! signame[0]) snprintf (signame, sizeof (signame), "SIGQUIT"); eerrorx ("%s: caught %s, aborting", applet, signame); + case SIGUSR1: + eerror ("rc: Aborting!"); + /* Kill any running services we have started */ + signal (SIGTERM, SIG_IGN); + killpg (getpgrp (), SIGTERM); + + /* If we're in boot runlevel then change into single-user mode */ + if (strcmp (rc_get_runlevel (), RC_LEVEL_BOOT) == 0) + single_user (); + + exit (EXIT_FAILURE); + break; default: eerror ("%s: caught unknown signal %d", applet, sig); @@ -480,6 +503,7 @@ int main (int argc, char **argv) bool interactive = false; int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; char ksoftbuffer [PATH_MAX]; + char pidstr[6]; if (argv[0]) applet = rc_xstrdup (basename (argv[0])); @@ -509,6 +533,19 @@ int main (int argc, char **argv) else if (strcmp (applet, "is_runlevel_stop") == 0) exit (rc_runlevel_stopping () ? 0 : 1); + if (strcmp (applet, "rc-abort") == 0) { + char *p = getenv ("RC_PID"); + pid_t pid = 0; + + if (p && sscanf (p, "%d", &pid) == 1) { + if (kill (pid, SIGUSR1) != 0) + eerrorx ("rc-abort: failed to signal parent %d: %s", + pid, strerror (errno)); + exit (EXIT_SUCCESS); + } + exit (EXIT_FAILURE); + } + if (strcmp (applet, "rc" ) != 0) eerrorx ("%s: unknown applet", applet); @@ -520,10 +557,14 @@ int main (int argc, char **argv) atexit (cleanup); newlevel = argv[0]; + /* Start a new process group */ + setpgrp(); + /* Setup a signal handler */ signal (SIGINT, handle_signal); signal (SIGQUIT, handle_signal); signal (SIGTERM, handle_signal); + signal (SIGUSR1, handle_signal); /* Ensure our environment is pure Also, add our configuration to it */ @@ -563,6 +604,10 @@ int main (int argc, char **argv) /* Enable logging */ setenv ("RC_ELOG", "rc", 1); + /* Export our PID */ + snprintf (pidstr, sizeof (pidstr), "%d", getpid ()); + setenv ("RC_PID", pidstr, 1); + interactive = rc_exists (INTERACTIVE); rc_plugin_load (); @@ -599,14 +644,14 @@ int main (int argc, char **argv) setenv ("RC_SOFTLEVEL", newlevel, 1); rc_plugin_run (rc_hook_runlevel_start_in, newlevel); - if ((pid = fork ()) == -1) - eerrorx ("%s: fork: %s", applet, strerror (errno)); + if ((pid = vfork ()) == -1) + eerrorx ("%s: vfork: %s", applet, strerror (errno)); if (pid == 0) { - mycmd = rc_xstrdup (INITSH); - execl (mycmd, mycmd, (char *) NULL); - eerrorx ("%s: unable to exec `" INITSH "': %s", - applet, strerror (errno)); + execl (INITSH, INITSH, (char *) NULL); + eerror ("%s: unable to exec `" INITSH "': %s", + applet, strerror (errno)); + _exit (EXIT_FAILURE); } do { @@ -695,9 +740,7 @@ int main (int argc, char **argv) eerrorx ("%s: couldn't find a runlevel called `%s'", applet, newlevel); } else { - mycmd = rc_xstrdup ("/sbin/telinit"); - myarg = rc_xstrdup (lvl); - execl (mycmd, mycmd, myarg, (char *) NULL); + execl ("/sbin/telinit", "/sbin/telinit", lvl, (char *) NULL); eerrorx ("%s: unable to exec `/sbin/telinit': %s", applet, strerror (errno)); } @@ -712,11 +755,6 @@ int main (int argc, char **argv) rc reboot */ if (newlevel) { - if (myarg) { - free (myarg); - myarg = NULL; - } - if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0) { if (! RUNLEVEL || (strcmp (RUNLEVEL, "S") != 0 && @@ -724,44 +762,29 @@ int main (int argc, char **argv) { /* Remember the current runlevel for when we come back */ set_ksoftlevel (runlevel); -#ifdef __linux__ - mycmd = rc_xstrdup ("/sbin/telinit"); - myarg = rc_xstrdup ("S"); - execl (mycmd, mycmd, myarg, (char *) NULL); - eerrorx ("%s: unable to exec `/%s': %s", - mycmd, applet, strerror (errno)); -#else - if (kill (1, SIGTERM) != 0) - eerrorx ("%s: unable to send SIGTERM to init (pid 1): %s", - applet, strerror (errno)); - exit (EXIT_SUCCESS); -#endif + single_user (); } } else if (strcmp (newlevel, RC_LEVEL_REBOOT) == 0) { if (! RUNLEVEL || strcmp (RUNLEVEL, "6") != 0) { - mycmd = rc_xstrdup ("/sbin/shutdown"); - myarg = rc_xstrdup ("-r"); - tmp = rc_xstrdup ("now"); - execl (mycmd, mycmd, myarg, tmp, (char *) NULL); - eerrorx ("%s: unable to exec `%s': %s", - mycmd, applet, strerror (errno)); + execl ("/sbin/shutdown", "/sbin/shutdown", "-r", "now", (char *) NULL); + eerrorx ("%s: unable to exec `/sbin/shutdown': %s", + applet, strerror (errno)); } } else if (strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0) { if (! RUNLEVEL || strcmp (RUNLEVEL, "0") != 0) { - mycmd = rc_xstrdup ("/sbin/shutdown"); -#ifdef __linux__ - myarg = rc_xstrdup ("-h"); + execl ("/sbin/shutdown", "/sbin/shutdown", +#ifdef __linux + "-h", #else - myarg = rc_xstrdup ("-p"); + "-p", #endif - tmp = rc_xstrdup ("now"); - execl (mycmd, mycmd, myarg, tmp, (char *) NULL); - eerrorx ("%s: unable to exec `%s': %s", - mycmd, applet, strerror (errno)); + "now", (char *) NULL); + eerrorx ("%s: unable to exec `/sbin/shutdown': %s", + applet, strerror (errno)); } } } @@ -1060,9 +1083,7 @@ int main (int argc, char **argv) if (strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 || strcmp (runlevel, RC_LEVEL_REBOOT) == 0) { - mycmd = rc_xstrdup (HALTSH); - myarg = rc_xstrdup (runlevel); - execl (mycmd, mycmd, myarg, (char *) NULL); + execl (HALTSH, HALTSH, runlevel, (char *) NULL); eerrorx ("%s: unable to exec `%s': %s", applet, HALTSH, strerror (errno)); } diff --git a/src/runscript.c b/src/runscript.c index 4ebd7917..c82c5f9a 100644 --- a/src/runscript.c +++ b/src/runscript.c @@ -43,9 +43,6 @@ 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; @@ -69,10 +66,12 @@ void setup_selinux (int argc, char **argv) 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 */ + /* + * 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"); @@ -216,12 +215,6 @@ static void cleanup (void) rc_strlist_free (need_services); if (tmplist) rc_strlist_free (tmplist); - if (mycmd) - free (mycmd); - if (myarg1) - free (myarg1); - if (myarg2) - free (myarg2); if (ibsave) free (ibsave); @@ -273,26 +266,23 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2) until our script returns. */ signal (SIGCHLD, NULL); - pid = fork(); + pid = vfork(); if (pid == -1) - eerrorx ("%s: fork: %s", service, strerror (errno)); + eerrorx ("%s: vfork: %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, + execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2, (char *) NULL); - eerrorx ("%s: exec `" RC_SVCDIR "runscript.sh': %s", + eerror ("%s: exec `" RC_SVCDIR "runscript.sh': %s", service, strerror (errno)); + _exit (EXIT_FAILURE); } else { - execl (RC_LIBDIR "sh/runscript.sh", mycmd, mycmd, myarg1, myarg2, + execl (RC_LIBDIR "sh/runscript.sh", service, service, arg1, arg2, (char *) NULL); - eerrorx ("%s: exec `" RC_LIBDIR "sh/runscript.sh': %s", + eerror ("%s: exec `" RC_LIBDIR "sh/runscript.sh': %s", service, strerror (errno)); + _exit (EXIT_FAILURE); } }