diff options
Diffstat (limited to 'src/machine')
-rw-r--r-- | src/machine/image-dbus.c | 5 | ||||
-rw-r--r-- | src/machine/machine-dbus.c | 673 | ||||
-rw-r--r-- | src/machine/machine-dbus.h | 1 | ||||
-rw-r--r-- | src/machine/machine.c | 148 | ||||
-rw-r--r-- | src/machine/machine.h | 8 | ||||
-rw-r--r-- | src/machine/machinectl.c | 325 | ||||
-rw-r--r-- | src/machine/machined-dbus.c | 328 | ||||
-rw-r--r-- | src/machine/machined.c | 64 | ||||
-rw-r--r-- | src/machine/machined.h | 2 | ||||
-rw-r--r-- | src/machine/org.freedesktop.machine1.conf | 24 | ||||
-rw-r--r-- | src/machine/org.freedesktop.machine1.policy.in | 53 |
11 files changed, 1279 insertions, 352 deletions
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 95d7bca4bf..2453a9ff04 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -43,6 +43,7 @@ int bus_image_method_remove( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -83,6 +84,7 @@ int bus_image_method_rename( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -123,6 +125,7 @@ int bus_image_method_clone( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -158,6 +161,7 @@ int bus_image_method_mark_read_only( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -194,6 +198,7 @@ int bus_image_method_set_limit( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index dc42ffdc52..21026829a9 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -44,6 +44,8 @@ #include "machine-dbus.h" #include "formats-util.h" #include "process-util.h" +#include "env-util.h" +#include "terminal-util.h" static int property_get_id( sd_bus *bus, @@ -122,6 +124,7 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus message, CAP_KILL, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -167,6 +170,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro message, CAP_KILL, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -185,141 +189,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *us = NULL, *them = NULL; - _cleanup_close_ int netns_fd = -1; Machine *m = userdata; - const char *p; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); - - r = readlink_malloc("/proc/self/ns/net", &us); - if (r < 0) - return r; - - p = procfs_file_alloca(m->leader, "ns/net"); - r = readlink_malloc(p, &them); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - if (streq(us, them)) - return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); - - r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL); + r = sd_bus_message_open_container(reply, 'a', "(iay)"); if (r < 0) return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + switch (m->class) { - if (child == 0) { + case MACHINE_HOST: { _cleanup_free_ struct local_address *addresses = NULL; struct local_address *a; - int i, n; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, -1, netns_fd, -1); - if (r < 0) - _exit(EXIT_FAILURE); + int n, i; n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n < 0) - _exit(EXIT_FAILURE); + return n; for (a = addresses, i = 0; i < n; a++, i++) { - struct iovec iov[2] = { - { .iov_base = &a->family, .iov_len = sizeof(a->family) }, - { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, - }; - r = writev(pair[1], iov, 2); + r = sd_bus_message_open_container(reply, 'r', "iay"); if (r < 0) - _exit(EXIT_FAILURE); - } - - pair[1] = safe_close(pair[1]); - - _exit(EXIT_SUCCESS); - } + return r; - pair[1] = safe_close(pair[1]); + r = sd_bus_message_append(reply, "i", addresses[i].family); + if (r < 0) + return r; - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; + r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - return r; + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } - for (;;) { - int family; - ssize_t n; - union in_addr_union in_addr; - struct iovec iov[2]; - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = 2, - }; + break; + } - iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; - iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + case MACHINE_CONTAINER: { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *us = NULL, *them = NULL; + _cleanup_close_ int netns_fd = -1; + const char *p; + siginfo_t si; + pid_t child; - n = recvmsg(pair[0], &mh, 0); - if (n < 0) - return -errno; - if ((size_t) n < sizeof(family)) - break; + r = readlink_malloc("/proc/self/ns/net", &us); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'r', "iay"); + p = procfs_file_alloca(m->leader, "ns/net"); + r = readlink_malloc(p, &them); if (r < 0) return r; - r = sd_bus_message_append(reply, "i", family); + if (streq(us, them)) + return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); + + r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); if (r < 0) return r; - switch (family) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; - case AF_INET: - if (n != sizeof(struct in_addr) + sizeof(family)) - return -EIO; + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); - break; + if (child == 0) { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int i, n; - case AF_INET6: - if (n != sizeof(struct in6_addr) + sizeof(family)) - return -EIO; + pair[0] = safe_close(pair[0]); - r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); - break; + r = namespace_enter(-1, -1, netns_fd, -1, -1); + if (r < 0) + _exit(EXIT_FAILURE); + + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + _exit(EXIT_FAILURE); + + for (a = addresses, i = 0; i < n; a++, i++) { + struct iovec iov[2] = { + { .iov_base = &a->family, .iov_len = sizeof(a->family) }, + { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, + }; + + r = writev(pair[1], iov, 2); + if (r < 0) + _exit(EXIT_FAILURE); + } + + pair[1] = safe_close(pair[1]); + + _exit(EXIT_SUCCESS); } - if (r < 0) - return r; - r = sd_bus_message_close_container(reply); + pair[1] = safe_close(pair[1]); + + for (;;) { + int family; + ssize_t n; + union in_addr_union in_addr; + struct iovec iov[2]; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; + iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + + n = recvmsg(pair[0], &mh, 0); + if (n < 0) + return -errno; + if ((size_t) n < sizeof(family)) + break; + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", family); + if (r < 0) + return r; + + switch (family) { + + case AF_INET: + if (n != sizeof(struct in_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); + break; + + case AF_INET6: + if (n != sizeof(struct in6_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); + break; + } + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + r = wait_for_terminate(child, &si); if (r < 0) - return r; + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + break; } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -330,73 +372,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_strv_free_ char **l = NULL; - _cleanup_fclose_ FILE *f = NULL; Machine *m = userdata; char **k, **v; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + switch (m->class) { - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd); - if (r < 0) - return r; + case MACHINE_HOST: + r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; + break; - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_fclose_ FILE *f = NULL; + siginfo_t si; + pid_t child; - if (child == 0) { - _cleanup_close_ int fd = -1; + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; - pair[0] = safe_close(pair[0]); + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - r = namespace_enter(-1, mntns_fd, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); + if (child == 0) { + _cleanup_close_ int fd = -1; - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } + + r = copy_bytes(fd, pair[1], (uint64_t) -1, false); + if (r < 0) _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); } - r = copy_bytes(fd, pair[1], (off_t) -1, false); - if (r < 0) - _exit(EXIT_FAILURE); + pair[1] = safe_close(pair[1]); - _exit(EXIT_SUCCESS); - } + f = fdopen(pair[0], "re"); + if (!f) + return -errno; - pair[1] = safe_close(pair[1]); + pair[0] = -1; - f = fdopen(pair[0], "re"); - if (!f) - return -errno; + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - pair[0] = -1; + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); - if (r < 0) - return r; + break; + } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + } r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -429,14 +486,25 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines."); + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; - r = ptsname_malloc(master, &pty_name); + r = ptsname_namespace(master, &pty_name); if (r < 0) return r; @@ -451,26 +519,70 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_send(NULL, reply, NULL); } +static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) { + int r; + + assert(m); + assert(ret); + + switch (m->class) { + + case MACHINE_HOST: + *ret = NULL; + break; + + case MACHINE_CONTAINER: { + _cleanup_bus_unref_ sd_bus *bus = NULL; + char *address; + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r == -ENOENT) + return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + break; + } + + default: + return -EOPNOTSUPP; + } + + return 0; +} + int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_free_ char *pty_name = NULL, *getty = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; _cleanup_close_ int master = -1; + sd_bus *container_bus = NULL; Machine *m = userdata; - const char *p; - char *address; + const char *p, *getty; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.machine1.login", + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -480,11 +592,11 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; - r = ptsname_malloc(master, &pty_name); + r = ptsname_namespace(master, &pty_name); if (r < 0) return r; @@ -492,42 +604,246 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (!p) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); - if (unlockpt(master) < 0) - return -errno; + r = container_bus_new(m, error, &allocated_bus); + if (r < 0) + return r; + + container_bus = allocated_bus ?: m->manager->bus; + + getty = strjoina("container-getty@", p, ".service"); - r = sd_bus_new(&container_bus); + r = sd_bus_call_method( + container_bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, NULL, + "ss", getty, "replace"); if (r < 0) return r; -# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI - if (asprintf(&address, ADDRESS_FMT, m->leader) < 0) - return log_oom(); + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; - container_bus->address = address; - container_bus->bus_client = true; - container_bus->trusted = false; - container_bus->is_system = true; + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} - r = sd_bus_start(container_bus); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; + sd_bus *container_bus = NULL; + _cleanup_close_ int master = -1; + _cleanup_strv_free_ char **env = NULL, **args = NULL; + Machine *m = userdata; + const char *p, *unit, *user, *path, *description, *utmp_id; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ss", &user, &path); if (r < 0) return r; + if (isempty(user)) + user = NULL; + if (isempty(path)) + path = "/bin/sh"; + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); + + r = sd_bus_message_read_strv(message, &args); + if (r < 0) + return r; + if (strv_isempty(args)) { + args = strv_free(args); - getty = strjoin("container-getty@", p, ".service", NULL); - if (!getty) - return log_oom(); + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; - r = sd_bus_call_method( + args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + } + + r = sd_bus_message_read_strv(message, &env); + if (r < 0) + return r; + if (!strv_env_is_valid(env)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_namespace(master, &pty_name); + if (r < 0) + return r; + + p = path_startswith(pty_name, "/dev/pts/"); + if (!p) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); + + utmp_id = path_startswith(pty_name, "/dev/"); + assert(utmp_id); + + r = container_bus_new(m, error, &allocated_bus); + if (r < 0) + return r; + + container_bus = allocated_bus ?: m->manager->bus; + + r = sd_bus_message_new_method_call( container_bus, + &tm, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "StartUnit", - error, NULL, - "ss", getty, "replace"); + "StartTransientUnit"); + if (r < 0) + return r; + + /* Name and mode */ + unit = strjoina("container-shell@", p, ".service", NULL); + r = sd_bus_message_append(tm, "ss", unit, "fail"); if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); + /* Properties */ + r = sd_bus_message_open_container(tm, 'a', "(sv)"); + if (r < 0) + return r; + + description = strjoina("Shell for User ", isempty(user) ? "root" : user); + r = sd_bus_message_append(tm, + "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", + "Description", "s", description, + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_name, + "SendSIGHUP", "b", true, + "IgnoreSIGPIPE", "b", false, + "KillMode", "s", "mixed", + "TTYVHangup", "b", true, + "TTYReset", "b", true, + "UtmpIdentifier", "s", utmp_id, + "UtmpMode", "s", "user", + "PAMName", "s", "login", + "WorkingDirectory", "s", "-~"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user); + if (r < 0) + return r; + + if (!strv_isempty(env)) { + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, env); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + } + + /* Exec container */ + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, args); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "b", true); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + /* Auxiliary units */ + r = sd_bus_message_append(tm, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(container_bus, tm, 0, error, NULL); + if (r < 0) + return r; r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -575,6 +891,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -724,11 +1041,11 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu r = wait_for_terminate(child, &si); if (r < 0) { - r = sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); goto finish; } if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); goto finish; } if (si.si_status != EXIT_SUCCESS) { @@ -736,7 +1053,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m"); else - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client failed."); + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed."); goto finish; } @@ -772,7 +1089,7 @@ static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void o->pid = 0; if (si->si_code != CLD_EXITED) { - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); goto fail; } @@ -780,7 +1097,7 @@ static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void 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, "Client failed."); + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); goto fail; } @@ -825,18 +1142,19 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro if (r < 0) return r; - if (!path_is_absolute(src) || !path_is_safe(src)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../."); + if (!path_is_absolute(src)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute."); if (isempty(dest)) dest = src; - else if (!path_is_absolute(dest) || !path_is_safe(dest)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../."); + else if (!path_is_absolute(dest)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute."); r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -966,8 +1284,9 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0), + SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index d309131860..38b46ad936 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -35,6 +35,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index 05fc4f849f..b52ecd015c 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -37,12 +37,17 @@ #include "machine-dbus.h" #include "formats-util.h" -Machine* machine_new(Manager *manager, const char *name) { +Machine* machine_new(Manager *manager, MachineClass class, const char *name) { Machine *m; assert(manager); + assert(class < _MACHINE_CLASS_MAX); assert(name); + /* Passing class == _MACHINE_CLASS_INVALID here is fine. It + * means as much as "we don't know yet", and that we'll figure + * it out later when loading the state file. */ + m = new0(Machine, 1); if (!m) return NULL; @@ -51,14 +56,17 @@ Machine* machine_new(Manager *manager, const char *name) { if (!m->name) goto fail; - m->state_file = strappend("/run/systemd/machines/", m->name); - if (!m->state_file) - goto fail; + if (class != MACHINE_HOST) { + m->state_file = strappend("/run/systemd/machines/", m->name); + if (!m->state_file) + goto fail; + } + + m->class = class; if (hashmap_put(manager->machines, m->name, m) < 0) goto fail; - m->class = _MACHINE_CLASS_INVALID; m->manager = manager; return m; @@ -86,6 +94,9 @@ void machine_free(Machine *m) { (void) hashmap_remove(m->manager->machines, m->name); + if (m->manager->host_machine == m) + m->manager->host_machine = NULL; + if (m->leader > 0) (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m); @@ -105,20 +116,22 @@ int machine_save(Machine *m) { int r; assert(m); - assert(m->state_file); + + if (!m->state_file) + return 0; if (!m->started) return 0; r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0); if (r < 0) - goto finish; + goto fail; r = fopen_temporary(m->state_file, &f, &temp_path); if (r < 0) - goto finish; + goto fail; - fchmod(fileno(f), 0644); + (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n" @@ -131,7 +144,7 @@ int machine_save(Machine *m) { escaped = cescape(m->unit); if (!escaped) { r = -ENOMEM; - goto finish; + goto fail; } fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */ @@ -146,7 +159,7 @@ int machine_save(Machine *m) { escaped = cescape(m->service); if (!escaped) { r = -ENOMEM; - goto finish; + goto fail; } fprintf(f, "SERVICE=%s\n", escaped); } @@ -157,7 +170,7 @@ int machine_save(Machine *m) { escaped = cescape(m->root_directory); if (!escaped) { r = -ENOMEM; - goto finish; + goto fail; } fprintf(f, "ROOT=%s\n", escaped); } @@ -195,16 +208,13 @@ int machine_save(Machine *m) { r = fflush_and_check(f); if (r < 0) - goto finish; + goto fail; if (rename(temp_path, m->state_file) < 0) { r = -errno; - goto finish; + goto fail; } - free(temp_path); - temp_path = NULL; - if (m->unit) { char *sl; @@ -215,14 +225,15 @@ int machine_save(Machine *m) { (void) symlink(m->name, sl); } -finish: - if (temp_path) - unlink(temp_path); + return 0; - if (r < 0) - log_error_errno(r, "Failed to save machine data %s: %m", m->state_file); +fail: + (void) unlink(m->state_file); - return r; + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file); } static void machine_unlink(Machine *m) { @@ -233,11 +244,11 @@ static void machine_unlink(Machine *m) { char *sl; sl = strjoina("/run/systemd/machines/unit:", m->unit); - unlink(sl); + (void) unlink(sl); } if (m->state_file) - unlink(m->state_file); + (void) unlink(m->state_file); } int machine_load(Machine *m) { @@ -246,6 +257,9 @@ int machine_load(Machine *m) { assert(m); + if (!m->state_file) + return 0; + r = parse_env_file(m->state_file, NEWLINE, "SCOPE", &m->unit, "SCOPE_JOB", &m->scope_job, @@ -327,6 +341,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er int r = 0; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) { _cleanup_free_ char *escaped = NULL; @@ -366,6 +381,9 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + if (m->started) return 0; @@ -404,6 +422,7 @@ static int machine_stop_scope(Machine *m) { int r; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) return 0; @@ -421,7 +440,22 @@ static int machine_stop_scope(Machine *m) { } int machine_stop(Machine *m) { - int r = 0, k; + int r; + assert(m); + + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + + r = machine_stop_scope(m); + + m->stopping = true; + + machine_save(m); + + return r; +} + +int machine_finalize(Machine *m) { assert(m); if (m->started) @@ -432,25 +466,23 @@ int machine_stop(Machine *m) { LOG_MESSAGE("Machine %s terminated.", m->name), NULL); - /* Kill cgroup */ - k = machine_stop_scope(m); - if (k < 0) - r = k; - machine_unlink(m); machine_add_to_gc_queue(m); - if (m->started) + if (m->started) { machine_send_signal(m, false); + m->started = false; + } - m->started = false; - - return r; + return 0; } bool machine_check_gc(Machine *m, bool drop_not_started) { assert(m); + if (m->class == MACHINE_HOST) + return true; + if (drop_not_started && !m->started) return false; @@ -476,8 +508,14 @@ void machine_add_to_gc_queue(Machine *m) { MachineState machine_get_state(Machine *s) { assert(s); + if (s->class == MACHINE_HOST) + return MACHINE_RUNNING; + + if (s->stopping) + return MACHINE_CLOSING; + if (s->scope_job) - return s->started ? MACHINE_OPENING : MACHINE_CLOSING; + return MACHINE_OPENING; return MACHINE_RUNNING; } @@ -485,6 +523,9 @@ MachineState machine_get_state(Machine *s) { int machine_kill(Machine *m, KillWho who, int signo) { assert(m); + if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER)) + return -EOPNOTSUPP; + if (!m->unit) return -ESRCH; @@ -501,6 +542,35 @@ int machine_kill(Machine *m, KillWho who, int signo) { return manager_kill_unit(m->manager, m->unit, signo, NULL); } +int machine_openpt(Machine *m, int flags) { + assert(m); + + switch (m->class) { + + case MACHINE_HOST: { + int fd; + + fd = posix_openpt(flags); + if (fd < 0) + return -errno; + + if (unlockpt(fd) < 0) + return -errno; + + return fd; + } + + case MACHINE_CONTAINER: + if (m->leader <= 0) + return -EINVAL; + + return openpt_in_namespace(m->leader, flags); + + default: + return -EOPNOTSUPP; + } +} + MachineOperation *machine_operation_unref(MachineOperation *o) { if (!o) return NULL; @@ -530,13 +600,13 @@ void machine_release_unit(Machine *m) { return; (void) hashmap_remove(m->manager->machine_units, m->unit); - free(m->unit); - m->unit = NULL; + m->unit = mfree(m->unit); } static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", - [MACHINE_VM] = "vm" + [MACHINE_VM] = "vm", + [MACHINE_HOST] = "host", }; DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass); diff --git a/src/machine/machine.h b/src/machine/machine.h index bbe5217f65..5f978289f2 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -39,6 +39,7 @@ typedef enum MachineState { typedef enum MachineClass { MACHINE_CONTAINER, MACHINE_VM, + MACHINE_HOST, _MACHINE_CLASS_MAX, _MACHINE_CLASS_INVALID = -1 } MachineClass; @@ -67,7 +68,6 @@ struct Machine { char *name; sd_id128_t id; - MachineState state; MachineClass class; char *state_file; @@ -83,6 +83,7 @@ struct Machine { bool in_gc_queue:1; bool started:1; + bool stopping:1; sd_bus_message *create_message; @@ -95,12 +96,13 @@ struct Machine { unsigned n_operations; }; -Machine* machine_new(Manager *manager, const char *name); +Machine* machine_new(Manager *manager, MachineClass class, const char *name); void machine_free(Machine *m); bool machine_check_gc(Machine *m, bool drop_not_started); void machine_add_to_gc_queue(Machine *m); int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error); int machine_stop(Machine *m); +int machine_finalize(Machine *m); int machine_save(Machine *m); int machine_load(Machine *m); int machine_kill(Machine *m, KillWho who, int signo); @@ -119,3 +121,5 @@ MachineState machine_state_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; + +int machine_openpt(Machine *m, int flags); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 7cb6ce77ac..17a186eb0e 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -19,42 +19,44 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/socket.h> -#include <unistd.h> +#include <arpa/inet.h> #include <errno.h> -#include <string.h> +#include <fcntl.h> #include <getopt.h> #include <locale.h> -#include <fcntl.h> -#include <netinet/in.h> -#include <arpa/inet.h> #include <net/if.h> +#include <netinet/in.h> +#include <string.h> #include <sys/mount.h> +#include <sys/socket.h> +#include <unistd.h> #include "sd-bus.h" -#include "log.h" -#include "util.h" -#include "macro.h" -#include "pager.h" -#include "spawn-polkit-agent.h" -#include "bus-util.h" + #include "bus-error.h" -#include "build.h" -#include "strv.h" -#include "unit-name.h" +#include "bus-util.h" #include "cgroup-show.h" -#include "logs-show.h" #include "cgroup-util.h" -#include "ptyfwd.h" -#include "event-util.h" -#include "path-util.h" -#include "mkdir.h" #include "copy.h" -#include "verbs.h" +#include "env-util.h" +#include "event-util.h" +#include "hostname-util.h" #include "import-util.h" +#include "log.h" +#include "logs-show.h" +#include "macro.h" +#include "mkdir.h" +#include "pager.h" +#include "path-util.h" #include "process-util.h" -#include "terminal-util.h" +#include "ptyfwd.h" #include "signal-util.h" +#include "spawn-polkit-agent.h" +#include "strv.h" +#include "terminal-util.h" +#include "unit-name.h" +#include "util.h" +#include "verbs.h" static char **arg_property = NULL; static bool arg_all = false; @@ -75,6 +77,8 @@ static bool arg_force = false; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static const char* arg_dkr_index_url = NULL; static const char* arg_format = NULL; +static const char *arg_uid = NULL; +static char **arg_setenv = NULL; static void pager_open_if_enabled(void) { @@ -154,6 +158,9 @@ static int list_machines(int argc, char *argv[], void *userdata) { while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { size_t l; + if (name[0] == '.' && !arg_all) + continue; + if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) return log_oom(); @@ -320,7 +327,7 @@ static int list_images(int argc, char *argv[], void *userdata) { printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n", (int) max_name, images[j].name, (int) max_type, images[j].type, - images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "", + images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "", (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)), (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)), (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime))); @@ -354,7 +361,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", + unit_dbus_interface_from_name(unit), "ControlGroup", &error, &reply, @@ -368,10 +375,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (r < 0) return bus_log_parse_error(r); - if (isempty(cgroup)) - return 0; - - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; c = columns(); @@ -593,7 +597,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { printf("\t Unit: %s\n", i->unit); show_unit_cgroup(bus, i->unit, i->leader); - if (arg_transport == BUS_TRANSPORT_LOCAL) { + if (arg_transport == BUS_TRANSPORT_LOCAL) show_journal_by_unit( stdout, @@ -607,7 +611,6 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { SD_JOURNAL_LOCAL_ONLY, true, NULL); - } } } @@ -790,7 +793,7 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) { printf("\t RO: %s%s%s\n", i->read_only ? ansi_highlight_red() : "", i->read_only ? "read-only" : "writable", - i->read_only ? ansi_highlight_off() : ""); + i->read_only ? ansi_normal() : ""); s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime); s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime); @@ -1073,6 +1076,8 @@ static int terminate_machine(int argc, char *argv[], void *userdata) { static int copy_files(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *abs_host_path = NULL; + char *dest, *host_path, *container_path; sd_bus *bus = userdata; bool copy_from; int r; @@ -1082,6 +1087,16 @@ static int copy_files(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); copy_from = streq(argv[0], "copy-from"); + dest = argv[3] ?: argv[2]; + host_path = copy_from ? dest : argv[2]; + container_path = copy_from ? argv[2] : dest; + + if (!path_is_absolute(host_path)) { + abs_host_path = path_make_absolute_cwd(host_path); + if (!abs_host_path) + return log_oom(); + host_path = abs_host_path; + } r = sd_bus_call_method( bus, @@ -1093,8 +1108,8 @@ static int copy_files(int argc, char *argv[], void *userdata) { NULL, "sss", argv[1], - argv[2], - argv[3]); + copy_from ? container_path : host_path, + copy_from ? host_path : container_path); if (r < 0) { log_error("Failed to copy: %s", bus_error_message(&error, -r)); return r; @@ -1158,20 +1173,72 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r return 0; } +static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) { + char last_char = 0; + bool machine_died; + int ret = 0, r; + + assert(event); + assert(master >= 0); + assert(name); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + if (streq(name, ".host")) + log_info("Connected to the local host. Press ^] three times within 1s to exit session."); + else + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + r = pty_forward_new(event, master, ignore_vhangup, false, forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(*forward, &last_char); + + machine_died = + ignore_vhangup && + pty_forward_get_ignore_vhangup(*forward) == 0; + + *forward = pty_forward_free(*forward); + + if (last_char != '\n') + fputc('\n', stdout); + + if (machine_died) + log_info("Machine %s terminated.", name); + else if (streq(name, ".host")) + log_info("Connection to the local host terminated."); + else + log_info("Connection to machine %s terminated.", name); + + sd_event_get_exit_code(event, &ret); + return ret; +} + static int login_machine(int argc, char *argv[], void *userdata) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; _cleanup_event_unref_ sd_event *event = NULL; - int master = -1, r, ret = 0; + int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match; - char last_char = 0; - bool machine_died; + const char *pty, *match, *machine; assert(bus); + if (!strv_isempty(arg_setenv) || arg_uid) { + log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead."); + return -EINVAL; + } + if (arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) { log_error("Login only supported on local machines."); @@ -1188,14 +1255,14 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); + machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + match = strjoina("type='signal'," - "sender='org.freedesktop.machine1'," - "path='/org/freedesktop/machine1',", - "interface='org.freedesktop.machine1.Manager'," - "member='MachineRemoved'," - "arg0='", - argv[1], - "'"); + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); if (r < 0) @@ -1209,9 +1276,9 @@ static int login_machine(int argc, char *argv[], void *userdata) { "OpenMachineLogin", &error, &reply, - "s", argv[1]); + "s", machine); if (r < 0) { - log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); + log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); return r; } @@ -1219,36 +1286,111 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + return process_forward(event, &forward, master, true, machine); +} - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]); +static int shell_machine(int argc, char *argv[], void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_event_unref_ sd_event *event = NULL; + int master = -1, r; + sd_bus *bus = userdata; + const char *pty, *match, *machine, *path, *uid = NULL; - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + assert(bus); - r = pty_forward_new(event, master, true, false, &forward); + if (arg_transport != BUS_TRANSPORT_LOCAL && + arg_transport != BUS_TRANSPORT_MACHINE) { + log_error("Shell only supported on local machines."); + return -EOPNOTSUPP; + } + + /* Pass $TERM to shell session, if not explicitly specified. */ + if (!strv_find_prefix(arg_setenv, "TERM=")) { + const char *t; + + t = strv_find_prefix(environ, "TERM="); + if (t) { + if (strv_extend(&arg_setenv, t) < 0) + return log_oom(); + } + } + + polkit_agent_open_if_enabled(); + + r = sd_event_default(&event); if (r < 0) - return log_error_errno(r, "Failed to create PTY forwarder: %m"); + return log_error_errno(r, "Failed to get event loop: %m"); - r = sd_event_loop(event); + r = sd_bus_attach_event(bus, event, 0); if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); + return log_error_errno(r, "Failed to attach bus to event loop: %m"); - pty_forward_get_last_char(forward, &last_char); - machine_died = pty_forward_get_ignore_vhangup(forward) == 0; + machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; - forward = pty_forward_free(forward); + if (arg_uid) + uid = arg_uid; + else if (machine) { + const char *at; - if (last_char != '\n') - fputc('\n', stdout); + at = strchr(machine, '@'); + if (at) { + uid = strndupa(machine, at - machine); + machine = at + 1; + } + } - if (machine_died) - log_info("Machine %s terminated.", argv[1]); - else - log_info("Connection to machine %s terminated.", argv[1]); + if (isempty(machine)) + machine = ".host"; - sd_event_get_exit_code(event, &ret); - return ret; + match = strjoina("type='signal'," + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); + + r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + if (r < 0) + return log_error_errno(r, "Failed to add machine removal match: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineShell"); + if (r < 0) + return bus_log_create_error(r); + + path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; + + r = sd_bus_message_append(m, "sss", machine, uid, path); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_setenv); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "hs", &master, &pty); + if (r < 0) + return bus_log_parse_error(r); + + return process_forward(event, &forward, master, false, machine); } static int remove_image(int argc, char *argv[], void *userdata) { @@ -2243,13 +2385,9 @@ static int set_limit(int argc, char *argv[], void *userdata) { if (streq(argv[argc-1], "-")) limit = (uint64_t) -1; else { - off_t off; - - r = parse_size(argv[argc-1], 1024, &off); + r = parse_size(argv[argc-1], 1024, &limit); if (r < 0) return log_error("Failed to parse size: %s", argv[argc-1]); - - limit = (uint64_t) off; } if (argc > 2) @@ -2302,6 +2440,8 @@ static int help(int argc, char *argv[], void *userdata) { " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" + " --uid=USER Specify user ID to invoke shell as\n" + " --setenv=VAR=VALUE Add an environment variable for shell\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" @@ -2316,9 +2456,13 @@ static int help(int argc, char *argv[], void *userdata) { "Machine Commands:\n" " list List running VMs and containers\n" " status NAME... Show VM/container details\n" - " show NAME... Show properties of one or more VMs/containers\n" + " show [NAME...] Show properties of one or more VMs/containers\n" " start NAME... Start container as a service\n" - " login NAME Get a login prompt on a container\n" + " login [NAME] Get a login prompt in a container or on the\n" + " local host\n" + " shell [[USER@]NAME [COMMAND...]]\n" + " Invoke a shell (or other command) in a container\n" + " or on the local host\n" " enable NAME... Enable automatic container start at boot\n" " disable NAME... Disable automatic container start at boot\n" " poweroff NAME... Power off one or more containers\n" @@ -2330,8 +2474,8 @@ static int help(int argc, char *argv[], void *userdata) { " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n" "Image Commands:\n" " list-images Show available container and VM images\n" - " image-status NAME... Show image details\n" - " show-image NAME... Show properties of image\n" + " image-status [NAME...] Show image details\n" + " show-image [NAME...] Show properties of image\n" " clone NAME NAME Clone an image\n" " rename NAME NAME Rename an image\n" " read-only NAME [BOOL] Mark or unmark image read-only\n" @@ -2366,6 +2510,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_DKR_INDEX_URL, ARG_FORMAT, + ARG_UID, + ARG_SETENV, }; static const struct option options[] = { @@ -2390,6 +2536,8 @@ static int parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, ARG_FORCE }, { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL }, { "format", required_argument, NULL, ARG_FORMAT }, + { "uid", required_argument, NULL, ARG_UID }, + { "setenv", required_argument, NULL, ARG_SETENV }, {} }; @@ -2406,9 +2554,7 @@ static int parse_argv(int argc, char *argv[]) { return help(0, NULL, NULL); case ARG_VERSION: - puts(PACKAGE_STRING); - puts(SYSTEMD_FEATURES); - return 0; + return version(); case 'p': r = strv_extend(&arg_property, optarg); @@ -2520,6 +2666,21 @@ static int parse_argv(int argc, char *argv[]) { arg_format = optarg; break; + case ARG_UID: + arg_uid = optarg; + break; + + case ARG_SETENV: + if (!env_assignment_is_valid(optarg)) { + log_error("Environment assignment invalid: %s", optarg); + return -EINVAL; + } + + r = strv_extend(&arg_setenv, optarg); + if (r < 0) + return log_oom(); + break; + case '?': return -EINVAL; @@ -2544,7 +2705,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "reboot", 2, VERB_ANY, 0, reboot_machine }, { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, { "kill", 2, VERB_ANY, 0, kill_machine }, - { "login", 2, 2, 0, login_machine }, + { "login", VERB_ANY, 2, 0, login_machine }, + { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, { "bind", 3, 4, 0, bind_mount }, { "copy-to", 3, 4, 0, copy_files }, { "copy-from", 3, 4, 0, copy_files }, @@ -2583,7 +2745,7 @@ int main(int argc, char*argv[]) { if (r <= 0) goto finish; - r = bus_open_transport(arg_transport, arg_host, false, &bus); + r = bus_connect_transport(arg_transport, arg_host, false, &bus); if (r < 0) { log_error_errno(r, "Failed to create bus connection: %m"); goto finish; @@ -2598,6 +2760,7 @@ finish: polkit_agent_close(); strv_free(arg_property); + strv_free(arg_setenv); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 0e971a6789..41bb106d28 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -31,12 +31,14 @@ #include "bus-common-errors.h" #include "cgroup-util.h" #include "btrfs-util.h" +#include "formats-util.h" +#include "process-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machine-pool.h" #include "image-dbus.h" #include "machined.h" #include "machine-dbus.h" -#include "formats-util.h" static int property_get_pool_path( sd_bus *bus, @@ -636,6 +638,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd return bus_machine_method_open_login(message, machine, error); } +static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_shell(message, machine, error); +} + static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; Machine *machine; @@ -787,6 +810,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -840,6 +864,242 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu return bus_image_method_set_limit(message, i, error); } +static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + const char *name, *p; + Machine *machine; + uint32_t uid; + int r; + + r = sd_bus_message_read(message, "su", &name, &uid); + if (r < 0) + return r; + + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + + p = procfs_file_alloca(machine->leader, "uid_map"); + f = fopen(p, "re"); + if (!f) + return -errno; + + for (;;) { + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno != 0) + return -errno; + + return -EIO; + } + + if (uid < uid_base || uid >= uid_base + uid_range) + continue; + + converted = uid - uid_base + uid_shift; + if (!uid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); +} + +static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + uid_t uid; + Iterator i; + int r; + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + if (uid < 0x10000) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid); + + HASHMAP_FOREACH(machine, m->machines, i) { + _cleanup_fclose_ FILE *f = NULL; + char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + + if (machine->class != MACHINE_CONTAINER) + continue; + + xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); + f = fopen(p, "re"); + if (!f) { + log_warning_errno(errno, "Failed top open %s, ignoring,", p); + continue; + } + + for (;;) { + _cleanup_free_ char *o = NULL; + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno != 0) + return -errno; + + return -EIO; + } + + if (uid < uid_shift || uid >= uid_shift + uid_range) + continue; + + converted = (uid - uid_shift + uid_base); + if (!uid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); + } + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); +} + +static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { + _cleanup_fclose_ FILE *f = NULL; + Manager *m = groupdata; + const char *name, *p; + Machine *machine; + uint32_t gid; + int r; + + r = sd_bus_message_read(message, "su", &name, &gid); + if (r < 0) + return r; + + if (!gid_is_valid(gid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + + p = procfs_file_alloca(machine->leader, "gid_map"); + f = fopen(p, "re"); + if (!f) + return -errno; + + for (;;) { + gid_t gid_base, gid_shift, gid_range, converted; + int k; + + errno = 0; + k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno != 0) + return -errno; + + return -EIO; + } + + if (gid < gid_base || gid >= gid_base + gid_range) + continue; + + converted = gid - gid_base + gid_shift; + if (!gid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name); +} + +static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { + Manager *m = groupdata; + Machine *machine; + gid_t gid; + Iterator i; + int r; + + r = sd_bus_message_read(message, "u", &gid); + if (r < 0) + return r; + if (!gid_is_valid(gid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + if (gid < 0x10000) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid); + + HASHMAP_FOREACH(machine, m->machines, i) { + _cleanup_fclose_ FILE *f = NULL; + char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + + if (machine->class != MACHINE_CONTAINER) + continue; + + xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); + f = fopen(p, "re"); + if (!f) { + log_warning_errno(errno, "Failed top open %s, ignoring,", p); + continue; + } + + for (;;) { + _cleanup_free_ char *o = NULL; + gid_t gid_base, gid_shift, gid_range, converted; + int k; + + errno = 0; + k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno != 0) + return -errno; + + return -EIO; + } + + if (gid < gid_shift || gid >= gid_shift + gid_range) + continue; + + converted = (gid - gid_shift + gid_base); + if (!gid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); + } + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); +} + const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0), @@ -860,6 +1120,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), @@ -869,6 +1130,10 @@ 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("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), + SD_BUS_METHOD("MapToMachineGroup", "u", "sou", method_map_to_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("MachineNew", "so", 0), SD_BUS_SIGNAL("MachineRemoved", "so", 0), SD_BUS_VTABLE_END @@ -887,7 +1152,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); if (r < 0) { bus_log_parse_error(r); - return r; + return 0; } machine = hashmap_get(m->machine_units, unit); @@ -895,8 +1160,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err return 0; if (streq_ptr(path, machine->scope_job)) { - free(machine->scope_job); - machine->scope_job = NULL; + machine->scope_job = mfree(machine->scope_job); if (machine->started) { if (streq(result, "done")) @@ -908,8 +1172,9 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err machine_send_create_reply(machine, &e); } - } else - machine_save(machine); + } + + machine_save(machine); } machine_add_to_gc_queue(machine); @@ -918,7 +1183,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *unit = NULL; - const char *path, *interface; + const char *path; Manager *m = userdata; Machine *machine; int r; @@ -942,36 +1207,6 @@ int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_err if (!machine) return 0; - r = sd_bus_message_read(message, "s", &interface); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (streq(interface, "org.freedesktop.systemd1.Unit")) { - struct properties { - char *active_state; - char *sub_state; - } properties = {}; - - const struct bus_properties_map map[] = { - { "ActiveState", "s", NULL, offsetof(struct properties, active_state) }, - { "SubState", "s", NULL, offsetof(struct properties, sub_state) }, - {} - }; - - r = bus_message_map_properties_changed(message, map, &properties); - if (r < 0) - bus_log_parse_error(r); - else if (streq_ptr(properties.active_state, "inactive") || - streq_ptr(properties.active_state, "failed") || - streq_ptr(properties.sub_state, "auto-restart")) - machine_release_unit(machine); - - free(properties.active_state); - free(properties.sub_state); - } - machine_add_to_gc_queue(machine); return 0; } @@ -995,9 +1230,7 @@ int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *er if (!machine) return 0; - machine_release_unit(machine); machine_add_to_gc_queue(machine); - return 0; } @@ -1013,7 +1246,7 @@ int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error r = sd_bus_message_read(message, "b", &b); if (r < 0) { bus_log_parse_error(r); - return r; + return 0; } if (b) return 0; @@ -1258,7 +1491,6 @@ int manager_job_is_active(Manager *manager, const char *path) { } int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { - _cleanup_free_ char *unit = NULL; Machine *mm; int r; @@ -1266,12 +1498,14 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { assert(pid >= 1); assert(machine); - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); - else - mm = hashmap_get(m->machine_units, unit); + mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); + if (!mm) { + _cleanup_free_ char *unit = NULL; + r = cg_pid_get_unit(pid, &unit); + if (r >= 0) + mm = hashmap_get(m->machine_units, unit); + } if (!mm) return 0; @@ -1287,7 +1521,7 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) { machine = hashmap_get(m->machines, name); if (!machine) { - machine = machine_new(m, name); + machine = machine_new(m, _MACHINE_CLASS_INVALID, name); if (!machine) return -ENOMEM; } diff --git a/src/machine/machined.c b/src/machine/machined.c index 9bfe2add54..df3cc9972a 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -30,6 +30,7 @@ #include "label.h" #include "formats-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machined.h" @@ -89,6 +90,45 @@ void manager_free(Manager *m) { free(m); } +static int manager_add_host_machine(Manager *m) { + _cleanup_free_ char *rd = NULL, *unit = NULL; + sd_id128_t mid; + Machine *t; + int r; + + if (m->host_machine) + return 0; + + r = sd_id128_get_machine(&mid); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + rd = strdup("/"); + if (!rd) + return log_oom(); + + unit = strdup("-.slice"); + if (!unit) + return log_oom(); + + t = machine_new(m, MACHINE_HOST, ".host"); + if (!t) + return log_oom(); + + t->leader = 1; + t->id = mid; + + t->root_directory = rd; + t->unit = unit; + rd = unit = NULL; + + dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); + + m->host_machine = t; + + return 0; +} + int manager_enumerate_machines(Manager *m) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -96,6 +136,10 @@ int manager_enumerate_machines(Manager *m) { assert(m); + r = manager_add_host_machine(m); + if (r < 0) + return r; + /* Read in machine data stored on disk */ d = opendir("/run/systemd/machines"); if (!d) { @@ -117,11 +161,12 @@ int manager_enumerate_machines(Manager *m) { if (startswith(de->d_name, "unit:")) continue; + if (!machine_name_is_valid(de->d_name)) + continue; + k = manager_add_machine(m, de->d_name, &machine); if (k < 0) { - log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); - - r = k; + r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); continue; } @@ -247,8 +292,16 @@ void manager_gc(Manager *m, bool drop_not_started) { LIST_REMOVE(gc_queue, m->machine_gc_queue, machine); machine->in_gc_queue = false; - if (!machine_check_gc(machine, drop_not_started)) { + /* First, if we are not closing yet, initiate stopping */ + if (!machine_check_gc(machine, drop_not_started) && + machine_get_state(machine) != MACHINE_CLOSING) machine_stop(machine); + + /* Now, the stop 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)) { + machine_finalize(machine); machine_free(machine); } } @@ -347,8 +400,7 @@ int main(int argc, char *argv[]) { log_debug("systemd-machined stopped as pid "PID_FMT, getpid()); finish: - if (m) - manager_free(m); + manager_free(m); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/machine/machined.h b/src/machine/machined.h index 61dbefb5f1..b3e59bf998 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -48,6 +48,8 @@ struct Manager { sd_event_source *image_cache_defer_event; LIST_HEAD(Machine, machine_gc_queue); + + Machine *host_machine; }; Manager *manager_new(void); diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index 93aaf6a377..9d40b90151 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -70,6 +70,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Manager" + send_member="OpenMachineShell"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" send_member="TerminateMachine"/> <allow send_destination="org.freedesktop.machine1" @@ -113,6 +117,22 @@ send_member="SetImageLimit"/> <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" + send_member="MapFromMachineUser"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" + send_member="MapToMachineUser"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" + send_member="MapFromMachineGroup"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" + send_member="MapToMachineGroup"/> + + <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Machine" send_member="GetAddresses"/> @@ -126,6 +146,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Machine" + send_member="OpenShell"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Machine" send_member="Terminate"/> <allow send_destination="org.freedesktop.machine1" diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index 02714e83ae..69f78a5c25 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,58 @@ </defaults> </action> + <action id="org.freedesktop.machine1.host-login"> + <_description>Log into the local host</_description> + <_message>Authentication is required to log into the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.machine1.shell"> + <_description>Acquire a shell in a local container</_description> + <_message>Authentication is required to acquire a shell in a local container.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.login</annotate> + </action> + + <action id="org.freedesktop.machine1.host-shell"> + <_description>Acquire a shell on the local host</_description> + <_message>Authentication is required to acquire a shell on the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.host-login</annotate> + </action> + + <action id="org.freedesktop.machine1.open-pty"> + <_description>Acquire a pseudo TTY in a local container</_description> + <_message>Authentication is required to acquire a pseudo TTY in a local container.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.machine1.host-open-pty"> + <_description>Acquire a pseudo TTY on the local host</_description> + <_message>Authentication is required to acquire a pseudo TTY on the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + <action id="org.freedesktop.machine1.manage-machines"> <_description>Manage local virtual machines and containers</_description> <_message>Authentication is required to manage local virtual machines and containers.</_message> @@ -34,6 +86,7 @@ <allow_inactive>auth_admin</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.shell org.freedesktop.login1.login</annotate> </action> <action id="org.freedesktop.machine1.manage-images"> |