chiark / gitweb /
systemctl: move "dump" command from systemctl to systemd-analyze
[elogind.git] / src / analyze / systemd-analyze.c
index ffdcd1470018a2e94c76d1cc463288ff4c2231d1..2748b3afc96656cc2304e1d25dfafed345f8d4aa 100644 (file)
@@ -38,6 +38,7 @@
 #include "unit-name.h"
 #include "special.h"
 #include "hashmap.h"
+#include "pager.h"
 
 #define SCALE_X (0.1 / 1000.0)   /* pixels per us */
 #define SCALE_Y 20.0
@@ -67,8 +68,8 @@ static enum dot {
 } arg_dot = DEP_ALL;
 static char** arg_dot_from_patterns = NULL;
 static char** arg_dot_to_patterns = NULL;
-
-usec_t arg_fuzz = 0;
+static usec_t arg_fuzz = 0;
+static bool arg_no_pager = false;
 
 struct boot_times {
         usec_t firmware_time;
@@ -83,6 +84,7 @@ struct boot_times {
         usec_t unitsload_start_time;
         usec_t unitsload_finish_time;
 };
+
 struct unit_times {
         char *name;
         usec_t ixt;
@@ -92,6 +94,14 @@ struct unit_times {
         usec_t time;
 };
 
+static void pager_open_if_enabled(void) {
+
+        if (arg_no_pager)
+                return;
+
+        pager_open(false);
+}
+
 static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         DBusMessageIter iter, sub;
@@ -590,7 +600,6 @@ static int analyze_plot(DBusConnection *bus) {
         svg_text("right", 400000, y, "Loading unit files");
         y++;
 
-
         svg("</g>\n\n");
 
         svg("</svg>");
@@ -600,7 +609,6 @@ static int analyze_plot(DBusConnection *bus) {
         return 0;
 }
 
-
 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
                                    bool last, struct unit_times *times, struct boot_times *boot) {
         unsigned int i;
@@ -914,6 +922,8 @@ static int analyze_critical_chain(DBusConnection *bus, char *names[]) {
         }
         unit_times_hashmap = h;
 
+        pager_open_if_enabled();
+
         puts("The time after the unit is active or started is printed after the \"@\" character.\n"
              "The time the unit takes to start is printed after the \"+\" character.\n");
 
@@ -921,9 +931,8 @@ static int analyze_critical_chain(DBusConnection *bus, char *names[]) {
                 char **name;
                 STRV_FOREACH(name, names)
                         list_dependencies(bus, *name);
-        } else {
+        } else
                 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
-        }
 
         hashmap_free(h);
         free_unit_times(times, (unsigned) n);
@@ -941,6 +950,8 @@ static int analyze_blame(DBusConnection *bus) {
 
         qsort(times, n, sizeof(struct unit_times), compare_unit_time);
 
+        pager_open_if_enabled();
+
         for (i = 0; i < (unsigned) n; i++) {
                 char ts[FORMAT_TIMESPAN_MAX];
 
@@ -1165,8 +1176,44 @@ static int dot(DBusConnection *bus, char* patterns[]) {
         return 0;
 }
 
-static void analyze_help(void)
-{
+static int dump(DBusConnection *bus, char **args) {
+        _cleanup_free_ DBusMessage *reply = NULL;
+        DBusError error;
+        int r;
+        const char *text;
+
+        dbus_error_init(&error);
+
+        pager_open_if_enabled();
+
+        r = bus_method_call_with_reply(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "Dump",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_INVALID);
+        if (r < 0)
+                return r;
+
+        if (!dbus_message_get_args(reply, &error,
+                                   DBUS_TYPE_STRING, &text,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                dbus_error_free(&error);
+                return  -EIO;
+        }
+
+        fputs(text, stdout);
+        return 0;
+}
+
+static void analyze_help(void) {
+
+        pager_open_if_enabled();
+
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
                "Process systemd profiling information\n\n"
                "  -h --help           Show this help\n"
@@ -1181,13 +1228,15 @@ static void analyze_help(void)
                "     --fuzz=TIMESPAN  When printing the tree of the critical chain, print also\n"
                "                      services, which finished TIMESPAN earlier, than the\n"
                "                      latest in the branch. The unit of TIMESPAN is seconds\n"
-               "                      unless specified with a different unit, i.e. 50ms\n\n"
+               "                      unless specified with a different unit, i.e. 50ms\n"
+               "     --no-pager       Do not pipe output into a pager\n\n"
                "Commands:\n"
                "  time                Print time spent in the kernel before reaching userspace\n"
                "  blame               Print list of running units ordered by time to init\n"
                "  critical-chain      Print a tree of the time critical chain of units\n"
                "  plot                Output SVG graphic showing service initialization\n"
-               "  dot                 Dump dependency graph (in dot(1) format)\n\n",
+               "  dot                 Output dependency graph in dot(1) format\n"
+               "  dump                Output state serialization of service manager\n",
                program_invocation_short_name);
 
         /* When updating this list, including descriptions, apply
@@ -1195,8 +1244,7 @@ static void analyze_help(void)
          * shell-completion/systemd-zsh-completion.zsh too. */
 }
 
-static int parse_argv(int argc, char *argv[])
-{
+static int parse_argv(int argc, char *argv[]) {
         int r;
 
         enum {
@@ -1207,20 +1255,22 @@ static int parse_argv(int argc, char *argv[])
                 ARG_SYSTEM,
                 ARG_DOT_FROM_PATTERN,
                 ARG_DOT_TO_PATTERN,
-                ARG_FUZZ
+                ARG_FUZZ,
+                ARG_NO_PAGER
         };
 
         static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "order",     no_argument,       NULL, ARG_ORDER     },
-                { "require",   no_argument,       NULL, ARG_REQUIRE   },
-                { "user",      no_argument,       NULL, ARG_USER      },
-                { "system",    no_argument,       NULL, ARG_SYSTEM    },
-                { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN},
-                { "to-pattern",   required_argument, NULL, ARG_DOT_TO_PATTERN  },
-                { "fuzz",      required_argument, NULL, ARG_FUZZ  },
-                { NULL,        0,                 NULL, 0             }
+                { "help",         no_argument,       NULL, 'h'                  },
+                { "version",      no_argument,       NULL, ARG_VERSION          },
+                { "order",        no_argument,       NULL, ARG_ORDER            },
+                { "require",      no_argument,       NULL, ARG_REQUIRE          },
+                { "user",         no_argument,       NULL, ARG_USER             },
+                { "system",       no_argument,       NULL, ARG_SYSTEM           },
+                { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
+                { "to-pattern",   required_argument, NULL, ARG_DOT_TO_PATTERN   },
+                { "fuzz",         required_argument, NULL, ARG_FUZZ             },
+                { "no-pager",     no_argument,       NULL, ARG_NO_PAGER         },
+                { NULL,           0,                 NULL, 0                    }
         };
 
         assert(argc >= 0);
@@ -1271,6 +1321,10 @@ static int parse_argv(int argc, char *argv[])
                                 return r;
                         break;
 
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
+                        break;
+
                 case -1:
                         return 1;
 
@@ -1293,14 +1347,14 @@ int main(int argc, char *argv[]) {
         log_open();
 
         r = parse_argv(argc, argv);
-        if (r < 0)
-                return EXIT_FAILURE;
-        else if (r <= 0)
-                return EXIT_SUCCESS;
+        if (r <= 0)
+                goto finish;
 
         bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
-        if (!bus)
-                return EXIT_FAILURE;
+        if (!bus) {
+                r = -EIO;
+                goto finish;
+        }
 
         if (!argv[optind] || streq(argv[optind], "time"))
                 r = analyze_time(bus);
@@ -1312,12 +1366,18 @@ int main(int argc, char *argv[]) {
                 r = analyze_plot(bus);
         else if (streq(argv[optind], "dot"))
                 r = dot(bus, argv+optind+1);
+        else if (streq(argv[optind], "dump"))
+                r = dump(bus, argv+optind+1);
         else
                 log_error("Unknown operation '%s'.", argv[optind]);
 
+        dbus_connection_unref(bus);
+
+finish:
+        pager_close();
+
         strv_free(arg_dot_from_patterns);
         strv_free(arg_dot_to_patterns);
-        dbus_connection_unref(bus);
 
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }