_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;
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);
_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(),
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"
const char *drop_in;
int r;
- assert(bus);
assert(context);
assert(names);
_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. */
.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)
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.");
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