mirror of
https://github.com/systemd/systemd.git
synced 2024-10-26 08:55:40 +03:00
Compare commits
33 Commits
5bda47379b
...
f8885450be
Author | SHA1 | Date | |
---|---|---|---|
|
f8885450be | ||
|
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 | ||
|
f53dcbf562 |
5
TODO
5
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
|
||||
|
@ -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>
|
||||
|
15
man/run0.xml
15
man/run0.xml
@ -192,6 +192,21 @@
|
||||
</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>--machine=</option></term>
|
||||
|
||||
|
@ -1922,6 +1922,22 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
|
||||
--pivot-root=/ostree/deploy/$OS/deploy/$CHECKSUM:/sysroot \
|
||||
--bind=+/sysroot/ostree/deploy/$OS/var:/var</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Run a container within a container</title>
|
||||
|
||||
<para>We're assuming that the outer container is in <filename index="false">./outer</filename> and
|
||||
the inner container in <filename index="false">./outer/inner</filename>. The inner
|
||||
<command>systemd-nspawn</command> will not be able to reach D-Bus, so we are passing flags to disable
|
||||
some of its functionality.</para>
|
||||
|
||||
<programlisting># systemd-nspawn \
|
||||
--directory outer --ephemeral --console interactive -- \
|
||||
systemd-nspawn \
|
||||
--directory inner --ephemeral --console interactive \
|
||||
--register false --keep-unit --link-journal no -- \
|
||||
echo OK</programlisting>
|
||||
</example>
|
||||
</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>
|
||||
|
||||
|
@ -6,6 +6,7 @@ Dependencies=
|
||||
exitrd
|
||||
initrd
|
||||
minimal-base
|
||||
minimal-systemd
|
||||
minimal-0
|
||||
minimal-1
|
||||
|
||||
@ -48,6 +49,7 @@ ExtraTrees=
|
||||
%O/minimal-1.root-%a-verity.raw:/usr/share/minimal_1.verity
|
||||
%O/minimal-1.root-%a-verity-sig.raw:/usr/share/minimal_1.verity.sig
|
||||
%O/minimal-base:/usr/share/TEST-13-NSPAWN-container-template
|
||||
%O/minimal-systemd:/usr/share/TEST-13-NSPAWN-container-systemd-template
|
||||
%O/exitrd:/exitrd
|
||||
|
||||
Initrds=%O/initrd
|
||||
|
27
mkosi.images/minimal-systemd/mkosi.conf
Normal file
27
mkosi.images/minimal-systemd/mkosi.conf
Normal file
@ -0,0 +1,27 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
[Config]
|
||||
Dependencies=minimal-base
|
||||
|
||||
[Output]
|
||||
Format=directory
|
||||
|
||||
[Content]
|
||||
BaseTrees=%O/minimal-base
|
||||
Bootable=no
|
||||
|
||||
VolatilePackages=
|
||||
systemd
|
||||
systemd-boot
|
||||
systemd-container
|
||||
systemd-devel
|
||||
systemd-journal-remote
|
||||
systemd-libs
|
||||
systemd-networkd
|
||||
systemd-networkd-defaults
|
||||
systemd-oomd-defaults
|
||||
systemd-pam
|
||||
systemd-resolved
|
||||
systemd-tests
|
||||
systemd-udev
|
||||
systemd-ukify
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -171,6 +171,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 +192,8 @@ 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"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
@ -674,7 +680,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,6 +776,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||
ARG_NICE,
|
||||
ARG_SETENV,
|
||||
ARG_BACKGROUND,
|
||||
ARG_PTY,
|
||||
ARG_PIPE,
|
||||
};
|
||||
|
||||
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
|
||||
@ -791,6 +799,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||
{ "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 },
|
||||
{},
|
||||
};
|
||||
|
||||
@ -883,6 +893,20 @@ 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 '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -913,7 +937,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;
|
||||
|
||||
@ -1181,7 +1207,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) {
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -11,9 +11,8 @@ TEST_FORCE_NEWIMAGE=1
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
test_append_files() {
|
||||
local workspace="${1:?}"
|
||||
local container="$workspace/usr/share/TEST-13-NSPAWN-container-template"
|
||||
_install_base_container() {
|
||||
local container="${1:?}"
|
||||
|
||||
# For virtual wlan interface.
|
||||
instmods mac80211_hwsim
|
||||
@ -55,4 +54,14 @@ EOF
|
||||
chmod +x "$container/sbin/init"
|
||||
}
|
||||
|
||||
test_append_files() {
|
||||
local workspace="${1:?}"
|
||||
local container="$workspace/usr/share/TEST-13-NSPAWN-container-template"
|
||||
local container_systemd="$workspace/usr/share/TEST-13-NSPAWN-container-systemd-template"
|
||||
|
||||
_install_base_container "$container"
|
||||
_install_base_container "$container_systemd"
|
||||
initdir="$container_systemd" install_systemd
|
||||
}
|
||||
|
||||
do_test "$@"
|
||||
|
@ -1214,4 +1214,21 @@ testcase_unpriv_fuse() {
|
||||
bash -c 'cat <>/dev/fuse' 2>&1)" == *'cat: -: Operation not permitted' ]]
|
||||
}
|
||||
|
||||
testcase_nested_nspawn() {
|
||||
local root
|
||||
root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.nested_nspawn.XXX)"
|
||||
create_dummy_container "$root" /usr/share/TEST-13-NSPAWN-container-systemd-template
|
||||
mkdir "$root/inner"
|
||||
create_dummy_container "$root/inner"
|
||||
|
||||
systemd-nspawn \
|
||||
--directory="$root" --ephemeral --pipe -- \
|
||||
systemd-nspawn \
|
||||
--directory=/inner --ephemeral --pipe \
|
||||
--register=false --keep-unit --link-journal=no -- \
|
||||
echo OK
|
||||
|
||||
rm -fr "$root"
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
@ -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,14 @@ 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
|
||||
|
@ -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
|
||||
|
||||
@ -166,14 +175,15 @@ coverage_create_nspawn_dropin() {
|
||||
|
||||
create_dummy_container() {
|
||||
local root="${1:?}"
|
||||
local source="${2:-/usr/share/TEST-13-NSPAWN-container-template}"
|
||||
|
||||
if [[ ! -d /usr/share/TEST-13-NSPAWN-container-template ]]; then
|
||||
if [[ ! -d "$source" ]]; then
|
||||
echo >&2 "Missing container template, probably not running in TEST-13-NSPAWN?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$root"
|
||||
cp -a /usr/share/TEST-13-NSPAWN-container-template/* "$root"
|
||||
cp -a "$source"/* "$root"
|
||||
coverage_create_nspawn_dropin "$root"
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user