diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 56906e2f3b2..717990edcc7 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -1977,6 +1977,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b DefaultDependencies = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b IgnoreOnSoftReboot = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s OnSuccessJobMode = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s OnFailureJobMode = '...'; @@ -2091,6 +2093,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -2303,6 +2307,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + diff --git a/man/systemd-soft-reboot.service.xml b/man/systemd-soft-reboot.service.xml index 5960cf90a6c..15cec5a6f37 100644 --- a/man/systemd-soft-reboot.service.xml +++ b/man/systemd-soft-reboot.service.xml @@ -124,12 +124,9 @@ otherwise the old root filesystem will be kept in memory as long as the unit is running. If units shall be left running until the very end of shutdown during a soft reboot operation, but - shall be terminated regularly during other forms of shutdown, it's recommended to set - DefaultDependencies=no and then place - Conflicts=/Before= onto reboot.target, - kexec.target, poweroff.target and - halt.target (but not onto - soft-reboot.target). + shall be terminated regularly during other forms of shutdown, IgnoreOnSoftReboot=yes + can be set. This will ensure that soft reboot operations do not affect it, but other types of reboot + or shutdown stop it as expected. diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 94a5a30f929..9646dea9134 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -979,6 +979,16 @@ ones. + + IgnoreOnSoftReboot= + + Takes a boolean argument. Defaults to . If , + the unit will be configured to survive a soft reboot operation (see: + systemd-soft-reboot.service8). + This will ensure the unit is stopped normally during reboot or shutdown operations, but it survives + a soft reboot by not being stopped on the way down or on the way back up. + + CollectMode= diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 629f08ebcc6..bacdeb5aa31 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -921,6 +921,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IgnoreOnSoftReboot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_soft_reboot), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* deprecated */ SD_BUS_PROPERTY("OnSuccessJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2175,6 +2176,9 @@ static int bus_unit_set_transient_property( if (streq(name, "DefaultDependencies")) return bus_set_transient_bool(u, name, &u->default_dependencies, message, flags, error); + if (streq(name, "IgnoreOnSoftReboot")) + return bus_set_transient_bool(u, name, &u->ignore_on_soft_reboot, message, flags, error); + if (streq(name, "OnSuccessJobMode")) return bus_set_transient_job_mode(u, name, &u->on_success_job_mode, message, flags, error); diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index b66adf28119..1ac57c63649 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -312,6 +312,7 @@ Unit.RefuseManualStart, config_parse_bool, Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) +Unit.IgnoreOnSoftReboot, config_parse_bool, 0, offsetof(Unit, ignore_on_soft_reboot) Unit.OnSuccessJobMode, config_parse_job_mode, 0, offsetof(Unit, on_success_job_mode) Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode) {# The following is a legacy alias name for compatibility #} diff --git a/src/core/path.c b/src/core/path.c index 5fb14d9a10b..c76110a02fb 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -342,12 +342,24 @@ static int path_add_default_dependencies(Path *p) { return r; if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) { - r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + r = unit_add_two_dependencies_by_name(UNIT(p), + UNIT_AFTER, + UNIT(p)->ignore_on_soft_reboot ? -EINVAL : UNIT_REQUIRES, + SPECIAL_SYSINIT_TARGET, + true, + UNIT_DEPENDENCY_DEFAULT); if (r < 0) return r; } - return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + if (!UNIT(p)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name( + UNIT(p), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, true, + UNIT_DEPENDENCY_DEFAULT); + + return unit_add_dependencies_on_real_shutdown_targets(UNIT(p)); } static int path_add_trigger_dependencies(Path *p) { diff --git a/src/core/scope.c b/src/core/scope.c index 72253421e23..a8461415bd8 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -131,23 +131,22 @@ static void scope_set_state(Scope *s, ScopeState state) { } static int scope_add_default_dependencies(Scope *s) { - int r; - assert(s); if (!UNIT(s)->default_dependencies) return 0; /* Make sure scopes are unloaded on shutdown */ - r = unit_add_two_dependencies_by_name( - UNIT(s), - UNIT_BEFORE, UNIT_CONFLICTS, - SPECIAL_SHUTDOWN_TARGET, true, - UNIT_DEPENDENCY_DEFAULT); - if (r < 0) - return r; + if (!UNIT(s)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name( + UNIT(s), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, true, + UNIT_DEPENDENCY_DEFAULT); - return 0; + /* Unless we are meant to survive soft reboot, in which case we need to conflict with + * non-soft-reboot targets. */ + return unit_add_dependencies_on_real_shutdown_targets(UNIT(s)); } static int scope_verify(Scope *s) { diff --git a/src/core/service.c b/src/core/service.c index 1b0af40b411..db289c7fbfa 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -722,11 +722,16 @@ static int service_add_default_dependencies(Service *s) { * majority of services. */ if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { - /* First, pull in the really early boot stuff, and - * require it, so that we fail if we can't acquire - * it. */ + /* First, pull in the really early boot stuff, and require it, so that we fail if we can't + * acquire it. But only add ordering if this is meant to survive a soft reboot, otherwise + * it will be pulled down. */ - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + r = unit_add_two_dependencies_by_name(UNIT(s), + UNIT_AFTER, + UNIT(s)->ignore_on_soft_reboot ? -EINVAL : UNIT_REQUIRES, + SPECIAL_SYSINIT_TARGET, + true, + UNIT_DEPENDENCY_DEFAULT); if (r < 0) return r; } else { @@ -747,7 +752,17 @@ static int service_add_default_dependencies(Service *s) { return r; /* Third, add us in for normal shutdown. */ - return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + if (!UNIT(s)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name(UNIT(s), + UNIT_BEFORE, + UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, + true, + UNIT_DEPENDENCY_DEFAULT); + + /* Unless we are meant to survive soft reboot, in which case we need to conflict with + * non-soft-reboot targets. */ + return unit_add_dependencies_on_real_shutdown_targets(UNIT(s)); } static void service_fix_stdio(Service *s) { diff --git a/src/core/slice.c b/src/core/slice.c index 490aabfd35e..069bfe1ea89 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -64,22 +64,22 @@ static int slice_add_parent_slice(Slice *s) { } static int slice_add_default_dependencies(Slice *s) { - int r; - assert(s); if (!UNIT(s)->default_dependencies) return 0; /* Make sure slices are unloaded on shutdown */ - r = unit_add_two_dependencies_by_name( - UNIT(s), - UNIT_BEFORE, UNIT_CONFLICTS, - SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); - if (r < 0) - return r; + if (!UNIT(s)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name( + UNIT(s), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, true, + UNIT_DEPENDENCY_DEFAULT); - return 0; + /* Unless we are meant to survive soft reboot, in which case we need to conflict with + * non-soft-reboot targets. */ + return unit_add_dependencies_on_real_shutdown_targets(UNIT(s)); } static int slice_verify(Slice *s) { diff --git a/src/core/socket.c b/src/core/socket.c index 03b8cbd164a..05a5e31be98 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -278,12 +278,24 @@ static int socket_add_default_dependencies(Socket *s) { return r; if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + r = unit_add_two_dependencies_by_name(UNIT(s), + UNIT_AFTER, + UNIT(s)->ignore_on_soft_reboot ? -EINVAL : UNIT_REQUIRES, + SPECIAL_SYSINIT_TARGET, + true, + UNIT_DEPENDENCY_DEFAULT); if (r < 0) return r; } - return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + if (!UNIT(s)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name( + UNIT(s), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, true, + UNIT_DEPENDENCY_DEFAULT); + + return unit_add_dependencies_on_real_shutdown_targets(UNIT(s)); } _pure_ static bool socket_has_exec(Socket *s) { diff --git a/src/core/target.c b/src/core/target.c index 3519b4b653a..3cb17b98434 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -66,7 +66,16 @@ static int target_add_default_dependencies(Target *t) { return 0; /* Make sure targets are unloaded on shutdown */ - return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + if (!UNIT(t)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name( + UNIT(t), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, true, + UNIT_DEPENDENCY_DEFAULT); + + /* Unless we are meant to survive soft reboot, in which case we need to conflict with + * non-soft-reboot targets. */ + return unit_add_dependencies_on_real_shutdown_targets(UNIT(t)); } static int target_load(Unit *u) { diff --git a/src/core/timer.c b/src/core/timer.c index f6e66055074..3aaebe72c68 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -94,7 +94,12 @@ static int timer_add_default_dependencies(Timer *t) { return r; if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { - r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + r = unit_add_two_dependencies_by_name(UNIT(t), + UNIT_AFTER, + UNIT(t)->ignore_on_soft_reboot ? -EINVAL : UNIT_REQUIRES, + SPECIAL_SYSINIT_TARGET, + true, + UNIT_DEPENDENCY_DEFAULT); if (r < 0) return r; @@ -112,7 +117,14 @@ static int timer_add_default_dependencies(Timer *t) { } } - return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT); + if (!UNIT(t)->ignore_on_soft_reboot) + return unit_add_two_dependencies_by_name( + UNIT(t), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, true, + UNIT_DEPENDENCY_DEFAULT); + + return unit_add_dependencies_on_real_shutdown_targets(UNIT(t)); } static int timer_add_trigger_dependencies(Timer *t) { diff --git a/src/core/transaction.c b/src/core/transaction.c index ffc063a6842..e47ddf0dd4e 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -628,7 +628,7 @@ static int transaction_apply( HASHMAP_FOREACH(j, m->jobs) { assert(j->installed); - if (j->unit->ignore_on_isolate) + if (j->unit->ignore_on_isolate || j->unit->ignore_on_soft_reboot) continue; if (hashmap_contains(tr->jobs, j->unit)) @@ -1159,6 +1159,9 @@ static bool shall_stop_on_isolate(Transaction *tr, Unit *u) { if (u->ignore_on_isolate) return false; + if (u->ignore_on_soft_reboot) + return false; + /* Is there already something listed for this? */ if (hashmap_contains(tr->jobs, u)) return false; diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c index cb209c5bde8..e96af716346 100644 --- a/src/core/unit-serialize.c +++ b/src/core/unit-serialize.c @@ -830,6 +830,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tRefuseManualStart: %s\n" "%s\tRefuseManualStop: %s\n" "%s\tDefaultDependencies: %s\n" + "%s\tIgnoreOnSoftReboot: %s\n" "%s\tOnSuccessJobMode: %s\n" "%s\tOnFailureJobMode: %s\n" "%s\tIgnoreOnIsolate: %s\n", @@ -837,6 +838,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->default_dependencies), + prefix, yes_no(u->ignore_on_soft_reboot), prefix, job_mode_to_string(u->on_success_job_mode), prefix, job_mode_to_string(u->on_failure_job_mode), prefix, yes_no(u->ignore_on_isolate)); diff --git a/src/core/unit.c b/src/core/unit.c index 6792bda9d01..468b193acd5 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3291,19 +3291,46 @@ int unit_add_dependency( } int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference, UnitDependencyMask mask) { - int r, s; + int r = 0, s = 0; + + assert(u); + assert(d >= 0 || e >= 0); + + if (d >= 0) { + r = unit_add_dependency(u, d, other, add_reference, mask); + if (r < 0) + return r; + } + + if (e >= 0) { + s = unit_add_dependency(u, e, other, add_reference, mask); + if (s < 0) + return s; + } + + return r > 0 || s > 0; +} + +int unit_add_dependencies_on_real_shutdown_targets(Unit *u) { + int r; assert(u); - r = unit_add_dependency(u, d, other, add_reference, mask); - if (r < 0) - return r; + STRV_FOREACH(target, STRV_MAKE(SPECIAL_REBOOT_TARGET, + SPECIAL_KEXEC_TARGET, + SPECIAL_HALT_TARGET, + SPECIAL_POWEROFF_TARGET)) { + r = unit_add_two_dependencies_by_name(u, + UNIT_BEFORE, + UNIT_CONFLICTS, + *target, + /* add_reference= */ true, + UNIT_DEPENDENCY_DEFAULT); + if (r < 0) + return r; + } - s = unit_add_dependency(u, e, other, add_reference, mask); - if (s < 0) - return s; - - return r > 0 || s > 0; + return 0; } static int resolve_template(Unit *u, const char *name, char **buf, const char **ret) { diff --git a/src/core/unit.h b/src/core/unit.h index c0710299a54..6aa8b82ed87 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -450,6 +450,9 @@ typedef struct Unit { /* Create default dependencies */ bool default_dependencies; + /* Configure so that the unit survives a soft reboot without stopping/starting. */ + bool ignore_on_soft_reboot; + /* Refuse manual starting, allow starting only indirectly via dependency. */ bool refuse_manual_start; @@ -852,6 +855,8 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, boo int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, bool add_reference, UnitDependencyMask mask); int unit_add_exec_dependencies(Unit *u, ExecContext *c); +/* Helper for IgnoreOnSoftReboot units that need to survive soft-reboot.target but not others */ +int unit_add_dependencies_on_real_shutdown_targets(Unit *u); int unit_choose_id(Unit *u, const char *name); int unit_set_description(Unit *u, const char *description); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index e7b44cc39ba..8258ee3aaa6 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2521,6 +2521,7 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const "RefuseManualStop", "AllowIsolate", "IgnoreOnIsolate", + "IgnoreOnSoftReboot", "DefaultDependencies")) return bus_append_parse_boolean(m, field, eq); diff --git a/test/units/testsuite-82.sh b/test/units/testsuite-82.sh index a1c82a9fc9d..a425d21c96a 100755 --- a/test/units/testsuite-82.sh +++ b/test/units/testsuite-82.sh @@ -136,11 +136,10 @@ rm "$T" exec sleep infinity EOF chmod +x "$T" - # This sets DefaultDependencies=no so that it remains running until the - # very end, and IgnoreOnIsolate=yes so that it isn't stopped via the - # "testsuite.target" isolation we do on next boot - systemd-run -p Type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-survive.service "$T" - systemd-run -p Type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive.service sleep infinity + # Configure this transient unit to survive the soft reboot - it will not conflict with shutdown.target + # and it will be ignored on the isolate that happens in the next boot. + systemd-run -p Type=notify -p IgnoreOnSoftReboot=yes --unit=testsuite-82-survive.service "$T" + systemd-run -p Type=exec -p IgnoreOnSoftReboot=yes --unit=testsuite-82-nosurvive.service sleep infinity # Now issue the soft reboot. We should be right back soon. touch /run/testsuite82.touch