#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"
#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"
{ "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 },
/* 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) {
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;
(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;
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");
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");
* 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);