diff options
author | Lennart Poettering <lennart@poettering.net> | 2016-04-20 15:28:28 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2016-04-22 16:06:20 +0200 |
commit | 291d565a04263452c03beaf537773ade4f0b1617 (patch) | |
tree | 257b4d239c7bbe9daf312a70c6aa5e2c5033ee65 /src/core | |
parent | 2b45d88163b29f04bf784385f4a490b2cf206861 (diff) | |
download | systemd-291d565a04263452c03beaf537773ade4f0b1617.tar.gz |
core,systemctl: add bus API to retrieve processes of a unit
This adds a new GetProcesses() bus call to the Unit object which returns an
array consisting of all PIDs, their process names, as well as their full cgroup
paths. This is then used by "systemctl status" to show the per-unit process
tree.
This has the benefit that the client-side no longer needs to access the
cgroupfs directly to show the process tree of a unit. Instead, it now uses this
new API, which means it also works if -H or -M are used correctly, as the
information from the specific host is used, and not the one from the local
system.
Fixes: #2945
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/busname.c | 10 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 25 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 143 | ||||
-rw-r--r-- | src/core/dbus-unit.h | 1 | ||||
-rw-r--r-- | src/core/mount.c | 10 | ||||
-rw-r--r-- | src/core/org.freedesktop.systemd1.conf | 4 | ||||
-rw-r--r-- | src/core/service.c | 19 | ||||
-rw-r--r-- | src/core/socket.c | 10 | ||||
-rw-r--r-- | src/core/swap.c | 10 | ||||
-rw-r--r-- | src/core/unit.c | 18 | ||||
-rw-r--r-- | src/core/unit.h | 9 |
11 files changed, 259 insertions, 0 deletions
diff --git a/src/core/busname.c b/src/core/busname.c index bbe61af4f0..f4f433340c 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -999,6 +999,14 @@ static bool busname_supported(void) { return supported; } +static int busname_control_pid(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + return n->control_pid; +} + static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_SUCCESS] = "success", [BUSNAME_FAILURE_RESOURCES] = "resources", @@ -1052,6 +1060,8 @@ const UnitVTable busname_vtable = { .supported = busname_supported, + .control_pid = busname_control_pid, + .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 0c86791fe3..d2eb388f7c 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -642,6 +642,30 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s return bus_unit_method_set_properties(message, u, error); } +static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_load_unit(m, name, NULL, error, &u); + if (r < 0) + return r; + + r = bus_unit_check_load_state(u, error); + if (r < 0) + return r; + + return bus_unit_method_get_processes(message, u, error); +} + static int transient_unit_from_message( Manager *m, sd_bus_message *message, @@ -2042,6 +2066,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index c507265070..ed207f15b9 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -24,8 +24,10 @@ #include "cgroup-util.h" #include "dbus-unit.h" #include "dbus.h" +#include "fd-util.h" #include "locale-util.h" #include "log.h" +#include "process-util.h" #include "selinux-access.h" #include "signal-util.h" #include "special.h" @@ -841,6 +843,146 @@ static int property_get_cgroup( return sd_bus_message_append(reply, "s", t); } +static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) { + _cleanup_free_ char *buf = NULL, *cmdline = NULL; + int r; + + assert(reply); + assert(pid > 0); + + r = set_put(pids, PID_TO_PTR(pid)); + if (r == -EEXIST || r == 0) + return 0; + if (r < 0) + return r; + + if (!p) { + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf); + if (r == -ESRCH) + return 0; + if (r < 0) + return r; + + p = buf; + } + + (void) get_process_cmdline(pid, 0, true, &cmdline); + + return sd_bus_message_append(reply, + "(sus)", + p, + (uint32_t) pid, + cmdline); +} + +static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(reply); + assert(p); + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f); + if (r == ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + pid_t pid; + + r = cg_read_pid(f, &pid); + if (r < 0) + return r; + if (r == 0) + break; + + if (is_kernel_thread(pid) > 0) + continue; + + r = append_process(reply, p, pid, pids); + if (r < 0) + return r; + } + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *g = NULL, *j = NULL; + + r = cg_read_subgroup(d, &g); + if (r < 0) + return r; + if (r == 0) + break; + + j = strjoin(p, "/", g, NULL); + if (!j) + return -ENOMEM; + + r = append_cgroup(reply, j, pids); + if (r < 0) + return r; + } + + return 0; +} + +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(set_freep) Set *pids = NULL; + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + pid_t pid; + int r; + + assert(message); + + pids = set_new(NULL); + if (!pids) + return -ENOMEM; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(sus)"); + if (r < 0) + return r; + + if (u->cgroup_path) { + r = append_cgroup(reply, u->cgroup_path, pids); + if (r < 0) + return r; + } + + /* The main and control pids might live outside of the cgroup, hence fetch them separately */ + pid = unit_main_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + pid = unit_control_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), @@ -848,6 +990,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), + SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 07948b9cd0..4db88dbebc 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -36,5 +36,6 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error); int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_check_load_state(Unit *u, sd_bus_error *error); diff --git a/src/core/mount.c b/src/core/mount.c index 632c5c824c..188fb0aa40 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1790,6 +1790,14 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error); } +static int mount_control_pid(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + return m->control_pid; +} + static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { [MOUNT_EXEC_MOUNT] = "ExecMount", [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", @@ -1851,6 +1859,8 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, + .control_pid = mount_control_pid, + .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index f78eedbd6e..b732501364 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -78,6 +78,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="GetUnitProcesses"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListJobs"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/service.c b/src/core/service.c index 58084e2f82..b46dd8bcdd 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3195,6 +3195,22 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); } +static int service_main_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->main_pid; +} + +static int service_control_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->control_pid; +} + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { [SERVICE_RESTART_NO] = "no", [SERVICE_RESTART_ON_SUCCESS] = "on-success", @@ -3303,6 +3319,9 @@ const UnitVTable service_vtable = { .notify_cgroup_empty = service_notify_cgroup_empty_event, .notify_message = service_notify_message, + .main_pid = service_main_pid, + .control_pid = service_control_pid, + .bus_name_owner_change = service_bus_name_owner_change, .bus_vtable = bus_service_vtable, diff --git a/src/core/socket.c b/src/core/socket.c index 65da0e3c5e..a9fff9c259 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2781,6 +2781,14 @@ char *socket_fdname(Socket *s) { return UNIT(s)->id; } +static int socket_control_pid(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + return s->control_pid; +} + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", [SOCKET_EXEC_START_CHOWN] = "StartChown", @@ -2846,6 +2854,8 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, + .control_pid = socket_control_pid, + .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/swap.c b/src/core/swap.c index c6502eb821..d8802470d2 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1426,6 +1426,14 @@ static bool swap_supported(void) { return supported; } +static int swap_control_pid(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + return s->control_pid; +} + static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { [SWAP_EXEC_ACTIVATE] = "ExecActivate", [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", @@ -1487,6 +1495,8 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, + .control_pid = swap_control_pid, + .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/unit.c b/src/core/unit.c index 1f57293a0b..cb79c7c6b1 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3801,3 +3801,21 @@ bool unit_is_pristine(Unit *u) { u->job || u->merged_into); } + +pid_t unit_control_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->control_pid) + return UNIT_VTABLE(u)->control_pid(u); + + return 0; +} + +pid_t unit_main_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->main_pid) + return UNIT_VTABLE(u)->main_pid(u); + + return 0; +} diff --git a/src/core/unit.h b/src/core/unit.h index cfdac852a5..5909652976 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -390,6 +390,12 @@ struct UnitVTable { /* Returns the next timeout of a unit */ int (*get_timeout)(Unit *u, usec_t *timeout); + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*main_pid)(Unit *u); + + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*control_pid)(Unit *u); + /* This is called for each unit type and should be used to * enumerate existing devices and load them. However, * everything that is loaded here should still stay in @@ -601,6 +607,9 @@ bool unit_type_supported(UnitType t); bool unit_is_pristine(Unit *u); +pid_t unit_control_pid(Unit *u); +pid_t unit_main_pid(Unit *u); + static inline bool unit_supported(Unit *u) { return unit_type_supported(u->type); } |