mirror of
https://github.com/systemd/systemd.git
synced 2024-10-26 08:55:40 +03:00
Compare commits
53 Commits
1eb51cf640
...
afe715223c
Author | SHA1 | Date | |
---|---|---|---|
|
afe715223c | ||
|
e485678631 | ||
|
c6cb7141ac | ||
|
b58c3d4e47 | ||
|
fa402c3c1e | ||
|
3d9ad7097e | ||
|
f956b7c936 | ||
|
af6cd8c0b5 | ||
|
177830bd84 | ||
|
4e3396932a | ||
|
416e927e62 | ||
|
1cf4e150aa | ||
|
f7078de515 | ||
|
6d6048b4cb | ||
|
10a48938ef | ||
|
b58b13f1c6 | ||
|
c8f59296bf | ||
|
d585085f57 | ||
|
ff4b6a1915 | ||
|
d9f68f48f7 | ||
|
0310b2a60b | ||
|
115fac3c29 | ||
|
9d8f5e22f8 | ||
|
6fb0c52295 | ||
|
edd10ab29c | ||
|
988053eac3 | ||
|
a586f57eb2 | ||
|
c18ac81f17 | ||
|
c4363051e4 | ||
|
f515ea1cd4 | ||
|
e4b4d9cc7a | ||
|
210fb8626f | ||
|
4167e9e210 | ||
|
4e69da071d | ||
|
1c6f542e81 | ||
|
9bbc424a60 | ||
|
c17a76982a | ||
|
2ea94b145e | ||
|
8bc86b1944 | ||
|
ad5de3222f | ||
|
11de19f3da | ||
|
e98e3f856d | ||
|
dcbfc7872e | ||
|
f19afb2177 | ||
|
f5b0e4f92e | ||
|
78270121c3 | ||
|
20366875f9 | ||
|
a6eeca9a00 | ||
|
a53e92a17c | ||
|
b8fa230596 | ||
|
c240f293b8 | ||
|
d845254b7f | ||
|
7e40b51a2e |
8
TODO
8
TODO
@ -129,6 +129,11 @@ Deprecations and removals:
|
||||
|
||||
Features:
|
||||
|
||||
* $LISTEN_PID, $MAINPID and $SYSTEMD_EXECPID env vars that the service manager
|
||||
sets should be augmented with $LISTEN_PIDFDID, $MAINPIDFDID and
|
||||
$SYSTEMD_EXECPIDFD (and similar for other env vars we might send). Also,
|
||||
MAINPID= in sd_notify() should be augmented with MAINPIDFDID=, and so on.
|
||||
|
||||
* port copy.c over to use LabelOps for all labelling.
|
||||
|
||||
* port remaining getmntent() users over to libmount. There are subtle
|
||||
@ -157,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().
|
||||
|
@ -12302,8 +12302,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>PassFileDescriptorsToExec</varname> were added in version 256.</para>
|
||||
<para><varname>PrivateTmpEx</varname>,
|
||||
<varname>ImportCredentialEx</varname>,
|
||||
<varname>BindLogSockets</varname>, and
|
||||
<varname>PrivateUsersEx</varname> were added in version 257.</para>
|
||||
<varname>BindLogSockets</varname>,
|
||||
<varname>PrivateUsersEx</varname>, and
|
||||
<varname>ManagedOOMMemoryPressureDurationUSec</varname> were added in version 257.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Mount Unit Objects</title>
|
||||
@ -12339,8 +12340,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
|
||||
<para><varname>PrivateTmpEx</varname>,
|
||||
<varname>ImportCredentialEx</varname>,
|
||||
<varname>BindLogSockets</varname>, and
|
||||
<varname>PrivateUsersEx</varname> were added in version 257.</para>
|
||||
<varname>BindLogSockets</varname>,
|
||||
<varname>PrivateUsersEx</varname>, and
|
||||
<varname>ManagedOOMMemoryPressureDurationUSec</varname> were added in version 257.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Swap Unit Objects</title>
|
||||
@ -12376,8 +12378,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
|
||||
<para><varname>PrivateTmpEx</varname>,
|
||||
<varname>ImportCredentialEx</varname>,
|
||||
<varname>BindLogSockets</varname>, and
|
||||
<varname>PrivateUsersEx</varname> were added in version 257.</para>
|
||||
<varname>BindLogSockets</varname>,
|
||||
<varname>PrivateUsersEx</varname>, and
|
||||
<varname>ManagedOOMMemoryPressureDurationUSec</varname> were added in version 257.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Slice Unit Objects</title>
|
||||
|
52
man/run0.xml
52
man/run0.xml
@ -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>
|
||||
|
@ -54,12 +54,24 @@
|
||||
|
||||
<listitem><para>Takes a boolean argument. Defaults to <literal>yes</literal>. If
|
||||
<literal>no</literal>, disables the generator entirely. <varname>rd.systemd.verity=</varname> is
|
||||
honored only by the initrd while <varname>systemd.verity=</varname> is honored by both the host
|
||||
system and the initrd.</para>
|
||||
honored only in the initrd while <varname>systemd.verity=</varname> is honored by both the main
|
||||
system and in the initrd.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v233"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>veritytab=</varname></term>
|
||||
<term><varname>rd.veritytab=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Defaults to <literal>yes</literal>. If
|
||||
<literal>no</literal>, causes the generator to ignore any devices configured in
|
||||
<filename>/etc/veritytab</filename>. <varname>rd.veritytab=</varname> is honored only in the initrd
|
||||
while <varname>veritytab=</varname> is honored by both the main system and in the initrd.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>roothash=</varname></term>
|
||||
|
||||
@ -104,7 +116,7 @@
|
||||
<option>hash=<replaceable>HASH</replaceable></option>, <option>fec-device=<replaceable>PATH</replaceable></option>,
|
||||
<option>fec-offset=<replaceable>BYTES</replaceable></option>, <option>fec-roots=<replaceable>NUM</replaceable></option> and
|
||||
<option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>HEX</replaceable></option>. See
|
||||
<citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more
|
||||
<citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more
|
||||
details.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||
@ -129,7 +141,7 @@
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
@ -97,7 +97,7 @@
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -174,6 +174,75 @@
|
||||
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--fuzzy</option></term>
|
||||
<term><option>-z</option></term>
|
||||
|
||||
<listitem><para>When used with the <command>user</command> or <command>group</command> command, do a
|
||||
fuzzy string search. Any specified arguments will be matched against the user name, the real name of
|
||||
the user record, the email address, and other descriptive strings of the user or group
|
||||
record. Moreover, instead of precise matching, a substring match or a match allowing slight
|
||||
deviations in spelling is applied.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--disposition=</option></term>
|
||||
|
||||
<listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||
filters by disposition of the record. Takes one of <literal>intrinsic</literal>,
|
||||
<literal>system</literal>, <literal>regular</literal>, <literal>dynamic</literal>,
|
||||
<literal>container</literal>. May be used multiple times, in which case only users matching any of
|
||||
the specified dispositions are shown.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-I</option></term>
|
||||
<term><option>-S</option></term>
|
||||
<term><option>-R</option></term>
|
||||
|
||||
<listitem><para>Shortcuts for <option>--disposition=intrinsic</option>,
|
||||
<option>--disposition=system</option>, <option>--disposition=regular</option>,
|
||||
respectively.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--uid-min=</option></term>
|
||||
<term><option>--uid-max=</option></term>
|
||||
|
||||
<listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||
filters the output by UID/GID ranges. Takes numeric minimum resp. maximum UID/GID values. Shows only
|
||||
records within the specified range. When applied to the <command>user</command> command matches
|
||||
against UIDs, when applied to the <command>group</command> command against GIDs (despite the name of
|
||||
the switch). If unspecified defaults to 0 (for the minimum) and 4294967294 (for the maximum), i.e. by
|
||||
default no filtering is applied as the whole UID/GID range is covered.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--boundaries=</option></term>
|
||||
|
||||
<listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||
controls whether to show relevant UID/GID range boundary information in the tabular output. Takes a
|
||||
boolean. Defaults to true.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-B</option></term>
|
||||
|
||||
<listitem><para>Shortcut for <option>--boundaries=no</option>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
|
@ -319,7 +319,7 @@ data /etc/data /etc/hash a5ee4b42f70ae1f46a08a7c92c2e0a20672ad2f514792730f5d49d7
|
||||
<member><citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -694,17 +694,6 @@ int access_fd(int fd, int mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unlink_tempfilep(char (*p)[]) {
|
||||
assert(p);
|
||||
|
||||
/* If the file is created with mkstemp(), it will (almost always)
|
||||
* change the suffix. Treat this as a sign that the file was
|
||||
* successfully created. We ignore both the rare case where the
|
||||
* original suffix is used and unlink failures. */
|
||||
if (!endswith(*p, ".XXXXXX"))
|
||||
(void) unlink(*p);
|
||||
}
|
||||
|
||||
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
|
||||
_cleanup_close_ int truncate_fd = -EBADF;
|
||||
struct stat st;
|
||||
|
@ -112,8 +112,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
|
||||
|
||||
int access_fd(int fd, int mode);
|
||||
|
||||
void unlink_tempfilep(char (*p)[]);
|
||||
|
||||
typedef enum UnlinkDeallocateFlags {
|
||||
UNLINK_REMOVEDIR = 1 << 0,
|
||||
UNLINK_ERASE = 1 << 1,
|
||||
|
@ -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"🦸",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -118,6 +118,17 @@ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unlink_tempfilep(char (*p)[]) {
|
||||
assert(p);
|
||||
|
||||
/* If the file is created with mkstemp(), it will (almost always) change the suffix.
|
||||
* Treat this as a sign that the file was successfully created. We ignore both the rare case
|
||||
* where the original suffix is used and unlink failures. */
|
||||
|
||||
if (!endswith(*p, ".XXXXXX"))
|
||||
(void) unlink(*p);
|
||||
}
|
||||
|
||||
static int tempfn_build(const char *p, const char *pre, const char *post, bool child, char **ret) {
|
||||
_cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL, *result = NULL;
|
||||
size_t len_pre, len_post, len_add;
|
||||
|
@ -18,6 +18,8 @@ static inline int fopen_temporary_child(const char *path, FILE **ret_file, char
|
||||
int mkostemp_safe(char *pattern);
|
||||
int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
|
||||
|
||||
void unlink_tempfilep(char (*p)[]);
|
||||
|
||||
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
|
||||
int tempfn_random(const char *p, const char *extra, char **ret);
|
||||
int tempfn_random_child(const char *p, const char *extra, char **ret);
|
||||
|
@ -884,6 +884,17 @@ bool valid_home(const char *p) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool valid_shell(const char *p) {
|
||||
/* We have the same requirements, so just piggy-back on the home check.
|
||||
*
|
||||
* Let's ignore /etc/shells because this is only applicable to real and not system users. It is also
|
||||
* incompatible with the idea of empty /etc/. */
|
||||
if (!valid_home(p))
|
||||
return false;
|
||||
|
||||
return !endswith(p, "/"); /* one additional restriction: shells may not be dirs */
|
||||
}
|
||||
|
||||
int maybe_setgroups(size_t size, const gid_t *list) {
|
||||
int r;
|
||||
|
||||
|
@ -108,15 +108,7 @@ bool valid_user_group_name(const char *u, ValidUserFlags flags);
|
||||
bool valid_gecos(const char *d);
|
||||
char* mangle_gecos(const char *d);
|
||||
bool valid_home(const char *p);
|
||||
|
||||
static inline bool valid_shell(const char *p) {
|
||||
/* We have the same requirements, so just piggy-back on the home check.
|
||||
*
|
||||
* Let's ignore /etc/shells because this is only applicable to real and
|
||||
* not system users. It is also incompatible with the idea of empty /etc.
|
||||
*/
|
||||
return valid_home(p);
|
||||
}
|
||||
bool valid_shell(const char *p);
|
||||
|
||||
int maybe_setgroups(size_t size, const gid_t *list);
|
||||
|
||||
|
@ -4061,7 +4061,7 @@ int exec_invoke(
|
||||
int r, ngids = 0;
|
||||
_cleanup_free_ gid_t *supplementary_gids = NULL;
|
||||
const char *username = NULL, *groupname = NULL;
|
||||
_cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL;
|
||||
_cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *own_user = NULL;
|
||||
const char *home = NULL, *shell = NULL;
|
||||
char **final_argv = NULL;
|
||||
dev_t journal_stream_dev = 0;
|
||||
@ -4298,8 +4298,23 @@ int exec_invoke(
|
||||
username = runtime->dynamic_creds->user->name;
|
||||
|
||||
} else {
|
||||
if (context->user) {
|
||||
r = get_fixed_user(context->user, &username, &uid, &gid, &home, &shell);
|
||||
const char *u;
|
||||
|
||||
if (context->user)
|
||||
u = context->user;
|
||||
else if (context->pam_name) {
|
||||
/* If PAM is enabled but no user name is explicitly selected, then use our own one. */
|
||||
own_user = getusername_malloc();
|
||||
if (!own_user) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_exec_error_errno(context, params, r, "Failed to determine my own user ID: %m");
|
||||
}
|
||||
u = own_user;
|
||||
} else
|
||||
u = NULL;
|
||||
|
||||
if (u) {
|
||||
r = get_fixed_user(u, &username, &uid, &gid, &home, &shell);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_exec_error_errno(context, params, r, "Failed to determine user credentials: %m");
|
||||
|
@ -64,6 +64,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_RELOAD] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
|
||||
[SERVICE_MOUNTING] = UNIT_REFRESHING,
|
||||
[SERVICE_STOP] = UNIT_DEACTIVATING,
|
||||
[SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
|
||||
[SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
|
||||
@ -79,7 +80,6 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
|
||||
[SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
|
||||
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
|
||||
[SERVICE_MOUNTING] = UNIT_REFRESHING,
|
||||
};
|
||||
|
||||
/* For Type=idle we never want to delay any other jobs, hence we
|
||||
@ -95,6 +95,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
|
||||
[SERVICE_RELOAD] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
|
||||
[SERVICE_MOUNTING] = UNIT_REFRESHING,
|
||||
[SERVICE_STOP] = UNIT_DEACTIVATING,
|
||||
[SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
|
||||
[SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
|
||||
@ -110,7 +111,6 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
|
||||
[SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
|
||||
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
|
||||
[SERVICE_MOUNTING] = UNIT_REFRESHING,
|
||||
};
|
||||
|
||||
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
|
||||
@ -136,9 +136,10 @@ static bool SERVICE_STATE_WITH_CONTROL_PROCESS(ServiceState state) {
|
||||
SERVICE_CONDITION,
|
||||
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_CLEANING, SERVICE_MOUNTING);
|
||||
SERVICE_CLEANING);
|
||||
}
|
||||
|
||||
static void service_init(Unit *u) {
|
||||
@ -982,8 +983,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
prefix, service_state_to_string(s->state),
|
||||
prefix, service_result_to_string(s->result),
|
||||
prefix, service_result_to_string(s->reload_result),
|
||||
prefix, service_result_to_string(s->clean_result),
|
||||
prefix, service_result_to_string(s->live_mount_result),
|
||||
prefix, service_result_to_string(s->clean_result),
|
||||
prefix, yes_no(s->permissions_start_only),
|
||||
prefix, yes_no(s->root_directory_start_only),
|
||||
prefix, yes_no(s->remain_after_exit),
|
||||
@ -1261,7 +1262,7 @@ static void service_search_main_pid(Service *s) {
|
||||
r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
|
||||
if (r < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", s->main_pid.pid);
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to watch main PID "PID_FMT": %m", s->main_pid.pid);
|
||||
}
|
||||
|
||||
static void service_set_state(Service *s, ServiceState state) {
|
||||
@ -1283,10 +1284,10 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_AUTO_RESTART,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_CLEANING))
|
||||
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
|
||||
|
||||
@ -1315,6 +1316,9 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_MOUNTING))
|
||||
service_stop_watchdog(s);
|
||||
|
||||
if (state != SERVICE_MOUNTING) /* Just in case */
|
||||
s->mount_request = sd_bus_message_unref(s->mount_request);
|
||||
|
||||
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(u->manager)) {
|
||||
/* For the inactive states unit_notify() will trim the cgroup. But for exit we have to
|
||||
* do that ourselves... */
|
||||
@ -1336,9 +1340,6 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
unit_destroy_runtime_data(u, &s->exec_context);
|
||||
}
|
||||
|
||||
if (state != SERVICE_MOUNTING) /* Just in case */
|
||||
s->mount_request = sd_bus_message_unref(s->mount_request);
|
||||
|
||||
if (old_state != state)
|
||||
log_unit_debug(u, "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
|
||||
|
||||
@ -1357,6 +1358,7 @@ static usec_t service_coldplug_timeout(Service *s) {
|
||||
case SERVICE_RELOAD:
|
||||
case SERVICE_RELOAD_SIGNAL:
|
||||
case SERVICE_RELOAD_NOTIFY:
|
||||
case SERVICE_MOUNTING:
|
||||
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
|
||||
|
||||
case SERVICE_RUNNING:
|
||||
@ -1380,9 +1382,6 @@ static usec_t service_coldplug_timeout(Service *s) {
|
||||
case SERVICE_CLEANING:
|
||||
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->exec_context.timeout_clean_usec);
|
||||
|
||||
case SERVICE_MOUNTING:
|
||||
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
|
||||
|
||||
default:
|
||||
return USEC_INFINITY;
|
||||
}
|
||||
@ -2889,14 +2888,16 @@ static int service_start(Unit *u) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void service_mount_request_reply(Service *s, bool success, const char *error) {
|
||||
static void service_live_mount_finish(Service *s, ServiceResult f, const char *error) {
|
||||
assert(s);
|
||||
assert(error);
|
||||
|
||||
s->live_mount_result = f;
|
||||
|
||||
if (!s->mount_request)
|
||||
return;
|
||||
|
||||
if (success) {
|
||||
if (f == SERVICE_SUCCESS) {
|
||||
(void) sd_bus_reply_method_return(s->mount_request, NULL);
|
||||
log_unit_debug(UNIT(s),
|
||||
"'%s' method succeeded",
|
||||
@ -2941,7 +2942,7 @@ static int service_stop(Unit *u) {
|
||||
|
||||
case SERVICE_MOUNTING:
|
||||
service_kill_control_process(s);
|
||||
service_mount_request_reply(s, /* success= */ false, BUS_ERROR_UNIT_INACTIVE);
|
||||
service_live_mount_finish(s, SERVICE_FAILURE_PROTOCOL, BUS_ERROR_UNIT_INACTIVE);
|
||||
_fallthrough_;
|
||||
case SERVICE_CONDITION:
|
||||
case SERVICE_START_PRE:
|
||||
@ -3087,6 +3088,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
(void) serialize_item(f, "state", service_state_to_string(s->state));
|
||||
(void) serialize_item(f, "result", service_result_to_string(s->result));
|
||||
(void) serialize_item(f, "reload-result", service_result_to_string(s->reload_result));
|
||||
(void) serialize_item(f, "live-mount-result", service_result_to_string(s->live_mount_result));
|
||||
|
||||
(void) serialize_pidref(f, fds, "control-pid", &s->control_pid);
|
||||
if (s->main_pid_known)
|
||||
@ -3315,7 +3317,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
|
||||
state = service_state_from_string(value);
|
||||
if (state < 0)
|
||||
log_unit_debug(u, "Failed to parse state value: %s", value);
|
||||
log_unit_debug_errno(u, state, "Failed to parse state value: %s", value);
|
||||
else
|
||||
s->deserialized_state = state;
|
||||
} else if (streq(key, "result")) {
|
||||
@ -3323,7 +3325,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
|
||||
f = service_result_from_string(value);
|
||||
if (f < 0)
|
||||
log_unit_debug(u, "Failed to parse result value: %s", value);
|
||||
log_unit_debug_errno(u, f, "Failed to parse result value: %s", value);
|
||||
else if (f != SERVICE_SUCCESS)
|
||||
s->result = f;
|
||||
|
||||
@ -3332,10 +3334,19 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
|
||||
f = service_result_from_string(value);
|
||||
if (f < 0)
|
||||
log_unit_debug(u, "Failed to parse reload result value: %s", value);
|
||||
log_unit_debug_errno(u, f, "Failed to parse reload result value: %s", value);
|
||||
else if (f != SERVICE_SUCCESS)
|
||||
s->reload_result = f;
|
||||
|
||||
} else if (streq(key, "live-mount-result")) {
|
||||
ServiceResult f;
|
||||
|
||||
f = service_result_from_string(value);
|
||||
if (f < 0)
|
||||
log_unit_debug_errno(u, f, "Failed to parse live mount result value: %s", value);
|
||||
else if (f != SERVICE_SUCCESS)
|
||||
s->live_mount_result = f;
|
||||
|
||||
} else if (streq(key, "control-pid")) {
|
||||
|
||||
if (!pidref_is_set(&s->control_pid))
|
||||
@ -4185,6 +4196,12 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
break;
|
||||
|
||||
case SERVICE_MOUNTING:
|
||||
service_live_mount_finish(s, f, SD_BUS_ERROR_FAILED);
|
||||
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
break;
|
||||
|
||||
case SERVICE_STOP:
|
||||
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
|
||||
break;
|
||||
@ -4219,14 +4236,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
service_enter_dead(s, SERVICE_SUCCESS, false);
|
||||
break;
|
||||
|
||||
case SERVICE_MOUNTING:
|
||||
s->live_mount_result = f;
|
||||
|
||||
service_mount_request_reply(s, f == SERVICE_SUCCESS, SD_BUS_ERROR_FAILED);
|
||||
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
@ -4303,8 +4312,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
|
||||
case SERVICE_MOUNTING:
|
||||
log_unit_warning(UNIT(s), "Mount operation timed out. Killing mount process.");
|
||||
service_kill_control_process(s);
|
||||
s->live_mount_result = SERVICE_FAILURE_TIMEOUT;
|
||||
service_mount_request_reply(s, /* success= */ false, SD_BUS_ERROR_TIMEOUT);
|
||||
service_live_mount_finish(s, SERVICE_FAILURE_TIMEOUT, SD_BUS_ERROR_TIMEOUT);
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
break;
|
||||
|
||||
@ -5013,8 +5021,8 @@ static void service_reset_failed(Unit *u) {
|
||||
|
||||
s->result = SERVICE_SUCCESS;
|
||||
s->reload_result = SERVICE_SUCCESS;
|
||||
s->clean_result = SERVICE_SUCCESS;
|
||||
s->live_mount_result = SERVICE_SUCCESS;
|
||||
s->clean_result = SERVICE_SUCCESS;
|
||||
s->n_restarts = 0;
|
||||
|
||||
(void) unit_set_debug_invocation(u, /* enable= */ false);
|
||||
@ -5159,7 +5167,8 @@ static int service_can_clean(Unit *u, ExecCleanMask *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_live_mount(Unit *u,
|
||||
static int service_live_mount(
|
||||
Unit *u,
|
||||
const char *src,
|
||||
const char *dst,
|
||||
sd_bus_message *message,
|
||||
@ -5167,9 +5176,8 @@ static int service_live_mount(Unit *u,
|
||||
const MountOptions *options,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(pidref_done) PidRef worker = PIDREF_NULL;
|
||||
Service *s = ASSERT_PTR(SERVICE(u));
|
||||
const char *propagate_directory;
|
||||
_cleanup_(pidref_done) PidRef worker = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@ -5180,7 +5188,7 @@ static int service_live_mount(Unit *u,
|
||||
assert(!s->mount_request);
|
||||
|
||||
if (s->state != SERVICE_RUNNING || !pidref_is_set(&s->main_pid)) {
|
||||
log_unit_warning(u, "Service is not running, cannot live mount");
|
||||
log_unit_warning(u, "Service is not running, cannot live mount.");
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
BUS_ERROR_UNIT_INACTIVE,
|
||||
@ -5191,7 +5199,7 @@ static int service_live_mount(Unit *u,
|
||||
}
|
||||
|
||||
if (mount_point_is_credentials(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], dst)) {
|
||||
log_unit_warning(u, "Refusing to live mount over credential mount '%s'", dst);
|
||||
log_unit_warning(u, "Refusing to live mount over credential mount '%s'.", dst);
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
@ -5202,7 +5210,7 @@ static int service_live_mount(Unit *u,
|
||||
}
|
||||
|
||||
if (path_startswith_strv(dst, s->exec_context.inaccessible_paths)) {
|
||||
log_unit_warning(u, "%s is not accessible to this unit, cannot live mount", dst);
|
||||
log_unit_warning(u, "%s is not accessible to this unit, cannot live mount.", dst);
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
@ -5219,18 +5227,14 @@ static int service_live_mount(Unit *u,
|
||||
|
||||
r = service_arm_timer(s, /* relative= */ true, s->timeout_start_usec);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(u, r, "Failed to install timer: %m");
|
||||
sd_bus_error_set_errnof(
|
||||
error,
|
||||
r,
|
||||
"Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: failed to install timer",
|
||||
src,
|
||||
dst,
|
||||
u->id);
|
||||
log_unit_error_errno(u, r, "Failed to install timer: %m");
|
||||
sd_bus_error_set_errnof(error, r,
|
||||
"Live mounting '%s' on '%s' for unit '%s': failed to install timer: %m",
|
||||
src, dst, u->id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
propagate_directory = strjoina("/run/systemd/propagate/", u->id);
|
||||
const char *propagate_directory = strjoina("/run/systemd/propagate/", u->id);
|
||||
|
||||
/* Given we are running from PID1, avoid doing potentially heavy I/O operations like opening images
|
||||
* directly, and instead fork a worker process. We record the D-Bus message, so that we can reply
|
||||
@ -5238,19 +5242,12 @@ static int service_live_mount(Unit *u,
|
||||
* resource is available (or the operation failed) once they receive the response. */
|
||||
r = unit_fork_helper_process(u, "(sd-mount-in-ns)", /* into_cgroup= */ false, &worker);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(
|
||||
u,
|
||||
r,
|
||||
"Failed to fork process to mount '%s' on '%s' in unit's namespace: %m",
|
||||
src,
|
||||
dst);
|
||||
sd_bus_error_set_errnof(
|
||||
error,
|
||||
r,
|
||||
"Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: failed to fork process",
|
||||
src,
|
||||
dst,
|
||||
u->id);
|
||||
log_unit_error_errno(u, r,
|
||||
"Failed to fork process to mount '%s' on '%s' in unit's namespace: %m",
|
||||
src, dst);
|
||||
sd_bus_error_set_errnof(error, r,
|
||||
"Live mounting '%s' on '%s' for unit '%s': failed to fork off helper process into namespace: %m",
|
||||
src, dst, u->id);
|
||||
goto fail;
|
||||
}
|
||||
if (r == 0) {
|
||||
@ -5271,26 +5268,21 @@ static int service_live_mount(Unit *u,
|
||||
src, dst,
|
||||
flags);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(
|
||||
u,
|
||||
r,
|
||||
"Failed to mount '%s' on '%s' in unit's namespace: %m",
|
||||
src,
|
||||
dst);
|
||||
log_unit_error_errno(u, r,
|
||||
"Failed to mount '%s' on '%s' in unit's namespace: %m",
|
||||
src, dst);
|
||||
else
|
||||
log_unit_debug(u, "Mounted '%s' on '%s' in unit's namespace", src, dst);
|
||||
|
||||
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = unit_watch_pidref(u, &worker, /* exclusive= */ true);
|
||||
if (r < 0) {
|
||||
sd_bus_error_set_errnof(
|
||||
error,
|
||||
r,
|
||||
"Live mounting '%s' on '%s' for unit '%s' failed: failed to watch worker process",
|
||||
src,
|
||||
dst,
|
||||
u->id);
|
||||
log_unit_warning_errno(u, r, "Failed to watch live mount helper process: %m");
|
||||
sd_bus_error_set_errnof(error, r,
|
||||
"Live mounting '%s' on '%s' for unit '%s': failed to watch live mount helper process: %m",
|
||||
src, dst, u->id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -5301,15 +5293,15 @@ static int service_live_mount(Unit *u,
|
||||
|
||||
fail:
|
||||
s->live_mount_result = SERVICE_FAILURE_RESOURCES;
|
||||
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int service_can_live_mount(const Unit *u, sd_bus_error *error) {
|
||||
assert(u);
|
||||
static int service_can_live_mount(Unit *u, sd_bus_error *error) {
|
||||
Service *s = ASSERT_PTR(SERVICE(u));
|
||||
|
||||
/* Ensure that the unit runs in a private mount namespace */
|
||||
if (!exec_needs_mount_namespace(unit_get_exec_context(u), /* params= */ NULL, unit_get_exec_runtime(u)))
|
||||
if (!exec_needs_mount_namespace(&s->exec_context, /* params= */ NULL, s->exec_runtime))
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
|
@ -193,8 +193,8 @@ struct Service {
|
||||
/* If we shut down, remember why */
|
||||
ServiceResult result;
|
||||
ServiceResult reload_result;
|
||||
ServiceResult clean_result;
|
||||
ServiceResult live_mount_result;
|
||||
ServiceResult clean_result;
|
||||
|
||||
bool main_pid_known:1;
|
||||
bool main_pid_alien:1;
|
||||
|
@ -2043,7 +2043,11 @@ int unit_reload(Unit *u) {
|
||||
return -EBADR;
|
||||
|
||||
state = unit_active_state(u);
|
||||
if (state == UNIT_RELOADING)
|
||||
if (IN_SET(state, UNIT_RELOADING, UNIT_REFRESHING))
|
||||
/* "refreshing" means some resources in the unit namespace is being updated. Unlike reload,
|
||||
* the unit processes aren't made aware of refresh. Let's put the job back to queue
|
||||
* in both cases, as refresh typically takes place before reload and it's better to wait
|
||||
* for it rather than failing. */
|
||||
return -EAGAIN;
|
||||
|
||||
if (state != UNIT_ACTIVE)
|
||||
@ -6392,22 +6396,21 @@ Condition *unit_find_failed_condition(Unit *u) {
|
||||
return failed_trigger && !has_succeeded_trigger ? failed_trigger : NULL;
|
||||
}
|
||||
|
||||
int unit_can_live_mount(const Unit *u, sd_bus_error *error) {
|
||||
int unit_can_live_mount(Unit *u, sd_bus_error *error) {
|
||||
assert(u);
|
||||
|
||||
if (!UNIT_VTABLE(u)->live_mount)
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Live mounting not supported for unit type '%s' of unit '%s'.",
|
||||
unit_type_to_string(u->type),
|
||||
u->id);
|
||||
SD_BUS_ERROR_NOT_SUPPORTED,
|
||||
"Live mounting not supported by unit type '%s'",
|
||||
unit_type_to_string(u->type));
|
||||
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
BUS_ERROR_NO_SUCH_UNIT,
|
||||
"Unit '%s' not loaded, cannot live mount.",
|
||||
"Unit '%s' not loaded, cannot live mount",
|
||||
u->id);
|
||||
|
||||
if (!UNIT_VTABLE(u)->can_live_mount)
|
||||
@ -6429,7 +6432,7 @@ int unit_live_mount(
|
||||
assert(UNIT_VTABLE(u)->live_mount);
|
||||
|
||||
if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
|
||||
log_unit_debug(u, "Unit not active");
|
||||
log_unit_debug(u, "Unit not active, cannot perform live mount.");
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
BUS_ERROR_UNIT_INACTIVE,
|
||||
@ -6440,7 +6443,7 @@ int unit_live_mount(
|
||||
}
|
||||
|
||||
if (unit_active_state(u) == UNIT_REFRESHING) {
|
||||
log_unit_debug(u, "Unit already live mounting");
|
||||
log_unit_debug(u, "Unit already live mounting, refusing further requests.");
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
BUS_ERROR_UNIT_BUSY,
|
||||
|
@ -587,7 +587,7 @@ typedef struct UnitVTable {
|
||||
|
||||
/* Add a bind/image mount into the unit namespace while it is running. */
|
||||
int (*live_mount)(Unit *u, const char *src, const char *dst, sd_bus_message *message, MountInNamespaceFlags flags, const MountOptions *options, sd_bus_error *error);
|
||||
int (*can_live_mount)(const Unit *u, sd_bus_error *error);
|
||||
int (*can_live_mount)(Unit *u, sd_bus_error *error);
|
||||
|
||||
/* Serialize state and file descriptors that should be carried over into the new
|
||||
* instance after reexecution. */
|
||||
@ -650,7 +650,7 @@ typedef struct UnitVTable {
|
||||
int (*bus_commit_properties)(Unit *u);
|
||||
|
||||
/* Return the unit this unit is following */
|
||||
Unit *(*following)(Unit *u);
|
||||
Unit* (*following)(Unit *u);
|
||||
|
||||
/* Return the set of units that are following each other */
|
||||
int (*following_set)(Unit *u, Set **s);
|
||||
@ -1047,7 +1047,7 @@ void unit_next_freezer_state(Unit *u, FreezerAction action, FreezerState *ret_ne
|
||||
void unit_set_freezer_state(Unit *u, FreezerState state);
|
||||
void unit_freezer_complete(Unit *u, FreezerState kernel_state);
|
||||
|
||||
int unit_can_live_mount(const Unit *u, sd_bus_error *error);
|
||||
int unit_can_live_mount(Unit *u, sd_bus_error *error);
|
||||
int unit_live_mount(Unit *u, const char *src, const char *dst, sd_bus_message *message, MountInNamespaceFlags flags, const MountOptions *options, sd_bus_error *error);
|
||||
|
||||
Condition *unit_find_failed_condition(Unit *u);
|
||||
|
@ -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;
|
||||
|
@ -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 }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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[] = {
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
431
src/run/run.c
431
src/run/run.c
@ -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);
|
||||
|
@ -439,6 +439,7 @@ int bus_connect_transport(
|
||||
/* Print a friendly message when the local system is actually not running systemd as PID 1. */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
|
||||
"System has not been booted with systemd as init system (PID 1). Can't operate.");
|
||||
|
||||
r = sd_bus_default_system(&bus);
|
||||
break;
|
||||
|
||||
@ -515,8 +516,10 @@ int bus_connect_transport_systemd(
|
||||
* private manager bus. To keep compat with existing code that was setting
|
||||
* DBUS_SESSION_BUS_ADDRESS without setting XDG_RUNTIME_DIR, connect to the user
|
||||
* session bus if DBUS_SESSION_BUS_ADDRESS is set and XDG_RUNTIME_DIR isn't. */
|
||||
if (r == -ENOMEDIUM && secure_getenv("DBUS_SESSION_BUS_ADDRESS"))
|
||||
if (r == -ENOMEDIUM && secure_getenv("DBUS_SESSION_BUS_ADDRESS")) {
|
||||
log_debug_errno(r, "$XDG_RUNTIME_DIR not set, unable to connect to private bus. Falling back to session bus.");
|
||||
r = sd_bus_default_user(ret_bus);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
|
@ -326,3 +326,28 @@ int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **
|
||||
*ret = TAKE_PTR(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int group_record_match(GroupRecord *h, const UserDBMatch *match) {
|
||||
assert(h);
|
||||
assert(match);
|
||||
|
||||
if (h->gid < match->gid_min || h->gid > match->gid_max)
|
||||
return false;
|
||||
|
||||
if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << group_record_disposition(h)))
|
||||
return false;
|
||||
|
||||
if (!strv_isempty(match->fuzzy_names)) {
|
||||
const char* names[] = {
|
||||
h->group_name,
|
||||
group_record_group_name_and_realm(h),
|
||||
h->description,
|
||||
};
|
||||
|
||||
if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
@ -43,5 +43,7 @@ int group_record_load(GroupRecord *h, sd_json_variant *v, UserRecordLoadFlags fl
|
||||
int group_record_build(GroupRecord **ret, ...);
|
||||
int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
|
||||
|
||||
int group_record_match(GroupRecord *h, const UserDBMatch *match);
|
||||
|
||||
const char* group_record_group_name_and_realm(GroupRecord *h);
|
||||
UserDisposition group_record_disposition(GroupRecord *h);
|
||||
|
@ -104,37 +104,37 @@ int nss_passwd_to_user_record(
|
||||
* just a password instead of the whole account, but that's mostly pointless in times of
|
||||
* password-less authorization, hence let's not bother. */
|
||||
|
||||
SET_IF(hr->locked,
|
||||
spwd && spwd->sp_expire >= 0,
|
||||
spwd->sp_expire <= 1, -1);
|
||||
SET_IF(hr->locked,
|
||||
spwd && spwd->sp_expire >= 0,
|
||||
spwd->sp_expire <= 1, -1);
|
||||
|
||||
SET_IF(hr->not_after_usec,
|
||||
spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_expire * USEC_PER_DAY, UINT64_MAX);
|
||||
SET_IF(hr->not_after_usec,
|
||||
spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_expire * USEC_PER_DAY, UINT64_MAX);
|
||||
|
||||
SET_IF(hr->password_change_now,
|
||||
spwd && spwd->sp_lstchg >= 0,
|
||||
spwd->sp_lstchg == 0, -1);
|
||||
SET_IF(hr->password_change_now,
|
||||
spwd && spwd->sp_lstchg >= 0,
|
||||
spwd->sp_lstchg == 0, -1);
|
||||
|
||||
SET_IF(hr->last_password_change_usec,
|
||||
spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX);
|
||||
SET_IF(hr->last_password_change_usec,
|
||||
spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX);
|
||||
|
||||
SET_IF(hr->password_change_min_usec,
|
||||
spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_min * USEC_PER_DAY, UINT64_MAX);
|
||||
SET_IF(hr->password_change_min_usec,
|
||||
spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_min * USEC_PER_DAY, UINT64_MAX);
|
||||
|
||||
SET_IF(hr->password_change_max_usec,
|
||||
spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_max * USEC_PER_DAY, UINT64_MAX);
|
||||
SET_IF(hr->password_change_max_usec,
|
||||
spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_max * USEC_PER_DAY, UINT64_MAX);
|
||||
|
||||
SET_IF(hr->password_change_warn_usec,
|
||||
spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_warn * USEC_PER_DAY, UINT64_MAX);
|
||||
SET_IF(hr->password_change_warn_usec,
|
||||
spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_warn * USEC_PER_DAY, UINT64_MAX);
|
||||
|
||||
SET_IF(hr->password_change_inactive_usec,
|
||||
spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_inact * USEC_PER_DAY, UINT64_MAX);
|
||||
SET_IF(hr->password_change_inactive_usec,
|
||||
spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY,
|
||||
spwd->sp_inact * USEC_PER_DAY, UINT64_MAX);
|
||||
|
||||
hr->json = sd_json_variant_unref(hr->json);
|
||||
r = sd_json_buildo(
|
||||
|
@ -2401,6 +2401,72 @@ int suitable_blob_filename(const char *name) {
|
||||
name[0] != '.';
|
||||
}
|
||||
|
||||
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches) {
|
||||
assert(names || n_names == 0);
|
||||
|
||||
/* Checks if any of the user record strings in the names[] array matches any of the search strings in
|
||||
* the matches** strv fuzzily. */
|
||||
|
||||
FOREACH_ARRAY(n, names, n_names) {
|
||||
if (!*n)
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *lcn = strdup(*n);
|
||||
if (!lcn)
|
||||
return -ENOMEM;
|
||||
|
||||
ascii_strlower(lcn);
|
||||
|
||||
STRV_FOREACH(i, matches) {
|
||||
_cleanup_free_ char *lc = strdup(*i);
|
||||
if (!lc)
|
||||
return -ENOMEM;
|
||||
|
||||
ascii_strlower(lc);
|
||||
|
||||
/* First do substring check */
|
||||
if (strstr(lcn, lc))
|
||||
return true;
|
||||
|
||||
/* Then do some fuzzy string comparison (but only if the needle is non-trivially long) */
|
||||
if (strlen(lc) >= 5 && strlevenshtein(lcn, lc) < 3)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int user_record_match(UserRecord *u, const UserDBMatch *match) {
|
||||
assert(u);
|
||||
assert(match);
|
||||
|
||||
if (u->uid < match->uid_min || u->uid > match->uid_max)
|
||||
return false;
|
||||
|
||||
if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << user_record_disposition(u)))
|
||||
return false;
|
||||
|
||||
if (!strv_isempty(match->fuzzy_names)) {
|
||||
|
||||
/* Note this array of names is sparse, i.e. various entries listed in it will be
|
||||
* NULL. Because of that we are not using a NULL terminated strv here, but a regular
|
||||
* array. */
|
||||
const char* names[] = {
|
||||
u->user_name,
|
||||
user_record_user_name_and_realm(u),
|
||||
u->real_name,
|
||||
u->email_address,
|
||||
u->cifs_user_name,
|
||||
};
|
||||
|
||||
if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
|
||||
[USER_CLASSIC] = "classic",
|
||||
[USER_LUKS] = "luks",
|
||||
|
@ -462,6 +462,24 @@ int user_group_record_mangle(sd_json_variant *v, UserRecordLoadFlags load_flags,
|
||||
#define BLOB_DIR_MAX_SIZE (UINT64_C(64) * U64_MB)
|
||||
int suitable_blob_filename(const char *name);
|
||||
|
||||
typedef struct UserDBMatch {
|
||||
char **fuzzy_names;
|
||||
uint64_t disposition_mask;
|
||||
union {
|
||||
uid_t uid_min;
|
||||
gid_t gid_min;
|
||||
};
|
||||
union {
|
||||
uid_t uid_max;
|
||||
gid_t gid_max;
|
||||
};
|
||||
} UserDBMatch;
|
||||
|
||||
#define USER_DISPOSITION_MASK_MAX ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||
|
||||
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches);
|
||||
int user_record_match(UserRecord *u, const UserDBMatch *match);
|
||||
|
||||
const char* user_storage_to_string(UserStorage t) _const_;
|
||||
UserStorage user_storage_from_string(const char *s) _pure_;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -318,6 +318,24 @@ TEST(valid_home) {
|
||||
assert_se(valid_home("/"));
|
||||
assert_se(valid_home("/home"));
|
||||
assert_se(valid_home("/home/foo"));
|
||||
assert_se(valid_home("/home/foo/"));
|
||||
}
|
||||
|
||||
TEST(valid_shell) {
|
||||
assert_se(!valid_shell(NULL));
|
||||
assert_se(!valid_shell(""));
|
||||
assert_se(!valid_shell("."));
|
||||
assert_se(!valid_shell("/shell/.."));
|
||||
assert_se(!valid_shell("/shell/../"));
|
||||
assert_se(!valid_shell("/shell\n/foo"));
|
||||
assert_se(!valid_shell("./piep"));
|
||||
assert_se(!valid_shell("piep"));
|
||||
assert_se(!valid_shell("/shell/user:lennart"));
|
||||
assert_se(!valid_shell("/"));
|
||||
assert_se(!valid_shell("/bin/sh/"));
|
||||
assert_se(valid_shell("/shell"));
|
||||
assert_se(valid_shell("/shell/foo"));
|
||||
assert_se(valid_shell("/bin/sh"));
|
||||
}
|
||||
|
||||
static void test_get_user_creds_one(const char *id, const char *name, uid_t uid, gid_t gid, const char *home, const char *shell) {
|
||||
|
@ -37,6 +37,11 @@ static char** arg_services = NULL;
|
||||
static UserDBFlags arg_userdb_flags = 0;
|
||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||
static bool arg_chain = false;
|
||||
static uint64_t arg_disposition_mask = UINT64_MAX;
|
||||
static uid_t arg_uid_min = 0;
|
||||
static uid_t arg_uid_max = UID_INVALID-1;
|
||||
static bool arg_fuzzy = false;
|
||||
static bool arg_boundaries = true;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
|
||||
|
||||
@ -63,6 +68,10 @@ static const char *user_disposition_to_color(UserDisposition d) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* shell_to_color(const char *shell) {
|
||||
return !shell || is_nologin_shell(shell) ? ansi_grey() : NULL;
|
||||
}
|
||||
|
||||
static int show_user(UserRecord *ur, Table *table) {
|
||||
int r;
|
||||
|
||||
@ -99,10 +108,9 @@ static int show_user(UserRecord *ur, Table *table) {
|
||||
break;
|
||||
|
||||
case OUTPUT_TABLE: {
|
||||
UserDisposition d;
|
||||
|
||||
assert(table);
|
||||
d = user_record_disposition(ur);
|
||||
UserDisposition d = user_record_disposition(ur);
|
||||
const char *sh = user_record_shell(ur);
|
||||
|
||||
r = table_add_many(
|
||||
table,
|
||||
@ -113,8 +121,9 @@ static int show_user(UserRecord *ur, Table *table) {
|
||||
TABLE_UID, ur->uid,
|
||||
TABLE_GID, user_record_gid(ur),
|
||||
TABLE_STRING, empty_to_null(ur->real_name),
|
||||
TABLE_STRING, user_record_home_directory(ur),
|
||||
TABLE_STRING, user_record_shell(ur),
|
||||
TABLE_PATH, user_record_home_directory(ur),
|
||||
TABLE_PATH, sh,
|
||||
TABLE_SET_COLOR, shell_to_color(sh),
|
||||
TABLE_INT, 0);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
@ -176,6 +185,9 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
|
||||
FOREACH_ELEMENT(i, uid_range_table) {
|
||||
_cleanup_free_ char *name = NULL, *comment = NULL;
|
||||
|
||||
if (!FLAGS_SET(arg_disposition_mask, UINT64_C(1) << i->disposition))
|
||||
continue;
|
||||
|
||||
if (!uid_range_covers(p, i->first, i->last - i->first + 1))
|
||||
continue;
|
||||
|
||||
@ -346,7 +358,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
int ret = 0, r;
|
||||
|
||||
if (arg_output < 0)
|
||||
arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
arg_output = argc > 1 && !arg_fuzzy ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
|
||||
if (arg_output == OUTPUT_TABLE) {
|
||||
table = table_new(" ", "name", "disposition", "uid", "gid", "realname", "home", "shell", "order");
|
||||
@ -357,10 +369,18 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
(void) table_set_align_percent(table, table_get_cell(table, 0, 4), 100);
|
||||
table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
|
||||
(void) table_set_sort(table, (size_t) 3, (size_t) 8);
|
||||
(void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) 7);
|
||||
(void) table_hide_column_from_display(table, (size_t) 8);
|
||||
if (!arg_boundaries)
|
||||
(void) table_hide_column_from_display(table, (size_t) 0);
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
UserDBMatch match = {
|
||||
.disposition_mask = arg_disposition_mask,
|
||||
.uid_min = arg_uid_min,
|
||||
.uid_max = arg_uid_max,
|
||||
};
|
||||
|
||||
if (argc > 1 && !arg_fuzzy)
|
||||
STRV_FOREACH(i, argv + 1) {
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
uid_t uid;
|
||||
@ -377,8 +397,10 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
else
|
||||
log_error_errno(r, "Failed to find user %s: %m", *i);
|
||||
|
||||
if (ret >= 0)
|
||||
ret = r;
|
||||
RET_GATHER(ret, r);
|
||||
} else if (!user_record_match(ur, &match)) {
|
||||
log_error("User '%s' does not match filter.", *i);
|
||||
RET_GATHER(ret, -ENOEXEC);
|
||||
} else {
|
||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||
putchar('\n');
|
||||
@ -392,6 +414,15 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
else {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
|
||||
if (argc > 1) {
|
||||
names = strv_copy(argv + 1);
|
||||
if (!names)
|
||||
return log_oom();
|
||||
|
||||
match.fuzzy_names = names;
|
||||
}
|
||||
|
||||
r = userdb_all(arg_userdb_flags, &iterator);
|
||||
if (r == -ENOLINK) /* ENOLINK → Didn't find answer without Varlink, and didn't try Varlink because was configured to off. */
|
||||
@ -412,6 +443,9 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed acquire next user: %m");
|
||||
|
||||
if (!user_record_match(ur, &match))
|
||||
continue;
|
||||
|
||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||
putchar('\n');
|
||||
|
||||
@ -425,20 +459,23 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
if (table) {
|
||||
_cleanup_(uid_range_freep) UIDRange *uid_range = NULL;
|
||||
int boundary_lines, uid_map_lines;
|
||||
int boundary_lines = 0, uid_map_lines = 0;
|
||||
|
||||
r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &uid_range);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to load /proc/self/uid_map, ignoring: %m");
|
||||
if (arg_boundaries) {
|
||||
_cleanup_(uid_range_freep) UIDRange *uid_range = NULL;
|
||||
|
||||
boundary_lines = table_add_uid_boundaries(table, uid_range);
|
||||
if (boundary_lines < 0)
|
||||
return boundary_lines;
|
||||
r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &uid_range);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to load /proc/self/uid_map, ignoring: %m");
|
||||
|
||||
uid_map_lines = table_add_uid_map(table, uid_range, add_unavailable_uid);
|
||||
if (uid_map_lines < 0)
|
||||
return uid_map_lines;
|
||||
boundary_lines = table_add_uid_boundaries(table, uid_range);
|
||||
if (boundary_lines < 0)
|
||||
return boundary_lines;
|
||||
|
||||
uid_map_lines = table_add_uid_map(table, uid_range, add_unavailable_uid);
|
||||
if (uid_map_lines < 0)
|
||||
return uid_map_lines;
|
||||
}
|
||||
|
||||
if (!table_isempty(table)) {
|
||||
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||
@ -650,7 +687,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
int ret = 0, r;
|
||||
|
||||
if (arg_output < 0)
|
||||
arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
arg_output = argc > 1 && !arg_fuzzy ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
|
||||
if (arg_output == OUTPUT_TABLE) {
|
||||
table = table_new(" ", "name", "disposition", "gid", "description", "order");
|
||||
@ -660,10 +697,18 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
(void) table_set_align_percent(table, table_get_cell(table, 0, 3), 100);
|
||||
table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
|
||||
(void) table_set_sort(table, (size_t) 3, (size_t) 5);
|
||||
(void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4);
|
||||
(void) table_hide_column_from_display(table, (size_t) 5);
|
||||
if (!arg_boundaries)
|
||||
(void) table_hide_column_from_display(table, (size_t) 0);
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
UserDBMatch match = {
|
||||
.disposition_mask = arg_disposition_mask,
|
||||
.gid_min = arg_uid_min,
|
||||
.gid_max = arg_uid_max,
|
||||
};
|
||||
|
||||
if (argc > 1 && !arg_fuzzy)
|
||||
STRV_FOREACH(i, argv + 1) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
gid_t gid;
|
||||
@ -680,8 +725,10 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
else
|
||||
log_error_errno(r, "Failed to find group %s: %m", *i);
|
||||
|
||||
if (ret >= 0)
|
||||
ret = r;
|
||||
RET_GATHER(ret, r);
|
||||
} else if (!group_record_match(gr, &match)) {
|
||||
log_error("Group '%s' does not match filter.", *i);
|
||||
RET_GATHER(ret, -ENOEXEC);
|
||||
} else {
|
||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||
putchar('\n');
|
||||
@ -695,6 +742,15 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
else {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
|
||||
if (argc > 1) {
|
||||
names = strv_copy(argv + 1);
|
||||
if (!names)
|
||||
return log_oom();
|
||||
|
||||
match.fuzzy_names = names;
|
||||
}
|
||||
|
||||
r = groupdb_all(arg_userdb_flags, &iterator);
|
||||
if (r == -ENOLINK)
|
||||
@ -715,6 +771,9 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed acquire next group: %m");
|
||||
|
||||
if (!group_record_match(gr, &match))
|
||||
continue;
|
||||
|
||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||
putchar('\n');
|
||||
|
||||
@ -728,20 +787,22 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
if (table) {
|
||||
_cleanup_(uid_range_freep) UIDRange *gid_range = NULL;
|
||||
int boundary_lines, gid_map_lines;
|
||||
int boundary_lines = 0, gid_map_lines = 0;
|
||||
|
||||
r = uid_range_load_userns(/* path = */ NULL, GID_RANGE_USERNS_INSIDE, &gid_range);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to load /proc/self/gid_map, ignoring: %m");
|
||||
if (arg_boundaries) {
|
||||
_cleanup_(uid_range_freep) UIDRange *gid_range = NULL;
|
||||
r = uid_range_load_userns(/* path = */ NULL, GID_RANGE_USERNS_INSIDE, &gid_range);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to load /proc/self/gid_map, ignoring: %m");
|
||||
|
||||
boundary_lines = table_add_gid_boundaries(table, gid_range);
|
||||
if (boundary_lines < 0)
|
||||
return boundary_lines;
|
||||
boundary_lines = table_add_gid_boundaries(table, gid_range);
|
||||
if (boundary_lines < 0)
|
||||
return boundary_lines;
|
||||
|
||||
gid_map_lines = table_add_uid_map(table, gid_range, add_unavailable_gid);
|
||||
if (gid_map_lines < 0)
|
||||
return gid_map_lines;
|
||||
gid_map_lines = table_add_uid_map(table, gid_range, add_unavailable_gid);
|
||||
if (gid_map_lines < 0)
|
||||
return gid_map_lines;
|
||||
}
|
||||
|
||||
if (!table_isempty(table)) {
|
||||
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||
@ -1090,6 +1151,15 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --multiplexer=BOOL Control whether to use the multiplexer\n"
|
||||
" --json=pretty|short JSON output mode\n"
|
||||
" --chain Chain another command\n"
|
||||
" --uid-min=ID Filter by minimum UID/GID (default 0)\n"
|
||||
" --uid-max=ID Filter by maximum UID/GID (default 4294967294)\n"
|
||||
" -z --fuzzy Do a fuzzy name search\n"
|
||||
" --disposition=VALUE Filter by disposition\n"
|
||||
" -I Equivalent to --disposition=intrinsic\n"
|
||||
" -S Equivalent to --disposition=system\n"
|
||||
" -R Equivalent to --disposition=regular\n"
|
||||
" --boundaries=BOOL Show/hide UID/GID range boundaries in output\n"
|
||||
" -B Equivalent to --boundaries=no\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
@ -1113,6 +1183,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_MULTIPLEXER,
|
||||
ARG_JSON,
|
||||
ARG_CHAIN,
|
||||
ARG_UID_MIN,
|
||||
ARG_UID_MAX,
|
||||
ARG_DISPOSITION,
|
||||
ARG_BOUNDARIES,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -1129,6 +1203,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "multiplexer", required_argument, NULL, ARG_MULTIPLEXER },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "chain", no_argument, NULL, ARG_CHAIN },
|
||||
{ "uid-min", required_argument, NULL, ARG_UID_MIN },
|
||||
{ "uid-max", required_argument, NULL, ARG_UID_MAX },
|
||||
{ "fuzzy", required_argument, NULL, 'z' },
|
||||
{ "disposition", required_argument, NULL, ARG_DISPOSITION },
|
||||
{ "boundaries", required_argument, NULL, ARG_BOUNDARIES },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1159,7 +1238,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
int c;
|
||||
|
||||
c = getopt_long(argc, argv,
|
||||
arg_chain ? "+hjs:N" : "hjs:N", /* When --chain was used disable parsing of further switches */
|
||||
arg_chain ? "+hjs:NISRzB" : "hjs:NISRzB", /* When --chain was used disable parsing of further switches */
|
||||
options, NULL);
|
||||
if (c < 0)
|
||||
break;
|
||||
@ -1275,6 +1354,65 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_chain = true;
|
||||
break;
|
||||
|
||||
case ARG_DISPOSITION: {
|
||||
UserDisposition d = user_disposition_from_string(optarg);
|
||||
if (d < 0)
|
||||
return log_error_errno(d, "Unknown user disposition: %s", optarg);
|
||||
|
||||
if (arg_disposition_mask == UINT64_MAX)
|
||||
arg_disposition_mask = 0;
|
||||
|
||||
arg_disposition_mask |= UINT64_C(1) << d;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'I':
|
||||
if (arg_disposition_mask == UINT64_MAX)
|
||||
arg_disposition_mask = 0;
|
||||
|
||||
arg_disposition_mask |= UINT64_C(1) << USER_INTRINSIC;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if (arg_disposition_mask == UINT64_MAX)
|
||||
arg_disposition_mask = 0;
|
||||
|
||||
arg_disposition_mask |= UINT64_C(1) << USER_SYSTEM;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if (arg_disposition_mask == UINT64_MAX)
|
||||
arg_disposition_mask = 0;
|
||||
|
||||
arg_disposition_mask |= UINT64_C(1) << USER_REGULAR;
|
||||
break;
|
||||
|
||||
case ARG_UID_MIN:
|
||||
r = parse_uid(optarg, &arg_uid_min);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --uid-min= value: %s", optarg);
|
||||
break;
|
||||
|
||||
case ARG_UID_MAX:
|
||||
r = parse_uid(optarg, &arg_uid_max);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --uid-max= value: %s", optarg);
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
arg_fuzzy = true;
|
||||
break;
|
||||
|
||||
case ARG_BOUNDARIES:
|
||||
r = parse_boolean_argument("boundaries", optarg, &arg_boundaries);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
arg_boundaries = false;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -1283,6 +1421,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_uid_min > arg_uid_max)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Minimum UID/GID " UID_FMT " is above maximum UID/GID " UID_FMT ", refusing.", arg_uid_min, arg_uid_max);
|
||||
|
||||
/* If not mask was specified, use the all bits on mask */
|
||||
if (arg_disposition_mask == UINT64_MAX)
|
||||
arg_disposition_mask = USER_DISPOSITION_MASK_MAX;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -453,7 +453,6 @@ static int add_veritytab_devices(void) {
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL, *name = NULL, *data_device = NULL, *hash_device = NULL,
|
||||
*roothash = NULL, *options = NULL;
|
||||
char *data_uuid, *hash_uuid;
|
||||
|
||||
r = read_stripped_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
@ -472,14 +471,6 @@ static int add_veritytab_devices(void) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data_uuid = startswith(data_device, "UUID=");
|
||||
if (!data_uuid)
|
||||
data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/");
|
||||
|
||||
hash_uuid = startswith(hash_device, "UUID=");
|
||||
if (!hash_uuid)
|
||||
hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/");
|
||||
|
||||
r = create_veritytab_device(
|
||||
name,
|
||||
data_device,
|
||||
|
@ -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')
|
||||
|
@ -426,6 +426,13 @@ userdbctl -j --json=short | jq
|
||||
userdbctl --with-varlink=no
|
||||
|
||||
userdbctl user
|
||||
userdbctl user -S
|
||||
userdbctl user -IS
|
||||
userdbctl user -R
|
||||
userdbctl user --disposition=regular --disposition=intrinsic
|
||||
userdbctl user kkkk -z
|
||||
userdbctl user --uid-min=100 --uid-max=100
|
||||
userdbctl user -B
|
||||
userdbctl user testuser
|
||||
userdbctl user root
|
||||
userdbctl user testuser root
|
||||
@ -448,6 +455,13 @@ userdbctl user --with-nss=no 2000000
|
||||
(! userdbctl user --with-dropin=no 2000000)
|
||||
|
||||
userdbctl group
|
||||
userdbctl group -S
|
||||
userdbctl group -IS
|
||||
userdbctl group -R
|
||||
userdbctl group --disposition=regular --disposition=intrinsic
|
||||
userdbctl group kkkk -z
|
||||
userdbctl group --uid-min=100 --uid-max=100
|
||||
userdbctl group -B
|
||||
userdbctl group testuser
|
||||
userdbctl group root
|
||||
userdbctl group testuser root
|
||||
|
@ -238,10 +238,13 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
|
||||
run0 ls /
|
||||
assert_eq "$(run0 echo foo)" "foo"
|
||||
# Check if we set some expected environment variables
|
||||
for arg in "" "--user=root" "--user=testuser"; do
|
||||
for arg in "" "--user=root" "--user=0" "--user=testuser"; do
|
||||
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
|
||||
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
|
||||
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
|
||||
|
||||
# Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
|
||||
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
|
||||
done
|
||||
# Let's chain a couple of run0 calls together, for fun
|
||||
readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
|
||||
@ -258,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'
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user