Merge pull request #1022 from poettering/machinectl-shell

Add new "machinectl shell" command for su(1)-like behaviour
This commit is contained in:
Tom Gundersen
2015-08-25 16:46:27 +02:00
40 changed files with 1351 additions and 308 deletions
+170 -17
View File
@@ -1,4 +1,4 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -65,6 +65,43 @@
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
virtual machine and container registration manager
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><command>machinectl</command> may be used to execute
operations on machines and images. Machines in this sense are
considered running instances of:</para>
<itemizedlist>
<listitem><para>Virtual Machines (VMs) that virtualize hardware
to run full operating system (OS) instances (including their kernels)
in a virtualized environment on top of the host OS.</para></listitem>
<listitem><para>Containers that share the hardware and
OS kernel with the host OS, in order to run
OS userspace instances on top the host OS.</para></listitem>
<listitem><para>The host system itself</para></listitem>
</itemizedlist>
<para>Machines are identified by names that follow the same rules
as UNIX and DNS host names, for details see below. Machines are
instantiated from disk or file system images, that frequently but not
necessarily carry the same name as machines running from
them. Images in this sense are considered:</para>
<itemizedlist>
<listitem><para>Directory trees containing an OS, including its
top-level directories <filename>/usr</filename>,
<filename>/etc</filename>, and so on.</para></listitem>
<listitem><para>btrfs subvolumes containing OS trees, similar to
normal directory trees.</para></listitem>
<listitem><para>Binary "raw" disk images containing MBR or GPT
partition tables and Linux file system partitions.</para></listitem>
<listitem><para>The file system tree of the host OS itself.</para></listitem>
</itemizedlist>
</refsect1>
<refsect1>
@@ -137,6 +174,30 @@
<constant>SIGTERM</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--uid=</option></term>
<listitem><para>When used with the <command>shell</command>
command, chooses the user ID to open the interactive shell
session as. If this switch is not specified, defaults to
<literal>root</literal>. Note that this switch is not
supported for the <command>login</command> command (see
below).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--setenv=</option></term>
<listitem><para>When used with the <command>shell</command>
command, sets an environment variable to pass to the executed
shell. Takes a pair of environment variable name and value,
separated by <literal>=</literal> as argument. This switch
may be used multiple times to set multiple environment
variables. Note that this switch is not supported for the
<command>login</command> command (see
below).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--mkdir</option></term>
@@ -145,7 +206,6 @@
mount.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--read-only</option></term>
@@ -247,9 +307,11 @@
<term><command>list</command></term>
<listitem><para>List currently running (online) virtual
machines and containers. To enumerate container images that
can be started, use <command>list-images</command> (see
below).</para></listitem>
machines and containers. To enumerate machine images that can
be started, use <command>list-images</command> (see
below). Note that this command hides the special
<literal>.host</literal> machine by default. Use the
<option>--all</option> switch to show it.</para></listitem>
</varlistentry>
<varlistentry>
@@ -267,7 +329,7 @@
</varlistentry>
<varlistentry>
<term><command>show</command> <replaceable>NAME</replaceable>...</term>
<term><command>show</command> [<replaceable>NAME</replaceable>...]</term>
<listitem><para>Show properties of one or more registered
virtual machines or containers or the manager itself. If no
@@ -316,21 +378,66 @@
</varlistentry>
<varlistentry>
<term><command>login</command> <replaceable>NAME</replaceable></term>
<term><command>login</command> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Open an interactive terminal login session to
a container. This will create a TTY connection to a specific
container and asks for the execution of a getty on it. Note
that this is only supported for containers running
<listitem><para>Open an interactive terminal login session in
a container or on the local host. If an argument is supplied
it refers to the container machine to connect to. If none is
specified, or the container name is specified as the empty
string, or the special machine name <literal>.host</literal>
(see below) is specified, the connection is made to the local
host instead. This will create a TTY connection to a specific
container or the local host and asks for the execution of a
getty on it. Note that this is only supported for containers
running
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
as init system.</para>
<para>This command will open a full login prompt on the
container, which then asks for username and password. Use
container or the local host, which then asks for username and
password. Use <command>shell</command> (see below) or
<citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
with the <option>--machine=</option> switch to invoke a single
command, either interactively or in the background within a
local container.</para></listitem>
with the <option>--machine=</option> switch to directly invoke
a single command, either interactively or in the
background.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>shell</command> [[<replaceable>NAME</replaceable>@]<replaceable>NAME</replaceable> [<replaceable>PATH</replaceable> [<replaceable>ARGUMENTS</replaceable>...]]] </term>
<listitem><para>Open an interactive shell session in a
container or on the local host. The first argument refers to
the container machine to connect to. If none is specified, or
the machine name is specified as the empty string, or the
special machine name <literal>.host</literal> (see below) is
specified, the connection is made to the local host
instead. This works similar to <command>login</command> but
immediately invokes a user process. This command runs the
specified executable with the specified arguments, or
<filename>/bin/sh</filename> if none is specified. By default
opens a <literal>root</literal> shell, but by using
<option>--uid=</option>, or by prefixing the machine name with
a username and an <literal>@</literal> character, a different
user may be selected. Use <option>--setenv=</option> to set
environment variables for the executed process.</para>
<para>When using the <command>shell</command> command without
arguments (thus invoking the executed shell or command on the
local host) it is similar in many ways to a <citerefentry
project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>
session, but unlike <command>su</command> completely isolates
the new session from the originating session, so that it
shares no process or session properties, and is in a clean and
well-defined state. It will be tracked in a new utmp, login,
audit and keyring session, and will not inherit an environment
variables or resource limits, among other properties.</para>
<para>Note that the
<citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
may be used in place of the <command>shell</command> command,
and allows more detailed, low-level configuration of the
invoked unit. However, it is frequently more privileged than
the <command>shell</command> command.</para></listitem>
</varlistentry>
<varlistentry>
@@ -453,7 +560,7 @@
</varlistentry>
<varlistentry>
<term><command>image-status</command> <replaceable>NAME</replaceable>...</term>
<term><command>image-status</command> [<replaceable>NAME</replaceable>...]</term>
<listitem><para>Show terse status information about one or
more container or VM images. This function is intended to
@@ -463,7 +570,7 @@
</varlistentry>
<varlistentry>
<term><command>show-image</command> <replaceable>NAME</replaceable>...</term>
<term><command>show-image</command> [<replaceable>NAME</replaceable>...]</term>
<listitem><para>Show properties of one or more registered
virtual machine or container images, or the manager itself. If
@@ -765,6 +872,41 @@
</refsect1>
<refsect1>
<title>Machine and Image Names</title>
<para>The <command>machinectl</command> tool operates on machines
and images, whose names must be chosen following strict
rules. Machine names must be suitable for use as host names
following a conservative subset of DNS and UNIX/Linux
semantics. Specifically, they must consist of one or more
non-empty label strings, separated by dots. No leading or trailing
dots are allowed. No sequences of multiple dots are allowed. The
label strings may only consists of alphanumeric characters as well
as the dash and underscore. The maximum length of a machine name
is 64 characters.</para>
<para>A special machine with the name <literal>.host</literal>
refers to the running host system itself. This is useful for execution
operations or inspecting the host system as well. Not that
<command>machinectl list</command> will not show this special
machine unless the <option>--all</option> switch is specified.</para>
<para>Requirements on image names are less strict, however must be
valid UTF-8, must be suitable as file names (hence not be the
single or double dot, and not include a slash), and may not
contain control characters. Since many operations search for an
image by the name of a requested machine it is recommended to name
images in the same strict fashion as machines.</para>
<para>A special image with the name <literal>.host</literal>
refers to the image of the running host system. It is hence
conceptually maps to the special <literal>.host</literal> machine
name described above. Note that <command>machinectl
list-images</command> won't show this special image either, unless
<option>--all</option> is specified.</para>
</refsect1>
<refsect1>
<title>Files and Directories</title>
@@ -872,6 +1014,17 @@
current directory.</para>
</example>
<example>
<title>Create a new shell session</title>
<programlisting># machinectl shell --uid=lennart</programlisting>
<para>This creates a new shell session on the local host, for
the user ID <literal>lennart</literal>, in a <citerefentry
project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>-like
fashion.</para>
</example>
</refsect1>
<refsect1>
+38 -3
View File
@@ -1,3 +1,4 @@
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -911,10 +912,16 @@
<term><varname>UtmpIdentifier=</varname></term>
<listitem><para>Takes a four character identifier string for
an utmp/wtmp entry for this service. This should only be set
for services such as <command>getty</command> implementations
an <citerefentry
project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and wtmp entry for this service. This should only be
set for services such as <command>getty</command>
implementations (such as <citerefentry
project='die-net'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
where utmp/wtmp entries must be created and cleared before and
after execution. If the configured string is longer than four
after execution, or for services that shall be executed as if
they were run by a <command>getty</command> process (see
below). If the configured string is longer than four
characters, it is truncated and the terminal four characters
are used. This setting interprets %I style string
replacements. This setting is unset by default, i.e. no
@@ -922,6 +929,34 @@
service.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>UtmpMode=</varname></term>
<listitem><para>Takes one of <literal>init</literal>,
<literal>login</literal> or <literal>user</literal>. If
<varname>UtmpIdentifier=</varname> is set, controls which
type of <citerefentry
project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry>/wtmp
entries for this service are generated. This setting has no
effect unless <varname>UtmpIdentifier=</varname> is set
too. If <literal>init</literal> is set, only an
<constant>INIT_PROCESS</constant> entry is generated and the
invoked process must implement a <command>getty</command>
compatible utmp/wtmp logic. If <literal>login</literal> is
set, first an <constant>INIT_PROCESS</constant> entry,
followed by an <constant>LOGIN_PROCESS</constant> entry is
generated. In this case the invoked process must implement a
<citerefentry
project='die-net'><refentrytitle>login</refentrytitle><manvolnum>1</manvolnum></citerefentry>-compatible
utmp/wtmp logic. If <literal>user</literal> is set, first an
<constant>INIT_PROCESS</constant> entry, then a
<constant>LOGIN_PROCESS</constant> entry and finally an
<constant>USER_PROCESS</constant> entry is generated. In this
case the invoked process may be any process that is suitable
to be run as session leader. Defaults to
<literal>init</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SELinuxContext=</varname></term>
+12 -7
View File
@@ -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;
+3 -1
View File
@@ -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);
+26
View File
@@ -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);
+1
View File
@@ -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) ||
+3 -15
View File
@@ -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)
-2
View File
@@ -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);
+105
View File
@@ -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;
+14 -1
View File
@@ -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);
+12
View File
@@ -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_;
+1
View File
@@ -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')
+2
View File
@@ -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,
+1
View File
@@ -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);
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -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;
+1
View File
@@ -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"
+2 -1
View File
@@ -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,
+1
View File
@@ -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"
+1
View File
@@ -43,6 +43,7 @@
#include "replace-var.h"
#include "fileio.h"
#include "formats-util.h"
#include "hostname-util.h"
#define JOURNAL_FILES_MAX 7168
+1
View File
@@ -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"
+2 -1
View File
@@ -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;
+503 -189
View File
@@ -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,77 +186,12 @@ 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);
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);
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");
if (child == 0) {
_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);
n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n < 0)
_exit(EXIT_FAILURE);
for (a = addresses, i = 0; i < n; a++, i++) {
struct iovec iov[2] = {
{ .iov_base = &a->family, .iov_len = sizeof(a->family) },
{ .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
};
r = writev(pair[1], iov, 2);
if (r < 0)
_exit(EXIT_FAILURE);
}
pair[1] = safe_close(pair[1]);
_exit(EXIT_SUCCESS);
}
pair[1] = safe_close(pair[1]);
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
@@ -264,62 +200,165 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
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,
};
switch (m->class) {
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_HOST: {
_cleanup_free_ struct local_address *addresses = NULL;
struct local_address *a;
int n, i;
n = recvmsg(pair[0], &mh, 0);
n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n < 0)
return -errno;
if ((size_t) n < sizeof(family))
break;
return n;
r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
return r;
for (a = addresses, i = 0; i < n; a++, i++) {
r = sd_bus_message_append(reply, "i", family);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
return r;
switch (family) {
r = sd_bus_message_append(reply, "i", addresses[i].family);
if (r < 0)
return r;
case AF_INET:
if (n != sizeof(struct in_addr) + sizeof(family))
return -EIO;
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_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;
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
}
if (r < 0)
return r;
r = sd_bus_message_close_container(reply);
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.");
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;
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);
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);
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");
if (child == 0) {
_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);
n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n < 0)
_exit(EXIT_FAILURE);
for (a = addresses, i = 0; i < n; a++, i++) {
struct iovec iov[2] = {
{ .iov_base = &a->family, .iov_len = sizeof(a->family) },
{ .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
};
r = writev(pair[1], iov, 2);
if (r < 0)
_exit(EXIT_FAILURE);
}
pair[1] = safe_close(pair[1]);
_exit(EXIT_SUCCESS);
}
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 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;
}
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;
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_close_ int fd = -1;
pair[0] = safe_close(pair[0]);
r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
case MACHINE_HOST:
r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l);
if (r < 0)
_exit(EXIT_FAILURE);
return r;
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)
break;
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;
r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
if (r < 0)
return r;
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
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;
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[0] = -1;
r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
if (r < 0)
return r;
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.");
break;
}
pair[1] = safe_close(pair[1]);
f = fdopen(pair[0], "re");
if (!f)
return -errno;
pair[0] = -1;
r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
if (r < 0)
return r;
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 = allocated_bus ?: m->manager->bus;
container_bus->address = address;
container_bus->bus_client = true;
container_bus->trusted = false;
container_bus->is_system = true;
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),
+1
View File
@@ -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);
+60 -7
View File
@@ -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);
+4 -1
View File
@@ -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);
+194 -46
View File
@@ -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 login_machine(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
int master = -1, r, ret = 0;
sd_bus *bus = userdata;
const char *pty, *match;
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_(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;
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);
r = pty_forward_new(event, master, true, false, &forward);
if (arg_transport != BUS_TRANSPORT_LOCAL &&
arg_transport != BUS_TRANSPORT_MACHINE) {
log_error("Shell only supported on local machines.");
return -EOPNOTSUPP;
}
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;
}
+43 -7
View File
@@ -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;
}
+48 -3
View File
@@ -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;
}
+2
View File
@@ -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);
@@ -68,6 +68,10 @@
send_interface="org.freedesktop.machine1.Manager"
send_member="OpenMachineLogin"/>
<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"/>
@@ -140,6 +144,10 @@
send_interface="org.freedesktop.machine1.Machine"
send_member="OpenLogin"/>
<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"/>
@@ -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">
+1
View File
@@ -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);
+1
View File
@@ -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 */
+22 -2
View File
@@ -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) {
+2 -2
View File
@@ -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(
+8 -3
View File
@@ -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"))