fields are imported into the local credential store, where they are used to configure and parameterize
the system. For details see below.</para></listitem>
</itemizedlist>
+
+ <para>In addition, when invoked as a Varlink service (i.e. via socket activation through
+ <filename>systemd-imds-metrics.socket</filename>), <command>systemd-imds</command> acts as an
+ <constant>io.systemd.Metrics</constant> provider for
+ <citerefentry><refentrytitle>systemd-report</refentrytitle><manvolnum>1</manvolnum></citerefentry>. It
+ exposes the detected cloud vendor and the well-known <literal>hostname</literal>,
+ <literal>region</literal>, <literal>zone</literal>, <literal>ipv4-public</literal> and
+ <literal>ipv6-public</literal> fields as metrics in the <literal>io.systemd.InstanceMetadata.</literal>
+ namespace. The data is acquired on demand from
+ <citerefentry><refentrytitle>systemd-imdsd@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ benefiting from its local cache.</para>
</refsect1>
<refsect1>
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-imdsd@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-imds-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-report</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
return log_error_errno(r, "Failed to hook in systemd-imds-import.service: %m");
}
+ /* Enable IMDS metrics in case IMDS is supported */
+ r = generator_add_symlink(dest_early, SPECIAL_SOCKETS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/systemd-imds-metrics.socket");
+ if (r < 0)
+ return log_error_errno(r, "Failed to hook in systemd-imds-metrics.socket: %m");
+
return 0;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+#include "sd-varlink.h"
+
+#include "imds-tool.h"
+#include "imds-tool-metrics.h"
+#include "imds-util.h"
+#include "log.h"
+#include "metrics.h"
+#include "string-util.h"
+#include "varlink-io.systemd.Metrics.h"
+#include "varlink-util.h"
+
+#define METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "io.systemd.InstanceMetadata."
+
+static int metric_vendor_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
+ sd_varlink *imds = userdata; /* The live systemd-imdsd connection, or NULL if unavailable */
+ _cleanup_free_ char *vendor = NULL;
+ int r;
+
+ assert(mf && mf->name);
+ assert(vl);
+
+ if (!imds)
+ return 0;
+
+ r = acquire_imds_vendor(imds, &vendor);
+ if (r <= 0)
+ return 0; /* On error or absence simply omit the metric */
+
+ return metric_build_send_string(mf, vl, /* object= */ NULL, vendor, /* fields= */ NULL);
+}
+
+static int metric_well_known_build_json(
+ const MetricFamily *mf,
+ sd_varlink *vl,
+ void *userdata,
+ ImdsWellKnown wk) {
+
+ sd_varlink *imds = userdata;
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ assert(mf && mf->name);
+ assert(vl);
+
+ if (!imds)
+ return 0;
+
+ r = acquire_imds_key_as_string(imds, wk, /* key= */ NULL, &value);
+ if (r <= 0 || isempty(value))
+ return 0; /* Field not supported/set, or error: omit the metric */
+
+ return metric_build_send_string(mf, vl, /* object= */ NULL, value, /* fields= */ NULL);
+}
+
+static int metric_hostname_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
+ return metric_well_known_build_json(mf, vl, userdata, IMDS_HOSTNAME);
+}
+
+static int metric_ipv4_public_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
+ return metric_well_known_build_json(mf, vl, userdata, IMDS_IPV4_PUBLIC);
+}
+
+static int metric_ipv6_public_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
+ return metric_well_known_build_json(mf, vl, userdata, IMDS_IPV6_PUBLIC);
+}
+
+static int metric_region_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
+ return metric_well_known_build_json(mf, vl, userdata, IMDS_REGION);
+}
+
+static int metric_zone_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
+ return metric_well_known_build_json(mf, vl, userdata, IMDS_ZONE);
+}
+
+/* Keep metrics ordered alphabetically */
+static const MetricFamily imds_metric_family_table[] = {
+ {
+ .name = METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "Hostname",
+ .description = "Instance hostname reported by IMDS",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate = metric_hostname_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "IPv4Public",
+ .description = "Public IPv4 address reported by IMDS",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate = metric_ipv4_public_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "IPv6Public",
+ .description = "Public IPv6 address reported by IMDS",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate = metric_ipv6_public_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "Region",
+ .description = "Cloud region reported by IMDS",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate = metric_region_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "Vendor",
+ .description = "Detected cloud vendor",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate = metric_vendor_build_json,
+ },
+ {
+ .name = METRIC_IO_SYSTEMD_INSTANCE_METADATA_PREFIX "Zone",
+ .description = "Cloud availability zone reported by IMDS",
+ .type = METRIC_FAMILY_TYPE_STRING,
+ .generate = metric_zone_build_json,
+ },
+ {}
+};
+
+static int vl_method_metrics_describe(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ return metrics_method_describe(imds_metric_family_table, link, parameters, flags, userdata);
+}
+
+static int vl_method_metrics_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ int r;
+
+ /* Acquire a single connection to systemd-imdsd, shared by all metric generators (so they benefit
+ * from the daemon's token/data cache). If the daemon is unreachable we still serve an empty list
+ * rather than failing the whole request. */
+ _cleanup_(sd_varlink_unrefp) sd_varlink *imds = NULL;
+ r = connect_imdsd(&imds);
+ if (r < 0)
+ log_debug_errno(r, "Failed to connect to systemd-imdsd, serving empty metrics list: %m");
+
+ return metrics_method_list(imds_metric_family_table, link, parameters, flags, imds);
+}
+
+int imds_metrics_run(void) {
+ int r;
+
+ /* Invocation as a Varlink metrics provider (io.systemd.Metrics), typically socket-activated via
+ * /run/systemd/report/io.systemd.InstanceMetadata. */
+
+ _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
+ r = varlink_server_new(&server, /* flags= */ 0, /* userdata= */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_Metrics);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = sd_varlink_server_bind_method_many(
+ server,
+ "io.systemd.Metrics.List", vl_method_metrics_list,
+ "io.systemd.Metrics.Describe", vl_method_metrics_describe);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+ r = sd_varlink_server_loop_auto(server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* Runs systemd-imds as an io.systemd.Metrics provider (socket-activated). */
+int imds_metrics_run(void);
#include "format-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
+#include "imds-tool.h"
+#include "imds-tool-metrics.h"
#include "imds-util.h"
#include "in-addr-util.h"
#include "io-util.h"
return 1;
}
-static int acquire_imds_key_as_string(
+int acquire_imds_key_as_string(
sd_varlink *link,
ImdsWellKnown wk,
const char *key,
return 1;
}
-static int action_summary(sd_varlink *link) {
+int acquire_imds_vendor(sd_varlink *link, char **ret) {
int r;
assert(link);
-
- _cleanup_(table_unrefp) Table *table = table_new_vertical();
- if (!table)
- return log_oom();
+ assert(ret);
const char *error_id = NULL;
sd_json_variant *reply = NULL;
&error_id);
if (r < 0)
return log_error_errno(r, "Failed to issue io.systemd.InstanceMetadata.GetVendorInfo(): %m");
- if (error_id)
+ if (error_id) {
+ if (streq(error_id, "io.systemd.InstanceMetadata.NotSupported")) {
+ *ret = NULL;
+ return 0;
+ }
+
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to issue io.systemd.InstanceMetadata.GetVendorInfo(): %s", error_id);
+ }
const char *vendor = NULL;
static const sd_json_dispatch_field dispatch_table[] = {
- { "vendor", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
+ { "vendor", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
{}
};
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS|SD_JSON_LOG, &vendor);
if (r < 0)
return r;
+ if (isempty(vendor)) {
+ *ret = NULL;
+ return 0;
+ }
+
+ if (strdup_to_full(ret, vendor) < 0)
+ return log_oom();
+
+ return 1;
+}
+
+static int action_summary(sd_varlink *link) {
+ int r;
+
+ assert(link);
+
+ _cleanup_(table_unrefp) Table *table = table_new_vertical();
+ if (!table)
+ return log_oom();
+
+ _cleanup_free_ char *vendor = NULL;
+ r = acquire_imds_vendor(link, &vendor);
+ if (r < 0)
+ return r;
+
if (vendor) {
r = table_add_many(table,
TABLE_FIELD, "Vendor",
return RET_GATHER(ret, import_credentials(data.iov_base));
}
-static int run(int argc, char* argv[]) {
+int connect_imdsd(sd_varlink **ret) {
int r;
- log_setup();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- return r;
+ assert(ret);
_cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
r = sd_varlink_connect_address(&link, "/run/systemd/io.systemd.InstanceMetadata");
return log_error_errno(r, "Failed to connect to imdsd service: %m");
}
+ *ret = TAKE_PTR(link);
+ return 0;
+}
+
+static int run(int argc, char* argv[]) {
+ int r;
+
+ log_setup();
+
+ /* When invoked as a Varlink service (socket activation) we act as an io.systemd.Metrics provider
+ * and run a server loop. The metric generators connect to systemd-imdsd on demand, so we never open
+ * a one-shot connection here. */
+ r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+ if (r > 0)
+ return imds_metrics_run();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
+ r = connect_imdsd(&link);
+ if (r < 0)
+ return r;
+
switch (arg_action) {
case ACTION_SUMMARY:
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "imds-util.h"
+#include "shared-forward.h"
+
+/* Helpers shared between imds-tool.c and imds-tool-metrics.c for talking to systemd-imdsd. */
+
+int connect_imdsd(sd_varlink **ret);
+int acquire_imds_key_as_string(sd_varlink *link, ImdsWellKnown wk, const char *key, char **ret);
+int acquire_imds_vendor(sd_varlink *link, char **ret);
libexec_template + {
'name' : 'systemd-imds',
'public' : true,
- 'sources' : files('imds-tool.c'),
+ 'sources' : files(
+ 'imds-tool.c',
+ 'imds-tool-metrics.c'),
'objects' : ['systemd-imdsd'],
},
generator_template + {
'file' : 'systemd-imds-import.service.in',
'conditions' : ['ENABLE_IMDS'],
},
+ {
+ 'file' : 'systemd-imds-metrics.socket',
+ 'conditions' : ['ENABLE_IMDS'],
+ },
+ {
+ 'file' : 'systemd-imds-metrics@.service.in',
+ 'conditions' : ['ENABLE_IMDS'],
+ },
{
'file' : 'systemd-importd.service.in',
'conditions' : ['ENABLE_IMPORTD'],
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Cloud Instance Metadata Metrics
+Documentation=man:systemd-imds(1)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/report/io.systemd.InstanceMetadata
+FileDescriptorName=varlink
+SocketMode=0666
+Accept=yes
+MaxConnectionsPerSource=16
+RemoveOnStop=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Cloud Instance Metadata Metrics
+Documentation=man:systemd-imds(1)
+DefaultDependencies=no
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+After=systemd-imdsd.socket
+
+[Service]
+# Runs systemd-imds as an io.systemd.Metrics provider. It connects to
+# systemd-imdsd to acquire the actual data. Runs as root so the daemon's Polkit
+# check on io.systemd.InstanceMetadata.Get() passes.
+ExecStart={{LIBEXECDIR}}/systemd-imds
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes