diff options
Diffstat (limited to 'src/grp-machine')
27 files changed, 376 insertions, 118 deletions
diff --git a/src/grp-machine/grp-import/libimport/Makefile b/src/grp-machine/grp-import/libimport/Makefile index 4162da6c9d..a07572e06e 100644 --- a/src/grp-machine/grp-import/libimport/Makefile +++ b/src/grp-machine/grp-import/libimport/Makefile @@ -36,7 +36,7 @@ test_qcow2_CFLAGS = \ $(ZLIB_CFLAGS) test_qcow2_LDADD = \ - libshared.la \ + libsystemd-shared.la \ $(ZLIB_LIBS) include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/libimport/import-common.c b/src/grp-machine/grp-import/libimport/import-common.c index cd69e900ec..9061cd4da1 100644 --- a/src/grp-machine/grp-import/libimport/import-common.c +++ b/src/grp-machine/grp-import/libimport/import-common.c @@ -126,9 +126,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) { if (null_fd != STDOUT_FILENO) null_fd = safe_close(null_fd); - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); + stdio_unset_cloexec(); if (unshare(CLONE_NEWNET) < 0) log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m"); @@ -200,9 +198,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (null_fd != STDIN_FILENO) null_fd = safe_close(null_fd); - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); + stdio_unset_cloexec(); if (unshare(CLONE_NEWNET) < 0) log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m"); diff --git a/src/grp-machine/grp-import/systemd-export/Makefile b/src/grp-machine/grp-import/systemd-export/Makefile index f6e15987a5..dfed613e6f 100644 --- a/src/grp-machine/grp-import/systemd-export/Makefile +++ b/src/grp-machine/grp-import/systemd-export/Makefile @@ -43,7 +43,7 @@ systemd_export_CFLAGS = \ $(BZIP2_CFLAGS) systemd_export_LDADD = \ - libshared.la \ + libsystemd-shared.la \ $(XZ_LIBS) \ $(ZLIB_LIBS) \ $(BZIP2_LIBS) diff --git a/src/grp-machine/grp-import/systemd-import/Makefile b/src/grp-machine/grp-import/systemd-import/Makefile index e117efc68a..7eba7cb044 100644 --- a/src/grp-machine/grp-import/systemd-import/Makefile +++ b/src/grp-machine/grp-import/systemd-import/Makefile @@ -44,7 +44,7 @@ systemd_import_CFLAGS = \ $(BZIP2_CFLAGS) systemd_import_LDADD = \ - libshared.la \ + libsystemd-shared.la \ $(XZ_LIBS) \ $(ZLIB_LIBS) \ $(BZIP2_LIBS) diff --git a/src/grp-machine/grp-import/systemd-import/import.c b/src/grp-machine/grp-import/systemd-import/import.c index 6744006312..38fe7f8110 100644 --- a/src/grp-machine/grp-import/systemd-import/import.c +++ b/src/grp-machine/grp-import/systemd-import/import.c @@ -91,7 +91,7 @@ static int import_tar(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); + log_error("Image '%s' already exists.", local); return -EEXIST; } } @@ -186,7 +186,7 @@ static int import_raw(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); + log_error("Image '%s' already exists.", local); return -EEXIST; } } diff --git a/src/grp-machine/grp-import/systemd-importd/Makefile b/src/grp-machine/grp-import/systemd-importd/Makefile index 1f5bae1267..e00f531a0d 100644 --- a/src/grp-machine/grp-import/systemd-importd/Makefile +++ b/src/grp-machine/grp-import/systemd-importd/Makefile @@ -34,9 +34,9 @@ systemd_importd_CFLAGS = \ -D SYSTEMD_EXPORT_PATH=\"$(rootlibexecdir)/systemd-export\" systemd_importd_LDADD = \ - libshared.la + libsystemd-shared.la -dist_rootlibexec_DATA = \ +dist_rootlibexec_DATA += \ src/import/import-pubring.gpg nodist_systemunit_DATA += \ diff --git a/src/grp-machine/grp-import/systemd-importd/importd.c b/src/grp-machine/grp-import/systemd-importd/importd.c index e790c0c475..f032f06fcf 100644 --- a/src/grp-machine/grp-import/systemd-importd/importd.c +++ b/src/grp-machine/grp-import/systemd-importd/importd.c @@ -448,9 +448,7 @@ static int transfer_start(Transfer *t) { safe_close(null_fd); } - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); + stdio_unset_cloexec(); setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1); setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1); diff --git a/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in index b74ad72cdc..0f5489e7e3 100644 --- a/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in +++ b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in @@ -17,3 +17,5 @@ CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_ NoNewPrivileges=yes WatchdogSec=3min KillMode=mixed +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/src/grp-machine/grp-import/systemd-pull/Makefile b/src/grp-machine/grp-import/systemd-pull/Makefile index 31a90fc90f..d7ece7396e 100644 --- a/src/grp-machine/grp-import/systemd-pull/Makefile +++ b/src/grp-machine/grp-import/systemd-pull/Makefile @@ -54,7 +54,7 @@ systemd_pull_CFLAGS = \ -D USER_KEYRING_PATH=\"$(pkgsysconfdir)/import-pubring.gpg\" systemd_pull_LDADD = \ - libshared.la \ + libsystemd-shared.la \ $(LIBCURL_LIBS) \ $(XZ_LIBS) \ $(ZLIB_LIBS) \ diff --git a/src/grp-machine/grp-import/systemd-pull/pull-common.c b/src/grp-machine/grp-import/systemd-pull/pull-common.c index f5815c216d..bd1623e003 100644 --- a/src/grp-machine/grp-import/systemd-pull/pull-common.c +++ b/src/grp-machine/grp-import/systemd-pull/pull-common.c @@ -507,9 +507,7 @@ int pull_verify(PullJob *main_job, cmd[k++] = "-"; cmd[k++] = NULL; - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); + stdio_unset_cloexec(); execvp("gpg2", (char * const *) cmd); execvp("gpg", (char * const *) cmd); diff --git a/src/grp-machine/grp-import/systemd-pull/pull.c b/src/grp-machine/grp-import/systemd-pull/pull.c index 166f91d6e1..4153c938d8 100644 --- a/src/grp-machine/grp-import/systemd-pull/pull.c +++ b/src/grp-machine/grp-import/systemd-pull/pull.c @@ -98,7 +98,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); + log_error("Image '%s' already exists.", local); return -EEXIST; } } @@ -184,7 +184,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); + log_error("Image '%s' already exists.", local); return -EEXIST; } } diff --git a/src/grp-machine/machinectl/Makefile b/src/grp-machine/machinectl/Makefile index a3ad40e59c..f6760f3174 100644 --- a/src/grp-machine/machinectl/Makefile +++ b/src/grp-machine/machinectl/Makefile @@ -27,7 +27,7 @@ machinectl_SOURCES = \ src/machine/machinectl.c machinectl_LDADD = \ - libshared.la + libsystemd-shared.la rootbin_PROGRAMS += \ machinectl diff --git a/src/grp-machine/machinectl/machinectl.c b/src/grp-machine/machinectl/machinectl.c index fa607c57e4..d5304dba37 100644 --- a/src/grp-machine/machinectl/machinectl.c +++ b/src/grp-machine/machinectl/machinectl.c @@ -50,6 +50,7 @@ #include "basic/util.h" #include "basic/verbs.h" #include "basic/web-util.h" +#include "sd-bus/bus-common-errors.h" #include "sd-bus/bus-error.h" #include "shared/bus-unit-util.h" #include "shared/bus-util.h" @@ -527,7 +528,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { fputs(strna(i->name), stdout); - if (!sd_id128_equal(i->id, SD_ID128_NULL)) + if (!sd_id128_is_null(i->id)) printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id)); else putchar('\n'); @@ -1523,8 +1524,33 @@ static int read_only_image(int argc, char *argv[], void *userdata) { return 0; } +static int image_exists(sd_bus *bus, const char *name) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(name); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetImage", + &error, + NULL, + "s", name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE)) + return 0; + + return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, -r)); + } + + return 1; +} + static int make_service_name(const char *name, char **ret) { - _cleanup_free_ char *e = NULL; int r; assert(name); @@ -1535,11 +1561,7 @@ static int make_service_name(const char *name, char **ret) { return -EINVAL; } - e = unit_name_escape(name); - if (!e) - return log_oom(); - - r = unit_name_build("systemd-nspawn", e, ".service", ret); + r = unit_name_build("systemd-nspawn", name, ".service", ret); if (r < 0) return log_error_errno(r, "Failed to build unit name: %m"); @@ -1569,6 +1591,14 @@ static int start_machine(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = image_exists(bus, argv[i]); + if (r < 0) + return r; + if (r == 0) { + log_error("Machine image '%s' does not exist.", argv[1]); + return -ENXIO; + } + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -1636,6 +1666,14 @@ static int enable_machine(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = image_exists(bus, argv[i]); + if (r < 0) + return r; + if (r == 0) { + log_error("Machine image '%s' does not exist.", argv[1]); + return -ENXIO; + } + r = sd_bus_message_append(m, "s", unit); if (r < 0) return bus_log_create_error(r); @@ -2379,7 +2417,7 @@ static int set_limit(int argc, char *argv[], void *userdata) { } static int clean_images(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *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]; @@ -2388,15 +2426,22 @@ static int clean_images(int argc, char *argv[], void *userdata) { unsigned c = 0; int r; - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "CleanPool", - &error, - &reply, - "s", arg_all ? "all" : "hidden"); + "CleanPool"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden"); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence permit a longer time for completion. */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply); if (r < 0) return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r)); @@ -2540,35 +2585,65 @@ static int parse_argv(int argc, char *argv[]) { }; bool reorder = false; - int c, r; + int c, r, shell = -1; assert(argc >= 0); assert(argv); for (;;) { - const char * const option_string = "+hp:als:H:M:qn:o:"; + static const char option_string[] = "-hp:als:H:M:qn:o:"; c = getopt_long(argc, argv, option_string + reorder, options, NULL); - if (c < 0) { + if (c < 0) + break; + + switch (c) { + + case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a + * non-option argument was discovered. */ + + assert(!reorder); + /* We generally are fine with the fact that getopt_long() reorders the command line, and looks * for switches after the main verb. However, for "shell" we really don't want that, since we - * want that switches passed after that are passed to the program to execute, and not processed - * by us. To make this possible, we'll first invoke getopt_long() with reordering disabled - * (i.e. with the "+" prefix in the option string), and as soon as we hit the end (i.e. the - * verb) we check if that's "shell". If it is, we exit the loop, since we don't want any - * further options processed. However, if it is anything else, we process the same argument - * again, but this time allow reordering. */ - - if (!reorder && optind < argc && !streq(argv[optind], "shell")) { + * want that switches specified after the machine name are passed to the program to execute, + * and not processed by us. To make this possible, we'll first invoke getopt_long() with + * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first + * non-option parameter. If it's the verb "shell" we remember its position and continue + * processing options. In this case, as soon as we hit the next non-option argument we found + * the machine name, and stop further processing. If the first non-option argument is any other + * verb than "shell" we switch to normal reordering mode and continue processing arguments + * normally. */ + + if (shell >= 0) { + /* If we already found the "shell" verb on the command line, and now found the next + * non-option argument, then this is the machine name and we should stop processing + * further arguments. */ + optind --; /* don't process this argument, go one step back */ + goto done; + } + if (streq(optarg, "shell")) + /* Remember the position of the "shell" verb, and continue processing normally. */ + shell = optind - 1; + else { + int saved_optind; + + /* OK, this is some other verb. In this case, turn on reordering again, and continue + * processing normally. */ reorder = true; - optind--; - continue; + + /* We changed the option string. getopt_long() only looks at it again if we invoke it + * at least once with a reset option index. Hence, let's reset the option index here, + * then invoke getopt_long() again (ignoring what it has to say, after all we most + * likely already processed it), and the bump the option index so that we read the + * intended argument again. */ + saved_optind = optind; + optind = 0; + (void) getopt_long(argc, argv, option_string + reorder, options, NULL); + optind = saved_optind - 1; /* go one step back, process this argument again */ } break; - } - - switch (c) { case 'h': return help(0, NULL, NULL); @@ -2704,6 +2779,22 @@ static int parse_argv(int argc, char *argv[]) { } } +done: + if (shell >= 0) { + char *t; + int i; + + /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the + * argument list initially let's readjust it now, and move the "shell" verb to the back. */ + + optind -= 1; /* place the option index where the "shell" verb will be placed */ + + t = argv[shell]; + for (i = shell; i < optind; i++) + argv[i] = argv[i+1]; + argv[optind] = t; + } + return 1; } @@ -2720,6 +2811,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "terminate", 2, VERB_ANY, 0, terminate_machine }, { "reboot", 2, VERB_ANY, 0, reboot_machine }, { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, + { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */ { "kill", 2, VERB_ANY, 0, kill_machine }, { "login", VERB_ANY, 2, 0, login_machine }, { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, @@ -2750,7 +2842,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { } int main(int argc, char*argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -2772,6 +2864,7 @@ int main(int argc, char*argv[]) { r = machinectl_main(argc, argv, bus); finish: + sd_bus_flush_close_unref(bus); pager_close(); polkit_agent_close(); diff --git a/src/grp-machine/machinectl/machinectl.completion.bash b/src/grp-machine/machinectl/machinectl.completion.bash index e7829ca968..aebe48304d 100644 --- a/src/grp-machine/machinectl/machinectl.completion.bash +++ b/src/grp-machine/machinectl/machinectl.completion.bash @@ -41,7 +41,7 @@ _machinectl() { local -A VERBS=( [STANDALONE]='list list-images pull-tar pull-raw import-tar import-raw export-tar export-raw list-transfers cancel-transfer' - [MACHINES]='status show start login shell enable disable poweroff reboot terminate kill copy-to copy-from image-status show-image clone rename read-only remove set-limit' + [MACHINES]='status show start stop login shell enable disable poweroff reboot terminate kill copy-to copy-from image-status show-image clone rename read-only remove set-limit' ) _init_completion || return diff --git a/src/grp-machine/machinectl/machinectl.completion.zsh b/src/grp-machine/machinectl/machinectl.completion.zsh index 198fa28f7b..92d77109a5 100644 --- a/src/grp-machine/machinectl/machinectl.completion.zsh +++ b/src/grp-machine/machinectl/machinectl.completion.zsh @@ -23,6 +23,7 @@ _available_machines() { "status:Show VM/container status" "show:Show properties of one or more VMs/containers" "start:Start container as a service" + "stop:Stop container (equal to poweroff)" "login:Get a login prompt on a VM/container" "enable:Enable automatic container start at boot" "disable:Disable automatic container start at boot" diff --git a/src/grp-machine/machinectl/machinectl.xml b/src/grp-machine/machinectl/machinectl.xml index 4b7f9a0391..597a5cc583 100644 --- a/src/grp-machine/machinectl/machinectl.xml +++ b/src/grp-machine/machinectl/machinectl.xml @@ -333,7 +333,7 @@ <listitem><para>Show properties of one or more registered virtual machines or containers or the manager itself. If no argument is specified, properties of the manager will be - shown. If an NAME is specified, properties of this virtual + shown. If a NAME is specified, properties of this virtual machine or container are shown. By default, empty properties are suppressed. Use <option>--all</option> to show those too. To select specific properties to show, use @@ -373,8 +373,7 @@ <para>To interactively start a container on the command line with full access to the container's console, please invoke <command>systemd-nspawn</command> directly. To stop a running - container use <command>machinectl poweroff</command>, see - below.</para></listitem> + container use <command>machinectl poweroff</command>.</para></listitem> </varlistentry> <varlistentry> @@ -461,8 +460,8 @@ <listitem><para>Power off one or more containers. This will trigger a reboot by sending SIGRTMIN+4 to the container's init process, which causes systemd-compatible init systems to shut - down cleanly. This operation does not work on containers that - do not run a + down cleanly. Use <command>stop</command> as alias for <command>poweroff</command>. + This operation does not work on containers that do not run a <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>-compatible init system, such as sysvinit. Use <command>terminate</command> (see below) to immediately @@ -576,7 +575,7 @@ <listitem><para>Show properties of one or more registered virtual machine or container images, or the manager itself. If no argument is specified, properties of the manager will be - shown. If an NAME is specified, properties of this virtual + shown. If a NAME is specified, properties of this virtual machine or container image are shown. By default, empty properties are suppressed. Use <option>--all</option> to show those too. To select specific properties to show, use diff --git a/src/grp-machine/nss-mymachines/Makefile b/src/grp-machine/nss-mymachines/Makefile index 78cb6419c0..bc7f9e791a 100644 --- a/src/grp-machine/nss-mymachines/Makefile +++ b/src/grp-machine/nss-mymachines/Makefile @@ -37,7 +37,8 @@ libnss_mymachines_la_LDFLAGS = \ -Wl,--version-script=$(srcdir)/nss-mymachines.sym libnss_mymachines_la_LIBADD = \ - libsystemd-internal.la + libsystemd-internal.la \ + libbasic.la lib_LTLIBRARIES += \ libnss_mymachines.la diff --git a/src/grp-machine/systemd-machined/Makefile b/src/grp-machine/systemd-machined/Makefile index ee17bfcd7c..c717267ed8 100644 --- a/src/grp-machine/systemd-machined/Makefile +++ b/src/grp-machine/systemd-machined/Makefile @@ -45,7 +45,7 @@ libmachine_core_la_SOURCES = \ src/machine/operation.h libmachine_core_la_LIBADD = \ - libshared.la + libsystemd-shared.la noinst_LTLIBRARIES += \ libmachine-core.la diff --git a/src/grp-machine/systemd-machined/image-dbus.c b/src/grp-machine/systemd-machined/image-dbus.c index 584777e5ad..6ab627a710 100644 --- a/src/grp-machine/systemd-machined/image-dbus.c +++ b/src/grp-machine/systemd-machined/image-dbus.c @@ -82,7 +82,7 @@ int bus_image_method_remove( errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); if (r < 0) { (void) sigkill_wait(child); return r; @@ -194,7 +194,7 @@ int bus_image_method_clone( errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); if (r < 0) { (void) sigkill_wait(child); return r; diff --git a/src/grp-machine/systemd-machined/machine-dbus.c b/src/grp-machine/systemd-machined/machine-dbus.c index 95f2084455..91b5dfa993 100644 --- a/src/grp-machine/systemd-machined/machine-dbus.c +++ b/src/grp-machine/systemd-machined/machine-dbus.c @@ -656,8 +656,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu r = sd_bus_message_read(message, "ss", &user, &path); if (r < 0) return r; - if (isempty(user)) - user = NULL; + user = empty_to_null(user); if (isempty(path)) path = "/bin/sh"; if (!path_is_absolute(path)) @@ -1202,8 +1201,6 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro child_fail: (void) write(errno_pipe_fd[1], &r, sizeof(r)); - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - _exit(EXIT_FAILURE); } @@ -1211,7 +1208,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro /* Copying might take a while, hence install a watch on the child, and return */ - r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]); + r = operation_new(m->manager, m, child, message, errno_pipe_fd[0], NULL); if (r < 0) { (void) sigkill_wait(child); return r; diff --git a/src/grp-machine/systemd-machined/machine.c b/src/grp-machine/systemd-machined/machine.c index 82d8f66395..701fec0e67 100644 --- a/src/grp-machine/systemd-machined/machine.c +++ b/src/grp-machine/systemd-machined/machine.c @@ -182,7 +182,7 @@ int machine_save(Machine *m) { fprintf(f, "ROOT=%s\n", escaped); } - if (!sd_id128_equal(m->id, SD_ID128_NULL)) + if (!sd_id128_is_null(m->id)) fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id)); if (m->leader != 0) diff --git a/src/grp-machine/systemd-machined/machined-dbus.c b/src/grp-machine/systemd-machined/machined-dbus.c index 7caecd43a0..349ffcf298 100644 --- a/src/grp-machine/systemd-machined/machined-dbus.c +++ b/src/grp-machine/systemd-machined/machined-dbus.c @@ -27,6 +27,7 @@ #include "basic/btrfs-util.h" #include "basic/cgroup-util.h" #include "basic/fd-util.h" +#include "basic/fileio.h" #include "basic/formats-util.h" #include "basic/hostname-util.h" #include "basic/io-util.h" @@ -823,22 +824,106 @@ 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 clean_pool_done(Operation *operation, int ret, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_fclose_ FILE *f = NULL; + bool success; + size_t n; + int r; + + assert(operation); + assert(operation->extra_fd >= 0); + + if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1) + return -errno; + + f = fdopen(operation->extra_fd, "re"); + if (!f) + return -errno; + + operation->extra_fd = -1; + + /* 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 != 0 ? -errno : -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, &name); + if (r < 0 || isempty(name)) /* Same here... */ + return ret; + + return sd_bus_error_set_errnof(error, ret, "Failed to remove image %s: %m", name); + } + + assert(success); + + r = sd_bus_message_new_method_return(operation->message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + /* 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. */ + for (;;) { + _cleanup_free_ char *name = NULL; + uint64_t size; + + r = read_nul_string(f, &name); + if (r < 0) + return r; + if (isempty(name)) /* reached the end */ + break; + + errno = 0; + n = fread(&size, 1, sizeof(size), f); + if (n != sizeof(size)) + return errno != 0 ? -errno : -EIO; + + r = sd_bus_message_append(reply, "(st)", name, size); + 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_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; + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; + _cleanup_close_ int result_fd = -1; Manager *m = userdata; - Image *image; + Operation *operation; const char *mm; - Iterator i; + pid_t child; int r; assert(message); + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + r = sd_bus_message_read(message, "s", &mm); if (r < 0) return r; @@ -864,50 +949,109 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err if (r == 0) return 1; /* Will call us back */ - images = hashmap_new(&string_hash_ops); - if (!images) - return -ENOMEM; + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - r = image_discover(images); - if (r < 0) - return r; + /* 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("/tmp/", O_RDWR|O_CLOEXEC); + if (result_fd < 0) + return -errno; - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; + /* This might be a slow operation, run it asynchronously in a background process */ + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - r = sd_bus_message_open_container(reply, 'a', "(st)"); - if (r < 0) - return r; + if (child == 0) { + _cleanup_(image_hashmap_freep) Hashmap *images = NULL; + bool success = true; + Image *image; + Iterator i; + ssize_t l; - HASHMAP_FOREACH(image, images, i) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - /* We can't remove vendor images (i.e. those in /usr) */ - if (IMAGE_IS_VENDOR(image)) - continue; + images = hashmap_new(&string_hash_ops); + if (!images) { + r = -ENOMEM; + goto child_fail; + } - if (IMAGE_IS_HOST(image)) - continue; + r = image_discover(images); + if (r < 0) + goto child_fail; - if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) - continue; + l = write(result_fd, &success, sizeof(success)); + if (l < 0) { + r = -errno; + goto child_fail; + } - 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); + HASHMAP_FOREACH(image, images, i) { - r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive); - if (r < 0) - return r; + /* 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); } - r = sd_bus_message_close_container(reply); - if (r < 0) + 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(m, NULL, child, message, errno_pipe_fd[0], &operation); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_send(NULL, reply, NULL); + operation->extra_fd = result_fd; + operation->done = clean_pool_done; + + result_fd = -1; + errno_pipe_fd[0] = -1; + + return 1; } static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { diff --git a/src/grp-machine/systemd-machined/machined.c b/src/grp-machine/systemd-machined/machined.c index 83a74e2549..a01d1820a1 100644 --- a/src/grp-machine/systemd-machined/machined.c +++ b/src/grp-machine/systemd-machined/machined.c @@ -304,7 +304,7 @@ void manager_gc(Manager *m, bool drop_not_started) { machine_get_state(machine) != MACHINE_CLOSING) machine_stop(machine); - /* Now, the stop stop probably made this referenced + /* Now, the stop probably made this referenced * again, but if it didn't, then it's time to let it * go entirely. */ if (!machine_check_gc(machine, drop_not_started)) { diff --git a/src/grp-machine/systemd-machined/operation.c b/src/grp-machine/systemd-machined/operation.c index f1bcb1d860..f16c38c47a 100644 --- a/src/grp-machine/systemd-machined/operation.c +++ b/src/grp-machine/systemd-machined/operation.c @@ -31,7 +31,7 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat assert(o); assert(si); - log_debug("Operating " PID_FMT " is now complete with with code=%s status=%i", + log_debug("Operating " PID_FMT " is now complete with code=%s status=%i", o->pid, sigchld_code_to_string(si->si_code), si->si_status); @@ -42,18 +42,33 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat goto fail; } - if (si->si_status != EXIT_SUCCESS) { - if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) - r = sd_bus_error_set_errnof(&error, r, "%m"); - else - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); - + if (si->si_status == EXIT_SUCCESS) + r = 0; + else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */ + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); goto fail; } - r = sd_bus_reply_method_return(o->message, NULL); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); + if (o->done) { + /* A completion routine is set for this operation, call it. */ + r = o->done(o, r, &error); + if (r < 0) { + if (!sd_bus_error_is_set(&error)) + sd_bus_error_set_errno(&error, r); + + goto fail; + } + + } else { + /* The default operation when done is to simply return an error on failure or an empty success + * message on success. */ + if (r < 0) + goto fail; + + r = sd_bus_reply_method_return(o->message, NULL); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + } operation_free(o); return 0; @@ -67,7 +82,7 @@ fail: return 0; } -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) { +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) { Operation *o; int r; @@ -80,6 +95,8 @@ int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_messag if (!o) return -ENOMEM; + o->extra_fd = -1; + r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o); if (r < 0) { free(o); @@ -103,6 +120,9 @@ int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_messag /* At this point we took ownership of both the child and the errno file descriptor! */ + if (ret) + *ret = o; + return 0; } @@ -113,6 +133,7 @@ Operation *operation_free(Operation *o) { sd_event_source_unref(o->event_source); safe_close(o->errno_fd); + safe_close(o->extra_fd); if (o->pid > 1) (void) sigkill_wait(o->pid); diff --git a/src/grp-machine/systemd-machined/operation.h b/src/grp-machine/systemd-machined/operation.h index 0821466a00..713ffe88c4 100644 --- a/src/grp-machine/systemd-machined/operation.h +++ b/src/grp-machine/systemd-machined/operation.h @@ -38,10 +38,12 @@ struct Operation { pid_t pid; sd_bus_message *message; int errno_fd; + int extra_fd; sd_event_source *event_source; + int (*done)(Operation *o, int ret, sd_bus_error *error); LIST_FIELDS(Operation, operations); LIST_FIELDS(Operation, operations_by_machine); }; -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd); +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret); Operation *operation_free(Operation *o); diff --git a/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf b/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf index 9d40b90151..562b9d3cc0 100644 --- a/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf +++ b/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf @@ -118,6 +118,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Manager" + send_member="CleanPool"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" send_member="MapFromMachineUser"/> <allow send_destination="org.freedesktop.machine1" diff --git a/src/grp-machine/systemd-machined/systemd-machined.service.in b/src/grp-machine/systemd-machined/systemd-machined.service.in index 685baab21d..dcf9f347b7 100644 --- a/src/grp-machine/systemd-machined/systemd-machined.service.in +++ b/src/grp-machine/systemd-machined/systemd-machined.service.in @@ -17,6 +17,8 @@ ExecStart=@rootlibexecdir@/systemd-machined BusName=org.freedesktop.machine1 CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD WatchdogSec=3min +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io # Note that machined cannot be placed in a mount namespace, since it # needs access to the host's mount namespace in order to implement the |