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:
parent
d5bd92bbbe
commit
d94c2b06f9
@ -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>
|
||||
|
@ -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 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user