chiark / gitweb /
sysv: implement /dev/initctl compatibility
[elogind.git] / service.c
index a983e0db93dfdfb2c4843a5b73bd95847793ddeb..53d5505c2ea16b007607291f7e32faf54b3d8763 100644 (file)
--- a/service.c
+++ b/service.c
@@ -370,7 +370,13 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                         if (r == 0)
                                                 continue;
 
-                                        r = unit_add_name(u, m);
+                                        if (unit_name_to_type(m) == UNIT_SERVICE)
+                                                r = unit_add_name(u, m);
+                                        else {
+                                                if ((r = unit_add_dependency_by_name_inverse(u, UNIT_REQUIRES, m)) >= 0)
+                                                        r = unit_add_dependency_by_name(u, UNIT_BEFORE, m);
+                                        }
+
                                         free(m);
 
                                         if (r < 0)
@@ -475,6 +481,10 @@ static int service_load_sysv_path(Service *s, const char *path) {
         if ((r = sysv_exec_commands(s)) < 0)
                 goto finish;
 
+        if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_SYSINIT_SERVICE)) < 0 ||
+            (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_SYSINIT_SERVICE)) < 0)
+                goto finish;
+
         r = 1;
 
 finish:
@@ -504,7 +514,7 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 r = service_load_sysv_path(s, path);
                 free(path);
 
-                if (r >= 0)
+                if (r != 0)
                         return r;
         }
 
@@ -557,6 +567,8 @@ static int service_init(Unit *u) {
         s->state = SERVICE_DEAD;
 
         s->sysv_start_priority = -1;
+        s->permissions_start_only = false;
+        s->root_directory_start_only = false;
 
         RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
 
@@ -566,7 +578,7 @@ static int service_init(Unit *u) {
                 return r;
         }
 
-        /* Load a classic init script as a fallback, if we couldn*t find anything */
+        /* Load a classic init script as a fallback, if we couldn't find anything */
         if (r == 0)
                 if ((r = service_load_sysv(s)) <= 0) {
                         service_done(u);
@@ -579,6 +591,12 @@ static int service_init(Unit *u) {
                 return r;
         }
 
+        /* Add default cgroup */
+        if ((r = unit_add_default_cgroup(u)) < 0) {
+                service_done(u);
+                return r;
+        }
+
         return 0;
 }
 
@@ -595,8 +613,16 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
         prefix2 = p2 ? p2 : prefix;
 
         fprintf(f,
-                "%sService State: %s\n",
-                prefix, service_state_to_string(s->state));
+                "%sService State: %s\n"
+                "%sPermissionsStartOnly: %s\n"
+                "%sRootDirectoryStartOnly: %s\n"
+                "%sValidNoProcess: %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, service_type_to_string(s->type));
 
         if (s->pid_file)
                 fprintf(f,
@@ -859,7 +885,15 @@ fail:
         return r;
 }
 
-static int service_spawn(Service *s, ExecCommand *c, bool timeout, bool pass_fds, pid_t *_pid) {
+static int service_spawn(
+                Service *s,
+                ExecCommand *c,
+                bool timeout,
+                bool pass_fds,
+                bool apply_permissions,
+                bool apply_chroot,
+                pid_t *_pid) {
+
         pid_t pid;
         int r;
         int *fds = NULL;
@@ -879,7 +913,13 @@ static int service_spawn(Service *s, ExecCommand *c, bool timeout, bool pass_fds
         } else
                 unit_unwatch_timer(UNIT(s), &s->timer_watch);
 
-        if ((r = exec_spawn(c, &s->exec_context, fds, n_fds, &pid)) < 0)
+        if ((r = exec_spawn(c,
+                            &s->exec_context,
+                            fds, n_fds,
+                            apply_permissions,
+                            apply_chroot,
+                            UNIT(s)->meta.cgroup_bondings,
+                            &pid)) < 0)
                 goto fail;
 
         if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
@@ -935,7 +975,13 @@ static void service_enter_stop_post(Service *s, bool success) {
                 s->failure = true;
 
         if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]))
-                if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0)
+                if ((r = service_spawn(s,
+                                       s->control_command,
+                                       true,
+                                       false,
+                                       !s->permissions_start_only,
+                                       !s->root_directory_start_only,
+                                       &s->control_pid)) < 0)
                         goto fail;
 
 
@@ -1011,7 +1057,13 @@ static void service_enter_stop(Service *s, bool success) {
                 s->failure = true;
 
         if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP]))
-                if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0)
+                if ((r = service_spawn(s,
+                                       s->control_command,
+                                       true,
+                                       false,
+                                       !s->permissions_start_only,
+                                       !s->root_directory_start_only,
+                                       &s->control_pid)) < 0)
                         goto fail;
 
         service_set_state(s, SERVICE_STOP);
@@ -1031,7 +1083,13 @@ static void service_enter_start_post(Service *s) {
         assert(s);
 
         if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST]))
-                if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0)
+                if ((r = service_spawn(s,
+                                       s->control_command,
+                                       true,
+                                       false,
+                                       !s->permissions_start_only,
+                                       !s->root_directory_start_only,
+                                       &s->control_pid)) < 0)
                         goto fail;
 
 
@@ -1056,7 +1114,13 @@ static void service_enter_start(Service *s) {
         assert(s->exec_command[SERVICE_EXEC_START]);
         assert(!s->exec_command[SERVICE_EXEC_START]->command_next);
 
-        if ((r = service_spawn(s, s->exec_command[SERVICE_EXEC_START], s->type == SERVICE_FORKING, true, &pid)) < 0)
+        if ((r = service_spawn(s,
+                               s->exec_command[SERVICE_EXEC_START],
+                               s->type == SERVICE_FORKING,
+                               true,
+                               true,
+                               true,
+                               &pid)) < 0)
                 goto fail;
 
         service_set_state(s, SERVICE_START);
@@ -1099,7 +1163,13 @@ static void service_enter_start_pre(Service *s) {
         assert(s);
 
         if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE]))
-                if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0)
+                if ((r = service_spawn(s,
+                                       s->control_command,
+                                       true,
+                                       false,
+                                       !s->permissions_start_only,
+                                       !s->root_directory_start_only,
+                                       &s->control_pid)) < 0)
                         goto fail;
 
         service_set_state(s, SERVICE_START_PRE);
@@ -1137,7 +1207,13 @@ static void service_enter_reload(Service *s) {
         assert(s);
 
         if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD]))
-                if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0)
+                if ((r = service_spawn(s,
+                                       s->control_command,
+                                       true,
+                                       false,
+                                       !s->permissions_start_only,
+                                       !s->root_directory_start_only,
+                                       &s->control_pid)) < 0)
                         goto fail;
 
         service_set_state(s, SERVICE_RELOAD);
@@ -1164,7 +1240,13 @@ static void service_run_next(Service *s, bool success) {
 
         s->control_command = s->control_command->command_next;
 
-        if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0)
+        if ((r = service_spawn(s,
+                               s->control_command,
+                               true,
+                               false,
+                               !s->permissions_start_only,
+                               !s->root_directory_start_only,
+                               &s->control_pid)) < 0)
                 goto fail;
 
         return;
@@ -1275,7 +1357,7 @@ static int main_pid_good(Service *s) {
                 return s->main_pid > 0;
 
         /* We don't know the pid */
-        return -1;
+        return -EAGAIN;
 }
 
 static bool control_pid_good(Service *s) {
@@ -1284,6 +1366,15 @@ static bool control_pid_good(Service *s) {
         return s->control_pid > 0;
 }
 
+static int cgroup_good(Service *s) {
+        assert(s);
+
+        if (s->valid_no_process)
+                return -EAGAIN;
+
+        return cgroup_bonding_is_empty_list(UNIT(s)->meta.cgroup_bondings);
+}
+
 static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         Service *s = SERVICE(u);
         bool success;
@@ -1357,7 +1448,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                  * don't care about failing commands. */
 
                 if (s->control_command->command_next &&
-                    (success || (s->state == SERVICE_EXEC_STOP || s->state == SERVICE_EXEC_STOP_POST)))
+                    (success || (s->state == SERVICE_STOP || s->state == SERVICE_STOP_POST)))
 
                         /* There is another command to *
                          * execute, so let's do that. */
@@ -1416,7 +1507,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                         case SERVICE_RELOAD:
                                 if (success) {
-                                        if (main_pid_good(s) != 0)
+                                        if (main_pid_good(s) != 0 && cgroup_good(s) != 0)
                                                 service_set_state(s, SERVICE_RUNNING);
                                         else
                                                 service_enter_stop(s, true);
@@ -1519,6 +1610,33 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
         }
 }
 
+static void service_cgroup_notify_event(Unit *u) {
+        Service *s = SERVICE(u);
+
+        assert(u);
+
+        log_debug("%s: cgroup is empty", unit_id(u));
+
+        switch (s->state) {
+
+                /* Waiting for SIGCHLD is usually more interesting,
+                 * because it includes return codes/signals. Which is
+                 * why we ignore the cgroup events for most cases,
+                 * except when we don't know pid which to expect the
+                 * SIGCHLD for. */
+
+        case SERVICE_RUNNING:
+
+                if (!s->valid_no_process && main_pid_good(s) <= 0)
+                        service_enter_stop(s, true);
+
+                break;
+
+        default:
+                ;
+        }
+}
+
 static int service_enumerate(Manager *m) {
 
         static const char * const rcnd[] = {
@@ -1597,11 +1715,6 @@ static int service_enumerate(Manager *m) {
                                 if ((r = manager_load_unit(m, name, &service)) < 0)
                                         goto finish;
 
-                                /* Don't allow that non-SysV services
-                                 * are started via rcN.d/ links. */
-                                if (!SERVICE(service)->sysv_path)
-                                        continue;
-
                                 if ((r = manager_load_unit(m, rcnd[i+1], &runlevel)) < 0)
                                         goto finish;
 
@@ -1697,5 +1810,7 @@ const UnitVTable service_vtable = {
         .sigchld_event = service_sigchld_event,
         .timer_event = service_timer_event,
 
+        .cgroup_notify_empty = service_cgroup_notify_event,
+
         .enumerate = service_enumerate
 };