--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "hashmap.h"
+#include "manager.h"
+#include "metrics.h"
+#include "service.h"
+#include "unit-def.h"
+#include "unit.h"
+#include "varlink-metrics.h"
+
+static int unit_active_state_build_json(MetricFamilyContext *context, void *userdata) {
+ Manager *manager = ASSERT_PTR(userdata);
+ Unit *unit;
+ char *key;
+ int r;
+
+ assert(context);
+
+ HASHMAP_FOREACH_KEY(unit, key, manager->units) {
+ /* ignore aliases */
+ if (key != unit->id)
+ continue;
+
+ r = metric_build_send_string(
+ context,
+ unit->id,
+ unit_active_state_to_string(unit_active_state(unit)),
+ /* field_pairs= */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int unit_load_state_build_json(MetricFamilyContext *context, void *userdata) {
+ Manager *manager = ASSERT_PTR(userdata);
+ Unit *unit;
+ char *key;
+ int r;
+
+ assert(context);
+
+ HASHMAP_FOREACH_KEY(unit, key, manager->units) {
+ /* ignore aliases */
+ if (key != unit->id)
+ continue;
+
+ r = metric_build_send_string(
+ context,
+ unit->id,
+ unit_load_state_to_string(unit->load_state),
+ /* field_pairs= */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int nrestarts_build_json(MetricFamilyContext *context, void *userdata) {
+ Manager *manager = ASSERT_PTR(userdata);
+ int r;
+
+ assert(context);
+
+ LIST_FOREACH(units_by_type, unit, manager->units_by_type[UNIT_SERVICE]) {
+ r = metric_build_send_unsigned(
+ context, unit->id, SERVICE(unit)->n_restarts, /* field_pairs= */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int units_by_type_total_build_json(MetricFamilyContext *context, void *userdata) {
+ Manager *manager = ASSERT_PTR(userdata);
+ int r;
+
+ assert(context);
+
+ for (UnitType type = 0; type < _UNIT_TYPE_MAX; type++) {
+ uint64_t counter = 0;
+
+ LIST_FOREACH(units_by_type, _u, manager->units_by_type[type])
+ counter++;
+
+ r = metric_build_send_unsigned(
+ context,
+ /* object= */ NULL,
+ counter,
+ STRV_MAKE("type", unit_type_to_string(type)));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int units_by_state_total_build_json(MetricFamilyContext *context, void *userdata) {
+ Manager *manager = ASSERT_PTR(userdata);
+ UnitActiveState counters[_UNIT_ACTIVE_STATE_MAX] = {};
+ Unit *unit;
+ char *key;
+ int r;
+
+ assert(context);
+
+ /* TODO need a rework probably with state counter */
+ HASHMAP_FOREACH_KEY(unit, key, manager->units) {
+ /* ignore aliases */
+ if (key != unit->id)
+ continue;
+
+ counters[unit_active_state(unit)]++;
+ }
+
+ for (UnitActiveState state = 0; state < _UNIT_ACTIVE_STATE_MAX; state++) {
+ r = metric_build_send_unsigned(
+ context,
+ /* object= */ NULL,
+ counters[state],
+ STRV_MAKE("state", unit_active_state_to_string(state)));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+const MetricFamily metric_family_table[] = {
+ // Keep metrics ordered alphabetically
+ {
+ .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "nrestarts",
+ .description = "Per unit metric: number of restarts",
+ .type = METRIC_FAMILY_TYPE_COUNTER,
+ .generate_cb = nrestarts_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unit_active_state",
+ .description = "Per unit metric: active state",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate_cb = unit_active_state_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unit_load_state",
+ .description = "Per unit metric: load state",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate_cb = unit_load_state_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "units_by_state_total",
+ .description = "Total number of units of different state",
+ .type = METRIC_FAMILY_TYPE_GAUGE,
+ .generate_cb = units_by_state_total_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "units_by_type_total",
+ .description = "Total number of units of different types",
+ .type = METRIC_FAMILY_TYPE_GAUGE,
+ .generate_cb = units_by_type_total_build_json,
+ },
+ {}
+};
+
+int vl_method_describe(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ return metrics_method_describe(metric_family_table, link, parameters, flags, userdata);
+}
+
+int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ return metrics_method_list(metric_family_table, link, parameters, flags, userdata);
+}
#include "constants.h"
#include "errno-util.h"
#include "manager.h"
+#include "metrics.h"
#include "path-util.h"
#include "pidref.h"
#include "string-util.h"
#include "varlink-io.systemd.UserDatabase.h"
#include "varlink-io.systemd.service.h"
#include "varlink-manager.h"
+#include "varlink-metrics.h"
#include "varlink-serialize.h"
#include "varlink-unit.h"
#include "varlink-util.h"
return 1;
}
+static int manager_setup_varlink_metrics_server(Manager *m) {
+ sd_varlink_server_flags_t flags = SD_VARLINK_SERVER_INHERIT_USERDATA;
+ int r;
+
+ assert(m);
+
+ if (MANAGER_IS_SYSTEM(m))
+ flags |= SD_VARLINK_SERVER_ACCOUNT_UID;
+
+ r = metrics_setup_varlink_server(
+ &m->metrics_varlink_server, flags, m->event, vl_method_list, vl_method_describe, m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int manager_varlink_init_system(Manager *m) {
int r;
+ _cleanup_free_ char *metrics_address = NULL;
assert(m);
return log_error_errno(r, "Failed to set up varlink server: %m");
bool fresh = r > 0;
+ r = manager_setup_varlink_metrics_server(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up metrics varlink server: %m");
+ bool metrics_fresh = r > 0;
+
+ r = runtime_directory_generic(m->runtime_scope, "systemd/report/io.systemd.Manager", &metrics_address);
+ if (r < 0)
+ return r;
+
if (!MANAGER_IS_TEST_RUN(m)) {
FOREACH_STRING(address,
"/run/systemd/userdb/io.systemd.DynamicUser",
VARLINK_PATH_MANAGED_OOM_SYSTEM,
- "/run/systemd/io.systemd.Manager") {
+ "/run/systemd/io.systemd.Manager",
+ metrics_address) {
+
+ sd_varlink_server *server = streq(address, metrics_address) ? m->metrics_varlink_server : m->varlink_server;
+ fresh = streq(address, metrics_address) ? metrics_fresh : fresh;
/* We might have got sockets through deserialization. Do not bind to them twice. */
- if (!fresh && varlink_server_contains_socket(m->varlink_server, address))
+ if (!fresh && varlink_server_contains_socket(server, address))
continue;
- r = sd_varlink_server_listen_address(m->varlink_server, address, 0666 | SD_VARLINK_SERVER_MODE_MKDIR_0755);
+ r = sd_varlink_server_listen_address(server, address, 0666 | SD_VARLINK_SERVER_MODE_MKDIR_0755);
if (r < 0)
return log_error_errno(r, "Failed to bind to varlink socket '%s': %m", address);
}
return log_error_errno(r, "Failed to bind to varlink socket '%s': %m", address);
}
+ r = manager_setup_varlink_metrics_server(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up metrics varlink server: %m");
+
return manager_varlink_managed_oom_connect(m);
}
m->varlink_server = sd_varlink_server_unref(m->varlink_server);
m->managed_oom_varlink = sd_varlink_close_unref(m->managed_oom_varlink);
+ m->metrics_varlink_server = sd_varlink_server_unref(m->metrics_varlink_server);
}
void manager_varlink_send_pending_reload_message(Manager *m) {