In practice I think this will ever only be used with ansi_highlight_red(),
but it's cleaner to add this generic extension rather than e.g. a boolean
highlight_footer_red.
The options is passed through via command_print_help_full(), not through
the COMMAND structure, which means that commands with 'help' verb are not
covered by this. The red footer highligting is used for legacy commands,
which don't have verbs, so I think this is OK. Making it generic and
covering 'help' verbs would require more refactoring, and I think it
doesn't make sense to add it until it's actually needed.
Almost all programs are simple and have only a single command. When
printing options, let's just use the first COMMAND block found by default.
This means we can drop the explicit program-name argument and reduce
copy&paste boilerplate.
Since run0 --help now goes through the pager, provide a way to turn that
off from the command line, like all other commands that page --help. The
option is documented the same way as for systemd-run.
The custom help() and help_sudo_mode() are replaced by COMMAND()
descriptions for systemd-run and run0, and --introspect-cli is added to
both. run0 --help now goes through the pager, like systemd-run --help.
Let's make timer prop handling less special, and more like path/socket
handling. Let's move the checks for at least one OnXYZ= setting to a
common place at the end of parsing, instead of explicit checks for each
property.
Let's shorten an already very long .c file, by splitting it apart a bit.
Splitting out the polkit code is relatively easy, since it does not
touch any of the arg_xyz variables.
Reproducer:
unit=run-wait-rae-$(date +%s)
sudo timeout 3s systemd-run --wait --remain-after-exit \
--unit="$unit" /bin/true
echo $?
systemctl is-active "$unit.service"
Before, the command timed out with exit status 124 while the service
stayed active. --wait waits for deactivation, but RemainAfterExit=yes
keeps the service active after the command exits.
Follow-up for 2a453c2ee3
Reproducer:
sudo systemd-run --wait --verbose --json=short /bin/echo hi
Before, systemd-run printed JSON metadata to stdout while --verbose
also spawned journalctl output on stdout. The resulting stream mixed JSON
with journal lines, so reject the conflicting options.
Follow-up for 744ca8f616
Reproducer:
sudo systemd-run --scope --json=short /bin/echo hi
Before, systemd-run printed JSON metadata to stdout and then executed
the scope command on the same stdout. The combined stream was not valid
JSON, so reject --json= in scope mode.
Follow-up for fe5a6c47af
Reproducer:
unit=run-json-trigger-$(date +%s)
sudo systemd-run --json=short --unit="$unit" \
--on-active=30s \
/bin/true
Before, trigger mode accepted --json=short but printed only human-readable
"Running timer as unit" and "Will run service" lines. Reject the option
until trigger mode has structured output.
Follow-up for fe5a6c47af
Reproducer:
sudo systemd-run --wait --pipe --json=short /bin/echo hi
Before, systemd-run wrote its JSON metadata to stdout and then passed
the command stdout through the same stream. The combined output was not
valid machine-readable JSON, so reject the conflicting modes.
Follow-up for fe5a6c47af
Reproducer:
sudo systemd-run --scope --uid=nobody /usr/bin/id
Before, the command ran as nobody but kept the caller supplementary
root group, for example groups=65534(nogroup),0(root). Scope mode
performs the uid/gid switch locally, so initialize the target user groups
before dropping privileges.
Follow-up for 4de33e7f32
Reproducer:
unit=run-explicit-path-$(date +%s)
systemd-run --unit="$unit.path" \
--path-property=PathExists=/tmp \
/bin/true
echo $?
Before, an explicit .path unit name was not recognized as the
trigger unit. It was mangled again as a service name, so PID 1 rejected
the transient request with an already-loaded unit conflict.
Follow-up for d59ef3e243
Reproducer:
sudo systemd-run --scope --ignore-failure /bin/false
echo $?
Before, the option was accepted but had no effect because scope mode
executes the command locally after creating the scope. The flag is only
encoded into service ExecStart properties, so accept it only where it can
be applied.
Follow-up for 1072d94731
Reproducer:
unit=run-nb-trigger-$(date +%s)
systemd-run --no-block --collect --unit="$unit" \
--socket-property=ListenStream=/proc/systemd-run-repro/socket \
/usr/bin/true
echo $?
Before, systemd-run still waited for the trigger unit job and
propagated the socket start failure. With --no-block it should only
verify and enqueue the request, as the service path already does.
Follow-up for 3d161f991e
--on-clock-change and --on-timezone-change are documented as shortcuts for
setting the corresponding timer properties with --timer-property=.
However, --timer-property= only treated the monotonic and calendar timer
settings as actual timer triggers. OnClockChange= and OnTimezoneChange=
were stored as timer properties, but arg_with_timer remained false and the
command was rejected as having no timer options.
Treat both properties as timer triggers too, matching the shortcut options
and the documented equivalent command line.
When a fixed date (e.g. 2027-01-01) is paired with a weekday constraint
(e.g. Thu) that does not match, the timer silently never elapses.
Add calendar_spec_from_string_full(..., warn_on_weekday_mismatch) so
user-facing tools can opt in to a log_warning() at parse time:
- systemd-analyze calendar: uses _full(true)
- systemd-run --on-calendar: uses _full(true)
- .timer OnCalendar=: uses log_syntax() with file/line context
Add test_calendar_spec_weekday_conflict(): forks a child with stderr
captured in a memfd via pidref_safe_fork_full(), verifies the warning
is emitted for conflicting specs and suppressed for valid ones.
Fixes: #40350
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
When neither --user=, --area= nor --empower is given, run0 already
behaves as if root was requested, but only implicitly. That can be
misleading downstream.
Set arg_exec_user to "root" up front in parse_argv_sudo_mode(), so the
intent is visible and the rest of the code can rely on it.
Fixes#40468
Signed-off-by: Shihao Ren <renshihao.rsh@bytedance.com>
In the systemd-run --scope mode, --no-block has no actual effect and will
be silently ignored. Therefore, this combination is explicitly rejected to
reduce confusion for users when using it.
Fixes: #42806
When using --slice-inherit, setting arg_slice would inadvertently munge
user.slice into the current user slice, usually producing a slice name
like user-1000-user.slice. This is treated as a user-UID slice by
user-.slice.d/10-defaults.conf, resulting in a strange description:
literally "User slice for UID user" (instead of an actual user UID).
Keep arg_slice empty when using --slice-inherit so that we actually
inherit from the relevant slice instead of a munged version.
Fixes: #42601
--slice and --slice-inherit are intended to make the new service unit
part of a specific slice. Logind is incompatible with that goal, as a
session of any kind will prompt logind to immediately yoink the new
command from the service unit into a new session scope, which does not
inherit from run0's own slice. The use can still explicitly request a
session with --setenv=XDG_SESSION_CLASS=<class>.
Also make --slice and --slice-inherit conflict with --lightweight and
--area, which depend on logind to be effective.
unit_name_mangle_with_suffix() is quite benevolent by default and allows
the unit to "transition" into a different unit type than what's
requested via its suffix argument. For example, calling
unit_name_mangle_with_suffix() with "/foo/bar" as a unit name and
".service" as a suffix would give you "foo-bar.mount", without any
warning or error.
This could then lead to a quite confusing errors in certain situations:
~# systemd-run --remain-after-exit --unit /foo/bar true
Failed to start transient service unit: Cannot set property RemainAfterExit, or unknown property.
Given we can't change the default behaviour of
unit_name_mangle_with_suffix() as some parts of systemd already depend
on its "benevolence" (like systemctl), let's introduce a new flag -
UNIT_NAME_MANGLE_STRICT - that checks if the mangled/resolved unit
name's suffix matches the requested one and errors out if not.
With the flag used throughout systemd-run's code, the error in the above
case is now a bit more clear:
~# build/systemd-run --remain-after-exit --unit /foo/bar true
Path "/foo/bar" resolves to unit type "mount", but "service" is expected as unit.
Failed to mangle unit name: Invalid argument
Resolves: #39996
This is meant to mirror sudo's -k/--reset-timestamp and
-K/--remove-timestamp options, which revoke the temporary authorization
provided by the timestamp files in /var/run/sudo/ts.
To achieve the same effect in run0, we ask polkit to revoke our
temporary authorization. If used with a command, run0 will revoke the
temporary auth and then immediately authorize the user again, just like
sudo -k. All the bus calls are completed synchronously, as they need to
complete before authorizing the user anyway.
Like sudo, the effect of -k/--reset-timestamp is to revoke only the
tmpauthz that polkit would have used to authorize the command, if
available. The -K/--remove-timestamp option will revoke all temporary
authorizations across all ttys.
run0 doesn't actually use the auth pam stack, since polkit does the
requisite authorization. However, if the service type is left undefined
pam falls back to the definitions of the "other" service, which, at
least in Arch Linux but possibly more, includes pam_warn.so to notify
the user about this apparent error.
This creates a bit of logspam, as systemd does actually call pam_setcred
in its generic pam code, which depends on the auth pam stack, creating a
warning message in the journal on every invocation of run0.
pam_deny.so is a no-op, which avoids falling back to the other pam
service.
get_user_creds would use getpwnam() and then returns strings pointing
into the static buffer. This seems very iffy. Let's duplicate the
strings properly
The param was input/output, which is unexpected and confusing and
actually made most callers much more complicated than they needed to be.
The function was playing fast and loose with the return value. In some
cases it was returning a static string, which would be completely fine.
But in other case it was returning a pointer into the getgrgid static
buffer, i.e. that return value could be overwritten. AFAICT, this didn't
matter in any of the callers, but we shouldn't do that anyway.
So use a separate output param with an allocated string that the caller
is responsible for.
It turns out that all callers pass NULL (outside of tests) and zero for
flags. So the function _could_ be simplified. But get_user_creds is
called with all the params actually used, and I think having the the
functions so different wouldn't be nice. I first wrote a commit to
drop the unused params, but then I discarded it.
The error message in exec-invoke.c is fixed.
I am really not a fan of full code lines passed to macros as parameters.
Let's get rid of the 3rd parameter of FOREACH_OPTION() hence:
1. Let's return errors just as a regular value (though a negative one),
that can be handled via a OPTION_ERROR case statement for the switch.
This normalizes handling of the error, just like any other event
returned by the option parser.
2. In order to avoid exploding the amount of boilerplate in each use
(that just propagates the error on OPTION_ERROR), let's then
introduce an explicit FOREACH_OPTION_OR_RETURN(), that returns from
the calling function on its own (and makes that clear in the name).
Together this cleans up, normalizes the logic and shortens the code.
DEFINE_POINTER_ARRAY_CLEAR_FUNC() generates a helper of the form
helper_array_clear(T *array, size_t n) that drops each element but does
not free the array itself, parallel to DEFINE_POINTER_ARRAY_FREE_FUNC()
for cases where the array has automatic storage duration.
CLEANUP_ELEMENTS() pairs with these helpers to provide a _cleanup_-like
attribute for fixed-size arrays: the bound is taken from ELEMENTSOF(),
and the helper is invoked across the elements at scope exit. Compared to
CLEANUP_ARRAY(), the storage is neither freed nor zeroed.
Migrate various logic across the tree over to the new macros.
sd-device: use DEFINE_POINTER_ARRAY_CLEAR_FUNC() for sd_device_unref_array_clear()
Replace the local device_unref_many() helper with the macro-generated
equivalent.
format-table: switch help-table arrays to CLEANUP_ELEMENTS()
Generate table_unref_array_clear() via DEFINE_POINTER_ARRAY_CLEAR_FUNC()
and convert the help-table arrays in bootctl, cryptenroll, nspawn,
repart and vmspawn to CLEANUP_ELEMENTS(). The arrays no longer need a
trailing NULL slot, so the size matches ELEMENTSOF() of the groups
array.
firewall-util: switch netlink message arrays to CLEANUP_ELEMENTS()
Generate sd_netlink_message_unref_array_clear() via
DEFINE_POINTER_ARRAY_CLEAR_FUNC() in place of the NULL-terminated
sd_netlink_message_unref_many(), and convert the two stack arrays of
sd_netlink_message pointers to CLEANUP_ELEMENTS().
It seems that clang reorders the entries in the options array that
originate from different functions, but not within a function. Using
"named namespaces" exclusively should sidestep the issue.
(A bigger hammer would be to sort the array. We *can* do this, since the
options have the increasing .id field. But that'd require duplicating
the memory or making it writable. Let's avoid this until we know for
sure that it's needed.)
This exercises the new option namespace code.
--help output is generally the same, except for the formatting changes
related to use of the new output helpers and the common option macros.
--same-root-dir is now documented (followup for
475729b805).
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
--system is now documented (fixup for 66b1e74605).
--capsule is now documented (fixup for 759b3c082d).
--help output is generally the same, except for the formatting changes
related to use of the new output helpers and common option macros.
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
Both parse_argv() and parse_argv_sudo_mode() handled options in an
order that no longer matched the help text. Reorder the case statements
so the source order mirrors what the user sees in --help.
In parse_argv_sudo_mode(), drop the case 'i' → ARG_VIA_SHELL fall-through
so the cases can be sequenced independently; 'i' now sets arg_via_shell
directly.
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
This is useful when moving from `--pty` or `--pipe` to using
`--verbose`: you can use `--verbose-output=cat` to get similar output on
stdout while still having all of the advantages of `--verbose` over the
other options.
Almost all callers of table_print() specify stdout or NULL (equivalent
to stdout) as the output stream. Simplify things by not requiring the
the stream to be specified.
In almost all cases, the printing of the table is surrounded by normal
printfs() that don't do explicit flushing and for which we don't check
the output stream status. Let's simplify most callers and skip this
step. The reason is not so much to avoid the extra step itself, but
instead to avoid the _handling_ of the potential failure. We generally
only want to print an error message for ENOMEM and other "internal"
errors, so strictly speaking we should filter out the errors from the
stream. By skipping the flush&check step we implicitly do this.
I expressed the issue I have with parse_tristate_argument()
in #37751: it doesn't add any value to direct use of parse_tristate();
on the contrary, it doesn't support means to reset the arg to "auto"/-1 state.
The mere reason it existed is that we need a int type ret param.
Since the previous attempt to address this mess failed, let's
try to make the function more useful by making it accept "auto".
I figure this is useful on its own.
As requested in
https://github.com/systemd/systemd/pull/40652#discussion_r2831833996,
the function name is suffixed with _with_auto() to establish
that "auto" is handled internally.
For many use cases DynamicUser= just doesn't cut it. Specifically lots
of stuff wants to run as root. Let's support this use case by relaxing
our checks for capsules a bit.
This got split in 5cabeed80b
to accommodate --empower, and later --empower received
dedicated handling again (c36942916b).
I think the new naming makes more sense - --empower is privileged
after all, just with uid left unchanged. Hence merge
privileged_execution back into it.
Follow-up for 4f6ef13f43
Special casing --area= rather than --empower makes the code
self-explanatory, as --area= is about alternative home dir
after all. On top of that this ensures when --area= and
--empower are specified in combination we honor the home dir
switch, too.
Also, unless --same-root-dir was specified, don't make the executable
absolute if we're running in a chroot. Situations like this are still iffy,
but we might as well handle them a little more gracefully.
When in --empower mode, all created files will be owned by the current
user, which could be problematic when creating files outside of the
current user's home directory, as other processes running as the same
user would be able to edit those files.
While this is a bit of an edge case since users already have to go through
the effort of writing --empower to indicate they want a privileged session
as the current user, it's not unphatomable to think they could start an
empowered session which they later return to and continue using. Currently,
it's not easy to differentiate a regular run0 session and an empowered session
at a glance, so users might think they're using a regular run0 session when
they're actually using an empowered session.
To address this problem, let's give empowered session their own identify, by
making the background orange, using the pumpkin emoji as the shell prompt
prefix and giving them an orange circle as the PTY title.