Merge pull request #29721 from poettering/systemd-project
New capsule@.service feature
This commit is contained in:
+19
-2
@@ -28,6 +28,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "capsule-util.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "set.h"
|
||||
#include "sort-util.h"
|
||||
@@ -72,6 +73,7 @@ static int json_transform_message(sd_bus_message *m, JsonVariant **ret);
|
||||
|
||||
static int acquire_bus(bool set_monitor, sd_bus **ret) {
|
||||
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_close_ int pin_fd = -EBADF;
|
||||
int r;
|
||||
|
||||
r = sd_bus_new(&bus);
|
||||
@@ -138,10 +140,13 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
|
||||
r = bus_set_address_machine(bus, arg_runtime_scope, arg_host);
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_CAPSULE:
|
||||
r = bus_set_address_capsule_bus(bus, arg_host, &pin_fd);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return bus_log_address_error(r, arg_transport);
|
||||
|
||||
@@ -2385,6 +2390,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "match", required_argument, NULL, ARG_MATCH },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
{ "machine", required_argument, NULL, 'M' },
|
||||
{ "capsule", required_argument, NULL, 'C' },
|
||||
{ "size", required_argument, NULL, ARG_SIZE },
|
||||
{ "list", no_argument, NULL, ARG_LIST },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
@@ -2406,7 +2412,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hH:M:qjl", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "hH:M:C:J:qjl", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
@@ -2490,6 +2496,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_host = optarg;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
r = capsule_name_is_valid(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
|
||||
|
||||
arg_host = optarg;
|
||||
arg_transport = BUS_TRANSPORT_CAPSULE;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
@@ -254,6 +254,9 @@ struct sd_bus {
|
||||
char *address;
|
||||
unsigned address_index;
|
||||
|
||||
uid_t connect_as_uid;
|
||||
gid_t connect_as_gid;
|
||||
|
||||
int last_connect_error;
|
||||
|
||||
enum bus_auth auth;
|
||||
|
||||
@@ -503,11 +503,38 @@ static int bus_socket_write_auth(sd_bus *b) {
|
||||
if (b->prefer_writev)
|
||||
k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
|
||||
else {
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {};
|
||||
|
||||
struct msghdr mh = {
|
||||
.msg_iov = b->auth_iovec + b->auth_index,
|
||||
.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index,
|
||||
};
|
||||
|
||||
if (uid_is_valid(b->connect_as_uid) || gid_is_valid(b->connect_as_gid)) {
|
||||
|
||||
/* If we shall connect under some specific UID/GID, then synthesize an
|
||||
* SCM_CREDENTIALS record accordingly. After all we want to adopt this UID/GID both
|
||||
* for SO_PEERCRED (where we have to fork()) and SCM_CREDENTIALS (where we can just
|
||||
* fake it via sendmsg()) */
|
||||
|
||||
struct ucred ucred = {
|
||||
.pid = getpid_cached(),
|
||||
.uid = uid_is_valid(b->connect_as_uid) ? b->connect_as_uid : getuid(),
|
||||
.gid = gid_is_valid(b->connect_as_gid) ? b->connect_as_gid : getgid(),
|
||||
};
|
||||
|
||||
mh.msg_control = &control;
|
||||
mh.msg_controllen = sizeof(control);
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
|
||||
*cmsg = (struct cmsghdr) {
|
||||
.cmsg_level = SOL_SOCKET,
|
||||
.cmsg_type = SCM_CREDENTIALS,
|
||||
.cmsg_len = CMSG_LEN(sizeof(struct ucred)),
|
||||
};
|
||||
|
||||
memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred));
|
||||
}
|
||||
|
||||
k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
|
||||
if (k < 0 && errno == ENOTSOCK) {
|
||||
b->prefer_writev = true;
|
||||
@@ -949,6 +976,66 @@ static int bind_description(sd_bus *b, int fd, int family) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connect_as(int fd, const struct sockaddr *sa, socklen_t salen, uid_t uid, gid_t gid) {
|
||||
_cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
/* Shortcut if we are not supposed to drop privileges */
|
||||
if (!uid_is_valid(uid) && !gid_is_valid(gid))
|
||||
return RET_NERRNO(connect(fd, sa, salen));
|
||||
|
||||
/* This changes identity to the specified uid/gid and issues connect() as that. This is useful to
|
||||
* make sure SO_PEERCRED reports the selected UID/GID rather than the usual one of the caller. */
|
||||
|
||||
if (pipe2(pfd, O_CLOEXEC) < 0)
|
||||
return -errno;
|
||||
|
||||
r = safe_fork("(sd-setresuid)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, /* ret_pid= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* child */
|
||||
|
||||
pfd[0] = safe_close(pfd[0]);
|
||||
|
||||
r = RET_NERRNO(setgroups(0, NULL));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
|
||||
if (gid_is_valid(gid)) {
|
||||
r = RET_NERRNO(setresgid(gid, gid, gid));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
}
|
||||
|
||||
if (uid_is_valid(uid)) {
|
||||
r = RET_NERRNO(setresuid(uid, uid, uid));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
}
|
||||
|
||||
r = RET_NERRNO(connect(fd, sa, salen));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
|
||||
r = 0;
|
||||
|
||||
child_finish:
|
||||
n = write(pfd[1], &r, sizeof(r));
|
||||
if (n != sizeof(r))
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
n = read(pfd[0], &r, sizeof(r));
|
||||
if (n != sizeof(r))
|
||||
return -EIO;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int bus_socket_connect(sd_bus *b) {
|
||||
bool inotify_done = false;
|
||||
int r;
|
||||
@@ -980,8 +1067,9 @@ int bus_socket_connect(sd_bus *b) {
|
||||
b->output_fd = b->input_fd;
|
||||
bus_socket_setup(b);
|
||||
|
||||
if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
r = connect_as(b->input_fd, &b->sockaddr.sa, b->sockaddr_size, b->connect_as_uid, b->connect_as_gid);
|
||||
if (r < 0) {
|
||||
if (r == -EINPROGRESS) {
|
||||
|
||||
/* If we have any inotify watches open, close them now, we don't need them anymore, as
|
||||
* we have successfully initiated a connection */
|
||||
@@ -994,7 +1082,7 @@ int bus_socket_connect(sd_bus *b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
|
||||
if (IN_SET(r, -ENOENT, -ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
|
||||
b->watch_bind &&
|
||||
b->sockaddr.sa.sa_family == AF_UNIX &&
|
||||
b->sockaddr.un.sun_path[0] != 0) {
|
||||
@@ -1022,7 +1110,7 @@ int bus_socket_connect(sd_bus *b) {
|
||||
inotify_done = true;
|
||||
|
||||
} else
|
||||
return -errno;
|
||||
return r;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -259,6 +259,8 @@ _public_ int sd_bus_new(sd_bus **ret) {
|
||||
.ucred = UCRED_INVALID,
|
||||
.pidfd = -EBADF,
|
||||
.runtime_scope = _RUNTIME_SCOPE_INVALID,
|
||||
.connect_as_uid = UID_INVALID,
|
||||
.connect_as_gid = GID_INVALID,
|
||||
};
|
||||
|
||||
/* We guarantee that wqueue always has space for at least one entry */
|
||||
@@ -716,7 +718,7 @@ static void skip_address_key(const char **p) {
|
||||
}
|
||||
|
||||
static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
|
||||
_cleanup_free_ char *path = NULL, *abstract = NULL;
|
||||
_cleanup_free_ char *path = NULL, *abstract = NULL, *uids = NULL, *gids = NULL;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
@@ -744,6 +746,18 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
|
||||
else if (r > 0)
|
||||
continue;
|
||||
|
||||
r = parse_address_key(p, "uid", &uids);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
continue;
|
||||
|
||||
r = parse_address_key(p, "gid", &gids);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
continue;
|
||||
|
||||
skip_address_key(p);
|
||||
}
|
||||
|
||||
@@ -780,6 +794,17 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
|
||||
b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
|
||||
}
|
||||
|
||||
if (uids) {
|
||||
r = parse_uid(uids, &b->connect_as_uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (gids) {
|
||||
r = parse_gid(gids, &b->connect_as_gid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
b->is_local = true;
|
||||
|
||||
return 0;
|
||||
|
||||
+50
-3
@@ -17,11 +17,14 @@
|
||||
#include "bus-unit-util.h"
|
||||
#include "bus-wait-for-jobs.h"
|
||||
#include "calendarspec.h"
|
||||
#include "capsule-util.h"
|
||||
#include "chase.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "exit-status.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "main-func.h"
|
||||
#include "parse-argument.h"
|
||||
@@ -35,6 +38,7 @@
|
||||
#include "special.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "uid-classification.h"
|
||||
#include "unit-def.h"
|
||||
#include "unit-name.h"
|
||||
#include "user-util.h"
|
||||
@@ -265,6 +269,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "capsule", required_argument, NULL, 'C' },
|
||||
{ "scope", no_argument, NULL, ARG_SCOPE },
|
||||
{ "unit", required_argument, NULL, 'u' },
|
||||
{ "description", required_argument, NULL, ARG_DESCRIPTION },
|
||||
@@ -317,7 +322,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
/* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
|
||||
* that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
|
||||
optind = 0;
|
||||
while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqGdSu:", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "+hrC:H:M:E:p:tPqGdSu:", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
@@ -339,6 +344,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
r = capsule_name_is_valid(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
|
||||
|
||||
arg_host = optarg;
|
||||
arg_transport = BUS_TRANSPORT_CAPSULE;
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case ARG_SCOPE:
|
||||
arg_scope = true;
|
||||
break;
|
||||
@@ -1598,6 +1615,28 @@ static void set_window_title(PTYForward *f) {
|
||||
(void) pty_forward_set_title_prefix(f, dot);
|
||||
}
|
||||
|
||||
static int chown_to_capsule(const char *path, const char *capsule) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(capsule);
|
||||
|
||||
p = path_join("/run/capsules/", capsule);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
struct stat st;
|
||||
r = chase_and_stat(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid)) /* paranoid safety check */
|
||||
return -EPERM;
|
||||
|
||||
return chmod_and_chown(path, 0600, st.st_uid, st.st_gid);
|
||||
}
|
||||
|
||||
static int start_transient_service(sd_bus *bus) {
|
||||
_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;
|
||||
@@ -1610,7 +1649,7 @@ static int start_transient_service(sd_bus *bus) {
|
||||
|
||||
if (arg_stdio == ARG_STDIO_PTY) {
|
||||
|
||||
if (arg_transport == BUS_TRANSPORT_LOCAL) {
|
||||
if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)) {
|
||||
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (master < 0)
|
||||
return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
|
||||
@@ -1619,6 +1658,14 @@ static int start_transient_service(sd_bus *bus) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine tty name: %m");
|
||||
|
||||
if (arg_transport == BUS_TRANSPORT_CAPSULE) {
|
||||
/* If we are in capsule mode, we must give the capsule UID/GID access to the PTY we just allocated first. */
|
||||
|
||||
r = chown_to_capsule(pty_path, arg_host);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to chown tty to capsule UID/GID: %m");
|
||||
}
|
||||
|
||||
if (unlockpt(master) < 0)
|
||||
return log_error_errno(errno, "Failed to unlock tty: %m");
|
||||
|
||||
@@ -2306,7 +2353,7 @@ static int run(int argc, char* argv[]) {
|
||||
* limited direct connection */
|
||||
if (arg_wait ||
|
||||
arg_stdio != ARG_STDIO_NONE ||
|
||||
(arg_runtime_scope == RUNTIME_SCOPE_USER && arg_transport != BUS_TRANSPORT_LOCAL))
|
||||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
|
||||
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
|
||||
else
|
||||
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
|
||||
|
||||
+166
-13
@@ -18,13 +18,17 @@
|
||||
#include "bus-internal.h"
|
||||
#include "bus-label.h"
|
||||
#include "bus-util.h"
|
||||
#include "capsule-util.h"
|
||||
#include "chase.h"
|
||||
#include "daemon-util.h"
|
||||
#include "data-fd-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "memstream-util.h"
|
||||
#include "path-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "uid-classification.h"
|
||||
|
||||
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
||||
sd_event *e = ASSERT_PTR(userdata);
|
||||
@@ -268,6 +272,140 @@ int bus_connect_user_systemd(sd_bus **ret_bus) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pin_capsule_socket(const char *capsule, const char *suffix, uid_t *ret_uid, gid_t *ret_gid) {
|
||||
_cleanup_close_ int inode_fd = -EBADF;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(capsule);
|
||||
assert(suffix);
|
||||
|
||||
p = path_join("/run/capsules", capsule, suffix);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We enter territory owned by the user, hence let's be paranoid about symlinks and ownership */
|
||||
r = chase(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &inode_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (fstat(inode_fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Paranoid safety check */
|
||||
if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid))
|
||||
return -EPERM;
|
||||
|
||||
*ret_uid = st.st_uid;
|
||||
*ret_gid = st.st_gid;
|
||||
|
||||
return TAKE_FD(inode_fd);
|
||||
}
|
||||
|
||||
int bus_connect_capsule_systemd(const char *capsule, sd_bus **ret_bus) {
|
||||
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_close_ int inode_fd = -EBADF;
|
||||
_cleanup_free_ char *pp = NULL;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int r;
|
||||
|
||||
assert(capsule);
|
||||
assert(ret_bus);
|
||||
|
||||
r = capsule_name_is_valid(capsule);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Connects to a capsule's user bus. We need to do so under the capsule's UID/GID, otherwise the
|
||||
* the service manager might refuse our connection. Hence fake it. */
|
||||
|
||||
inode_fd = pin_capsule_socket(capsule, "systemd/private", &uid, &gid);
|
||||
if (inode_fd < 0)
|
||||
return inode_fd;
|
||||
|
||||
pp = bus_address_escape(FORMAT_PROC_FD_PATH(inode_fd));
|
||||
if (!pp)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (asprintf(&bus->address, "unix:path=%s,uid=" UID_FMT ",gid=" GID_FMT, pp, uid, gid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_bus_start(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret_bus = TAKE_PTR(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_set_address_capsule_bus(sd_bus *bus, const char *capsule, int *ret_pin_fd) {
|
||||
_cleanup_free_ char *pp = NULL;
|
||||
_cleanup_close_ int inode_fd = -EBADF;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(capsule);
|
||||
assert(ret_pin_fd);
|
||||
|
||||
r = capsule_name_is_valid(capsule);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
inode_fd = pin_capsule_socket(capsule, "bus", &uid, &gid);
|
||||
if (inode_fd < 0)
|
||||
return inode_fd;
|
||||
|
||||
pp = bus_address_escape(FORMAT_PROC_FD_PATH(inode_fd));
|
||||
if (!pp)
|
||||
return -ENOMEM;
|
||||
|
||||
if (asprintf(&bus->address, "unix:path=%s,uid=" UID_FMT ",gid=" GID_FMT, pp, uid, gid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_pin_fd = TAKE_FD(inode_fd); /* This fd must be kept pinned until the connection has been established */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_connect_capsule_bus(const char *capsule, sd_bus **ret_bus) {
|
||||
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_close_ int inode_fd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert(capsule);
|
||||
assert(ret_bus);
|
||||
|
||||
r = sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_set_address_capsule_bus(bus, capsule, &inode_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_set_bus_client(bus, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_start(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret_bus = TAKE_PTR(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_connect_transport(
|
||||
BusTransport transport,
|
||||
const char *host,
|
||||
@@ -281,12 +419,10 @@ int bus_connect_transport(
|
||||
assert(transport < _BUS_TRANSPORT_MAX);
|
||||
assert(ret);
|
||||
|
||||
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
|
||||
assert_return(transport != BUS_TRANSPORT_REMOTE || runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
|
||||
|
||||
switch (transport) {
|
||||
|
||||
case BUS_TRANSPORT_LOCAL:
|
||||
assert_return(!host, -EINVAL);
|
||||
|
||||
switch (runtime_scope) {
|
||||
|
||||
@@ -308,11 +444,12 @@ int bus_connect_transport(
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_REMOTE:
|
||||
assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
|
||||
|
||||
r = sd_bus_open_system_remote(&bus, host);
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_MACHINE:
|
||||
|
||||
switch (runtime_scope) {
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
@@ -329,6 +466,12 @@ int bus_connect_transport(
|
||||
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_CAPSULE:
|
||||
assert_return(runtime_scope == RUNTIME_SCOPE_USER, -EINVAL);
|
||||
|
||||
r = bus_connect_capsule_bus(host, &bus);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
@@ -343,28 +486,32 @@ int bus_connect_transport(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_connect_transport_systemd(BusTransport transport, const char *host, RuntimeScope runtime_scope, sd_bus **bus) {
|
||||
int bus_connect_transport_systemd(
|
||||
BusTransport transport,
|
||||
const char *host,
|
||||
RuntimeScope runtime_scope,
|
||||
sd_bus **ret_bus) {
|
||||
|
||||
assert(transport >= 0);
|
||||
assert(transport < _BUS_TRANSPORT_MAX);
|
||||
assert(bus);
|
||||
|
||||
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
|
||||
assert_return(transport == BUS_TRANSPORT_LOCAL || runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
|
||||
assert(ret_bus);
|
||||
|
||||
switch (transport) {
|
||||
|
||||
case BUS_TRANSPORT_LOCAL:
|
||||
assert_return(!host, -EINVAL);
|
||||
|
||||
switch (runtime_scope) {
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
return bus_connect_user_systemd(bus);
|
||||
return bus_connect_user_systemd(ret_bus);
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
if (sd_booted() <= 0)
|
||||
/* Print a friendly message when the local system is actually not running systemd as PID 1. */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
|
||||
"System has not been booted with systemd as init system (PID 1). Can't operate.");
|
||||
return bus_connect_system_systemd(bus);
|
||||
return bus_connect_system_systemd(ret_bus);
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
@@ -373,10 +520,16 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, Runt
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_REMOTE:
|
||||
return sd_bus_open_system_remote(bus, host);
|
||||
assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
|
||||
return sd_bus_open_system_remote(ret_bus, host);
|
||||
|
||||
case BUS_TRANSPORT_MACHINE:
|
||||
return sd_bus_open_system_machine(bus, host);
|
||||
assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
|
||||
return sd_bus_open_system_machine(ret_bus, host);
|
||||
|
||||
case BUS_TRANSPORT_CAPSULE:
|
||||
assert_return(runtime_scope == RUNTIME_SCOPE_USER, -EINVAL);
|
||||
return bus_connect_capsule_systemd(host, ret_bus);
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
@@ -21,6 +21,7 @@ typedef enum BusTransport {
|
||||
BUS_TRANSPORT_LOCAL,
|
||||
BUS_TRANSPORT_REMOTE,
|
||||
BUS_TRANSPORT_MACHINE,
|
||||
BUS_TRANSPORT_CAPSULE,
|
||||
_BUS_TRANSPORT_MAX,
|
||||
_BUS_TRANSPORT_INVALID = -EINVAL,
|
||||
} BusTransport;
|
||||
@@ -36,8 +37,12 @@ bool bus_error_is_unknown_service(const sd_bus_error *error);
|
||||
|
||||
int bus_check_peercred(sd_bus *c);
|
||||
|
||||
int bus_set_address_capsule_bus(sd_bus *bus, const char *capsule, int *ret_pin_fd);
|
||||
|
||||
int bus_connect_system_systemd(sd_bus **ret_bus);
|
||||
int bus_connect_user_systemd(sd_bus **ret_bus);
|
||||
int bus_connect_capsule_systemd(const char *capsule, sd_bus **ret_bus);
|
||||
int bus_connect_capsule_bus(const char *capsule, sd_bus **ret_bus);
|
||||
|
||||
int bus_connect_transport(BusTransport transport, const char *host, RuntimeScope runtime_scope, sd_bus **bus);
|
||||
int bus_connect_transport_systemd(BusTransport transport, const char *host, RuntimeScope runtime_scope, sd_bus **bus);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "capsule-util.h"
|
||||
#include "path-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int capsule_name_is_valid(const char *name) {
|
||||
|
||||
if (!filename_is_valid(name))
|
||||
return false;
|
||||
|
||||
_cleanup_free_ char *prefixed = strjoin("c-", name);
|
||||
if (!prefixed)
|
||||
return -ENOMEM;
|
||||
|
||||
return valid_user_group_name(prefixed, /* flags= */ 0);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int capsule_name_is_valid(const char *name);
|
||||
@@ -140,6 +140,7 @@ shared_sources = files(
|
||||
'pkcs11-util.c',
|
||||
'plymouth-util.c',
|
||||
'pretty-print.c',
|
||||
'capsule-util.c',
|
||||
'ptyfwd.c',
|
||||
'qrcode-util.c',
|
||||
'quota-util.c',
|
||||
|
||||
@@ -255,14 +255,29 @@ static const char** make_extra_args(const char *extra_args[static 4]) {
|
||||
if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
|
||||
extra_args[n++] = "--user";
|
||||
|
||||
if (arg_transport == BUS_TRANSPORT_REMOTE) {
|
||||
switch (arg_transport) {
|
||||
|
||||
case BUS_TRANSPORT_REMOTE:
|
||||
extra_args[n++] = "-H";
|
||||
extra_args[n++] = arg_host;
|
||||
} else if (arg_transport == BUS_TRANSPORT_MACHINE) {
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_MACHINE:
|
||||
extra_args[n++] = "-M";
|
||||
extra_args[n++] = arg_host;
|
||||
} else
|
||||
assert(arg_transport == BUS_TRANSPORT_LOCAL);
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_CAPSULE:
|
||||
extra_args[n++] = "-C";
|
||||
extra_args[n++] = arg_host;
|
||||
break;
|
||||
|
||||
case BUS_TRANSPORT_LOCAL:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
extra_args[n] = NULL;
|
||||
return extra_args;
|
||||
|
||||
@@ -42,7 +42,7 @@ int acquire_bus(BusFocus focus, sd_bus **ret) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--global is not supported for this operation.");
|
||||
|
||||
/* We only go directly to the manager, if we are using a local transport */
|
||||
if (arg_transport != BUS_TRANSPORT_LOCAL)
|
||||
if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE))
|
||||
focus = BUS_FULL;
|
||||
|
||||
if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
#include "capsule-util.h"
|
||||
#include "reboot-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "sigbus.h"
|
||||
@@ -63,6 +64,7 @@
|
||||
#include "systemctl.h"
|
||||
#include "terminal-util.h"
|
||||
#include "time-util.h"
|
||||
#include "user-util.h"
|
||||
#include "verbs.h"
|
||||
#include "virt.h"
|
||||
|
||||
@@ -262,6 +264,7 @@ static int systemctl_help(void) {
|
||||
" --version Show package version\n"
|
||||
" --system Connect to system manager\n"
|
||||
" --user Connect to user service manager\n"
|
||||
" -C --capsule=NAME Connect to service manager of specified capsule\n"
|
||||
" -H --host=[USER@]HOST Operate on remote host\n"
|
||||
" -M --machine=CONTAINER Operate on a local container\n"
|
||||
" -t --type=TYPE List units of a particular type\n"
|
||||
@@ -490,6 +493,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "global", no_argument, NULL, ARG_GLOBAL },
|
||||
{ "capsule", required_argument, NULL, 'C' },
|
||||
{ "wait", no_argument, NULL, ARG_WAIT },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "legend", required_argument, NULL, ARG_LEGEND },
|
||||
@@ -544,7 +548,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
/* We default to allowing interactive authorization only in systemctl (not in the legacy commands) */
|
||||
arg_ask_password = true;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "ht:p:P:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "hC:t:p:P:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
@@ -679,6 +683,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
arg_runtime_scope = RUNTIME_SCOPE_GLOBAL;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
r = capsule_name_is_valid(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
|
||||
|
||||
arg_host = optarg;
|
||||
arg_transport = BUS_TRANSPORT_CAPSULE;
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case ARG_WAIT:
|
||||
arg_wait = true;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user