From a6bcef29579409872735a2cfbf77d1c61ea91332 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Dec 2017 15:58:06 +0100 Subject: [PATCH] analyze: port verb dispatching to verbs.[ch] API Let's unify the code for parsing command line verbs, and reuse the common verbs.[ch] API in systemd-analyze too. This adds a couple of error messages when people pass too many arguments. Moreover thus pushes bus allocation into the verb functions, which corrects a couple of cases where we previously allocated a bus but really didn't need to. Other than that behaviour shouldn't really change. --- src/analyze/analyze.c | 213 +++++++++++++++++++++--------------------- 1 file changed, 108 insertions(+), 105 deletions(-) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 7f35b04c31..9efef5d7ec 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -47,6 +47,7 @@ #include "terminal-util.h" #include "unit-name.h" #include "util.h" +#include "verbs.h" #define SCALE_X (0.1 / 1000.0) /* pixels per us */ #define SCALE_Y (20.0) @@ -582,15 +583,20 @@ static void svg_graph_box(double height, double begin, double end) { } } -static int analyze_plot(sd_bus *bus) { +static int analyze_plot(int argc, char *argv[], void *userdata) { _cleanup_(free_host_infop) struct host_info *host = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct unit_times *times; struct boot_times *boot; - int n, m = 1, y=0; + int n, m = 1, y = 0, r; double width; _cleanup_free_ char *pretty_times = NULL; struct unit_times *u; + r = acquire_bus(true, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + n = acquire_boot_times(bus, &boot); if (n < 0) return n; @@ -984,12 +990,17 @@ static int list_dependencies(sd_bus *bus, const char *name) { return list_dependencies_one(bus, name, 0, &units, 0); } -static int analyze_critical_chain(sd_bus *bus, char *names[]) { +static int analyze_critical_chain(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct unit_times *times; unsigned int i; Hashmap *h; int n, r; + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + n = acquire_time_data(bus, ×); if (n <= 0) return n; @@ -1010,22 +1021,27 @@ static int analyze_critical_chain(sd_bus *bus, char *names[]) { 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"); - if (!strv_isempty(names)) { + if (argc > 1) { char **name; - STRV_FOREACH(name, names) + STRV_FOREACH(name, strv_skip(argv, 1)) list_dependencies(bus, *name); } else list_dependencies(bus, SPECIAL_DEFAULT_TARGET); - hashmap_free(h); + h = hashmap_free(h); free_unit_times(times, (unsigned) n); return 0; } -static int analyze_blame(sd_bus *bus) { +static int analyze_blame(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct unit_times *times; unsigned i; - int n; + int n, r; + + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); n = acquire_time_data(bus, ×); if (n <= 0) @@ -1046,10 +1062,15 @@ static int analyze_blame(sd_bus *bus) { return 0; } -static int analyze_time(sd_bus *bus) { +static int analyze_time(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *buf = NULL; int r; + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + r = pretty_boot_time(bus, &buf); if (r < 0) return r; @@ -1170,16 +1191,21 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { return 0; } -static int dot(sd_bus *bus, char* patterns[]) { +static int dot(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_strv_free_ char **expanded_patterns = NULL; _cleanup_strv_free_ char **expanded_from_patterns = NULL; _cleanup_strv_free_ char **expanded_to_patterns = NULL; int r; UnitInfo u; - r = expand_patterns(bus, patterns, &expanded_patterns); + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); if (r < 0) return r; @@ -1235,16 +1261,16 @@ static int dot(sd_bus *bus, char* patterns[]) { return 0; } -static int dump(sd_bus *bus, char **args) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +static int dump(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; const char *text = NULL; int r; - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); pager_open(arg_no_pager, false); @@ -1268,17 +1294,17 @@ static int dump(sd_bus *bus, char **args) { return 0; } -static int set_log_level(sd_bus *bus, char **args) { +static int set_log_level(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; - assert(bus); - assert(args); + assert(argc == 2); + assert(argv); - if (strv_length(args) != 1) { - log_error("This command expects one argument only."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_set_property( bus, @@ -1288,25 +1314,22 @@ static int set_log_level(sd_bus *bus, char **args) { "LogLevel", &error, "s", - args[0]); + argv[1]); if (r < 0) return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); return 0; } -static int get_log_level(sd_bus *bus, char **args) { +static int get_log_level(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *level = NULL; + int r; - assert(bus); - assert(args); - - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_get_property_string( bus, @@ -1323,17 +1346,17 @@ static int get_log_level(sd_bus *bus, char **args) { return 0; } -static int set_log_target(sd_bus *bus, char **args) { +static int set_log_target(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; - assert(bus); - assert(args); + assert(argc == 2); + assert(argv); - if (strv_length(args) != 1) { - log_error("This command expects one argument only."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_set_property( bus, @@ -1343,25 +1366,22 @@ static int set_log_target(sd_bus *bus, char **args) { "LogTarget", &error, "s", - args[0]); + argv[1]); if (r < 0) return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); return 0; } -static int get_log_target(sd_bus *bus, char **args) { +static int get_log_target(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *target = NULL; + int r; - assert(bus); - assert(args); - - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_get_property_string( bus, @@ -1388,12 +1408,12 @@ static void dump_syscall_filter(const SyscallFilterSet *set) { printf(" %s\n", syscall); } -static int dump_syscall_filters(char** names) { +static int dump_syscall_filters(int argc, char *argv[], void *userdata) { bool first = true; pager_open(arg_no_pager, false); - if (strv_isempty(names)) { + if (strv_isempty(strv_skip(argv, 1))) { int i; for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { @@ -1405,7 +1425,7 @@ static int dump_syscall_filters(char** names) { } else { char **name; - STRV_FOREACH(name, names) { + STRV_FOREACH(name, strv_skip(argv, 1)) { const SyscallFilterSet *set; if (!first) @@ -1435,19 +1455,14 @@ static int dump_syscall_filters(char** names) { } #endif -static int test_calendar(char **args) { +static int test_calendar(int argc, char *argv[], void *userdata) { int ret = 0, r; char **p; usec_t n; - if (strv_isempty(args)) { - log_error("Expected at least one calendar specification string as argument."); - return -EINVAL; - } - n = now(CLOCK_REALTIME); - STRV_FOREACH(p, args) { + STRV_FOREACH(p, strv_skip(argv, 1)) { _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; _cleanup_free_ char *t = NULL; usec_t next; @@ -1499,7 +1514,14 @@ static int test_calendar(char **args) { return ret; } -static void help(void) { +static int do_verify(int argc, char *argv[], void *userdata) { + return verify_units(strv_skip(argv, 1), + arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, + arg_man, + arg_generators); +} + +static int help(int argc, char *argv[], void *userdata) { pager_open(arg_no_pager, false); @@ -1539,6 +1561,8 @@ static void help(void) { /* When updating this list, including descriptions, apply * changes to shell-completion/bash/systemd-analyze and * shell-completion/zsh/_systemd-analyze too. */ + + return 0; } static int parse_argv(int argc, char *argv[]) { @@ -1583,8 +1607,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { case 'h': - help(); - return 0; + return help(0, NULL, NULL); case ARG_VERSION: return version(); @@ -1676,10 +1699,30 @@ static int parse_argv(int argc, char *argv[]) { } int main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time }, + { "blame", VERB_ANY, 1, 0, analyze_blame }, + { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain }, + { "plot", VERB_ANY, 1, 0, analyze_plot }, + { "dot", VERB_ANY, VERB_ANY, 0, dot }, + { "set-log-level", 2, 2, 0, set_log_level }, + { "get-log-level", VERB_ANY, 1, 0, get_log_level }, + { "set-log-target", 2, 2, 0, set_log_target }, + { "get-log-target", VERB_ANY, 1, 0, get_log_target }, + { "dump", VERB_ANY, 1, 0, dump }, + { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, + { "verify", 2, VERB_ANY, 0, do_verify }, + { "calendar", 2, VERB_ANY, 0, test_calendar }, + {} + }; + int r; setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */ + log_parse_environment(); log_open(); @@ -1687,47 +1730,7 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - if (streq_ptr(argv[optind], "verify")) - r = verify_units(argv+optind+1, - arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, - arg_man, - arg_generators); - else { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - - r = acquire_bus(streq_ptr(argv[optind], "plot"), &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - - if (!argv[optind] || streq(argv[optind], "time")) - r = analyze_time(bus); - else if (streq(argv[optind], "blame")) - r = analyze_blame(bus); - else if (streq(argv[optind], "critical-chain")) - r = analyze_critical_chain(bus, argv+optind+1); - else if (streq(argv[optind], "plot")) - 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 if (streq(argv[optind], "set-log-level")) - r = set_log_level(bus, argv+optind+1); - else if (streq(argv[optind], "get-log-level")) - r = get_log_level(bus, argv+optind+1); - else if (streq(argv[optind], "set-log-target")) - r = set_log_target(bus, argv+optind+1); - else if (streq(argv[optind], "get-log-target")) - r = get_log_target(bus, argv+optind+1); - else if (streq(argv[optind], "syscall-filter")) - r = dump_syscall_filters(argv+optind+1); - else if (streq(argv[optind], "calendar")) - r = test_calendar(argv+optind+1); - else - log_error("Unknown operation '%s'.", argv[optind]); - } + r = dispatch_verb(argc, argv, verbs, NULL); finish: pager_close();