From: Ivan Kruglov Date: Wed, 8 Jan 2025 12:56:14 +0000 (+0100) Subject: machine: introduce io.systemd.MachineImage.CleanPool X-Git-Tag: v258-rc1~1426^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f6a7328e24dafcabf9f33886d0a3684de0396601;p=thirdparty%2Fsystemd.git machine: introduce io.systemd.MachineImage.CleanPool --- diff --git a/src/machine/image-varlink.c b/src/machine/image-varlink.c index 0e616da9d1d..eb0fc12dc6b 100644 --- a/src/machine/image-varlink.c +++ b/src/machine/image-varlink.c @@ -6,8 +6,11 @@ #include "bus-polkit.h" #include "btrfs-util.h" #include "fd-util.h" +#include "fileio.h" +#include "image.h" #include "image-varlink.h" #include "io-util.h" +#include "json-util.h" #include "machine.h" #include "machine-pool.h" #include "string-util.h" @@ -277,3 +280,132 @@ int vl_method_set_pool_limit(sd_varlink *link, sd_json_variant *parameters, sd_v return sd_varlink_reply(link, NULL); } + +static int clean_pool_list_one_image(sd_varlink *link, const char *name, uint64_t usage_exclusive, bool more) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + int r; + + assert(link); + assert(name); + + r = sd_json_buildo( + &v, + SD_JSON_BUILD_PAIR_STRING("name", name), + JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("usageExclusive", usage_exclusive, UINT64_MAX)); + if (r < 0) + return r; + + if (more) + return sd_varlink_notify(link, v); + + return sd_varlink_reply(link, v); +} + +static int clean_pool_done_internal(Operation *operation, FILE *file, int child_error) { + int r; + + assert(operation); + assert(operation->link); + + r = clean_pool_read_first_entry(file, child_error, /* error = */ NULL); + if (r < 0) + return log_debug_errno(r, "Failed to read first entry from tmp file: %m"); + + /* On success the resulting temporary file will contain a list of image names that were removed followed by + * their size on disk. Let's read that and turn it into a bus message. */ + _cleanup_free_ char *previous_name = NULL; + uint64_t previous_usage; + for (;;) { + _cleanup_free_ char *name = NULL; + uint64_t usage; + r = clean_pool_read_next_entry(file, &name, &usage); + if (r < 0) + return log_debug_errno(r, "Failed to read next entry from tmp file: %m"); + if (r == 0) + break; + + if (previous_name) { + r = clean_pool_list_one_image(operation->link, previous_name, previous_usage, /* more = */ true); + if (r < 0) + return r; + /* freeing memory to avoid memleak at the following assignment */ + previous_name = mfree(previous_name); + } + + previous_name = TAKE_PTR(name); + previous_usage = usage; + } + + if (previous_name) + return clean_pool_list_one_image(operation->link, previous_name, previous_usage, /* more = */ false); + + return sd_varlink_error(operation->link, "io.systemd.MachineImage.NoSuchImage", NULL); +} + +static int clean_pool_done(Operation *operation, int child_error, sd_bus_error *error) { + _cleanup_fclose_ FILE *file = NULL; + int r; + + assert(operation); + assert(operation->link); + assert(operation->extra_fd >= 0); + + file = take_fdopen(&operation->extra_fd, "r"); + if (!file) + return log_debug_errno(errno, "Failed to take opened tmp file's fd: %m"); + + r = clean_pool_done_internal(operation, file, child_error); + if (r < 0) { + r = sd_varlink_error_errno(operation->link, r); + if (r < 0) + log_debug_errno(r, "Failed to reply to varlink request, ignoring: %m"); + } + + return 0; +} + +static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_image_clean_pool_mode, ImageCleanPoolMode, image_clean_pool_mode_from_string); + +int vl_method_clean_pool(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + static const sd_json_dispatch_field dispatch_table[] = { + { "mode", SD_JSON_VARIANT_STRING, json_dispatch_image_clean_pool_mode, 0, SD_JSON_MANDATORY }, + VARLINK_DISPATCH_POLKIT_FIELD, + {} + }; + + Manager *manager = ASSERT_PTR(userdata); + ImageCleanPoolMode mode; + int r; + + assert(link); + assert(parameters); + + if (manager->n_operations >= OPERATIONS_MAX) + return sd_varlink_error(link, "io.systemd.MachineImage.TooManyOperations", NULL); + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &mode); + if (r != 0) + return r; + + if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)) + return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL); + + r = varlink_verify_polkit_async( + link, + manager->bus, + "org.freedesktop.machine1.manage-images", + (const char**) STRV_MAKE("mode", image_clean_pool_mode_to_string(mode), + "verb", "clean_pool"), + &manager->polkit_registry); + if (r <= 0) + return r; + + Operation *op; + r = image_clean_pool_operation(manager, mode, &op); + if (r < 0) + return log_debug_errno(r, "Failed to clean pool of images: %m"); + + operation_attach_varlink_reply(op, link); + op->done = clean_pool_done; + return 1; +} diff --git a/src/machine/image-varlink.h b/src/machine/image-varlink.h index 5d94edf6786..ab91b5eee14 100644 --- a/src/machine/image-varlink.h +++ b/src/machine/image-varlink.h @@ -11,3 +11,4 @@ int vl_method_update_image(sd_varlink *link, sd_json_variant *parameters, sd_var int vl_method_clone_image(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_remove_image(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_set_pool_limit(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_clean_pool(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 5beec14ccd3..f3548f10210 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -801,6 +801,7 @@ static int manager_varlink_init_machine(Manager *m) { "io.systemd.MachineImage.Clone", vl_method_clone_image, "io.systemd.MachineImage.Remove", vl_method_remove_image, "io.systemd.MachineImage.SetPoolLimit", vl_method_set_pool_limit, + "io.systemd.MachineImage.CleanPool", vl_method_clean_pool, "io.systemd.service.Ping", varlink_method_ping, "io.systemd.service.SetLogLevel", varlink_method_set_log_level, "io.systemd.service.GetEnvironment", varlink_method_get_environment); diff --git a/src/shared/varlink-io.systemd.MachineImage.c b/src/shared/varlink-io.systemd.MachineImage.c index 8e9d90fe70a..00acdc1cfc8 100644 --- a/src/shared/varlink-io.systemd.MachineImage.c +++ b/src/shared/varlink-io.systemd.MachineImage.c @@ -86,6 +86,24 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_FIELD_COMMENT("New image quota limit"), SD_VARLINK_DEFINE_INPUT(limit, SD_VARLINK_INT, 0)); +static SD_VARLINK_DEFINE_ENUM_TYPE( + CleanPoolMode, + SD_VARLINK_FIELD_COMMENT("Remove all unused images"), + SD_VARLINK_DEFINE_ENUM_VALUE(all), + SD_VARLINK_FIELD_COMMENT("Remove only hidden images"), + SD_VARLINK_DEFINE_ENUM_VALUE(hidden)); + +static SD_VARLINK_DEFINE_METHOD_FULL( + CleanPool, + SD_VARLINK_SUPPORTS_MORE, + VARLINK_DEFINE_POLKIT_INPUT, + SD_VARLINK_FIELD_COMMENT("Allows removing all or only hidden images"), + SD_VARLINK_DEFINE_INPUT_BY_TYPE(mode, CleanPoolMode, 0), + SD_VARLINK_FIELD_COMMENT("Image name"), + SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("The image disk usage (exclusive)"), + SD_VARLINK_DEFINE_OUTPUT(usageExclusive, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); + static SD_VARLINK_DEFINE_ERROR(NoSuchImage); static SD_VARLINK_DEFINE_ERROR(TooManyOperations); static SD_VARLINK_DEFINE_ERROR(NotSupported); @@ -105,6 +123,10 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_Remove, SD_VARLINK_SYMBOL_COMMENT("Sets an overall quota limit on the pool of images"), &vl_method_SetPoolLimit, + SD_VARLINK_SYMBOL_COMMENT("A enum field allowing to control what type of images are cleaned up"), + &vl_type_CleanPoolMode, + SD_VARLINK_SYMBOL_COMMENT("Clean unused images depending on the specified mode. Note that it does not process vendon and host images."), + &vl_method_CleanPool, SD_VARLINK_SYMBOL_COMMENT("No matching image exists"), &vl_error_NoSuchImage, SD_VARLINK_SYMBOL_COMMENT("Too many ongoing background operations"),