1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-23 17:34:00 +03:00

machinectl: add new "machinectl clean" command

This new command removes all, or all hidden container images that have been
downloaded.
This commit is contained in:
Lennart Poettering 2016-04-11 17:24:08 +02:00
parent d5bd92bbbe
commit d94c2b06f9
5 changed files with 192 additions and 12 deletions

View File

@ -133,7 +133,9 @@
<para>When listing VM or container images, do not suppress
images beginning in a dot character
(<literal>.</literal>).</para></listitem>
(<literal>.</literal>).</para>
<para>When cleaning VM or container images, remove all images, not just hidden ones.</para></listitem>
</varlistentry>
<varlistentry>
@ -217,9 +219,11 @@
<term><option>--read-only</option></term>
<listitem><para>When used with <command>bind</command>, applies
a read-only bind mount.</para></listitem>
</varlistentry>
a read-only bind mount.</para>
<para>When used with <command>clone</command>, <command>import-raw</command> or <command>import-tar</command> a
read-only container or VM image is created.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
@ -599,7 +603,10 @@
all other settings that could identify the instance
unmodified. The original image and the cloned copy will hence
share these credentials, and it might be necessary to manually
change them in the copy.</para></listitem>
change them in the copy.</para>
<para>If combined with the <option>--read-only</option> switch a read-only cloned image is
created.</para></listitem>
</varlistentry>
<varlistentry>
@ -660,6 +667,23 @@
itself.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>clean</command></term>
<listitem><para>Remove hidden VM or container images (or all). This command removes all hidden machine images
from <filename>/var/lib/machines</filename>, i.e. those whose name begins with a dot. Use <command>machinectl
list-images --all</command> to see a list of all machine images, including the hidden ones.</para>
<para>When combined with the <option>--all</option> switch removes all images, not just hidden ones. This
command effectively empties <filename>/var/lib/machines</filename>.</para>
<para>Note that commands such as <command>machinectl pull-tar</command> or <command>machinectl
pull-raw</command> usually create hidden, read-only, unmodified machine images from the downloaded image first,
before cloning a writable working copy of it, in order to avoid duplicate downloads in case of images that are
reused multiple times. Use <command>machinectl clean</command> to remove old, hidden images created this
way.</para></listitem>
</varlistentry>
</variablelist></refsect2>
<refsect2><title>Image Transfer Commands</title><variablelist>

View File

@ -2338,6 +2338,50 @@ static int set_limit(int argc, char *argv[], void *userdata) {
return 0;
}
static int clean_images(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
uint64_t usage, total = 0;
char fb[FORMAT_BYTES_MAX];
sd_bus *bus = userdata;
const char *name;
unsigned c = 0;
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"CleanPool",
&error,
&reply,
"s", arg_all ? "all" : "hidden");
if (r < 0)
return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, 'a', "(st)");
if (r < 0)
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
log_info("Removed image '%s'. Freed exclusive disk space: %s",
name, format_bytes(fb, sizeof(fb), usage));
total += usage;
c++;
}
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
log_info("Removed %u images in total. Total freed exclusive disk space %s.",
c, format_bytes(fb, sizeof(fb), total));
return 0;
}
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@ -2396,6 +2440,7 @@ static int help(int argc, char *argv[], void *userdata) {
" read-only NAME [BOOL] Mark or unmark image read-only\n"
" remove NAME... Remove an image\n"
" set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
" clean Remove hidden (or all) images\n"
"Image Transfer Commands:\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
@ -2635,6 +2680,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "list-transfers", VERB_ANY, 1, 0, list_transfers },
{ "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
{ "set-limit", 2, 3, 0, set_limit },
{ "clean", VERB_ANY, 1, 0, clean_images },
{}
};

View File

@ -802,6 +802,93 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
return bus_image_method_mark_read_only(message, i, error);
}
static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) {
enum {
REMOVE_ALL,
REMOVE_HIDDEN,
} mode;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
Manager *m = userdata;
Image *image;
const char *mm;
Iterator i;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &mm);
if (r < 0)
return r;
if (streq(mm, "all"))
mode = REMOVE_ALL;
else if (streq(mm, "hidden"))
mode = REMOVE_HIDDEN;
else
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
images = hashmap_new(&string_hash_ops);
if (!images)
return -ENOMEM;
r = image_discover(images);
if (r < 0)
return r;
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(st)");
if (r < 0)
return r;
HASHMAP_FOREACH(image, images, i) {
/* 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)
return sd_bus_error_set_errnof(error, r, "Failed to remove image %s: %m", image->name);
r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
uint64_t limit;
@ -1144,6 +1231,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),

View File

@ -401,8 +401,7 @@ int image_remove(Image *i) {
assert(i);
if (path_equal(i->path, "/") ||
path_startswith(i->path, "/usr"))
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
settings = image_settings_path(i);
@ -474,8 +473,7 @@ int image_rename(Image *i, const char *new_name) {
if (!image_name_is_valid(new_name))
return -EINVAL;
if (path_equal(i->path, "/") ||
path_startswith(i->path, "/usr"))
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
settings = image_settings_path(i);
@ -642,8 +640,7 @@ int image_read_only(Image *i, bool b) {
int r;
assert(i);
if (path_equal(i->path, "/") ||
path_startswith(i->path, "/usr"))
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
/* Make sure we don't interfere with a running nspawn */
@ -751,8 +748,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
int image_set_limit(Image *i, uint64_t referenced_max) {
assert(i);
if (path_equal(i->path, "/") ||
path_startswith(i->path, "/usr"))
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
if (i->type != IMAGE_SUBVOLUME)

View File

@ -25,6 +25,8 @@
#include "hashmap.h"
#include "lockfile-util.h"
#include "macro.h"
#include "path-util.h"
#include "string-util.h"
#include "time-util.h"
typedef enum ImageType {
@ -75,3 +77,27 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
int image_name_lock(const char *name, int operation, LockFile *ret);
int image_set_limit(Image *i, uint64_t referenced_max);
static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
assert(i);
return i->name && i->name[0] == '.';
}
static inline bool IMAGE_IS_VENDOR(const struct Image *i) {
assert(i);
return i->path && path_startswith(i->path, "/usr");
}
static inline bool IMAGE_IS_HOST(const struct Image *i) {
assert(i);
if (i->name && streq(i->name, ".host"))
return true;
if (i->path && path_equal(i->path, "/"))
return true;
return false;
}