core: add GetUnitByPIDFD method and use it in systemctl

A pid can be recycled, but a pidfd is pinned. Add a new method that is safer
as it takes a pidfd as input.
Return not only the D-Bus object path, but also the unit id and the last
recorded invocation id, as they are both useful (especially the id, as
converting from a path object to a unit id from a script requires another
round-trip via D-Bus).

Note that the manager still tracks processes by pid, so theorethically this
is not fully error-proof, but on the other hand the method response is
synchronous and the manager is single-threaded, so once a call is being
processed the unit database will not change anyway. Once the manager
switches to use pidfds everywhere, this can be further hardened.
This commit is contained in:
Luca Boccassi
2023-01-18 10:58:46 +01:00
committed by Lennart Poettering
parent 9ca3bfb6c4
commit e0e7bc8223
5 changed files with 151 additions and 13 deletions
+73 -12
View File
@@ -13,6 +13,7 @@
#include "errno-util.h"
#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
#include "format-util.h"
#include "hexdecoct.h"
#include "hostname-util.h"
@@ -2100,29 +2101,93 @@ static int show_one(
return 0;
}
static int get_unit_dbus_path_by_pid(
static int get_unit_dbus_path_by_pid_fallback(
sd_bus *bus,
uint32_t pid,
char **unit) {
char **ret_path,
char **ret_unit) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
char *u;
_cleanup_free_ char *path = NULL, *unit = NULL;
char *p;
int r;
assert(bus);
assert(ret_path);
assert(ret_unit);
r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", pid);
if (r < 0)
return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
r = sd_bus_message_read(reply, "o", &u);
r = sd_bus_message_read(reply, "o", &p);
if (r < 0)
return bus_log_parse_error(r);
u = strdup(u);
if (!u)
path = strdup(p);
if (!path)
return log_oom();
*unit = u;
r = unit_name_from_dbus_path(path, &unit);
if (r < 0)
return log_oom();
*ret_unit = TAKE_PTR(unit);
*ret_path = TAKE_PTR(path);
return 0;
}
static int get_unit_dbus_path_by_pid(
sd_bus *bus,
uint32_t pid,
char **ret_path,
char **ret_unit) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL, *unit = NULL;
_cleanup_close_ int pidfd = -EBADFD;
char *p, *u;
int r;
assert(bus);
assert(ret_path);
assert(ret_unit);
/* First, try to send a PIDFD across the wire, so that we can pin the process and there's no race
* condition possible while we wait for the D-Bus reply. If we either don't have PIDFD support in
* the kernel or the new D-Bus method is not available, then fallback to the older method that
* sends the numeric PID. */
pidfd = pidfd_open(pid, 0);
if (pidfd < 0 && ERRNO_IS_NOT_SUPPORTED(errno))
return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
if (pidfd < 0)
return log_error_errno(errno, "Failed to open PID %"PRIu32": %m", pid);
r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pidfd);
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
if (r < 0)
return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
r = sd_bus_message_read(reply, "os", &p, &u);
if (r < 0)
return bus_log_parse_error(r);
path = strdup(p);
if (!path)
return log_oom();
unit = strdup(u);
if (!unit)
return log_oom();
*ret_unit = TAKE_PTR(unit);
*ret_path = TAKE_PTR(path);
return 0;
}
@@ -2297,15 +2362,11 @@ int verb_show(int argc, char *argv[], void *userdata) {
} else {
/* Interpret as PID */
r = get_unit_dbus_path_by_pid(bus, id, &path);
r = get_unit_dbus_path_by_pid(bus, id, &path, &unit);
if (r < 0) {
ret = r;
continue;
}
r = unit_name_from_dbus_path(path, &unit);
if (r < 0)
return log_oom();
}
r = show_one(bus, path, unit, show_mode, &new_line, &ellipsized);