chiark / gitweb /
notify: add minimal readiness/status protocol for spawned daemons
[elogind.git] / src / service.c
index 651f7c82ca484e730bc587c427c5cc15456bddf3..6e35a4da58e9df2b3b1e83324424615bdaee8e85 100644 (file)
@@ -149,6 +149,9 @@ static void service_done(Unit *u) {
         free(s->sysv_runlevels);
         s->sysv_runlevels = NULL;
 
+        free(s->status_text);
+        s->status_text = NULL;
+
         exec_context_done(&s->exec_context);
         exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
         s->control_command = NULL;
@@ -675,7 +678,7 @@ static int service_load_sysv_name(Service *s, const char *name) {
             endswith(name, ".sh.service"))
                 return -ENOENT;
 
-        STRV_FOREACH(p, UNIT(s)->meta.manager->sysvinit_path) {
+        STRV_FOREACH(p, UNIT(s)->meta.manager->lookup_paths.sysvinit_path) {
                 char *path;
                 int r;
 
@@ -726,7 +729,7 @@ static int service_load_sysv(Service *s) {
         /* Load service data from SysV init scripts, preferably with
          * LSB headers ... */
 
-        if (strv_isempty(UNIT(s)->meta.manager->sysvinit_path))
+        if (strv_isempty(UNIT(s)->meta.manager->lookup_paths.sysvinit_path))
                 return 0;
 
         if ((t = UNIT(s)->meta.id))
@@ -907,6 +910,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 fprintf(f, "%sSysVRunLevels: %s\n",
                         prefix, s->sysv_runlevels);
 
+        if (s->status_text)
+                fprintf(f, "%sStatus Text: %s\n",
+                        prefix, s->status_text);
+
         free(p2);
 }
 
@@ -936,6 +943,9 @@ static int service_load_pid_file(Service *s) {
         if ((unsigned long) (pid_t) p != p)
                 return -ERANGE;
 
+        if (p <= 1)
+                return -ERANGE;
+
         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);
@@ -1117,7 +1127,9 @@ static int service_coldplug(Unit *u) {
 
                 if ((s->deserialized_state == SERVICE_START &&
                      (s->type == SERVICE_FORKING ||
-                      s->type == SERVICE_DBUS)) ||
+                      s->type == SERVICE_DBUS ||
+                      s->type == SERVICE_FINISH ||
+                      s->type == SERVICE_NOTIFY)) ||
                     s->deserialized_state == SERVICE_START_POST ||
                     s->deserialized_state == SERVICE_RUNNING ||
                     s->deserialized_state == SERVICE_RELOAD ||
@@ -1377,7 +1389,7 @@ static void service_enter_stop_post(Service *s, bool success) {
         return;
 
 fail:
-        log_warning("%s failed to run stop-post executable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->meta.id, strerror(-r));
         service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
 }
 
@@ -1473,7 +1485,7 @@ static void service_enter_stop(Service *s, bool success) {
         return;
 
 fail:
-        log_warning("%s failed to run stop executable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run 'stop' task: %s", UNIT(s)->meta.id, strerror(-r));
         service_enter_signal(s, SERVICE_STOP_SIGTERM, false);
 }
 
@@ -1518,7 +1530,7 @@ static void service_enter_start_post(Service *s) {
         return;
 
 fail:
-        log_warning("%s failed to run start-post executable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->meta.id, strerror(-r));
         service_enter_stop(s, false);
 }
 
@@ -1538,7 +1550,7 @@ static void service_enter_start(Service *s) {
 
         if ((r = service_spawn(s,
                                s->exec_command[SERVICE_EXEC_START],
-                               s->type == SERVICE_FORKING || s->type == SERVICE_DBUS,
+                               s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY,
                                true,
                                true,
                                true,
@@ -1566,13 +1578,15 @@ static void service_enter_start(Service *s) {
                 service_set_state(s, SERVICE_START);
 
         } else if (s->type == SERVICE_FINISH ||
-                   s->type == SERVICE_DBUS) {
+                   s->type == SERVICE_DBUS ||
+                   s->type == SERVICE_NOTIFY) {
 
                 /* For finishing services we wait until the start
                  * process exited, too, but it is our main process. */
 
                 /* For D-Bus services we know the main pid right away,
-                 * but wait for the bus name to appear on the bus. */
+                 * but wait for the bus name to appear on the
+                 * bus. Notify services are similar. */
 
                 s->main_pid = pid;
                 s->main_pid_known = true;
@@ -1584,7 +1598,7 @@ static void service_enter_start(Service *s) {
         return;
 
 fail:
-        log_warning("%s failed to run start exectuable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run 'start' task: %s", UNIT(s)->meta.id, strerror(-r));
         service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
 }
 
@@ -1613,7 +1627,7 @@ static void service_enter_start_pre(Service *s) {
         return;
 
 fail:
-        log_warning("%s failed to run start-pre executable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->meta.id, strerror(-r));
         service_enter_dead(s, false, true);
 }
 
@@ -1660,7 +1674,7 @@ static void service_enter_reload(Service *s) {
         return;
 
 fail:
-        log_warning("%s failed to run reload executable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run 'reload' task: %s", UNIT(s)->meta.id, strerror(-r));
         service_enter_stop(s, false);
 }
 
@@ -1690,7 +1704,7 @@ static void service_run_next(Service *s, bool success) {
         return;
 
 fail:
-        log_warning("%s failed to run spawn next executable: %s", UNIT(s)->meta.id, strerror(-r));
+        log_warning("%s failed to run spawn next task: %s", UNIT(s)->meta.id, strerror(-r));
 
         if (s->state == SERVICE_START_PRE)
                 service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
@@ -1943,7 +1957,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 exec_status_fill(&s->main_exec_status, pid, code, status);
                 s->main_pid = 0;
 
-                if (s->type == SERVICE_SIMPLE || s->type == SERVICE_FINISH) {
+                if (s->type != SERVICE_FORKING) {
                         assert(s->exec_command[SERVICE_EXEC_START]);
                         s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status;
                 }
@@ -1971,7 +1985,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
                                 break;
                         } else {
-                                assert(s->type == SERVICE_DBUS);
+                                assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY);
 
                                 /* Fall through */
                         }
@@ -2098,8 +2112,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 assert_not_reached("Uh, control process died at wrong time.");
                         }
                 }
-        } else
-                assert_not_reached("Got SIGCHLD for unkown PID");
+        }
 }
 
 static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
@@ -2192,6 +2205,57 @@ static void service_cgroup_notify_event(Unit *u) {
         }
 }
 
+static void service_notify_message(Unit *u, char **tags) {
+        Service *s = SERVICE(u);
+        const char *e;
+
+        assert(u);
+
+        log_debug("%s: Got message", u->meta.id);
+
+        /* Interpret MAINPID= */
+        if ((e = strv_find_prefix(tags, "MAINPID=")) &&
+            (s->state == SERVICE_START ||
+             s->state == SERVICE_START_POST ||
+             s->state == SERVICE_RUNNING ||
+             s->state == SERVICE_RELOAD)) {
+                unsigned long pid;
+
+                if (safe_atolu(e + 8, &pid) < 0 ||
+                    (unsigned long) (pid_t) pid != pid ||
+                    pid <= 1)
+                        log_warning("Failed to parse %s", e);
+                else {
+                        log_debug("%s: got %s", u->meta.id, e);
+                        s->main_pid = (pid_t) pid;
+                }
+        }
+
+        /* Interpret READY= */
+        if (s->type == SERVICE_NOTIFY &&
+            s->state == SERVICE_START &&
+            strv_find(tags, "READY=1")) {
+                log_debug("%s: got READY=1", u->meta.id);
+
+                service_enter_start_post(s);
+        }
+
+        /* Interpret STATUS= */
+        if ((e = strv_find_prefix(tags, "STATUS="))) {
+                char *t;
+
+                if (!(t = strdup(e+7))) {
+                        log_error("Failed to allocate string.");
+                        return;
+                }
+
+                log_debug("%s: got %s", u->meta.id, e);
+
+                free(s->status_text);
+                s->status_text = t;
+        }
+}
+
 static int service_enumerate(Manager *m) {
         char **p;
         unsigned i;
@@ -2201,7 +2265,7 @@ static int service_enumerate(Manager *m) {
 
         assert(m);
 
-        STRV_FOREACH(p, m->sysvrcnd_path)
+        STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
                 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
                         struct dirent *de;
 
@@ -2453,7 +2517,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
         [SERVICE_FORKING] = "forking",
         [SERVICE_SIMPLE] = "simple",
         [SERVICE_FINISH] = "finish",
-        [SERVICE_DBUS] = "dbus"
+        [SERVICE_DBUS] = "dbus",
+        [SERVICE_NOTIFY] = "notify"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
@@ -2499,6 +2564,7 @@ const UnitVTable service_vtable = {
         .timer_event = service_timer_event,
 
         .cgroup_notify_empty = service_cgroup_notify_event,
+        .notify_message = service_notify_message,
 
         .bus_name_owner_change = service_bus_name_owner_change,
         .bus_query_pid_done = service_bus_query_pid_done,