diff options
author | Tom Gundersen <teg@jklm.no> | 2015-08-28 12:01:46 +0200 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2015-08-28 12:01:46 +0200 |
commit | 84d449b55215dfe81305d3ee89606fdc17150ad6 (patch) | |
tree | ebb5250b2cedcb493a435119a6f408a1f72b6162 | |
parent | 9c92a077b667fe3947cc8a9caa197dea8b80b8e0 (diff) | |
parent | e049fa164f0062caab6e53113b3fd7102dc739ce (diff) |
Merge pull request #1063 from poettering/dbus-interface-from-type
cgls/cgtop: a variety of modernizations
-rw-r--r-- | man/systemd-cgtop.xml | 29 | ||||
-rw-r--r-- | src/basic/time-util.c | 20 | ||||
-rw-r--r-- | src/basic/time-util.h | 3 | ||||
-rw-r--r-- | src/basic/unit-name.c | 38 | ||||
-rw-r--r-- | src/basic/unit-name.h | 3 | ||||
-rw-r--r-- | src/cgls/cgls.c | 210 | ||||
-rw-r--r-- | src/cgtop/cgtop.c | 293 | ||||
-rw-r--r-- | src/core/automount.c | 1 | ||||
-rw-r--r-- | src/core/busname.c | 1 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 2 | ||||
-rw-r--r-- | src/core/dbus.c | 34 | ||||
-rw-r--r-- | src/core/device.c | 1 | ||||
-rw-r--r-- | src/core/mount.c | 1 | ||||
-rw-r--r-- | src/core/path.c | 1 | ||||
-rw-r--r-- | src/core/scope.c | 1 | ||||
-rw-r--r-- | src/core/service.c | 1 | ||||
-rw-r--r-- | src/core/slice.c | 1 | ||||
-rw-r--r-- | src/core/snapshot.c | 1 | ||||
-rw-r--r-- | src/core/socket.c | 1 | ||||
-rw-r--r-- | src/core/swap.c | 1 | ||||
-rw-r--r-- | src/core/target.c | 1 | ||||
-rw-r--r-- | src/core/timer.c | 1 | ||||
-rw-r--r-- | src/core/unit.h | 3 | ||||
-rw-r--r-- | src/machine/machinectl.c | 3 |
24 files changed, 375 insertions, 276 deletions
diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml index d4b041a1f9..e4bc22f278 100644 --- a/man/systemd-cgtop.xml +++ b/man/systemd-cgtop.xml @@ -1,4 +1,4 @@ -<?xml version='1.0'?> <!--*-nxml-*--> +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> @@ -64,10 +64,10 @@ regular intervals (by default every 1s), similar in style to <citerefentry project='man-pages'><refentrytitle>top</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para> - <para>If <command>systemd-cgtop</command> is not connected to a tty, no - column headers are printed and the default is to only run one iteration. - The <varname>--iterations</varname> argument, if given, is still honored. - This mode is suitable for scripting.</para> + <para>If <command>systemd-cgtop</command> is not connected to a + tty, no column headers are printed and the default is to only run + one iteration. The <varname>--iterations=</varname> argument, if + given, is honored. This mode is suitable for scripting.</para> <para>Resource usage is only accounted for control groups in the relevant hierarchy, i.e. CPU usage is only accounted for control @@ -104,6 +104,7 @@ <variablelist> <varlistentry> <term><option>-p</option></term> + <term><option>--order=path</option></term> <listitem><para>Order by control group path name.</para></listitem> @@ -111,25 +112,28 @@ <varlistentry> <term><option>-t</option></term> + <term><option>--order=tasks</option></term> - <listitem><para>Order by number of tasks in control group - (i.e. threads and processes).</para></listitem> + <listitem><para>Order by number of processes in control group.</para></listitem> </varlistentry> <varlistentry> <term><option>-c</option></term> + <term><option>--order=cpu</option></term> <listitem><para>Order by CPU load.</para></listitem> </varlistentry> <varlistentry> <term><option>-m</option></term> + <term><option>--order=memory</option></term> <listitem><para>Order by memory usage.</para></listitem> </varlistentry> <varlistentry> <term><option>-i</option></term> + <term><option>--order=io</option></term> <listitem><para>Order by disk I/O load.</para></listitem> </varlistentry> @@ -140,7 +144,7 @@ <listitem><para>Run in "batch" mode: do not accept input and run until the iteration limit set with - <option>--iterations</option> is exhausted or until killed. + <option>--iterations=</option> is exhausted or until killed. This mode could be useful for sending output from <command>systemd-cgtop</command> to other programs or to a file.</para></listitem> @@ -156,6 +160,15 @@ </varlistentry> <varlistentry> + <term><option>--cpu=percentage</option></term> + <term><option>--cpu=time</option></term> + + <listitem><para>Controls whether the CPU usage is shown as + percentage or time. By default the CPU usage is shown as + percentage.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>-n</option></term> <term><option>--iterations=</option></term> diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 3d8d5d7568..afc6a6eb24 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -37,6 +37,14 @@ usec_t now(clockid_t clock_id) { return timespec_load(&ts); } +nsec_t now_nsec(clockid_t clock_id) { + struct timespec ts; + + assert_se(clock_gettime(clock_id, &ts) == 0); + + return timespec_load_nsec(&ts); +} + dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { assert(ts); @@ -130,6 +138,18 @@ usec_t timespec_load(const struct timespec *ts) { (usec_t) ts->tv_nsec / NSEC_PER_USEC; } +nsec_t timespec_load_nsec(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec == (time_t) -1 && + ts->tv_nsec == (long) -1) + return NSEC_INFINITY; + + return + (nsec_t) ts->tv_sec * NSEC_PER_SEC + + (nsec_t) ts->tv_nsec; +} + struct timespec *timespec_store(struct timespec *ts, usec_t u) { assert(ts); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 03a47f310d..de881e8fe1 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -70,6 +70,7 @@ typedef struct dual_timestamp { #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL }) usec_t now(clockid_t clock); +nsec_t now_nsec(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); @@ -87,6 +88,8 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u); usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval *timeval_store(struct timeval *tv, usec_t u); +nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; + char *format_timestamp(char *buf, size_t l, usec_t t); char *format_timestamp_utc(char *buf, size_t l, usec_t t); char *format_timestamp_us(char *buf, size_t l, usec_t t); diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index fa530da456..8742ee757f 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -586,6 +586,42 @@ int unit_name_from_dbus_path(const char *path, char **name) { return 0; } +const char* unit_dbus_interface_from_type(UnitType t) { + + static const char *const table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", + [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", + [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", + [UNIT_TARGET] = "org.freedesktop.systemd1.Target", + [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot", + [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", + [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", + [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", + [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", + [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", + [UNIT_PATH] = "org.freedesktop.systemd1.Path", + [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", + [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", + }; + + if (t < 0) + return NULL; + if (t >= _UNIT_TYPE_MAX) + return NULL; + + return table[t]; +} + +const char *unit_dbus_interface_from_name(const char *name) { + UnitType t; + + t = unit_name_to_type(name); + if (t < 0) + return NULL; + + return unit_dbus_interface_from_type(t); +} + static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { const char *valid_chars; @@ -787,7 +823,7 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_TIMER] = "timer", [UNIT_PATH] = "path", [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope" + [UNIT_SCOPE] = "scope", }; DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index b2043d0870..28b3a555f3 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -152,6 +152,9 @@ int unit_name_to_path(const char *name, char **ret); char *unit_dbus_path_from_name(const char *name); int unit_name_from_dbus_path(const char *path, char **name); +const char* unit_dbus_interface_from_type(UnitType t); +const char *unit_dbus_interface_from_name(const char *name); + typedef enum UnitNameMangle { UNIT_NAME_NOGLOB, UNIT_NAME_GLOB, diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b8d1d2ccaf..fae9398aa5 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -54,7 +54,7 @@ static void help(void) { " -a --all Show all groups, including empty\n" " -l --full Do not ellipsize output\n" " -k Include kernel threads in output\n" - " -M --machine Show container\n" + " -M --machine= Show container\n" , program_invocation_short_name); } @@ -123,146 +123,158 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { - int r = 0, retval = EXIT_FAILURE; - int output_flags; - _cleanup_free_ char *root = NULL; +static int get_cgroup_root(char **ret) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + _cleanup_free_ char *unit = NULL, *path = NULL; + const char *m; + int r; + + if (!arg_machine) { + r = cg_get_root_path(ret); + if (r < 0) + return log_error_errno(r, "Failed to get root control group path: %m"); + + return 0; + } + + m = strjoina("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %m"); + + return 0; +} + +int main(int argc, char *argv[]) { + int r, output_flags; log_parse_environment(); log_open(); r = parse_argv(argc, argv); - if (r < 0) + if (r <= 0) goto finish; - else if (r == 0) { - retval = EXIT_SUCCESS; - goto finish; - } if (!arg_no_pager) { r = pager_open(false); - if (r > 0) { - if (arg_full == -1) - arg_full = true; - } + if (r > 0 && arg_full < 0) + arg_full = true; } output_flags = arg_all * OUTPUT_SHOW_ALL | (arg_full > 0) * OUTPUT_FULL_WIDTH; - r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - if (optind < argc) { + _cleanup_free_ char *root = NULL; int i; + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + for (i = optind; i < argc; i++) { int q; - fprintf(stdout, "%s:\n", argv[i]); - fflush(stdout); - - if (arg_machine) - root = strjoin("machine/", arg_machine, "/", argv[i], NULL); - else - root = strdup(argv[i]); - if (!root) - return log_oom(); - - q = show_cgroup_by_path(root, NULL, 0, - arg_kernel_threads, output_flags); - if (q < 0) - r = q; - } + if (path_startswith(argv[i], "/sys/fs/cgroup")) { - } else { - _cleanup_free_ char *p; + printf("Directory %s:\n", argv[i]); + fflush(stdout); - p = get_current_dir_name(); - if (!p) { - log_error_errno(errno, "Cannot determine current working directory: %m"); - goto finish; - } + q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); + } else { + _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; + const char *controller, *path; - if (path_startswith(p, "/sys/fs/cgroup") && !arg_machine) { - printf("Working Directory %s:\n", p); - r = show_cgroup_by_path(p, NULL, 0, - arg_kernel_threads, output_flags); - } else { - if (arg_machine) { - char *m; - const char *cgroup; - _cleanup_free_ char *unit = NULL; - _cleanup_free_ char *path = NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + r = cg_split_spec(argv[i], &c, &p); if (r < 0) { - log_error_errno(r, "Failed to get machine path: %m"); + log_error_errno(r, "Failed to split argument %s: %m", argv[i]); goto finish; } - path = unit_dbus_path_from_name(unit); - if (!path) { - log_oom(); - goto finish; - } + controller = c ?: SYSTEMD_CGROUP_CONTROLLER; + if (p) { + j = strjoin(root, "/", p, NULL); + if (!j) { + r = log_oom(); + goto finish; + } - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", - "ControlGroup", - &error, - &reply, - "s"); + path_kill_slashes(j); + path = j; + } else + path = root; - if (r < 0) { - log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); - goto finish; - } + printf("Controller %s; control group %s:\n", controller, path); + fflush(stdout); - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) { - bus_log_parse_error(r); - goto finish; - } + q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); + } - root = strdup(cgroup); - if (!root) { - log_oom(); - goto finish; - } + if (q < 0) + r = q; + } + + } else { + bool done = false; - } else - r = cg_get_root_path(&root); - if (r < 0) { - log_error_errno(r, "Failed to get %s path: %m", - arg_machine ? "machine" : "root"); + if (!arg_machine) { + _cleanup_free_ char *cwd = NULL; + + cwd = get_current_dir_name(); + if (!cwd) { + r = log_error_errno(errno, "Cannot determine current working directory: %m"); goto finish; } - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, - arg_kernel_threads, output_flags); + if (path_startswith(cwd, "/sys/fs/cgroup")) { + printf("Working directory %s:\n", cwd); + fflush(stdout); + + r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags); + done = true; + } + } + + if (!done) { + _cleanup_free_ char *root = NULL; + + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + + printf("Control group %s:\n", root); + fflush(stdout); + + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags); } } - if (r < 0) { - log_error_errno(r, "Failed to list cgroup tree %s: %m", root); - retval = EXIT_FAILURE; - } else - retval = EXIT_SUCCESS; + if (r < 0) + log_error_errno(r, "Failed to list cgroup tree: %m"); finish: pager_close(); - return retval; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index f953c9e624..26b104f111 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#define __STDC_FORMAT_MACROS #include <errno.h> #include <string.h> #include <stdlib.h> @@ -48,20 +47,20 @@ typedef struct Group { unsigned n_tasks; unsigned cpu_iteration; - uint64_t cpu_usage; - struct timespec cpu_timestamp; + nsec_t cpu_usage; + nsec_t cpu_timestamp; double cpu_fraction; uint64_t memory; unsigned io_iteration; uint64_t io_input, io_output; - struct timespec io_timestamp; + nsec_t io_timestamp; uint64_t io_input_bps, io_output_bps; } Group; static unsigned arg_depth = 3; -static unsigned arg_iterations = (unsigned)-1; +static unsigned arg_iterations = (unsigned) -1; static bool arg_batch = false; static bool arg_raw = false; static usec_t arg_delay = 1*USEC_PER_SEC; @@ -111,9 +110,6 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { Group *g; int r; - FILE *f = NULL; - pid_t pid; - unsigned n; assert(controller); assert(path); @@ -142,84 +138,81 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap r = hashmap_move_one(a, b, path); if (r < 0) return r; + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } - /* Regardless which controller, let's find the maximum number - * of processes in any of it */ + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid; - r = cg_enumerate_processes(controller, path, &f); - if (r < 0) - return r; + r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; - n = 0; - while (cg_read_pid(f, &pid) > 0) - n++; - fclose(f); + g->n_tasks = 0; + while (cg_read_pid(f, &pid) > 0) + g->n_tasks++; - if (n > 0) { - if (g->n_tasks_valid) - g->n_tasks = MAX(g->n_tasks, n); - else - g->n_tasks = n; + if (g->n_tasks > 0) + g->n_tasks_valid = true; - g->n_tasks_valid = true; - } - - if (streq(controller, "cpuacct")) { + } else if (streq(controller, "cpuacct")) { + _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; - char *p, *v; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); - free(v); if (r < 0) return r; - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); - if (g->cpu_iteration == iteration - 1) { - uint64_t x, y; + if (g->cpu_iteration == iteration - 1 && + (nsec_t) new_usage > g->cpu_usage) { - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); + nsec_t x, y; - y = new_usage - g->cpu_usage; + x = timestamp - g->cpu_timestamp; + if (x < 1) + x = 1; - if (y > 0) { - g->cpu_fraction = (double) y / (double) x; - g->cpu_valid = true; - } + y = (nsec_t) new_usage - g->cpu_usage; + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; } - g->cpu_usage = new_usage; - g->cpu_timestamp = ts; + g->cpu_usage = (nsec_t) new_usage; + g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { - char *p, *v; + _cleanup_free_ char *p = NULL, *v = NULL; r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); - free(v); if (r < 0) return r; @@ -227,19 +220,21 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->memory_valid = true; } else if (streq(controller, "blkio")) { - char *p; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; uint64_t wr = 0, rd = 0; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); - free(p); - - if (!f) + if (!f) { + if (errno == ENOENT) + return 0; return -errno; + } for (;;) { char line[LINE_MAX], *l; @@ -269,20 +264,26 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap *q += k; } - fclose(f); - - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); + x = (uint64_t) (timestamp - g->io_timestamp); + if (x < 1) + x = 1; - yr = rd - g->io_input; - yw = wr - g->io_output; + if (rd > g->io_input) + yr = rd - g->io_input; + else + yr = 0; + + if (wr > g->io_output) + yw = wr - g->io_output; + else + yw = 0; - if (g->io_input > 0 || g->io_output > 0) { + if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; @@ -291,7 +292,7 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->io_input = rd; g->io_output = wr; - g->io_timestamp = ts; + g->io_timestamp = timestamp; g->io_iteration = iteration; } @@ -306,7 +307,7 @@ static int refresh_one( unsigned iteration, unsigned depth) { - DIR *d = NULL; + _cleanup_closedir_ DIR *d = NULL; int r; assert(controller); @@ -321,41 +322,29 @@ static int refresh_one( return r; r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) { - if (r == -ENOENT) - return 0; - + if (r == -ENOENT) + return 0; + if (r < 0) return r; - } for (;;) { - char *fn, *p; + _cleanup_free_ char *fn = NULL, *p = NULL; r = cg_read_subgroup(d, &fn); if (r <= 0) - goto finish; + return r; p = strjoin(path, "/", fn, NULL); - free(fn); - - if (!p) { - r = -ENOMEM; - goto finish; - } + if (!p) + return -ENOMEM; path_kill_slashes(p); r = refresh_one(controller, p, a, b, iteration, depth + 1); - free(p); - if (r < 0) - goto finish; + return r; } -finish: - if (d) - closedir(d); - return r; } @@ -364,35 +353,43 @@ static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) { assert(a); - r = refresh_one("name=systemd", "/", a, b, iteration, 0); + r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; + return r; r = refresh_one("cpuacct", "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; + return r; r = refresh_one("memory", "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; - + return r; r = refresh_one("blkio", "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; + return r; + return 0; } static int group_compare(const void*a, const void *b) { const Group *x = *(Group**)a, *y = *(Group**)b; - if (path_startswith(y->path, x->path)) - return -1; - if (path_startswith(x->path, y->path)) - return 1; + if (arg_order != ORDER_TASKS) { + /* Let's make sure that the parent is always before + * the child. Except when ordering by tasks, since + * that is actually not accumulative for all + * children. */ + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + } + + switch (arg_order) { + + case ORDER_PATH: + break; - if (arg_order == ORDER_CPU) { + case ORDER_CPU: if (arg_cpu_type == CPU_PERCENT) { if (x->cpu_valid && y->cpu_valid) { if (x->cpu_fraction > y->cpu_fraction) @@ -409,10 +406,10 @@ static int group_compare(const void*a, const void *b) { else if (x->cpu_usage < y->cpu_usage) return 1; } - } - if (arg_order == ORDER_TASKS) { + break; + case ORDER_TASKS: if (x->n_tasks_valid && y->n_tasks_valid) { if (x->n_tasks > y->n_tasks) return -1; @@ -422,9 +419,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->n_tasks_valid) return 1; - } - if (arg_order == ORDER_MEMORY) { + break; + + case ORDER_MEMORY: if (x->memory_valid && y->memory_valid) { if (x->memory > y->memory) return -1; @@ -434,9 +432,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->memory_valid) return 1; - } - if (arg_order == ORDER_IO) { + break; + + case ORDER_IO: if (x->io_valid && y->io_valid) { if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) return -1; @@ -448,7 +447,7 @@ static int group_compare(const void*a, const void *b) { return 1; } - return strcmp(x->path, y->path); + return path_compare(x->path, y->path); } #define ON ANSI_HIGHLIGHT_ON @@ -481,9 +480,10 @@ static int display(Hashmap *a) { for (j = 0; j < n; j++) { unsigned cputlen, pathtlen; - format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); + format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); cputlen = strlen(buffer); maxtcpu = MAX(maxtcpu, cputlen); + pathtlen = strlen(array[j]->path); maxtpath = MAX(maxtpath, pathtlen); } @@ -503,7 +503,7 @@ static int display(Hashmap *a) { path_columns = 10; printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n", - arg_order == ORDER_PATH ? ON : "", path_columns, "Path", + arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group", arg_order == ORDER_PATH ? OFF : "", arg_order == ORDER_TASKS ? ON : "", "Tasks", arg_order == ORDER_TASKS ? OFF : "", @@ -519,7 +519,7 @@ static int display(Hashmap *a) { path_columns = maxtpath; for (j = 0; j < n; j++) { - char *p; + _cleanup_free_ char *p = NULL; if (on_tty() && j + 5 > rows) break; @@ -527,8 +527,7 @@ static int display(Hashmap *a) { g = array[j]; p = ellipsize(g->path, path_columns, 33); - printf("%-*s", path_columns, p ? p : g->path); - free(p); + printf("%-*s", path_columns, p ?: g->path); if (g->n_tasks_valid) printf(" %7u", g->n_tasks); @@ -541,7 +540,7 @@ static int display(Hashmap *a) { else fputs(" -", stdout); } else - printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); + printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps)); @@ -557,14 +556,15 @@ static void help(void) { printf("%s [OPTIONS...]\n\n" "Show top control groups by their resource usage.\n\n" " -h --help Show this help\n" - " --version Print version and exit\n" - " -p Order by path\n" - " -t Order by number of tasks\n" - " -c Order by CPU load\n" - " -m Order by memory load\n" - " -i Order by IO load\n" + " --version Show package version\n" + " -p --order=path Order by path\n" + " -t --order=tasks Order by number of tasks\n" + " -c --order=cpu Order by CPU load (default)\n" + " -m --order=memory Order by memory load\n" + " -i --order=io Order by IO load\n" " -r --raw Provide raw (not human-readable) numbers\n" - " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n" + " --cpu=percentage Show CPU usage as percentage (default)\n" + " --cpu=time Show CPU usage as time\n" " -d --delay=DELAY Delay between updates\n" " -n --iterations=N Run for N iterations before exiting\n" " -b --batch Run in batch mode, accepting no input\n" @@ -577,18 +577,20 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_DEPTH, - ARG_CPU_TYPE + ARG_CPU_TYPE, + ARG_ORDER, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "delay", required_argument, NULL, 'd' }, - { "iterations", required_argument, NULL, 'n' }, - { "batch", no_argument, NULL, 'b' }, - { "raw", no_argument, NULL, 'r' }, - { "depth", required_argument, NULL, ARG_DEPTH }, - { "cpu", optional_argument, NULL, ARG_CPU_TYPE}, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "delay", required_argument, NULL, 'd' }, + { "iterations", required_argument, NULL, 'n' }, + { "batch", no_argument, NULL, 'b' }, + { "raw", no_argument, NULL, 'r' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { "cpu", optional_argument, NULL, ARG_CPU_TYPE }, + { "order", required_argument, NULL, ARG_ORDER }, {} }; @@ -613,13 +615,17 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CPU_TYPE: if (optarg) { - if (strcmp(optarg, "time") == 0) + if (streq(optarg, "time")) arg_cpu_type = CPU_TIME; - else if (strcmp(optarg, "percentage") == 0) + else if (streq(optarg, "percentage")) arg_cpu_type = CPU_PERCENT; - else + else { + log_error("Unknown argument to --cpu=: %s", optarg); return -EINVAL; - } + } + } else + arg_cpu_type = CPU_TIME; + break; case ARG_DEPTH: @@ -677,6 +683,23 @@ static int parse_argv(int argc, char *argv[]) { arg_order = ORDER_IO; break; + case ARG_ORDER: + if (streq(optarg, "path")) + arg_order = ORDER_PATH; + else if (streq(optarg, "tasks")) + arg_order = ORDER_TASKS; + else if (streq(optarg, "cpu")) + arg_order = ORDER_CPU; + else if (streq(optarg, "memory")) + arg_order = ORDER_MEMORY; + else if (streq(optarg, "io")) + arg_order = ORDER_IO; + else { + log_error("Invalid argument to --order=: %s", optarg); + return -EINVAL; + } + break; + case '?': return -EINVAL; @@ -715,7 +738,7 @@ int main(int argc, char *argv[]) { signal(SIGWINCH, columns_lines_cache_reset); - if (arg_iterations == (unsigned)-1) + if (arg_iterations == (unsigned) -1) arg_iterations = on_tty() ? 0 : 1; while (!quit) { @@ -753,11 +776,10 @@ int main(int argc, char *argv[]) { fputs("\n", stdout); fflush(stdout); - if (arg_batch) { + if (arg_batch) usleep(last_refresh + arg_delay - t); - } else { - r = read_one_char(stdin, &key, - last_refresh + arg_delay - t, NULL); + else { + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); if (r == -ETIMEDOUT) continue; if (r < 0) { @@ -843,7 +865,10 @@ int main(int argc, char *argv[]) { break; default: - fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + if (key < ' ') + fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); + else + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); fflush(stdout); sleep(1); break; diff --git a/src/core/automount.c b/src/core/automount.c index 4af381b4b6..b8171ddad7 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1075,7 +1075,6 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Automount", .bus_vtable = bus_automount_vtable, .shutdown = automount_shutdown, diff --git a/src/core/busname.c b/src/core/busname.c index 9530a87311..d3c1282239 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -1058,7 +1058,6 @@ const UnitVTable busname_vtable = { .supported = busname_supported, - .bus_interface = "org.freedesktop.systemd1.BusName", .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 0a9effda71..42bb653cc1 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -783,7 +783,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) { r = sd_bus_emit_properties_changed_strv( bus, p, - UNIT_VTABLE(u)->bus_interface, + unit_dbus_interface_from_type(u->type), NULL); if (r < 0) return r; diff --git a/src/core/dbus.c b/src/core/dbus.c index 44bf5cab28..d091aa5419 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -356,7 +356,7 @@ static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; *found = u; @@ -378,7 +378,7 @@ static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *inter if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; if (!unit_get_cgroup_context(u)) @@ -404,7 +404,7 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_cgroup_context(u); @@ -431,7 +431,7 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_exec_context(u); @@ -458,7 +458,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_kill_context(u); @@ -555,30 +555,34 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { return log_error_errno(r, "Failed to add job enumerator: %m"); for (t = 0; t < _UNIT_TYPE_MAX; t++) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); + const char *interface; + + assert_se(interface = unit_dbus_interface_from_type(t)); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); if (r < 0) - return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); if (unit_vtable[t]->cgroup_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); } if (unit_vtable[t]->exec_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); } if (unit_vtable[t]->kill_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); } } diff --git a/src/core/device.c b/src/core/device.c index e7efcf0f0a..3f574b1832 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -849,7 +849,6 @@ const UnitVTable device_vtable = { .active_state = device_active_state, .sub_state_to_string = device_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Device", .bus_vtable = bus_device_vtable, .following = device_following, diff --git a/src/core/mount.c b/src/core/mount.c index c0d1cdfbd4..7e19e66a51 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1871,7 +1871,6 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Mount", .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/path.c b/src/core/path.c index 20995d920c..e9111d0612 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -770,6 +770,5 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Path", .bus_vtable = bus_path_vtable }; diff --git a/src/core/scope.c b/src/core/scope.c index ab1769b46b..bf89936153 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -561,7 +561,6 @@ const UnitVTable scope_vtable = { .notify_cgroup_empty = scope_notify_cgroup_empty_event, - .bus_interface = "org.freedesktop.systemd1.Scope", .bus_vtable = bus_scope_vtable, .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, diff --git a/src/core/service.c b/src/core/service.c index b790ec98be..097e7c710c 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3214,7 +3214,6 @@ const UnitVTable service_vtable = { .bus_name_owner_change = service_bus_name_owner_change, - .bus_interface = "org.freedesktop.systemd1.Service", .bus_vtable = bus_service_vtable, .bus_set_property = bus_service_set_property, .bus_commit_properties = bus_service_commit_properties, diff --git a/src/core/slice.c b/src/core/slice.c index 064eb5d933..7442d23391 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -289,7 +289,6 @@ const UnitVTable slice_vtable = { .active_state = slice_active_state, .sub_state_to_string = slice_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Slice", .bus_vtable = bus_slice_vtable, .bus_set_property = bus_slice_set_property, .bus_commit_properties = bus_slice_commit_properties, diff --git a/src/core/snapshot.c b/src/core/snapshot.c index 9518e21f36..336ff20f84 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -302,6 +302,5 @@ const UnitVTable snapshot_vtable = { .active_state = snapshot_active_state, .sub_state_to_string = snapshot_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Snapshot", .bus_vtable = bus_snapshot_vtable }; diff --git a/src/core/socket.c b/src/core/socket.c index a387057473..08efc3754c 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2709,7 +2709,6 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Socket", .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 0bc3827ff0..349fd6f7b9 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1485,7 +1485,6 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Swap", .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/target.c b/src/core/target.c index b492a7c4c7..f714cb31c2 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -221,7 +221,6 @@ const UnitVTable target_vtable = { .active_state = target_active_state, .sub_state_to_string = target_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Target", .bus_vtable = bus_target_vtable, .status_message_formats = { diff --git a/src/core/timer.c b/src/core/timer.c index 89758c6b19..eb6567bbfa 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -772,7 +772,6 @@ const UnitVTable timer_vtable = { .reset_failed = timer_reset_failed, .time_change = timer_time_change, - .bus_interface = "org.freedesktop.systemd1.Timer", .bus_vtable = bus_timer_vtable, .bus_set_property = bus_timer_set_property, diff --git a/src/core/unit.h b/src/core/unit.h index 9df5a7e6fb..f53b7f6da1 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -404,9 +404,6 @@ struct UnitVTable { * of this type will immediately fail. */ bool (*supported)(void); - /* The interface name */ - const char *bus_interface; - /* The bus vtable */ const sd_bus_vtable *bus_vtable; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 926035d185..8bd0ed756b 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -361,8 +361,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : - endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", + unit_dbus_interface_from_name(unit), "ControlGroup", &error, &reply, |