chiark / gitweb /
service: drop support for SysV scripts for the early boot
[elogind.git] / src / core / service.c
index e74da54eac5b288ed54c7c839335fb73a460807e..aad6d664396e0bc594e6eb727bdf1646b467e6fa 100644 (file)
@@ -33,6 +33,7 @@
 #include "log.h"
 #include "strv.h"
 #include "unit-name.h"
+#include "unit-printf.h"
 #include "dbus-service.h"
 #include "special.h"
 #include "bus-errors.h"
@@ -48,8 +49,7 @@
 
 typedef enum RunlevelType {
         RUNLEVEL_UP,
-        RUNLEVEL_DOWN,
-        RUNLEVEL_SYSINIT
+        RUNLEVEL_DOWN
 } RunlevelType;
 
 static const struct {
@@ -64,16 +64,6 @@ static const struct {
         { "rc4.d",  SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
         { "rc5.d",  SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
 
-#ifdef TARGET_SUSE
-        /* SUSE style boot.d */
-        { "boot.d", SPECIAL_SYSINIT_TARGET,   RUNLEVEL_SYSINIT },
-#endif
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
-        /* Debian style rcS.d */
-        { "rcS.d",  SPECIAL_SYSINIT_TARGET,   RUNLEVEL_SYSINIT },
-#endif
-
         /* Standard SysV runlevels for shutdown */
         { "rc0.d",  SPECIAL_POWEROFF_TARGET,  RUNLEVEL_DOWN },
         { "rc6.d",  SPECIAL_REBOOT_TARGET,    RUNLEVEL_DOWN }
@@ -82,12 +72,10 @@ static const struct {
            directories in this order, and we want to make sure that
            sysv_start_priority is known when we first load the
            unit. And that value we only know from S links. Hence
-           UP/SYSINIT must be read before DOWN */
+           UP must be read before DOWN */
 };
 
 #define RUNLEVELS_UP "12345"
-/* #define RUNLEVELS_DOWN "06" */
-#define RUNLEVELS_BOOT "bBsS"
 #endif
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
@@ -294,6 +282,16 @@ static void service_done(Unit *u) {
         s->control_command = NULL;
         s->main_command = NULL;
 
+        set_free(s->restart_ignore_status.code);
+        s->restart_ignore_status.code = NULL;
+        set_free(s->restart_ignore_status.signal);
+        s->restart_ignore_status.signal = NULL;
+
+        set_free(s->success_status.code);
+        s->success_status.code = NULL;
+        set_free(s->success_status.signal);
+        s->success_status.signal = NULL;
+
         /* This will leak a process, but at least no memory or any of
          * our resources */
         service_unwatch_main_pid(s);
@@ -328,11 +326,6 @@ static char *sysv_translate_name(const char *name) {
                 /* Drop Debian-style .sh suffix */
                 strcpy(stpcpy(r, name) - 3, ".service");
 #endif
-#ifdef TARGET_SUSE
-        if (startswith(name, "boot."))
-                /* Drop SuSE-style boot. prefix */
-                strcpy(stpcpy(r, name + 5), ".service");
-#endif
 #ifdef TARGET_FRUGALWARE
         if (startswith(name, "rc."))
                 /* Drop Frugalware-style rc. prefix */
@@ -379,12 +372,6 @@ static int sysv_translate_facility(const char *name, const char *filename, char
                 "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
 #endif
 
-#ifdef TARGET_FEDORA
-                "MTA",                  SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-                "smtpdaemon",           SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-                "httpd",                SPECIAL_HTTP_DAEMON_TARGET,
-#endif
-
 #ifdef TARGET_SUSE
                 "smtp",                 SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
 #endif
@@ -515,7 +502,7 @@ static ExecCommand *exec_command_new(const char *path, const char *arg1) {
         return c;
 }
 
-static int sysv_exec_commands(Service *s) {
+static int sysv_exec_commands(Service *s, const bool supports_reload) {
         ExecCommand *c;
 
         assert(s);
@@ -532,14 +519,25 @@ static int sysv_exec_commands(Service *s) {
                 return -ENOMEM;
         exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c);
 
-        c = exec_command_new(UNIT(s)->source_path, "reload");
-        if (!c)
-                return -ENOMEM;
-        exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
+        if (supports_reload) {
+                c = exec_command_new(UNIT(s)->source_path, "reload");
+                if (!c)
+                        return -ENOMEM;
+                exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
+        }
 
         return 0;
 }
 
+static bool usage_contains_reload(const char *line) {
+        return (strcasestr(line, "{reload|") ||
+                strcasestr(line, "{reload}") ||
+                strcasestr(line, "{reload\"") ||
+                strcasestr(line, "|reload|") ||
+                strcasestr(line, "|reload}") ||
+                strcasestr(line, "|reload\""));
+}
+
 static int service_load_sysv_path(Service *s, const char *path) {
         FILE *f;
         Unit *u;
@@ -549,10 +547,12 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 NORMAL,
                 DESCRIPTION,
                 LSB,
-                LSB_DESCRIPTION
+                LSB_DESCRIPTION,
+                USAGE_CONTINUATION
         } state = NORMAL;
         char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
         struct stat st;
+        bool supports_reload = false;
 
         assert(s);
         assert(path);
@@ -601,8 +601,23 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 line++;
 
                 t = strstrip(l);
-                if (*t != '#')
+                if (*t != '#') {
+                        /* Try to figure out whether this init script supports
+                         * the reload operation. This heuristic looks for
+                         * "Usage" lines which include the reload option. */
+                        if ( state == USAGE_CONTINUATION ||
+                            (state == NORMAL && strcasestr(t, "usage"))) {
+                                if (usage_contains_reload(t)) {
+                                        supports_reload = true;
+                                        state = NORMAL;
+                                } else if (t[strlen(t)-1] == '\\')
+                                        state = USAGE_CONTINUATION;
+                                else
+                                        state = NORMAL;
+                        }
+
                         continue;
+                }
 
                 if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
                         state = LSB;
@@ -895,15 +910,8 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 }
         }
 
-        if ((r = sysv_exec_commands(s)) < 0)
+        if ((r = sysv_exec_commands(s, supports_reload)) < 0)
                 goto finish;
-        if (s->sysv_runlevels &&
-            chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) &&
-            chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
-                /* Service has both boot and "up" runlevels
-                   configured.  Kill the "up" ones. */
-                delete_chars(s->sysv_runlevels, RUNLEVELS_UP);
-        }
 
         if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
                 /* If there a runlevels configured for this service
@@ -922,7 +930,6 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 s->timeout_stop_usec = DEFAULT_SYSV_TIMEOUT_USEC;
         }
 
-
         /* Special setting for all SysV services */
         s->type = SERVICE_FORKING;
         s->remain_after_exit = !s->pid_file;
@@ -981,18 +988,13 @@ static int service_load_sysv_name(Service *s, const char *name) {
         assert(s);
         assert(name);
 
-        /* For SysV services we strip the boot.*, rc.* and *.sh
+        /* For SysV services we strip the rc.* and *.sh
          * prefixes/suffixes. */
 #if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
         if (endswith(name, ".sh.service"))
                 return -ENOENT;
 #endif
 
-#ifdef TARGET_SUSE
-        if (startswith(name, "boot."))
-                return -ENOENT;
-#endif
-
 #ifdef TARGET_FRUGALWARE
         if (startswith(name, "rc."))
                 return -ENOENT;
@@ -1020,21 +1022,6 @@ static int service_load_sysv_name(Service *s, const char *name) {
 #endif
                 free(path);
 
-#ifdef TARGET_SUSE
-                if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
-                        /* Try SUSE style boot.* init scripts */
-
-                        path = strjoin(*p, "/boot.", name, NULL);
-                        if (!path)
-                                return -ENOMEM;
-
-                        /* Drop .service suffix */
-                        path[strlen(path)-8] = 0;
-                        r = service_load_sysv_path(s, path);
-                        free(path);
-                }
-#endif
-
 #ifdef TARGET_FRUGALWARE
                 if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
                         /* Try Frugalware style rc.* init scripts */
@@ -1151,12 +1138,6 @@ static int service_verify(Service *s) {
                 return -EINVAL;
         }
 
-        if (s->type == SERVICE_ONESHOT &&
-            s->exec_command[SERVICE_EXEC_RELOAD]) {
-                log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
-                return -EINVAL;
-        }
-
         if (s->type == SERVICE_DBUS && !s->bus_name) {
                 log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
                 return -EINVAL;
@@ -1182,12 +1163,12 @@ static int service_add_default_dependencies(Service *s) {
          * majority of services. */
 
         /* First, pull in base system */
-        if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+        if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
 
                 if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
                         return r;
 
-        } else if (UNIT(s)->manager->running_as == MANAGER_USER) {
+        } else if (UNIT(s)->manager->running_as == SYSTEMD_USER) {
 
                 if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
                         return r;
@@ -1582,7 +1563,12 @@ static void service_set_state(Service *s, ServiceState state) {
                 cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true);
 
         if (old_state != state)
-                log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
+                log_struct(LOG_DEBUG,
+                           "UNIT=%s", UNIT(s)->id,
+                           "MESSAGE=%s changed %s -> %s", UNIT(s)->id,
+                                   service_state_to_string(old_state),
+                                   service_state_to_string(state),
+                           NULL);
 
         unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
         s->reload_result = SERVICE_SUCCESS;
@@ -1769,7 +1755,8 @@ static int service_spawn(
                 goto fail;
         }
 
-        if (!(our_env = new0(char*, 4))) {
+        our_env = new0(char*, 5);
+        if (!our_env) {
                 r = -ENOMEM;
                 goto fail;
         }
@@ -1792,10 +1779,14 @@ static int service_spawn(
                         goto fail;
                 }
 
-        if (!(final_env = strv_env_merge(2,
-                                         UNIT(s)->manager->environment,
-                                         our_env,
-                                         NULL))) {
+        if (s->meta.manager->running_as != SYSTEMD_SYSTEM)
+                if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+
+        final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
+        if (!final_env) {
                 r = -ENOMEM;
                 goto fail;
         }
@@ -1819,7 +1810,6 @@ static int service_spawn(
         if (r < 0)
                 goto fail;
 
-
         if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
                 /* FIXME: we need to do something here */
                 goto fail;
@@ -1902,7 +1892,12 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
              (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
              (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
              (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
-                                                         s->result == SERVICE_FAILURE_CORE_DUMP)))) {
+                                                         s->result == SERVICE_FAILURE_CORE_DUMP))) &&
+            (s->result != SERVICE_FAILURE_EXIT_CODE ||
+             !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) &&
+            (s->result != SERVICE_FAILURE_SIGNAL ||
+             !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status)))
+                ) {
 
                 r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
                 if (r < 0)
@@ -2445,6 +2440,7 @@ static int service_start_limit_test(Service *s) {
 
         case SERVICE_START_LIMIT_REBOOT_IMMEDIATE:
                 log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id);
+                sync();
                 reboot(RB_AUTOBOOT);
                 break;
 
@@ -2493,7 +2489,7 @@ static int service_start(Unit *u) {
         /* Make sure we don't enter a busy loop of some kind. */
         r = service_start_limit_test(s);
         if (r < 0) {
-                service_notify_sockets_dead(s, true);
+                service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false);
                 return r;
         }
 
@@ -2874,7 +2870,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         assert(s);
         assert(pid >= 0);
 
-        if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
+        if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
+                                     is_clean_exit_lsb(code, status, &s->success_status))
                 f = SERVICE_SUCCESS;
         else if (code == CLD_EXITED)
                 f = SERVICE_FAILURE_EXIT_CODE;
@@ -2906,8 +2903,16 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 f = SERVICE_SUCCESS;
                 }
 
-                log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
-                         "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status);
+                log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+                           "MESSAGE=%s: main process exited, code=%s, status=%i/%s",
+                                  u->id, sigchld_code_to_string(code), status,
+                                  strna(code == CLD_EXITED
+                                        ? exit_status_to_string(status, EXIT_STATUS_FULL)
+                                        : signal_to_string(status)),
+                           "UNIT=%s", u->id,
+                           "EXIT_CODE=%s", sigchld_code_to_string(code),
+                           "EXIT_STATUS=%i", status,
+                           NULL);
 
                 if (f != SERVICE_SUCCESS)
                         s->result = f;
@@ -2945,12 +2950,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         else
                                                 service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
                                         break;
-                                } else {
-                                        assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY);
-
-                                        /* Fall through */
                                 }
 
+                                /* Fall through */
+
                         case SERVICE_RUNNING:
                                 service_enter_running(s, f);
                                 break;
@@ -3020,7 +3023,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 break;
 
                         case SERVICE_START:
-                                assert(s->type == SERVICE_FORKING);
+                                if (s->type != SERVICE_FORKING)
+                                        /* Maybe spurious event due to a reload that changed the type? */
+                                        break;
 
                                 if (f != SERVICE_SUCCESS) {
                                         service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
@@ -3413,7 +3418,7 @@ static int service_enumerate(Manager *m) {
 
         assert(m);
 
-        if (m->running_as != MANAGER_SYSTEM)
+        if (m->running_as != SYSTEMD_SYSTEM)
                 return 0;
 
         zero(runlevel_services);
@@ -3485,7 +3490,7 @@ static int service_enumerate(Manager *m) {
 
                                 if (de->d_name[0] == 'S')  {
 
-                                        if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) {
+                                        if (rcnd_table[i].type == RUNLEVEL_UP) {
                                                 SERVICE(service)->sysv_start_priority_from_rcnd =
                                                         MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd);
 
@@ -3499,8 +3504,7 @@ static int service_enumerate(Manager *m) {
                                                 goto finish;
 
                                 } else if (de->d_name[0] == 'K' &&
-                                           (rcnd_table[i].type == RUNLEVEL_DOWN ||
-                                            rcnd_table[i].type == RUNLEVEL_SYSINIT)) {
+                                           (rcnd_table[i].type == RUNLEVEL_DOWN)) {
 
                                         if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0)
                                                 goto finish;
@@ -3534,9 +3538,7 @@ static int service_enumerate(Manager *m) {
          * runlevels we assume the stop jobs will be implicitly added
          * by the core logic. Also, we don't really distinguish here
          * between the runlevels 0 and 6 and just add them to the
-         * special shutdown target. On SUSE the boot.d/ runlevel is
-         * also used for shutdown, so we add links for that too to the
-         * shutdown target.*/
+         * special shutdown target. */
         SET_FOREACH(service, shutdown_services, j) {
                 service = unit_follow_merge(service);
 
@@ -3805,7 +3807,8 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
         [SERVICE_FAILURE_EXIT_CODE] = "exit-code",
         [SERVICE_FAILURE_SIGNAL] = "signal",
         [SERVICE_FAILURE_CORE_DUMP] = "core-dump",
-        [SERVICE_FAILURE_WATCHDOG] = "watchdog"
+        [SERVICE_FAILURE_WATCHDOG] = "watchdog",
+        [SERVICE_FAILURE_START_LIMIT] = "start-limit"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
@@ -3820,6 +3823,8 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction);
 
 const UnitVTable service_vtable = {
         .object_size = sizeof(Service),
+        .exec_context_offset = offsetof(Service, exec_context),
+
         .sections =
                 "Unit\0"
                 "Service\0"