#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"
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;
+}
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);
&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"),