chiark / gitweb /
execute: improve exec_spawn() logging
[elogind.git] / service.c
index 865f84128c69d68c96815ff840f483acaf489b74..e5a9658bcfa7784edef26acb0cf17f917998c194 100644 (file)
--- a/service.c
+++ b/service.c
 #define LINE_MAX 4096
 
 static const char * const rcnd_table[] = {
-        "/rc0.d", SPECIAL_RUNLEVEL0_TARGET,
-        "/rc1.d", SPECIAL_RUNLEVEL1_TARGET,
-        "/rc2.d", SPECIAL_RUNLEVEL2_TARGET,
-        "/rc3.d", SPECIAL_RUNLEVEL3_TARGET,
-        "/rc4.d", SPECIAL_RUNLEVEL4_TARGET,
-        "/rc5.d", SPECIAL_RUNLEVEL5_TARGET,
-        "/rc6.d", SPECIAL_RUNLEVEL6_TARGET
+        "/rc0.d",  SPECIAL_RUNLEVEL0_TARGET,
+        "/rc1.d",  SPECIAL_RUNLEVEL1_TARGET,
+        "/rc2.d",  SPECIAL_RUNLEVEL2_TARGET,
+        "/rc3.d",  SPECIAL_RUNLEVEL3_TARGET,
+        "/rc4.d",  SPECIAL_RUNLEVEL4_TARGET,
+        "/rc5.d",  SPECIAL_RUNLEVEL5_TARGET,
+        "/rc6.d",  SPECIAL_RUNLEVEL6_TARGET,
+        "/boot.d", SPECIAL_BASIC_TARGET
 };
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
@@ -73,6 +74,9 @@ static void service_done(Unit *u) {
         free(s->sysv_path);
         s->sysv_path = NULL;
 
+        free(s->sysv_runlevels);
+        s->sysv_runlevels = NULL;
+
         exec_context_done(&s->exec_context);
         exec_command_free_array(s->exec_command, _SERVICE_EXEC_MAX);
         s->control_command = NULL;
@@ -153,7 +157,10 @@ static int sysv_chkconfig_order(Service *s) {
                 if (t->sysv_start_priority < 0)
                         continue;
 
-                if (s->sysv_has_lsb && t->sysv_has_lsb)
+                /* If both units have modern headers we don't care
+                 * about the priorities */
+                if ((!s->sysv_path || s->sysv_has_lsb) &&
+                    (!t->sysv_path || t->sysv_has_lsb))
                         continue;
 
                 if (t->sysv_start_priority < s->sysv_start_priority)
@@ -343,22 +350,38 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
 
                         if (startswith(t, "chkconfig:")) {
                                 int start_priority;
+                                char runlevels[16], *k;
 
                                 state = NORMAL;
 
-                                if (sscanf(t+10, "%*15s %i %*i",
-                                           &start_priority) != 1) {
+                                if (sscanf(t+10, "%15s %i %*i",
+                                           runlevels,
+                                           &start_priority) != 2) {
 
                                         log_warning("[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line);
                                         continue;
                                 }
 
-                                if (start_priority < 0 || start_priority > 99) {
+                                if (start_priority < 0 || start_priority > 99)
                                         log_warning("[%s:%u] Start priority out of range. Ignoring.", path, line);
-                                        continue;
+                                else
+                                        s->sysv_start_priority = start_priority;
+
+                                char_array_0(runlevels);
+                                k = delete_chars(runlevels, WHITESPACE "-");
+
+                                if (k[0]) {
+                                        char *d;
+
+                                        if (!(d = strdup(k))) {
+                                                r = -ENOMEM;
+                                                goto finish;
+                                        }
+
+                                        free(s->sysv_runlevels);
+                                        s->sysv_runlevels = d;
                                 }
 
-                                s->sysv_start_priority = start_priority;
 
                         } else if (startswith(t, "description:")) {
 
@@ -483,20 +506,28 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
                                         if (r == 0)
                                                 continue;
 
-                                        if (!(r = unit_add_dependency_by_name(u, UNIT_AFTER, m)) < 0) {
-                                                free(m);
-                                                goto finish;
-                                        }
-
-                                        r = unit_add_dependency_by_name(
-                                                        u,
-                                                        startswith(t, "Required-Start:") ? UNIT_REQUIRES : UNIT_WANTS,
-                                                        m);
+                                        r = unit_add_dependency_by_name(u, UNIT_AFTER, m);
                                         free(m);
 
                                         if (r < 0)
                                                 goto finish;
                                 }
+                        } else if (startswith(t, "Default-Start:")) {
+                                char *k, *d;
+
+                                state = LSB;
+
+                                k = delete_chars(t+14, WHITESPACE "-");
+
+                                if (k[0] != 0) {
+                                        if (!(d = strdup(k))) {
+                                                r = -ENOMEM;
+                                                goto finish;
+                                        }
+
+                                        free(s->sysv_runlevels);
+                                        s->sysv_runlevels = d;
+                                }
 
                         } else if (startswith(t, "Description:")) {
                                 char *d;
@@ -511,7 +542,8 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
                                 free(u->meta.description);
                                 u->meta.description = d;
 
-                        } else if (startswith(t, "Short-Description:") && !u->meta.description) {
+                        } else if (startswith(t, "Short-Description:") &&
+                                   !u->meta.description) {
                                 char *d;
 
                                 /* We use the short description only
@@ -524,7 +556,6 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
                                         goto finish;
                                 }
 
-                                free(u->meta.description);
                                 u->meta.description = d;
 
                         } else if (state == LSB_DESCRIPTION) {
@@ -564,9 +595,17 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
         if ((r = sysv_exec_commands(s)) < 0)
                 goto finish;
 
-        if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_BASIC_SERVICE)) < 0 ||
-            (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_BASIC_SERVICE)) < 0)
-                goto finish;
+        if (!s->sysv_runlevels || chars_intersect("12345", s->sysv_runlevels)) {
+                /* If there a runlevels configured for this service
+                 * but none of the standard ones, then we assume this
+                 * is some special kind of service (which might be
+                 * needed for early boot) and don't create any links
+                 * to it. */
+
+                if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_BASIC_TARGET)) < 0 ||
+                    (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_BASIC_TARGET)) < 0)
+                        goto finish;
+        }
 
         *new_state = UNIT_LOADED;
         r = 0;
@@ -664,8 +703,12 @@ static int service_init(Unit *u, UnitLoadState *new_state) {
         s->sysv_start_priority = -1;
         s->permissions_start_only = false;
         s->root_directory_start_only = false;
-
+        s->valid_no_process = false;
+        s->kill_mode = 0;
         s->sysv_has_lsb = false;
+        s->main_pid = s->control_pid = 0;
+        s->main_pid_known = false;
+        s->failure = false;
 
         RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
 
@@ -719,13 +762,25 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sPermissionsStartOnly: %s\n"
                 "%sRootDirectoryStartOnly: %s\n"
                 "%sValidNoProcess: %s\n"
+                "%sKillMode: %s\n"
                 "%sType: %s\n",
                 prefix, service_state_to_string(s->state),
                 prefix, yes_no(s->permissions_start_only),
                 prefix, yes_no(s->root_directory_start_only),
                 prefix, yes_no(s->valid_no_process),
+                prefix, kill_mode_to_string(s->kill_mode),
                 prefix, service_type_to_string(s->type));
 
+        if (s->control_pid > 0)
+                fprintf(f,
+                        "%sControl PID: %llu\n",
+                        prefix, (unsigned long long) s->control_pid);
+
+        if (s->main_pid > 0)
+                fprintf(f,
+                        "%sMain PID: %llu\n",
+                        prefix, (unsigned long long) s->main_pid);
+
         if (s->pid_file)
                 fprintf(f,
                         "%sPIDFile: %s\n",
@@ -756,6 +811,9 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                         "%sSysVStartPriority: %i\n",
                         prefix, s->sysv_start_priority);
 
+        if (s->sysv_runlevels)
+                fprintf(f, "%sSysVRunLevels: %s\n",
+                        prefix, s->sysv_runlevels);
 
         free(p2);
 }
@@ -784,7 +842,17 @@ static int service_load_pid_file(Service *s) {
         if ((unsigned long) (pid_t) p != p)
                 return -ERANGE;
 
-        s->main_pid = p;
+        if (kill((pid_t) p, 0) < 0 && errno != EPERM) {
+                log_warning("PID %llu read from file %s does not exist. Your service or init script might be broken.",
+                            (unsigned long long) p, s->pid_file);
+                return -ESRCH;
+        }
+
+        if ((r = unit_watch_pid(UNIT(s), (pid_t) p)) < 0)
+                /* FIXME: we need to do something here */
+                return r;
+
+        s->main_pid = (pid_t) p;
         s->main_pid_known = true;
 
         return 0;
@@ -923,6 +991,9 @@ static void service_set_state(Service *s, ServiceState state) {
             state == SERVICE_AUTO_RESTART)
                 service_notify_sockets(s);
 
+        if (old_state == state)
+                return;
+
         log_debug("%s changed %s → %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state));
 
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
@@ -1115,23 +1186,34 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) {
 
                 sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? SIGTERM : SIGKILL;
 
-                r = 0;
-                if (s->main_pid > 0) {
-                        if (kill(s->main_pid, sig) < 0 && errno != ESRCH)
-                                r = -errno;
-                        else
-                                sent = true;
-                }
+                if (s->kill_mode == KILL_CONTROL_GROUP) {
 
-                if (s->control_pid > 0) {
-                        if (kill(s->control_pid, sig) < 0 && errno != ESRCH)
-                                r = -errno;
-                        else
+                        if ((r = cgroup_bonding_kill_list(UNIT(s)->meta.cgroup_bondings, sig)) < 0) {
+                                if (r != -EAGAIN && r != -ESRCH)
+                                        goto fail;
+                        else
                                 sent = true;
                 }
 
-                if (r < 0)
-                        goto fail;
+                if (!sent) {
+                        r = 0;
+                        if (s->main_pid > 0) {
+                                if (kill(s->kill_mode == KILL_PROCESS ? s->main_pid : -s->main_pid, sig) < 0 && errno != ESRCH)
+                                        r = -errno;
+                                else
+                                        sent = true;
+                        }
+
+                        if (s->control_pid > 0) {
+                                if (kill(s->kill_mode == KILL_PROCESS ? s->control_pid : -s->control_pid, sig) < 0 && errno != ESRCH)
+                                        r = -errno;
+                                else
+                                        sent = true;
+                        }
+
+                        if (r < 0)
+                                goto fail;
+                }
         }
 
         service_set_state(s, state);
@@ -1499,7 +1581,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                         s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status;
                 }
 
-                log_debug("%s: main process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status);
+                log_debug("%s: main process exited, code=%s, status=%i", unit_id(u), sigchld_code_to_string(code), status);
 
                 /* The service exited, so the service is officially
                  * gone. */
@@ -1750,7 +1832,7 @@ static int service_enumerate(Manager *m) {
 
         assert(m);
 
-        STRV_FOREACH(p, m->sysvinit_path)
+        STRV_FOREACH(p, m->sysvrcnd_path)
                 for (i = 0; i < ELEMENTSOF(rcnd_table); i += 2) {
                         struct dirent *de;