]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/cgtop/cgtop.c
cgtop: minimize aux variable scope
[thirdparty/systemd.git] / src / cgtop / cgtop.c
index 4786a155dad5a9f28baaed8a2994f82985c2d1aa..c67b328b3882176f1781d8301a453192b9aace54 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
 #include <alloca.h>
+#include <errno.h>
 #include <getopt.h>
 #include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sd-bus.h"
 
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "parse-util.h"
 #include "path-util.h"
-#include "terminal-util.h"
 #include "process-util.h"
+#include "stdio-util.h"
+#include "terminal-util.h"
+#include "unit-name.h"
 #include "util.h"
-#include "hashmap.h"
-#include "cgroup-util.h"
-#include "build.h"
-#include "fileio.h"
 
 typedef struct Group {
         char *path;
@@ -65,13 +71,15 @@ 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;
+static char* arg_machine = NULL;
+static char* arg_root = NULL;
+static bool arg_recursive = true;
 
-enum {
+static enum {
         COUNT_PIDS,
         COUNT_USERSPACE_PROCESSES,
         COUNT_ALL_PROCESSES,
 } arg_count = COUNT_PIDS;
-static bool arg_recursive = true;
 
 static enum {
         ORDER_PATH,
@@ -262,13 +270,15 @@ static int process(
                 if (g->memory > 0)
                         g->memory_valid = true;
 
-        } else if (streq(controller, "blkio") && cg_unified() <= 0) {
+        } else if ((streq(controller, "io") && cg_unified() > 0) ||
+                   (streq(controller, "blkio") && cg_unified() <= 0)) {
                 _cleanup_fclose_ FILE *f = NULL;
                 _cleanup_free_ char *p = NULL;
+                bool unified = cg_unified() > 0;
                 uint64_t wr = 0, rd = 0;
                 nsec_t timestamp;
 
-                r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
+                r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p);
                 if (r < 0)
                         return r;
 
@@ -286,25 +296,38 @@ static int process(
                         if (!fgets(line, sizeof(line), f))
                                 break;
 
+                        /* Trim and skip the device */
                         l = strstrip(line);
                         l += strcspn(l, WHITESPACE);
                         l += strspn(l, WHITESPACE);
 
-                        if (first_word(l, "Read")) {
-                                l += 4;
-                                q = &rd;
-                        } else if (first_word(l, "Write")) {
-                                l += 5;
-                                q = &wr;
-                        } else
-                                continue;
-
-                        l += strspn(l, WHITESPACE);
-                        r = safe_atou64(l, &k);
-                        if (r < 0)
-                                continue;
+                        if (unified) {
+                                while (!isempty(l)) {
+                                        if (sscanf(l, "rbytes=%" SCNu64, &k))
+                                                rd += k;
+                                        else if (sscanf(l, "wbytes=%" SCNu64, &k))
+                                                wr += k;
 
-                        *q += k;
+                                        l += strcspn(l, WHITESPACE);
+                                        l += strspn(l, WHITESPACE);
+                                }
+                        } else {
+                                if (first_word(l, "Read")) {
+                                        l += 4;
+                                        q = &rd;
+                                } else if (first_word(l, "Write")) {
+                                        l += 5;
+                                        q = &wr;
+                                } else
+                                        continue;
+
+                                l += strspn(l, WHITESPACE);
+                                r = safe_atou64(l, &k);
+                                if (r < 0)
+                                        continue;
+
+                                *q += k;
+                        }
                 }
 
                 timestamp = now_nsec(CLOCK_MONOTONIC);
@@ -355,7 +378,7 @@ static int refresh_one(
                 Group **ret) {
 
         _cleanup_closedir_ DIR *d = NULL;
-        Group *ours;
+        Group *ours = NULL;
         int r;
 
         assert(controller);
@@ -430,6 +453,9 @@ static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration)
         if (r < 0)
                 return r;
         r = refresh_one("memory", root, a, b, iteration, 0, NULL);
+        if (r < 0)
+                return r;
+        r = refresh_one("io", root, a, b, iteration, 0, NULL);
         if (r < 0)
                 return r;
         r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
@@ -523,9 +549,6 @@ static int group_compare(const void*a, const void *b) {
         return path_compare(x->path, y->path);
 }
 
-#define ON ANSI_HIGHLIGHT_ON
-#define OFF ANSI_HIGHLIGHT_OFF
-
 static void display(Hashmap *a) {
         Iterator i;
         Group *g;
@@ -536,10 +559,8 @@ static void display(Hashmap *a) {
 
         assert(a);
 
-        /* Set cursor to top left corner and clear screen */
-        if (on_tty())
-                fputs("\033[H"
-                      "\033[2J", stdout);
+        if (!terminal_is_dumb())
+                fputs(ANSI_HOME_CLEAR, stdout);
 
         array = alloca(sizeof(Group*) * hashmap_size(a));
 
@@ -562,32 +583,39 @@ static void display(Hashmap *a) {
         }
 
         if (arg_cpu_type == CPU_PERCENT)
-                snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
+                xsprintf(buffer, "%6s", "%CPU");
         else
-                snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
+                xsprintf(buffer, "%*s", maxtcpu, "CPU Time");
 
         rows = lines();
         if (rows <= 10)
                 rows = 10;
 
         if (on_tty()) {
+                const char *on, *off;
+
                 path_columns = columns() - 36 - strlen(buffer);
                 if (path_columns < 10)
                         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, "Control Group",
-                       arg_order == ORDER_PATH ? OFF : "",
-                       arg_order == ORDER_TASKS ? ON : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+",
-                       arg_order == ORDER_TASKS ? OFF : "",
-                       arg_order == ORDER_CPU ? ON : "", buffer,
-                       arg_order == ORDER_CPU ? OFF : "",
-                       arg_order == ORDER_MEMORY ? ON : "", "Memory",
-                       arg_order == ORDER_MEMORY ? OFF : "",
-                       arg_order == ORDER_IO ? ON : "", "Input/s",
-                       arg_order == ORDER_IO ? OFF : "",
-                       arg_order == ORDER_IO ? ON : "", "Output/s",
-                       arg_order == ORDER_IO ? OFF : "");
+                on = ansi_highlight_underline();
+                off = ansi_underline();
+
+                printf("%s%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s%s\n",
+                       ansi_underline(),
+                       arg_order == ORDER_PATH ? on : "", path_columns, "Control Group",
+                       arg_order == ORDER_PATH ? off : "",
+                       arg_order == ORDER_TASKS ? on : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+",
+                       arg_order == ORDER_TASKS ? off : "",
+                       arg_order == ORDER_CPU ? on : "", buffer,
+                       arg_order == ORDER_CPU ? off : "",
+                       arg_order == ORDER_MEMORY ? on : "", "Memory",
+                       arg_order == ORDER_MEMORY ? off : "",
+                       arg_order == ORDER_IO ? on : "", "Input/s",
+                       arg_order == ORDER_IO ? off : "",
+                       arg_order == ORDER_IO ? on : "", "Output/s",
+                       arg_order == ORDER_IO ? off : "",
+                       ansi_normal());
         } else
                 path_columns = maxtpath;
 
@@ -595,7 +623,7 @@ static void display(Hashmap *a) {
                 _cleanup_free_ char *ellipsized = NULL;
                 const char *path;
 
-                if (on_tty() && j + 5 > rows)
+                if (on_tty() && j + 6 > rows)
                         break;
 
                 g = array[j];
@@ -626,7 +654,7 @@ static void display(Hashmap *a) {
 }
 
 static void help(void) {
-        printf("%s [OPTIONS...]\n\n"
+        printf("%s [OPTIONS...] [CGROUP]\n\n"
                "Show top control groups by their resource usage.\n\n"
                "  -h --help           Show this help\n"
                "     --version        Show package version\n"
@@ -645,6 +673,7 @@ static void help(void) {
                "  -n --iterations=N   Run for N iterations before exiting\n"
                "  -b --batch          Run in batch mode, accepting no input\n"
                "     --depth=DEPTH    Maximum traversal depth (default: %u)\n"
+               "  -M --machine=       Show container\n"
                , program_invocation_short_name, arg_depth);
 }
 
@@ -669,6 +698,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "cpu",          optional_argument, NULL, ARG_CPU_TYPE  },
                 { "order",        required_argument, NULL, ARG_ORDER     },
                 { "recursive",    required_argument, NULL, ARG_RECURSIVE },
+                { "machine",      required_argument, NULL, 'M'           },
                 {}
         };
 
@@ -678,7 +708,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 1);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hptcmin:brd:kP", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -687,9 +717,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return 0;
 
                 case ARG_VERSION:
-                        puts(PACKAGE_STRING);
-                        puts(SYSTEMD_FEATURES);
-                        return 0;
+                        return version();
 
                 case ARG_CPU_TYPE:
                         if (optarg) {
@@ -797,6 +825,10 @@ static int parse_argv(int argc, char *argv[]) {
                         recursive_unset = r == 0;
                         break;
 
+                case 'M':
+                        arg_machine = optarg;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -804,7 +836,13 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached("Unhandled option");
                 }
 
-        if (optind < argc) {
+        if (optind == argc-1) {
+                if (arg_machine) {
+                        log_error("Specifying a control group path together with the -M option is not allowed");
+                        return -EINVAL;
+                }
+                arg_root = argv[optind];
+        } else if (optind < argc) {
                 log_error("Too many arguments.");
                 return -EINVAL;
         }
@@ -826,6 +864,59 @@ static const char* counting_what(void) {
                 return "userspace processes (excl. kernel)";
 }
 
+static int get_cgroup_root(char **ret) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_free_ char *unit = NULL, *path = NULL;
+        const char *m;
+        int r;
+
+        if (arg_root) {
+                char *aux;
+
+                aux = strdup(arg_root);
+                if (!aux)
+                        return log_oom();
+
+                *ret = aux;
+                return 0;
+        }
+
+        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_connect_transport_systemd(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: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
 int main(int argc, char *argv[]) {
         int r;
         Hashmap *a = NULL, *b = NULL;
@@ -850,7 +941,7 @@ int main(int argc, char *argv[]) {
         if (r <= 0)
                 goto finish;
 
-        r = cg_get_root_path(&root);
+        r = get_cgroup_root(&root);
         if (r < 0) {
                 log_error_errno(r, "Failed to get root control group path: %m");
                 goto finish;
@@ -1008,6 +1099,10 @@ int main(int argc, char *argv[]) {
 
                 case '?':
                 case 'h':
+
+#define ON ANSI_HIGHLIGHT
+#define OFF ANSI_NORMAL
+
                         fprintf(stdout,
                                 "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n"
                                 "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n"