systemctl-wait: do not fail for non-existent unit as non-root user

Although the man page says

> A unit that does not exist is treated like a unit that terminated
> successfully, and a message about this is shown.

systemctl wait fails as follows when invoked by a non-root user:
```
$ systemctl wait nonexistent.service
Unit nonexistent.service has been removed while waiting for it.
Failed to wait for unit nonexistent.service to terminate.
$ echo $?
1
```

This makes the unit be treated as non-existent when the initial
GetAll() call returns LoadState=not-found. With this change,

```
$ systemctl wait nonexistent.service
Unit nonexistent.service does not exist, treating as terminated.
$ echo $?
0
```

Follow-up for 0e51517319.
This commit is contained in:
Yu Watanabe committed 2026-11-04 08:17:14 +09:00
1 parent 30bbc1a5a5
commit ac08033068
1 file changed
+26
+26
View File
@@ -455,6 +455,10 @@ static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m)
assert(item);
assert(m);
/* LoadState is a constant property, hence it is only contained in the reply to GetAll(), but never in
* PropertiesChanged signals. */
bool had_load_state = item->load_state;
r = bus_message_map_all_properties(m, map, BUS_MAP_STRDUP, /* reterr_error= */ NULL, item);
if (r < 0)
return r;
@@ -481,6 +485,28 @@ static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m)
item->removed = false;
}
if (!had_load_state && streq_ptr(item->load_state, "not-found")) {
/* The unit did not exist from the beginning. Treat it as terminated successfully. */
log_debug("Unit %s is not loaded.", item->name);
/* No result is collected for a unit that does not exist. Hence, create a fake result that
* only contains the load state, and pass it to the callback, so that the caller can tell
* that the unit does not exist. */
if (FLAGS_SET(item->flags, BUS_WAIT_COLLECT_RESULT)) {
r = free_and_strdup(&item->result.load_state, item->load_state);
if (r < 0) {
log_oom_debug();
wait_for_item_fail(item);
return 0;
}
}
BusWaitForUnits *d = ASSERT_PTR(item->parent);
call_unit_callback_and_wait(d, item, /* good= */ true);
bus_wait_for_units_check_ready(d);
return 0;
}
wait_for_item_check_ready(item);
return 0;
}