mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
systemctl: support --global and --root in edit and cat
Make bus acquisition conditional in verb_edit() and verb_cat(), following
the same pattern used in verb_enable(). When install_client_side() returns
non-zero (indicating --global, --root, offline, or similar scenarios), skip
acquiring a D-Bus connection and perform all operations client-side.
Changes:
- Only acquire bus when install_client_side() returns NO
- Use mangle_names() instead of expand_unit_names() in client-side mode
- Pass force_client_side flag based on bus availability
- Skip bus-dependent operations (need_daemon_reload, etc.) when bus is NULL
This allows 'systemctl edit --global' and 'systemctl cat --global' to work
correctly, fixing the regression introduced by commit d77d42ed3a.
Test cases added to verify:
- Creating and editing global user units with --runtime
- Reading global units with cat --global
- Proper detection and rejection of masked units in client-side mode
- Tests use /run/ instead of /etc/ for safer temporary testing
Fixes https://github.com/systemd/systemd/issues/31272
This commit is contained in:
@@ -24,7 +24,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
|
||||
_cleanup_(lookup_paths_done) LookupPaths lp = {};
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
sd_bus *bus;
|
||||
sd_bus *bus = NULL;
|
||||
bool first = true;
|
||||
int r, rc = 0;
|
||||
|
||||
@@ -39,17 +39,24 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acquire_bus(BUS_MANAGER, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (install_client_side() == INSTALL_CLIENT_SIDE_NO) {
|
||||
r = acquire_bus(BUS_MANAGER, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to expand names: %m");
|
||||
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to expand names: %m");
|
||||
|
||||
r = maybe_extend_with_unit_dependencies(bus, &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = maybe_extend_with_unit_dependencies(bus, &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
/* In client-side mode (--global, --root, etc.), just mangle names without bus interaction */
|
||||
r = mangle_names("to cat", strv_skip(argv, 1), &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
@@ -57,7 +64,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *fragment_path = NULL;
|
||||
_cleanup_strv_free_ char **dropin_paths = NULL;
|
||||
|
||||
r = unit_find_paths(bus, *name, &lp, false, &cached_id_map, &cached_name_map, &fragment_path, &dropin_paths);
|
||||
r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ !bus, &cached_id_map, &cached_name_map, &fragment_path, &dropin_paths);
|
||||
if (r == -ERFKILL) {
|
||||
printf("%s# Unit %s is masked%s.\n",
|
||||
ansi_highlight_magenta(),
|
||||
@@ -87,7 +94,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
||||
else
|
||||
puts("");
|
||||
|
||||
if (need_daemon_reload(bus, *name) > 0) /* ignore errors (<0), this is informational output */
|
||||
if (bus && need_daemon_reload(bus, *name) > 0) /* ignore errors (<0), this is informational output */
|
||||
fprintf(stderr,
|
||||
"%s# Warning: %s changed on disk, the version systemd has loaded is outdated.\n"
|
||||
"%s# This output shows the current version of the unit's original fragment and drop-in files.\n"
|
||||
@@ -211,7 +218,6 @@ static int find_paths_to_edit(
|
||||
const char *drop_in;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(context);
|
||||
assert(names);
|
||||
|
||||
@@ -241,8 +247,8 @@ static int find_paths_to_edit(
|
||||
_cleanup_free_ char *path = NULL;
|
||||
_cleanup_strv_free_ char **unit_paths = NULL;
|
||||
|
||||
r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ false, &cached_id_map, &cached_name_map, &path, &unit_paths);
|
||||
if (r == -EKEYREJECTED) {
|
||||
r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ !bus, &cached_id_map, &cached_name_map, &path, &unit_paths);
|
||||
if (r == -EKEYREJECTED && bus) {
|
||||
/* If loading of the unit failed server side complete, then the server won't tell us
|
||||
* the unit file path. In that case, find the file client side. */
|
||||
|
||||
@@ -327,7 +333,7 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
||||
.read_from_stdin = arg_stdin,
|
||||
};
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
sd_bus *bus;
|
||||
sd_bus *bus = NULL;
|
||||
int r;
|
||||
|
||||
if (!on_tty() && !arg_stdin)
|
||||
@@ -340,13 +346,20 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acquire_bus(BUS_MANAGER, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (install_client_side() == INSTALL_CLIENT_SIDE_NO) {
|
||||
r = acquire_bus(BUS_MANAGER, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to expand names: %m");
|
||||
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to expand names: %m");
|
||||
} else {
|
||||
/* In client-side mode (--global, --root, etc.), just mangle names without bus interaction */
|
||||
r = mangle_names("to edit", strv_skip(argv, 1), &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (strv_isempty(names))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No units matched the specified patterns.");
|
||||
|
||||
|
||||
@@ -570,4 +570,48 @@ systemctl daemon-reload
|
||||
systemctl enable --now test-WantedBy.service || :
|
||||
systemctl daemon-reload
|
||||
|
||||
# Test systemctl edit --global and systemctl cat --global (issue #31272)
|
||||
GLOBAL_UNIT_NAME="systemctl-test-$RANDOM.service"
|
||||
GLOBAL_MASKED_UNIT="systemctl-test-masked-$RANDOM.service"
|
||||
|
||||
# Test 1: Create a new global user unit with --force and --runtime
|
||||
systemctl edit --global --runtime --stdin --full --force "$GLOBAL_UNIT_NAME" <<EOF
|
||||
[Unit]
|
||||
Description=Test global unit
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/true
|
||||
EOF
|
||||
|
||||
# Verify the unit file was created in /run/systemd/user/
|
||||
test -f "/run/systemd/user/$GLOBAL_UNIT_NAME"
|
||||
|
||||
# Test 2: Read the global unit with systemctl cat --global
|
||||
systemctl cat --global "$GLOBAL_UNIT_NAME" | grep -q "ExecStart=/bin/true"
|
||||
|
||||
# Test 3: Edit existing global unit (add a drop-in)
|
||||
systemctl edit --global --runtime --stdin "$GLOBAL_UNIT_NAME" <<EOF
|
||||
[Service]
|
||||
Environment=TEST=value
|
||||
EOF
|
||||
|
||||
# Verify drop-in was created
|
||||
test -f "/run/systemd/user/$GLOBAL_UNIT_NAME.d/override.conf"
|
||||
systemctl cat --global "$GLOBAL_UNIT_NAME" | grep -q "Environment=TEST=value"
|
||||
|
||||
# Test 4: Create a masked global unit in /run/
|
||||
mkdir -p /run/systemd/user
|
||||
ln -sf /dev/null "/run/systemd/user/$GLOBAL_MASKED_UNIT"
|
||||
|
||||
# Test 5: Verify cat shows it's masked
|
||||
systemctl cat --global "$GLOBAL_MASKED_UNIT" 2>&1 | grep -q "masked"
|
||||
|
||||
# Test 6: Verify edit refuses to edit masked unit
|
||||
(! systemctl edit --global --runtime --stdin --full "$GLOBAL_MASKED_UNIT" </dev/null 2>&1) | grep -q "masked"
|
||||
|
||||
# Cleanup global test units
|
||||
rm -f "/run/systemd/user/$GLOBAL_UNIT_NAME"
|
||||
rm -rf "/run/systemd/user/$GLOBAL_UNIT_NAME.d"
|
||||
rm -f "/run/systemd/user/$GLOBAL_MASKED_UNIT"
|
||||
|
||||
touch /testok
|
||||
|
||||
Reference in New Issue
Block a user