1 commit e266c068b5597e18b2299f9c9d3ee6cf04198c41
2 Author: Michal Sekletar <msekleta@redhat.com>
3 Date: Mon Jan 23 17:12:35 2017 +0100
5 service: serialize information about currently executing command
7 Stored information will help us to resume execution after the
10 This commit implements following scheme,
13 - we count rank of the currently executing command
14 - we store command type, its rank and command line arguments
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
25 To better illustrate how does above scheme works, please consider
26 following cases (<<< denotes position where we resume execution after reload)
30 ExecStart=/bin/true <<<
34 ; Second command is not going to be executed
37 ExecStart=/bin/true <<<
39 ; Commands added before
40 ; Same commands are problematic and execution could be restarted at wrong place
44 ExecStart=/bin/true <<<
47 ; Commands added after
48 ; Same commands are not an issue in this case
50 ExecStart=/bin/true <<<
55 ; New commands interleaved with old commands
56 ; Some new commands will be executed while others won't
58 ExecStart=/bin/true <<<
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.
70 Signed-off-by: Scott Murray <scott.murray@konsulko.com>
72 Upstream-Status: backport
74 diff --git a/src/core/service.c b/src/core/service.c
75 index 74054887b..5e681fb71 100644
76 --- a/src/core/service.c
77 +++ b/src/core/service.c
80 #include "signal-util.h"
82 +#include "stdio-util.h"
83 #include "string-table.h"
84 #include "string-util.h"
86 @@ -2140,6 +2141,80 @@ _pure_ static bool service_can_reload(Unit *u) {
87 return !!s->exec_command[SERVICE_EXEC_RELOAD];
90 +static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) {
91 + Service *s = SERVICE(u);
93 + ExecCommand *first, *c;
97 + first = s->exec_command[id];
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)
106 +static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
107 + Service *s = SERVICE(u);
108 + ServiceExecCommand id;
112 + _cleanup_strv_free_ char **escaped_args = NULL;
113 + _cleanup_free_ char *args = NULL, *p = NULL;
114 + size_t allocated = 0, length = 0;
122 + if (command == s->control_command) {
124 + id = s->control_command_id;
127 + id = SERVICE_EXEC_START;
130 + idx = service_exec_command_index(u, id, command);
132 + STRV_FOREACH(arg, command->argv) {
134 + _cleanup_free_ char *e = NULL;
136 + e = xescape(*arg, WHITESPACE);
141 + if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
145 + args[length++] = ' ';
147 + memcpy(args + length, e, n);
151 + if (!GREEDY_REALLOC(args, allocated, length + 1))
153 + args[length++] = 0;
155 + p = xescape(command->path, WHITESPACE);
159 + fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
164 static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
165 Service *s = SERVICE(u);
167 @@ -2167,11 +2242,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
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);
179 r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
181 @@ -2227,6 +2299,106 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
185 +static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) {
186 + Service *s = SERVICE(u);
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;
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,
208 + control = streq(key, "control-command");
210 + state = STATE_EXEC_COMMAND_TYPE;
213 + _cleanup_free_ char *arg = NULL;
215 + r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
222 + case STATE_EXEC_COMMAND_TYPE:
223 + id = service_exec_command_from_string(arg);
227 + state = STATE_EXEC_COMMAND_INDEX;
229 + case STATE_EXEC_COMMAND_INDEX:
230 + r = safe_atou(arg, &idx);
234 + state = STATE_EXEC_COMMAND_PATH;
236 + case STATE_EXEC_COMMAND_PATH:
239 + state = STATE_EXEC_COMMAND_ARGS;
241 + if (!path_is_absolute(path))
244 + case STATE_EXEC_COMMAND_ARGS:
245 + r = strv_extend(&argv, arg);
250 + assert_not_reached("Unknown error at deserialization of exec command");
255 + if (state != STATE_EXEC_COMMAND_ARGS)
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++) {
263 + found = strv_equal(argv, command->argv) && streq(command->path, path);
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))
275 + if (command && control)
276 + s->control_command = command;
278 + s->main_command = command;
280 + log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed.");
285 static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
286 Service *s = SERVICE(u);
288 @@ -2309,16 +2481,6 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
292 - } else if (streq(key, "control-command")) {
293 - ServiceExecCommand id;
295 - id = service_exec_command_from_string(value);
297 - log_unit_debug(u, "Failed to parse exec-command value: %s", value);
299 - s->control_command_id = id;
300 - s->control_command = s->exec_command[id];
302 } else if (streq(key, "accept-socket")) {
305 @@ -2437,6 +2599,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
306 s->watchdog_override_enable = true;
307 s->watchdog_override_usec = watchdog_override_usec;
309 + } else if (STR_IN_SET(key, "main-command", "control-command")) {
310 + r = service_deserialize_exec_command(u, key, value);
312 + log_unit_debug_errno(u, r, "Failed to parse serialized command \"%s\": %m", value);
314 log_unit_debug(u, "Unknown serialization key: %s", key);