Merge pull request #8620 from poettering/portablectl

an implementation of the "portable services" concept
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2018-05-25 10:48:12 +02:00
committed by GitHub
98 changed files with 6332 additions and 716 deletions
+86 -42
View File
@@ -21,18 +21,28 @@
#include "macro.h"
#include "missing.h"
#include "path-util.h"
#include "set.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "util.h"
static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) {
static int files_add(
Hashmap *h,
Set *masked,
const char *suffix,
const char *root,
unsigned flags,
const char *path) {
_cleanup_closedir_ DIR *dir = NULL;
const char *dirpath;
struct dirent *de;
int r;
assert(h);
assert((flags & CONF_FILES_FILTER_MASKED) == 0 || masked);
assert(path);
dirpath = prefix_roota(root, path);
@@ -41,62 +51,89 @@ static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned
if (!dir) {
if (errno == ENOENT)
return 0;
return -errno;
return log_debug_errno(errno, "Failed to open directory '%s': %m", dirpath);
}
FOREACH_DIRENT(de, dir, return -errno) {
char *p;
struct stat st;
char *p, *key;
if (!dirent_is_file_with_suffix(de, suffix)) {
log_debug("Ignoring %s/%s, because it's not a regular file with suffix %s.", dirpath, de->d_name, strna(suffix));
/* Does this match the suffix? */
if (suffix && !endswith(de->d_name, suffix))
continue;
/* Has this file already been found in an earlier directory? */
if (hashmap_contains(h, de->d_name)) {
log_debug("Skipping overridden file '%s/%s'.", dirpath, de->d_name);
continue;
}
if (flags & CONF_FILES_EXECUTABLE) {
struct stat st;
/* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK)
* here, as we care about whether the file is marked executable at all, and not whether it is
* executable for us, because if such errors are stuff we should log about. */
/* Has this been masked in an earlier directory? */
if ((flags & CONF_FILES_FILTER_MASKED) && set_contains(masked, de->d_name)) {
log_debug("File '%s/%s' is masked by previous entry.", dirpath, de->d_name);
continue;
}
/* Read file metadata if we shall validate the check for file masks, for node types or whether the node is marked executable. */
if (flags & (CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR|CONF_FILES_DIRECTORY|CONF_FILES_EXECUTABLE))
if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) {
log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", dirpath, de->d_name);
log_debug_errno(errno, "Failed to stat '%s/%s', ignoring: %m", dirpath, de->d_name);
continue;
}
if (!null_or_empty(&st)) {
/* A mask is a symlink to /dev/null or an empty file. It does not even
* have to be executable. Other entries must be regular executable files
* or symlinks to them. */
if (S_ISREG(st.st_mode)) {
if ((st.st_mode & 0111) == 0) { /* not executable */
log_debug("Ignoring %s/%s, as it is not marked executable.",
dirpath, de->d_name);
continue;
}
} else {
log_debug("Ignoring %s/%s, as it is neither a regular file nor a mask.",
dirpath, de->d_name);
continue;
}
/* Is this a masking entry? */
if ((flags & CONF_FILES_FILTER_MASKED))
if (null_or_empty(&st)) {
/* Mark this one as masked */
r = set_put_strdup(masked, de->d_name);
if (r < 0)
return r;
log_debug("File '%s/%s' is a mask.", dirpath, de->d_name);
continue;
}
/* Does this node have the right type? */
if (flags & (CONF_FILES_REGULAR|CONF_FILES_DIRECTORY))
if (!((flags & CONF_FILES_DIRECTORY) && S_ISDIR(st.st_mode)) &&
!((flags & CONF_FILES_REGULAR) && S_ISREG(st.st_mode))) {
log_debug("Ignoring '%s/%s', as it is not a of the right type.", dirpath, de->d_name);
continue;
}
/* Does this node have the executable bit set? */
if (flags & CONF_FILES_EXECUTABLE)
/* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK)
* here, as we care about whether the file is marked executable at all, and not whether it is
* executable for us, because if so, such errors are stuff we should log about. */
if ((st.st_mode & 0111) == 0) { /* not executable */
log_debug("Ignoring '%s/%s', as it is not marked executable.", dirpath, de->d_name);
continue;
}
if (flags & CONF_FILES_BASENAME) {
p = strdup(de->d_name);
if (!p)
return -ENOMEM;
key = p;
} else {
p = strjoin(dirpath, "/", de->d_name);
if (!p)
return -ENOMEM;
key = basename(p);
}
p = strjoin(dirpath, "/", de->d_name);
if (!p)
return -ENOMEM;
r = hashmap_put(h, basename(p), p);
if (r == -EEXIST) {
log_debug("Skipping overridden file: %s.", p);
free(p);
} else if (r < 0) {
free(p);
return r;
} else if (r == 0) {
log_debug("Duplicate file %s", p);
r = hashmap_put(h, key, p);
if (r < 0) {
free(p);
return log_debug_errno(r, "Failed to add item to hashmap: %m");
}
assert(r > 0);
}
return 0;
@@ -112,6 +149,7 @@ static int base_cmp(const void *a, const void *b) {
static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, unsigned flags, char **dirs) {
_cleanup_hashmap_free_ Hashmap *fh = NULL;
_cleanup_set_free_free_ Set *masked = NULL;
char **files, **p;
int r;
@@ -121,12 +159,18 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
if (!path_strv_resolve_uniq(dirs, root))
return -ENOMEM;
fh = hashmap_new(&string_hash_ops);
fh = hashmap_new(&path_hash_ops);
if (!fh)
return -ENOMEM;
if (flags & CONF_FILES_FILTER_MASKED) {
masked = set_new(&path_hash_ops);
if (!masked)
return -ENOMEM;
}
STRV_FOREACH(p, dirs) {
r = files_add(fh, suffix, root, flags, *p);
r = files_add(fh, masked, suffix, root, flags, *p);
if (r == -ENOMEM)
return r;
if (r < 0)
+5 -1
View File
@@ -9,7 +9,11 @@
***/
enum {
CONF_FILES_EXECUTABLE = 1,
CONF_FILES_EXECUTABLE = 1U << 0,
CONF_FILES_REGULAR = 1U << 1,
CONF_FILES_DIRECTORY = 1U << 2,
CONF_FILES_BASENAME = 1U << 3,
CONF_FILES_FILTER_MASKED = 1U << 4,
};
int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...);
+1 -1
View File
@@ -89,7 +89,7 @@ static int do_execute(
* If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
*/
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
if (r < 0)
return r;
+199
View File
@@ -12,10 +12,13 @@
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "copy.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
#include "memfd-util.h"
#include "missing.h"
@@ -562,6 +565,202 @@ try_dev_shm_without_o_tmpfile:
return -EOPNOTSUPP;
}
/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
#define DATA_FD_MEMORY_LIMIT (64U*1024U)
/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
#define DATA_FD_TMP_LIMIT (1024U*1024U)
int fd_duplicate_data_fd(int fd) {
_cleanup_close_ int copy_fd = -1, tmp_fd = -1;
_cleanup_free_ void *remains = NULL;
_cleanup_free_ char *t = NULL;
size_t remains_size = 0;
const char *td;
struct stat st;
int r;
/* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
* independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
* somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
* uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
* /var/tmp. */
if (fstat(fd, &st) < 0)
return -errno;
/* For now, let's only accept regular files, sockets, pipes and char devices */
if (S_ISDIR(st.st_mode))
return -EISDIR;
if (S_ISLNK(st.st_mode))
return -ELOOP;
if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
return -EBADFD;
/* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
* that we use the reported regular file size only as a hint, given that there are plenty special files in
* /proc and /sys which report a zero file size but can be read from. */
if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
/* Try a memfd first */
copy_fd = memfd_new("data-fd");
if (copy_fd >= 0) {
off_t f;
r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
if (r < 0)
return r;
f = lseek(copy_fd, 0, SEEK_SET);
if (f != 0)
return -errno;
if (r == 0) {
/* Did it fit into the limit? If so, we are done. */
r = memfd_set_sealed(copy_fd);
if (r < 0)
return r;
return TAKE_FD(copy_fd);
}
/* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
} else {
_cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
int isz;
/* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
* then block indefinitely when we hit the pipe size limit */
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
return -errno;
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
/* Try to enlarge the pipe size if necessary */
if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
(void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
}
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size);
if (r < 0 && r != -EAGAIN)
return r; /* If we get EAGAIN it could be because of the source or because of
* the destination fd, we can't know, as sendfile() and friends won't
* tell us. Hence, treat this as reason to fall back, just to be
* sure. */
if (r == 0) {
/* Everything fit in, yay! */
(void) fd_nonblock(pipefds[0], false);
return TAKE_FD(pipefds[0]);
}
/* Things didn't fit in. But we read data into the pipe, let's remember that, so that
* when writing the new file we incorporate this first. */
copy_fd = TAKE_FD(pipefds[0]);
}
}
}
/* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
(DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
off_t f;
tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
if (tmp_fd < 0)
return tmp_fd;
if (copy_fd >= 0) {
/* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
* temporary file first. */
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
if (r < 0)
return r;
assert(r == 0);
}
if (remains_size > 0) {
/* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
* failed copy operation, let's flush them out next. */
r = loop_write(tmp_fd, remains, remains_size, false);
if (r < 0)
return r;
}
r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
if (r < 0)
return r;
if (r == 0)
goto finish; /* Yay, it fit in */
/* It didn't fit in. Let's not forget to use what we already used */
f = lseek(tmp_fd, 0, SEEK_SET);
if (f != 0)
return -errno;
safe_close(copy_fd);
copy_fd = TAKE_FD(tmp_fd);
remains = mfree(remains);
remains_size = 0;
}
/* As last fallback use /var/tmp */
r = var_tmp_dir(&td);
if (r < 0)
return r;
tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
if (tmp_fd < 0)
return tmp_fd;
if (copy_fd >= 0) {
/* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
* into the temporary file first. */
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
if (r < 0)
return r;
assert(r == 0);
}
if (remains_size > 0) {
/* Then, copy in any read but not yet written bytes. */
r = loop_write(tmp_fd, remains, remains_size, false);
if (r < 0)
return r;
}
/* Copy in the rest */
r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
if (r < 0)
return r;
assert(r == 0);
finish:
/* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
* file again */
return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
}
int fd_move_above_stdio(int fd) {
int flags, copy;
PROTECT_ERRNO;
+2
View File
@@ -81,6 +81,8 @@ enum {
int acquire_data_fd(const void *data, size_t size, unsigned flags);
int fd_duplicate_data_fd(int fd);
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
+44 -18
View File
@@ -263,29 +263,35 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
}
int read_full_stream(FILE *f, char **contents, size_t *size) {
size_t n, l;
_cleanup_free_ char *buf = NULL;
struct stat st;
size_t n, l;
int fd;
assert(f);
assert(contents);
if (fstat(fileno(f), &st) < 0)
return -errno;
n = LINE_MAX;
if (S_ISREG(st.st_mode)) {
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
* optimize our buffering) */
/* Safety check */
if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
if (fstat(fileno(f), &st) < 0)
return -errno;
/* Start with the right file size, but be prepared for files from /proc which generally report a file
* size of 0. Note that we increase the size to read here by one, so that the first read attempt
* already makes us notice the EOF. */
if (st.st_size > 0)
n = st.st_size + 1;
if (S_ISREG(st.st_mode)) {
/* Safety check */
if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for files from /proc which generally report a file
* size of 0. Note that we increase the size to read here by one, so that the first read attempt
* already makes us notice the EOF. */
if (st.st_size > 0)
n = st.st_size + 1;
}
}
l = 0;
@@ -676,21 +682,41 @@ static int parse_env_file_push(
return 0;
}
int parse_env_file(
int parse_env_filev(
FILE *f,
const char *fname,
const char *newline, ...) {
const char *newline,
va_list ap) {
va_list ap;
int r, n_pushed = 0;
va_list aq;
if (!newline)
newline = NEWLINE;
va_copy(aq, ap);
r = parse_env_file_internal(f, fname, newline, parse_env_file_push, &aq, &n_pushed);
va_end(aq);
if (r < 0)
return r;
return n_pushed;
}
int parse_env_file(
FILE *f,
const char *fname,
const char *newline,
...) {
va_list ap;
int r;
va_start(ap, newline);
r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
r = parse_env_filev(f, fname, newline, ap);
va_end(ap);
return r < 0 ? r : n_pushed;
return r;
}
static int load_env_file_push(
+2 -1
View File
@@ -45,7 +45,8 @@ int read_full_stream(FILE *f, char **contents, size_t *size);
int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int parse_env_filev(FILE *f, const char *fname, const char *separator, va_list ap);
int parse_env_file(FILE *f, const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+2
View File
@@ -131,6 +131,8 @@ basic_sources = files('''
ordered-set.h
pager.c
pager.h
os-util.c
os-util.h
parse-util.c
parse-util.h
path-util.c
+117
View File
@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "os-util.h"
#include "strv.h"
#include "fileio.h"
#include "string-util.h"
int path_is_os_tree(const char *path) {
int r;
assert(path);
/* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
* always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
* the case where just the os-release file is missing. */
if (laccess(path, F_OK) < 0)
return -errno;
/* We use {/etc|/usr/lib}/os-release as flag file if something is an OS */
r = open_os_release(path, NULL, NULL);
if (r == -ENOENT) /* We got nothing */
return 0;
if (r < 0)
return r;
return 1;
}
int open_os_release(const char *root, char **ret_path, int *ret_fd) {
_cleanup_free_ char *q = NULL;
const char *p;
int k;
FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
k = chase_symlinks(p, root, CHASE_PREFIX_ROOT|(ret_fd ? CHASE_OPEN : 0), (ret_path ? &q : NULL));
if (k != -ENOENT)
break;
}
if (k < 0)
return k;
if (ret_fd) {
int real_fd;
/* Convert the O_PATH fd into a proper, readable one */
real_fd = fd_reopen(k, O_RDONLY|O_CLOEXEC|O_NOCTTY);
safe_close(k);
if (real_fd < 0)
return real_fd;
*ret_fd = real_fd;
}
if (ret_path)
*ret_path = TAKE_PTR(q);
return 0;
}
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
FILE *f;
int r;
if (!ret_file)
return open_os_release(root, ret_path, NULL);
r = open_os_release(root, ret_path ? &p : NULL, &fd);
if (r < 0)
return r;
f = fdopen(fd, "re");
if (!f)
return -errno;
fd = -1;
*ret_file = f;
if (ret_path)
*ret_path = TAKE_PTR(p);
return 0;
}
int parse_os_release(const char *root, ...) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
va_list ap;
int r;
r = fopen_os_release(root, &p, &f);
if (r < 0)
return r;
va_start(ap, root);
r = parse_env_filev(f, p, NEWLINE, ap);
va_end(ap);
return r;
}
int load_os_release_pairs(const char *root, char ***ret) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
r = fopen_os_release(root, &p, &f);
if (r < 0)
return r;
return load_env_file_pairs(f, p, NEWLINE, ret);
}
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdio.h>
int path_is_os_tree(const char *path);
int open_os_release(const char *root, char **ret_path, int *ret_fd);
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file);
int parse_os_release(const char *root, ...);
int load_os_release_pairs(const char *root, char ***ret);
+11
View File
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/types.h>
@@ -1344,6 +1345,16 @@ int safe_fork_full(
}
}
if ((flags & (FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE)) == (FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE)) {
/* Optionally, make sure we never propagate mounts to the host. */
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m");
_exit(EXIT_FAILURE);
}
}
if (flags & FORK_CLOSE_ALL_FDS) {
/* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
log_close();
+1
View File
@@ -163,6 +163,7 @@ typedef enum ForkFlags {
FORK_LOG = 1U << 5,
FORK_WAIT = 1U << 6,
FORK_NEW_MOUNTNS = 1U << 7,
FORK_MOUNTNS_SLAVE = 1U << 8,
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
-26
View File
@@ -132,32 +132,6 @@ int path_is_read_only_fs(const char *path) {
return false;
}
int path_is_os_tree(const char *path) {
int r;
assert(path);
/* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
* always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
* the case where just the os-release file is missing. */
if (laccess(path, F_OK) < 0)
return -errno;
/* We use /usr/lib/os-release as flag file if something is an OS */
r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
if (r == -ENOENT) {
/* Also check for the old location in /etc, just in case. */
r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
if (r == -ENOENT)
return 0; /* We got nothing */
}
if (r < 0)
return r;
return 1;
}
int files_same(const char *filea, const char *fileb, int flags) {
struct stat a, b;
-1
View File
@@ -35,7 +35,6 @@ int null_or_empty_path(const char *fn);
int null_or_empty_fd(int fd);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
int files_same(const char *filea, const char *fileb, int flags);
+1 -1
View File
@@ -264,7 +264,7 @@ int container_get_leader(const char *machine, pid_t *pid) {
return -EINVAL;
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
r = parse_env_file(NULL, p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
+1
View File
@@ -26,6 +26,7 @@
#include "fs-util.h"
#include "install.h"
#include "log.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "selinux-access.h"
+2 -2
View File
@@ -24,7 +24,7 @@ int locale_setup(char ***environment) {
int r = 0, i;
if (detect_container() <= 0) {
r = parse_env_file("/proc/cmdline", WHITESPACE,
r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
"locale.LANG", &variables[VARIABLE_LANG],
"locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
"locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
@@ -48,7 +48,7 @@ int locale_setup(char ***environment) {
/* Hmm, nothing set on the kernel cmd line? Then let's
* try /etc/locale.conf */
if (r <= 0) {
r = parse_env_file("/etc/locale.conf", NEWLINE,
r = parse_env_file(NULL, "/etc/locale.conf", NEWLINE,
"LANG", &variables[VARIABLE_LANG],
"LANGUAGE", &variables[VARIABLE_LANGUAGE],
"LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+8 -12
View File
@@ -57,6 +57,7 @@
#include "manager.h"
#include "missing.h"
#include "mount-setup.h"
#include "os-util.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
@@ -1215,23 +1216,18 @@ static int enforce_syscall_archs(Set *archs) {
static int status_welcome(void) {
_cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
const char *fn;
int r;
if (arg_show_status <= 0)
return 0;
FOREACH_STRING(fn, "/etc/os-release", "/usr/lib/os-release") {
r = parse_env_file(fn, NEWLINE,
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
if (r != -ENOENT)
break;
}
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
r = parse_os_release(NULL,
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
if (r < 0)
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to read os-release file, ignoring: %m");
if (log_get_show_color())
return status_printf(NULL, false, false,
+5 -15
View File
@@ -33,6 +33,7 @@
#include "hostname-util.h"
#include "locale-util.h"
#include "mkdir.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
@@ -79,27 +80,16 @@ static bool press_any_key(void) {
static void print_welcome(void) {
_cleanup_free_ char *pretty_name = NULL;
const char *os_release = NULL;
static bool done = false;
int r;
if (done)
return;
os_release = prefix_roota(arg_root, "/etc/os-release");
r = parse_env_file(os_release, NEWLINE,
"PRETTY_NAME", &pretty_name,
NULL);
if (r == -ENOENT) {
os_release = prefix_roota(arg_root, "/usr/lib/os-release");
r = parse_env_file(os_release, NEWLINE,
"PRETTY_NAME", &pretty_name,
NULL);
}
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read os-release file: %m");
r = parse_os_release(arg_root, "PRETTY_NAME", &pretty_name, NULL);
if (r < 0)
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to read os-release file, ignoring: %m");
printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n",
isempty(pretty_name) ? "Linux" : pretty_name);
+7 -13
View File
@@ -16,6 +16,7 @@
#include "env-util.h"
#include "fileio-label.h"
#include "hostname-util.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "selinux-util.h"
@@ -88,7 +89,7 @@ static int context_read_data(Context *c) {
if (r < 0 && r != -ENOENT)
return r;
r = parse_env_file("/etc/machine-info", NEWLINE,
r = parse_env_file(NULL, "/etc/machine-info", NEWLINE,
"PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
"ICON_NAME", &c->data[PROP_ICON_NAME],
"CHASSIS", &c->data[PROP_CHASSIS],
@@ -98,18 +99,11 @@ static int context_read_data(Context *c) {
if (r < 0 && r != -ENOENT)
return r;
r = parse_env_file("/etc/os-release", NEWLINE,
"PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
"CPE_NAME", &c->data[PROP_OS_CPE_NAME],
"HOME_URL", &c->data[PROP_HOME_URL],
NULL);
if (r == -ENOENT)
r = parse_env_file("/usr/lib/os-release", NEWLINE,
"PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
"CPE_NAME", &c->data[PROP_OS_CPE_NAME],
"HOME_URL", &c->data[PROP_HOME_URL],
NULL);
r = parse_os_release(NULL,
"PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
"CPE_NAME", &c->data[PROP_OS_CPE_NAME],
"HOME_URL", &c->data[PROP_HOME_URL],
NULL);
if (r < 0 && r != -ENOENT)
return r;
+6 -10
View File
@@ -69,13 +69,11 @@ static int export_tar(int argc, char *argv[], void *userdata) {
int r, fd;
if (machine_name_is_valid(argv[1])) {
r = image_find(argv[1], &image);
r = image_find(IMAGE_MACHINE, argv[1], &image);
if (r == -ENOENT)
return log_error_errno(r, "Machine image %s not found.", argv[1]);
if (r < 0)
return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
if (r == 0) {
log_error("Machine image %s not found.", argv[1]);
return -ENOENT;
}
local = image->path;
} else
@@ -148,13 +146,11 @@ static int export_raw(int argc, char *argv[], void *userdata) {
int r, fd;
if (machine_name_is_valid(argv[1])) {
r = image_find(argv[1], &image);
r = image_find(IMAGE_MACHINE, argv[1], &image);
if (r == -ENOENT)
return log_error_errno(r, "Machine image %s not found.", argv[1]);
if (r < 0)
return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
if (r == 0) {
log_error("Machine image %s not found.", argv[1]);
return -ENOENT;
}
local = image->path;
} else
+10 -8
View File
@@ -75,10 +75,11 @@ static int import_tar(int argc, char *argv[], void *userdata) {
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
r = image_find(IMAGE_MACHINE, local, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
} else {
log_error("Image '%s' already exists.", local);
return -EEXIST;
}
@@ -170,10 +171,11 @@ static int import_raw(int argc, char *argv[], void *userdata) {
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
r = image_find(IMAGE_MACHINE, local, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
} else {
log_error("Image '%s' already exists.", local);
return -EEXIST;
}
+10 -8
View File
@@ -83,10 +83,11 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
r = image_find(IMAGE_MACHINE, local, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
} else {
log_error("Image '%s' already exists.", local);
return -EEXIST;
}
@@ -169,10 +170,11 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
r = image_find(IMAGE_MACHINE, local, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
} else {
log_error("Image '%s' already exists.", local);
return -EEXIST;
}
+3 -4
View File
@@ -24,6 +24,7 @@
#include "log.h"
#include "logs-show.h"
#include "microhttpd-util.h"
#include "os-util.h"
#include "parse-util.h"
#include "sigbus.h"
#include "util.h"
@@ -777,10 +778,8 @@ static int request_handler_machine(
if (r < 0)
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT)
(void) parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
get_virtualization(&v);
(void) parse_os_release(NULL, "PRETTY_NAME", &os_name, NULL);
(void) get_virtualization(&v);
r = asprintf(&json,
"{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\","
+1 -1
View File
@@ -152,7 +152,7 @@ static int load_cursor_state(Uploader *u) {
if (!u->state_file)
return 0;
r = parse_env_file(u->state_file, NEWLINE,
r = parse_env_file(NULL, u->state_file, NEWLINE,
"LAST_CURSOR", &u->last_cursor,
NULL);
+1 -1
View File
@@ -646,7 +646,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
return log_oom();
}
r = parse_env_file(stream->state_file, NEWLINE,
r = parse_env_file(NULL, stream->state_file, NEWLINE,
"PRIORITY", &priority,
"LEVEL_PREFIX", &level_prefix,
"FORWARD_TO_SYSLOG", &forward_to_syslog,
+1 -1
View File
@@ -1894,7 +1894,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
assert_return(machine_name_is_valid(machine), -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
r = parse_env_file(NULL, p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
+1 -1
View File
@@ -1034,7 +1034,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (r < 0)
return r;
r = parse_env_file(lease_file, NEWLINE,
r = parse_env_file(NULL, lease_file, NEWLINE,
"ADDRESS", &address,
"ROUTER", &router,
"NETMASK", &netmask,
@@ -41,6 +41,8 @@
#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping"
#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping"
#define BUS_ERROR_NO_SUCH_PORTABLE_IMAGE "org.freedesktop.portable1.NoSuchImage"
#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession"
#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID"
#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser"
+14 -14
View File
@@ -273,7 +273,7 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, "STATE", &s, NULL);
if (r == -ENOENT) {
free(s);
s = strdup("offline");
@@ -304,7 +304,7 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, "DISPLAY", &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -359,7 +359,7 @@ _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat)
variable = require_active ? "ACTIVE_UID" : "UIDS";
r = parse_env_file(p, NEWLINE, variable, &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, variable, &s, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -388,7 +388,7 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, variable, &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, variable, &s, NULL);
if (r == -ENOENT || (r >= 0 && isempty(s))) {
if (array)
*array = NULL;
@@ -466,7 +466,7 @@ _public_ int sd_session_is_active(const char *session) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, "ACTIVE", &s, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -485,7 +485,7 @@ _public_ int sd_session_is_remote(const char *session) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, "REMOTE", &s, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -506,7 +506,7 @@ _public_ int sd_session_get_state(const char *session, char **state) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, "STATE", &s, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -529,7 +529,7 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, "UID", &s, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -551,7 +551,7 @@ static int session_get_string(const char *session, const char *field, char **val
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, field, &s, NULL);
r = parse_env_file(NULL, p, NEWLINE, field, &s, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -643,7 +643,7 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE,
r = parse_env_file(NULL, p, NEWLINE,
"ACTIVE", &s,
"ACTIVE_UID", &t,
NULL);
@@ -681,7 +681,7 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE,
r = parse_env_file(NULL, p, NEWLINE,
"SESSIONS", &s,
"UIDS", &t,
NULL);
@@ -750,7 +750,7 @@ static int seat_get_can(const char *seat, const char *variable) {
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE,
r = parse_env_file(NULL, p, NEWLINE,
variable, &s,
NULL);
if (r == -ENOENT)
@@ -899,7 +899,7 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
assert_return(class, -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL);
r = parse_env_file(NULL, p, NEWLINE, "CLASS", &c, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -923,7 +923,7 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
assert_return(ifindices, -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL);
r = parse_env_file(NULL, p, NEWLINE, "NETIF", &netif, NULL);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
+5 -5
View File
@@ -30,7 +30,7 @@ _public_ int sd_network_get_operational_state(char **state) {
assert_return(state, -EINVAL);
r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL);
r = parse_env_file(NULL, "/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -50,7 +50,7 @@ static int network_get_strv(const char *key, char ***ret) {
assert_return(ret, -EINVAL);
r = parse_env_file("/run/systemd/netif/state", NEWLINE, key, &s, NULL);
r = parse_env_file(NULL, "/run/systemd/netif/state", NEWLINE, key, &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -98,7 +98,7 @@ static int network_link_get_string(int ifindex, const char *field, char **ret) {
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
r = parse_env_file(path, NEWLINE, field, &s, NULL);
r = parse_env_file(NULL, path, NEWLINE, field, &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -121,7 +121,7 @@ static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
assert_return(ret, -EINVAL);
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
r = parse_env_file(path, NEWLINE, key, &s, NULL);
r = parse_env_file(NULL, path, NEWLINE, key, &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -218,7 +218,7 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
assert_return(ret, -EINVAL);
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
r = parse_env_file(path, NEWLINE, key, &s, NULL);
r = parse_env_file(NULL, path, NEWLINE, key, &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
+2 -2
View File
@@ -93,7 +93,7 @@ static int locale_read_data(Context *c) {
context_free_locale(c);
r = parse_env_file("/etc/locale.conf", NEWLINE,
r = parse_env_file(NULL, "/etc/locale.conf", NEWLINE,
"LANG", &c->locale[VARIABLE_LANG],
"LANGUAGE", &c->locale[VARIABLE_LANGUAGE],
"LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE],
@@ -137,7 +137,7 @@ static int vconsole_read_data(Context *c) {
context_free_vconsole(c);
r = parse_env_file("/etc/vconsole.conf", NEWLINE,
r = parse_env_file(NULL, "/etc/vconsole.conf", NEWLINE,
"KEYMAP", &c->vc_keymap,
"KEYMAP_TOGGLE", &c->vc_keymap_toggle,
NULL);
+1 -1
View File
@@ -61,7 +61,7 @@ static void print_overridden_variables(void) {
if (detect_container() > 0 || arg_host)
return;
r = parse_env_file("/proc/cmdline", WHITESPACE,
r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
"locale.LANG", &variables[VARIABLE_LANG],
"locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
"locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+1 -1
View File
@@ -198,7 +198,7 @@ int inhibitor_load(Inhibitor *i) {
char *cc;
int r;
r = parse_env_file(i->state_file, NEWLINE,
r = parse_env_file(NULL, i->state_file, NEWLINE,
"WHAT", &what,
"UID", &uid,
"PID", &pid,
+1 -1
View File
@@ -365,7 +365,7 @@ int session_load(Session *s) {
assert(s);
r = parse_env_file(s->state_file, NEWLINE,
r = parse_env_file(NULL, s->state_file, NEWLINE,
"REMOTE", &remote,
"SCOPE", &s->scope,
"SCOPE_JOB", &s->scope_job,
+1 -1
View File
@@ -287,7 +287,7 @@ int user_load(User *u) {
assert(u);
r = parse_env_file(u->state_file, NEWLINE,
r = parse_env_file(NULL, u->state_file, NEWLINE,
"SERVICE_JOB", &u->service_job,
"SLICE_JOB", &u->slice_job,
"DISPLAY", &display,
+5 -3
View File
@@ -435,8 +435,10 @@ int image_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r < 0)
return r;
r = image_find(e, &image);
if (r <= 0)
r = image_find(IMAGE_MACHINE, e, &image);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
image->userdata = m;
@@ -478,7 +480,7 @@ int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char **
if (!images)
return -ENOMEM;
r = image_discover(images);
r = image_discover(IMAGE_MACHINE, images);
if (r < 0)
return r;
+6 -8
View File
@@ -32,6 +32,7 @@
#include "machine-dbus.h"
#include "machine.h"
#include "mkdir.h"
#include "os-util.h"
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
@@ -330,7 +331,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
switch (m->class) {
case MACHINE_HOST:
r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l);
r = load_os_release_pairs(NULL, &l);
if (r < 0)
return r;
@@ -361,13 +362,10 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (r < 0)
_exit(EXIT_FAILURE);
fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0 && errno == ENOENT) {
fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0 && errno == ENOENT)
_exit(EXIT_NOT_FOUND);
}
if (fd < 0)
r = open_os_release(NULL, NULL, &fd);
if (r == -ENOENT)
_exit(EXIT_NOT_FOUND);
if (r < 0)
_exit(EXIT_FAILURE);
r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
-2
View File
@@ -31,5 +31,3 @@ int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
int bus_reply_pair_array(sd_bus_message *m, char **l);
+1 -1
View File
@@ -255,7 +255,7 @@ int machine_load(Machine *m) {
if (!m->state_file)
return 0;
r = parse_env_file(m->state_file, NEWLINE,
r = parse_env_file(NULL, m->state_file, NEWLINE,
"SCOPE", &m->unit,
"SCOPE_JOB", &m->scope_job,
"SERVICE", &m->service,
+45 -383
View File
@@ -145,8 +145,8 @@ static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_erro
if (r < 0)
return r;
r = image_find(name, NULL);
if (r == 0)
r = image_find(IMAGE_MACHINE, name, NULL);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
if (r < 0)
return r;
@@ -450,14 +450,14 @@ static int method_register_machine(sd_bus_message *message, void *userdata, sd_b
return method_register_machine_internal(message, false, userdata, error);
}
static int method_terminate_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
static int redirect_method_to_machine(sd_bus_message *message, Manager *m, sd_bus_error *error, sd_bus_message_handler_t method) {
Machine *machine;
const char *name;
int r;
assert(message);
assert(m);
assert(method);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
@@ -467,67 +467,23 @@ static int method_terminate_machine(sd_bus_message *message, void *userdata, sd_
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
return bus_machine_method_terminate(message, machine, error);
return method(message, machine, error);
}
static int method_terminate_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_machine(message, userdata, error, bus_machine_method_terminate);
}
static int method_kill_machine(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 sd_bus_error_set_errno(error, 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_kill(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_kill);
}
static int method_get_machine_addresses(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 sd_bus_error_set_errno(error, 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_get_addresses(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_get_addresses);
}
static int method_get_machine_os_release(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 sd_bus_error_set_errno(error, 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_get_os_release(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_get_os_release);
}
static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -545,7 +501,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
if (!images)
return -ENOMEM;
r = image_discover(images);
r = image_discover(IMAGE_MACHINE, images);
if (r < 0)
return r;
@@ -584,336 +540,89 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
}
static int method_open_machine_pty(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 sd_bus_error_set_errno(error, 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_pty(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_open_pty);
}
static int method_open_machine_login(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_login(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_open_login);
}
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);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_open_shell);
}
static int method_bind_mount_machine(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_bind_mount(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_bind_mount);
}
static int method_copy_machine(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_copy(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_copy);
}
static int method_open_machine_root_directory(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_root_directory(message, machine, error);
return redirect_method_to_machine(message, userdata, error, bus_machine_method_open_root_directory);
}
static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
return redirect_method_to_machine(message, userdata, error, bus_machine_method_get_uid_shift);
}
static int redirect_method_to_image(sd_bus_message *message, Manager *m, sd_bus_error *error, sd_bus_message_handler_t method) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
int r;
assert(message);
assert(m);
assert(method);
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);
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
return bus_machine_method_get_uid_shift(message, machine, error);
r = image_find(IMAGE_MACHINE, name, &i);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
if (r < 0)
return r;
i->userdata = m;
return method(message, i, error);
}
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_remove(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_remove);
}
static int method_rename_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *old_name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &old_name);
if (r < 0)
return r;
if (!image_name_is_valid(old_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
r = image_find(old_name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
i->userdata = userdata;
return bus_image_method_rename(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_rename);
}
static int method_clone_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *old_name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &old_name);
if (r < 0)
return r;
if (!image_name_is_valid(old_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
r = image_find(old_name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
i->userdata = userdata;
return bus_image_method_clone(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_clone);
}
static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_mark_read_only(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_mark_read_only);
}
static int method_get_image_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_hostname(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_get_hostname);
}
static int method_get_image_machine_id(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_machine_id(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_get_machine_id);
}
static int method_get_image_machine_info(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_machine_info(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_get_machine_info);
}
static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_get_os_release(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_get_os_release);
}
static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
@@ -1070,7 +779,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
goto child_fail;
}
r = image_discover(images);
r = image_discover(IMAGE_MACHINE, images);
if (r < 0)
goto child_fail;
@@ -1198,27 +907,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
}
static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image *i = NULL;
const char *name;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
r = image_find(name, &i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
i->userdata = userdata;
return bus_image_method_set_limit(message, i, error);
return redirect_method_to_image(message, userdata, error, bus_image_method_set_limit);
}
static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1899,30 +1588,3 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
return 0;
}
int bus_reply_pair_array(sd_bus_message *m, char **l) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
char **k, **v;
int r;
r = sd_bus_message_new_method_return(m, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "{ss}");
if (r < 0)
return r;
STRV_FOREACH_PAIR(k, v, l) {
r = sd_bus_message_append(reply, "{ss}", *k, *v);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
+1 -1
View File
@@ -2866,7 +2866,7 @@ static int link_load(Link *link) {
assert(link);
r = parse_env_file(link->state_file, NEWLINE,
r = parse_env_file(NULL, link->state_file, NEWLINE,
"NETWORK_FILE", &network_file,
"ADDRESSES", &addresses,
"ROUTES", &routes,
+1 -1
View File
@@ -249,7 +249,7 @@ static int link_send_lldp(Link *link) {
return r;
(void) gethostname_strict(&hostname);
(void) parse_env_file("/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL);
(void) parse_env_file(NULL, "/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL);
assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC);
ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC);
+7 -6
View File
@@ -77,6 +77,7 @@
#include "nspawn-settings.h"
#include "nspawn-setuid.h"
#include "nspawn-stub-pid1.h"
#include "os-util.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
@@ -2398,13 +2399,11 @@ static int determine_names(void) {
if (arg_machine) {
_cleanup_(image_unrefp) Image *i = NULL;
r = image_find(arg_machine, &i);
r = image_find(IMAGE_MACHINE, arg_machine, &i);
if (r == -ENOENT)
return log_error_errno(r, "No image for machine '%s'.", arg_machine);
if (r < 0)
return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine);
if (r == 0) {
log_error("No image for machine '%s'.", arg_machine);
return -ENOENT;
}
if (IN_SET(i->type, IMAGE_RAW, IMAGE_BLOCK))
r = free_and_strdup(&arg_image, i->path);
@@ -2883,7 +2882,9 @@ static int outer_child(
* makes sure ESP partitions and userns are compatible. */
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0)|
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
if (r < 0)
return r;
}
+29
View File
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: LGPL-2.1+
systemd_portabled_sources = files('''
portable.c
portable.h
portabled-bus.c
portabled-image-bus.c
portabled-image-bus.h
portabled-image.c
portabled-image.h
portabled-operation.c
portabled-operation.h
portabled.c
portabled.h
'''.split())
if conf.get('ENABLE_PORTABLED') == 1
install_data('org.freedesktop.portable1.conf',
install_dir : dbuspolicydir)
install_data('org.freedesktop.portable1.service',
install_dir : dbussystemservicedir)
install_data('org.freedesktop.portable1.policy',
install_dir : polkitpolicydir)
install_data('profile/default/service.conf', install_dir : join_paths(profiledir, 'default'))
install_data('profile/nonetwork/service.conf', install_dir : join_paths(profiledir, 'nonetwork'))
install_data('profile/strict/service.conf', install_dir : join_paths(profiledir, 'strict'))
install_data('profile/trusted/service.conf', install_dir : join_paths(profiledir, 'trusted'))
endif
+117
View File
@@ -0,0 +1,117 @@
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<busconfig>
<policy user="root">
<allow own="org.freedesktop.portable1"/>
<allow send_destination="org.freedesktop.portable1"/>
<allow receive_sender="org.freedesktop.portable1"/>
</policy>
<policy context="default">
<deny send_destination="org.freedesktop.portable1"/>
<!-- generic interfaces -->
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.DBus.Peer"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.DBus.Properties"
send_member="Get"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
<!-- Manager object -->
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="GetImage"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="ListImages"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="GetImageOSRelease"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="GetImageUnitFiles"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="GetImageState"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="AttachImage"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="DetachImage"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="RemoveImage"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="MarkImageReadOnly"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="SetImageLimit"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="SetPoolLimit"/>
<!-- Image object -->
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="GetOSRelease"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="GetUnitFiles"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="GetImageState"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="Attach"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="Detach"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="Remove"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="MarkReadOnly"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="SetLimit"/>
<allow receive_sender="org.freedesktop.portable1"/>
</policy>
</busconfig>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<policyconfig>
<vendor>The systemd Project</vendor>
<vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
<action id="org.freedesktop.portable1.inspect-images">
<description gettext-domain="systemd">Inspect a portable service</description>
<message gettext-domain="systemd">Authentication is required to inspect a portable service.</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.portable1.attach-images">
<description gettext-domain="systemd">Attach or detach a portable service</description>
<message gettext-domain="systemd">Authentication is required to attach or detach a portable service.</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.systemd1.reload-daemon</annotate>
</action>
<action id="org.freedesktop.portable1.manage-images">
<description gettext-domain="systemd">Delete or modify portable service image</description>
<message gettext-domain="systemd">Authentication is required to delete or modify a portable service image.</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1+
[D-BUS Service]
Name=org.freedesktop.portable1
Exec=/bin/false
User=root
SystemdService=dbus-org.freedesktop.portable1.service
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
#include "hashmap.h"
#include "macro.h"
#include "set.h"
#include "string-util.h"
typedef struct PortableMetadata {
int fd;
char *source;
char name[];
} PortableMetadata;
#define PORTABLE_METADATA_IS_OS_RELEASE(m) (streq((m)->name, "/etc/os-release"))
#define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
typedef enum PortableFlags {
PORTABLE_PREFER_COPY = 1U << 0,
PORTABLE_PREFER_SYMLINK = 1U << 1,
PORTABLE_RUNTIME = 1U << 2,
} PortableFlags;
typedef enum PortableChangeType {
PORTABLE_COPY,
PORTABLE_SYMLINK,
PORTABLE_UNLINK,
PORTABLE_WRITE,
PORTABLE_MKDIR,
_PORTABLE_CHANGE_TYPE_MAX,
_PORTABLE_CHANGE_TYPE_INVALID = INT_MIN,
} PortableChangeType;
typedef enum PortableState {
PORTABLE_DETACHED,
PORTABLE_ATTACHED,
PORTABLE_ATTACHED_RUNTIME,
PORTABLE_ENABLED,
PORTABLE_ENABLED_RUNTIME,
PORTABLE_RUNNING,
PORTABLE_RUNNING_RUNTIME,
_PORTABLE_STATE_MAX,
_PORTABLE_STATE_INVALID = -1
} PortableState;
typedef struct PortableChange {
int type; /* PortableFileChangeType or negative error number */
char *path;
char *source;
} PortableChange;
PortableMetadata *portable_metadata_unref(PortableMetadata *i);
DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
Hashmap *portable_metadata_hashmap_unref(Hashmap *h);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, portable_metadata_hashmap_unref);
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
int portable_extract(const char *image, char **matches, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
int portable_detach(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
int portable_get_state(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableState *ret, sd_bus_error *error);
int portable_get_profiles(char ***ret);
void portable_changes_free(PortableChange *changes, size_t n_changes);
const char *portable_change_type_to_string(PortableChangeType t) _const_;
PortableChangeType portable_change_type_from_string(const char *t) _pure_;
const char *portable_state_to_string(PortableState t) _const_;
PortableState portable_state_from_string(const char *t) _pure_;
+965
View File
@@ -0,0 +1,965 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include <getopt.h>
#include "sd-bus.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "def.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-table.h"
#include "fs-util.h"
#include "locale-util.h"
#include "machine-image.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
#include "spawn-polkit-agent.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "verbs.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
static bool arg_ask_password = true;
static bool arg_quiet = false;
static const char *arg_profile = "default";
static const char* arg_copy_mode = NULL;
static bool arg_runtime = false;
static bool arg_reload = true;
static bool arg_cat = false;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static char *arg_host = NULL;
static int determine_image(const char *image, bool permit_non_existing, char **ret) {
int r;
/* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the
* usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side
* (among other things, to make the path independent of the client's working directory) before passing it
* over. */
if (image_name_is_valid(image)) {
char *c;
if (!arg_quiet && laccess(image, F_OK) >= 0)
log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. "
"Prefix argument with './' to force reference to file in current working directory.", image);
c = strdup(image);
if (!c)
return log_oom();
*ret = c;
return 0;
}
if (arg_transport != BUS_TRANSPORT_LOCAL) {
log_error("Operations on images by path not supported when connecting to remote systems.");
return -EOPNOTSUPP;
}
r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret);
if (r < 0)
return log_error_errno(r, "Cannot normalize specified image path '%s': %m", image);
return 0;
}
static int extract_prefix(const char *path, char **ret) {
_cleanup_free_ char *name = NULL;
const char *bn, *underscore;
size_t m;
bn = basename(path);
underscore = strchr(bn, '_');
if (underscore)
m = underscore - bn;
else {
const char *e;
e = endswith(bn, ".raw");
if (!e)
e = strchr(bn, 0);
m = e - bn;
}
name = strndup(bn, m);
if (!name)
return -ENOMEM;
/* A slightly reduced version of what's permitted in unit names. With ':' and '\' are removed, as well as '_'
* which we use as delimiter for the second part of the image string, which we ignore for now. */
if (!in_charset(name, DIGITS LETTERS "-."))
return -EINVAL;
if (!filename_is_valid(name))
return -EINVAL;
*ret = name;
name = NULL;
return 0;
}
static int determine_matches(const char *image, char **l, bool allow_any, char ***ret) {
char **k;
int r;
/* Determine the matches to apply. If the list is empty we derive the match from the image name. If the list
* contains exactly the "-" we return a wildcard list (which is the empty list), but only if this is expressly
* permitted. */
if (strv_isempty(l)) {
char *prefix;
r = extract_prefix(image, &prefix);
if (r < 0)
return log_error_errno(r, "Failed to extract prefix of image name '%s': %m", image);
if (!arg_quiet)
log_info("(Matching unit files with prefix '%s'.)", prefix);
k = NULL;
r = strv_consume(&k, prefix);
if (r < 0)
return log_oom();
} else if (strv_equal(l, STRV_MAKE("-"))) {
if (!allow_any) {
log_error("Refusing all unit file match.");
return -EINVAL;
}
if (!arg_quiet)
log_info("(Matching all unit files.)");
k = NULL;
} else {
_cleanup_free_ char *joined = NULL;
k = strv_copy(l);
if (!k)
return log_oom();
joined = strv_join(k, "', '");
if (!joined)
return log_oom();
if (!arg_quiet)
log_info("(Matching unit files with prefixes '%s'.)", joined);
}
*ret = k;
return 0;
}
static int acquire_bus(sd_bus **bus) {
int r;
assert(bus);
if (*bus)
return 0;
r = bus_connect_transport(arg_transport, arg_host, false, bus);
if (r < 0)
return log_error_errno(r, "Failed to connect to bus: %m");
(void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
return 0;
}
static int maybe_reload(sd_bus **bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
if (!arg_reload)
return 0;
r = acquire_bus(bus);
if (r < 0)
return r;
r = sd_bus_message_new_method_call(
*bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Reload");
if (r < 0)
return bus_log_create_error(r);
/* Reloading the daemon may take long, hence set a longer timeout here */
r = sd_bus_call(*bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL);
if (r < 0)
return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r));
return 0;
}
static int inspect_image(int argc, char *argv[], void *userdata) {
_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;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_strv_free_ char **matches = NULL;
_cleanup_free_ char *image = NULL;
bool nl = false, header = false;
const void *data;
const char *path;
size_t sz;
int r;
r = determine_image(argv[1], false, &image);
if (r < 0)
return r;
r = determine_matches(argv[1], argv + 2, true, &matches);
if (r < 0)
return r;
r = acquire_bus(&bus);
if (r < 0)
return r;
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"GetImageMetadata");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", image);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, matches);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0)
return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &path);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read_array(reply, 'y', &data, &sz);
if (r < 0)
return bus_log_parse_error(r);
(void) pager_open(arg_no_pager, false);
if (arg_cat) {
printf("%s-- OS Release: --%s\n", ansi_highlight(), ansi_normal());
fwrite(data, sz, 1, stdout);
fflush(stdout);
nl = true;
} else {
const char *pretty_portable = NULL, *pretty_os = NULL;
_cleanup_fclose_ FILE *f;
f = fmemopen((void*) data, sz, "re");
if (!f)
return log_error_errno(errno, "Failed to open /etc/os-release buffer: %m");
r = parse_env_file(f, "/etc/os-release", NEWLINE,
"PORTABLE_PRETTY_NAME", &pretty_portable,
"PRETTY_NAME", &pretty_os,
NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
printf("Image:\n\t%s\n"
"Portable Service:\n\t%s\n"
"Operating System:\n\t%s\n",
path,
strna(pretty_portable),
strna(pretty_os));
}
r = sd_bus_message_enter_container(reply, 'a', "{say}");
if (r < 0)
return bus_log_parse_error(r);
for (;;) {
const char *name;
r = sd_bus_message_enter_container(reply, 'e', "say");
if (r < 0)
return bus_log_parse_error(r);
if (r == 0)
break;
r = sd_bus_message_read(reply, "s", &name);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read_array(reply, 'y', &data, &sz);
if (r < 0)
return bus_log_parse_error(r);
if (arg_cat) {
if (nl)
fputc('\n', stdout);
printf("%s-- Unit file: %s --%s\n", ansi_highlight(), name, ansi_normal());
fwrite(data, sz, 1, stdout);
fflush(stdout);
nl = true;
} else {
if (!header) {
fputs("Unit files:\n", stdout);
header = true;
}
fputc('\t', stdout);
fputs(name, stdout);
fputc('\n', stdout);
}
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
}
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
return 0;
}
static int print_changes(sd_bus_message *m) {
int r;
if (arg_quiet)
return 0;
r = sd_bus_message_enter_container(m, 'a', "(sss)");
if (r < 0)
return bus_log_parse_error(r);
for (;;) {
const char *type, *path, *source;
r = sd_bus_message_read(m, "(sss)", &type, &path, &source);
if (r < 0)
return bus_log_parse_error(r);
if (r == 0)
break;
if (streq(type, "symlink"))
log_info("Created symlink %s %s %s.", path, special_glyph(ARROW), source);
else if (streq(type, "copy")) {
if (isempty(source))
log_info("Copied %s.", path);
else
log_info("Copied %s %s %s.", source, special_glyph(ARROW), path);
} else if (streq(type, "unlink"))
log_info("Removed %s.", path);
else if (streq(type, "write"))
log_info("Written %s.", path);
else if (streq(type, "mkdir"))
log_info("Created directory %s.", path);
else
log_error("Unexpected change: %s/%s/%s", type, path, source);
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
static int attach_image(int argc, char *argv[], void *userdata) {
_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;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_strv_free_ char **matches = NULL;
_cleanup_free_ char *image = NULL;
int r;
r = determine_image(argv[1], false, &image);
if (r < 0)
return r;
r = determine_matches(argv[1], argv + 2, false, &matches);
if (r < 0)
return r;
r = acquire_bus(&bus);
if (r < 0)
return r;
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"AttachImage");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", image);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, matches);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "sbs", arg_profile, arg_runtime, arg_copy_mode);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0)
return log_error_errno(r, "Failed to attach image: %s", bus_error_message(&error, r));
(void) maybe_reload(&bus);
print_changes(reply);
return 0;
}
static int detach_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *image = NULL;
int r;
r = determine_image(argv[1], true, &image);
if (r < 0)
return r;
r = acquire_bus(&bus);
if (r < 0)
return r;
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"DetachImage",
&error,
&reply,
"sb", image, arg_runtime);
if (r < 0)
return log_error_errno(r, "Failed to detach image: %s", bus_error_message(&error, r));
(void) maybe_reload(&bus);
print_changes(reply);
return 0;
}
static int list_images(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(table_unrefp) Table *table = NULL;
int r;
r = acquire_bus(&bus);
if (r < 0)
return r;
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"ListImages",
&error,
&reply,
NULL);
if (r < 0)
return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
table = table_new("NAME", "TYPE", "RO", "CRTIME", "MTIME", "USAGE", "STATE");
if (!table)
return log_oom();
r = sd_bus_message_enter_container(reply, 'a', "(ssbtttso)");
if (r < 0)
return bus_log_parse_error(r);
for (;;) {
const char *name, *type, *state, *object;
uint64_t crtime, mtime, usage;
TableCell *cell;
bool ro_bool;
int ro_int;
r = sd_bus_message_read(reply, "(ssbtttso)", &name, &type, &ro_int, &crtime, &mtime, &usage, &state, &object);
if (r < 0)
return bus_log_parse_error(r);
if (r == 0)
break;
r = table_add_many(table,
TABLE_STRING, name,
TABLE_STRING, type);
if (r < 0)
return log_error_errno(r, "Failed to add row to table: %m");
ro_bool = ro_int;
r = table_add_cell(table, &cell, TABLE_BOOLEAN, &ro_bool);
if (r < 0)
return log_error_errno(r, "Failed to add row to table: %m");
if (ro_bool) {
r = table_set_color(table, cell, ansi_highlight_red());
if (r < 0)
return log_error_errno(r, "Failed to set table cell color: %m");
}
r = table_add_many(table,
TABLE_TIMESTAMP, crtime,
TABLE_TIMESTAMP, mtime,
TABLE_SIZE, usage);
if (r < 0)
return log_error_errno(r, "Failed to add row to table: %m");
r = table_add_cell(table, &cell, TABLE_STRING, state);
if (r < 0)
return log_error_errno(r, "Failed to add row to table: %m");
if (!streq(state, "detached")) {
r = table_set_color(table, cell, ansi_highlight_green());
if (r < 0)
return log_error_errno(r, "Failed to set table cell color: %m");
}
}
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
if (table_get_rows(table) > 1) {
r = table_set_sort(table, (size_t) 0, (size_t) -1);
if (r < 0)
return log_error_errno(r, "Failed to sort table: %m");
table_set_header(table, arg_legend);
r = table_print(table, NULL);
if (r < 0)
return log_error_errno(r, "Failed to show table: %m");
}
if (arg_legend) {
if (table_get_rows(table) > 1)
printf("\n%zu images listed.\n", table_get_rows(table) - 1);
else
printf("No images.\n");
}
return 0;
}
static int remove_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r, i;
r = acquire_bus(&bus);
if (r < 0)
return r;
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
for (i = 1; i < argc; i++) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"RemoveImage");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", argv[i]);
if (r < 0)
return bus_log_create_error(r);
/* This is a slow operation, hence turn off any method call timeouts */
r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
if (r < 0)
return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
}
return 0;
}
static int read_only_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int b = true, r;
if (argc > 2) {
b = parse_boolean(argv[2]);
if (b < 0)
return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
}
r = acquire_bus(&bus);
if (r < 0)
return r;
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"MarkImageReadOnly",
&error,
NULL,
"sb", argv[1], b);
if (r < 0)
return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
return 0;
}
static int set_limit(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
uint64_t limit;
int r;
r = acquire_bus(&bus);
if (r < 0)
return r;
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
limit = (uint64_t) -1;
else {
r = parse_size(argv[argc-1], 1024, &limit);
if (r < 0)
return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
}
if (argc > 2)
/* With two arguments changes the quota limit of the specified image */
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"SetImageLimit",
&error,
NULL,
"st", argv[1], limit);
else
/* With one argument changes the pool quota limit */
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"SetPoolLimit",
&error,
NULL,
"t", limit);
if (r < 0)
return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
return 0;
}
static int is_image_attached(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *image = NULL;
const char *state;
int r;
r = determine_image(argv[1], true, &image);
if (r < 0)
return r;
r = acquire_bus(&bus);
if (r < 0)
return r;
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"GetImageState",
&error,
&reply,
"s", image);
if (r < 0)
return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &state);
if (r < 0)
return r;
if (!arg_quiet)
puts(state);
return streq(state, "detached");
}
static int dump_profiles(void) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_strv_free_ char **l = NULL;
_cleanup_(closedirp) DIR *d = NULL;
char **i;
int r;
r = acquire_bus(&bus);
if (r < 0)
return r;
r = sd_bus_get_property_strv(
bus,
"org.freedesktop.portable1",
"/org/freedesktop/portable1",
"org.freedesktop.portable1.Manager",
"Profiles",
&error,
&l);
if (r < 0)
return log_error_errno(r, "Failed to acquire list of profiles: %s", bus_error_message(&error, r));
if (arg_legend)
log_info("Available unit profiles:");
STRV_FOREACH(i, l) {
fputs(*i, stdout);
fputc('\n', stdout);
}
return 0;
}
static int help(int argc, char *argv[], void *userdata) {
(void) pager_open(arg_no_pager, false);
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Attach or detach portable services from the local system.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" --no-ask-password Do not ask for system passwords\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" -q --quiet Suppress informational messages\n"
" -p --profile=PROFILE Pick security profile for portable service\n"
" --copy=copy|auto|symlink Prefer copying or symlinks if possible\n"
" --runtime Attach portable service until next reboot only\n"
" --no-reload Don't reload the system and service manager\n"
" --cat When inspecting include unit and os-release file\n"
" contents\n\n"
"Commands:\n"
" list List available portable service images\n"
" attach NAME|PATH [PREFIX...]\n"
" Attach the specified portable service image\n"
" detach NAME|PATH Detach the specified portable service image\n"
" inspect NAME|PATH [PREFIX...]\n"
" Show details of specified portable service image\n"
" is-attached NAME|PATH Query if portable service image is attached\n"
" read-only NAME|PATH [BOOL] Mark or unmark portable service image read-only\n"
" remove NAME|PATH... Remove a portable service image\n"
" set-limit [NAME|PATH] Set image or pool size limit (disk quota)\n"
, program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_NO_ASK_PASSWORD,
ARG_COPY,
ARG_RUNTIME,
ARG_NO_RELOAD,
ARG_CAT,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "quiet", no_argument, NULL, 'q' },
{ "profile", required_argument, NULL, 'p' },
{ "copy", required_argument, NULL, ARG_COPY },
{ "runtime", no_argument, NULL, ARG_RUNTIME },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
{ "cat", no_argument, NULL, ARG_CAT },
{}
};
assert(argc >= 0);
assert(argv);
for (;;) {
int c;
c = getopt_long(argc, argv, "hH:M:qp:", options, NULL);
if (c < 0)
break;
switch (c) {
case 'h':
help(0, NULL, NULL);
return 0;
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = optarg;
break;
case 'M':
arg_transport = BUS_TRANSPORT_MACHINE;
arg_host = optarg;
break;
case 'q':
arg_quiet = true;
break;
case 'p':
if (!filename_is_valid(optarg)) {
log_error("Unit profile name not valid: %s", optarg);
return -EINVAL;
}
if (streq(optarg, "help"))
return dump_profiles();
arg_profile = optarg;
break;
case ARG_COPY:
if (streq(optarg, "auto"))
arg_copy_mode = NULL;
else if (STR_IN_SET(optarg, "copy", "symlink"))
arg_copy_mode = optarg;
else {
log_error("Failed to parse --copy= argument: %s", optarg);
return -EINVAL;
}
break;
case ARG_RUNTIME:
arg_runtime = true;
break;
case ARG_NO_RELOAD:
arg_reload = false;
break;
case ARG_CAT:
arg_cat = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
return 1;
}
int main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_images },
{ "attach", 2, VERB_ANY, 0, attach_image },
{ "detach", 2, 2, 0, detach_image },
{ "inspect", 2, VERB_ANY, 0, inspect_image },
{ "is-attached", 2, 2, 0, is_image_attached },
{ "read-only", 2, 3, 0, read_only_image },
{ "remove", 2, VERB_ANY, 0, remove_image },
{ "set-limit", 3, 3, 0, set_limit },
{}
};
int r;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = dispatch_verb(argc, argv, verbs, NULL);
finish:
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
+402
View File
@@ -0,0 +1,402 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "btrfs-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "fd-util.h"
#include "io-util.h"
#include "machine-image.h"
#include "portable.h"
#include "portabled-bus.h"
#include "portabled-image-bus.h"
#include "portabled-image.h"
#include "portabled.h"
#include "strv.h"
#include "user-util.h"
static int property_get_pool_path(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
assert(bus);
assert(reply);
return sd_bus_message_append(reply, "s", "/var/lib/portables");
}
static int property_get_pool_usage(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
_cleanup_close_ int fd = -1;
uint64_t usage = (uint64_t) -1;
assert(bus);
assert(reply);
fd = open("/var/lib/portables", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd >= 0) {
BtrfsQuotaInfo q;
if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
usage = q.referenced;
}
return sd_bus_message_append(reply, "t", usage);
}
static int property_get_pool_limit(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
_cleanup_close_ int fd = -1;
uint64_t size = (uint64_t) -1;
assert(bus);
assert(reply);
fd = open("/var/lib/portables", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd >= 0) {
BtrfsQuotaInfo q;
if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
size = q.referenced_max;
}
return sd_bus_message_append(reply, "t", size);
}
static int property_get_profiles(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
int r;
assert(bus);
assert(reply);
r = portable_get_profiles(&l);
if (r < 0)
return r;
return sd_bus_message_append_strv(reply, l);
}
static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
const char *name;
Image *image;
int r;
assert(message);
assert(m);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
r = bus_image_acquire(m, message, name, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
if (r < 0)
return r;
r = bus_image_path(image, &p);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, "o", p);
}
static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
Manager *m = userdata;
Image *image;
Iterator i;
int r;
assert(message);
assert(m);
images = hashmap_new(&string_hash_ops);
if (!images)
return -ENOMEM;
r = manager_image_cache_discover(m, images, error);
if (r < 0)
return r;
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(ssbtttso)");
if (r < 0)
return r;
HASHMAP_FOREACH(image, images, i) {
_cleanup_(sd_bus_error_free) sd_bus_error error_state = SD_BUS_ERROR_NULL;
PortableState state = _PORTABLE_STATE_INVALID;
_cleanup_free_ char *p = NULL;
r = bus_image_path(image, &p);
if (r < 0)
return r;
r = portable_get_state(
sd_bus_message_get_bus(message),
image->path,
0,
&state,
&error_state);
if (r < 0)
log_debug_errno(r, "Failed to get state of image '%s', ignoring: %s",
image->path, bus_error_message(&error_state, r));
r = sd_bus_message_append(reply, "(ssbtttso)",
image->name,
image_type_to_string(image->type),
image->read_only,
image->crtime,
image->mtime,
image->usage,
portable_state_to_string(state),
p);
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 redirect_method_to_image(
Manager *m,
sd_bus_message *message,
sd_bus_error *error,
int (*method)(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error* error)) {
const char *name_or_path;
int r;
assert(m);
assert(message);
assert(method);
r = sd_bus_message_read(message, "s", &name_or_path);
if (r < 0)
return r;
return method(m, message, name_or_path, NULL, error);
}
static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_image(userdata, message, error, bus_image_common_get_os_release);
}
static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_image(userdata, message, error, bus_image_common_get_metadata);
}
static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *name_or_path;
PortableState state;
int r;
assert(message);
r = sd_bus_message_read(message, "s", &name_or_path);
if (r < 0)
return r;
r = portable_get_state(
sd_bus_message_get_bus(message),
name_or_path,
0,
&state,
error);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
}
static int method_attach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_image(userdata, message, error, bus_image_common_attach);
}
static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
PortableChange *changes = NULL;
Manager *m = userdata;
size_t n_changes = 0;
const char *name_or_path;
int r, runtime;
assert(message);
assert(m);
/* Note that we do not redirect detaching to the image object here, because we want to allow that users can
* detach already deleted images too, in case the user already deleted an image before properly detaching
* it. */
r = sd_bus_message_read(message, "sb", &name_or_path, &runtime);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.portable1.attach-images",
NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = portable_detach(
sd_bus_message_get_bus(message),
name_or_path,
runtime ? PORTABLE_RUNTIME : 0,
&changes,
&n_changes,
error);
if (r < 0)
goto finish;
r = reply_portable_changes(message, changes, n_changes);
finish:
portable_changes_free(changes, n_changes);
return r;
}
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_image(userdata, message, error, bus_image_common_remove);
}
static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_image(userdata, message, error, bus_image_common_mark_read_only);
}
static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return redirect_method_to_image(userdata, message, error, bus_image_common_set_limit);
}
static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
uint64_t limit;
int r;
assert(message);
r = sd_bus_message_read(message, "t", &limit);
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.portable1.manage-images",
NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
(void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit);
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit);
if (r == -ENOTTY)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
return sd_bus_reply_method_return(message, NULL);
}
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
SD_BUS_PROPERTY("Profiles", "as", property_get_profiles, 0, 0),
SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListImages", NULL, "a(ssbtttso)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageState", "s", "s", method_get_image_state, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetImageMetadata", "sas", "saya{say}", method_get_image_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AttachImage", "sassbs", "a(sss)", method_attach_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("DetachImage", "sb", "a(sss)", method_detach_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
size_t i;
int r;
assert(m);
assert(changes || n_changes == 0);
r = sd_bus_message_new_method_return(m, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(sss)");
if (r < 0)
return r;
for (i = 0; i < n_changes; i++) {
r = sd_bus_message_append(reply, "(sss)",
portable_change_type_to_string(changes[i].type),
changes[i].path,
changes[i].source);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
+10
View File
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
#include "portable.h"
extern const sd_bus_vtable manager_vtable[];
int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes);
+733
View File
@@ -0,0 +1,733 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
#include "bus-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "io-util.h"
#include "machine-image.h"
#include "portable.h"
#include "portabled-bus.h"
#include "portabled-image-bus.h"
#include "portabled-image.h"
#include "portabled.h"
#include "process-util.h"
#include "strv.h"
#include "user-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
int bus_image_common_get_os_release(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
sd_bus_error *error) {
int r;
assert(name_or_path || image);
assert(message);
if (!m) {
assert(image);
m = image->userdata;
}
r = bus_image_acquire(m,
message,
name_or_path,
image,
BUS_IMAGE_AUTHENTICATE_BY_PATH,
"org.freedesktop.portable1.inspect-images",
&image,
error);
if (r < 0)
return r;
if (r == 0) /* Will call us back */
return 1;
if (!image->metadata_valid) {
r = image_read_metadata(image);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
return bus_reply_pair_array(message, image->os_release);
}
static int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return bus_image_common_get_os_release(NULL, message, NULL, userdata, error);
}
static int append_fd(sd_bus_message *m, PortableMetadata *d) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *buf = NULL;
size_t n;
int r;
assert(m);
assert(d);
assert(d->fd >= 0);
f = fdopen(d->fd, "re");
if (!f)
return -errno;
d->fd = -1;
r = read_full_stream(f, &buf, &n);
if (r < 0)
return r;
return sd_bus_message_append_array(m, 'y', buf, n);
}
int bus_image_common_get_metadata(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
sd_bus_error *error) {
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ PortableMetadata **sorted = NULL;
_cleanup_strv_free_ char **matches = NULL;
size_t i;
int r;
assert(name_or_path || image);
assert(message);
if (!m) {
assert(image);
m = image->userdata;
}
r = sd_bus_message_read_strv(message, &matches);
if (r < 0)
return r;
r = bus_image_acquire(m,
message,
name_or_path,
image,
BUS_IMAGE_AUTHENTICATE_BY_PATH,
"org.freedesktop.portable1.inspect-images",
&image,
error);
if (r < 0)
return r;
if (r == 0) /* Will call us back */
return 1;
r = portable_extract(
image->path,
matches,
&os_release,
&unit_files,
error);
if (r < 0)
return r;
r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
if (r < 0)
return r;
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = sd_bus_message_append(reply, "s", image->path);
if (r < 0)
return r;
r = append_fd(reply, os_release);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "{say}");
if (r < 0)
return r;
for (i = 0; i < hashmap_size(unit_files); i++) {
r = sd_bus_message_open_container(reply, 'e', "say");
if (r < 0)
return r;
r = sd_bus_message_append(reply, "s", sorted[i]->name);
if (r < 0)
return r;
r = append_fd(reply, sorted[i]);
if (r < 0)
return r;
r = sd_bus_message_close_container(reply);
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 bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
}
static int bus_image_method_get_state(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
PortableState state;
int r;
assert(message);
assert(image);
r = portable_get_state(
sd_bus_message_get_bus(message),
image->path,
0,
&state,
error);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
}
int bus_image_common_attach(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
sd_bus_error *error) {
_cleanup_free_ char **matches = NULL;
PortableChange *changes = NULL;
PortableFlags flags = 0;
const char *copy_mode;
size_t n_changes = 0;
const char *profile;
int runtime, r;
assert(message);
assert(name_or_path || image);
if (!m) {
assert(image);
m = image->userdata;
}
r = sd_bus_message_read_strv(message, &matches);
if (r < 0)
return r;
r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
if (r < 0)
return r;
if (streq(copy_mode, "symlink"))
flags |= PORTABLE_PREFER_SYMLINK;
else if (streq(copy_mode, "copy"))
flags |= PORTABLE_PREFER_COPY;
else if (!isempty(copy_mode))
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
if (runtime)
flags |= PORTABLE_RUNTIME;
r = bus_image_acquire(m,
message,
name_or_path,
image,
BUS_IMAGE_AUTHENTICATE_ALL,
"org.freedesktop.portable1.attach-images",
&image,
error);
if (r < 0)
return r;
if (r == 0) /* Will call us back */
return 1;
r = portable_attach(
sd_bus_message_get_bus(message),
image->path,
matches,
profile,
flags,
&changes,
&n_changes,
error);
if (r < 0)
goto finish;
r = reply_portable_changes(message, changes, n_changes);
finish:
portable_changes_free(changes, n_changes);
return r;
}
static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return bus_image_common_attach(NULL, message, NULL, userdata, error);
}
static int bus_image_method_detach(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
PortableChange *changes = NULL;
Image *image = userdata;
Manager *m = image->userdata;
size_t n_changes = 0;
int r, runtime;
assert(message);
assert(image);
assert(m);
r = sd_bus_message_read(message, "b", &runtime);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.portable1.attach-images",
NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = portable_detach(
sd_bus_message_get_bus(message),
image->path,
runtime ? PORTABLE_RUNTIME : 0,
&changes,
&n_changes,
error);
if (r < 0)
goto finish;
r = reply_portable_changes(message, changes, n_changes);
finish:
portable_changes_free(changes, n_changes);
return r;
}
int bus_image_common_remove(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
sd_bus_error *error) {
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
_cleanup_(sigkill_waitp) pid_t child = 0;
PortableState state;
int r;
assert(message);
assert(name_or_path || image);
if (!m) {
assert(image);
m = image->userdata;
}
if (m->n_operations >= OPERATIONS_MAX)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = bus_image_acquire(m,
message,
name_or_path,
image,
BUS_IMAGE_AUTHENTICATE_ALL,
"org.freedesktop.portable1.manage-images",
&image,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = portable_get_state(
sd_bus_message_get_bus(message),
image->path,
0,
&state,
error);
if (r < 0)
return r;
if (state != PORTABLE_DETACHED)
return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = image_remove(image);
if (r < 0) {
(void) write(errno_pipe_fd[1], &r, sizeof(r));
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
if (r < 0)
return r;
child = 0;
errno_pipe_fd[0] = -1;
return 1;
}
static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return bus_image_common_remove(NULL, message, NULL, userdata, error);
}
int bus_image_common_mark_read_only(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
sd_bus_error *error) {
int r, read_only;
assert(message);
assert(name_or_path || image);
if (!m) {
assert(image);
m = image->userdata;
}
r = sd_bus_message_read(message, "b", &read_only);
if (r < 0)
return r;
r = bus_image_acquire(m,
message,
name_or_path,
image,
BUS_IMAGE_AUTHENTICATE_ALL,
"org.freedesktop.portable1.manage-images",
&image,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = image_read_only(image, read_only);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, NULL);
}
static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
}
int bus_image_common_set_limit(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
sd_bus_error *error) {
uint64_t limit;
int r;
assert(message);
assert(name_or_path || image);
if (!m) {
assert(image);
m = image->userdata;
}
r = sd_bus_message_read(message, "t", &limit);
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_image_acquire(m,
message,
name_or_path,
image,
BUS_IMAGE_AUTHENTICATE_ALL,
"org.freedesktop.portable1.manage-images",
&image,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = image_set_limit(image, limit);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, NULL);
}
static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
}
const sd_bus_vtable image_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMedatadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
int bus_image_path(Image *image, char **ret) {
assert(image);
assert(ret);
if (!image->discoverable)
return -EINVAL;
return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
}
int bus_image_acquire(
Manager *m,
sd_bus_message *message,
const char *name_or_path,
Image *image,
ImageAcquireMode mode,
const char *polkit_action,
Image **ret,
sd_bus_error *error) {
_cleanup_(image_unrefp) Image *loaded = NULL;
Image *cached;
int r;
assert(m);
assert(message);
assert(name_or_path || image);
assert(mode >= 0);
assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
assert(ret);
/* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
polkit_action,
NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0) { /* Will call us back */
*ret = NULL;
return 0;
}
}
/* Already passed in? */
if (image) {
*ret = image;
return 1;
}
/* Let's see if this image is already cached? */
cached = manager_image_cache_get(m, name_or_path);
if (cached) {
*ret = cached;
return 1;
}
if (image_name_is_valid(name_or_path)) {
/* If it's a short name, let's search for it */
r = image_find(IMAGE_PORTABLE, name_or_path, &loaded);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
/* other errors are handled below… */
} else {
/* Don't accept path if this is always forbidden */
if (mode == BUS_IMAGE_REFUSE_BY_PATH)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path);
if (!path_is_absolute(name_or_path))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is not valid or not a valid path.", name_or_path);
if (!path_is_normalized(name_or_path))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path);
if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
polkit_action,
NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0) { /* Will call us back */
*ret = NULL;
return 0;
}
}
r = image_from_path(name_or_path, &loaded);
}
if (r < 0)
return r;
/* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
* cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
* means we don't actually need to ref the return object. */
r = manager_image_cache_add(m, loaded);
if (r < 0)
return r;
*ret = loaded;
return 1;
}
int bus_image_object_find(
sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **found,
sd_bus_error *error) {
_cleanup_free_ char *e = NULL;
Manager *m = userdata;
Image *image = NULL;
int r;
assert(bus);
assert(path);
assert(interface);
assert(found);
r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
if (r < 0)
return 0;
if (r == 0)
goto not_found;
r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
if (r == -ENOENT)
goto not_found;
if (r < 0)
return r;
*found = image;
return 1;
not_found:
*found = NULL;
return 0;
}
int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
_cleanup_strv_free_ char **l = NULL;
size_t n_allocated = 0, n = 0;
Manager *m = userdata;
Image *image;
Iterator i;
int r;
assert(bus);
assert(path);
assert(nodes);
images = hashmap_new(&string_hash_ops);
if (!images)
return -ENOMEM;
r = manager_image_cache_discover(m, images, error);
if (r < 0)
return r;
HASHMAP_FOREACH(image, images, i) {
char *p;
r = bus_image_path(image, &p);
if (r < 0)
return r;
if (!GREEDY_REALLOC(l, n_allocated, n+2)) {
free(p);
return -ENOMEM;
}
l[n++] = p;
l[n] = NULL;
}
*nodes = TAKE_PTR(l);
return 1;
}
+40
View File
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "sd-bus.h"
#include "machine-image.h"
#include "portabled.h"
int bus_image_common_get_os_release(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
int bus_image_common_get_metadata(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
int bus_image_common_attach(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
int bus_image_common_remove(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
int bus_image_common_mark_read_only(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
int bus_image_common_set_limit(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
extern const sd_bus_vtable image_vtable[];
int bus_image_path(Image *image, char **ret);
/* So here's some complexity: some of operations can either take an image name, or a fully qualified file system path
* to an image. We need to authenticate differently when processing these two: images referenced via simple image names
* mean the images are located in the image search path and thus safe for limited read access for unprivileged
* clients. For operations on images located anywhere else we need explicit authentication however, so that
* unprivileged clients can't make us open arbitrary files in the file system.
*
* The "Image" bus objects directly represent images in the image search path, but do not exist for path-referenced
* images. Hence, when requesting a bus object we need to refuse references by file system path, but still allow
* references by image name. Depending on the operation to execute potentially we need to authenticate in all cases. */
typedef enum ImageAcquireMode {
BUS_IMAGE_REFUSE_BY_PATH, /* allow by name + prohibit by path */
BUS_IMAGE_AUTHENTICATE_BY_PATH, /* allow by name + polkit by path */
BUS_IMAGE_AUTHENTICATE_ALL, /* polkit by name + polkit by path */
_BUS_IMAGE_ACQUIRE_MODE_MAX,
_BUS_IMAGE_ACQUIRE_MODE_INVALID = -1
} ImageAcquireMode;
int bus_image_acquire(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, ImageAcquireMode mode, const char *polkit_action, Image **ret, sd_bus_error *error);
int bus_image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+105
View File
@@ -0,0 +1,105 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "portable.h"
#include "portabled-image.h"
#include "portabled.h"
Image *manager_image_cache_get(Manager *m, const char *name_or_path) {
assert(m);
return hashmap_get(m->image_cache, name_or_path);
}
static int image_cache_flush(sd_event_source *s, void *userdata) {
Manager *m = userdata;
assert(s);
assert(m);
hashmap_clear_with_destructor(m->image_cache, image_unref);
return 0;
}
static int manager_image_cache_initialize(Manager *m) {
int r;
assert(m);
r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
if (r < 0)
return r;
/* We flush the cache as soon as we are idle again */
if (!m->image_cache_defer_event) {
r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_cache_flush, m);
if (r < 0)
return r;
r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
if (r < 0)
return r;
}
r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
if (r < 0)
return r;
return 0;
}
int manager_image_cache_add(Manager *m, Image *image) {
int r;
assert(m);
/* We add the specified image to the cache under two keys.
*
* 1. Always under its path
*
* 2. If the image was discovered in the search path (i.e. its discoverable boolean set) we'll also add it
* under its short name.
*
*/
r = manager_image_cache_initialize(m);
if (r < 0)
return r;
image->userdata = m;
r = hashmap_put(m->image_cache, image->path, image);
if (r < 0)
return r;
image_ref(image);
if (image->discoverable) {
r = hashmap_put(m->image_cache, image->name, image);
if (r < 0)
return r;
image_ref(image);
}
return 0;
}
int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *error) {
Image *image;
Iterator i;
int r;
assert(m);
/* A wrapper around image_discover() (for finding images in search path) and portable_discover_attached() (for
* finding attached images). */
r = image_discover(IMAGE_PORTABLE, images);
if (r < 0)
return r;
HASHMAP_FOREACH(image, images, i)
(void) manager_image_cache_add(m, image);
return 0;
}
+11
View File
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "hashmap.h"
#include "machine-image.h"
#include "portabled.h"
Image *manager_image_cache_get(Manager *m, const char *name_or_path);
int manager_image_cache_add(Manager *m, Image *image);
int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *error);
+128
View File
@@ -0,0 +1,128 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "fd-util.h"
#include "portabled-operation.h"
#include "process-util.h"
static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Operation *o = userdata;
int r;
assert(o);
assert(si);
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);
o->pid = 0;
if (si->si_code != CLD_EXITED) {
r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto fail;
}
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;
}
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) {
sd_bus_error_set_errno(&error, r);
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;
fail:
r = sd_bus_reply_method_error(o->message, &error);
if (r < 0)
log_error_errno(r, "Failed to reply to message: %m");
operation_free(o);
return 0;
}
int operation_new(Manager *manager, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) {
Operation *o;
int r;
assert(manager);
assert(child > 1);
assert(message);
assert(errno_fd >= 0);
o = new0(Operation, 1);
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);
return r;
}
o->pid = child;
o->message = sd_bus_message_ref(message);
o->errno_fd = errno_fd;
LIST_PREPEND(operations, manager->operations, o);
manager->n_operations++;
o->manager = manager;
log_debug("Started new operation " PID_FMT ".", child);
/* At this point we took ownership of both the child and the errno file descriptor! */
if (ret)
*ret = o;
return 0;
}
Operation *operation_free(Operation *o) {
if (!o)
return NULL;
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);
sd_bus_message_unref(o->message);
if (o->manager) {
LIST_REMOVE(operations, o->manager->operations, o);
o->manager->n_operations--;
}
return mfree(o);
}
+29
View File
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <sys/types.h>
#include "sd-bus.h"
#include "sd-event.h"
#include "list.h"
typedef struct Operation Operation;
#include "portabled.h"
#define OPERATIONS_MAX 64
struct Operation {
Manager *manager;
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);
};
int operation_new(Manager *manager, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret);
Operation *operation_free(Operation *o);
+168
View File
@@ -0,0 +1,168 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "sd-bus.h"
#include "sd-daemon.h"
#include "alloc-util.h"
#include "bus-util.h"
#include "def.h"
#include "portabled-bus.h"
#include "portabled-image-bus.h"
#include "portabled.h"
#include "process-util.h"
#include "signal-util.h"
static Manager* manager_unref(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
static int manager_new(Manager **ret) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
assert(ret);
m = new0(Manager, 1);
if (!m)
return -ENOMEM;
r = sd_event_default(&m->event);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;
(void) sd_event_set_watchdog(m->event, true);
*ret = TAKE_PTR(m);
return 0;
}
static Manager* manager_unref(Manager *m) {
assert(m);
hashmap_free_with_destructor(m->image_cache, image_unref);
sd_event_source_unref(m->image_cache_defer_event);
bus_verify_polkit_async_registry_free(m->polkit_registry);
sd_bus_unref(m->bus);
sd_event_unref(m->event);
return mfree(m);
}
static int manager_connect_bus(Manager *m) {
int r;
assert(m);
assert(!m->bus);
r = sd_bus_default_system(&m->bus);
if (r < 0)
return log_error_errno(r, "Failed to connect to system bus: %m");
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/portable1", "org.freedesktop.portable1.Manager", manager_vtable, m);
if (r < 0)
return log_error_errno(r, "Failed to add manager object vtable: %m");
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/portable1/image", "org.freedesktop.portable1.Image", image_vtable, bus_image_object_find, m);
if (r < 0)
return log_error_errno(r, "Failed to add image object vtable: %m");
r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/portable1/image", bus_image_node_enumerator, m);
if (r < 0)
return log_error_errno(r, "Failed to add image enumerator: %m");
r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.portable1", 0, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
(void) sd_bus_set_exit_on_disconnect(m->bus, true);
return 0;
}
static int manager_startup(Manager *m) {
int r;
assert(m);
r = manager_connect_bus(m);
if (r < 0)
return r;
return 0;
}
static bool check_idle(void *userdata) {
Manager *m = userdata;
return !m->operations;
}
static int manager_run(Manager *m) {
assert(m);
return bus_event_loop_with_idle(
m->event,
m->bus,
"org.freedesktop.portable1",
DEFAULT_EXIT_USEC,
check_idle, m);
}
int main(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
umask(0022);
if (argc != 1) {
log_error("This program takes no arguments.");
r = -EINVAL;
goto finish;
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m);
if (r < 0) {
log_error_errno(r, "Failed to allocate manager object: %m");
goto finish;
}
r = manager_startup(m);
if (r < 0) {
log_error_errno(r, "Failed to fully start up daemon: %m");
goto finish;
}
log_debug("systemd-portabled running as pid " PID_FMT, getpid_cached());
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
r = manager_run(m);
log_debug("systemd-portabled stopped as pid " PID_FMT, getpid_cached());
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
+25
View File
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
#include "sd-event.h"
#include "hashmap.h"
#include "list.h"
typedef struct Manager Manager;
#include "portabled-operation.h"
struct Manager {
sd_event *event;
sd_bus *bus;
Hashmap *polkit_registry;
Hashmap *image_cache;
sd_event_source *image_cache_defer_event;
LIST_HEAD(Operation, operations);
unsigned n_operations;
};
+30
View File
@@ -0,0 +1,30 @@
# The "default" security profile for services, i.e. a number of useful restrictions
[Service]
MountAPIVFS=yes
TemporaryFileSystem=/run
BindReadOnlyPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/etc/resolv.conf
BindReadOnlyPaths=/run/dbus/system_bus_socket
DynamicUser=yes
RemoveIPC=yes
CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER \
CAP_FSETID CAP_IPC_LOCK CAP_IPC_OWNER CAP_KILL CAP_MKNOD CAP_NET_ADMIN \
CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_SETGID CAP_SETPCAP \
CAP_SETUID CAP_SYS_ADMIN CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_RESOURCE
PrivateTmp=yes
PrivateDevices=yes
PrivateUsers=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictNamespaces=yes
SystemCallArchitectures=native
@@ -0,0 +1,30 @@
# The "nonetwork" security profile for services, i.e. like "default" but without networking
[Service]
MountAPIVFS=yes
TemporaryFileSystem=/run
BindReadOnlyPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/run/dbus/system_bus_socket
DynamicUser=yes
RemoveIPC=yes
CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER \
CAP_FSETID CAP_IPC_LOCK CAP_IPC_OWNER CAP_KILL CAP_MKNOD CAP_SETGID CAP_SETPCAP \
CAP_SETUID CAP_SYS_ADMIN CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_RESOURCE
PrivateTmp=yes
PrivateDevices=yes
PrivateUsers=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictNamespaces=yes
SystemCallArchitectures=native
PrivateNetwork=yes
IPAddressDeny=any
+29
View File
@@ -0,0 +1,29 @@
# The "strict" security profile for services, all options turned on
[Service]
MountAPIVFS=yes
TemporaryFileSystem=/run
BindReadOnlyPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id
DynamicUser=yes
RemoveIPC=yes
CapabilityBoundingSet=
PrivateTmp=yes
PrivateDevices=yes
PrivateUsers=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX
LockPersonality=yes
NoNewPrivileges=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictNamespaces=yes
SystemCallArchitectures=native
PrivateNetwork=yes
IPAddressDeny=any
TasksMax=4
@@ -0,0 +1,7 @@
# The "trusted" profile for services, i.e. no restrictions are applied
[Service]
MountAPIVFS=yes
BindPaths=/run
BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/etc/resolv.conf
+1 -1
View File
@@ -1202,7 +1202,7 @@ int link_load_user(Link *l) {
if (l->is_managed)
return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */
r = parse_env_file(l->state_file, NEWLINE,
r = parse_env_file(NULL, l->state_file, NEWLINE,
"LLMNR", &llmnr,
"MDNS", &mdns,
"DNSSEC", &dnssec,
+31
View File
@@ -1847,3 +1847,34 @@ int bus_request_name_async_may_reload_dbus(sd_bus *bus, sd_bus_slot **ret_slot,
return sd_bus_request_name_async(bus, ret_slot, name, flags, request_name_handler_may_reload_dbus, data);
}
int bus_reply_pair_array(sd_bus_message *m, char **l) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
char **k, **v;
int r;
assert(m);
/* Reply to the specified message with a message containing a dictionary put together from the specified
* strv */
r = sd_bus_message_new_method_return(m, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "{ss}");
if (r < 0)
return r;
STRV_FOREACH_PAIR(k, v, l) {
r = sd_bus_message_append(reply, "{ss}", *k, *v);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
+2
View File
@@ -184,3 +184,5 @@ static inline int bus_open_system_watch_bind(sd_bus **ret) {
}
int bus_request_name_async_may_reload_dbus(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, void *userdata);
int bus_reply_pair_array(sd_bus_message *m, char **l);
+1 -1
View File
@@ -344,7 +344,7 @@ int show_cgroup_get_path_and_warn(
const char *m;
m = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
r = parse_env_file(NULL, m, NEWLINE, "SCOPE", &unit, NULL);
if (r < 0)
return log_error_errno(r, "Failed to load machine data: %m");
+1 -1
View File
@@ -464,7 +464,7 @@ static int condition_test_needs_update(Condition *c) {
uint64_t timestamp;
int r;
r = parse_env_file(p, NULL, "TIMESTAMP_NSEC", &timestamp_str, NULL);
r = parse_env_file(NULL, p, NULL, "TIMESTAMP_NSEC", &timestamp_str, NULL);
if (r < 0) {
log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
return true;
+11 -6
View File
@@ -30,6 +30,7 @@
#include "linux-3.13/dm-ioctl.h"
#include "missing.h"
#include "mount-util.h"
#include "os-util.h"
#include "path-util.h"
#include "process-util.h"
#include "raw-clone.h"
@@ -779,6 +780,14 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
if (r < 0)
return r;
if (flags & DISSECT_IMAGE_VALIDATE_OS) {
r = path_is_os_tree(where);
if (r < 0)
return r;
if (r == 0)
return -EMEDIUMTYPE;
}
}
if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY))
@@ -1270,15 +1279,11 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
if (r < 0)
goto finish;
r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS, &child);
r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
if (r < 0)
goto finish;
if (r == 0) {
/* Make sure we never propagate to the host */
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
_exit(EXIT_FAILURE);
r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY);
r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_VALIDATE_OS);
if (r < 0) {
log_debug_errno(r, "Failed to mount dissected image: %m");
_exit(EXIT_FAILURE);
+1
View File
@@ -62,6 +62,7 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_REQUIRE_ROOT = 1 << 5, /* Don't accept disks without root partition */
DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root partition */
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only non-root partitions */
DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifyable as OS images */
} DissectImageFlags;
struct DissectedImage {
+1 -3
View File
@@ -99,8 +99,6 @@ static inline void presets_freep(Presets *p) {
p->n_rules = 0;
}
static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret);
bool unit_type_may_alias(UnitType type) {
return IN_SET(type,
UNIT_SERVICE,
@@ -2643,7 +2641,7 @@ int unit_file_get_default(
return 0;
}
static int unit_file_lookup_state(
int unit_file_lookup_state(
UnitFileScope scope,
const LookupPaths *paths,
const char *name,
+6
View File
@@ -207,6 +207,12 @@ int unit_file_add_dependency(
UnitFileChange **changes,
size_t *n_changes);
int unit_file_lookup_state(
UnitFileScope scope,
const LookupPaths *paths,
const char *name,
UnitFileState *ret);
int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name);
+274 -76
View File
@@ -35,6 +35,7 @@
#include "machine-image.h"
#include "macro.h"
#include "mkdir.h"
#include "os-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "string-table.h"
@@ -45,16 +46,31 @@
#include "util.h"
#include "xattr-util.h"
static const char image_search_path[] =
"/var/lib/machines\0"
"/var/lib/container\0" /* legacy */
"/usr/local/lib/machines\0"
"/usr/lib/machines\0";
static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
[IMAGE_MACHINE] = "/etc/machines\0" /* only place symlinks here */
"/run/machines\0" /* and here too */
"/var/lib/machines\0" /* the main place for images */
"/var/lib/container\0" /* legacy */
"/usr/local/lib/machines\0"
"/usr/lib/machines\0",
[IMAGE_PORTABLE] = "/etc/portables\0" /* only place symlinks here */
"/run/portables\0" /* and here too */
"/var/lib/portables\0" /* the main place for images */
"/usr/local/lib/portables\0"
"/usr/lib/portables\0",
};
Image *image_unref(Image *i) {
if (!i)
return NULL;
assert(i->n_ref > 0);
i->n_ref--;
if (i->n_ref > 0)
return NULL;
free(i->name);
free(i->path);
@@ -65,6 +81,16 @@ Image *image_unref(Image *i) {
return mfree(i);
}
Image *image_ref(Image *i) {
if (!i)
return NULL;
assert(i->n_ref > 0);
i->n_ref++;
return i;
}
static char **image_settings_path(Image *image) {
_cleanup_strv_free_ char **l = NULL;
const char *fn, *s;
@@ -125,6 +151,7 @@ static int image_new(
if (!i)
return -ENOMEM;
i->n_ref = 1;
i->type = t;
i->read_only = read_only;
i->crtime = crtime;
@@ -140,7 +167,6 @@ static int image_new(
i->path = strjoin(path, "/", filename);
else
i->path = strdup(filename);
if (!i->path)
return -ENOMEM;
@@ -151,45 +177,92 @@ static int image_new(
return 0;
}
static int extract_pretty(const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *name = NULL;
const char *p;
size_t n;
assert(path);
assert(ret);
p = last_path_component(path);
n = strcspn(p, "/");
name = strndup(p, n);
if (!name)
return -ENOMEM;
if (suffix) {
char *e;
e = endswith(name, suffix);
if (!e)
return -EINVAL;
*e = 0;
}
if (!image_name_is_valid(name))
return -EINVAL;
*ret = TAKE_PTR(name);
return 0;
}
static int image_make(
const char *pretty,
int dfd,
const char *path,
const char *filename,
const struct stat *st,
Image **ret) {
struct stat st;
_cleanup_free_ char *pretty_buffer = NULL;
struct stat stbuf;
bool read_only;
int r;
assert(dfd >= 0 || dfd == AT_FDCWD);
assert(filename);
/* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block
* devices into /var/lib/machines/, and treat them normally. */
* devices into /var/lib/machines/, and treat them normally.
*
* This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we
* recognize. */
if (fstatat(dfd, filename, &st, 0) < 0)
return -errno;
if (!st) {
if (fstatat(dfd, filename, &stbuf, 0) < 0)
return -errno;
st = &stbuf;
}
read_only =
(path && path_startswith(path, "/usr")) ||
(faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
if (S_ISDIR(st.st_mode)) {
if (S_ISDIR(st->st_mode)) {
_cleanup_close_ int fd = -1;
unsigned file_attr = 0;
if (!ret)
return 1;
return 0;
if (!pretty)
pretty = filename;
if (!pretty) {
r = extract_pretty(filename, NULL, &pretty_buffer);
if (r < 0)
return r;
pretty = pretty_buffer;
}
fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
if (fd < 0)
return -errno;
/* btrfs subvolumes have inode 256 */
if (st.st_ino == 256) {
if (st->st_ino == 256) {
r = btrfs_is_filesystem(fd);
if (r < 0)
@@ -227,7 +300,7 @@ static int image_make(
}
}
return 1;
return 0;
}
}
@@ -248,56 +321,69 @@ static int image_make(
if (r < 0)
return r;
return 1;
return 0;
} else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) {
} else if (S_ISREG(st->st_mode) && endswith(filename, ".raw")) {
usec_t crtime = 0;
/* It's a RAW disk image */
if (!ret)
return 1;
return 0;
fd_getcrtime_at(dfd, filename, &crtime, 0);
(void) fd_getcrtime_at(dfd, filename, &crtime, 0);
if (!pretty)
pretty = strndupa(filename, strlen(filename) - 4);
if (!pretty) {
r = extract_pretty(filename, ".raw", &pretty_buffer);
if (r < 0)
return r;
pretty = pretty_buffer;
}
r = image_new(IMAGE_RAW,
pretty,
path,
filename,
!(st.st_mode & 0222) || read_only,
!(st->st_mode & 0222) || read_only,
crtime,
timespec_load(&st.st_mtim),
timespec_load(&st->st_mtim),
ret);
if (r < 0)
return r;
(*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512;
(*ret)->limit = (*ret)->limit_exclusive = st.st_size;
(*ret)->usage = (*ret)->usage_exclusive = st->st_blocks * 512;
(*ret)->limit = (*ret)->limit_exclusive = st->st_size;
return 1;
return 0;
} else if (S_ISBLK(st.st_mode)) {
} else if (S_ISBLK(st->st_mode)) {
_cleanup_close_ int block_fd = -1;
uint64_t size = UINT64_MAX;
/* A block device */
if (!ret)
return 1;
return 0;
if (!pretty)
pretty = filename;
if (!pretty) {
r = extract_pretty(filename, NULL, &pretty_buffer);
if (r < 0)
return r;
pretty = pretty_buffer;
}
block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
if (block_fd < 0)
log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename);
else {
if (fstat(block_fd, &st) < 0)
/* Refresh stat data after opening the node */
if (fstat(block_fd, &stbuf) < 0)
return -errno;
if (!S_ISBLK(st.st_mode)) /* Verify that what we opened is actually what we think it is */
st = &stbuf;
if (!S_ISBLK(st->st_mode)) /* Verify that what we opened is actually what we think it is */
return -ENOTTY;
if (!read_only) {
@@ -310,7 +396,7 @@ static int image_make(
}
if (ioctl(block_fd, BLKGETSIZE64, &size) < 0)
log_debug_errno(errno, "Failed to issue BLKFLSBUF on device %s/%s, ignoring: %m", path, filename);
log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path, filename);
block_fd = safe_close(block_fd);
}
@@ -319,7 +405,7 @@ static int image_make(
pretty,
path,
filename,
!(st.st_mode & 0222) || read_only,
!(st->st_mode & 0222) || read_only,
0,
0,
ret);
@@ -329,24 +415,27 @@ static int image_make(
if (size != 0 && size != UINT64_MAX)
(*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size;
return 1;
return 0;
}
return 0;
return -EMEDIUMTYPE;
}
int image_find(const char *name, Image **ret) {
int image_find(ImageClass class, const char *name, Image **ret) {
const char *path;
int r;
assert(class >= 0);
assert(class < _IMAGE_CLASS_MAX);
assert(name);
/* There are no images with invalid names */
if (!image_name_is_valid(name))
return 0;
return -ENOENT;
NULSTR_FOREACH(path, image_search_path) {
NULSTR_FOREACH(path, image_search_path[class]) {
_cleanup_closedir_ DIR *d = NULL;
struct stat st;
d = opendir(path);
if (!d) {
@@ -356,37 +445,90 @@ int image_find(const char *name, Image **ret) {
return -errno;
}
r = image_make(NULL, dirfd(d), path, name, ret);
if (IN_SET(r, 0, -ENOENT)) {
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people to
* symlink block devices into the search path */
if (fstatat(dirfd(d), name, &st, 0) < 0) {
_cleanup_free_ char *raw = NULL;
if (errno != ENOENT)
return -errno;
raw = strappend(name, ".raw");
if (!raw)
return -ENOMEM;
r = image_make(NULL, dirfd(d), path, raw, ret);
if (IN_SET(r, 0, -ENOENT))
if (fstatat(dirfd(d), raw, &st, 0) < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
if (!S_ISREG(st.st_mode))
continue;
r = image_make(name, dirfd(d), path, raw, &st, ret);
} else {
if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
continue;
r = image_make(name, dirfd(d), path, name, &st, ret);
}
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
if (r < 0)
return r;
if (ret)
(*ret)->discoverable = true;
return 1;
}
if (streq(name, ".host"))
return image_make(".host", AT_FDCWD, NULL, "/", ret);
if (class == IMAGE_MACHINE && streq(name, ".host")) {
r = image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
if (r < 0)
return r;
return 0;
if (ret)
(*ret)->discoverable = true;
return r;
}
return -ENOENT;
};
int image_discover(Hashmap *h) {
int image_from_path(const char *path, Image **ret) {
/* Note that we don't set the 'discoverable' field of the returned object, because we don't check here whether
* the image is in the image search path. And if it is we don't know if the path we used is actually not
* overriden by another, different image earlier in the search path */
if (path_equal(path, "/"))
return image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
return image_make(NULL, AT_FDCWD, NULL, path, NULL, ret);
}
int image_find_harder(ImageClass class, const char *name_or_path, Image **ret) {
if (image_name_is_valid(name_or_path))
return image_find(class, name_or_path, ret);
return image_from_path(name_or_path, ret);
}
int image_discover(ImageClass class, Hashmap *h) {
const char *path;
int r;
assert(class >= 0);
assert(class < _IMAGE_CLASS_MAX);
assert(h);
NULSTR_FOREACH(path, image_search_path) {
NULSTR_FOREACH(path, image_search_path[class]) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -400,19 +542,53 @@ int image_discover(Hashmap *h) {
FOREACH_DIRENT_ALL(de, d, return -errno) {
_cleanup_(image_unrefp) Image *image = NULL;
_cleanup_free_ char *truncated = NULL;
const char *pretty;
struct stat st;
if (!image_name_is_valid(de->d_name))
if (dot_or_dot_dot(de->d_name))
continue;
if (hashmap_contains(h, de->d_name))
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people
* to symlink block devices into the search path */
if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
if (S_ISREG(st.st_mode)) {
const char *e;
e = endswith(de->d_name, ".raw");
if (!e)
continue;
truncated = strndup(de->d_name, e - de->d_name);
if (!truncated)
return -ENOMEM;
pretty = truncated;
} else if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode))
pretty = de->d_name;
else
continue;
r = image_make(NULL, dirfd(d), path, de->d_name, &image);
if (IN_SET(r, 0, -ENOENT))
if (!image_name_is_valid(pretty))
continue;
if (hashmap_contains(h, pretty))
continue;
r = image_make(pretty, dirfd(d), path, de->d_name, &st, &image);
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
if (r < 0)
return r;
image->discoverable = true;
r = hashmap_put(h, image->name, image);
if (r < 0)
return r;
@@ -421,19 +597,20 @@ int image_discover(Hashmap *h) {
}
}
if (!hashmap_contains(h, ".host")) {
if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
_cleanup_(image_unrefp) Image *image = NULL;
r = image_make(".host", AT_FDCWD, NULL, "/", &image);
r = image_make(".host", AT_FDCWD, NULL, "/", NULL, &image);
if (r < 0)
return r;
image->discoverable = true;
r = hashmap_put(h, image->name, image);
if (r < 0)
return r;
image = NULL;
}
return 0;
@@ -566,11 +743,11 @@ int image_rename(Image *i, const char *new_name) {
if (r < 0)
return r;
r = image_find(new_name, NULL);
if (r < 0)
return r;
if (r > 0)
r = image_find(IMAGE_MACHINE, new_name, NULL);
if (r >= 0)
return -EEXIST;
if (r != -ENOENT)
return r;
switch (i->type) {
@@ -679,11 +856,11 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
if (r < 0)
return r;
r = image_find(new_name, NULL);
if (r < 0)
return r;
if (r > 0)
r = image_find(IMAGE_MACHINE, new_name, NULL);
if (r >= 0)
return -EEXIST;
if (r != -ENOENT)
return r;
switch (i->type) {
@@ -923,6 +1100,7 @@ int image_read_metadata(Image *i) {
sd_id128_t machine_id = SD_ID128_NULL;
_cleanup_free_ char *hostname = NULL;
_cleanup_free_ char *path = NULL;
_cleanup_fclose_ FILE *f = NULL;
r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path);
if (r < 0 && r != -ENOENT)
@@ -962,18 +1140,9 @@ int image_read_metadata(Image *i) {
log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name);
}
path = mfree(path);
r = chase_symlinks("/etc/os-release", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path);
if (r == -ENOENT)
r = chase_symlinks("/usr/lib/os-release", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase os-release in image: %m");
else if (r >= 0) {
r = load_env_file_pairs(NULL, path, NULL, &os_release);
if (r < 0)
log_debug_errno(r, "Failed to parse os-release data of %s: %m", i->name);
}
r = load_os_release_pairs(i->path, &os_release);
if (r < 0)
log_debug_errno(r, "Failed to read os-release in image, ignoring: %m");
free_and_replace(i->hostname, hostname);
i->machine_id = machine_id;
@@ -1059,6 +1228,35 @@ bool image_name_is_valid(const char *s) {
return true;
}
bool image_in_search_path(ImageClass class, const char *image) {
const char *path;
assert(image);
NULSTR_FOREACH(path, image_search_path[class]) {
const char *p;
size_t k;
p = path_startswith(image, path);
if (!p)
continue;
/* Make sure there's a filename following */
k = strcspn(p, "/");
if (k == 0)
continue;
p += k;
/* Accept trailing slashes */
if (p[strspn(p, "/")] == 0)
return true;
}
return false;
}
static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
[IMAGE_DIRECTORY] = "directory",
[IMAGE_SUBVOLUME] = "subvolume",
+19 -3
View File
@@ -17,6 +17,13 @@
#include "string-util.h"
#include "time-util.h"
typedef enum ImageClass {
IMAGE_MACHINE,
IMAGE_PORTABLE,
_IMAGE_CLASS_MAX,
_IMAGE_CLASS_INVALID = -1
} ImageClass;
typedef enum ImageType {
IMAGE_DIRECTORY,
IMAGE_SUBVOLUME,
@@ -27,6 +34,8 @@ typedef enum ImageType {
} ImageType;
typedef struct Image {
unsigned n_ref;
ImageType type;
char *name;
char *path;
@@ -45,12 +54,15 @@ typedef struct Image {
char **machine_info;
char **os_release;
bool metadata_valid;
bool metadata_valid:1;
bool discoverable:1; /* true if we know for sure that image_find() would find the image given just the short name */
void *userdata;
} Image;
Image *image_unref(Image *i);
Image *image_ref(Image *i);
static inline Hashmap* image_hashmap_free(Hashmap *map) {
return hashmap_free_with_destructor(map, image_unref);
}
@@ -58,8 +70,10 @@ static inline Hashmap* image_hashmap_free(Hashmap *map) {
DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free);
int image_find(const char *name, Image **ret);
int image_discover(Hashmap *map);
int image_find(ImageClass class, const char *name, Image **ret);
int image_from_path(const char *path, Image **ret);
int image_find_harder(ImageClass class, const char *name_or_path, Image **ret);
int image_discover(ImageClass class, Hashmap *map);
int image_remove(Image *i);
int image_rename(Image *i, const char *new_name);
@@ -78,6 +92,8 @@ int image_set_limit(Image *i, uint64_t referenced_max);
int image_read_metadata(Image *i);
bool image_in_search_path(ImageClass class, const char *image);
static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
assert(i);
+5 -5
View File
@@ -263,10 +263,8 @@ static int acquire_generator_dirs(
if (tempdir)
prefix = tempdir;
else if (scope == UNIT_FILE_SYSTEM)
prefix = "/run/systemd";
else if (scope == UNIT_FILE_USER) {
const char *e;
@@ -484,6 +482,10 @@ int lookup_paths_init(
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
#if HAVE_SPLIT_USR
flags |= LOOKUP_PATHS_SPLIT_USR;
#endif
if (!empty_or_root(root_dir)) {
if (scope == UNIT_FILE_USER)
return -EINVAL;
@@ -582,9 +584,7 @@ int lookup_paths_init(
"/usr/local/lib/systemd/system",
SYSTEM_DATA_UNIT_PATH,
"/usr/lib/systemd/system",
#if HAVE_SPLIT_USR
"/lib/systemd/system",
#endif
STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
STRV_IFNOTNULL(generator_late),
NULL);
break;
+3 -2
View File
@@ -15,8 +15,9 @@ typedef struct LookupPaths LookupPaths;
#include "macro.h"
typedef enum LookupPathsFlags {
LOOKUP_PATHS_EXCLUDE_GENERATED = 1 << 0,
LOOKUP_PATHS_TEMPORARY_GENERATED = 1 << 1,
LOOKUP_PATHS_EXCLUDE_GENERATED = 1U << 0,
LOOKUP_PATHS_TEMPORARY_GENERATED = 1U << 1,
LOOKUP_PATHS_SPLIT_USR = 1U << 2,
} LookupPathsFlags;
struct LookupPaths {
+1 -1
View File
@@ -18,7 +18,7 @@ int udev_parse_config(void) {
size_t n;
int r;
r = parse_env_file("/etc/udev/udev.conf", NEWLINE, "udev_log", &val, NULL);
r = parse_env_file(NULL, "/etc/udev/udev.conf", NEWLINE, "udev_log", &val, NULL);
if (r == -ENOENT || !val)
return 0;
if (r < 0)
+1 -1
View File
@@ -5664,7 +5664,7 @@ static int switch_root(int argc, char *argv[], void *userdata) {
if (argc >= 3)
init = argv[2];
else {
r = parse_env_file("/proc/cmdline", WHITESPACE,
r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
"init", &cmdline_init,
NULL);
if (r < 0)
+4
View File
@@ -242,6 +242,10 @@ tests += [
[],
[]],
[['src/test/test-os-util.c'],
[],
[]],
[['src/test/test-escape.c'],
[],
[]],
+19 -9
View File
@@ -10,8 +10,10 @@
#include "alloc-util.h"
#include "conf-files.h"
#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "mkdir.h"
#include "parse-util.h"
#include "rm-rf.h"
#include "string-util.h"
@@ -22,12 +24,16 @@
static void setup_test_dir(char *tmp_dir, const char *files, ...) {
va_list ap;
assert_se(mkdtemp(tmp_dir) != NULL);
assert_se(mkdtemp(tmp_dir));
va_start(ap, files);
while (files != NULL) {
_cleanup_free_ char *path = strappend(tmp_dir, files);
assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID) == 0);
while (files) {
_cleanup_free_ char *path;
assert_se(path = strappend(tmp_dir, files));
(void) mkdir_parents(path, 0755);
assert_se(write_string_file(path, "foobar", WRITE_STRING_FILE_CREATE) >= 0);
files = va_arg(ap, const char *);
}
va_end(ap);
@@ -36,7 +42,7 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) {
static void test_conf_files_list(bool use_root) {
char tmp_dir[] = "/tmp/test-conf-files-XXXXXX";
_cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL;
const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c;
const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c, *mask;
log_debug("/* %s */", __func__);
@@ -45,8 +51,12 @@ static void test_conf_files_list(bool use_root) {
"/dir2/a.conf",
"/dir2/b.conf",
"/dir2/c.foo",
"/dir2/d.conf",
NULL);
mask = strjoina(tmp_dir, "/dir1/d.conf");
assert_se(symlink("/dev/null", mask) >= 0);
if (use_root) {
root_dir = tmp_dir;
search_1 = "/dir1";
@@ -63,23 +73,23 @@ static void test_conf_files_list(bool use_root) {
log_debug("/* Check when filtered by suffix */");
assert_se(conf_files_list(&found_files, ".conf", root_dir, 0, search_1, search_2, NULL) == 0);
assert_se(conf_files_list(&found_files, ".conf", root_dir, CONF_FILES_FILTER_MASKED, search_1, search_2, NULL) == 0);
strv_print(found_files);
assert_se(found_files);
assert_se(streq_ptr(found_files[0], expect_a));
assert_se(streq_ptr(found_files[1], expect_b));
assert_se(found_files[2] == NULL);
assert_se(!found_files[2]);
log_debug("/* Check when unfiltered */");
assert_se(conf_files_list(&found_files2, NULL, root_dir, 0, search_1, search_2, NULL) == 0);
assert_se(conf_files_list(&found_files2, NULL, root_dir, CONF_FILES_FILTER_MASKED, search_1, search_2, NULL) == 0);
strv_print(found_files2);
assert_se(found_files2);
assert_se(streq_ptr(found_files2[0], expect_a));
assert_se(streq_ptr(found_files2[1], expect_b));
assert_se(streq_ptr(found_files2[2], expect_c));
assert_se(found_files2[3] == NULL);
assert_se(!found_files2[3]);
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
}
+88
View File
@@ -228,6 +228,93 @@ static void test_rearrange_stdio(void) {
}
}
static void assert_equal_fd(int fd1, int fd2) {
for (;;) {
uint8_t a[4096], b[4096];
ssize_t x, y;
x = read(fd1, a, sizeof(a));
assert(x >= 0);
y = read(fd2, b, sizeof(b));
assert(y >= 0);
assert(x == y);
if (x == 0)
break;
assert(memcmp(a, b, x) == 0);
}
}
static void test_fd_duplicate_data_fd(void) {
_cleanup_close_ int fd1 = -1, fd2 = -1;
_cleanup_(close_pairp) int sfd[2] = { -1, -1 };
_cleanup_(sigkill_waitp) pid_t pid = -1;
uint64_t i, j;
int r;
fd1 = open("/etc/fstab", O_RDONLY|O_CLOEXEC);
if (fd1 >= 0) {
fd2 = fd_duplicate_data_fd(fd1);
assert_se(fd2 >= 0);
assert_se(lseek(fd1, 0, SEEK_SET) == 0);
assert_equal_fd(fd1, fd2);
}
fd1 = safe_close(fd1);
fd2 = safe_close(fd2);
fd1 = acquire_data_fd("hallo", 6, 0);
assert_se(fd1 >= 0);
fd2 = fd_duplicate_data_fd(fd1);
assert_se(fd2 >= 0);
safe_close(fd1);
fd1 = acquire_data_fd("hallo", 6, 0);
assert_se(fd1 >= 0);
assert_equal_fd(fd1, fd2);
fd1 = safe_close(fd1);
fd2 = safe_close(fd2);
assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sfd) >= 0);
r = safe_fork("(sd-pipe)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
assert_se(r >= 0);
if (r == 0) {
/* child */
sfd[0] = safe_close(sfd[0]);
for (i = 0; i < 1536*1024 / sizeof(uint64_t); i++)
assert_se(write(sfd[1], &i, sizeof(i)) == sizeof(i));
sfd[1] = safe_close(sfd[1]);
_exit(EXIT_SUCCESS);
}
sfd[1] = safe_close(sfd[1]);
fd2 = fd_duplicate_data_fd(sfd[0]);
assert_se(fd2 >= 0);
for (i = 0; i < 1536*1024 / sizeof(uint64_t); i++) {
assert_se(read(fd2, &j, sizeof(j)) == sizeof(j));
assert_se(i == j);
}
assert_se(read(fd2, &j, sizeof(j)) == 0);
}
int main(int argc, char *argv[]) {
test_close_many();
test_close_nointr();
@@ -236,6 +323,7 @@ int main(int argc, char *argv[]) {
test_acquire_data_fd();
test_fd_move_above_stdio();
test_rearrange_stdio();
test_fd_duplicate_data_fd();
return 0;
}
+1 -1
View File
@@ -95,7 +95,7 @@ static void test_parse_env_file(void) {
}
r = parse_env_file(
t, NULL,
NULL, t, NULL,
"one", &one,
"two", &two,
"three", &three,
+22
View File
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include "log.h"
#include "os-util.h"
static void test_path_is_os_tree(void) {
assert_se(path_is_os_tree("/") > 0);
assert_se(path_is_os_tree("/etc") == 0);
assert_se(path_is_os_tree("/idontexist") == -ENOENT);
}
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
log_parse_environment();
log_open();
test_path_is_os_tree();
return 0;
}
-7
View File
@@ -52,12 +52,6 @@ static void test_is_symlink(void) {
unlink(name_link);
}
static void test_path_is_os_tree(void) {
assert_se(path_is_os_tree("/") > 0);
assert_se(path_is_os_tree("/etc") == 0);
assert_se(path_is_os_tree("/idontexist") == -ENOENT);
}
static void test_path_is_fs_type(void) {
/* run might not be a mount point in build chroots */
if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) {
@@ -81,7 +75,6 @@ static void test_path_is_temporary_fs(void) {
int main(int argc, char *argv[]) {
test_files_same();
test_is_symlink();
test_path_is_os_tree();
test_path_is_fs_type();
test_path_is_temporary_fs();
+2 -2
View File
@@ -420,7 +420,7 @@ int main(int argc, char **argv) {
utf8 = is_locale_utf8();
r = parse_env_file("/etc/vconsole.conf", NEWLINE,
r = parse_env_file(NULL, "/etc/vconsole.conf", NEWLINE,
"KEYMAP", &vc_keymap,
"KEYMAP_TOGGLE", &vc_keymap_toggle,
"FONT", &vc_font,
@@ -432,7 +432,7 @@ int main(int argc, char **argv) {
/* Let the kernel command line override /etc/vconsole.conf */
if (detect_container() <= 0) {
r = parse_env_file("/proc/cmdline", WHITESPACE,
r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
"vconsole.keymap", &vc_keymap,
"vconsole.keymap_toggle", &vc_keymap_toggle,
"vconsole.font", &vc_font,