From: Adrian Vovk Date: Sat, 23 Dec 2023 21:57:47 +0000 (-0500) Subject: bus-unit-util: Add utility to freeze/thaw units X-Git-Tag: v256-rc1~617^2~3 X-Git-Url: http://git-history.diyao.me/?a=commitdiff_plain;h=7483708131b474d92c9207c8c6340b450b58cb94;p=systemd%2F.git bus-unit-util: Add utility to freeze/thaw units This utility lets us freeze units, and then automatically thaw them when via a _cleanup_ handler. For example, you can now write something like: ``` _cleanup_(unit_freezer_thaw) UnitFreezer freezer = UNIT_FREEZER_NULL; r = unit_freezer_freeze("myunit.service", &freezer); if (r < 0) return r; // Freeze is thawed once this scope ends ``` Aside from the basic _freeze and _thaw methods, there's also _cancel and _restore. Cancel destroys the UnitFreezer without thawing the unit. Restore creates a UnitFreezer without freezing it. The idea of these two methods is that it allows the freeze/thaw to be separated from each other (i.e. done in response to two separate DBus method calls). For example: ``` _cleanup_(unit_freezer_thaw) UnitFreezer freezer = UNIT_FREEZER_NULL; r = unit_freezer_freeze("myunit.service", &freezer); if (r < 0) return r; // Freeze is thawed once this scope ends r = do_something() if (r < 0) return r; // Freeze is thawed unit_freezer_cancel(&freezer); // Thaw is canceled. ``` Then in another scope: ``` // Bring back a UnitFreezer object for the already-frozen service _cleanup_(unit_freezer_thaw) UnitFreezer freezer = UNIT_FREEZER_NULL; r = unit_freezer_restore("myunit.service", &freezer); if (r < 0) return r; // Freeze is thawed once this scope ends ``` --- diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 783ce31b3d..2f485f810d 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -10,6 +10,7 @@ #include "cgroup-setup.h" #include "cgroup-util.h" #include "condition.h" +#include "constants.h" #include "coredump-util.h" #include "cpu-set-util.h" #include "dissect-image.h" @@ -2937,3 +2938,89 @@ int bus_service_manager_reload(sd_bus *bus) { return 0; } + +int unit_freezer_new(const char *name, UnitFreezer *ret) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *namedup = NULL; + int r; + + assert(name); + assert(ret); + + namedup = strdup(name); + if (!namedup) + return log_oom_debug(); + + r = bus_connect_system_systemd(&bus); + if (r < 0) + return log_debug_errno(r, "Failed to open connection to systemd: %m"); + + (void) sd_bus_set_method_call_timeout(bus, FREEZE_TIMEOUT); + + *ret = (UnitFreezer) { + .name = TAKE_PTR(namedup), + .bus = TAKE_PTR(bus), + }; + return 0; +} + +static int unit_freezer_action(bool freeze, UnitFreezer *f) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(f); + assert(f->bus); + assert(f->name); + + r = bus_call_method(f->bus, bus_systemd_mgr, freeze ? "FreezeUnit" : "ThawUnit", + &error, NULL, "s", f->name); + if (r < 0) + return log_debug_errno(r, "Failed to %s unit %s: %s", freeze ? "freeze" : "thaw", + f->name, bus_error_message(&error, r)); + + return 0; +} + +int unit_freezer_freeze(UnitFreezer *f) { + return unit_freezer_action(true, f); +} + +int unit_freezer_thaw(UnitFreezer *f) { + return unit_freezer_action(false, f); +} + +void unit_freezer_done(UnitFreezer *f) { + assert(f); + + f->name = mfree(f->name); + f->bus = sd_bus_flush_close_unref(f->bus); +} + +int unit_freezer_new_freeze(const char *name, UnitFreezer *ret) { + _cleanup_(unit_freezer_done) UnitFreezer f = {}; + int r; + + assert(name); + assert(ret); + + r = unit_freezer_new(name, &f); + if (r < 0) + return r; + + r = unit_freezer_freeze(&f); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(f); + return 0; +} + +void unit_freezer_done_thaw(UnitFreezer *f) { + assert(f); + + if (!f->name) + return; + + (void) unit_freezer_thaw(f); + unit_freezer_done(f); +} diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index d52c8475ca..3c6a001069 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -35,3 +35,20 @@ int unit_load_state(sd_bus *bus, const char *name, char **load_state); int unit_info_compare(const UnitInfo *a, const UnitInfo *b); int bus_service_manager_reload(sd_bus *bus); + +typedef struct UnitFreezer { + char *name; + sd_bus *bus; +} UnitFreezer; + +int unit_freezer_new(const char *name, UnitFreezer *ret); + +int unit_freezer_freeze(UnitFreezer *freezer); + +int unit_freezer_thaw(UnitFreezer *freezer); + +void unit_freezer_done(UnitFreezer *freezer); + +int unit_freezer_new_freeze(const char *name, UnitFreezer *ret); + +void unit_freezer_done_thaw(UnitFreezer *freezer);