summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2015-08-25 16:46:27 +0200
committerTom Gundersen <teg@jklm.no>2015-08-25 16:46:27 +0200
commit498fb56739a5d18cb85d2ba3523759b1d141d770 (patch)
treed68a7330da711f320063046007cddfbf469fea6f /src
parent3ad216b44f4f5cba168f449ae455607a442cfd4c (diff)
parentef3100e9637adda26fa19e7ee8606788320dcde3 (diff)
Merge pull request #1022 from poettering/machinectl-shell
Add new "machinectl shell" command for su(1)-like behaviour
Diffstat (limited to 'src')
-rw-r--r--src/basic/hostname-util.c19
-rw-r--r--src/basic/hostname-util.h4
-rw-r--r--src/basic/time-util.c26
-rw-r--r--src/basic/time-util.h1
-rw-r--r--src/basic/util.c18
-rw-r--r--src/basic/util.h2
-rw-r--r--src/core/dbus-execute.c105
-rw-r--r--src/core/execute.c15
-rw-r--r--src/core/execute.h12
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/load-fragment.c2
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/import/export.c1
-rw-r--r--src/import/import-raw.c1
-rw-r--r--src/import/import-tar.c1
-rw-r--r--src/import/import.c1
-rw-r--r--src/import/importd.c1
-rw-r--r--src/import/pull-raw.c1
-rw-r--r--src/import/pull-tar.c3
-rw-r--r--src/import/pull.c1
-rw-r--r--src/journal/sd-journal.c1
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c1
-rw-r--r--src/libsystemd/sd-login/sd-login.c3
-rw-r--r--src/machine/machine-dbus.c642
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machine.c67
-rw-r--r--src/machine/machine.h5
-rw-r--r--src/machine/machinectl.c234
-rw-r--r--src/machine/machined-dbus.c50
-rw-r--r--src/machine/machined.c51
-rw-r--r--src/machine/machined.h2
-rw-r--r--src/machine/org.freedesktop.machine1.conf8
-rw-r--r--src/machine/org.freedesktop.machine1.policy.in53
-rw-r--r--src/nss-mymachines/nss-mymachines.c1
-rw-r--r--src/shared/logs-show.c1
-rw-r--r--src/shared/utmp-wtmp.c24
-rw-r--r--src/shared/utmp-wtmp.h4
-rw-r--r--src/systemctl/systemctl.c11
38 files changed, 1115 insertions, 260 deletions
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index dc5434fcd1..1b816fb77a 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -62,16 +62,19 @@ static bool hostname_valid_char(char c) {
}
/**
- * Check if s looks like a valid host name or fqdn. This does not do
+ * Check if s looks like a valid host name or FQDN. This does not do
* full DNS validation, but only checks if the name is composed of
* allowed characters and the length is not above the maximum allowed
* by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
- * relax is true and at least two components are present in the name.
+ * allow_trailing_dot is true and at least two components are present
+ * in the name. Note that due to the restricted charset and length
+ * this call is substantially more conservative than
+ * dns_domain_is_valid().
*/
-bool hostname_is_valid(const char *s, bool relax) {
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
+ unsigned n_dots = 0;
const char *p;
bool dot;
- unsigned dots = 0;
if (isempty(s))
return false;
@@ -87,7 +90,7 @@ bool hostname_is_valid(const char *s, bool relax) {
return false;
dot = true;
- dots ++;
+ n_dots ++;
} else {
if (!hostname_valid_char(*p))
return false;
@@ -96,10 +99,12 @@ bool hostname_is_valid(const char *s, bool relax) {
}
}
- if (dot && (dots < 2 || !relax))
+ if (dot && (n_dots < 2 || !allow_trailing_dot))
return false;
- if (p-s > HOST_NAME_MAX)
+ if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
+ * Linux, but DNS allows domain names
+ * up to 255 characters */
return false;
return true;
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
index a1ca94980d..d4f5bfe45e 100644
--- a/src/basic/hostname-util.h
+++ b/src/basic/hostname-util.h
@@ -29,9 +29,11 @@ bool hostname_is_set(void);
char* gethostname_malloc(void);
-bool hostname_is_valid(const char *s, bool relax) _pure_;
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
char* hostname_cleanup(char *s);
+#define machine_name_is_valid(s) hostname_is_valid(s, false)
+
bool is_localhost(const char *hostname);
bool is_gateway_hostname(const char *hostname);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 12f1b193be..e278196c90 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -88,6 +88,32 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
return ts;
}
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+
+ if (u == USEC_INFINITY) {
+ ts->realtime = ts->monotonic = USEC_INFINITY;
+ return ts;
+ }
+ ts->realtime = now(CLOCK_REALTIME);
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
+
+ if ((int64_t) ts->realtime > delta)
+ ts->realtime -= delta;
+ else
+ ts->realtime = 0;
+
+ if ((int64_t) ts->monotonic > delta)
+ ts->monotonic -= delta;
+ else
+ ts->monotonic = 0;
+
+ return ts;
+}
+
+
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 7a64d454a0..2aba042217 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -74,6 +74,7 @@ usec_t now(clockid_t clock);
dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
diff --git a/src/basic/util.c b/src/basic/util.c
index 9571f0ab12..f752595ca1 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -3006,21 +3006,6 @@ char* strshorten(char *s, size_t l) {
return s;
}
-bool machine_name_is_valid(const char *s) {
-
- if (!hostname_is_valid(s, false))
- return false;
-
- /* Machine names should be useful hostnames, but also be
- * useful in unit names, hence we enforce a stricter length
- * limitation. */
-
- if (strlen(s) > 64)
- return false;
-
- return true;
-}
-
int pipe_eof(int fd) {
struct pollfd pollfd = {
.fd = fd,
@@ -4928,6 +4913,9 @@ int container_get_leader(const char *machine, pid_t *pid) {
assert(machine);
assert(pid);
+ if (!machine_name_is_valid(machine))
+ return -EINVAL;
+
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
if (r == -ENOENT)
diff --git a/src/basic/util.h b/src/basic/util.h
index 8f21d56ed8..1ead7b5419 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -394,8 +394,6 @@ bool nulstr_contains(const char*nulstr, const char *needle);
bool plymouth_running(void);
-bool machine_name_is_valid(const char *s) _pure_;
-
char* strshorten(char *s, size_t l);
int symlink_idempotent(const char *from, const char *to);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index a9f7971cde..b60025c48a 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -46,6 +46,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
+
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem);
@@ -653,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -932,6 +935,108 @@ int bus_exec_context_set_transient_property(
return 1;
+ } else if (streq(name, "IgnoreSIGPIPE")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->ignore_sigpipe = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "TTYVHangup")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tty_vhangup = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+
+ } else if (streq(name, "TTYReset")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tty_reset = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "UtmpIdentifier")) {
+ const char *id;
+
+ r = sd_bus_message_read(message, "s", &id);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(id))
+ c->utmp_id = mfree(c->utmp_id);
+ else if (free_and_strdup(&c->utmp_id, id) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s\n", strempty(id));
+ }
+
+ return 1;
+
+ } else if (streq(name, "UtmpMode")) {
+ const char *s;
+ ExecUtmpMode m;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ m = exec_utmp_mode_from_string(s);
+ if (m < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode");
+
+ if (mode != UNIT_CHECK) {
+ c->utmp_mode = m;
+
+ unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s\n", exec_utmp_mode_to_string(m));
+ }
+
+ return 1;
+
+ } else if (streq(name, "PAMName")) {
+ const char *n;
+
+ r = sd_bus_message_read(message, "s", &n);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(n))
+ c->pam_name = mfree(c->pam_name);
+ else if (free_and_strdup(&c->pam_name, n) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "PAMName=%s\n", strempty(n));
+ }
+
+ return 1;
+
} else if (streq(name, "Environment")) {
_cleanup_strv_free_ char **l = NULL;
diff --git a/src/core/execute.c b/src/core/execute.c
index 3820165241..28eeeaad18 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -31,6 +31,7 @@
#include <grp.h>
#include <poll.h>
#include <glob.h>
+#include <utmpx.h>
#include <sys/personality.h>
#ifdef HAVE_PAM
@@ -1504,7 +1505,11 @@ static int exec_child(
}
if (context->utmp_id)
- utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
+ utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path,
+ context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
+ context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
+ USER_PROCESS,
+ username ? "root" : context->user);
if (context->user && is_terminal_input(context->std_input)) {
r = chown_terminal(STDIN_FILENO, uid);
@@ -2968,3 +2973,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
+
+static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
+ [EXEC_UTMP_INIT] = "init",
+ [EXEC_UTMP_LOGIN] = "login",
+ [EXEC_UTMP_USER] = "user",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
diff --git a/src/core/execute.h b/src/core/execute.h
index f5d5c1dee7..5d46bf154c 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -38,6 +38,14 @@ typedef struct ExecParameters ExecParameters;
#include "namespace.h"
#include "bus-endpoint.h"
+typedef enum ExecUtmpMode {
+ EXEC_UTMP_INIT,
+ EXEC_UTMP_LOGIN,
+ EXEC_UTMP_USER,
+ _EXEC_UTMP_MODE_MAX,
+ _EXEC_UTMP_MODE_INVALID,
+} ExecUtmpMode;
+
typedef enum ExecInput {
EXEC_INPUT_NULL,
EXEC_INPUT_TTY,
@@ -131,6 +139,7 @@ struct ExecContext {
char *pam_name;
char *utmp_id;
+ ExecUtmpMode utmp_mode;
bool selinux_context_ignore;
char *selinux_context;
@@ -265,3 +274,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_;
const char* exec_input_to_string(ExecInput i) _const_;
ExecInput exec_input_from_string(const char *s) _pure_;
+
+const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_;
+ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index aae81c80cb..60b97722db 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -91,6 +91,7 @@ m4_ifdef(`HAVE_PAM',
`$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe)
$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id)
+$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode)
m4_ifdef(`HAVE_SELINUX',
`$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)',
`$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e1e3a5ffb7..b3bf8bdb40 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1142,6 +1142,8 @@ int config_parse_sysv_priority(const char *unit,
}
#endif
+DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
int config_parse_kill_signal(const char *unit,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ce10d03c3f..fcca2b0221 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -104,6 +104,7 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line
int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/import/export.c b/src/import/export.c
index ec7dbe210a..b88d71fec6 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -24,6 +24,7 @@
#include "sd-event.h"
#include "event-util.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "verbs.h"
#include "build.h"
#include "machine-image.h"
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index 43cd413042..a27e81bbd4 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "path-util.h"
#include "btrfs-util.h"
+#include "hostname-util.h"
#include "copy.h"
#include "mkdir.h"
#include "rm-rf.h"
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 2bf0b0680c..7ffe83cc33 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "path-util.h"
#include "btrfs-util.h"
+#include "hostname-util.h"
#include "copy.h"
#include "mkdir.h"
#include "rm-rf.h"
diff --git a/src/import/import.c b/src/import/import.c
index b7772390e9..929a840298 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -26,6 +26,7 @@
#include "verbs.h"
#include "build.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "import-util.h"
#include "import-tar.h"
diff --git a/src/import/importd.c b/src/import/importd.c
index dd314f5b00..8b508eaeec 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -35,6 +35,7 @@
#include "import-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "hostname-util.h"
typedef struct Transfer Transfer;
typedef struct Manager Manager;
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index 5bfaf012c0..d0e0faa261 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -33,6 +33,7 @@
#include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h"
+#include "hostname-util.h"
#include "import-util.h"
#include "import-common.h"
#include "curl-util.h"
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 71b8908a58..d38a2158c4 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -32,13 +32,14 @@
#include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h"
+#include "process-util.h"
+#include "hostname-util.h"
#include "import-util.h"
#include "import-common.h"
#include "curl-util.h"
#include "pull-job.h"
#include "pull-common.h"
#include "pull-tar.h"
-#include "process-util.h"
typedef enum TarProgress {
TAR_DOWNLOADING,
diff --git a/src/import/pull.c b/src/import/pull.c
index ca7be6be85..e13cd6af97 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -26,6 +26,7 @@
#include "verbs.h"
#include "build.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "import-util.h"
#include "pull-tar.h"
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index bfd1901e16..13fa9b52fc 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -43,6 +43,7 @@
#include "replace-var.h"
#include "fileio.h"
#include "formats-util.h"
+#include "hostname-util.h"
#define JOURNAL_FILES_MAX 7168
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index db4f21e9ff..31cdcb4e5b 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -33,6 +33,7 @@
#include "missing.h"
#include "def.h"
#include "cgroup-util.h"
+#include "hostname-util.h"
#include "bus-label.h"
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index 9bbf8974dd..0eadc8c747 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -32,6 +32,7 @@
#include "fileio.h"
#include "login-util.h"
#include "formats-util.h"
+#include "hostname-util.h"
#include "sd-login.h"
_public_ int sd_pid_get_session(pid_t pid, char **session) {
@@ -790,7 +791,7 @@ _public_ int sd_get_machine_names(char ***machines) {
/* Filter out the unit: symlinks */
for (a = l, b = l; *a; a++) {
- if (startswith(*a, "unit:"))
+ if (startswith(*a, "unit:") || !machine_name_is_valid(*a))
free(*a);
else {
*b = *a;
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index b62a9bd813..af2b8eff06 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -44,6 +44,7 @@
#include "machine-dbus.h"
#include "formats-util.h"
#include "process-util.h"
+#include "env-util.h"
static int property_get_id(
sd_bus *bus,
@@ -185,141 +186,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_close_pair_ int pair[2] = { -1, -1 };
- _cleanup_free_ char *us = NULL, *them = NULL;
- _cleanup_close_ int netns_fd = -1;
Machine *m = userdata;
- const char *p;
- siginfo_t si;
- pid_t child;
int r;
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
-
- r = readlink_malloc("/proc/self/ns/net", &us);
- if (r < 0)
- return r;
-
- p = procfs_file_alloca(m->leader, "ns/net");
- r = readlink_malloc(p, &them);
+ r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
- if (streq(us, them))
- return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
-
- r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
+ r = sd_bus_message_open_container(reply, 'a', "(iay)");
if (r < 0)
return r;
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
- return -errno;
-
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ switch (m->class) {
- if (child == 0) {
+ case MACHINE_HOST: {
_cleanup_free_ struct local_address *addresses = NULL;
struct local_address *a;
- int i, n;
-
- pair[0] = safe_close(pair[0]);
-
- r = namespace_enter(-1, -1, netns_fd, -1, -1);
- if (r < 0)
- _exit(EXIT_FAILURE);
+ int n, i;
n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n < 0)
- _exit(EXIT_FAILURE);
+ return n;
for (a = addresses, i = 0; i < n; a++, i++) {
- struct iovec iov[2] = {
- { .iov_base = &a->family, .iov_len = sizeof(a->family) },
- { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
- };
- r = writev(pair[1], iov, 2);
+ r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
- _exit(EXIT_FAILURE);
- }
-
- pair[1] = safe_close(pair[1]);
+ return r;
- _exit(EXIT_SUCCESS);
- }
-
- pair[1] = safe_close(pair[1]);
+ r = sd_bus_message_append(reply, "i", addresses[i].family);
+ if (r < 0)
+ return r;
- r = sd_bus_message_new_method_return(message, &reply);
- if (r < 0)
- return r;
+ r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family));
+ if (r < 0)
+ return r;
- r = sd_bus_message_open_container(reply, 'a', "(iay)");
- if (r < 0)
- return r;
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
- for (;;) {
- int family;
- ssize_t n;
- union in_addr_union in_addr;
- struct iovec iov[2];
- struct msghdr mh = {
- .msg_iov = iov,
- .msg_iovlen = 2,
- };
+ break;
+ }
- iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
- iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
+ case MACHINE_CONTAINER: {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ _cleanup_free_ char *us = NULL, *them = NULL;
+ _cleanup_close_ int netns_fd = -1;
+ const char *p;
+ siginfo_t si;
+ pid_t child;
- n = recvmsg(pair[0], &mh, 0);
- if (n < 0)
- return -errno;
- if ((size_t) n < sizeof(family))
- break;
+ r = readlink_malloc("/proc/self/ns/net", &us);
+ if (r < 0)
+ return r;
- r = sd_bus_message_open_container(reply, 'r', "iay");
+ p = procfs_file_alloca(m->leader, "ns/net");
+ r = readlink_malloc(p, &them);
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "i", family);
+ if (streq(us, them))
+ return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
+
+ r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
if (r < 0)
return r;
- switch (family) {
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ struct local_address *a;
+ int i, n;
+
+ pair[0] = safe_close(pair[0]);
- case AF_INET:
- if (n != sizeof(struct in_addr) + sizeof(family))
- return -EIO;
+ r = namespace_enter(-1, -1, netns_fd, -1, -1);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
+ if (n < 0)
+ _exit(EXIT_FAILURE);
+
+ for (a = addresses, i = 0; i < n; a++, i++) {
+ struct iovec iov[2] = {
+ { .iov_base = &a->family, .iov_len = sizeof(a->family) },
+ { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
+ };
- r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
- break;
+ r = writev(pair[1], iov, 2);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+ }
- case AF_INET6:
- if (n != sizeof(struct in6_addr) + sizeof(family))
- return -EIO;
+ pair[1] = safe_close(pair[1]);
- r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
- break;
+ _exit(EXIT_SUCCESS);
}
- if (r < 0)
- return r;
- r = sd_bus_message_close_container(reply);
+ pair[1] = safe_close(pair[1]);
+
+ for (;;) {
+ int family;
+ ssize_t n;
+ union in_addr_union in_addr;
+ struct iovec iov[2];
+ struct msghdr mh = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
+ iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
+
+ n = recvmsg(pair[0], &mh, 0);
+ if (n < 0)
+ return -errno;
+ if ((size_t) n < sizeof(family))
+ break;
+
+ r = sd_bus_message_open_container(reply, 'r', "iay");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "i", family);
+ if (r < 0)
+ return r;
+
+ switch (family) {
+
+ case AF_INET:
+ if (n != sizeof(struct in_addr) + sizeof(family))
+ return -EIO;
+
+ r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
+ break;
+
+ case AF_INET6:
+ if (n != sizeof(struct in6_addr) + sizeof(family))
+ return -EIO;
+
+ r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
+ break;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+
+ r = wait_for_terminate(child, &si);
if (r < 0)
- return r;
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ break;
}
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ default:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
+ }
r = sd_bus_message_close_container(reply);
if (r < 0)
@@ -330,73 +369,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_close_ int mntns_fd = -1, root_fd = -1;
- _cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_strv_free_ char **l = NULL;
- _cleanup_fclose_ FILE *f = NULL;
Machine *m = userdata;
char **k, **v;
- siginfo_t si;
- pid_t child;
int r;
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
+ switch (m->class) {
- r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
- if (r < 0)
- return r;
+ case MACHINE_HOST:
+ r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l);
+ if (r < 0)
+ return r;
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
- return -errno;
+ break;
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ case MACHINE_CONTAINER: {
+ _cleanup_close_ int mntns_fd = -1, root_fd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ _cleanup_fclose_ FILE *f = NULL;
+ siginfo_t si;
+ pid_t child;
- if (child == 0) {
- _cleanup_close_ int fd = -1;
+ r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ if (r < 0)
+ return r;
- pair[0] = safe_close(pair[0]);
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+ return -errno;
- r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
- if (r < 0)
- _exit(EXIT_FAILURE);
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ _cleanup_close_ int fd = -1;
- fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ _exit(EXIT_FAILURE);
+ }
+
+ r = copy_bytes(fd, pair[1], (off_t) -1, false);
+ if (r < 0)
_exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
}
- r = copy_bytes(fd, pair[1], (off_t) -1, false);
- if (r < 0)
- _exit(EXIT_FAILURE);
+ pair[1] = safe_close(pair[1]);
- _exit(EXIT_SUCCESS);
- }
+ f = fdopen(pair[0], "re");
+ if (!f)
+ return -errno;
- pair[1] = safe_close(pair[1]);
+ pair[0] = -1;
- f = fdopen(pair[0], "re");
- if (!f)
- return -errno;
+ r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
+ if (r < 0)
+ return r;
- pair[0] = -1;
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
- r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
- if (r < 0)
- return r;
+ break;
+ }
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ default:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
+ }
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -429,10 +483,20 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines.");
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
- master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (master < 0)
return master;
@@ -451,26 +515,67 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_send(NULL, reply, NULL);
}
+static int container_bus_new(Machine *m, sd_bus **ret) {
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ *ret = NULL;
+ break;
+
+ case MACHINE_CONTAINER: {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ char *address;
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0)
+ return -ENOMEM;
+
+ bus->address = address;
+ bus->bus_client = true;
+ bus->trusted = false;
+ bus->is_system = true;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+ break;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_free_ char *pty_name = NULL, *getty = NULL;
- _cleanup_bus_unref_ sd_bus *container_bus = NULL;
+ _cleanup_free_ char *pty_name = NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL;
_cleanup_close_ int master = -1;
+ sd_bus *container_bus = NULL;
Machine *m = userdata;
- const char *p;
- char *address;
+ const char *p, *getty;
int r;
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
-
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
- "org.freedesktop.machine1.login",
+ m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -480,7 +585,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (r == 0)
return 1; /* Will call us back */
- master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (master < 0)
return master;
@@ -495,26 +600,13 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (unlockpt(master) < 0)
return -errno;
- r = sd_bus_new(&container_bus);
+ r = container_bus_new(m, &allocated_bus);
if (r < 0)
return r;
-# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI
- if (asprintf(&address, ADDRESS_FMT, m->leader) < 0)
- return log_oom();
-
- container_bus->address = address;
- container_bus->bus_client = true;
- container_bus->trusted = false;
- container_bus->is_system = true;
+ container_bus = allocated_bus ?: m->manager->bus;
- r = sd_bus_start(container_bus);
- if (r < 0)
- return r;
-
- getty = strjoin("container-getty@", p, ".service", NULL);
- if (!getty)
- return log_oom();
+ getty = strjoina("container-getty@", p, ".service");
r = sd_bus_call_method(
container_bus,
@@ -527,7 +619,228 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
- container_bus = sd_bus_unref(container_bus);
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "hs", master, pty_name);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL;
+ _cleanup_free_ char *pty_name = NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL;
+ sd_bus *container_bus = NULL;
+ _cleanup_close_ int master = -1;
+ _cleanup_strv_free_ char **env = NULL, **args = NULL;
+ Machine *m = userdata;
+ const char *p, *unit, *user, *path, *description, *utmp_id;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "ss", &user, &path);
+ if (r < 0)
+ return r;
+ if (isempty(user))
+ user = NULL;
+ if (isempty(path))
+ path = "/bin/sh";
+ if (!path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
+
+ r = sd_bus_message_read_strv(message, &args);
+ if (r < 0)
+ return r;
+ if (strv_isempty(args)) {
+ args = strv_free(args);
+
+ args = strv_new(path, NULL);
+ if (!args)
+ return -ENOMEM;
+
+ args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */
+ }
+
+ r = sd_bus_message_read_strv(message, &env);
+ if (r < 0)
+ return r;
+ if (!strv_env_is_valid(env))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (master < 0)
+ return master;
+
+ r = ptsname_malloc(master, &pty_name);
+ if (r < 0)
+ return r;
+
+ p = path_startswith(pty_name, "/dev/pts/");
+ if (!p)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
+
+ utmp_id = path_startswith(pty_name, "/dev/");
+ assert(utmp_id);
+
+ if (unlockpt(master) < 0)
+ return -errno;
+
+ r = container_bus_new(m, &allocated_bus);
+ if (r < 0)
+ return r;
+
+ container_bus = allocated_bus ?: m->manager->bus;
+
+ r = sd_bus_message_new_method_call(
+ container_bus,
+ &tm,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit");
+ if (r < 0)
+ return r;
+
+ /* Name and mode */
+ unit = strjoina("container-shell@", p, ".service", NULL);
+ r = sd_bus_message_append(tm, "ss", unit, "fail");
+ if (r < 0)
+ return r;
+
+ /* Properties */
+ r = sd_bus_message_open_container(tm, 'a', "(sv)");
+ if (r < 0)
+ return r;
+
+ description = strjoina("Shell for User ", isempty(user) ? "root" : user);
+ r = sd_bus_message_append(tm,
+ "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
+ "Description", "s", description,
+ "StandardInput", "s", "tty",
+ "StandardOutput", "s", "tty",
+ "StandardError", "s", "tty",
+ "TTYPath", "s", pty_name,
+ "SendSIGHUP", "b", true,
+ "IgnoreSIGPIPE", "b", false,
+ "KillMode", "s", "mixed",
+ "TTYVHangup", "b", true,
+ "TTYReset", "b", true,
+ "UtmpIdentifier", "s", utmp_id,
+ "UtmpMode", "s", "user",
+ "PAMName", "s", "login");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user);
+ if (r < 0)
+ return r;
+
+ if (!strv_isempty(env)) {
+ r = sd_bus_message_open_container(tm, 'r', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "s", "Environment");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'v', "as");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(tm, env);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+ }
+
+ /* Exec container */
+ r = sd_bus_message_open_container(tm, 'r', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "s", "ExecStart");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'a', "(sasb)");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'r', "sasb");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "s", path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(tm, args);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "b", true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ /* Auxiliary units */
+ r = sd_bus_message_append(tm, "a(sa(sv))", 0);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call(container_bus, tm, 0, error, NULL);
+ if (r < 0)
+ return r;
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -966,8 +1279,9 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
+ SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index d309131860..38b46ad936 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -35,6 +35,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index f045159d41..0d1b119dc1 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -37,12 +37,17 @@
#include "machine-dbus.h"
#include "formats-util.h"
-Machine* machine_new(Manager *manager, const char *name) {
+Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
Machine *m;
assert(manager);
+ assert(class < _MACHINE_CLASS_MAX);
assert(name);
+ /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
+ * means as much as "we don't know yet", and that we'll figure
+ * it out later when loading the state file. */
+
m = new0(Machine, 1);
if (!m)
return NULL;
@@ -51,14 +56,17 @@ Machine* machine_new(Manager *manager, const char *name) {
if (!m->name)
goto fail;
- m->state_file = strappend("/run/systemd/machines/", m->name);
- if (!m->state_file)
- goto fail;
+ if (class != MACHINE_HOST) {
+ m->state_file = strappend("/run/systemd/machines/", m->name);
+ if (!m->state_file)
+ goto fail;
+ }
+
+ m->class = class;
if (hashmap_put(manager->machines, m->name, m) < 0)
goto fail;
- m->class = _MACHINE_CLASS_INVALID;
m->manager = manager;
return m;
@@ -86,6 +94,9 @@ void machine_free(Machine *m) {
(void) hashmap_remove(m->manager->machines, m->name);
+ if (m->manager->host_machine == m)
+ m->manager->host_machine = NULL;
+
if (m->leader > 0)
(void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
@@ -105,7 +116,9 @@ int machine_save(Machine *m) {
int r;
assert(m);
- assert(m->state_file);
+
+ if (!m->state_file)
+ return 0;
if (!m->started)
return 0;
@@ -244,6 +257,9 @@ int machine_load(Machine *m) {
assert(m);
+ if (!m->state_file)
+ return 0;
+
r = parse_env_file(m->state_file, NEWLINE,
"SCOPE", &m->unit,
"SCOPE_JOB", &m->scope_job,
@@ -325,6 +341,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er
int r = 0;
assert(m);
+ assert(m->class != MACHINE_HOST);
if (!m->unit) {
_cleanup_free_ char *escaped = NULL;
@@ -364,6 +381,9 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
assert(m);
+ if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
+ return -EOPNOTSUPP;
+
if (m->started)
return 0;
@@ -402,6 +422,7 @@ static int machine_stop_scope(Machine *m) {
int r;
assert(m);
+ assert(m->class != MACHINE_HOST);
if (!m->unit)
return 0;
@@ -422,6 +443,9 @@ int machine_stop(Machine *m) {
int r;
assert(m);
+ if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
+ return -EOPNOTSUPP;
+
r = machine_stop_scope(m);
m->stopping = true;
@@ -456,6 +480,9 @@ int machine_finalize(Machine *m) {
bool machine_check_gc(Machine *m, bool drop_not_started) {
assert(m);
+ if (m->class == MACHINE_HOST)
+ return true;
+
if (drop_not_started && !m->started)
return false;
@@ -481,6 +508,9 @@ void machine_add_to_gc_queue(Machine *m) {
MachineState machine_get_state(Machine *s) {
assert(s);
+ if (s->class == MACHINE_HOST)
+ return MACHINE_RUNNING;
+
if (s->stopping)
return MACHINE_CLOSING;
@@ -493,6 +523,9 @@ MachineState machine_get_state(Machine *s) {
int machine_kill(Machine *m, KillWho who, int signo) {
assert(m);
+ if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
+ return -EOPNOTSUPP;
+
if (!m->unit)
return -ESRCH;
@@ -509,6 +542,25 @@ int machine_kill(Machine *m, KillWho who, int signo) {
return manager_kill_unit(m->manager, m->unit, signo, NULL);
}
+int machine_openpt(Machine *m, int flags) {
+ assert(m);
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ return posix_openpt(flags);
+
+ case MACHINE_CONTAINER:
+ if (m->leader <= 0)
+ return -EINVAL;
+
+ return openpt_in_namespace(m->leader, flags);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
MachineOperation *machine_operation_unref(MachineOperation *o) {
if (!o)
return NULL;
@@ -544,7 +596,8 @@ void machine_release_unit(Machine *m) {
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
- [MACHINE_VM] = "vm"
+ [MACHINE_VM] = "vm",
+ [MACHINE_HOST] = "host",
};
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 0132b65a97..5f978289f2 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -39,6 +39,7 @@ typedef enum MachineState {
typedef enum MachineClass {
MACHINE_CONTAINER,
MACHINE_VM,
+ MACHINE_HOST,
_MACHINE_CLASS_MAX,
_MACHINE_CLASS_INVALID = -1
} MachineClass;
@@ -95,7 +96,7 @@ struct Machine {
unsigned n_operations;
};
-Machine* machine_new(Manager *manager, const char *name);
+Machine* machine_new(Manager *manager, MachineClass class, const char *name);
void machine_free(Machine *m);
bool machine_check_gc(Machine *m, bool drop_not_started);
void machine_add_to_gc_queue(Machine *m);
@@ -120,3 +121,5 @@ MachineState machine_state_from_string(const char *s) _pure_;
const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
+
+int machine_openpt(Machine *m, int flags);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 66ed41087c..6b29e61642 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -55,6 +55,8 @@
#include "process-util.h"
#include "terminal-util.h"
#include "signal-util.h"
+#include "env-util.h"
+#include "hostname-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
@@ -75,6 +77,8 @@ static bool arg_force = false;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
static const char* arg_dkr_index_url = NULL;
static const char* arg_format = NULL;
+static const char *arg_uid = NULL;
+static char **arg_setenv = NULL;
static void pager_open_if_enabled(void) {
@@ -154,6 +158,9 @@ static int list_machines(int argc, char *argv[], void *userdata) {
while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
size_t l;
+ if (name[0] == '.' && !arg_all)
+ continue;
+
if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
return log_oom();
@@ -354,7 +361,8 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
bus,
"org.freedesktop.systemd1",
path,
- endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
+ endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" :
+ endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service",
"ControlGroup",
&error,
&reply,
@@ -368,9 +376,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
if (r < 0)
return bus_log_parse_error(r);
- if (isempty(cgroup))
- return 0;
-
if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
return 0;
@@ -1170,20 +1175,72 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r
return 0;
}
+static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) {
+ char last_char = 0;
+ bool machine_died;
+ int ret = 0, r;
+
+ assert(event);
+ assert(master >= 0);
+ assert(name);
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+
+ if (streq(name, ".host"))
+ log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
+ else
+ log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
+
+ sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+
+ r = pty_forward_new(event, master, ignore_vhangup, false, forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ pty_forward_get_last_char(*forward, &last_char);
+
+ machine_died =
+ ignore_vhangup &&
+ pty_forward_get_ignore_vhangup(*forward) == 0;
+
+ *forward = pty_forward_free(*forward);
+
+ if (last_char != '\n')
+ fputc('\n', stdout);
+
+ if (machine_died)
+ log_info("Machine %s terminated.", name);
+ else if (streq(name, ".host"))
+ log_info("Connection to the local host terminated.");
+ else
+ log_info("Connection to machine %s terminated.", name);
+
+ sd_event_get_exit_code(event, &ret);
+ return ret;
+}
+
static int login_machine(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
- int master = -1, r, ret = 0;
+ int master = -1, r;
sd_bus *bus = userdata;
- const char *pty, *match;
- char last_char = 0;
- bool machine_died;
+ const char *pty, *match, *machine;
assert(bus);
+ if (!strv_isempty(arg_setenv) || arg_uid) {
+ log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
+ return -EINVAL;
+ }
+
if (arg_transport != BUS_TRANSPORT_LOCAL &&
arg_transport != BUS_TRANSPORT_MACHINE) {
log_error("Login only supported on local machines.");
@@ -1200,14 +1257,14 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
+ machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
+
match = strjoina("type='signal',"
- "sender='org.freedesktop.machine1',"
- "path='/org/freedesktop/machine1',",
- "interface='org.freedesktop.machine1.Manager',"
- "member='MachineRemoved',"
- "arg0='",
- argv[1],
- "'");
+ "sender='org.freedesktop.machine1',"
+ "path='/org/freedesktop/machine1',",
+ "interface='org.freedesktop.machine1.Manager',"
+ "member='MachineRemoved',"
+ "arg0='", machine, "'");
r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
if (r < 0)
@@ -1221,9 +1278,9 @@ static int login_machine(int argc, char *argv[], void *userdata) {
"OpenMachineLogin",
&error,
&reply,
- "s", argv[1]);
+ "s", machine);
if (r < 0) {
- log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
+ log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
return r;
}
@@ -1231,36 +1288,100 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ return process_forward(event, &forward, master, true, machine);
+}
- log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
+static int shell_machine(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ int master = -1, r;
+ sd_bus *bus = userdata;
+ const char *pty, *match, *machine, *path, *uid = NULL;
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ assert(bus);
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL &&
+ arg_transport != BUS_TRANSPORT_MACHINE) {
+ log_error("Shell only supported on local machines.");
+ return -EOPNOTSUPP;
+ }
- r = pty_forward_new(event, master, true, false, &forward);
+ polkit_agent_open_if_enabled();
+
+ r = sd_event_default(&event);
if (r < 0)
- return log_error_errno(r, "Failed to create PTY forwarder: %m");
+ return log_error_errno(r, "Failed to get event loop: %m");
- r = sd_event_loop(event);
+ r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
+ return log_error_errno(r, "Failed to attach bus to event loop: %m");
- pty_forward_get_last_char(forward, &last_char);
- machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
+ machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
- forward = pty_forward_free(forward);
+ if (arg_uid)
+ uid = arg_uid;
+ else if (machine) {
+ const char *at;
- if (last_char != '\n')
- fputc('\n', stdout);
+ at = strchr(machine, '@');
+ if (at) {
+ uid = strndupa(machine, at - machine);
+ machine = at + 1;
+ }
+ }
- if (machine_died)
- log_info("Machine %s terminated.", argv[1]);
- else
- log_info("Connection to machine %s terminated.", argv[1]);
+ if (isempty(machine))
+ machine = ".host";
- sd_event_get_exit_code(event, &ret);
- return ret;
+ match = strjoina("type='signal',"
+ "sender='org.freedesktop.machine1',"
+ "path='/org/freedesktop/machine1',",
+ "interface='org.freedesktop.machine1.Manager',"
+ "member='MachineRemoved',"
+ "arg0='", machine, "'");
+
+ r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add machine removal match: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "OpenMachineShell");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
+
+ r = sd_bus_message_append(m, "sss", machine, uid, path);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_setenv);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
+ return r;
+ }
+
+ r = sd_bus_message_read(reply, "hs", &master, &pty);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return process_forward(event, &forward, master, false, machine);
}
static int remove_image(int argc, char *argv[], void *userdata) {
@@ -2314,6 +2435,8 @@ static int help(int argc, char *argv[], void *userdata) {
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
+ " --uid=USER Specify user ID to invoke shell as\n"
+ " --setenv=VAR=VALUE Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
@@ -2328,9 +2451,13 @@ static int help(int argc, char *argv[], void *userdata) {
"Machine Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container details\n"
- " show NAME... Show properties of one or more VMs/containers\n"
+ " show [NAME...] Show properties of one or more VMs/containers\n"
" start NAME... Start container as a service\n"
- " login NAME Get a login prompt on a container\n"
+ " login [NAME] Get a login prompt in a container or on the\n"
+ " local host\n"
+ " shell [[USER@]NAME [COMMAND...]]\n"
+ " Invoke a shell (or other command) in a container\n"
+ " or on the local host\n"
" enable NAME... Enable automatic container start at boot\n"
" disable NAME... Disable automatic container start at boot\n"
" poweroff NAME... Power off one or more containers\n"
@@ -2342,8 +2469,8 @@ static int help(int argc, char *argv[], void *userdata) {
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
"Image Commands:\n"
" list-images Show available container and VM images\n"
- " image-status NAME... Show image details\n"
- " show-image NAME... Show properties of image\n"
+ " image-status [NAME...] Show image details\n"
+ " show-image [NAME...] Show properties of image\n"
" clone NAME NAME Clone an image\n"
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
@@ -2378,6 +2505,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FORCE,
ARG_DKR_INDEX_URL,
ARG_FORMAT,
+ ARG_UID,
+ ARG_SETENV,
};
static const struct option options[] = {
@@ -2402,6 +2531,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "force", no_argument, NULL, ARG_FORCE },
{ "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
{ "format", required_argument, NULL, ARG_FORMAT },
+ { "uid", required_argument, NULL, ARG_UID },
+ { "setenv", required_argument, NULL, ARG_SETENV },
{}
};
@@ -2532,6 +2663,21 @@ static int parse_argv(int argc, char *argv[]) {
arg_format = optarg;
break;
+ case ARG_UID:
+ arg_uid = optarg;
+ break;
+
+ case ARG_SETENV:
+ if (!env_assignment_is_valid(optarg)) {
+ log_error("Environment assignment invalid: %s", optarg);
+ return -EINVAL;
+ }
+
+ r = strv_extend(&arg_setenv, optarg);
+ if (r < 0)
+ return log_oom();
+ break;
+
case '?':
return -EINVAL;
@@ -2556,7 +2702,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "reboot", 2, VERB_ANY, 0, reboot_machine },
{ "poweroff", 2, VERB_ANY, 0, poweroff_machine },
{ "kill", 2, VERB_ANY, 0, kill_machine },
- { "login", 2, 2, 0, login_machine },
+ { "login", VERB_ANY, 2, 0, login_machine },
+ { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
{ "bind", 3, 4, 0, bind_mount },
{ "copy-to", 3, 4, 0, copy_files },
{ "copy-from", 3, 4, 0, copy_files },
@@ -2610,6 +2757,7 @@ finish:
polkit_agent_close();
strv_free(arg_property);
+ strv_free(arg_setenv);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 08a7f58ef5..0d52c693e4 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -33,6 +33,7 @@
#include "btrfs-util.h"
#include "formats-util.h"
#include "process-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "machine-pool.h"
#include "image-dbus.h"
@@ -637,6 +638,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd
return bus_machine_method_open_login(message, machine, error);
}
+static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_open_shell(message, machine, error);
+}
+
static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
@@ -860,6 +882,9 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+ if (machine->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+
p = procfs_file_alloca(machine->leader, "uid_map");
f = fopen(p, "re");
if (!f)
@@ -912,6 +937,9 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
_cleanup_fclose_ FILE *f = NULL;
char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ if (machine->class != MACHINE_CONTAINER)
+ continue;
+
xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader);
f = fopen(p, "re");
if (!f) {
@@ -972,6 +1000,9 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+ if (machine->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+
p = procfs_file_alloca(machine->leader, "gid_map");
f = fopen(p, "re");
if (!f)
@@ -1024,6 +1055,9 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
_cleanup_fclose_ FILE *f = NULL;
char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ if (machine->class != MACHINE_CONTAINER)
+ continue;
+
xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader);
f = fopen(p, "re");
if (!f) {
@@ -1085,6 +1119,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1455,7 +1490,6 @@ int manager_job_is_active(Manager *manager, const char *path) {
}
int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
- _cleanup_free_ char *unit = NULL;
Machine *mm;
int r;
@@ -1463,12 +1497,14 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
assert(pid >= 1);
assert(machine);
- r = cg_pid_get_unit(pid, &unit);
- if (r < 0)
- mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
- else
- mm = hashmap_get(m->machine_units, unit);
+ mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
+ if (!mm) {
+ _cleanup_free_ char *unit = NULL;
+ r = cg_pid_get_unit(pid, &unit);
+ if (r >= 0)
+ mm = hashmap_get(m->machine_units, unit);
+ }
if (!mm)
return 0;
@@ -1484,7 +1520,7 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
machine = hashmap_get(m->machines, name);
if (!machine) {
- machine = machine_new(m, name);
+ machine = machine_new(m, _MACHINE_CLASS_INVALID, name);
if (!machine)
return -ENOMEM;
}
diff --git a/src/machine/machined.c b/src/machine/machined.c
index c8ad157326..df3cc9972a 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -30,6 +30,7 @@
#include "label.h"
#include "formats-util.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "machined.h"
@@ -89,6 +90,45 @@ void manager_free(Manager *m) {
free(m);
}
+static int manager_add_host_machine(Manager *m) {
+ _cleanup_free_ char *rd = NULL, *unit = NULL;
+ sd_id128_t mid;
+ Machine *t;
+ int r;
+
+ if (m->host_machine)
+ return 0;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get machine ID: %m");
+
+ rd = strdup("/");
+ if (!rd)
+ return log_oom();
+
+ unit = strdup("-.slice");
+ if (!unit)
+ return log_oom();
+
+ t = machine_new(m, MACHINE_HOST, ".host");
+ if (!t)
+ return log_oom();
+
+ t->leader = 1;
+ t->id = mid;
+
+ t->root_directory = rd;
+ t->unit = unit;
+ rd = unit = NULL;
+
+ dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0);
+
+ m->host_machine = t;
+
+ return 0;
+}
+
int manager_enumerate_machines(Manager *m) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -96,6 +136,10 @@ int manager_enumerate_machines(Manager *m) {
assert(m);
+ r = manager_add_host_machine(m);
+ if (r < 0)
+ return r;
+
/* Read in machine data stored on disk */
d = opendir("/run/systemd/machines");
if (!d) {
@@ -117,11 +161,12 @@ int manager_enumerate_machines(Manager *m) {
if (startswith(de->d_name, "unit:"))
continue;
+ if (!machine_name_is_valid(de->d_name))
+ continue;
+
k = manager_add_machine(m, de->d_name, &machine);
if (k < 0) {
- log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
-
- r = k;
+ r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
continue;
}
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 61dbefb5f1..b3e59bf998 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -48,6 +48,8 @@ struct Manager {
sd_event_source *image_cache_defer_event;
LIST_HEAD(Machine, machine_gc_queue);
+
+ Machine *host_machine;
};
Manager *manager_new(void);
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index d58f01507b..9d40b90151 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -70,6 +70,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
+ send_member="OpenMachineShell"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
send_member="TerminateMachine"/>
<allow send_destination="org.freedesktop.machine1"
@@ -142,6 +146,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Machine"
+ send_member="OpenShell"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
send_member="Terminate"/>
<allow send_destination="org.freedesktop.machine1"
diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in
index 02714e83ae..6e35c5c045 100644
--- a/src/machine/org.freedesktop.machine1.policy.in
+++ b/src/machine/org.freedesktop.machine1.policy.in
@@ -26,6 +26,58 @@
</defaults>
</action>
+ <action id="org.freedesktop.machine1.host-login">
+ <_description>Log into the local host</_description>
+ <_message>Authentication is required to log into the local host.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.machine1.shell">
+ <_description>Acquire a shell in a local container</_description>
+ <_message>Authentication is required to acquire a shell in a local container.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.login</annotate>
+ </action>
+
+ <action id="org.freedesktop.machine1.host-shell">
+ <_description>Acquire a shell on the local host</_description>
+ <_message>Authentication is required to acquire a shell on the local host.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.host-login</annotate>
+ </action>
+
+ <action id="org.freedesktop.machine1.open-pty">
+ <_description>Acquire a pseudo TTY in a local container</_description>
+ <_message>Authentication is acquire a pseudo TTY in a local container.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.machine1.host-open-pty">
+ <_description>Acquire a pseudo TTY on the local host</_description>
+ <_message>Authentication is acquire a pseudo TTY on the local host.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.machine1.manage-machines">
<_description>Manage local virtual machines and containers</_description>
<_message>Authentication is required to manage local virtual machines and containers.</_message>
@@ -34,6 +86,7 @@
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.shell org.freedesktop.login1.login</annotate>
</action>
<action id="org.freedesktop.machine1.manage-images">
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index cdec83d074..5758ea1569 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -30,6 +30,7 @@
#include "bus-util.h"
#include "bus-common-errors.h"
#include "in-addr-util.h"
+#include "hostname-util.h"
NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
NSS_GETPW_PROTOTYPES(mymachines);
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 60144ab8a6..9a5ffb7a6c 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -34,6 +34,7 @@
#include "formats-util.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "hostname-util.h"
/* up to three lines (each up to 100 characters),
or 300 characters, whichever is less */
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index 8f66df7718..63f1e4ca6f 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -204,12 +204,13 @@ _pure_ static const char *sanitize_id(const char *id) {
return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
}
-int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
+int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
struct utmpx store = {
.ut_type = INIT_PROCESS,
.ut_pid = pid,
.ut_session = sid,
};
+ int r;
assert(id);
@@ -221,7 +222,26 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line
if (line)
strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
- return write_entry_both(&store);
+ r = write_entry_both(&store);
+ if (r < 0)
+ return r;
+
+ if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) {
+ store.ut_type = LOGIN_PROCESS;
+ r = write_entry_both(&store);
+ if (r < 0)
+ return r;
+ }
+
+ if (ut_type == USER_PROCESS) {
+ store.ut_type = USER_PROCESS;
+ strncpy(store.ut_user, user, sizeof(store.ut_user)-1);
+ r = write_entry_both(&store);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
}
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h
index 5d26ba6fb1..e0ceb873ac 100644
--- a/src/shared/utmp-wtmp.h
+++ b/src/shared/utmp-wtmp.h
@@ -31,7 +31,7 @@ int utmp_put_reboot(usec_t timestamp);
int utmp_put_runlevel(int runlevel, int previous);
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
-int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line);
+int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user);
int utmp_wall(
const char *message,
@@ -57,7 +57,7 @@ static inline int utmp_put_runlevel(int runlevel, int previous) {
static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
return 0;
}
-static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
+static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
return 0;
}
static inline int utmp_wall(
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 3cb5f61868..587793fb17 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3635,7 +3635,14 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
if (r < 0)
return bus_log_parse_error(r);
- if (!isempty(s)) {
+ if (streq(name, "ControlGroup"))
+ i->control_group = s;
+ else if (!isempty(s)) {
+ /* For all but the cgroup path (see above) we
+ * consider the empty string as unset. For the
+ * cgroup path the empty string refers to the
+ * root of the cgroup tree. */
+
if (streq(name, "Id"))
i->id = s;
else if (streq(name, "LoadState"))
@@ -3658,8 +3665,6 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->control_group = e;
}
#endif
- else if (streq(name, "ControlGroup"))
- i->control_group = s;
else if (streq(name, "StatusText"))
i->status_text = s;
else if (streq(name, "PIDFile"))