summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--src/basic/process-util.c12
-rw-r--r--src/basic/process-util.h2
-rw-r--r--src/core/busname.c10
-rw-r--r--src/core/dbus-manager.c25
-rw-r--r--src/core/dbus-unit.c143
-rw-r--r--src/core/dbus-unit.h1
-rw-r--r--src/core/mount.c10
-rw-r--r--src/core/org.freedesktop.systemd1.conf4
-rw-r--r--src/core/service.c19
-rw-r--r--src/core/socket.c10
-rw-r--r--src/core/swap.c10
-rw-r--r--src/core/unit.c18
-rw-r--r--src/core/unit.h9
-rw-r--r--src/shared/bus-unit-util.c446
-rw-r--r--src/shared/bus-unit-util.h27
-rw-r--r--src/shared/cgroup-show.c70
-rw-r--r--src/systemctl/systemctl.c35
18 files changed, 819 insertions, 34 deletions
diff --git a/Makefile.am b/Makefile.am
index f2ef83b0c7..b0118fd9a5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1037,6 +1037,8 @@ libshared_la_SOURCES = \
src/shared/machine-pool.h \
src/shared/resolve-util.c \
src/shared/resolve-util.h \
+ src/shared/bus-unit-util.c \
+ src/shared/bus-unit-util.h \
src/shared/tests.h \
src/shared/tests.c
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index ae3f6109ad..f2cea01979 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -731,6 +731,18 @@ void valgrind_summary_hack(void) {
#endif
}
+int pid_compare_func(const void *a, const void *b) {
+ const pid_t *p = a, *q = b;
+
+ /* Suitable for usage in qsort() */
+
+ if (*p < *q)
+ return -1;
+ if (*p > *q)
+ return 1;
+ return 0;
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index f5d193e762..ffd4bcb0ff 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -101,3 +101,5 @@ int sched_policy_from_string(const char *s);
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
void valgrind_summary_hack(void);
+
+int pid_compare_func(const void *a, const void *b);
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);
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
new file mode 100644
index 0000000000..25ecbd3df3
--- /dev/null
+++ b/src/shared/bus-unit-util.c
@@ -0,0 +1,446 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "bus-internal.h"
+#include "bus-unit-util.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "env-util.h"
+#include "escape.h"
+#include "hashmap.h"
+#include "list.h"
+#include "locale-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "syslog-util.h"
+#include "terminal-util.h"
+#include "utf8.h"
+#include "util.h"
+
+struct CGroupInfo {
+ char *cgroup_path;
+ bool is_const; /* If false, cgroup_path should be free()'d */
+
+ Hashmap *pids; /* PID → process name */
+ bool done;
+
+ struct CGroupInfo *parent;
+ LIST_FIELDS(struct CGroupInfo, siblings);
+ LIST_HEAD(struct CGroupInfo, children);
+ size_t n_children;
+};
+
+static bool IS_ROOT(const char *p) {
+ return isempty(p) || streq(p, "/");
+}
+
+static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
+ struct CGroupInfo *parent = NULL, *cg;
+ int r;
+
+ assert(cgroups);
+ assert(ret);
+
+ if (IS_ROOT(path))
+ path = "/";
+
+ cg = hashmap_get(cgroups, path);
+ if (cg) {
+ *ret = cg;
+ return 0;
+ }
+
+ if (!IS_ROOT(path)) {
+ const char *e, *pp;
+
+ e = strrchr(path, '/');
+ if (!e)
+ return -EINVAL;
+
+ pp = strndupa(path, e - path);
+ if (!pp)
+ return -ENOMEM;
+
+ r = add_cgroup(cgroups, pp, false, &parent);
+ if (r < 0)
+ return r;
+ }
+
+ cg = new0(struct CGroupInfo, 1);
+ if (!cg)
+ return -ENOMEM;
+
+ if (is_const)
+ cg->cgroup_path = (char*) path;
+ else {
+ cg->cgroup_path = strdup(path);
+ if (!cg->cgroup_path) {
+ free(cg);
+ return -ENOMEM;
+ }
+ }
+
+ cg->is_const = is_const;
+ cg->parent = parent;
+
+ r = hashmap_put(cgroups, cg->cgroup_path, cg);
+ if (r < 0) {
+ if (!is_const)
+ free(cg->cgroup_path);
+ free(cg);
+ return r;
+ }
+
+ if (parent) {
+ LIST_PREPEND(siblings, parent->children, cg);
+ parent->n_children++;
+ }
+
+ *ret = cg;
+ return 1;
+}
+
+static int add_process(
+ Hashmap *cgroups,
+ const char *path,
+ pid_t pid,
+ const char *name) {
+
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(cgroups);
+ assert(name);
+ assert(pid > 0);
+
+ r = add_cgroup(cgroups, path, true, &cg);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
+}
+
+static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
+ assert(cgroups);
+ assert(cg);
+
+ while (cg->children)
+ remove_cgroup(cgroups, cg->children);
+
+ hashmap_remove(cgroups, cg->cgroup_path);
+
+ if (!cg->is_const)
+ free(cg->cgroup_path);
+
+ hashmap_free(cg->pids);
+
+ if (cg->parent)
+ LIST_REMOVE(siblings, cg->parent->children, cg);
+
+ free(cg);
+}
+
+static int cgroup_info_compare_func(const void *a, const void *b) {
+ const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
+
+ assert(x);
+ assert(y);
+
+ return strcmp(x->cgroup_path, y->cgroup_path);
+}
+
+static int dump_processes(
+ Hashmap *cgroups,
+ const char *cgroup_path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(prefix);
+
+ if (IS_ROOT(cgroup_path))
+ cgroup_path = "/";
+
+ cg = hashmap_get(cgroups, cgroup_path);
+ if (!cg)
+ return 0;
+
+ if (!hashmap_isempty(cg->pids)) {
+ const char *name;
+ size_t n = 0, i;
+ pid_t *pids;
+ void *pidp;
+ Iterator j;
+ int width;
+
+ /* Order processes by their PID */
+ pids = newa(pid_t, hashmap_size(cg->pids));
+
+ HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
+ pids[n++] = PTR_TO_PID(pidp);
+
+ assert(n == hashmap_size(cg->pids));
+ qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+
+ width = DECIMAL_STR_WIDTH(pids[n-1]);
+
+ for (i = 0; i < n; i++) {
+ _cleanup_free_ char *e = NULL;
+ const char *special;
+ bool more;
+
+ name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
+ assert(name);
+
+ if (n_columns != 0) {
+ unsigned k;
+
+ k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
+
+ e = ellipsize(name, k, 100);
+ if (e)
+ name = e;
+ }
+
+ more = i+1 < n || cg->children;
+ special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
+
+ fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
+ prefix,
+ special,
+ width, pids[i],
+ name);
+ }
+ }
+
+ if (cg->children) {
+ struct CGroupInfo **children, *child;
+ size_t n = 0, i;
+
+ /* Order subcgroups by their name */
+ children = newa(struct CGroupInfo*, cg->n_children);
+ LIST_FOREACH(siblings, child, cg->children)
+ children[n++] = child;
+ assert(n == cg->n_children);
+ qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
+
+ n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
+
+ for (i = 0; i < n; i++) {
+ _cleanup_free_ char *pp = NULL;
+ const char *name, *special;
+ bool more;
+
+ child = children[i];
+
+ name = strrchr(child->cgroup_path, '/');
+ if (!name)
+ return -EINVAL;
+ name++;
+
+ more = i+1 < n;
+ special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
+
+ fputs(prefix, stdout);
+ fputs(special, stdout);
+ fputs(name, stdout);
+ fputc('\n', stdout);
+
+ special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE);
+
+ pp = strappend(prefix, special);
+ if (!pp)
+ return -ENOMEM;
+
+ r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ cg->done = true;
+ return 0;
+}
+
+static int dump_extra_processes(
+ Hashmap *cgroups,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
+ _cleanup_free_ pid_t *pids = NULL;
+ _cleanup_hashmap_free_ Hashmap *names = NULL;
+ struct CGroupInfo *cg;
+ size_t n_allocated = 0, n = 0, k;
+ Iterator i;
+ int width, r;
+
+ /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
+ * combined, sorted, linear list. */
+
+ HASHMAP_FOREACH(cg, cgroups, i) {
+ const char *name;
+ void *pidp;
+ Iterator j;
+
+ if (cg->done)
+ continue;
+
+ if (hashmap_isempty(cg->pids))
+ continue;
+
+ r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
+ return -ENOMEM;
+
+ HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
+ pids[n++] = PTR_TO_PID(pidp);
+
+ r = hashmap_put(names, pidp, (void*) name);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (n == 0)
+ return 0;
+
+ qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+ width = DECIMAL_STR_WIDTH(pids[n-1]);
+
+ for (k = 0; k < n; k++) {
+ _cleanup_free_ char *e = NULL;
+ const char *name;
+
+ name = hashmap_get(names, PID_TO_PTR(pids[k]));
+ assert(name);
+
+ if (n_columns != 0) {
+ unsigned z;
+
+ z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
+
+ e = ellipsize(name, z, 100);
+ if (e)
+ name = e;
+ }
+
+ fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
+ prefix,
+ draw_special_char(DRAW_TRIANGULAR_BULLET),
+ width, pids[k],
+ name);
+ }
+
+ return 0;
+}
+
+int unit_show_processes(
+ sd_bus *bus,
+ const char *unit,
+ const char *cgroup_path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags,
+ sd_bus_error *error) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Hashmap *cgroups = NULL;
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(bus);
+ assert(unit);
+
+ if (flags & OUTPUT_FULL_WIDTH)
+ n_columns = 0;
+ else if (n_columns <= 0)
+ n_columns = columns();
+
+ prefix = strempty(prefix);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitProcesses",
+ error,
+ &reply,
+ "s",
+ unit);
+ if (r < 0)
+ return r;
+
+ cgroups = hashmap_new(&string_hash_ops);
+ if (!cgroups)
+ return -ENOMEM;
+
+ r = sd_bus_message_enter_container(reply, 'a', "(sus)");
+ if (r < 0)
+ goto finish;
+
+ for (;;) {
+ const char *path = NULL, *name = NULL;
+ uint32_t pid;
+
+ r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ break;
+
+ r = add_process(cgroups, path, pid, name);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ goto finish;
+
+ r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
+ if (r < 0)
+ goto finish;
+
+ r = dump_extra_processes(cgroups, prefix, n_columns, flags);
+
+finish:
+ while ((cg = hashmap_first(cgroups)))
+ remove_cgroup(cgroups, cg);
+
+ hashmap_free(cgroups);
+
+ return r;
+}
diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h
new file mode 100644
index 0000000000..55486f82ee
--- /dev/null
+++ b/src/shared/bus-unit-util.h
@@ -0,0 +1,27 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+#include "output-mode.h"
+#include "install.h"
+
+int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index f3039b23f7..65a2c554d5 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -37,23 +37,13 @@
#include "string-util.h"
#include "terminal-util.h"
-static int compare(const void *a, const void *b) {
- const pid_t *p = a, *q = b;
-
- if (*p < *q)
- return -1;
- if (*p > *q)
- return 1;
- return 0;
-}
-
static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
unsigned i, j, pid_width;
if (n_pids == 0)
return;
- qsort(pids, n_pids, sizeof(pid_t), compare);
+ qsort(pids, n_pids, sizeof(pid_t), pid_compare_func);
/* Filter duplicates */
for (j = 0, i = 1; i < n_pids; i++) {
@@ -86,8 +76,14 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un
}
}
+static int show_cgroup_one_by_path(
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ bool more,
+ bool kernel_threads,
+ OutputFlags flags) {
-static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
char *fn;
_cleanup_fclose_ FILE *f = NULL;
size_t n = 0, n_allocated = 0;
@@ -125,7 +121,13 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
return 0;
}
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+int show_cgroup_by_path(
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ bool kernel_threads,
+ OutputFlags flags) {
+
_cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
_cleanup_closedir_ DIR *d = NULL;
char *gn = NULL;
@@ -137,8 +139,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (n_columns <= 0)
n_columns = columns();
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
r = cg_mangle_path(path, &fn);
if (r < 0)
@@ -202,7 +203,13 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return 0;
}
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+int show_cgroup(const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ bool kernel_threads,
+
+ OutputFlags flags) {
_cleanup_free_ char *p = NULL;
int r;
@@ -215,7 +222,15 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un
return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
}
-static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) {
+static int show_extra_pids(
+ const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t pids[],
+ unsigned n_pids,
+ OutputFlags flags) {
+
_cleanup_free_ pid_t *copy = NULL;
unsigned i, j;
int r;
@@ -252,7 +267,16 @@ static int show_extra_pids(const char *controller, const char *path, const char
return 0;
}
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
+int show_cgroup_and_extra(
+ const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ bool kernel_threads,
+ const pid_t extra_pids[],
+ unsigned n_extra_pids,
+ OutputFlags flags) {
+
int r;
assert(path);
@@ -264,7 +288,15 @@ int show_cgroup_and_extra(const char *controller, const char *path, const char *
return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
+int show_cgroup_and_extra_by_spec(
+ const char *spec,
+ const char *prefix,
+ unsigned n_columns,
+ bool kernel_threads,
+ const pid_t extra_pids[],
+ unsigned n_extra_pids,
+ OutputFlags flags) {
+
_cleanup_free_ char *controller = NULL, *path = NULL;
int r;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 04205411dd..c74fc11ca6 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -39,6 +39,7 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-message.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -3434,6 +3435,7 @@ typedef struct UnitStatusInfo {
} UnitStatusInfo;
static void print_status_info(
+ sd_bus *bus,
UnitStatusInfo *i,
bool *ellipsized) {
@@ -3444,6 +3446,7 @@ static void print_status_info(
char since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *path;
char **t, **t2;
+ int r;
assert(i);
@@ -3716,25 +3719,26 @@ static void print_status_info(
printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC));
}
- if (i->control_group &&
- (i->main_pid > 0 || i->control_pid > 0 ||
- (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) {
+ if (i->control_group)
+ printf(" CGroup: %s\n", i->control_group);
+
+ {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ static const char prefix[] = " ";
unsigned c;
- printf(" CGroup: %s\n", i->control_group);
+ c = columns();
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
+ else
+ c = 0;
- if (IN_SET(arg_transport,
- BUS_TRANSPORT_LOCAL,
- BUS_TRANSPORT_MACHINE)) {
+ r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error);
+ if (r == -EBADR) {
unsigned k = 0;
pid_t extra[2];
- static const char prefix[] = " ";
- c = columns();
- if (c > sizeof(prefix) - 1)
- c -= sizeof(prefix) - 1;
- else
- c = 0;
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
if (i->main_pid > 0)
extra[k++] = i->main_pid;
@@ -3743,7 +3747,8 @@ static void print_status_info(
extra[k++] = i->control_pid;
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags());
- }
+ } else if (r < 0)
+ log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r));
}
if (i->id && arg_transport == BUS_TRANSPORT_LOCAL)
@@ -4504,7 +4509,7 @@ static int show_one(
if (streq(verb, "help"))
show_unit_help(&info);
else
- print_status_info(&info, ellipsized);
+ print_status_info(bus, &info, ellipsized);
}
strv_free(info.documentation);