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

Compare commits

...

39 Commits

Author SHA1 Message Date
Colin Foster
fff2138a9a network/dhcpv4: add ability to use BOOTP
BOOTP can be used to sign a static IP to clients. Instead of using the
four message exchange, and Option 53 (DHCP Message Type) there is only a
two message exchange.

Add the following network option to enable BOOTP:

[DHCPv4]
Bootp=yes

This will allow a two message request / reply sequence that doesn't
require DHCP message types.
2024-10-25 12:50:31 -05:00
Yu Watanabe
6d6048b4cb
Merge pull request #34881 from poettering/run0-ui-tweaks
run0: various UI tweaks
2024-10-26 02:49:48 +09:00
Ivan Kruglov
10a48938ef machine: operation should not send a response when 'done' callback set 2024-10-26 02:45:53 +09:00
Lennart Poettering
d585085f57 update TODO 2024-10-25 17:32:19 +02:00
Lennart Poettering
ff4b6a1915 run: drop "-" prefix from command line when generating unit description
Let's not confuse users with the login shell indicator and drop it from
the description. This means a run0 session will now usually show up with
a description of "[run0] /bin/bash" rather than "[run0] -/bin/bash".
2024-10-25 17:32:19 +02:00
Lennart Poettering
d9f68f48f7 run: prefix unit description with our own process name
I think we should try to communicate clearly if something is a run0
session, or a systemd-run invocation. Hence, let's initialize the
description so that the command is prefixed by
program_invocation_short_name.

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

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

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

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

View File

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

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

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

View File

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

View File

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

@ -14,19 +14,13 @@
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init(
int bootp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint8_t type,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr,
size_t optlen,
size_t *optoffset) {
size_t offset = 0;
int r;
const uint8_t *chaddr) {
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
assert(chaddr || hlen == 0);
@ -51,6 +45,27 @@ int dhcp_message_init(
message->xid = htobe32(xid);
message->magic = htobe32(DHCP_MAGIC_COOKIE);
return 0;
}
int dhcp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint8_t type,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr,
size_t optlen,
size_t *optoffset) {
size_t offset = 0;
int r;
r = bootp_message_init(message, op, xid, arp_type, hlen, chaddr);
if (r < 0)
return r;
r = dhcp_option_append(message, optlen, &offset, 0,
SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
if (r < 0)

View File

@ -6,6 +6,14 @@
#include "dhcp-protocol.h"
int bootp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr);
int dhcp_message_init(
DHCPMessage *message,
uint8_t op,

View File

@ -105,6 +105,7 @@ struct sd_dhcp_client {
int socket_priority;
bool socket_priority_set;
bool ipv6_acquired;
bool bootp;
};
static const uint8_t default_req_opts[] = {
@ -656,6 +657,15 @@ int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint64_t
return 0;
}
int sd_dhcp_client_set_bootp(sd_dhcp_client *client, bool bootp) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->bootp = bootp;
return 0;
}
static void client_set_state(sd_dhcp_client *client, DHCPState state) {
assert(client);
@ -793,9 +803,15 @@ static int client_message_init(
if (!packet)
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
optlen, &optoffset);
if (client->bootp) {
r = bootp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, client->arp_type,
client->hw_addr.length, client->hw_addr.bytes);
} else {
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
optlen, &optoffset);
}
if (r < 0)
return r;
@ -825,14 +841,16 @@ static int client_message_init(
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
packet->dhcp.flags = htobe16(0x8000);
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
Identifier option is not set */
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id.size,
client->client_id.raw);
if (r < 0)
return r;
if (!client->bootp) {
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
Identifier option is not set */
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id.size,
client->client_id.raw);
if (r < 0)
return r;
}
/* RFC2131 section 3.5:
in its initial DHCPDISCOVER or DHCPREQUEST message, a
@ -1509,13 +1527,15 @@ static int client_parse_message(
}
r = dhcp_option_parse(message, len, dhcp_lease_parse_options, lease, &error_message);
if (r < 0)
if (r == -ENOMSG && client->bootp)
log_dhcp_client(client, "No DHCP options found in bootp message, continuing.");
else if (r < 0)
return log_dhcp_client_errno(client, r, "Failed to parse DHCP options, ignoring: %m");
switch (client->state) {
case DHCP_STATE_SELECTING:
if (r == DHCP_ACK) {
if (!client->rapid_commit)
if (!client->rapid_commit && !client->bootp)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received unexpected ACK, ignoring.");
if (!lease->rapid_commit)
@ -1532,8 +1552,11 @@ static int client_parse_message(
if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
lease->lifetime = client->fallback_lease_lifetime;
} else
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received unexpected message, ignoring.");
if (client->bootp)
log_dhcp_client(client, "Ignoring return value %d for bootp message", r);
else
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received unexpected message, ignoring.");
break;
@ -1561,6 +1584,9 @@ static int client_parse_message(
lease->next_server = message->siaddr;
lease->address = message->yiaddr;
if (client->bootp)
lease->lifetime = USEC_INFINITY;
if (lease->address == 0 ||
lease->server_address == 0 ||
lease->lifetime == 0)
@ -1601,7 +1627,7 @@ static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage
dhcp_lease_unref_and_replace(client->lease, lease);
if (client->lease->rapid_commit) {
if (client->lease->rapid_commit || client->bootp) {
log_dhcp_client(client, "ACK");
return SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
}
@ -2007,7 +2033,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, s
if (r < 0)
return 0; /* invalid message, let's ignore it */
if (client->lease->rapid_commit)
if (client->lease->rapid_commit || client->bootp)
/* got a successful rapid commit */
return client_enter_bound(client, r);

View File

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

View File

@ -1485,6 +1485,10 @@ static int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to allocate DHCPv4 client: %m");
r = sd_dhcp_client_set_bootp(link->dhcp_client, link->network->dhcp_send_bootp);
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set BOOTP flag: %m");
r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0);
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to attach event to DHCPv4 client: %m");

View File

@ -243,6 +243,7 @@ DHCPv4.QuickAck, config_parse_bool,
DHCPv4.RequestOptions, config_parse_dhcp_request_options, AF_INET, 0
DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCPv4.SendHostname, config_parse_dhcp_send_hostname, AF_INET, 0
DHCPv4.Bootp, config_parse_bool, 0, offsetof(Network, dhcp_send_bootp)
DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
DHCPv4.Label, config_parse_dhcp_label, 0, offsetof(Network, dhcp_label)
DHCPv4.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast)

View File

@ -405,6 +405,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp_broadcast = -1,
.dhcp_ipv6_only_mode = -1,
.dhcp_6rd_prefix_route_type = RTN_UNREACHABLE,
.dhcp_send_bootp = false,
.dhcp6_use_address = true,
.dhcp6_use_pd_prefix = true,

View File

@ -179,6 +179,7 @@ struct Network {
OrderedHashmap *dhcp_client_send_vendor_options;
char *dhcp_netlabel;
NFTSetContext dhcp_nft_set_context;
bool dhcp_send_bootp;
/* DHCPv6 Client support */
bool dhcp6_use_address;

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

@ -85,6 +85,7 @@ static char *arg_exec_path = NULL;
static bool arg_ignore_failure = false;
static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@ -96,6 +97,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -171,6 +173,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 +194,9 @@ static int help_sudo_mode(void) {
" -D --chdir=PATH Set working directory\n"
" --setenv=NAME[=VALUE] Set environment variable\n"
" --background=COLOR Set ANSI color for background\n"
" --pty Request allocation of a pseudo TTY for stdio\n"
" --pipe Request direct pipe for stdio\n"
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -674,7 +683,7 @@ static int parse_argv(int argc, char *argv[]) {
/* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
* to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
* pipeline, but we are neatly interactive with tty-level isolation otherwise. */
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ?
ARG_STDIO_PTY :
ARG_STDIO_DIRECT;
@ -770,27 +779,33 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_NICE,
ARG_SETENV,
ARG_BACKGROUND,
ARG_PTY,
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
* though (but limit the extension to long options). */
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "machine", required_argument, NULL, ARG_MACHINE },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE },
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "machine", required_argument, NULL, ARG_MACHINE },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE },
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "pty", no_argument, NULL, ARG_PTY },
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{},
};
@ -883,6 +898,26 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
break;
case ARG_PTY:
if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_PTY;
break;
case ARG_PIPE:
if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_DIRECT;
break;
case ARG_SHELL_PROMPT_PREFIX:
r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -913,7 +948,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_wait = true;
arg_aggressive_gc = true;
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
arg_expand_environment = false;
arg_send_sighup = true;
@ -993,6 +1030,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
}
if (!arg_shell_prompt_prefix) {
const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
if (e) {
arg_shell_prompt_prefix = strdup(e);
if (!arg_shell_prompt_prefix)
return log_oom();
} else if (emoji_enabled()) {
arg_shell_prompt_prefix = strjoin(special_glyph(SPECIAL_GLYPH_SUPERHERO), " ");
if (!arg_shell_prompt_prefix)
return log_oom();
}
}
if (!isempty(arg_shell_prompt_prefix)) {
r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
if (r < 0)
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
return 1;
}
@ -1181,7 +1237,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
if (r < 0)
return bus_log_create_error(r);
send_term = isatty_safe(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
send_term = isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO);
}
if (send_term) {
@ -1345,70 +1401,38 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
unsigned soft_reboots_count = 0;
const char *unique, *id;
char *p;
static int make_unit_name(UnitType t, char **ret) {
int r;
assert(bus);
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
assert(ret);
r = sd_bus_get_unique_name(bus, &unique);
/* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
* managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
* logic in place. */
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
r = pidref_set_self(&self);
if (r < 0)
return log_error_errno(r, "Failed to get reference to my own process: %m");
r = pidref_acquire_pidfd_id(&self);
if (r < 0) {
log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
/* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
sd_id128_t rnd;
/* We couldn't get the unique name, which is a pretty
* common case if we are connected to systemd
* directly. In that case, just pick a random uuid as
* name */
r = sd_id128_randomize(&rnd);
if (r < 0)
return log_error_errno(r, "Failed to generate random run unit name: %m");
if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
return log_oom();
r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
} else
r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
if (r < 0)
return log_oom();
return 0;
}
/* We managed to get the unique name, then let's use that to name our transient units. */
id = startswith(unique, ":1."); /* let' strip the usual prefix */
if (!id)
id = startswith(unique, ":"); /* the spec only requires things to start with a colon, hence
* let's add a generic fallback for that. */
if (!id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unique name %s has unexpected format.",
unique);
/* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
* and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
* hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = bus_get_property_trivial(
bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
if (r < 0)
log_debug_errno(r,
"Failed to get SoftRebootsCount property, ignoring: %s",
bus_error_message(&error, r));
}
if (soft_reboots_count > 0) {
if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
return log_oom();
} else {
p = strjoin("run-u", id, ".", unit_type_to_string(t));
if (!p)
return log_oom();
}
*ret = p;
return 0;
}
@ -1818,7 +1842,7 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
}
@ -2033,7 +2057,7 @@ static int start_transient_scope(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
} else {
r = make_unit_name(bus, UNIT_SCOPE, &scope);
r = make_unit_name(UNIT_SCOPE, &scope);
if (r < 0)
return r;
}
@ -2332,7 +2356,7 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
break;
}
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
@ -2411,16 +2435,30 @@ static int run(int argc, char* argv[]) {
}
if (!arg_description) {
char *t;
_cleanup_free_ char *t = NULL;
if (strv_isempty(arg_cmdline))
t = strdup(arg_unit);
else
else if (startswith(arg_cmdline[0], "-")) {
/* Drop the login shell marker from the command line when generating the description,
* in order to minimize user confusion. */
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
if (!l)
return log_oom();
r = free_and_strdup_warn(l + 0, l[0] + 1);
if (r < 0)
return r;
t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
} else
t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
if (!t)
return log_oom();
free_and_replace(arg_description, t);
arg_description = strjoin("[", program_invocation_short_name, "] ", t);
if (!arg_description)
return log_oom();
}
/* For backward compatibility reasons env var expansion is disabled by default for scopes, and

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

@ -147,6 +147,9 @@ int sd_dhcp_client_set_socket_priority(
int sd_dhcp_client_set_fallback_lease_lifetime(
sd_dhcp_client *client,
uint64_t fallback_lease_lifetime);
int sd_dhcp_client_set_bootp(
sd_dhcp_client *client,
bool bootp);
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);

View File

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

View File

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

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