systemctl: generalize GetUnitByPIDFD handling

This commit is contained in:
Mike Yuan
2024-02-24 12:24:41 +08:00
parent d9219935ab
commit 716a4cdb0e
3 changed files with 170 additions and 143 deletions
+148
View File
@@ -18,6 +18,8 @@
#include "glob-util.h"
#include "macro.h"
#include "path-util.h"
#include "pidref.h"
#include "process-util.h"
#include "reboot-util.h"
#include "set.h"
#include "spawn-ask-password-agent.h"
@@ -985,3 +987,149 @@ int halt_now(enum action a) {
assert_not_reached();
}
}
int get_unit_by_pid(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(bus);
assert(pid >= 0); /* 0 is accepted by GetUnitByPID for querying our own process. */
r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", (uint32_t) pid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_UNIT_FOR_PID))
return log_error_errno(r, "%s", bus_error_message(&error, r));
return log_error_errno(r,
"Failed to get unit that PID " PID_FMT " belongs to: %s",
pid > 0 ? pid : getpid_cached(),
bus_error_message(&error, r));
}
_cleanup_free_ char *u = NULL, *p = NULL;
const char *path;
r = sd_bus_message_read_basic(reply, 'o', &path);
if (r < 0)
return bus_log_parse_error(r);
if (ret_unit) {
r = unit_name_from_dbus_path(path, &u);
if (r < 0)
return log_error_errno(r,
"Failed to extract unit name from D-Bus object path '%s': %m",
path);
}
if (ret_path) {
p = strdup(path);
if (!p)
return log_oom();
}
if (ret_unit)
*ret_unit = TAKE_PTR(u);
if (ret_path)
*ret_path = TAKE_PTR(p);
return 0;
}
static int get_unit_by_pidfd(sd_bus *bus, const PidRef *pid, char **ret_unit, char **ret_path) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(bus);
assert(pidref_is_set(pid));
if (pid->fd < 0)
return -EOPNOTSUPP;
r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pid->fd);
if (r < 0) {
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
return -EOPNOTSUPP;
if (sd_bus_error_has_names(&error, BUS_ERROR_NO_UNIT_FOR_PID, BUS_ERROR_NO_SUCH_PROCESS))
return log_error_errno(r, "%s", bus_error_message(&error, r));
return log_error_errno(r,
"Failed to get unit that PID " PID_FMT " belongs to: %s",
pid->pid, bus_error_message(&error, r));
}
_cleanup_free_ char *u = NULL, *p = NULL;
const char *path, *unit;
r = sd_bus_message_read(reply, "os", &path, &unit);
if (r < 0)
return bus_log_parse_error(r);
if (ret_unit) {
u = strdup(unit);
if (!u)
return log_oom();
}
if (ret_path) {
p = strdup(path);
if (!p)
return log_oom();
}
if (ret_unit)
*ret_unit = TAKE_PTR(u);
if (ret_path)
*ret_path = TAKE_PTR(p);
return 0;
}
int lookup_unit_by_pidref(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path) {
int r;
assert(bus);
assert(pid >= 0); /* 0 means our own process */
if (arg_transport != BUS_TRANSPORT_LOCAL)
return get_unit_by_pid(bus, pid, ret_unit, ret_path);
static bool use_pidfd = true;
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return log_error_errno(r,
r == -ESRCH ?
"PID " PID_FMT " doesn't exist or is already gone." :
"Failed to create reference to PID " PID_FMT ": %m",
pid);
if (use_pidfd) {
r = get_unit_by_pidfd(bus, &pidref, ret_unit, ret_path);
if (r != -EOPNOTSUPP)
return r;
use_pidfd = false;
log_debug_errno(r, "Unable to look up process using pidfd, falling back to pid.");
}
_cleanup_free_ char *u = NULL, *p = NULL;
r = get_unit_by_pid(bus, pidref.pid, ret_unit ? &u : NULL, ret_path ? &p : NULL);
if (r < 0)
return r;
r = pidref_verify(&pidref);
if (r < 0)
return log_error_errno(r, "Failed to verify our reference to PID " PID_FMT ": %m", pidref.pid);
if (ret_unit)
*ret_unit = TAKE_PTR(u);
if (ret_path)
*ret_path = TAKE_PTR(p);
return 0;
}