From 7eb2a8a1259043e107ebec94e30ed160a93f40a7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Nov 2017 15:08:49 +0100 Subject: [PATCH 1/4] unit: rework a bit how we keep the service fdstore from being destroyed during service restart When preparing for a restart we quickly go through the DEAD/INACTIVE service state before entering AUTO_RESTART. When doing this, we need to make sure we don't destroy the FD store. Previously this was done by checking the failure state of the unit, and keeping the FD store around when the unit failed, under the assumption that the restart logic will then get into action. This is not entirely correct howver, as there might be failure states that will no result in restarts. With this commit we slightly alter the logic: a ref counter for the fd store is added, that is increased right before we handle the restart logic, and decreased again right-after. This should ensure that the fdstore lives exactly as long as it needs. Follow-up for f0bfbfac43b7faa68ef1bb2ad659c191b9ec85d2. --- src/core/service.c | 23 ++++++++++++++++++----- src/core/service.h | 1 + src/core/unit.c | 12 ++++-------- src/core/unit.h | 5 ++--- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/core/service.c b/src/core/service.c index ac3f14665d..4b912fc3b9 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -294,6 +294,9 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { static void service_release_fd_store(Service *s) { assert(s); + if (s->n_keep_fd_store > 0) + return; + log_unit_debug(UNIT(s), "Releasing all stored fds"); while (s->fd_store) service_fd_store_unlink(s->fd_store); @@ -301,7 +304,7 @@ static void service_release_fd_store(Service *s) { assert(s->n_fd_store == 0); } -static void service_release_resources(Unit *u, bool inactive) { +static void service_release_resources(Unit *u) { Service *s = SERVICE(u); assert(s); @@ -315,8 +318,7 @@ static void service_release_resources(Unit *u, bool inactive) { s->stdout_fd = safe_close(s->stdout_fd); s->stderr_fd = safe_close(s->stderr_fd); - if (inactive) - service_release_fd_store(s); + service_release_fd_store(s); } static void service_done(Unit *u) { @@ -360,7 +362,7 @@ static void service_done(Unit *u) { s->timer_event_source = sd_event_source_unref(s->timer_event_source); - service_release_resources(u, true); + service_release_resources(u); } static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { @@ -1524,6 +1526,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) if (s->result != SERVICE_SUCCESS) log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result)); + /* Make sure service_release_resources() doesn't destroy our FD store, while we are changing through + * SERVICE_FAILED/SERVICE_DEAD before entering into SERVICE_AUTO_RESTART. */ + s->n_keep_fd_store ++; + service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); if (s->result != SERVICE_SUCCESS) @@ -1532,8 +1538,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) if (allow_restart && service_shall_restart(s)) { r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec)); - if (r < 0) + if (r < 0) { + s->n_keep_fd_store--; goto fail; + } service_set_state(s, SERVICE_AUTO_RESTART); } else @@ -1541,6 +1549,11 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) * user can still introspect the counter. Do so on the next start. */ s->flush_n_restarts = true; + /* The new state is in effect, let's decrease the fd store ref counter again. Let's also readd us to the GC + * queue, so that the fd store is possibly gc'ed again */ + s->n_keep_fd_store--; + unit_add_to_gc_queue(UNIT(s)); + /* The next restart might not be a manual stop, hence reset the flag indicating manual stops */ s->forbid_restart = false; diff --git a/src/core/service.h b/src/core/service.h index 16b700637c..236d37fcbc 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -186,6 +186,7 @@ struct Service { ServiceFDStore *fd_store; unsigned n_fd_store; unsigned n_fd_store_max; + unsigned n_keep_fd_store; char *usb_function_descriptors; char *usb_function_strings; diff --git a/src/core/unit.c b/src/core/unit.c index 785bd5dc44..5c8dc4347a 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -331,7 +331,6 @@ int unit_set_description(Unit *u, const char *description) { bool unit_check_gc(Unit *u) { UnitActiveState state; - bool inactive; assert(u); if (u->job) @@ -341,17 +340,14 @@ bool unit_check_gc(Unit *u) { return true; state = unit_active_state(u); - inactive = state == UNIT_INACTIVE; - /* If the unit is inactive and failed and no job is queued for - * it, then release its runtime resources */ + /* If the unit is inactive and failed and no job is queued for it, then release its runtime resources */ if (UNIT_IS_INACTIVE_OR_FAILED(state) && UNIT_VTABLE(u)->release_resources) - UNIT_VTABLE(u)->release_resources(u, inactive); + UNIT_VTABLE(u)->release_resources(u); - /* But we keep the unit object around for longer when it is - * referenced or configured to not be gc'ed */ - if (!inactive) + /* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */ + if (state != UNIT_INACTIVE) return true; if (u->perpetual) diff --git a/src/core/unit.h b/src/core/unit.h index adab6eb80f..b0df341a03 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -453,9 +453,8 @@ struct UnitVTable { * way */ bool (*check_gc)(Unit *u); - /* When the unit is not running and no job for it queued we - * shall release its runtime resources */ - void (*release_resources)(Unit *u, bool inactive); + /* When the unit is not running and no job for it queued we shall release its runtime resources */ + void (*release_resources)(Unit *u); /* Invoked on every child that died */ void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); From 5afe510c89f26b0e721b276a0e78af914b47f0b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Nov 2017 17:14:07 +0100 Subject: [PATCH 2/4] core: add a new unit file setting CollectMode= for tweaking the GC logic Right now, the option only takes one of two possible values "inactive" or "inactive-or-failed", the former being the default, and exposing same behaviour as the status quo ante. If set to "inactive-or-failed" units may be collected by the GC logic when in the "failed" state too. This logic should be a nicer alternative to using the "-" modifier for ExecStart= and friends, as the exit data is collected and logged about and only removed when the GC comes along. This should be useful in particular for per-connection socket-activated services, as well as "systemd-run" command lines that shall leave no artifacts in the system. I was thinking about whether to expose this as a boolean, but opted for an enum instead, as I have the suspicion other tweaks like this might be a added later on, in which case we extend this setting instead of having to add yet another one. Also, let's add some documentation for the GC logic. --- man/systemd.unit.xml | 57 ++++++++++++++++++++++++++- src/core/dbus-unit.c | 21 ++++++++++ src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/load-fragment.c | 2 + src/core/load-fragment.h | 1 + src/core/unit.c | 39 +++++++++++++++--- src/core/unit.h | 13 ++++++ src/shared/bus-unit-util.c | 2 +- 8 files changed, 129 insertions(+), 7 deletions(-) diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index d6feaa1817..72c66028f8 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -213,7 +213,6 @@ socket-based activation which make dependencies implicit, resulting in a both simpler and more flexible system. - Optionally, units may be instantiated from a template file at runtime. This allows creation of multiple units from a single configuration file. If @@ -417,6 +416,45 @@ + + Unit Garbage Collection + + The system and service manager loads a unit's configuration automatically when a unit is referenced for the + first time. It will automatically unload the unit configuration and state again when the unit is not needed anymore + ("garbage collection"). A unit may be referenced through a number of different mechanisms: + + + Another loaded unit references it with a dependency such as After=, + Wants=, … + + The unit is currently starting, running, reloading or stopping. + + The unit is currently in the failed state. (But see below.) + + A job for the unit is pending. + + The unit is pinned by an active IPC client program. + + The unit is a special "perpetual" unit that is always active and loaded. Examples for perpetual + units are the root mount unit -.mount or the scope unit init.scope that + the service manager itself lives in. + + The unit has running processes associated with it. + + + The garbage collection logic may be altered with the CollectMode= option, which allows + configuration whether automatic unloading of units that are in failed state is permissible, + see below. + + Note that when a unit's configuration and state is unloaded, all execution results, such as exit codes, exit + signals, resource consumption and other statistics are lost, except for what is stored in the log subsystem. + + Use systemctl daemon-reload or an equivalent command to reload unit configuration while + the unit is already loaded. In this case all configuration settings are flushed out and replaced with the new + configuration (which however might not be in effect immediately), however all runtime state is + saved/restored. + + [Unit] Section Options @@ -747,6 +785,23 @@ ones. + + CollectMode= + + Tweaks the "garbage collection" algorithm for this unit. Takes one of + or . If set to the unit will be unloaded if it is + in the inactive state and is not referenced by clients, jobs or other units — however it + is not unloaded if it is in the failed state. In mode, failed + units are not unloaded until the user invoked systemctl reset-failed on them to reset the + failed state, or an equivalent command. This behaviour is altered if this option is set to + : in this case the unit is unloaded even if the unit is in a + failed state, and thus an explicitly resetting of the failed state is + not necessary. Note that if this mode is used unit results (such as exit codes, exit signals, consumed + resources, …) are flushed out immediately after the unit completed, except for what is stored in the logging + subsystem. Defaults to . + + + JobTimeoutSec= JobRunningTimeoutSec= diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 32fcb5ef1c..561cf453f6 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -37,6 +37,7 @@ #include "strv.h" #include "user-util.h" +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); @@ -798,6 +799,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0), + SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), 0), SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1354,6 +1356,25 @@ static int bus_unit_set_transient_property( return 1; + } else if (streq(name, "CollectMode")) { + const char *s; + CollectMode m; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + m = collect_mode_from_string(s); + if (m < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown garbage collection mode: %s", s); + + if (mode != UNIT_CHECK) { + u->collect_mode = m; + unit_write_drop_in_format(u, mode, name, "[Unit]\nCollectMode=%s", collect_mode_to_string(m)); + } + + return 1; + } else if (streq(name, "Slice")) { Unit *slice; const char *s; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5b73f9aa9b..ffc3e20359 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -264,6 +264,7 @@ Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_A Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts) Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts) Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts) +Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode) m4_dnl Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 34fc04b65a..c1cf8379e8 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -103,6 +103,8 @@ int config_parse_warn_compat( return 0; } +DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode"); + int config_parse_unit_deps( const char *unit, const char *filename, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index b0a3ce2c67..fbf2de23eb 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -122,6 +122,7 @@ int config_parse_exec_keyring_mode(const char *unit, const char *filename, unsig int config_parse_job_timeout_sec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_running_timeout_sec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_log_extra_fields(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_collect_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/core/unit.c b/src/core/unit.c index 5c8dc4347a..25cdc04506 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -56,6 +56,7 @@ #include "special.h" #include "stat-util.h" #include "stdio-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "umask-util.h" @@ -75,7 +76,7 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_TIMER] = &timer_vtable, [UNIT_PATH] = &path_vtable, [UNIT_SLICE] = &slice_vtable, - [UNIT_SCOPE] = &scope_vtable + [UNIT_SCOPE] = &scope_vtable, }; static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency); @@ -331,8 +332,12 @@ int unit_set_description(Unit *u, const char *description) { bool unit_check_gc(Unit *u) { UnitActiveState state; + assert(u); + /* Checks whether the unit is ready to be unloaded for garbage collection. Returns true, when the unit shall + * stay around, false if there's no reason to keep it loaded. */ + if (u->job) return true; @@ -346,10 +351,6 @@ bool unit_check_gc(Unit *u) { UNIT_VTABLE(u)->release_resources) UNIT_VTABLE(u)->release_resources(u); - /* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */ - if (state != UNIT_INACTIVE) - return true; - if (u->perpetual) return true; @@ -359,6 +360,25 @@ bool unit_check_gc(Unit *u) { if (sd_bus_track_count(u->bus_track) > 0) return true; + /* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */ + switch (u->collect_mode) { + + case COLLECT_INACTIVE: + if (state != UNIT_INACTIVE) + return true; + + break; + + case COLLECT_INACTIVE_OR_FAILED: + if (!IN_SET(state, UNIT_INACTIVE, UNIT_FAILED)) + return true; + + break; + + default: + assert_not_reached("Unknown garbage collection mode"); + } + if (UNIT_VTABLE(u)->check_gc) if (UNIT_VTABLE(u)->check_gc(u)) return true; @@ -1083,6 +1103,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tNeed Daemon Reload: %s\n" "%s\tTransient: %s\n" "%s\tPerpetual: %s\n" + "%s\tGarbage Collection Mode: %s\n" "%s\tSlice: %s\n" "%s\tCGroup: %s\n" "%s\tCGroup realized: %s\n", @@ -1100,6 +1121,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(unit_need_daemon_reload(u)), prefix, yes_no(u->transient), prefix, yes_no(u->perpetual), + prefix, collect_mode_to_string(u->collect_mode), prefix, strna(unit_slice_name(u)), prefix, strna(u->cgroup_path), prefix, yes_no(u->cgroup_realized)); @@ -5121,3 +5143,10 @@ void unit_unlink_state_files(Unit *u) { u->exported_log_extra_fields = false; } } + +static const char* const collect_mode_table[_COLLECT_MODE_MAX] = { + [COLLECT_INACTIVE] = "inactive", + [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed", +}; + +DEFINE_STRING_TABLE_LOOKUP(collect_mode, CollectMode); diff --git a/src/core/unit.h b/src/core/unit.h index b0df341a03..03dd88dcca 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -45,6 +45,13 @@ typedef enum KillOperation { _KILL_OPERATION_INVALID = -1 } KillOperation; +typedef enum CollectMode { + COLLECT_INACTIVE, + COLLECT_INACTIVE_OR_FAILED, + _COLLECT_MODE_MAX, + _COLLECT_MODE_INVALID = -1, +} CollectMode; + static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) { return IN_SET(t, UNIT_ACTIVE, UNIT_RELOADING); } @@ -282,6 +289,9 @@ struct Unit { /* How to start OnFailure units */ JobMode on_failure_job_mode; + /* Tweaking the GC logic */ + CollectMode collect_mode; + /* The current invocation ID */ sd_id128_t invocation_id; char invocation_id_string[SD_ID128_STRING_MAX]; /* useful when logging */ @@ -773,3 +783,6 @@ void unit_unlink_state_files(Unit *u); #define LOG_UNIT_MESSAGE(unit, fmt, ...) "MESSAGE=%s: " fmt, (unit)->id, ##__VA_ARGS__ #define LOG_UNIT_ID(unit) (unit)->manager->unit_log_format_string, (unit)->id #define LOG_UNIT_INVOCATION_ID(unit) (unit)->manager->invocation_log_format_string, (unit)->invocation_id_string + +const char* collect_mode_to_string(CollectMode m) _const_; +CollectMode collect_mode_from_string(const char *s) _pure_; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index e24c0d4e1c..2b2480c2e1 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -371,7 +371,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "RootDirectory", "SyslogIdentifier", "ProtectSystem", "ProtectHome", "SELinuxContext", "Restart", "RootImage", "NotifyAccess", "RuntimeDirectoryPreserve", "Personality", - "KeyringMode")) + "KeyringMode", "CollectMode")) r = sd_bus_message_append(m, "v", "s", eq); else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { From fe9d0be90ba142bf06d43a831d8be53283415caa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Nov 2017 17:17:53 +0100 Subject: [PATCH 3/4] run: add "-G" as shortcut for "--property=CollectMode=inactive-or-failed" This option is likely to be very useful for systemd-run invocations, hence let's add a shortcut for it. With this new concepts it's now very easy to put together systemd-run invocations that leave zero artifacts in the system, including when they fail. --- man/systemd-run.xml | 15 +++++++++++++++ src/run/run.c | 17 +++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 2c74c1f39c..010c9c7197 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -324,6 +324,21 @@ , or the various timer options. + + + + + Unload the transient unit after it completed, even if it failed. Normally, without this option, + all units that ran and failed are kept in memory until the user explicitly resets their failure state with + systemctl reset-failed or an equivalent command. On the other hand, units that ran + successfully are unloaded immediately. If this option is turned on the "garbage collection" of units is more + aggressive, and unloads units regardless if they exited successfully or failed. This option is a shortcut for + --property=CollectMode=inactive-or-failed, see the explanation for + CollectMode= in + systemd.unit5 for further + information. + + diff --git a/src/run/run.c b/src/run/run.c index 111c7f2ef3..578b59e163 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -75,6 +75,7 @@ static usec_t arg_on_unit_inactive = 0; static const char *arg_on_calendar = NULL; static char **arg_timer_property = NULL; static bool arg_quiet = false; +static bool arg_aggressive_gc = false; static void help(void) { printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" @@ -102,7 +103,8 @@ static void help(void) { " -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n" " STDERR\n" " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n" - " -q --quiet Suppress information messages during runtime\n\n" + " -q --quiet Suppress information messages during runtime\n" + " -G --collect Unload unit after it ran, even when failed\n\n" "Timer options:\n" " --on-active=SECONDS Run after SECONDS delay\n" " --on-boot=SECONDS Run SECONDS after machine was booted up\n" @@ -178,6 +180,7 @@ static int parse_argv(int argc, char *argv[]) { { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "collect", no_argument, NULL, 'G' }, {}, }; @@ -186,7 +189,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPq", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqG", options, NULL)) >= 0) switch (c) { @@ -372,6 +375,10 @@ static int parse_argv(int argc, char *argv[]) { arg_wait = true; break; + case 'G': + arg_aggressive_gc = true; + break; + case '?': return -EINVAL; @@ -461,6 +468,12 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) { if (r < 0) return r; + if (arg_aggressive_gc) { + r = sd_bus_message_append(m, "(sv)", "CollectMode", "s", "inactive-or-failed"); + if (r < 0) + return r; + } + r = bus_append_unit_property_assignment_many(m, properties); if (r < 0) return r; From 2368e9748a32ae56011ddf97eded357757d2b399 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Nov 2017 17:21:27 +0100 Subject: [PATCH 4/4] update TODO --- TODO | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index c885639d4d..553575d087 100644 --- a/TODO +++ b/TODO @@ -38,11 +38,6 @@ Features: * maybe use SOURCE_DATE_EPOCH (i.e. the env var the reproducible builds folks introduced) as the RTC epoch, instead of the mtime of NEWS. -* Introduce GCMode= as unit file property or so, for tweaking the GC - logic. Specifically, there should be a way to tell systemd to collect - individual units even on failure. Then, make systemd-run --wait use this, so - that failed transient units in that case don't stick around. - * add a way to lock down cgroup migration: a boolean, which when set for a unit makes sure the processes in it can never migrate out of it @@ -87,6 +82,10 @@ Features: friendly log messages for the returned errors, so that we don't have to duplicate that in nspawn, systemd-dissect and PID 1. +* add "systemctl wait" or so, which does what "systemd-run --wait" does, but + for all units. It should be both a way to pin units into memory as well as a + wait to retrieve their exit data. + * maybe set a new set of env vars for services, based on RuntimeDirectory=, StateDirectory=, LogsDirectory=, CacheDirectory= and ConfigurationDirectory= automatically. For example, there could be $RUNTIME_DIRECTORY,