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:
+75
-43
@@ -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);
|
||||
|
||||
@@ -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
@@ -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,
|
||||
},
|
||||
{}
|
||||
|
||||
@@ -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
@@ -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", ¤t);
|
||||
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", ¤t);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user