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

Compare commits

...

33 Commits

Author SHA1 Message Date
Paweł Zmarzły
f8885450be
Merge f53dcbf562 into 9d8f5e22f8 2024-10-25 15:44:16 +01:00
Lennart Poettering
9d8f5e22f8
Merge pull request #34891 from poettering/run0-pty
run0: add --pty and --pipe switches to force allocation of a pty or pipe
2024-10-25 16:25:01 +02:00
Lennart Poettering
6fb0c52295 ci: add some basic testing of the new --pty and --pipe switches 2024-10-25 14:14:26 +02:00
Lennart Poettering
edd10ab29c run0: add options to force allocation of PTY or of pipe use
Fixes: #33033
2024-10-25 14:14:26 +02:00
Lennart Poettering
988053eac3 tree-wide: use isatty_safe() everywhere 2024-10-25 14:09:38 +02:00
Lennart Poettering
a586f57eb2 update TODO 2024-10-25 13:57:44 +02:00
Lennart Poettering
c18ac81f17
Merge pull request #34877 from aafeijoo-suse/veritysetup-fixes
veritysetup-generator: minor man/code changes
2024-10-25 10:06:31 +02:00
Lennart Poettering
c4363051e4
Merge pull request #34880 from poettering/change-user-on-pam-always
core: make sure that if PAMName= is set we always do the full user ch…
2024-10-25 09:22:03 +02:00
Lennart Poettering
f515ea1cd4 test: add quick test to verify the PAM stack really ran in all run0 modes of operation 2024-10-24 22:56:44 +02:00
Lennart Poettering
e4b4d9cc7a core: make sure that if PAMName= is set we always do the full user changing even if no user is specified explicitly
When PAMName= is set this should be enough to go through our entire user
changing story, so that PAM is definitely run, and environment variables
definitely pulled in and so on.

Previously, it would happen that under some circumstances we might no do
this when transitioning from root to root itself even though PAM was
enabled.

Fixes: #34682
2024-10-24 22:37:00 +02:00
Lennart Poettering
210fb8626f
Merge pull request #34875 from poettering/userdbctl-filter
userdbctl: add some basic client-side filtering
2024-10-24 22:36:22 +02:00
Lennart Poettering
4167e9e210 user-util: tighten shell validation a tiny bit 2024-10-24 22:28:17 +02:00
Mike Yuan
4e69da071d
Merge pull request #34799 from YHNdnzj/service-followups
core: follow-ups for live mount
2024-10-24 19:44:10 +02:00
Lennart Poettering
1c6f542e81 ci: give new userdbctl some CI exposure 2024-10-24 10:17:35 +02:00
Lennart Poettering
9bbc424a60 user-record: fix indentation 2024-10-24 10:17:35 +02:00
Lennart Poettering
c17a76982a userdbctl: set shell/home cell type to TABLE_PATH
This only matters for sorting, and we currently don't support sorting by
path, hence this is of no real effect, but it certainly is more correct.
2024-10-24 10:17:35 +02:00
Lennart Poettering
2ea94b145e userdbctl: grey out nologin shell in tabular output 2024-10-24 10:17:35 +02:00
Lennart Poettering
8bc86b1944 userdbctl: optionally hide UID range boundaries in output 2024-10-24 10:17:35 +02:00
Lennart Poettering
ad5de3222f userdbctl: add some basic client-side filtering
This adds some basic client-side user/group filtering to "userdbctl":

1. by uid/gid min/max
2. by user "disposition" (i.e. show only regular users with "userdbctl
   user -R")
3. by fuzzy name (i.e. search by substring/levenshtein of user name,
   real name, and other identifiers of the user/group record).

In the long run we also want to support this server side, but let's
start out with doing this client-side, since many backends won't support
server-side filtering anytime soon anyway, so we need it in either case.
2024-10-24 10:17:23 +02:00
Antonio Alvarez Feijoo
11de19f3da
veritysetup-generator: remove unused code 2024-10-24 10:07:45 +02:00
Antonio Alvarez Feijoo
e98e3f856d
man/veritysetup-generator: document veritytab kernel command line option 2024-10-24 10:07:28 +02:00
Antonio Alvarez Feijoo
dcbfc7872e
man: fix links to veritysetup(8) 2024-10-24 09:54:48 +02:00
Mike Yuan
f19afb2177
core: clean up errors for live mounting
* Use SD_BUS_ERROR_NOT_SUPPORTED where appropriate
* Use Service object in service_can_live_mount()
* Include errno in bus error message
2024-10-22 19:52:24 +02:00
Mike Yuan
f5b0e4f92e
core/service: fix one wording 2024-10-22 19:51:02 +02:00
Mike Yuan
78270121c3
core/service: add missing serialization for Service.live_mount_result 2024-10-22 19:51:01 +02:00
Mike Yuan
20366875f9
core/service: call service_enter_running() if live mount fails
service_enter_running() would re-arm timer for RuntimeMaxSec=,
hence it should be called instead of disabling timer completely
when live mount operation fails, in a similar fashion as
service_enter_reload_by_notify().
2024-10-22 19:51:01 +02:00
Mike Yuan
a6eeca9a00
core/service: introduce service_live_mount_finish()
that combines updating Service.live_mount_result and
service_mount_request_reply()
2024-10-22 19:19:47 +02:00
Mike Yuan
a53e92a17c
core/service: place occurrences of SERVICE_MOUNTING closer to reload states 2024-10-22 19:19:47 +02:00
Mike Yuan
b8fa230596
core/unit: put the reload job back to queue if unit is refreshing 2024-10-22 19:19:46 +02:00
Mike Yuan
c240f293b8
shared/bus-util: debug log when falling back to session bus
Follow-up for d0316b7a0d
2024-10-22 19:19:46 +02:00
Mike Yuan
d845254b7f
basic/fs-util: move unlink_tempfilep() to tmpfile-util 2024-10-22 19:19:39 +02:00
Mike Yuan
7e40b51a2e
man/org.freedesktop.systemd1: complete version info for ManagedOOMMemoryPressureDurationUSec
Follow-up for 63d4c4271c

Some unit types were left out.
2024-10-22 19:12:27 +02:00
Pawel Zmarzly
f53dcbf562 nspawn: document and test nested nspawn
Running systemd-nspawn within systemd-nspawn is currently possible.
Let's make sure we don't regress.
2024-10-18 19:10:44 +01:00
37 changed files with 727 additions and 208 deletions

5
TODO
View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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

View 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

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

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

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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(

View File

@ -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",

View File

@ -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_;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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,

View File

@ -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 "$@"

View File

@ -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

View File

@ -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

View File

@ -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

View File

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