systemd: backport serialization change from v234
[AGL/meta-agl.git] / meta-agl / recipes-core / systemd / systemd / backport-v234-e266c06-v230.patch
1 commit e266c068b5597e18b2299f9c9d3ee6cf04198c41
2 Author: Michal Sekletar <msekleta@redhat.com>
3 Date:   Mon Jan 23 17:12:35 2017 +0100
4
5     service: serialize information about currently executing command
6     
7     Stored information will help us to resume execution after the
8     daemon-reload.
9     
10     This commit implements following scheme,
11     
12     * On serialization:
13       - we count rank of the currently executing command
14       - we store command type, its rank and command line arguments
15     
16     * On deserialization:
17       - configuration is parsed and loaded
18       - we deserialize stored data, command type, rank and arguments
19       - we look at the given rank in the list and if command there has same
20         arguments then we restore execution at that point
21       - otherwise we search respective command list and we look for command
22         that has the same arguments
23       - if both methods fail we do not do not resume execution at all
24     
25     To better illustrate how does above scheme works, please consider
26     following cases (<<< denotes position where we resume execution after reload)
27     
28     ; Original unit file
29     [Service]
30     ExecStart=/bin/true <<<
31     ExecStart=/bin/false
32     
33     ; Swapped commands
34     ; Second command is not going to be executed
35     [Service]
36     ExecStart=/bin/false
37     ExecStart=/bin/true <<<
38     
39     ; Commands added before
40     ; Same commands are problematic and execution could be restarted at wrong place
41     [Service]
42     ExecStart=/bin/foo
43     ExecStart=/bin/bar
44     ExecStart=/bin/true <<<
45     ExecStart=/bin/false
46     
47     ; Commands added after
48     ; Same commands are not an issue in this case
49     [Service]
50     ExecStart=/bin/true <<<
51     ExecStart=/bin/false
52     ExecStart=/bin/foo
53     ExecStart=/bin/bar
54     
55     ; New commands interleaved with old commands
56     ; Some new commands will be executed while others won't
57     ExecStart=/bin/foo
58     ExecStart=/bin/true <<<
59     ExecStart=/bin/bar
60     ExecStart=/bin/false
61     
62     As you can see, above scheme has some drawbacks. However, in most
63     cases (we assume that in most common case unit file command list is not
64     changed while some other command is running for the same unit) it
65     should cause that systemd does the right thing, which is restoring
66     execution exactly at the point we were before daemon-reload.
67     
68     Fixes #518
69
70 Signed-off-by: Scott Murray <scott.murray@konsulko.com>
71
72 Upstream-Status: backport
73
74 diff --git a/src/core/service.c b/src/core/service.c
75 index 7ebabca5d..faa65efca 100644
76 --- a/src/core/service.c
77 +++ b/src/core/service.c
78 @@ -45,6 +45,7 @@
79  #include "service.h"
80  #include "signal-util.h"
81  #include "special.h"
82 +#include "stdio-util.h"
83  #include "string-table.h"
84  #include "string-util.h"
85  #include "strv.h"
86 @@ -2059,6 +2060,80 @@ _pure_ static bool service_can_reload(Unit *u) {
87          return !!s->exec_command[SERVICE_EXEC_RELOAD];
88  }
89  
90 +static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) {
91 +        Service *s = SERVICE(u);
92 +        unsigned idx = 0;
93 +        ExecCommand *first, *c;
94 +
95 +        assert(s);
96 +
97 +        first = s->exec_command[id];
98 +
99 +        /* Figure out where we are in the list by walking back to the beginning */
100 +        for (c = current; c != first; c = c->command_prev)
101 +                idx++;
102 +
103 +        return idx;
104 +}
105 +
106 +static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
107 +        Service *s = SERVICE(u);
108 +        ServiceExecCommand id;
109 +        unsigned idx;
110 +        const char *type;
111 +        char **arg;
112 +        _cleanup_strv_free_ char **escaped_args = NULL;
113 +        _cleanup_free_ char *args = NULL, *p = NULL;
114 +        size_t allocated = 0, length = 0;
115 +
116 +        assert(s);
117 +        assert(f);
118 +
119 +        if (!command)
120 +                return 0;
121 +
122 +        if (command == s->control_command) {
123 +                type = "control";
124 +                id = s->control_command_id;
125 +        } else {
126 +                type = "main";
127 +                id = SERVICE_EXEC_START;
128 +        }
129 +
130 +        idx = service_exec_command_index(u, id, command);
131 +
132 +        STRV_FOREACH(arg, command->argv) {
133 +                size_t n;
134 +                _cleanup_free_ char *e = NULL;
135 +
136 +                e = xescape(*arg, WHITESPACE);
137 +                if (!e)
138 +                        return -ENOMEM;
139 +
140 +                n = strlen(e);
141 +                if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
142 +                        return -ENOMEM;
143 +
144 +                if (length > 0)
145 +                        args[length++] = ' ';
146 +
147 +                memcpy(args + length, e, n);
148 +                length += n;
149 +        }
150 +
151 +        if (!GREEDY_REALLOC(args, allocated, length + 1))
152 +                return -ENOMEM;
153 +        args[length++] = 0;
154 +
155 +        p = xescape(command->path, WHITESPACE);
156 +        if (!p)
157 +                return -ENOMEM;
158 +
159 +        fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
160 +
161 +        return 0;
162 +}
163 +
164  static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
165          Service *s = SERVICE(u);
166          ServiceFDStore *fs;
167 @@ -2086,11 +2161,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
168          if (r < 0)
169                  return r;
170  
171 -        /* FIXME: There's a minor uncleanliness here: if there are
172 -         * multiple commands attached here, we will start from the
173 -         * first one again */
174 -        if (s->control_command_id >= 0)
175 -                unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
176 +        service_serialize_exec_command(u, f, s->control_command);
177 +        service_serialize_exec_command(u, f, s->main_command);
178  
179          r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
180          if (r < 0)
181 @@ -2137,6 +2209,106 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
182          return 0;
183  }
184  
185 +static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) {
186 +        Service *s = SERVICE(u);
187 +        int r;
188 +        unsigned idx = 0, i;
189 +        bool control, found = false;
190 +        ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID;
191 +        ExecCommand *command = NULL;
192 +        _cleanup_free_ char *args = NULL, *path = NULL;
193 +        _cleanup_strv_free_ char **argv = NULL;
194 +
195 +        enum ExecCommandState {
196 +                STATE_EXEC_COMMAND_TYPE,
197 +                STATE_EXEC_COMMAND_INDEX,
198 +                STATE_EXEC_COMMAND_PATH,
199 +                STATE_EXEC_COMMAND_ARGS,
200 +                _STATE_EXEC_COMMAND_MAX,
201 +                _STATE_EXEC_COMMAND_INVALID = -1,
202 +        } state;
203 +
204 +        assert(s);
205 +        assert(key);
206 +        assert(value);
207 +
208 +        control = streq(key, "control-command");
209 +
210 +        state = STATE_EXEC_COMMAND_TYPE;
211 +
212 +        for (;;) {
213 +                _cleanup_free_ char *arg = NULL;
214 +
215 +                r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
216 +                if (r == 0)
217 +                        break;
218 +                else if (r < 0)
219 +                        return r;
220 +
221 +                switch (state) {
222 +                case STATE_EXEC_COMMAND_TYPE:
223 +                        id = service_exec_command_from_string(arg);
224 +                        if (id < 0)
225 +                                return -EINVAL;
226 +
227 +                        state = STATE_EXEC_COMMAND_INDEX;
228 +                        break;
229 +                case STATE_EXEC_COMMAND_INDEX:
230 +                        r = safe_atou(arg, &idx);
231 +                        if (r < 0)
232 +                                return -EINVAL;
233 +
234 +                        state = STATE_EXEC_COMMAND_PATH;
235 +                        break;
236 +                case STATE_EXEC_COMMAND_PATH:
237 +                        path = arg;
238 +                        arg = NULL;
239 +                        state = STATE_EXEC_COMMAND_ARGS;
240 +
241 +                        if (!path_is_absolute(path))
242 +                                return -EINVAL;
243 +                        break;
244 +                case STATE_EXEC_COMMAND_ARGS:
245 +                        r = strv_extend(&argv, arg);
246 +                        if (r < 0)
247 +                                return -ENOMEM;
248 +                        break;
249 +                default:
250 +                        assert_not_reached("Unknown error at deserialization of exec command");
251 +                        break;
252 +                }
253 +        }
254 +
255 +        if (state != STATE_EXEC_COMMAND_ARGS)
256 +                return -EINVAL;
257 +
258 +        /* Let's check whether exec command on given offset matches data that we just deserialized */
259 +        for (command = s->exec_command[id], i = 0; command; command = command->command_next, i++) {
260 +                if (i != idx)
261 +                        continue;
262 +
263 +                found = strv_equal(argv, command->argv) && streq(command->path, path);
264 +                break;
265 +        }
266 +
267 +        if (!found) {
268 +                /* Command at the index we serialized is different, let's look for command that exactly
269 +                 * matches but is on different index. If there is no such command we will not resume execution. */
270 +                for (command = s->exec_command[id]; command; command = command->command_next)
271 +                        if (strv_equal(command->argv, argv) && streq(command->path, path))
272 +                                break;
273 +        }
274 +
275 +        if (command && control)
276 +                s->control_command = command;
277 +        else if (command)
278 +                s->main_command = command;
279 +        else
280 +                log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed.");
281 +
282 +        return 0;
283 +}
284 +
285  static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
286          Service *s = SERVICE(u);
287          int r;
288 @@ -2219,16 +2391,6 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
289                          s->status_text = t;
290                  }
291  
292 -        } else if (streq(key, "control-command")) {
293 -                ServiceExecCommand id;
294 -
295 -                id = service_exec_command_from_string(value);
296 -                if (id < 0)
297 -                        log_unit_debug(u, "Failed to parse exec-command value: %s", value);
298 -                else {
299 -                        s->control_command_id = id;
300 -                        s->control_command = s->exec_command[id];
301 -                }
302          } else if (streq(key, "socket-fd")) {
303                  int fd;
304  
305 @@ -2328,6 +2490,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
306                          s->stderr_fd = fdset_remove(fds, fd);
307                          s->exec_context.stdio_as_fds = true;
308                  }
309 +        } else if (STR_IN_SET(key, "main-command", "control-command")) {
310 +                r = service_deserialize_exec_command(u, key, value);
311 +                if (r < 0)
312 +                        log_unit_debug_errno(u, r, "Failed to parse serialized command \"%s\": %m", value);
313          } else
314                  log_unit_debug(u, "Unknown serialization key: %s", key);
315