#include "fileio.h"
#include "format-util.h"
#include "hostname-util.h"
+#include "image.h"
#include "image-dbus.h"
#include "io-util.h"
#include "machine-dbus.h"
return redirect_method_to_image(message, userdata, error, bus_image_method_get_os_release);
}
-static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
+static int clean_pool_done(Operation *operation, int child_error, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- bool success;
- size_t n;
+ _cleanup_fclose_ FILE *file = NULL;
int r;
assert(operation);
+ assert(operation->message);
assert(operation->extra_fd >= 0);
- if (lseek(operation->extra_fd, 0, SEEK_SET) < 0)
- return -errno;
+ file = take_fdopen(&operation->extra_fd, "r");
+ if (!file)
+ return log_debug_errno(errno, "Failed to take opened tmp file's fd: %m");
- f = take_fdopen(&operation->extra_fd, "r");
- if (!f)
- return -errno;
-
- /* The resulting temporary file starts with a boolean value that indicates success or not. */
- errno = 0;
- n = fread(&success, 1, sizeof(success), f);
- if (n != sizeof(success))
- return ret < 0 ? ret : errno_or_else(EIO);
-
- if (ret < 0) {
- _cleanup_free_ char *name = NULL;
-
- /* The clean-up operation failed. In this case the resulting temporary file should contain a boolean
- * set to false followed by the name of the failed image. Let's try to read this and use it for the
- * error message. If we can't read it, don't mind, and return the naked error. */
-
- if (success) /* The resulting temporary file could not be updated, ignore it. */
- return ret;
-
- r = read_nul_string(f, LONG_LINE_MAX, &name);
- if (r <= 0) /* Same here... */
- return ret;
-
- return sd_bus_error_set_errnof(error, ret, "Failed to remove image %s: %m", name);
- }
-
- assert(success);
+ r = clean_pool_read_first_entry(file, child_error, error);
+ if (r < 0)
+ return r;
r = sd_bus_message_new_method_return(operation->message, &reply);
if (r < 0)
* their size on disk. Let's read that and turn it into a bus message. */
for (;;) {
_cleanup_free_ char *name = NULL;
- uint64_t size;
+ uint64_t usage;
- r = read_nul_string(f, LONG_LINE_MAX, &name);
+ r = clean_pool_read_next_entry(file, &name, &usage);
if (r < 0)
return r;
- if (r == 0) /* reached the end */
+ if (r == 0)
break;
- errno = 0;
- n = fread(&size, 1, sizeof(size), f);
- if (n != sizeof(size))
- return errno_or_else(EIO);
-
- r = sd_bus_message_append(reply, "(st)", name, size);
+ r = sd_bus_message_append(reply, "(st)", name, usage);
if (r < 0)
return r;
}
}
static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- enum {
- REMOVE_ALL,
- REMOVE_HIDDEN,
- } mode;
-
- _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
- _cleanup_close_ int result_fd = -EBADF;
+ ImageCleanPoolMode mode;
Manager *m = userdata;
Operation *operation;
const char *mm;
- pid_t child;
int r;
assert(message);
return r;
if (streq(mm, "all"))
- mode = REMOVE_ALL;
+ mode = IMAGE_CLEAN_POOL_REMOVE_ALL;
else if (streq(mm, "hidden"))
- mode = REMOVE_HIDDEN;
+ mode = IMAGE_CLEAN_POOL_REMOVE_HIDDEN;
else
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
if (r == 0)
return 1; /* Will call us back */
- if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
-
- /* Create a temporary file we can dump information about deleted images into. We use a temporary file for this
- * instead of a pipe or so, since this might grow quit large in theory and we don't want to process this
- * continuously */
- result_fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC);
- if (result_fd < 0)
- return -errno;
-
- /* This might be a slow operation, run it asynchronously in a background process */
- r = safe_fork("(sd-clean)", FORK_RESET_SIGNALS, &child);
+ r = image_clean_pool_operation(m, mode, &operation);
if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
- if (r == 0) {
- _cleanup_hashmap_free_ Hashmap *images = NULL;
- bool success = true;
- Image *image;
- ssize_t l;
-
- errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
-
- images = hashmap_new(&image_hash_ops);
- if (!images) {
- r = -ENOMEM;
- goto child_fail;
- }
-
- r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
- if (r < 0)
- goto child_fail;
+ return log_debug_errno(r, "Failed to clean pool of images: %m");
- l = write(result_fd, &success, sizeof(success));
- if (l < 0) {
- r = -errno;
- goto child_fail;
- }
-
- HASHMAP_FOREACH(image, images) {
-
- /* We can't remove vendor images (i.e. those in /usr) */
- if (IMAGE_IS_VENDOR(image))
- continue;
-
- if (IMAGE_IS_HOST(image))
- continue;
-
- if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image))
- continue;
-
- r = image_remove(image);
- if (r == -EBUSY) /* keep images that are currently being used. */
- continue;
- if (r < 0) {
- /* If the operation failed, let's override everything we wrote, and instead write there at which image we failed. */
- success = false;
- (void) ftruncate(result_fd, 0);
- (void) lseek(result_fd, 0, SEEK_SET);
- (void) write(result_fd, &success, sizeof(success));
- (void) write(result_fd, image->name, strlen(image->name)+1);
- goto child_fail;
- }
-
- l = write(result_fd, image->name, strlen(image->name)+1);
- if (l < 0) {
- r = -errno;
- goto child_fail;
- }
-
- l = write(result_fd, &image->usage_exclusive, sizeof(image->usage_exclusive));
- if (l < 0) {
- r = -errno;
- goto child_fail;
- }
- }
-
- result_fd = safe_close(result_fd);
- _exit(EXIT_SUCCESS);
-
- child_fail:
- (void) write(errno_pipe_fd[1], &r, sizeof(r));
- _exit(EXIT_FAILURE);
- }
-
- errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
-
- /* The clean-up might take a while, hence install a watch on the child and return */
-
- r = operation_new_with_bus_reply(m, /* machine= */ NULL, child, message, errno_pipe_fd[0], &operation);
- if (r < 0) {
- (void) sigkill_wait(child);
- return r;
- }
-
- operation->extra_fd = result_fd;
+ operation_attach_bus_reply(operation, message);
operation->done = clean_pool_done;
-
- result_fd = -EBADF;
- errno_pipe_fd[0] = -EBADF;
-
return 1;
}