/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
+#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include "sd-device.h"
/* fields= */ NULL);
}
+static int load_average_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
+ enum {
+ LOAD_AVERAGE_FIELD_1MIN,
+ LOAD_AVERAGE_FIELD_5MIN,
+ LOAD_AVERAGE_FIELD_15MIN,
+ _LOAD_AVERAGE_FIELD_MAX,
+ };
+
+ int r;
+
+ assert(mf && mf->name);
+ assert(link);
+
+ /* The classic Linux load average, i.e. the exponentially damped moving average of the number of
+ * runnable plus uninterruptible tasks over the last 1, 5 and 15 minutes. The kernel exposes these as
+ * fixed-point numbers shifted left by SI_LOAD_SHIFT bits. */
+
+ struct sysinfo info;
+ if (sysinfo(&info) < 0)
+ return log_debug_errno(errno, "Failed to call sysinfo(): %m");
+
+ assert_cc(_LOAD_AVERAGE_FIELD_MAX == ELEMENTSOF(info.loads));
+
+ for (size_t i = 0; i < _LOAD_AVERAGE_FIELD_MAX; i++) {
+
+ r = metric_build_send_double(
+ mf + i,
+ link,
+ /* object= */ NULL,
+ (double) info.loads[i] / (UINT64_C(1) << SI_LOAD_SHIFT),
+ /* fields= */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int swap_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
+ assert(mf && mf->name);
+ assert(link);
+
+ /* The total amount of configured swap space, in bytes. */
+
+ struct sysinfo info;
+ if (sysinfo(&info) < 0)
+ return log_debug_errno(errno, "Failed to call sysinfo(): %m");
+
+ /* Overflow is unrealistic (would need >16 EiB of swap), but use MUL_SAFE to make this obvious to
+ * static analyzers. */
+ uint64_t swap;
+ assert_se(MUL_SAFE(&swap, (uint64_t) info.totalswap, (uint64_t) info.mem_unit));
+
+ return metric_build_send_unsigned(
+ mf,
+ link,
+ /* object= */ NULL,
+ swap,
+ /* fields= */ NULL);
+}
+
enum {
FIELD_PRETTY_NAME,
FIELD_NAME,
METRIC_FAMILY_TYPE_STRING,
.generate = kernel_version_generate,
},
+ {
+ METRIC_IO_SYSTEMD_BASIC_PREFIX "LoadAverage1Min",
+ "System load average over the last 1 minute",
+ METRIC_FAMILY_TYPE_GAUGE,
+ .generate = load_average_generate,
+ },
+ {
+ METRIC_IO_SYSTEMD_BASIC_PREFIX "LoadAverage5Min",
+ "System load average over the last 5 minutes",
+ METRIC_FAMILY_TYPE_GAUGE,
+ .generate = NULL,
+ },
+ {
+ METRIC_IO_SYSTEMD_BASIC_PREFIX "LoadAverage15Min",
+ "System load average over the last 15 minutes",
+ METRIC_FAMILY_TYPE_GAUGE,
+ .generate = NULL,
+ },
+ /* Keep those ↑ in sync with load_average_generate(). */
{
METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineID",
"Machine ID",
SMBIOS_STANDARD_FIELD("ChassisSerialNumber"),
SMBIOS_STANDARD_FIELD("ChassisAssetTagNumber"),
/* Keep those ↑ in sync with smbios_generate(). */
+ {
+ METRIC_IO_SYSTEMD_BASIC_PREFIX "SwapBytes",
+ "Total configured swap space in bytes",
+ METRIC_FAMILY_TYPE_GAUGE,
+ .generate = swap_generate,
+ },
{
METRIC_IO_SYSTEMD_BASIC_PREFIX "TPM2.Manufacturer",
"TPM2 device manufacturer (ID_TPM2_MANUFACTURER property of the tpmrm0 device)",
return metric_build_send(mf, link, object, v, fields);
}
+
+int metric_build_send_double(
+ const MetricFamily *mf,
+ sd_varlink *link,
+ const char *object,
+ double 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_real(&v, value);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to allocate JSON real: %m");
+
+ return metric_build_send(mf, link, object, v, fields);
+}
id2="$(. /etc/os-release; echo "$ID")"
[ "$id1" = "$id2" ]
+# test io.systemd.Basic load average and swap metrics
+basic_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {})"
+# NB: '| tostring' turns the numeric value into a raw string, so 'jq -r' prints it cleanly
+# (numbers, unlike strings, are otherwise still emitted with the json-seq record separator).
+basic_value() { echo "$basic_metrics" | jq --seq -r "select(.name == \"$1\") | .value | tostring"; }
+
+# The three classic load average fields must be present and numeric (jq's 'numbers' filter
+# emits the value only if it is a JSON number, so a non-empty result confirms both).
+for field in LoadAverage1Min LoadAverage5Min LoadAverage15Min; do
+ loadavg="$(echo "$basic_metrics" | jq --seq -r "select(.name == \"io.systemd.Basic.$field\") | .value | numbers | tostring")"
+ test -n "$loadavg"
+done
+
+# SwapBytes must match the total the kernel reports in /proc/meminfo (which is in kB).
+swap_reported="$(basic_value io.systemd.Basic.SwapBytes)"
+swap_expected=$(( $(awk '/^SwapTotal:/ { print $2; found=1 } END { if (!found) print 0 }' /proc/meminfo) * 1024 ))
+[ "$swap_reported" = "$swap_expected" ]
+
# test io.systemd.Basic.MachineInfo.* metrics, sourced from /etc/machine-info
if [ -e /etc/machine-info ]; then
MACHINE_INFO_BACKUP="$(mktemp)"