report-basic: expose os-release fields as a metric (#41988)

Add io.systemd.Basic.OSRelease metric family that reports all the fields
in os-release.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-05-13 08:07:27 +02:00
committed by GitHub
9 changed files with 537 additions and 578 deletions
+75 -43
View File
@@ -11,13 +11,14 @@
#include "unit.h"
#include "varlink-metrics.h"
static int active_timestamp_build_json(MetricFamilyContext *context, void *userdata) {
static int active_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *enter_fields = NULL;
r = sd_json_buildo(&enter_fields, SD_JSON_BUILD_PAIR_STRING("event", "enter"));
@@ -35,7 +36,8 @@ static int active_timestamp_build_json(MetricFamilyContext *context, void *userd
continue;
r = metric_build_send_unsigned(
context,
mf,
vl,
unit->id,
unit->active_enter_timestamp.realtime,
enter_fields);
@@ -43,7 +45,8 @@ static int active_timestamp_build_json(MetricFamilyContext *context, void *userd
return r;
r = metric_build_send_unsigned(
context,
mf,
vl,
unit->id,
unit->active_exit_timestamp.realtime,
exit_fields);
@@ -54,13 +57,14 @@ static int active_timestamp_build_json(MetricFamilyContext *context, void *userd
return 0;
}
static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void *userdata) {
static int inactive_exit_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
/* ignore aliases */
@@ -68,7 +72,8 @@ static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void
continue;
r = metric_build_send_unsigned(
context,
mf,
vl,
unit->id,
unit->inactive_exit_timestamp.realtime,
/* fields= */ NULL);
@@ -79,13 +84,14 @@ static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void
return 0;
}
static int state_change_timestamp_build_json(MetricFamilyContext *context, void *userdata) {
static int state_change_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
/* ignore aliases */
@@ -93,7 +99,8 @@ static int state_change_timestamp_build_json(MetricFamilyContext *context, void
continue;
r = metric_build_send_unsigned(
context,
mf,
vl,
unit->id,
unit->state_change_timestamp.realtime,
/* fields= */ NULL);
@@ -104,15 +111,17 @@ static int state_change_timestamp_build_json(MetricFamilyContext *context, void
return 0;
}
static int status_errno_build_json(MetricFamilyContext *context, void *userdata) {
static int status_errno_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
LIST_FOREACH(units_by_type, unit, manager->units_by_type[UNIT_SERVICE]) {
r = metric_build_send_unsigned(
context,
mf,
vl,
unit->id,
(uint64_t) SERVICE(unit)->status_errno,
/* fields= */ NULL);
@@ -123,13 +132,14 @@ static int status_errno_build_json(MetricFamilyContext *context, void *userdata)
return 0;
}
static int unit_active_state_build_json(MetricFamilyContext *context, void *userdata) {
static int unit_active_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
/* ignore aliases */
@@ -137,7 +147,8 @@ static int unit_active_state_build_json(MetricFamilyContext *context, void *user
continue;
r = metric_build_send_string(
context,
mf,
vl,
unit->id,
unit_active_state_to_string(unit_active_state(unit)),
/* fields= */ NULL);
@@ -148,13 +159,14 @@ static int unit_active_state_build_json(MetricFamilyContext *context, void *user
return 0;
}
static int unit_load_state_build_json(MetricFamilyContext *context, void *userdata) {
static int unit_load_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
/* ignore aliases */
@@ -162,7 +174,8 @@ static int unit_load_state_build_json(MetricFamilyContext *context, void *userda
continue;
r = metric_build_send_string(
context,
mf,
vl,
unit->id,
unit_load_state_to_string(unit->load_state),
/* fields= */ NULL);
@@ -173,15 +186,20 @@ static int unit_load_state_build_json(MetricFamilyContext *context, void *userda
return 0;
}
static int nrestarts_build_json(MetricFamilyContext *context, void *userdata) {
static int nrestarts_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
LIST_FOREACH(units_by_type, unit, manager->units_by_type[UNIT_SERVICE]) {
r = metric_build_send_unsigned(
context, unit->id, SERVICE(unit)->n_restarts, /* fields= */ NULL);
mf,
vl,
unit->id,
SERVICE(unit)->n_restarts,
/* fields= */ NULL);
if (r < 0)
return r;
}
@@ -189,23 +207,26 @@ static int nrestarts_build_json(MetricFamilyContext *context, void *userdata) {
return 0;
}
static int reload_count_build_json(MetricFamilyContext *context, void *userdata) {
static int reload_count_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
assert(context);
assert(mf && mf->name);
assert(vl);
return metric_build_send_unsigned(
context,
mf,
vl,
/* object= */ NULL,
manager->reload_count,
/* fields= */ NULL);
}
static int units_by_type_total_build_json(MetricFamilyContext *context, void *userdata) {
static int units_by_type_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
for (UnitType type = 0; type < _UNIT_TYPE_MAX; type++) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
@@ -219,7 +240,8 @@ static int units_by_type_total_build_json(MetricFamilyContext *context, void *us
return r;
r = metric_build_send_unsigned(
context,
mf,
vl,
/* object= */ NULL,
counter,
fields);
@@ -230,14 +252,15 @@ static int units_by_type_total_build_json(MetricFamilyContext *context, void *us
return 0;
}
static int units_by_state_total_build_json(MetricFamilyContext *context, void *userdata) {
static int units_by_state_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
uint64_t counters[_UNIT_ACTIVE_STATE_MAX] = {};
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
/* TODO need a rework probably with state counter */
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
@@ -256,7 +279,8 @@ static int units_by_state_total_build_json(MetricFamilyContext *context, void *u
return r;
r = metric_build_send_unsigned(
context,
mf,
vl,
/* object= */ NULL,
counters[state],
fields);
@@ -267,38 +291,43 @@ static int units_by_state_total_build_json(MetricFamilyContext *context, void *u
return 0;
}
static int jobs_queued_build_json(MetricFamilyContext *context, void *userdata) {
static int jobs_queued_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
assert(context);
assert(mf && mf->name);
assert(vl);
return metric_build_send_unsigned(
context,
mf,
vl,
/* object= */ NULL,
hashmap_size(manager->jobs),
/* fields= */ NULL);
}
static int system_state_build_json(MetricFamilyContext *context, void *userdata) {
static int system_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
assert(context);
assert(mf && mf->name);
assert(vl);
return metric_build_send_string(
context,
mf,
vl,
/* object= */ NULL,
manager_state_to_string(manager_state(manager)),
/* fields= */ NULL);
}
static int units_by_load_state_total_build_json(MetricFamilyContext *context, void *userdata) {
static int units_by_load_state_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
uint64_t counters[_UNIT_LOAD_STATE_MAX] = {};
Unit *unit;
char *key;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
/* ignore aliases */
@@ -316,7 +345,8 @@ static int units_by_load_state_total_build_json(MetricFamilyContext *context, vo
return r;
r = metric_build_send_unsigned(
context,
mf,
vl,
/* object= */ NULL,
counters[state],
fields);
@@ -327,13 +357,14 @@ static int units_by_load_state_total_build_json(MetricFamilyContext *context, vo
return 0;
}
static int units_total_build_json(MetricFamilyContext *context, void *userdata) {
static int units_total_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
uint64_t count = 0;
Unit *unit;
char *key;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH_KEY(unit, key, manager->units) {
/* ignore aliases */
@@ -344,7 +375,8 @@ static int units_total_build_json(MetricFamilyContext *context, void *userdata)
}
return metric_build_send_unsigned(
context,
mf,
vl,
/* object= */ NULL,
count,
/* fields= */ NULL);
+30 -23
View File
@@ -19,7 +19,8 @@
typedef const char* (*link_metric_extractor_t)(const Link *link);
static int link_metric_build_json(
MetricFamilyContext *context,
const MetricFamily *mf,
sd_varlink *vl,
link_metric_extractor_t extractor,
void *userdata) {
@@ -27,11 +28,12 @@ static int link_metric_build_json(
Link *link;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
assert(extractor);
HASHMAP_FOREACH(link, manager->links_by_index) {
r = metric_build_send_string(context, link->ifname, extractor(link), /* fields= */ NULL);
r = metric_build_send_string(mf, vl, link->ifname, extractor(link), /* fields= */ NULL);
if (r < 0)
return r;
}
@@ -63,50 +65,52 @@ static const char* link_get_oper_state(const Link *l) {
return link_operstate_to_string(ASSERT_PTR(l)->operstate);
}
static int link_address_state_build_json(MetricFamilyContext *ctx, void *userdata) {
return link_metric_build_json(ctx, link_get_address_state, userdata);
static int link_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
return link_metric_build_json(mf, vl, link_get_address_state, userdata);
}
static int link_admin_state_build_json(MetricFamilyContext *ctx, void *userdata) {
return link_metric_build_json(ctx, link_get_admin_state, userdata);
static int link_admin_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
return link_metric_build_json(mf, vl, link_get_admin_state, userdata);
}
static int link_carrier_state_build_json(MetricFamilyContext *ctx, void *userdata) {
return link_metric_build_json(ctx, link_get_carrier_state, userdata);
static int link_carrier_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
return link_metric_build_json(mf, vl, link_get_carrier_state, userdata);
}
static int link_ipv4_address_state_build_json(MetricFamilyContext *ctx, void *userdata) {
return link_metric_build_json(ctx, link_get_ipv4_address_state, userdata);
static int link_ipv4_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
return link_metric_build_json(mf, vl, link_get_ipv4_address_state, userdata);
}
static int link_ipv6_address_state_build_json(MetricFamilyContext *ctx, void *userdata) {
return link_metric_build_json(ctx, link_get_ipv6_address_state, userdata);
static int link_ipv6_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
return link_metric_build_json(mf, vl, link_get_ipv6_address_state, userdata);
}
static int link_oper_state_build_json(MetricFamilyContext *ctx, void *userdata) {
return link_metric_build_json(ctx, link_get_oper_state, userdata);
static int link_oper_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
return link_metric_build_json(mf, vl, link_get_oper_state, userdata);
}
static int managed_interfaces_build_json(MetricFamilyContext *context, void *userdata) {
static int managed_interfaces_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Link *link;
uint64_t count = 0;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH(link, manager->links_by_index)
if (link->network)
count++;
return metric_build_send_unsigned(context, /* object= */ NULL, count, /* fields= */ NULL);
return metric_build_send_unsigned(mf, vl, /* object= */ NULL, count, /* fields= */ NULL);
}
static int required_for_online_build_json(MetricFamilyContext *context, void *userdata) {
static int required_for_online_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
Link *link;
int r;
assert(context);
assert(mf && mf->name);
assert(vl);
HASHMAP_FOREACH(link, manager->links_by_index) {
if (!link->network)
@@ -114,7 +118,8 @@ static int required_for_online_build_json(MetricFamilyContext *context, void *us
if (link->network->required_for_online == 0) {
r = metric_build_send_string(
context,
mf,
vl,
link->ifname,
"no",
/* fields= */ NULL);
@@ -127,7 +132,8 @@ static int required_for_online_build_json(MetricFamilyContext *context, void *us
if (range.min == range.max)
r = metric_build_send_string(
context,
mf,
vl,
link->ifname,
min_str,
/* fields= */ NULL);
@@ -137,7 +143,8 @@ static int required_for_online_build_json(MetricFamilyContext *context, void *us
return -ENOMEM;
r = metric_build_send_string(
context,
mf,
vl,
link->ifname,
value,
/* fields= */ NULL);
+141 -38
View File
@@ -9,137 +9,240 @@
#include "alloc-util.h"
#include "architecture.h"
#include "hostname-setup.h"
#include "log.h"
#include "metrics.h"
#include "os-util.h"
#include "report-basic.h"
#include "virt.h"
static int architecture_generate(MetricFamilyContext *context, void *userdata) {
assert(context);
static int architecture_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
assert(mf && mf->name);
assert(link);
return metric_build_send_string(
context,
mf,
link,
/* object= */ NULL,
architecture_to_string(uname_architecture()),
/* fields= */ NULL);
}
static int boot_id_generate(MetricFamilyContext *context, void *userdata) {
static int boot_id_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
sd_id128_t id;
int r;
assert(context);
assert(mf && mf->name);
assert(link);
r = sd_id128_get_boot(&id);
if (r < 0)
return r;
return metric_build_send_string(
context,
mf,
link,
/* object= */ NULL,
SD_ID128_TO_STRING(id),
/* fields= */ NULL);
}
static int hostname_generate(MetricFamilyContext *context, void *userdata) {
static int hostname_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
_cleanup_free_ char *hostname = NULL;
int r;
assert(context);
assert(mf && mf->name);
assert(link);
r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &hostname);
if (r < 0)
return r;
return metric_build_send_string(
context,
mf,
link,
/* object= */ NULL,
hostname,
/* fields= */ NULL);
}
static int kernel_version_generate(MetricFamilyContext *context, void *userdata) {
static int kernel_version_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
struct utsname u;
assert(context);
assert(mf && mf->name);
assert(link);
assert_se(uname(&u) >= 0);
return metric_build_send_string(
context,
mf,
link,
/* object= */ NULL,
u.release,
/* fields= */ NULL);
}
static int machine_id_generate(MetricFamilyContext *context, void *userdata) {
static int machine_id_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
sd_id128_t id;
int r;
assert(context);
assert(mf && mf->name);
assert(link);
r = sd_id128_get_machine(&id);
if (r < 0)
return r;
return metric_build_send_string(
context,
mf,
link,
/* object= */ NULL,
SD_ID128_TO_STRING(id),
/* fields= */ NULL);
}
static int virtualization_generate(MetricFamilyContext *context, void *userdata) {
Virtualization v;
enum {
FIELD_PRETTY_NAME,
FIELD_NAME,
FIELD_ID,
FIELD_CPE_NAME,
FIELD_VARIANT_ID,
FIELD_VERSION_ID,
FIELD_BUILD_ID,
FIELD_IMAGE_VERSION,
FIELD_IMAGE_ID,
FIELD_SUPPORT_END,
FIELD_EXPERIMENT,
FIELD_SYSEXT_LEVEL,
FIELD_CONFEXT_LEVEL,
_FIELD_MAX,
};
assert(context);
static int os_release_generate(const MetricFamily mf[static _FIELD_MAX - 1], sd_varlink *link, void *userdata) {
char* values[_FIELD_MAX] = {};
CLEANUP_ELEMENTS(values, free_many_charp);
int r;
v = detect_virtualization();
assert(mf && mf->name);
assert(link);
r = parse_os_release(NULL,
"PRETTY_NAME", &values[FIELD_PRETTY_NAME],
"NAME", &values[FIELD_NAME],
"ID", &values[FIELD_ID],
"CPE_NAME", &values[FIELD_CPE_NAME],
"VARIANT_ID", &values[FIELD_VARIANT_ID],
"VERSION_ID", &values[FIELD_VERSION_ID],
"BUILD_ID", &values[FIELD_BUILD_ID],
"IMAGE_VERSION", &values[FIELD_IMAGE_VERSION],
"IMAGE_ID", &values[FIELD_IMAGE_ID],
"SUPPORT_END", &values[FIELD_SUPPORT_END],
"EXPERIMENT", &values[FIELD_EXPERIMENT],
"SYSEXT_LEVEL", &values[FIELD_SYSEXT_LEVEL],
"CONFEXT_LEVEL", &values[FIELD_CONFEXT_LEVEL]);
if (r < 0) {
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to read os-release file, ignoring: %m");
return 0;
}
for (size_t i = 1; i < _FIELD_MAX; i++) {
const char *v = values[i];
if (i == FIELD_NAME && values[FIELD_PRETTY_NAME])
v = values[FIELD_PRETTY_NAME]; /* Prefer PRETTY_NAME to NAME */
if (v) {
r = metric_build_send_string(
mf + i - 1,
link,
/* object= */ NULL,
v,
/* fields= */ NULL);
if (r < 0)
return r;
}
}
return 0;
}
static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
assert(mf && mf->name);
assert(link);
Virtualization v = detect_virtualization();
if (v < 0)
return v;
return metric_build_send_string(
context,
mf,
link,
/* object= */ NULL,
virtualization_to_string(v),
/* fields= */ NULL);
}
#define OS_RELEASE_STANDARD_FIELD(name) \
{ \
METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease." name, \
"Operating system identification (" name "= field from os-release)", \
METRIC_FAMILY_TYPE_STRING, \
.generate = NULL, \
}
static const MetricFamily metric_family_table[] = {
/* Keep entries ordered alphabetically */
{
.name = METRIC_IO_SYSTEMD_BASIC_PREFIX "Architecture",
.description = "CPU architecture",
.type = METRIC_FAMILY_TYPE_STRING,
METRIC_IO_SYSTEMD_BASIC_PREFIX "Architecture",
"CPU architecture",
METRIC_FAMILY_TYPE_STRING,
.generate = architecture_generate,
},
{
.name = METRIC_IO_SYSTEMD_BASIC_PREFIX "BootID",
.description = "Current boot ID",
.type = METRIC_FAMILY_TYPE_STRING,
METRIC_IO_SYSTEMD_BASIC_PREFIX "BootID",
"Current boot ID",
METRIC_FAMILY_TYPE_STRING,
.generate = boot_id_generate,
},
{
.name = METRIC_IO_SYSTEMD_BASIC_PREFIX "Hostname",
.description = "System hostname",
.type = METRIC_FAMILY_TYPE_STRING,
METRIC_IO_SYSTEMD_BASIC_PREFIX "Hostname",
"System hostname",
METRIC_FAMILY_TYPE_STRING,
.generate = hostname_generate,
},
{
.name = METRIC_IO_SYSTEMD_BASIC_PREFIX "KernelVersion",
.description = "Kernel version",
.type = METRIC_FAMILY_TYPE_STRING,
METRIC_IO_SYSTEMD_BASIC_PREFIX "KernelVersion",
"Kernel version",
METRIC_FAMILY_TYPE_STRING,
.generate = kernel_version_generate,
},
{
.name = METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineID",
.description = "Machine ID",
.type = METRIC_FAMILY_TYPE_STRING,
METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineID",
"Machine ID",
METRIC_FAMILY_TYPE_STRING,
.generate = machine_id_generate,
},
{
.name = METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization",
.description = "Virtualization type",
.type = METRIC_FAMILY_TYPE_STRING,
METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease.NAME",
"Operating system human-readable name (PRETTY_NAME= or NAME= field from os-release)",
METRIC_FAMILY_TYPE_STRING,
.generate = os_release_generate,
},
OS_RELEASE_STANDARD_FIELD("ID"),
OS_RELEASE_STANDARD_FIELD("CPE_NAME"),
OS_RELEASE_STANDARD_FIELD("VARIANT_ID"),
OS_RELEASE_STANDARD_FIELD("VERSION_ID"),
OS_RELEASE_STANDARD_FIELD("BUILD_ID"),
OS_RELEASE_STANDARD_FIELD("IMAGE_VERSION"),
OS_RELEASE_STANDARD_FIELD("IMAGE_ID"),
OS_RELEASE_STANDARD_FIELD("SUPPORT_END"),
OS_RELEASE_STANDARD_FIELD("EXPERIMENT"),
OS_RELEASE_STANDARD_FIELD("SYSEXT_LEVEL"),
OS_RELEASE_STANDARD_FIELD("CONFEXT_LEVEL"),
/* Keep those ↑ in sync with os_release_generate(). */
{
METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization",
"Virtualization type",
METRIC_FAMILY_TYPE_STRING,
.generate = virtualization_generate,
},
{}
+1 -7
View File
@@ -2,7 +2,6 @@
#include "sd-varlink.h"
#include "alloc-util.h"
#include "build.h"
#include "format-table.h"
#include "help-util.h"
@@ -15,14 +14,9 @@
static int vl_server(void) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *vs = NULL;
_cleanup_(cgroup_context_freep) CGroupContext *ctx = NULL;
int r;
ctx = new0(CGroupContext, 1);
if (!ctx)
return log_oom();
r = varlink_server_new(&vs, SD_VARLINK_SERVER_INHERIT_USERDATA, ctx);
r = varlink_server_new(&vs, SD_VARLINK_SERVER_INHERIT_USERDATA, /* userdata= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
+235 -419
View File
@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "cgroup-util.h"
#include "errno-util.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
@@ -16,349 +17,67 @@
#include "string-util.h"
#include "time-util.h"
typedef struct CGroupInfo {
char *unit;
char *path;
uint64_t io_rbytes;
uint64_t io_rios;
int io_stat_cached; /* 0 = not attempted, > 0 = cached, < 0 = -errno */
uint64_t cpu_total_nsec;
uint64_t cpu_user_nsec;
uint64_t cpu_system_nsec;
int cpu_stat_cached; /* 0 = not attempted, > 0 = cached, < 0 = -errno */
} CGroupInfo;
static CGroupInfo *cgroup_info_free(CGroupInfo *info) {
if (!info)
return NULL;
free(info->unit);
free(info->path);
return mfree(info);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupInfo*, cgroup_info_free);
static void cgroup_info_array_free(CGroupInfo **infos, size_t n) {
FOREACH_ARRAY(i, infos, n)
cgroup_info_free(*i);
free(infos);
}
static void cgroup_context_flush(CGroupContext *ctx) {
assert(ctx);
cgroup_info_array_free(ctx->cgroups, ctx->n_cgroups);
ctx->cgroups = NULL;
ctx->n_cgroups = 0;
ctx->cache_populated = false;
}
CGroupContext *cgroup_context_free(CGroupContext *ctx) {
if (!ctx)
return NULL;
cgroup_context_flush(ctx);
return mfree(ctx);
}
static int walk_cgroups_recursive(const char *path, CGroupInfo ***infos, size_t *n_infos) {
_cleanup_closedir_ DIR *d = NULL;
int r;
assert(path);
assert(infos);
assert(n_infos);
/* Collect any unit cgroup we encounter */
_cleanup_free_ char *name = NULL;
r = cg_path_get_unit(path, &name);
if (r >= 0) {
_cleanup_(cgroup_info_freep) CGroupInfo *info = new(CGroupInfo, 1);
if (!info)
return log_oom();
*info = (CGroupInfo) {
.unit = TAKE_PTR(name),
.path = strdup(path),
};
if (!info->path)
return log_oom();
if (!GREEDY_REALLOC(*infos, *n_infos + 1))
return log_oom();
(*infos)[(*n_infos)++] = TAKE_PTR(info);
return 0; /* Unit cgroups are leaf nodes for our purposes */
}
/* Stop at delegation boundaries — don't descend into delegated subtrees */
r = cg_is_delegated(path);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to check delegation for '%s': %m", path);
if (r > 0)
return 0;
r = cg_enumerate_subgroups(path, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to enumerate cgroup '%s': %m", path);
for (;;) {
_cleanup_free_ char *fn = NULL, *child = NULL;
r = cg_read_subgroup(d, &fn);
if (r < 0)
return log_debug_errno(r, "Failed to read subgroup from '%s': %m", path);
if (r == 0)
break;
child = path_join(empty_to_root(path), fn);
if (!child)
return log_oom();
path_simplify(child);
r = walk_cgroups_recursive(child, infos, n_infos);
if (r < 0)
return r;
}
return 0;
}
static int walk_cgroups(CGroupContext *ctx, CGroupInfo ***ret, size_t *ret_n) {
int r;
assert(ctx);
assert(ret);
assert(ret_n);
/* Return cached result if available */
if (ctx->cache_populated) {
*ret = ctx->cgroups;
*ret_n = ctx->n_cgroups;
return 0;
}
CGroupInfo **infos = NULL;
size_t n_infos = 0;
CLEANUP_ARRAY(infos, n_infos, cgroup_info_array_free);
r = walk_cgroups_recursive("", &infos, &n_infos);
if (r < 0)
return r;
ctx->cgroups = TAKE_PTR(infos);
ctx->n_cgroups = TAKE_GENERIC(n_infos, size_t, 0);
ctx->cache_populated = true;
*ret = ctx->cgroups;
*ret_n = ctx->n_cgroups;
return 0;
}
/* Parse cpu.stat for a cgroup once, extracting usage_usec, user_usec and system_usec
* in a single read so each scrape only opens the file once per cgroup. */
static int cpu_stat_parse(
const char *cgroup_path,
uint64_t *ret_total_nsec,
uint64_t *ret_user_nsec,
uint64_t *ret_system_nsec) {
char *values[3] = {};
uint64_t total_us, user_us, system_us;
static int cpu_stat_parse(const char *cgroup_path, uint64_t ret[static 3]) {
char* strings[3] = {};
CLEANUP_ELEMENTS(strings, free_many_charp);
uint64_t values[3];
int r;
assert(cgroup_path);
assert(ret_total_nsec);
assert(ret_user_nsec);
assert(ret_system_nsec);
r = cg_get_keyed_attribute(
cgroup_path,
"cpu.stat",
STRV_MAKE("usage_usec", "user_usec", "system_usec"),
values);
strings);
if (r < 0)
return r;
r = safe_atou64(values[0], &total_us);
if (r >= 0)
r = safe_atou64(values[1], &user_us);
if (r >= 0)
r = safe_atou64(values[2], &system_us);
for (unsigned i = 0; i < 3; i++) {
r = safe_atou64(strings[i], &values[i]);
if (r < 0)
return r;
}
free_many_charp(values, ELEMENTSOF(values));
if (r < 0)
return r;
*ret_total_nsec = total_us * NSEC_PER_USEC;
*ret_user_nsec = user_us * NSEC_PER_USEC;
*ret_system_nsec = system_us * NSEC_PER_USEC;
for (unsigned i = 0; i < 3; i++)
ret[i] = values[i] * NSEC_PER_USEC;
return 0;
}
static int ensure_cpu_stat_cached(CGroupInfo *info) {
static int cpu_usage_send(
const MetricFamily *mf,
sd_varlink *link,
const char *path,
const char *unit) {
static const char* const types[] = { "total", "user", "system" };
uint64_t values[3];
int r;
assert(info);
assert(mf && mf->name);
assert(link);
assert(path);
assert(unit);
if (info->cpu_stat_cached > 0)
return 0;
if (info->cpu_stat_cached < 0)
return info->cpu_stat_cached;
r = cpu_stat_parse(info->path, &info->cpu_total_nsec, &info->cpu_user_nsec, &info->cpu_system_nsec);
r = cpu_stat_parse(path, values);
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Failed to parse cpu.stat for '%s': %m", info->path);
info->cpu_stat_cached = r;
return r;
}
info->cpu_stat_cached = 1;
return 0;
}
static int cpu_usage_send_one(
MetricFamilyContext *context,
const char *unit,
uint64_t value_nsec,
const char *type) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
int r;
assert(context);
assert(unit);
assert(type);
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", type));
if (r < 0)
return r;
return metric_build_send_unsigned(context, unit, value_nsec, fields);
}
static int cpu_usage_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0; /* Skip metric on failure */
FOREACH_ARRAY(c, cgroups, n_cgroups) {
if (ensure_cpu_stat_cached(*c) < 0)
continue;
r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_total_nsec, "total");
if (r < 0)
return r;
r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_user_nsec, "user");
if (r < 0)
return r;
r = cpu_usage_send_one(context, (*c)->unit, (*c)->cpu_system_nsec, "system");
if (r < 0)
return r;
}
return 0;
}
static int memory_usage_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "cpu.stat");
return 0;
}
FOREACH_ARRAY(c, cgroups, n_cgroups) {
uint64_t current = 0, limit = UINT64_MAX;
for (unsigned i = 0; i < 3; i++) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = cg_get_attribute_as_uint64((*c)->path, "memory.current", &current);
if (r >= 0) {
/* Walk up the cgroup tree to find the tightest memory limit */
_cleanup_free_ char *path_buf = strdup((*c)->path);
if (!path_buf)
return log_oom();
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", types[i]));
if (r < 0)
return r;
for (char *p = path_buf;;) {
uint64_t high, max;
r = cg_get_attribute_as_uint64(p, "memory.max", &max);
if (r >= 0 && max < limit)
limit = max;
r = cg_get_attribute_as_uint64(p, "memory.high", &high);
if (r >= 0 && high < limit)
limit = high;
/* Move to parent */
const char *e;
r = path_find_last_component(p, /* accept_dot_dot= */ false, &e, NULL);
if (r <= 0)
break;
p[e - p] = '\0';
}
if (limit != UINT64_MAX && limit > current) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "available"));
if (r < 0)
return r;
r = metric_build_send_unsigned(
context,
(*c)->unit,
limit - current,
fields);
if (r < 0)
return r;
}
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "current"));
if (r < 0)
return r;
r = metric_build_send_unsigned(
context,
(*c)->unit,
current,
fields);
if (r < 0)
return r;
}
uint64_t val;
r = cg_get_attribute_as_uint64((*c)->path, "memory.peak", &val);
if (r >= 0) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "peak"));
if (r < 0)
return r;
r = metric_build_send_unsigned(
context,
(*c)->unit,
val,
fields);
if (r < 0)
return r;
}
r = metric_build_send_unsigned(mf, link, unit, values[i], fields);
if (r < 0)
return r;
}
return 0;
@@ -366,22 +85,20 @@ static int memory_usage_build_json(MetricFamilyContext *context, void *userdata)
/* Parse io.stat for a cgroup once, summing both rbytes= and rios= fields in a
* single pass to avoid reading the file twice. */
static int io_stat_parse(const char *cgroup_path, uint64_t *ret_rbytes, uint64_t *ret_rios) {
static int io_stat_parse(const char *cgroup_path, uint64_t ret[static 2]) {
_cleanup_free_ char *path = NULL;
_cleanup_fclose_ FILE *f = NULL;
uint64_t rbytes = 0, rios = 0;
int r;
assert(ret_rbytes);
assert(ret_rios);
assert(ret);
r = cg_get_path(cgroup_path, "io.stat", &path);
if (r < 0)
return r;
f = fopen(path, "re");
_cleanup_fclose_ FILE *f = fopen(path, "re");
if (!f)
return -errno;
return errno_or_else(EIO);
for (;;) {
_cleanup_free_ char *line = NULL;
@@ -421,54 +138,34 @@ static int io_stat_parse(const char *cgroup_path, uint64_t *ret_rbytes, uint64_t
}
}
*ret_rbytes = rbytes;
*ret_rios = rios;
ret[0] = rbytes;
ret[1] = rios;
return 0;
}
static int ensure_io_stat_cached(CGroupInfo *info) {
static int io_read_send(
const MetricFamily mf[static 2],
sd_varlink *link,
const char *path,
const char *unit) {
uint64_t values[2];
int r;
assert(info);
assert(mf && mf[0].name && mf[1].name);
assert(link);
assert(path);
assert(unit);
if (info->io_stat_cached > 0)
return 0;
if (info->io_stat_cached < 0)
return info->io_stat_cached;
r = io_stat_parse(info->path, &info->io_rbytes, &info->io_rios);
r = io_stat_parse(path, values);
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Failed to parse IO stats for '%s': %m", info->path);
info->io_stat_cached = r;
return r;
log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "io.stat");
return 0;
}
info->io_stat_cached = 1;
return 0;
}
static int io_read_bytes_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0;
FOREACH_ARRAY(c, cgroups, n_cgroups) {
if (ensure_io_stat_cached(*c) < 0)
continue;
r = metric_build_send_unsigned(
context,
(*c)->unit,
(*c)->io_rbytes,
/* fields= */ NULL);
for (unsigned i = 0; i < 2; i++) {
r = metric_build_send_unsigned(mf + i, link, unit, values[i], /* fields= */ NULL);
if (r < 0)
return r;
}
@@ -476,27 +173,76 @@ static int io_read_bytes_build_json(MetricFamilyContext *context, void *userdata
return 0;
}
static int io_read_operations_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
static int memory_usage_send(
const MetricFamily *mf,
sd_varlink *link,
const char *path,
const char *unit) {
static const char* const types[] = { "current", "available", "peak" };
bool bad[ELEMENTSOF(types)] = {};
uint64_t current = 0, limit = UINT64_MAX, peak = 0;
int r;
assert(context);
assert(mf && mf->name);
assert(link);
assert(path);
assert(unit);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0;
r = cg_get_attribute_as_uint64(path, "memory.current", &current);
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "memory.current");
FOREACH_ARRAY(c, cgroups, n_cgroups) {
if (ensure_io_stat_cached(*c) < 0)
bad[0] = bad[1] = true;
} else {
/* Walk up the cgroup tree to find the tightest memory limit */
_cleanup_free_ char *path_buf = strdup(path);
if (!path_buf)
return log_oom();
for (char *p = path_buf;;) {
uint64_t high, max;
r = cg_get_attribute_as_uint64(p, "memory.max", &max);
if (r >= 0 && max < limit)
limit = max;
r = cg_get_attribute_as_uint64(p, "memory.high", &high);
if (r >= 0 && high < limit)
limit = high;
/* Move to parent */
const char *e;
r = path_find_last_component(p, /* accept_dot_dot= */ false, &e, NULL);
if (r <= 0)
break;
p[e - p] = '\0';
}
if (limit == UINT64_MAX || limit <= current)
bad[1] = true;
}
r = cg_get_attribute_as_uint64(path, "memory.peak", &peak);
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "memory.peak");
bad[2] = true;
}
uint64_t values[] = { current, limit - current, peak };
for (unsigned i = 0; i < ELEMENTSOF(values); i++) {
if (bad[i])
continue;
r = metric_build_send_unsigned(
context,
(*c)->unit,
(*c)->io_rios,
/* fields= */ NULL);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", types[i]));
if (r < 0)
return r;
r = metric_build_send_unsigned(mf, link, unit, values[i], fields);
if (r < 0)
return r;
}
@@ -504,30 +250,95 @@ static int io_read_operations_build_json(MetricFamilyContext *context, void *use
return 0;
}
static int tasks_current_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
static int tasks_current_send(
const MetricFamily *mf,
sd_varlink *link,
const char *path,
const char *unit) {
uint64_t val;
int r;
assert(context);
assert(mf && mf->name);
assert(link);
assert(path);
assert(unit);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
r = cg_get_attribute_as_uint64(path, "pids.current", &val);
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Failed to read %s/%s, ignoring: %m", path, "pids.current");
return 0;
}
return metric_build_send_unsigned(mf, link, unit, val, /* fields= */ NULL);
}
static int walk_cgroups(
const MetricFamily mf[static 5],
sd_varlink *link,
const char *path) {
int r;
assert(mf && mf[0].name && mf[1].name && mf[2].name && mf[3].name && mf[4].name);
assert(mf[0].generate && !mf[1].generate && !mf[2].generate && !mf[3].generate && !mf[4].generate);
assert(path);
_cleanup_free_ char *unit = NULL;
r = cg_path_get_unit(path, &unit);
if (r >= 0) {
r = cpu_usage_send(mf + 0, link, path, unit);
if (r < 0)
return r;
r = io_read_send(mf + 1, link, path, unit);
if (r < 0)
return r;
r = memory_usage_send(mf + 3, link, path, unit);
if (r < 0)
return r;
r = tasks_current_send(mf + 4, link, path, unit);
if (r < 0)
return r;
return 0; /* Unit cgroups are leaf nodes for our purposes */
}
/* Stop at delegation boundaries — don't descend into delegated subtrees */
r = cg_is_delegated(path);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to check delegation for '%s': %m", path);
if (r > 0)
return 0;
FOREACH_ARRAY(c, cgroups, n_cgroups) {
uint64_t val;
_cleanup_closedir_ DIR *d = NULL;
r = cg_enumerate_subgroups(path, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to enumerate cgroup '%s': %m", path);
r = cg_get_attribute_as_uint64((*c)->path, "pids.current", &val);
for (;;) {
_cleanup_free_ char *fn = NULL, *child = NULL;
r = cg_read_subgroup(d, &fn);
if (r < 0)
continue;
return log_debug_errno(r, "Failed to read subgroup from '%s': %m", path);
if (r == 0)
break;
r = metric_build_send_unsigned(
context,
(*c)->unit,
val,
/* fields= */ NULL);
child = path_join(empty_to_root(path), fn);
if (!child)
return log_oom();
path_simplify(child);
r = walk_cgroups(mf, link, child);
if (r < 0)
return r;
}
@@ -535,37 +346,49 @@ static int tasks_current_build_json(MetricFamilyContext *context, void *userdata
return 0;
}
static int cgroup_stats_send(
const MetricFamily mf[static 5],
sd_varlink *link,
void *userdata) {
assert(mf);
assert(link);
assert(!userdata);
return walk_cgroups(mf, link, "");
}
static const MetricFamily cgroup_metric_family_table[] = {
/* Keep metrics ordered alphabetically */
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "CpuUsage",
.description = "Per unit metric: CPU usage in nanoseconds (type=total|user|system)",
.type = METRIC_FAMILY_TYPE_COUNTER,
.generate = cpu_usage_build_json,
METRIC_IO_SYSTEMD_CGROUP_PREFIX "CpuUsage",
"Per unit metric: CPU usage in nanoseconds (type=total|user|system)",
METRIC_FAMILY_TYPE_COUNTER,
.generate = cgroup_stats_send,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadBytes",
.description = "Per unit metric: IO bytes read",
.type = METRIC_FAMILY_TYPE_COUNTER,
.generate = io_read_bytes_build_json,
METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadBytes",
"Per unit metric: IO bytes read",
METRIC_FAMILY_TYPE_COUNTER,
.generate = NULL,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadOperations",
.description = "Per unit metric: IO read operations",
.type = METRIC_FAMILY_TYPE_COUNTER,
.generate = io_read_operations_build_json,
METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadOperations",
"Per unit metric: IO read operations",
METRIC_FAMILY_TYPE_COUNTER,
.generate = NULL,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "MemoryUsage",
.description = "Per unit metric: memory usage in bytes",
.type = METRIC_FAMILY_TYPE_GAUGE,
.generate = memory_usage_build_json,
METRIC_IO_SYSTEMD_CGROUP_PREFIX "MemoryUsage",
"Per unit metric: memory usage in bytes",
METRIC_FAMILY_TYPE_GAUGE,
.generate = NULL,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "TasksCurrent",
.description = "Per unit metric: current number of tasks",
.type = METRIC_FAMILY_TYPE_GAUGE,
.generate = tasks_current_build_json,
METRIC_IO_SYSTEMD_CGROUP_PREFIX "TasksCurrent",
"Per unit metric: current number of tasks",
METRIC_FAMILY_TYPE_GAUGE,
.generate = NULL,
},
{}
};
@@ -575,12 +398,5 @@ int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd
}
int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
int r;
r = metrics_method_list(cgroup_metric_family_table, link, parameters, flags, userdata);
cgroup_context_flush(ctx);
return r;
return metrics_method_list(cgroup_metric_family_table, link, parameters, flags, userdata);
}
-11
View File
@@ -5,16 +5,5 @@
#define METRIC_IO_SYSTEMD_CGROUP_PREFIX "io.systemd.CGroup."
typedef struct CGroupInfo CGroupInfo;
typedef struct CGroupContext {
CGroupInfo **cgroups;
size_t n_cgroups;
bool cache_populated;
} CGroupContext;
CGroupContext *cgroup_context_free(CGroupContext *ctx);
DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupContext*, cgroup_context_free);
int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
+46 -27
View File
@@ -73,15 +73,14 @@ static int metric_family_build_json(const MetricFamily *mf, sd_json_variant **re
}
int metrics_method_describe(
const MetricFamily metric_family_table[],
const MetricFamily mfs[],
sd_varlink *link,
sd_json_variant *parameters,
sd_varlink_method_flags_t flags,
void *userdata) {
int r;
assert(metric_family_table);
assert(mfs);
assert(link);
assert(parameters);
assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
@@ -94,7 +93,7 @@ int metrics_method_describe(
if (r < 0)
return r;
for (const MetricFamily *mf = metric_family_table; mf && mf->name; mf++) {
for (const MetricFamily *mf = mfs; mf->name; mf++) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = metric_family_build_json(mf, &v);
@@ -110,15 +109,14 @@ int metrics_method_describe(
}
int metrics_method_list(
const MetricFamily metric_family_table[],
const MetricFamily mfs[],
sd_varlink *link,
sd_json_variant *parameters,
sd_varlink_method_flags_t flags,
void *userdata) {
int r;
assert(metric_family_table);
assert(mfs);
assert(link);
assert(parameters);
assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
@@ -131,25 +129,28 @@ int metrics_method_list(
if (r < 0)
return r;
MetricFamilyContext ctx = { .link = link };
for (const MetricFamily *mf = metric_family_table; mf && mf->name; mf++) {
assert(mf->generate);
ctx.metric_family = mf;
r = mf->generate(&ctx, userdata);
if (r < 0)
return log_debug_errno(
r, "Failed to list metrics for metric family '%s': %m", mf->name);
}
for (const MetricFamily *mf = mfs; mf->name; mf++)
/* Metrics without .generate are handled by the preceding entry with .generate.
* Call the generating functions that are defined. */
if (mf->generate) {
r = mf->generate(mf, link, userdata);
if (r < 0)
return log_debug_errno(r, "Failed to list metrics for metric family '%s': %m", mf->name);
}
return 0;
}
static int metric_build_send(MetricFamilyContext *context, const char *object, sd_json_variant *value, sd_json_variant *fields) {
assert(context);
static int metric_build_send(
const MetricFamily *mf,
sd_varlink *link,
const char *object,
sd_json_variant *value,
sd_json_variant *fields) {
assert(mf);
assert(link);
assert(value);
assert(context->link);
assert(context->metric_family);
if (fields) {
assert(sd_json_variant_is_object(fields));
@@ -160,33 +161,51 @@ static int metric_build_send(MetricFamilyContext *context, const char *object, s
assert(sd_json_variant_is_string(e));
}
return sd_varlink_replybo(context->link,
SD_JSON_BUILD_PAIR_STRING("name", context->metric_family->name),
return sd_varlink_replybo(
link,
SD_JSON_BUILD_PAIR_STRING("name", mf->name),
JSON_BUILD_PAIR_STRING_NON_EMPTY("object", object),
SD_JSON_BUILD_PAIR_VARIANT("value", value),
JSON_BUILD_PAIR_VARIANT_NON_NULL("fields", fields));
}
int metric_build_send_string(MetricFamilyContext *context, const char *object, const char *value, sd_json_variant *fields) {
int metric_build_send_string(
const MetricFamily *mf,
sd_varlink *link,
const char *object,
const char *value,
sd_json_variant *fields) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
int r;
assert(mf);
assert(link);
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 metric_build_send(context, object, v, fields);
return metric_build_send(mf, link, object, v, fields);
}
int metric_build_send_unsigned(MetricFamilyContext *context, const char *object, uint64_t value, sd_json_variant *fields) {
int metric_build_send_unsigned(
const MetricFamily *mf,
sd_varlink *link,
const char *object,
uint64_t value,
sd_json_variant *fields) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
int r;
assert(mf);
assert(link);
r = sd_json_variant_new_unsigned(&v, value);
if (r < 0)
return log_debug_errno(r, "Failed to allocate JSON unsigned: %m");
return metric_build_send(context, object, v, fields);
return metric_build_send(mf, link, object, v, fields);
}
+5 -10
View File
@@ -13,12 +13,7 @@ typedef enum MetricFamilyType {
typedef struct MetricFamily MetricFamily;
typedef struct MetricFamilyContext {
const MetricFamily* metric_family;
sd_varlink *link;
} MetricFamilyContext;
typedef int (*metric_family_generate_func_t) (MetricFamilyContext *mfc, void *userdata);
typedef int (*metric_family_generate_func_t) (const MetricFamily *mf, sd_varlink *link, void *userdata);
typedef struct MetricFamily {
const char *name;
@@ -38,8 +33,8 @@ int metrics_setup_varlink_server(
DECLARE_STRING_TABLE_LOOKUP_TO_STRING(metric_family_type, MetricFamilyType);
int metrics_method_describe(const MetricFamily metric_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int metrics_method_list(const MetricFamily metric_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int metrics_method_describe(const MetricFamily mfs[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int metrics_method_list(const MetricFamily mfs[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int metric_build_send_string(MetricFamilyContext* context, const char *object, const char *value, sd_json_variant *fields);
int metric_build_send_unsigned(MetricFamilyContext* context, const char *object, uint64_t value, sd_json_variant *fields);
int metric_build_send_string(const MetricFamily* mf, sd_varlink *link, const char *object, const char *value, sd_json_variant *fields);
int metric_build_send_unsigned(const MetricFamily* mf, sd_varlink *link, const char *object, uint64_t value, sd_json_variant *fields);
+4
View File
@@ -58,6 +58,10 @@ varlinkctl list-methods /run/systemd/report/io.systemd.Basic
varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {}
varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.Describe {}
id1="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {} | jq --seq -r 'select(.name == "io.systemd.Basic.OSRelease.ID") | .value')"
id2="$(. /etc/os-release; echo "$ID")"
[ "$id1" = "$id2" ]
# Test HTTP upload (plain http)
FAKE_SERVER=/usr/lib/systemd/tests/integration-tests/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/fake-report-server.py
CERTDIR=$(mktemp -d)