systemd: backport serialization change from v234
[AGL/meta-agl.git] / meta-agl / recipes-core / systemd / systemd / backport-v234-e266c06.patch
diff --git a/meta-agl/recipes-core/systemd/systemd/backport-v234-e266c06.patch b/meta-agl/recipes-core/systemd/systemd/backport-v234-e266c06.patch
new file mode 100644 (file)
index 0000000..1f4a23e
--- /dev/null
@@ -0,0 +1,315 @@
+commit e266c068b5597e18b2299f9c9d3ee6cf04198c41
+Author: Michal Sekletar <msekleta@redhat.com>
+Date:   Mon Jan 23 17:12:35 2017 +0100
+
+    service: serialize information about currently executing command
+    
+    Stored information will help us to resume execution after the
+    daemon-reload.
+    
+    This commit implements following scheme,
+    
+    * On serialization:
+      - we count rank of the currently executing command
+      - we store command type, its rank and command line arguments
+    
+    * On deserialization:
+      - configuration is parsed and loaded
+      - we deserialize stored data, command type, rank and arguments
+      - we look at the given rank in the list and if command there has same
+        arguments then we restore execution at that point
+      - otherwise we search respective command list and we look for command
+        that has the same arguments
+      - if both methods fail we do not do not resume execution at all
+    
+    To better illustrate how does above scheme works, please consider
+    following cases (<<< denotes position where we resume execution after reload)
+    
+    ; Original unit file
+    [Service]
+    ExecStart=/bin/true <<<
+    ExecStart=/bin/false
+    
+    ; Swapped commands
+    ; Second command is not going to be executed
+    [Service]
+    ExecStart=/bin/false
+    ExecStart=/bin/true <<<
+    
+    ; Commands added before
+    ; Same commands are problematic and execution could be restarted at wrong place
+    [Service]
+    ExecStart=/bin/foo
+    ExecStart=/bin/bar
+    ExecStart=/bin/true <<<
+    ExecStart=/bin/false
+    
+    ; Commands added after
+    ; Same commands are not an issue in this case
+    [Service]
+    ExecStart=/bin/true <<<
+    ExecStart=/bin/false
+    ExecStart=/bin/foo
+    ExecStart=/bin/bar
+    
+    ; New commands interleaved with old commands
+    ; Some new commands will be executed while others won't
+    ExecStart=/bin/foo
+    ExecStart=/bin/true <<<
+    ExecStart=/bin/bar
+    ExecStart=/bin/false
+    
+    As you can see, above scheme has some drawbacks. However, in most
+    cases (we assume that in most common case unit file command list is not
+    changed while some other command is running for the same unit) it
+    should cause that systemd does the right thing, which is restoring
+    execution exactly at the point we were before daemon-reload.
+    
+    Fixes #518
+
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+
+Upstream-Status: backport
+
+diff --git a/src/core/service.c b/src/core/service.c
+index 74054887b..5e681fb71 100644
+--- a/src/core/service.c
++++ b/src/core/service.c
+@@ -45,6 +45,7 @@
+ #include "service.h"
+ #include "signal-util.h"
+ #include "special.h"
++#include "stdio-util.h"
+ #include "string-table.h"
+ #include "string-util.h"
+ #include "strv.h"
+@@ -2140,6 +2141,80 @@ _pure_ static bool service_can_reload(Unit *u) {
+         return !!s->exec_command[SERVICE_EXEC_RELOAD];
+ }
++static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) {
++        Service *s = SERVICE(u);
++        unsigned idx = 0;
++        ExecCommand *first, *c;
++
++        assert(s);
++
++        first = s->exec_command[id];
++
++        /* Figure out where we are in the list by walking back to the beginning */
++        for (c = current; c != first; c = c->command_prev)
++                idx++;
++
++        return idx;
++}
++
++static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
++        Service *s = SERVICE(u);
++        ServiceExecCommand id;
++        unsigned idx;
++        const char *type;
++        char **arg;
++        _cleanup_strv_free_ char **escaped_args = NULL;
++        _cleanup_free_ char *args = NULL, *p = NULL;
++        size_t allocated = 0, length = 0;
++
++        assert(s);
++        assert(f);
++
++        if (!command)
++                return 0;
++
++        if (command == s->control_command) {
++                type = "control";
++                id = s->control_command_id;
++        } else {
++                type = "main";
++                id = SERVICE_EXEC_START;
++        }
++
++        idx = service_exec_command_index(u, id, command);
++
++        STRV_FOREACH(arg, command->argv) {
++                size_t n;
++                _cleanup_free_ char *e = NULL;
++
++                e = xescape(*arg, WHITESPACE);
++                if (!e)
++                        return -ENOMEM;
++
++                n = strlen(e);
++                if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
++                        return -ENOMEM;
++
++                if (length > 0)
++                        args[length++] = ' ';
++
++                memcpy(args + length, e, n);
++                length += n;
++        }
++
++        if (!GREEDY_REALLOC(args, allocated, length + 1))
++                return -ENOMEM;
++        args[length++] = 0;
++
++        p = xescape(command->path, WHITESPACE);
++        if (!p)
++                return -ENOMEM;
++
++        fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
++
++        return 0;
++}
++
+ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
+         Service *s = SERVICE(u);
+         ServiceFDStore *fs;
+@@ -2167,11 +2242,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
+         if (r < 0)
+                 return r;
+-        /* FIXME: There's a minor uncleanliness here: if there are
+-         * multiple commands attached here, we will start from the
+-         * first one again */
+-        if (s->control_command_id >= 0)
+-                unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
++        service_serialize_exec_command(u, f, s->control_command);
++        service_serialize_exec_command(u, f, s->main_command);
+         r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
+         if (r < 0)
+@@ -2227,6 +2299,106 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
+         return 0;
+ }
++static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) {
++        Service *s = SERVICE(u);
++        int r;
++        unsigned idx = 0, i;
++        bool control, found = false;
++        ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID;
++        ExecCommand *command = NULL;
++        _cleanup_free_ char *args = NULL, *path = NULL;
++        _cleanup_strv_free_ char **argv = NULL;
++
++        enum ExecCommandState {
++                STATE_EXEC_COMMAND_TYPE,
++                STATE_EXEC_COMMAND_INDEX,
++                STATE_EXEC_COMMAND_PATH,
++                STATE_EXEC_COMMAND_ARGS,
++                _STATE_EXEC_COMMAND_MAX,
++                _STATE_EXEC_COMMAND_INVALID = -1,
++        } state;
++
++        assert(s);
++        assert(key);
++        assert(value);
++
++        control = streq(key, "control-command");
++
++        state = STATE_EXEC_COMMAND_TYPE;
++
++        for (;;) {
++                _cleanup_free_ char *arg = NULL;
++
++                r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
++                if (r == 0)
++                        break;
++                else if (r < 0)
++                        return r;
++
++                switch (state) {
++                case STATE_EXEC_COMMAND_TYPE:
++                        id = service_exec_command_from_string(arg);
++                        if (id < 0)
++                                return -EINVAL;
++
++                        state = STATE_EXEC_COMMAND_INDEX;
++                        break;
++                case STATE_EXEC_COMMAND_INDEX:
++                        r = safe_atou(arg, &idx);
++                        if (r < 0)
++                                return -EINVAL;
++
++                        state = STATE_EXEC_COMMAND_PATH;
++                        break;
++                case STATE_EXEC_COMMAND_PATH:
++                        path = arg;
++                        arg = NULL;
++                        state = STATE_EXEC_COMMAND_ARGS;
++
++                        if (!path_is_absolute(path))
++                                return -EINVAL;
++                        break;
++                case STATE_EXEC_COMMAND_ARGS:
++                        r = strv_extend(&argv, arg);
++                        if (r < 0)
++                                return -ENOMEM;
++                        break;
++                default:
++                        assert_not_reached("Unknown error at deserialization of exec command");
++                        break;
++                }
++        }
++
++        if (state != STATE_EXEC_COMMAND_ARGS)
++                return -EINVAL;
++
++        /* Let's check whether exec command on given offset matches data that we just deserialized */
++        for (command = s->exec_command[id], i = 0; command; command = command->command_next, i++) {
++                if (i != idx)
++                        continue;
++
++                found = strv_equal(argv, command->argv) && streq(command->path, path);
++                break;
++        }
++
++        if (!found) {
++                /* Command at the index we serialized is different, let's look for command that exactly
++                 * matches but is on different index. If there is no such command we will not resume execution. */
++                for (command = s->exec_command[id]; command; command = command->command_next)
++                        if (strv_equal(command->argv, argv) && streq(command->path, path))
++                                break;
++        }
++
++        if (command && control)
++                s->control_command = command;
++        else if (command)
++                s->main_command = command;
++        else
++                log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed.");
++
++        return 0;
++}
++
+ static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+         Service *s = SERVICE(u);
+         int r;
+@@ -2309,16 +2481,6 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
+                         s->status_text = t;
+                 }
+-        } else if (streq(key, "control-command")) {
+-                ServiceExecCommand id;
+-
+-                id = service_exec_command_from_string(value);
+-                if (id < 0)
+-                        log_unit_debug(u, "Failed to parse exec-command value: %s", value);
+-                else {
+-                        s->control_command_id = id;
+-                        s->control_command = s->exec_command[id];
+-                }
+         } else if (streq(key, "accept-socket")) {
+                 Unit *socket;
+@@ -2437,6 +2599,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
+                         s->watchdog_override_enable = true;
+                         s->watchdog_override_usec = watchdog_override_usec;
+                 }
++        } else if (STR_IN_SET(key, "main-command", "control-command")) {
++                r = service_deserialize_exec_command(u, key, value);
++                if (r < 0)
++                        log_unit_debug_errno(u, r, "Failed to parse serialized command \"%s\": %m", value);
+         } else
+                 log_unit_debug(u, "Unknown serialization key: %s", key);