diff --git a/src/report/meson.build b/src/report/meson.build index 2813f9d033b16..55cfe26880f74 100644 --- a/src/report/meson.build +++ b/src/report/meson.build @@ -4,6 +4,9 @@ executables += [ libexec_template + { 'name' : 'systemd-report', 'public' : true, - 'sources' : files('report.c'), + 'sources' : files( + 'report.c', + 'report-facts.c', + ), }, ] diff --git a/src/report/report-facts.c b/src/report/report-facts.c new file mode 100644 index 0000000000000..b19b8cbedfde9 --- /dev/null +++ b/src/report/report-facts.c @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-id128.h" +#include "sd-json.h" + +#include "alloc-util.h" +#include "architecture.h" +#include "hostname-setup.h" +#include "report-facts.h" +#include "virt.h" + +typedef struct LocalFact { + const char *name; + const char *description; + int (*generate)(sd_json_variant **ret); +} LocalFact; + +static int architecture_generate(sd_json_variant **ret) { + return sd_json_variant_new_string(ret, architecture_to_string(uname_architecture())); +} + +static int boot_id_generate(sd_json_variant **ret) { + sd_id128_t id; + int r; + + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + return sd_json_variant_new_string(ret, SD_ID128_TO_STRING(id)); +} + +static int hostname_generate(sd_json_variant **ret) { + _cleanup_free_ char *hostname = NULL; + int r; + + r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &hostname); + if (r < 0) + return r; + + return sd_json_variant_new_string(ret, hostname); +} + +static int kernel_version_generate(sd_json_variant **ret) { + struct utsname u; + + assert_se(uname(&u) >= 0); + + return sd_json_variant_new_string(ret, u.release); +} + +static int machine_id_generate(sd_json_variant **ret) { + sd_id128_t id; + int r; + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + return sd_json_variant_new_string(ret, SD_ID128_TO_STRING(id)); +} + +static int virtualization_generate(sd_json_variant **ret) { + Virtualization v; + + v = detect_virtualization(); + if (v < 0) + return v; + + return sd_json_variant_new_string(ret, virtualization_to_string(v)); +} + +static const LocalFact local_facts[] = { + /* Keep facts ordered alphabetically */ + { FACT_PREFIX "Architecture", "CPU architecture", architecture_generate }, + { FACT_PREFIX "BootID", "Current boot ID", boot_id_generate }, + { FACT_PREFIX "Hostname", "System hostname", hostname_generate }, + { FACT_PREFIX "KernelVersion", "Kernel version", kernel_version_generate }, + { FACT_PREFIX "MachineID", "Machine ID", machine_id_generate }, + { FACT_PREFIX "Virtualization", "Virtualization type", virtualization_generate }, + {} +}; + +int local_facts_list(sd_json_variant ***ret, size_t *ret_n) { + _cleanup_free_ sd_json_variant **facts = NULL; + size_t n = 0; + int r; + + assert(ret); + assert(ret_n); + + for (const LocalFact *f = local_facts; f->name; f++) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *value = NULL; + + r = f->generate(&value); + if (r < 0) + return r; + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL; + r = sd_json_buildo( + &entry, + SD_JSON_BUILD_PAIR_STRING("name", f->name), + SD_JSON_BUILD_PAIR("value", SD_JSON_BUILD_VARIANT(value))); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(facts, n + 1)) + return -ENOMEM; + + facts[n++] = TAKE_PTR(entry); + } + + *ret = TAKE_PTR(facts); + *ret_n = n; + return 0; +} + +int local_facts_describe(sd_json_variant ***ret, size_t *ret_n) { + _cleanup_free_ sd_json_variant **facts = NULL; + size_t n = 0; + int r; + + assert(ret); + assert(ret_n); + + for (const LocalFact *f = local_facts; f->name; f++) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL; + + r = sd_json_buildo( + &entry, + SD_JSON_BUILD_PAIR_STRING("name", f->name), + SD_JSON_BUILD_PAIR_STRING("description", f->description)); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(facts, n + 1)) + return -ENOMEM; + + facts[n++] = TAKE_PTR(entry); + } + + *ret = TAKE_PTR(facts); + *ret_n = n; + return 0; +} diff --git a/src/report/report-facts.h b/src/report/report-facts.h new file mode 100644 index 0000000000000..76b072b252278 --- /dev/null +++ b/src/report/report-facts.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-json.h" + +#define FACT_PREFIX "io.systemd.Facts." + +int local_facts_list(sd_json_variant ***ret, size_t *ret_n); +int local_facts_describe(sd_json_variant ***ret, size_t *ret_n); diff --git a/src/report/report.c b/src/report/report.c index 349d1fb15bd43..793b318a3dd5e 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -17,6 +17,7 @@ #include "path-lookup.h" #include "pretty-print.h" #include "recurse-dir.h" +#include "report-facts.h" #include "runtime-scope.h" #include "set.h" #include "sort-util.h" @@ -41,6 +42,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep); typedef enum Action { ACTION_LIST, ACTION_DESCRIBE, + ACTION_LIST_FACTS, + ACTION_DESCRIBE_FACTS, _ACTION_MAX, _ACTION_INVALID = -EINVAL, } Action; @@ -289,7 +292,12 @@ static int metrics_call(Context *context, const char *name, const char *path) { if (r < 0) return log_error_errno(r, "Failed to bind reply callback: %m"); - const char *method = context->action == ACTION_LIST ? "io.systemd.Metrics.List" : "io.systemd.Metrics.Describe"; + const char *method; + switch (context->action) { + case ACTION_LIST: method = "io.systemd.Metrics.List"; break; + case ACTION_DESCRIBE: method = "io.systemd.Metrics.Describe"; break; + default: assert_not_reached(); + } r = sd_varlink_observe(vl, method, /* parameters= */ NULL); @@ -423,6 +431,105 @@ static int metrics_output_describe(Context *context, Table **ret) { return 0; } +static int facts_output_list(Context *context, Table **ret) { + int r; + + assert(context); + + _cleanup_(table_unrefp) Table *table = table_new("family", "object", "value"); + if (!table) + return log_oom(); + + table_set_ersatz_string(table, TABLE_ERSATZ_DASH); + table_set_sort(table, (size_t) 0, (size_t) 1, (size_t) 2); + + FOREACH_ARRAY(m, context->metrics, context->n_metrics) { + struct { + const char *name; + const char *object; + sd_json_variant *value; + } d = {}; + + static const sd_json_dispatch_field dispatch_table[] = { + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(d, name), SD_JSON_MANDATORY }, + { "object", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(d, object), 0 }, + { "value", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_variant_noref, voffsetof(d, value), SD_JSON_MANDATORY }, + {} + }; + + r = sd_json_dispatch(*m, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &d); + if (r < 0) { + _cleanup_free_ char *t = NULL; + int k = sd_json_variant_format(*m, /* flags= */ 0, &t); + if (k < 0) + return log_error_errno(k, "Failed to format JSON: %m"); + + log_warning_errno(r, "Cannot parse fact, skipping: %s", t); + continue; + } + + r = table_add_many( + table, + TABLE_STRING, d.name, + TABLE_STRING, d.object, + TABLE_JSON, d.value, + TABLE_SET_WEIGHT, 50U); + if (r < 0) + return table_log_add_error(r); + } + + *ret = TAKE_PTR(table); + return 0; +} + +static int facts_output_describe(Context *context, Table **ret) { + int r; + + assert(context); + + _cleanup_(table_unrefp) Table *table = table_new("family", "description"); + if (!table) + return log_oom(); + + table_set_ersatz_string(table, TABLE_ERSATZ_DASH); + table_set_sort(table, (size_t) 0, (size_t) 1); + + FOREACH_ARRAY(m, context->metrics, context->n_metrics) { + struct { + const char *name; + const char *description; + } d = {}; + + static const sd_json_dispatch_field dispatch_table[] = { + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(d, name), SD_JSON_MANDATORY }, + { "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(d, description), 0 }, + {} + }; + + r = sd_json_dispatch(*m, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &d); + if (r < 0) { + _cleanup_free_ char *t = NULL; + int k = sd_json_variant_format(*m, /* flags= */ 0, &t); + if (k < 0) + return log_error_errno(k, "Failed to format JSON: %m"); + + log_warning_errno(r, "Cannot parse fact description, skipping: %s", t); + continue; + } + + r = table_add_many( + table, + TABLE_STRING, d.name, + TABLE_STRING, d.description, + TABLE_SET_WEIGHT, 50U); + if (r < 0) + return table_log_add_error(r); + } + + *ret = TAKE_PTR(table); + return 0; +} + static int metrics_output(Context *context) { int r; @@ -441,8 +548,12 @@ static int metrics_output(Context *context) { return log_error_errno(r, "Failed to write JSON: %m"); } - if (context->n_metrics == 0 && arg_legend) - log_info("No metrics collected."); + if (context->n_metrics == 0 && arg_legend) { + if (IN_SET(context->action, ACTION_LIST_FACTS, ACTION_DESCRIBE_FACTS)) + log_info("No facts collected."); + else + log_info("No metrics collected."); + } return 0; } @@ -458,6 +569,14 @@ static int metrics_output(Context *context) { r = metrics_output_describe(context, &table); break; + case ACTION_LIST_FACTS: + r = facts_output_list(context, &table); + break; + + case ACTION_DESCRIBE_FACTS: + r = facts_output_describe(context, &table); + break; + default: assert_not_reached(); } @@ -471,10 +590,12 @@ static int metrics_output(Context *context) { } if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) { + bool is_facts = IN_SET(context->action, ACTION_LIST_FACTS, ACTION_DESCRIBE_FACTS); + if (table_isempty(table)) - printf("No metrics available.\n"); + printf("No %s available.\n", is_facts ? "facts" : "metrics"); else - printf("\n%zu metrics listed.\n", table_get_rows(table) - 1); + printf("\n%zu %s listed.\n", table_get_rows(table) - 1, is_facts ? "facts" : "metrics"); } return 0; @@ -662,6 +783,95 @@ static int verb_metrics(int argc, char *argv[], void *userdata) { return 0; } +static int add_local_facts(Context *context) { + sd_json_variant **local = NULL; + size_t n_local = 0; + int r; + + assert(context); + + if (context->action == ACTION_LIST_FACTS) + r = local_facts_list(&local, &n_local); + else { + assert(context->action == ACTION_DESCRIBE_FACTS); + r = local_facts_describe(&local, &n_local); + } + if (r < 0) + return log_error_errno(r, "Failed to generate local facts: %m"); + + CLEANUP_ARRAY(local, n_local, sd_json_variant_unref_many); + + for (size_t i = 0; i < n_local; i++) { + const char *name = sd_json_variant_string(sd_json_variant_by_key(local[i], "name")); + + /* Apply match filtering */ + if (!strv_isempty(arg_matches)) { + bool match = false; + + STRV_FOREACH(m, arg_matches) + if (streq(name, *m) || metric_startswith_prefix(name, *m)) { + match = true; + break; + } + + if (!match) + continue; + } + + if (context->n_metrics >= METRICS_MAX) { + context->n_skipped_metrics++; + continue; + } + + if (!GREEDY_REALLOC(context->metrics, context->n_metrics + 1)) + return log_oom(); + + context->metrics[context->n_metrics++] = TAKE_PTR(local[i]); + } + + return 0; +} + +static int verb_facts(int argc, char *argv[], void *userdata) { + Action action; + int r; + + assert(argc >= 1); + assert(argv); + + /* Enable JSON-SEQ mode here, since we'll dump a large series of JSON objects */ + arg_json_format_flags |= SD_JSON_FORMAT_SEQ; + + if (streq_ptr(argv[0], "facts")) + action = ACTION_LIST_FACTS; + else { + assert(streq_ptr(argv[0], "describe-facts")); + action = ACTION_DESCRIBE_FACTS; + } + + r = parse_metrics_matches(argv + 1); + if (r < 0) + return r; + + _cleanup_(context_done) Context context = { + .action = action, + }; + + r = add_local_facts(&context); + if (r < 0) + return r; + + r = metrics_output(&context); + if (r < 0) + return r; + + if (context.n_skipped_metrics > 0) + return log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Too many facts, only %zu facts collected, %zu facts skipped.", + context.n_metrics, context.n_skipped_metrics); + return 0; +} + static int verb_list_sources(int argc, char *argv[], void *userdata) { int r; @@ -726,11 +936,14 @@ static int verb_help(int argc, char *argv[], void *userdata) { return log_oom(); printf("%1$s [OPTIONS...] COMMAND ...\n" - "\n%5$sAcquire metrics from local sources.%6$s\n" + "\n%5$sAcquire metrics and facts from local sources.%6$s\n" "\n%3$sCommands:%4$s\n" " metrics [MATCH...] Acquire list of metrics and their values\n" " describe-metrics [MATCH...]\n" " Describe available metrics\n" + " facts [MATCH...] Acquire list of facts and their values\n" + " describe-facts [MATCH...]\n" + " Describe available facts\n" " list-sources Show list of known metrics sources\n" "\n%3$sOptions:%4$s\n" " -h --help Show this help\n" @@ -831,6 +1044,8 @@ static int report_main(int argc, char *argv[]) { { "help", VERB_ANY, 1, 0, verb_help }, { "metrics", VERB_ANY, VERB_ANY, 0, verb_metrics }, { "describe-metrics", VERB_ANY, VERB_ANY, 0, verb_metrics }, + { "facts", VERB_ANY, VERB_ANY, 0, verb_facts }, + { "describe-facts", VERB_ANY, VERB_ANY, 0, verb_facts }, { "list-sources", VERB_ANY, 1, 0, verb_list_sources }, {} }; diff --git a/src/shared/facts.c b/src/shared/facts.c new file mode 100644 index 0000000000000..37f6da8749556 --- /dev/null +++ b/src/shared/facts.c @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "facts.h" +#include "json-util.h" +#include "log.h" +#include "varlink-io.systemd.Facts.h" +#include "varlink-util.h" + +int facts_add_to_varlink_server( + sd_varlink_server *server, + sd_varlink_method_t vl_method_list_cb, + sd_varlink_method_t vl_method_describe_cb) { + + int r; + + assert(server); + assert(vl_method_list_cb); + assert(vl_method_describe_cb); + + r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_Facts); + if (r < 0) + return log_debug_errno(r, "Failed to add varlink facts interface to varlink server: %m"); + + r = sd_varlink_server_bind_method_many( + server, + "io.systemd.Facts.List", vl_method_list_cb, + "io.systemd.Facts.Describe", vl_method_describe_cb); + if (r < 0) + return log_debug_errno(r, "Failed to register varlink facts methods: %m"); + + return 0; +} + +static int fact_family_build_json(const FactFamily *ff, sd_json_variant **ret) { + assert(ff); + + return sd_json_buildo( + ret, + SD_JSON_BUILD_PAIR_STRING("name", ff->name), + SD_JSON_BUILD_PAIR_STRING("description", ff->description)); +} + +int facts_method_describe( + const FactFamily fact_family_table[], + sd_varlink *link, + sd_json_variant *parameters, + sd_varlink_method_flags_t flags, + void *userdata) { + + int r; + + assert(fact_family_table); + assert(link); + assert(parameters); + assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)); + + r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + if (r != 0) + return r; + + r = varlink_set_sentinel(link, "io.systemd.Facts.NoSuchFact"); + if (r < 0) + return r; + + for (const FactFamily *ff = fact_family_table; ff && ff->name; ff++) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + + r = fact_family_build_json(ff, &v); + if (r < 0) + return log_debug_errno(r, "Failed to describe fact family '%s': %m", ff->name); + + r = sd_varlink_reply(link, v); + if (r < 0) + return log_debug_errno(r, "Failed to send varlink reply: %m"); + } + + return 0; +} + +int facts_method_list( + const FactFamily fact_family_table[], + sd_varlink *link, + sd_json_variant *parameters, + sd_varlink_method_flags_t flags, + void *userdata) { + + int r; + + assert(fact_family_table); + assert(link); + assert(parameters); + assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)); + + r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + if (r != 0) + return r; + + r = varlink_set_sentinel(link, "io.systemd.Facts.NoSuchFact"); + if (r < 0) + return r; + + FactFamilyContext ctx = { .link = link }; + for (const FactFamily *ff = fact_family_table; ff && ff->name; ff++) { + assert(ff->generate); + + ctx.fact_family = ff; + r = ff->generate(&ctx, userdata); + if (r < 0) + return log_debug_errno( + r, "Failed to list facts for fact family '%s': %m", ff->name); + } + + return 0; +} + +static int fact_build_send(FactFamilyContext *context, const char *object, sd_json_variant *value) { + assert(context); + assert(value); + assert(context->link); + assert(context->fact_family); + + return sd_varlink_replybo(context->link, + SD_JSON_BUILD_PAIR_STRING("name", context->fact_family->name), + JSON_BUILD_PAIR_STRING_NON_EMPTY("object", object), + SD_JSON_BUILD_PAIR("value", SD_JSON_BUILD_VARIANT(value))); +} + +int fact_build_send_string(FactFamilyContext *context, const char *object, const char *value) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + int r; + + assert(value); + + r = sd_json_variant_new_string(&v, value); + if (r < 0) + return log_debug_errno(r, "Failed to allocate JSON string: %m"); + + return fact_build_send(context, object, v); +} + +int fact_build_send_unsigned(FactFamilyContext *context, const char *object, uint64_t value) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + int r; + + r = sd_json_variant_new_unsigned(&v, value); + if (r < 0) + return log_debug_errno(r, "Failed to allocate JSON unsigned: %m"); + + return fact_build_send(context, object, v); +} diff --git a/src/shared/facts.h b/src/shared/facts.h new file mode 100644 index 0000000000000..8a8a94cd91f77 --- /dev/null +++ b/src/shared/facts.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "shared-forward.h" + +typedef struct FactFamily FactFamily; + +typedef struct FactFamilyContext { + const FactFamily *fact_family; + sd_varlink *link; +} FactFamilyContext; + +typedef int (*fact_family_generate_func_t)(FactFamilyContext *ffc, void *userdata); + +typedef struct FactFamily { + const char *name; + const char *description; + fact_family_generate_func_t generate; +} FactFamily; + +/* Add io.systemd.Facts interface + methods to an existing varlink server */ +int facts_add_to_varlink_server( + sd_varlink_server *server, + sd_varlink_method_t vl_method_list_cb, + sd_varlink_method_t vl_method_describe_cb); + +int facts_method_describe(const FactFamily fact_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int facts_method_list(const FactFamily fact_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); + +int fact_build_send_string(FactFamilyContext *context, const char *object, const char *value); +int fact_build_send_unsigned(FactFamilyContext *context, const char *object, uint64_t value); diff --git a/src/shared/meson.build b/src/shared/meson.build index bbc0307999324..25e5b5118712e 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -127,6 +127,7 @@ shared_sources = files( 'macvlan-util.c', 'main-func.c', 'metrics.c', + 'facts.c', 'mkdir-label.c', 'mkfs-util.c', 'module-util.c', @@ -212,6 +213,7 @@ shared_sources = files( 'varlink-io.systemd.ManagedOOM.c', 'varlink-io.systemd.Manager.c', 'varlink-io.systemd.Metrics.c', + 'varlink-io.systemd.Facts.c', 'varlink-io.systemd.MountFileSystem.c', 'varlink-io.systemd.MuteConsole.c', 'varlink-io.systemd.NamespaceResource.c', diff --git a/src/shared/varlink-io.systemd.Facts.c b/src/shared/varlink-io.systemd.Facts.c new file mode 100644 index 0000000000000..c7cf10290aa63 --- /dev/null +++ b/src/shared/varlink-io.systemd.Facts.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-varlink-idl.h" + +#include "varlink-io.systemd.Facts.h" + +static SD_VARLINK_DEFINE_ERROR(NoSuchFact); + +static SD_VARLINK_DEFINE_METHOD_FULL( + List, + SD_VARLINK_REQUIRES_MORE, + SD_VARLINK_FIELD_COMMENT("Fact family name, e.g. io.systemd.Manager.Hostname"), + SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Fact object name"), + SD_VARLINK_DEFINE_OUTPUT(object, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Fact value"), + SD_VARLINK_DEFINE_OUTPUT(value, SD_VARLINK_ANY, 0)); + +static SD_VARLINK_DEFINE_METHOD_FULL( + Describe, + SD_VARLINK_REQUIRES_MORE, + SD_VARLINK_FIELD_COMMENT("Fact family name"), + SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Fact family description"), + SD_VARLINK_DEFINE_OUTPUT(description, SD_VARLINK_STRING, 0)); + +SD_VARLINK_DEFINE_INTERFACE( + io_systemd_Facts, + "io.systemd.Facts", + SD_VARLINK_INTERFACE_COMMENT("Facts APIs"), + SD_VARLINK_SYMBOL_COMMENT("Method to get a list of facts and their values"), + &vl_method_List, + SD_VARLINK_SYMBOL_COMMENT("Method to get the fact families"), + &vl_method_Describe, + SD_VARLINK_SYMBOL_COMMENT("No such fact found"), + &vl_error_NoSuchFact); diff --git a/src/shared/varlink-io.systemd.Facts.h b/src/shared/varlink-io.systemd.Facts.h new file mode 100644 index 0000000000000..ce07de32fb9df --- /dev/null +++ b/src/shared/varlink-io.systemd.Facts.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-varlink-idl.h" + +extern const sd_varlink_interface vl_interface_io_systemd_Facts; diff --git a/test/units/TEST-74-AUX-UTILS.report.sh b/test/units/TEST-74-AUX-UTILS.report.sh index 876c5b2cad49e..e39186255d946 100755 --- a/test/units/TEST-74-AUX-UTILS.report.sh +++ b/test/units/TEST-74-AUX-UTILS.report.sh @@ -35,3 +35,21 @@ varlinkctl info /run/systemd/report/io.systemd.Network varlinkctl list-methods /run/systemd/report/io.systemd.Network varlinkctl --more call /run/systemd/report/io.systemd.Network io.systemd.Metrics.List {} varlinkctl --more call /run/systemd/report/io.systemd.Network io.systemd.Metrics.Describe {} + +# Test facts verbs +"$REPORT" facts +"$REPORT" facts -j +"$REPORT" facts --no-legend +"$REPORT" describe-facts +"$REPORT" describe-facts -j +"$REPORT" describe-facts --no-legend + +# Test facts with match filters +"$REPORT" facts io +"$REPORT" facts io.systemd piff +"$REPORT" facts io.systemd.Facts +"$REPORT" facts piff +"$REPORT" describe-facts io +"$REPORT" describe-facts io.systemd piff +"$REPORT" describe-facts io.systemd.Facts +"$REPORT" describe-facts piff