1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 08:55:40 +03:00

Compare commits

...

27 Commits

Author SHA1 Message Date
Yu Watanabe
414251d8b3
Merge afe715223c into f7078de515 2024-10-25 21:23:57 +02:00
Yu Watanabe
afe715223c machine: use JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY()
Also use JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL().
2024-10-26 04:23:45 +09:00
Yu Watanabe
e485678631 sd-json: introduce JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY() macro
It is similar to JSON_BUILD_PAIR_STRV_NON_EMPTY, but takes the
list of environment variables.
2024-10-26 04:23:45 +09:00
Yu Watanabe
c6cb7141ac sd-json: use strv_env_get_merged() 2024-10-26 04:23:45 +09:00
Yu Watanabe
b58c3d4e47 env-util: introduce strv_env_get_merged() 2024-10-26 04:23:45 +09:00
Yu Watanabe
fa402c3c1e env-util: replace 'char **' with 'char**' 2024-10-26 04:23:45 +09:00
Yu Watanabe
3d9ad7097e TEST-13-NSPAWN: trivially kill all process in the container on termination
Follow-up for 841988f80d.

No functional change, as "kill $PID" in the trap is equivalent to 'kill 0'.
Let's make the code trivial.
2024-10-26 04:23:45 +09:00
Yu Watanabe
f956b7c936 machined: do not send more replies if an error is sent
Previously, if an enumerated machine get an error in fetching
metadata, e.g. missing os-release file, then
list_machine_one_and_maybe_read_metadata() calls sd_varlink_error(),
which returns 1. Hence, the caller vl_method_list() may continue the
loop and call list_machine_one_and_maybe_read_metadata() again to send
information about another machine. But that would fail with -EBUSY, as
we already sent an error message.

Let's check the varlink state before processing machines or machine
images, and skip further operations if an error is sent.

Follow-up for a94fbcaa35.
2024-10-26 04:23:42 +09:00
Yu Watanabe
af6cd8c0b5 sd-varlink: introduce sd_varlink_is_processing_method()
Currently unused, but will be used in a later commit.
2024-10-26 04:22:06 +09:00
Yu Watanabe
177830bd84 TEST-13-NSPAWN: check returned machine list 2024-10-26 04:22:06 +09:00
Yu Watanabe
4e3396932a TEST-13-NSPAWN: fix race between container exit and varlink call
Follow-up for 3cb72c7862.

The test container existed shortly, hence when varlinkctl is called, the
container may be already terminated. Let's make the container live
infinitely.
Also, this makes the os-release files removed after the container is started.
2024-10-26 04:22:06 +09:00
Yu Watanabe
416e927e62 machined: ACQUIRE_METADATA_NO is zero
Follow-ups for a94fbcaa35 and
9de215219c.
2024-10-26 04:22:05 +09:00
Yu Watanabe
1cf4e150aa machine: use sd_json_variant_append_arraybo() and JSON_BUILD_PAIR_VARIANT_NON_NULL()
Follow-up for 45755275e5.
2024-10-26 04:22:05 +09:00
Yu Watanabe
f7078de515
Merge pull request #34884 from poettering/run0-disconnect-fix
run: reconnect if our dbus connection is terminated
2024-10-26 02:50:48 +09:00
Yu Watanabe
6d6048b4cb
Merge pull request #34881 from poettering/run0-ui-tweaks
run0: various UI tweaks
2024-10-26 02:49:48 +09:00
Ivan Kruglov
10a48938ef machine: operation should not send a response when 'done' callback set 2024-10-26 02:45:53 +09:00
Lennart Poettering
b58b13f1c6 test: add brief testcase for systemd-run disconnect handling 2024-10-25 17:51:04 +02:00
Lennart Poettering
c8f59296bf run: reconnect if our dbus connection is terminated
We must be prepared that systemd temporarily drops off the bus or
disconnects our direct connections (due to systemctl daemon-reexec or
so). Hence automatically reconnect when we watch the unit status, and
handle this case gracefully.

Fixes: #32906 #27204
2024-10-25 17:51:04 +02:00
Lennart Poettering
d585085f57 update TODO 2024-10-25 17:32:19 +02:00
Lennart Poettering
ff4b6a1915 run: drop "-" prefix from command line when generating unit description
Let's not confuse users with the login shell indicator and drop it from
the description. This means a run0 session will now usually show up with
a description of "[run0] /bin/bash" rather than "[run0] -/bin/bash".
2024-10-25 17:32:19 +02:00
Lennart Poettering
d9f68f48f7 run: prefix unit description with our own process name
I think we should try to communicate clearly if something is a run0
session, or a systemd-run invocation. Hence, let's initialize the
description so that the command is prefixed by
program_invocation_short_name.

Effectively this means that our run0 sessions now appear as services
with a description of "[run0] -/bin/bash"
2024-10-25 17:32:19 +02:00
Lennart Poettering
0310b2a60b run: tweak how we name our transient units
The current logic is a bit complex how systemd-run units are called. It
used to be just the unique ID of the dbus connection. Which was nice,
since its system-widely, uniquely assigned to us. But this didn't work
out well, due to direct connections to PID 1 and due to soft reboots.

We nowadays have a better ID to use though, with nicer properties: the
kernel manages a pidfd ID for every process after all, and it's globally
unique, for any process, and regardless of soft reboots. Hence use that
for naming preferably, and just keep one branch with a randomized name
as fallback.
2024-10-25 17:32:19 +02:00
Lennart Poettering
115fac3c29 run0: optionally show superhero emoji on each shell prompt
This makes use of the infra introduced in 229d4a9806 to indicate visually on each prompt that we are in superuser mode temporarily.
pick ad5de3222f userdbctl: add some basic client-side filtering
2024-10-25 17:31:06 +02:00
Lennart Poettering
9d8f5e22f8
Merge pull request #34891 from poettering/run0-pty
run0: add --pty and --pipe switches to force allocation of a pty or pipe
2024-10-25 16:25:01 +02:00
Lennart Poettering
6fb0c52295 ci: add some basic testing of the new --pty and --pipe switches 2024-10-25 14:14:26 +02:00
Lennart Poettering
edd10ab29c run0: add options to force allocation of PTY or of pipe use
Fixes: #33033
2024-10-25 14:14:26 +02:00
Lennart Poettering
988053eac3 tree-wide: use isatty_safe() everywhere 2024-10-25 14:09:38 +02:00
20 changed files with 548 additions and 200 deletions

3
TODO
View File

@ -162,9 +162,6 @@ Features:
sd_event_add_child(), and then get rid of many more explicit sigprocmask()
calls.
* maybe set shell.prompt.prefix credential in run0 to some warning emoji,
i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑‍🔧 or so.
* introduce new structure Tpm2CombinedPolicy, that combines the various TPm2
policy bits into one structure, i.e. public key info, pcr masks, pcrlock
stuff, pin and so on. Then pass that around in tpm2_seal() and tpm2_unseal().

View File

@ -192,6 +192,35 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pty</option></term>
<term><option>--pipe</option></term>
<listitem><para>Request allocation of a pseudo TTY for the <command>run0</command> session (in case
of <option>--pty</option>), or request passing the caller's STDIO file descriptors directly through
(in case of <option>--pipe</option>). If neither switch is specified, or if both switches are
specified, the mode will be picked automatically: if standard input, standard output and standard
error output are all connected to a TTY then a pseudo TTY is allocated, otherwise the relevant file
descriptors are passed through directly.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--shell-prompt-prefix=<replaceable>STRING</replaceable></option></term>
<listitem><para>Set a shell prompt prefix string. This ultimately controls the
<varname>$SHELL_PROMPT_PREFIX</varname> environment variable for the invoked program, which is
typically imported into the shell prompt. By default if emojis are supported a superhero emoji is
shown (🦸). This default may also be changed (or turned off) by passing the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable to <varname>run0</varname>,
see below. Set to an empty string to disable shell prompt prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>
@ -256,7 +285,30 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>By default set to the superhero emoji (if supported), but may be overriden with the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable (see below), or the
<option>--shell-prompt-prefix=</option> switch (see above).</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
<para>The following variables may be passed to <command>run0</command>:</para>
<variablelist>
<varlistentry>
<term><varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>If set, overrides the default shell prompt prefix that <command>run0</command> sets
for the invoked shell (the superhero emoji). Set to an empty string to disable shell prompt
prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>

View File

@ -266,7 +266,7 @@ static bool env_entry_has_name(const char *entry, const char *name) {
return *t == '=';
}
char **strv_env_delete(char **x, size_t n_lists, ...) {
char** strv_env_delete(char **x, size_t n_lists, ...) {
size_t n, i = 0;
_cleanup_strv_free_ char **t = NULL;
va_list ap;
@ -564,7 +564,30 @@ char* strv_env_pairs_get(char **l, const char *name) {
return result;
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
int strv_env_get_merged(char **l, char ***ret) {
_cleanup_strv_free_ char **v = NULL;
size_t n = 0;
int r;
assert(ret);
STRV_FOREACH_PAIR(key, value, l) {
char *s = NULL;
s = strjoin(*key, "=", *value);
if (!s)
return -ENOMEM;
r = strv_consume_with_size(&v, &n, s);
if (r < 0)
return r;
}
*ret = TAKE_PTR(v);
return 0;
}
char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
int k = 0;
STRV_FOREACH(p, e) {

View File

@ -34,14 +34,14 @@ int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_var
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
char** strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
char** _strv_env_merge(char **first, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char** strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char** strv_env_unset(char **l, const char *p); /* In place ... */
char** strv_env_unset_many_internal(char **l, ...) _sentinel_;
@ -60,6 +60,7 @@ static inline char* strv_env_get(char * const *x, const char *n) {
}
char* strv_env_pairs_get(char **l, const char *name) _pure_;
int strv_env_get_merged(char **l, char ***ret);
int getenv_bool(const char *p);
int secure_getenv_bool(const char *p);

View File

@ -80,6 +80,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S",
},
/* UTF-8 */
@ -149,6 +150,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
},
};

View File

@ -55,6 +55,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_SUPERHERO,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;

View File

@ -998,6 +998,7 @@ global:
sd_varlink_invoke;
sd_varlink_invokeb;
sd_varlink_is_idle;
sd_varlink_is_processing_method;
sd_varlink_notify;
sd_varlink_notifyb;
sd_varlink_observe;

View File

@ -157,6 +157,7 @@ enum {
_JSON_BUILD_PAIR_FINITE_USEC,
_JSON_BUILD_PAIR_STRING_NON_EMPTY,
_JSON_BUILD_PAIR_STRV_NON_EMPTY,
_JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY,
_JSON_BUILD_PAIR_VARIANT_NON_NULL,
/* _SD_JSON_BUILD_PAIR_VARIANT_ARRAY_NON_EMPTY, */
_JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY,
@ -204,6 +205,7 @@ enum {
#define JSON_BUILD_PAIR_FINITE_USEC(name, u) _JSON_BUILD_PAIR_FINITE_USEC, (const char*) { name }, (usec_t) { u }
#define JSON_BUILD_PAIR_STRING_NON_EMPTY(name, s) _JSON_BUILD_PAIR_STRING_NON_EMPTY, (const char*) { name }, (const char*) { s }
#define JSON_BUILD_PAIR_STRV_NON_EMPTY(name, l) _JSON_BUILD_PAIR_STRV_NON_EMPTY, (const char*) { name }, (char**) { l }
#define JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY(name, l) _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY, (const char*) { name }, (char**) { l }
#define JSON_BUILD_PAIR_VARIANT_NON_NULL(name, v) _JSON_BUILD_PAIR_VARIANT_NON_NULL, (const char*) { name }, (sd_json_variant*) { v }
#define JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY(name, v, n) _JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY, (const char*) { name }, (const void*) { v }, (size_t) { n }
#define JSON_BUILD_PAIR_IN4_ADDR_NON_NULL(name, v) _JSON_BUILD_PAIR_IN4_ADDR_NON_NULL, (const char*) { name }, (const struct in_addr*) { v }

View File

@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "ansi-color.h"
#include "env-util.h"
#include "errno-util.h"
#include "escape.h"
#include "ether-addr-util.h"
@ -3867,22 +3868,13 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
l = va_arg(ap, char **);
_cleanup_strv_free_ char **el = NULL;
STRV_FOREACH_PAIR(x, y, l) {
char *n = NULL;
if (current->n_suppress == 0) {
_cleanup_strv_free_ char **el = NULL;
n = strjoin(*x, "=", *y);
if (!n) {
r = -ENOMEM;
goto finish;
}
r = strv_consume(&el, n);
r = strv_env_get_merged(l, &el);
if (r < 0)
goto finish;
}
if (current->n_suppress == 0) {
r = sd_json_variant_new_array_strv(&add, el);
if (r < 0)
goto finish;
@ -4541,7 +4533,8 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
break;
}
case _JSON_BUILD_PAIR_STRV_NON_EMPTY: {
case _JSON_BUILD_PAIR_STRV_NON_EMPTY:
case _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY: {
const char *n;
char **l;
@ -4554,11 +4547,19 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
l = va_arg(ap, char **);
if (!strv_isempty(l) && current->n_suppress == 0) {
_cleanup_strv_free_ char **el = NULL;
if (command == _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY) {
r = strv_env_get_merged(l, &el);
if (r < 0)
goto finish;
}
r = sd_json_variant_new_string(&add, n);
if (r < 0)
goto finish;
r = sd_json_variant_new_array_strv(&add_more, l);
r = sd_json_variant_new_array_strv(&add_more, el ?: l);
if (r < 0)
goto finish;
}

View File

@ -1655,6 +1655,13 @@ _public_ int sd_varlink_is_idle(sd_varlink *v) {
return IN_SET(v->state, VARLINK_DISCONNECTED, VARLINK_IDLE_CLIENT, VARLINK_IDLE_SERVER);
}
_public_ int sd_varlink_is_processing_method(sd_varlink *v) {
if (!v)
return false;
return IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE, VARLINK_PROCESSING_METHOD_ONEWAY);
}
_public_ int sd_varlink_get_fd(sd_varlink *v) {
assert_return(v, -EINVAL);

View File

@ -392,56 +392,52 @@ static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *paramete
}
static int json_build_local_addresses(const struct local_address *addresses, size_t n_addresses, sd_json_variant **ret) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
int r;
if (n_addresses == 0)
return 0;
assert(addresses);
assert(addresses || n_addresses == 0);
assert(ret);
FOREACH_ARRAY(a, addresses, n_addresses) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
r = sd_json_buildo(
&entry,
r = sd_json_variant_append_arraybo(
&array,
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("ifindex", a->ifindex),
SD_JSON_BUILD_PAIR_INTEGER("family", a->family),
SD_JSON_BUILD_PAIR_BYTE_ARRAY("address", &a->address.bytes, FAMILY_ADDRESS_SIZE(a->family)));
if (r < 0)
return r;
r = sd_json_variant_append_array(ret, entry);
if (r < 0)
return r;
}
*ret = TAKE_PTR(array);
return 0;
}
static int list_machine_one_and_maybe_read_metadata(sd_varlink *link, Machine *m, bool more, AcquireMetadata am) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *addr_array = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *addr_array = NULL;
_cleanup_strv_free_ char **os_release = NULL;
uid_t shift = UID_INVALID;
int r, n = 0;
int r;
assert(link);
assert(m);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
if (!sd_varlink_is_processing_method(link))
return 0;
if (should_acquire_metadata(am)) {
_cleanup_free_ struct local_address *addresses = NULL;
n = machine_get_addresses(m, &addresses);
if (n < 0 && am == ACQUIRE_METADATA_GRACEFUL)
log_debug_errno(n, "Failed to get address (graceful mode), ignoring: %m");
else if (n == -ENONET)
r = machine_get_addresses(m, &addresses);
if (r < 0 && am == ACQUIRE_METADATA_GRACEFUL)
log_debug_errno(r, "Failed to get address (graceful mode), ignoring: %m");
else if (r == -ENONET)
return sd_varlink_error(link, "io.systemd.Machine.NoPrivateNetworking", NULL);
else if (ERRNO_IS_NEG_NOT_SUPPORTED(n))
else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return sd_varlink_error(link, "io.systemd.Machine.NotAvailable", NULL);
else if (n < 0)
return log_debug_errno(n, "Failed to get addresses: %m");
else if (r < 0)
return log_debug_errno(r, "Failed to get addresses: %m");
else {
r = json_build_local_addresses(addresses, n, &addr_array);
r = json_build_local_addresses(addresses, r, &addr_array);
if (r < 0)
return r;
}
@ -477,11 +473,11 @@ static int list_machine_one_and_maybe_read_metadata(sd_varlink *link, Machine *m
JSON_BUILD_PAIR_STRING_NON_EMPTY("unit", m->unit),
SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->leader), "leader", JSON_BUILD_PIDREF(&m->leader)),
SD_JSON_BUILD_PAIR_CONDITION(dual_timestamp_is_set(&m->timestamp), "timestamp", JSON_BUILD_DUAL_TIMESTAMP(&m->timestamp)),
SD_JSON_BUILD_PAIR_CONDITION(m->vsock_cid != VMADDR_CID_ANY, "vSockCid", SD_JSON_BUILD_UNSIGNED(m->vsock_cid)),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("vSockCid", m->vsock_cid, VMADDR_CID_ANY),
JSON_BUILD_PAIR_STRING_NON_EMPTY("sshAddress", m->ssh_address),
JSON_BUILD_PAIR_STRING_NON_EMPTY("sshPrivateKeyPath", m->ssh_private_key_path),
SD_JSON_BUILD_PAIR_CONDITION(n > 0, "addresses", SD_JSON_BUILD_VARIANT(addr_array)),
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(os_release), "OSRelease", JSON_BUILD_STRV_ENV_PAIR(os_release)),
JSON_BUILD_PAIR_VARIANT_NON_NULL("addresses", addr_array),
JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", os_release),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("UIDShift", shift, UID_INVALID));
if (r < 0)
return r;
@ -517,7 +513,6 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
Manager *m = ASSERT_PTR(userdata);
_cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = {
.pidref = PIDREF_NULL,
.acquire_metadata = ACQUIRE_METADATA_NO,
};
Machine *machine;
@ -604,6 +599,9 @@ static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image
assert(link);
assert(image);
if (!sd_varlink_is_processing_method(link))
return 0;
if (should_acquire_metadata(am) && !image->metadata_valid) {
r = image_read_metadata(image, &image_policy_container);
if (r < 0 && am != ACQUIRE_METADATA_GRACEFUL)
@ -635,8 +633,8 @@ static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image
&v,
JSON_BUILD_PAIR_STRING_NON_EMPTY("hostname", image->hostname),
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(image->machine_id), "machineId", SD_JSON_BUILD_ID128(image->machine_id)),
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(image->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(image->machine_info)),
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(image->os_release), "OSRelease", JSON_BUILD_STRV_ENV_PAIR(image->os_release)));
JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("machineInfo", image->machine_info),
JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", image->os_release));
if (r < 0)
return r;
}
@ -651,7 +649,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
struct params {
const char *image_name;
AcquireMetadata acquire_metadata;
} p = { .acquire_metadata = ACQUIRE_METADATA_NO };
} p = {};
int r;
static const sd_json_dispatch_field dispatch_table[] = {

View File

@ -8,7 +8,7 @@
#include "operation.h"
#include "process-util.h"
static int operation_done_internal(const siginfo_t *si, Operation *o, sd_bus_error *error) {
static int read_operation_errno(const siginfo_t *si, Operation *o) {
int r;
assert(si);
@ -27,15 +27,6 @@ static int operation_done_internal(const siginfo_t *si, Operation *o, sd_bus_err
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received unexpectedly short message when reading operation's errno");
}
if (o->done)
/* A completion routine is set for this operation, call it. */
return o->done(o, r, error);
/* The default operation when done is to simply return an error on failure or an empty success
* message on success. */
if (r < 0)
log_debug_errno(r, "Operation failed: %m");
return r;
}
@ -51,10 +42,18 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
o->pid = 0;
r = read_operation_errno(si, o);
if (r < 0)
log_debug_errno(r, "Operation failed: %m");
/* If a completion routine (o->done) is set for this operation, call it. It sends a response, but can return an error in which case it expect us to reply.
* Otherwise, the default action is to simply return an error on failure or an empty success message on success. */
if (o->message) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (o->done)
r = o->done(o, r, &error);
r = operation_done_internal(si, o, &error);
if (r < 0) {
if (!sd_bus_error_is_set(&error))
sd_bus_error_set_errno(&error, r);
@ -62,16 +61,20 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
r = sd_bus_reply_method_error(o->message, &error);
if (r < 0)
log_error_errno(r, "Failed to reply to dbus message: %m");
} else {
} else if (!o->done) {
/* when o->done set it's responsible for sending reply in a happy-path case */
r = sd_bus_reply_method_return(o->message, NULL);
if (r < 0)
log_error_errno(r, "Failed to reply to dbus message: %m");
}
} else if (o->link) {
r = operation_done_internal(si, o, /* error = */ NULL);
if (o->done)
r = o->done(o, r, /* error = */ NULL);
if (r < 0)
(void) sd_varlink_error_errno(o->link, r);
else
else if (!o->done)
/* when o->done set it's responsible for sending reply in a happy-path case */
(void) sd_varlink_reply(o->link, NULL);
} else
assert_not_reached();

View File

@ -290,7 +290,7 @@ static int handle_arg_console(const char *arg) {
else if (streq(arg, "passive"))
arg_console_mode = CONSOLE_PASSIVE;
else if (streq(arg, "pipe")) {
if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO))
if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO))
log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE,
"Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. "
"Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. "
@ -298,7 +298,7 @@ static int handle_arg_console(const char *arg) {
arg_console_mode = CONSOLE_PIPE;
} else if (streq(arg, "autopipe")) {
if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO))
if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO))
arg_console_mode = CONSOLE_INTERACTIVE;
else
arg_console_mode = CONSOLE_PIPE;
@ -5981,7 +5981,7 @@ static int run(int argc, char *argv[]) {
umask(0022);
if (arg_console_mode < 0)
arg_console_mode = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
arg_console_mode = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) ?
CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */

View File

@ -22,6 +22,7 @@
#include "chase.h"
#include "env-util.h"
#include "escape.h"
#include "event-util.h"
#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
@ -85,6 +86,7 @@ static char *arg_exec_path = NULL;
static bool arg_ignore_failure = false;
static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@ -96,6 +98,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -171,6 +174,10 @@ static int help_sudo_mode(void) {
if (r < 0)
return log_oom();
/* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with
* sudo's short switches, hence please do not introduce new short switches unless they have a roughly
* equivalent purpose on sudo. Use long options for everything private to run0. */
printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
"\n%sElevate privileges interactively.%s\n\n"
" -h --help Show this help\n"
@ -188,6 +195,9 @@ static int help_sudo_mode(void) {
" -D --chdir=PATH Set working directory\n"
" --setenv=NAME[=VALUE] Set environment variable\n"
" --background=COLOR Set ANSI color for background\n"
" --pty Request allocation of a pseudo TTY for stdio\n"
" --pipe Request direct pipe for stdio\n"
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -674,7 +684,7 @@ static int parse_argv(int argc, char *argv[]) {
/* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
* to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
* pipeline, but we are neatly interactive with tty-level isolation otherwise. */
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ?
ARG_STDIO_PTY :
ARG_STDIO_DIRECT;
@ -770,27 +780,33 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_NICE,
ARG_SETENV,
ARG_BACKGROUND,
ARG_PTY,
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
* though (but limit the extension to long options). */
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "machine", required_argument, NULL, ARG_MACHINE },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE },
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "machine", required_argument, NULL, ARG_MACHINE },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE },
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "pty", no_argument, NULL, ARG_PTY },
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{},
};
@ -883,6 +899,26 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
break;
case ARG_PTY:
if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_PTY;
break;
case ARG_PIPE:
if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_DIRECT;
break;
case ARG_SHELL_PROMPT_PREFIX:
r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -913,7 +949,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_wait = true;
arg_aggressive_gc = true;
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
arg_expand_environment = false;
arg_send_sighup = true;
@ -993,6 +1031,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
}
if (!arg_shell_prompt_prefix) {
const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
if (e) {
arg_shell_prompt_prefix = strdup(e);
if (!arg_shell_prompt_prefix)
return log_oom();
} else if (emoji_enabled()) {
arg_shell_prompt_prefix = strjoin(special_glyph(SPECIAL_GLYPH_SUPERHERO), " ");
if (!arg_shell_prompt_prefix)
return log_oom();
}
}
if (!isempty(arg_shell_prompt_prefix)) {
r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
if (r < 0)
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
return 1;
}
@ -1181,7 +1238,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
if (r < 0)
return bus_log_create_error(r);
send_term = isatty_safe(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
send_term = isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO);
}
if (send_term) {
@ -1345,78 +1402,75 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
unsigned soft_reboots_count = 0;
const char *unique, *id;
char *p;
static int make_unit_name(UnitType t, char **ret) {
int r;
assert(bus);
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
assert(ret);
r = sd_bus_get_unique_name(bus, &unique);
/* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
* managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
* logic in place. */
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
r = pidref_set_self(&self);
if (r < 0)
return log_error_errno(r, "Failed to get reference to my own process: %m");
r = pidref_acquire_pidfd_id(&self);
if (r < 0) {
log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
/* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
sd_id128_t rnd;
/* We couldn't get the unique name, which is a pretty
* common case if we are connected to systemd
* directly. In that case, just pick a random uuid as
* name */
r = sd_id128_randomize(&rnd);
if (r < 0)
return log_error_errno(r, "Failed to generate random run unit name: %m");
if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
return log_oom();
r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
} else
r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
if (r < 0)
return log_oom();
return 0;
}
return 0;
}
/* We managed to get the unique name, then let's use that to name our transient units. */
static int connect_bus(sd_bus **ret) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
id = startswith(unique, ":1."); /* let' strip the usual prefix */
if (!id)
id = startswith(unique, ":"); /* the spec only requires things to start with a colon, hence
* let's add a generic fallback for that. */
if (!id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unique name %s has unexpected format.",
unique);
assert(ret);
/* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
* and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
* hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = bus_get_property_trivial(
bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
if (r < 0)
log_debug_errno(r,
"Failed to get SoftRebootsCount property, ignoring: %s",
bus_error_message(&error, r));
}
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
* limited direct connection */
if (arg_wait ||
arg_stdio != ARG_STDIO_NONE ||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
if (soft_reboots_count > 0) {
if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
return log_oom();
} else {
p = strjoin("run-u", id, ".", unit_type_to_string(t));
if (!p)
return log_oom();
}
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
*ret = p;
*ret = TAKE_PTR(bus);
return 0;
}
typedef struct RunContext {
sd_bus *bus;
sd_event *event;
PTYForward *forward;
sd_bus_slot *match;
char *service;
char *bus_path;
/* Bus objects */
sd_bus *bus;
sd_bus_slot *match_properties_changed;
sd_bus_slot *match_disconnected;
sd_event_source *retry_timer;
/* Current state of the unit */
char *active_state;
@ -1437,16 +1491,72 @@ typedef struct RunContext {
uint32_t exit_status;
} RunContext;
static void run_context_free(RunContext *c) {
static int run_context_update(RunContext *c);
static int run_context_attach_bus(RunContext *c, sd_bus *bus);
static void run_context_detach_bus(RunContext *c);
static int run_context_reconnect(RunContext *c);
static void run_context_done(RunContext *c) {
assert(c);
run_context_detach_bus(c);
c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
c->forward = pty_forward_free(c->forward);
c->match = sd_bus_slot_unref(c->match);
c->bus = sd_bus_unref(c->bus);
c->event = sd_event_unref(c->event);
free(c->active_state);
free(c->result);
free(c->bus_path);
free(c->service);
}
static int on_retry_timer(sd_event_source *s, uint64_t usec, void *userdata) {
RunContext *c = ASSERT_PTR(userdata);
c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
return run_context_reconnect(c);
}
static int run_context_reconnect(RunContext *c) {
int r;
assert(c);
run_context_detach_bus(c);
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
r = connect_bus(&bus);
if (r < 0) {
log_warning_errno(r, "Failed to reconnect, retrying in 2s: %m");
r = event_reset_time_relative(
c->event,
&c->retry_timer,
CLOCK_MONOTONIC,
2 * USEC_PER_SEC, /* accuracy= */ 0,
on_retry_timer, c,
SD_EVENT_PRIORITY_NORMAL,
"retry-timeout",
/* force_reset= */ false);
if (r < 0) {
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(r, "Failed to install retry timer: %m");
}
return 0;
}
r = run_context_attach_bus(c, bus);
if (r < 0) {
(void) sd_event_exit(c->event, EXIT_FAILURE);
return r;
}
log_info("Reconnected to bus.");
return run_context_update(c);
}
static void run_context_check_done(RunContext *c) {
@ -1454,16 +1564,13 @@ static void run_context_check_done(RunContext *c) {
assert(c);
if (c->match)
done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
else
done = true;
done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
if (c->forward && !pty_forward_is_done(c->forward) && done) /* If the service is gone, it's time to drain the output */
done = pty_forward_drain(c->forward);
if (done)
sd_event_exit(c->event, EXIT_SUCCESS);
(void) sd_event_exit(c->event, EXIT_SUCCESS);
}
static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
@ -1480,7 +1587,7 @@ static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_er
return 0;
}
static int run_context_update(RunContext *c, const char *path) {
static int run_context_update(RunContext *c) {
static const struct bus_properties_map map[] = {
{ "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
@ -1503,16 +1610,35 @@ static int run_context_update(RunContext *c, const char *path) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = bus_map_all_properties(c->bus,
"org.freedesktop.systemd1",
path,
map,
BUS_MAP_STRDUP,
&error,
NULL,
c);
assert(c);
assert(c->bus);
r = bus_map_all_properties(
c->bus,
"org.freedesktop.systemd1",
c->bus_path,
map,
BUS_MAP_STRDUP,
&error,
NULL,
c);
if (r < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
/* If this is a connection error, then try to reconnect. This might be because the service
* manager is being restarted. Handle this gracefully. */
if (sd_bus_error_has_names(
&error,
SD_BUS_ERROR_NO_REPLY,
SD_BUS_ERROR_DISCONNECTED,
SD_BUS_ERROR_TIMED_OUT,
SD_BUS_ERROR_SERVICE_UNKNOWN,
SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
log_info("Bus call failed due to connection problems. Trying to reconnect...");
/* Not propagating error, because we handled it already, by reconnecting. */
return run_context_reconnect(c);
}
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r));
}
@ -1521,11 +1647,67 @@ static int run_context_update(RunContext *c, const char *path) {
}
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
RunContext *c = ASSERT_PTR(userdata);
return run_context_update(ASSERT_PTR(userdata));
}
assert(m);
static int on_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
/* If our connection gets terminated, then try to reconnect. This might be because the service
* manager is being restarted. Handle this gracefully. */
log_info("Got disconnected from bus connection. Trying to reconnect...");
return run_context_reconnect(ASSERT_PTR(userdata));
}
return run_context_update(c, sd_bus_message_get_path(m));
static int run_context_attach_bus(RunContext *c, sd_bus *bus) {
int r;
assert(c);
assert(bus);
assert(!c->bus);
assert(!c->match_properties_changed);
assert(!c->match_disconnected);
c->bus = sd_bus_ref(bus);
r = sd_bus_match_signal_async(
c->bus,
&c->match_properties_changed,
"org.freedesktop.systemd1",
c->bus_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
on_properties_changed, NULL, c);
if (r < 0)
return log_error_errno(r, "Failed to request PropertiesChanged signal match: %m");
r = sd_bus_match_signal_async(
bus,
&c->match_disconnected,
"org.freedesktop.DBus.Local",
/* path= */ NULL,
"org.freedesktop.DBus.Local",
"Disconnected",
on_disconnected, NULL, c);
if (r < 0)
return log_error_errno(r, "Failed to request Disconnected signal match: %m");
r = sd_bus_attach_event(c->bus, c->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
return 0;
}
static void run_context_detach_bus(RunContext *c) {
assert(c);
if (c->bus) {
(void) sd_bus_detach_event(c->bus);
c->bus = sd_bus_unref(c->bus);
}
c->match_properties_changed = sd_bus_slot_unref(c->match_properties_changed);
c->match_disconnected = sd_bus_slot_unref(c->match_disconnected);
}
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
@ -1541,7 +1723,7 @@ static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
/* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
* for the service to end. If the user hits ^C we'll exit too. */
} else if (rcode < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
}
@ -1818,7 +2000,7 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
}
@ -1860,7 +2042,7 @@ static int start_transient_service(sd_bus *bus) {
}
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
_cleanup_(run_context_free) RunContext c = {
_cleanup_(run_context_done) RunContext c = {
.cpu_usage_nsec = NSEC_INFINITY,
.memory_peak = UINT64_MAX,
.memory_swap_peak = UINT64_MAX,
@ -1871,14 +2053,19 @@ static int start_transient_service(sd_bus *bus) {
.inactive_exit_usec = USEC_INFINITY,
.inactive_enter_usec = USEC_INFINITY,
};
_cleanup_free_ char *path = NULL;
c.bus = sd_bus_ref(bus);
r = sd_event_default(&c.event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
c.service = strdup(service);
if (!c.service)
return log_oom();
c.bus_path = unit_dbus_path_from_name(service);
if (!c.bus_path)
return log_oom();
if (master >= 0) {
(void) sd_event_set_signal_exit(c.event, true);
@ -1900,26 +2087,11 @@ static int start_transient_service(sd_bus *bus) {
set_window_title(c.forward);
}
path = unit_dbus_path_from_name(service);
if (!path)
return log_oom();
r = sd_bus_match_signal_async(
bus,
&c.match,
"org.freedesktop.systemd1",
path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
on_properties_changed, NULL, &c);
r = run_context_attach_bus(&c, bus);
if (r < 0)
return log_error_errno(r, "Failed to request properties changed signal match: %m");
return r;
r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = run_context_update(&c, path);
r = run_context_update(&c);
if (r < 0)
return r;
@ -2033,7 +2205,7 @@ static int start_transient_scope(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
} else {
r = make_unit_name(bus, UNIT_SCOPE, &scope);
r = make_unit_name(UNIT_SCOPE, &scope);
if (r < 0)
return r;
}
@ -2332,7 +2504,7 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
break;
}
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
@ -2411,16 +2583,30 @@ static int run(int argc, char* argv[]) {
}
if (!arg_description) {
char *t;
_cleanup_free_ char *t = NULL;
if (strv_isempty(arg_cmdline))
t = strdup(arg_unit);
else
else if (startswith(arg_cmdline[0], "-")) {
/* Drop the login shell marker from the command line when generating the description,
* in order to minimize user confusion. */
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
if (!l)
return log_oom();
r = free_and_strdup_warn(l + 0, l[0] + 1);
if (r < 0)
return r;
t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
} else
t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
if (!t)
return log_oom();
free_and_replace(arg_description, t);
arg_description = strjoin("[", program_invocation_short_name, "] ", t);
if (!arg_description)
return log_oom();
}
/* For backward compatibility reasons env var expansion is disabled by default for scopes, and
@ -2435,18 +2621,9 @@ static int run(int argc, char* argv[]) {
" Use --expand-environment=yes/no to explicitly control it as needed.");
}
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
* limited direct connection */
if (arg_wait ||
arg_stdio != ARG_STDIO_NONE ||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
r = connect_bus(&bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
return r;
if (arg_scope)
return start_transient_scope(bus);

View File

@ -107,6 +107,7 @@ int sd_varlink_process(sd_varlink *v);
int sd_varlink_wait(sd_varlink *v, uint64_t timeout);
int sd_varlink_is_idle(sd_varlink *v);
int sd_varlink_is_processing_method(sd_varlink *v);
int sd_varlink_flush(sd_varlink *v);
int sd_varlink_close(sd_varlink *v);

View File

@ -581,4 +581,15 @@ TEST(getenv_path_list) {
assert_se(unsetenv("TEST_GETENV_PATH_LIST") >= 0);
}
TEST(strv_env_get_merged) {
char **l = STRV_MAKE("ONE", "1", "TWO", "2", "THREE", "3", "FOUR", "4", "FIVE", "5"),
**expected = STRV_MAKE("ONE=1", "TWO=2", "THREE=3", "FOUR=4", "FIVE=5");
_cleanup_strv_free_ char **m = NULL;
ASSERT_OK(strv_env_get_merged(NULL, &m));
ASSERT_NULL(m);
ASSERT_OK(strv_env_get_merged(l, &m));
ASSERT_TRUE(strv_equal(m, expected));
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -82,7 +82,7 @@ TEST(keymaps) {
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
TEST(dump_special_glyphs) {
assert_cc(SPECIAL_GLYPH_GREEN_CIRCLE + 1 == _SPECIAL_GLYPH_MAX);
assert_cc(SPECIAL_GLYPH_SUPERHERO + 1 == _SPECIAL_GLYPH_MAX);
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
@ -133,6 +133,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_YELLOW_CIRCLE);
dump_glyph(SPECIAL_GLYPH_BLUE_CIRCLE);
dump_glyph(SPECIAL_GLYPH_GREEN_CIRCLE);
dump_glyph(SPECIAL_GLYPH_SUPERHERO);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -39,10 +39,10 @@ cat >/var/lib/machines/long-running/sbin/init <<\EOF
PID=0
trap "touch /terminate; kill $PID" RTMIN+3
trap "touch /poweroff" RTMIN+4
trap "touch /reboot" INT
trap "touch /trap" TRAP
trap 'touch /terminate; kill 0' RTMIN+3
trap 'touch /poweroff' RTMIN+4
trap 'touch /reboot' INT
trap 'touch /trap' TRAP
trap 'kill $PID' EXIT
# We need to wait for the sleep process asynchronously in order to allow
@ -304,11 +304,59 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unreg
# test io.systemd.Machine.List with addresses, OSRelease, and UIDShift fields
create_dummy_container "/var/lib/machines/container-without-os-release"
cat >>/var/lib/machines/container-without-os-release/sbin/init <<\EOF
ip link add hoge type dummy
ip link set hoge up
ip address add 192.0.2.1/24 dev hoge
PID=0
trap 'kill 0' RTMIN+3
trap 'kill $PID' EXIT
# We need to wait for the sleep process asynchronously in order to allow
# bash to process signals
sleep infinity &
# notify that the process is ready
touch /ready
PID=$!
while :; do
wait || :
done
EOF
machinectl start "container-without-os-release"
timeout 30 bash -c "until test -e /var/lib/machines/container-without-os-release/ready; do sleep .5; done"
rm -f /var/lib/machines/container-without-os-release/etc/os-release /var/lib/machines/container-without-os-release/usr/lib/os-release
(! varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": "container-without-os-release", "acquireMetadata": "yes"}')
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": "container-without-os-release", "acquireMetadata": "graceful"}'
output=$(varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": "container-without-os-release", "acquireMetadata": "graceful"}')
assert_eq "$(echo "$output" | jq --seq .name | tr -d \\036)" '"container-without-os-release"'
assert_eq "$(echo "$output" | jq --seq .class | tr -d \\036)" '"container"'
assert_eq "$(echo "$output" | jq --seq .service | tr -d \\036)" '"systemd-nspawn"'
assert_eq "$(echo "$output" | jq --seq .rootDirectory | tr -d \\036)" '"/var/lib/machines/container-without-os-release"'
assert_eq "$(echo "$output" | jq --seq .unit | tr -d \\036)" '"systemd-nspawn@container-without-os-release.service"'
assert_eq "$(echo "$output" | jq --seq .addresses[0].family | tr -d \\036)" '2'
assert_eq "$(echo "$output" | jq --seq .addresses[0].address[0] | tr -d \\036)" '192'
assert_eq "$(echo "$output" | jq --seq .addresses[0].address[1] | tr -d \\036)" '0'
assert_eq "$(echo "$output" | jq --seq .addresses[0].address[2] | tr -d \\036)" '2'
assert_eq "$(echo "$output" | jq --seq .addresses[0].address[3] | tr -d \\036)" '1'
# test for listing multiple machines.
long_running_machine_start
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}'
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"acquireMetadata": "no"}'
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"acquireMetadata": "graceful"}'
# check if machined does not try to send anything after error message
journalctl --sync
TS="$(date '+%H:%M:%S')"
(! varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"acquireMetadata": "yes"}')
journalctl --sync
(! journalctl -u systemd-machined.service --since="$TS" --grep 'Connection busy')
# terminate machines
machinectl terminate "container-without-os-release"
machinectl terminate "long-running"
# wait for the container being stopped, otherwise acquiring image metadata by io.systemd.MachineImage.List may fail in the below.
timeout 30 bash -c 'until [[ "$(systemctl is-active systemd-nspawn@long-running.service)" == "inactive" ]]; do sleep .5; done'
(ip addr show lo | grep -q 192.168.1.100) || ip address add 192.168.1.100/24 dev lo
(! varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": ".host"}' | grep 'addresses')

View File

@ -261,4 +261,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
assert_eq "$(run0 -D / pwd)" "/"
assert_eq "$(run0 --user=testuser pwd)" "/home/testuser"
assert_eq "$(run0 -D / --user=testuser pwd)" "/"
# Verify that all combinations of --pty/--pipe come to the sam results
assert_eq "$(run0 echo -n foo)" "foo"
assert_eq "$(run0 --pty echo -n foo)" "foo"
assert_eq "$(run0 --pipe echo -n foo)" "foo"
assert_eq "$(run0 --pipe --pty echo -n foo)" "foo"
# Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not
assert_neq "$(run0 --pty tty < /dev/null)" "not a tty"
assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty"
fi
# Tests whether intermediate disconnects corrupt us (modified testcase from https://github.com/systemd/systemd/issues/27204)
assert_rc "37" systemd-run --unit=disconnecttest --wait --pipe --user -M testuser@.host bash -ec 'systemctl --user daemon-reexec; sleep 3; exit 37'

View File

@ -39,6 +39,15 @@ assert_eq() {(
fi
)}
assert_neq() {(
set +ex
if [[ "${1?}" = "${2?}" ]]; then
echo "FAIL: not expected: '$2' actual: '$1'" >&2
exit 1
fi
)}
assert_le() {(
set +ex