Skip to content

Commit 8ea9aa9

Browse files
authored
Merge pull request systemd#5354 from msekletar/issue-518
service: serialize information about currently executing command
2 parents fd63f36 + 123d672 commit 8ea9aa9

File tree

3 files changed

+375
-16
lines changed

3 files changed

+375
-16
lines changed

Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5946,7 +5946,8 @@ EXTRA_DIST += \
59465946
src/network/systemd-networkd.pkla \
59475947
units/systemd-networkd.service.m4.in \
59485948
units/systemd-networkd-wait-online.service.in \
5949-
test/networkd-test.py
5949+
test/networkd-test.py \
5950+
test/test-exec-deserialization.py
59505951

59515952
# ------------------------------------------------------------------------------
59525953
if ENABLE_LOGIND

src/core/service.c

Lines changed: 181 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "service.h"
4646
#include "signal-util.h"
4747
#include "special.h"
48+
#include "stdio-util.h"
4849
#include "string-table.h"
4950
#include "string-util.h"
5051
#include "strv.h"
@@ -2140,6 +2141,80 @@ _pure_ static bool service_can_reload(Unit *u) {
21402141
return !!s->exec_command[SERVICE_EXEC_RELOAD];
21412142
}
21422143

2144+
static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) {
2145+
Service *s = SERVICE(u);
2146+
unsigned idx = 0;
2147+
ExecCommand *first, *c;
2148+
2149+
assert(s);
2150+
2151+
first = s->exec_command[id];
2152+
2153+
/* Figure out where we are in the list by walking back to the beginning */
2154+
for (c = current; c != first; c = c->command_prev)
2155+
idx++;
2156+
2157+
return idx;
2158+
}
2159+
2160+
static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
2161+
Service *s = SERVICE(u);
2162+
ServiceExecCommand id;
2163+
unsigned idx;
2164+
const char *type;
2165+
char **arg;
2166+
_cleanup_strv_free_ char **escaped_args = NULL;
2167+
_cleanup_free_ char *args = NULL, *p = NULL;
2168+
size_t allocated = 0, length = 0;
2169+
2170+
assert(s);
2171+
assert(f);
2172+
2173+
if (!command)
2174+
return 0;
2175+
2176+
if (command == s->control_command) {
2177+
type = "control";
2178+
id = s->control_command_id;
2179+
} else {
2180+
type = "main";
2181+
id = SERVICE_EXEC_START;
2182+
}
2183+
2184+
idx = service_exec_command_index(u, id, command);
2185+
2186+
STRV_FOREACH(arg, command->argv) {
2187+
size_t n;
2188+
_cleanup_free_ char *e = NULL;
2189+
2190+
e = xescape(*arg, WHITESPACE);
2191+
if (!e)
2192+
return -ENOMEM;
2193+
2194+
n = strlen(e);
2195+
if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
2196+
return -ENOMEM;
2197+
2198+
if (length > 0)
2199+
args[length++] = ' ';
2200+
2201+
memcpy(args + length, e, n);
2202+
length += n;
2203+
}
2204+
2205+
if (!GREEDY_REALLOC(args, allocated, length + 1))
2206+
return -ENOMEM;
2207+
args[length++] = 0;
2208+
2209+
p = xescape(command->path, WHITESPACE);
2210+
if (!p)
2211+
return -ENOMEM;
2212+
2213+
fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
2214+
2215+
return 0;
2216+
}
2217+
21432218
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
21442219
Service *s = SERVICE(u);
21452220
ServiceFDStore *fs;
@@ -2167,11 +2242,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
21672242
if (r < 0)
21682243
return r;
21692244

2170-
/* FIXME: There's a minor uncleanliness here: if there are
2171-
* multiple commands attached here, we will start from the
2172-
* first one again */
2173-
if (s->control_command_id >= 0)
2174-
unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
2245+
service_serialize_exec_command(u, f, s->control_command);
2246+
service_serialize_exec_command(u, f, s->main_command);
21752247

21762248
r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
21772249
if (r < 0)
@@ -2227,6 +2299,106 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
22272299
return 0;
22282300
}
22292301

2302+
static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) {
2303+
Service *s = SERVICE(u);
2304+
int r;
2305+
unsigned idx = 0, i;
2306+
bool control, found = false;
2307+
ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID;
2308+
ExecCommand *command = NULL;
2309+
_cleanup_free_ char *args = NULL, *path = NULL;
2310+
_cleanup_strv_free_ char **argv = NULL;
2311+
2312+
enum ExecCommandState {
2313+
STATE_EXEC_COMMAND_TYPE,
2314+
STATE_EXEC_COMMAND_INDEX,
2315+
STATE_EXEC_COMMAND_PATH,
2316+
STATE_EXEC_COMMAND_ARGS,
2317+
_STATE_EXEC_COMMAND_MAX,
2318+
_STATE_EXEC_COMMAND_INVALID = -1,
2319+
} state;
2320+
2321+
assert(s);
2322+
assert(key);
2323+
assert(value);
2324+
2325+
control = streq(key, "control-command");
2326+
2327+
state = STATE_EXEC_COMMAND_TYPE;
2328+
2329+
for (;;) {
2330+
_cleanup_free_ char *arg = NULL;
2331+
2332+
r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
2333+
if (r == 0)
2334+
break;
2335+
else if (r < 0)
2336+
return r;
2337+
2338+
switch (state) {
2339+
case STATE_EXEC_COMMAND_TYPE:
2340+
id = service_exec_command_from_string(arg);
2341+
if (id < 0)
2342+
return -EINVAL;
2343+
2344+
state = STATE_EXEC_COMMAND_INDEX;
2345+
break;
2346+
case STATE_EXEC_COMMAND_INDEX:
2347+
r = safe_atou(arg, &idx);
2348+
if (r < 0)
2349+
return -EINVAL;
2350+
2351+
state = STATE_EXEC_COMMAND_PATH;
2352+
break;
2353+
case STATE_EXEC_COMMAND_PATH:
2354+
path = arg;
2355+
arg = NULL;
2356+
state = STATE_EXEC_COMMAND_ARGS;
2357+
2358+
if (!path_is_absolute(path))
2359+
return -EINVAL;
2360+
break;
2361+
case STATE_EXEC_COMMAND_ARGS:
2362+
r = strv_extend(&argv, arg);
2363+
if (r < 0)
2364+
return -ENOMEM;
2365+
break;
2366+
default:
2367+
assert_not_reached("Unknown error at deserialization of exec command");
2368+
break;
2369+
}
2370+
}
2371+
2372+
if (state != STATE_EXEC_COMMAND_ARGS)
2373+
return -EINVAL;
2374+
2375+
/* Let's check whether exec command on given offset matches data that we just deserialized */
2376+
for (command = s->exec_command[id], i = 0; command; command = command->command_next, i++) {
2377+
if (i != idx)
2378+
continue;
2379+
2380+
found = strv_equal(argv, command->argv) && streq(command->path, path);
2381+
break;
2382+
}
2383+
2384+
if (!found) {
2385+
/* Command at the index we serialized is different, let's look for command that exactly
2386+
* matches but is on different index. If there is no such command we will not resume execution. */
2387+
for (command = s->exec_command[id]; command; command = command->command_next)
2388+
if (strv_equal(command->argv, argv) && streq(command->path, path))
2389+
break;
2390+
}
2391+
2392+
if (command && control)
2393+
s->control_command = command;
2394+
else if (command)
2395+
s->main_command = command;
2396+
else
2397+
log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed.");
2398+
2399+
return 0;
2400+
}
2401+
22302402
static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
22312403
Service *s = SERVICE(u);
22322404
int r;
@@ -2309,16 +2481,6 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
23092481
s->status_text = t;
23102482
}
23112483

2312-
} else if (streq(key, "control-command")) {
2313-
ServiceExecCommand id;
2314-
2315-
id = service_exec_command_from_string(value);
2316-
if (id < 0)
2317-
log_unit_debug(u, "Failed to parse exec-command value: %s", value);
2318-
else {
2319-
s->control_command_id = id;
2320-
s->control_command = s->exec_command[id];
2321-
}
23222484
} else if (streq(key, "accept-socket")) {
23232485
Unit *socket;
23242486

@@ -2437,6 +2599,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
24372599
s->watchdog_override_enable = true;
24382600
s->watchdog_override_usec = watchdog_override_usec;
24392601
}
2602+
} else if (STR_IN_SET(key, "main-command", "control-command")) {
2603+
r = service_deserialize_exec_command(u, key, value);
2604+
if (r < 0)
2605+
log_unit_debug_errno(u, r, "Failed to parse serialized command \"%s\": %m", value);
24402606
} else
24412607
log_unit_debug(u, "Unknown serialization key: %s", key);
24422608

0 commit comments

Comments
 (0)