chiark / gitweb /
systemd: use structured logging for unit changes
[elogind.git] / src / core / service.c
index 5f7ddfaf14d4db41610993f097e179c69a20445a..2febdb69d148ffd68e4d412717b56bbcd6dbd2d2 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"
@@ -525,7 +526,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);
@@ -542,14 +543,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;
@@ -559,10 +571,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);
@@ -611,8 +625,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;
@@ -905,7 +934,7 @@ 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) &&
@@ -1160,12 +1189,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;
@@ -1191,12 +1214,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;
@@ -1591,7 +1614,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;
@@ -1778,7 +1806,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;
         }
@@ -1801,10 +1830,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;
         }
@@ -1828,7 +1861,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;
@@ -2921,8 +2953,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;
@@ -3428,7 +3468,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);
@@ -3836,6 +3876,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"