summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/plugins/in_podman_metrics
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:18 +0000
commit5da14042f70711ea5cf66e034699730335462f66 (patch)
tree0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/fluent-bit/plugins/in_podman_metrics
parentReleasing debian version 1.44.3-2. (diff)
downloadnetdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz
netdata-5da14042f70711ea5cf66e034699730335462f66.zip
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fluent-bit/plugins/in_podman_metrics')
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/CMakeLists.txt6
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/podman_metrics.c515
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/podman_metrics.h98
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/podman_metrics_config.h211
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c407
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.h51
6 files changed, 1288 insertions, 0 deletions
diff --git a/src/fluent-bit/plugins/in_podman_metrics/CMakeLists.txt b/src/fluent-bit/plugins/in_podman_metrics/CMakeLists.txt
new file mode 100644
index 000000000..9de0e5331
--- /dev/null
+++ b/src/fluent-bit/plugins/in_podman_metrics/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(src
+ podman_metrics.c
+ podman_metrics_data.c
+ )
+
+FLB_PLUGIN(in_podman_metrics "${src}" "")
diff --git a/src/fluent-bit/plugins/in_podman_metrics/podman_metrics.c b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics.c
new file mode 100644
index 000000000..df64452ff
--- /dev/null
+++ b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics.c
@@ -0,0 +1,515 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_input_plugin.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_metrics.h>
+#include <fluent-bit/flb_metrics_exporter.h>
+#include <fluent-bit/flb_jsmn.h>
+
+#include <monkey/mk_core/mk_list.h>
+
+#include "podman_metrics.h"
+#include "podman_metrics_config.h"
+#include "podman_metrics_data.h"
+
+/*
+ * Collect information about podman containers (ID and Name) from podman configuration
+ * file (default is /var/lib/containers/storage/overlay-containers/containers.json).
+ * Since flb_jsmn library show JSON as a tree, search for objects with parent 0 (objects
+ * that are children to root array, and in them, search for ID and name (which is also
+ * an array.
+ */
+static int collect_container_data(struct flb_in_metrics *ctx)
+{
+ /* Buffers for reading data from JSON */
+ char *buffer;
+ char name[CONTAINER_NAME_SIZE];
+ char id[CONTAINER_ID_SIZE];
+ char image_name[IMAGE_NAME_SIZE];
+ char metadata[CONTAINER_METADATA_SIZE];
+ char *metadata_token_start;
+ char *metadata_token_stop;
+ int metadata_token_size;
+
+ int array_id;
+ int r, i, j;
+ size_t read_bytes = 0;
+ int collected_containers = 0;
+ int token_len;
+
+ jsmn_parser p;
+ jsmntok_t t[JSON_TOKENS];
+
+ flb_utils_read_file(ctx->config, &buffer, &read_bytes);
+ if (!read_bytes) {
+ flb_plg_warn(ctx->ins, "Failed to open %s", ctx->config);
+ return -1;
+ }
+ buffer[read_bytes] = 0;
+ flb_plg_debug(ctx->ins, "Read %zu bytes", read_bytes);
+
+ jsmn_init(&p);
+ r = jsmn_parse(&p, buffer, strlen(buffer), t, sizeof(t) / sizeof(t[0]));
+ if (r < 0) {
+ flb_plg_warn(ctx->ins, "Failed to parse JSON %d: %s", r, buffer);
+ free(buffer);
+ return -1;
+ }
+
+ flb_plg_debug(ctx->ins, "Got %d nested tokens", t[0].size);
+
+ if (r < 1 || t[0].type != JSMN_ARRAY) {
+ flb_plg_warn(ctx->ins, "Expected array at the json root");
+ free(buffer);
+ return -1;
+ }
+
+ for (i=0; i<r; i++) {
+ if (t[i].type == JSMN_STRING) {
+ if (sizeof(JSON_FIELD_ID)-1 == t[i].end - t[i].start &&
+ strncmp(buffer + t[i].start, JSON_FIELD_ID, t[i].end - t[i].start) == 0) {
+ token_len = t[i + 1].end - t[i + 1].start;
+ strncpy(id, buffer + t[i+1].start, t[i + 1].end - t[i + 1].start);
+ id[token_len] = '\0';
+ flb_plg_trace(ctx->ins, "Found id %s", id);
+ }
+ else if (sizeof(JSON_FIELD_NAMES)-1 == t[i].end - t[i].start &&
+ strncmp(buffer + t[i].start, JSON_FIELD_NAMES, t[i].end - t[i].start) == 0) {
+ array_id = i + 1;
+ if (t[array_id].type == JSMN_ARRAY) {
+ j = array_id + 1;
+ while (t[j].parent == array_id)
+ {
+ strncpy(name, buffer + t[j].start, t[j].end - t[j].start);
+ name[t[j].end - t[j].start] = '\0';
+ flb_plg_trace(ctx->ins, "Found name %s", name);
+ j++;
+ }
+ }
+ }
+ else if (sizeof(JSON_FIELD_METADATA)-1 == t[i].end - t[i].start &&
+ strncmp(buffer + t[i].start, JSON_FIELD_METADATA, t[i].end - t[i].start) == 0) {
+ token_len = t[i + 1].end - t[i + 1].start;
+ strncpy(metadata, buffer + t[i+1].start, t[i + 1].end - t[i + 1].start);
+ metadata[token_len] = '\0';
+
+ metadata_token_start = strstr(metadata, JSON_SUBFIELD_IMAGE_NAME);
+ if (metadata_token_start) {
+ metadata_token_stop = strstr(metadata_token_start + JSON_SUBFIELD_SIZE_IMAGE_NAME+1, "\\\"");
+ metadata_token_size = metadata_token_stop - metadata_token_start - JSON_SUBFIELD_SIZE_IMAGE_NAME;
+
+ strncpy(image_name, metadata_token_start+JSON_SUBFIELD_SIZE_IMAGE_NAME, metadata_token_size);
+ image_name[metadata_token_size] = '\0';
+
+ flb_plg_trace(ctx->ins, "Found image name %s", image_name);
+ add_container_to_list(ctx, id, name, image_name);
+ }
+ else {
+ flb_plg_warn(ctx->ins, "Image name was not found for %s", id);
+ add_container_to_list(ctx, id, name, "unknown");
+ }
+ collected_containers++;
+ }
+ }
+ }
+
+ flb_plg_debug(ctx->ins, "Collected %d containers from podman config file", collected_containers);
+ free(buffer);
+ return collected_containers;
+}
+
+/*
+ * Create structure instance based on previously found id, name and image name. Set all its values (like
+ * memory or cpu to UINT64_MAX, in case it won't be found later. This function also adds this structure
+ * to internal list, so it can be found by iteration later on.
+ */
+static int add_container_to_list(struct flb_in_metrics *ctx, flb_sds_t id, flb_sds_t name, flb_sds_t image_name)
+{
+ struct container *cnt;
+ cnt = flb_malloc(sizeof(struct container));
+ if (!cnt) {
+ flb_errno();
+ return -1;
+ }
+ cnt->id = flb_sds_create(id);
+ cnt->name = flb_sds_create(name);
+ cnt->image_name = flb_sds_create(image_name);
+
+ cnt->memory_usage = UINT64_MAX;
+ cnt->memory_max_usage = UINT64_MAX;
+ cnt->memory_limit = UINT64_MAX;
+ cnt->rss = UINT64_MAX;
+ cnt->cpu_user = UINT64_MAX;
+ cnt->cpu = UINT64_MAX;
+
+ mk_list_init(&cnt->net_data);
+
+ mk_list_add(&cnt->_head, &ctx->items);
+ return 0;
+}
+
+/*
+ * Iterate over container list and remove collected data
+ */
+static int destroy_container_list(struct flb_in_metrics *ctx)
+{
+ struct container *cnt;
+ struct net_iface *iface;
+ struct sysfs_path *pth;
+ struct mk_list *head;
+ struct mk_list *tmp;
+ struct mk_list *inner_head;
+ struct mk_list *inner_tmp;
+
+ mk_list_foreach_safe(head, tmp, &ctx->items) {
+ cnt = mk_list_entry(head, struct container, _head);
+ flb_plg_debug(ctx->ins, "Destroying container data (id: %s, name: %s", cnt->id, cnt->name);
+
+ flb_sds_destroy(cnt->id);
+ flb_sds_destroy(cnt->name);
+ flb_sds_destroy(cnt->image_name);
+ mk_list_foreach_safe(inner_head, inner_tmp, &cnt->net_data) {
+ iface = mk_list_entry(inner_head, struct net_iface, _head);
+ flb_sds_destroy(iface->name);
+ mk_list_del(&iface->_head);
+ flb_free(iface);
+ }
+ mk_list_del(&cnt->_head);
+ flb_free(cnt);
+ }
+
+ mk_list_foreach_safe(head, tmp, &ctx->sysfs_items) {
+ pth = mk_list_entry(head, struct sysfs_path, _head);
+ flb_plg_trace(ctx->ins, "Destroying sysfs data (name: %s", pth->path);
+ flb_sds_destroy(pth->path);
+ mk_list_del(&pth->_head);
+ flb_free(pth);
+ }
+ return 0;
+}
+
+
+/*
+ * Create counter for given metric name, using name, image name and value as counter labels. Counters
+ * are created per counter name, so they are "shared" between multiple containers - counter
+ * name remains the same, only labels like ID are changed.
+ * This function creates counter only once per counter name - every next call only sets counter
+ * value for specific labels.
+ */
+static int create_counter(struct flb_in_metrics *ctx, struct cmt_counter **counter, flb_sds_t id, flb_sds_t name, flb_sds_t image_name, flb_sds_t metric_prefix,
+ flb_sds_t *fields, flb_sds_t metric_name, flb_sds_t description, flb_sds_t interface, uint64_t value)
+{
+ flb_sds_t *labels;
+ uint64_t fvalue = value;
+
+ int label_count;
+ if (value == UINT64_MAX) {
+ flb_plg_debug(ctx->ins, "Ignoring invalid counter for %s, %s_%s_%s", name, COUNTER_PREFIX, metric_prefix, metric_name);
+ return -1;
+ }
+
+ if (strcmp(metric_name, COUNTER_CPU) == 0 || strcmp(metric_name, COUNTER_CPU_USER) == 0) {
+ fvalue = fvalue / 1000000000;
+ flb_plg_trace(ctx->ins, "Converting %s from nanoseconds to seconds (%lu -> %lu)", metric_name, value, fvalue);
+
+ }
+
+ if (interface == NULL) {
+ labels = (char *[]){id, name, image_name};
+ label_count = 3;
+ }
+ else {
+ labels = (char *[]){id, name, image_name, interface};
+ label_count = 4;
+ }
+
+ /* if counter was not yet created, it means that this function is called for the first time per counter type */
+ if (*counter == NULL) {
+ flb_plg_debug(ctx->ins, "Creating counter for %s, %s_%s_%s", name, COUNTER_PREFIX, metric_prefix, metric_name);
+ *counter = cmt_counter_create(ctx->ins->cmt, COUNTER_PREFIX, metric_prefix, metric_name, description, label_count, fields);
+ }
+
+ /* Allow setting value that is not grater that current one (if, for example, memory usage stays exactly the same) */
+ cmt_counter_allow_reset(*counter);
+ flb_plg_debug(ctx->ins, "Set counter for %s, %s_%s_%s: %lu", name, COUNTER_PREFIX, metric_prefix, metric_name, fvalue);
+ if (cmt_counter_set(*counter, cfl_time_now(), fvalue, label_count, labels) == -1) {
+ flb_plg_warn(ctx->ins, "Failed to set counter for %s, %s_%s_%s", name, COUNTER_PREFIX, metric_prefix, metric_name);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Create gauge for given metric name, using name, image name and value as counter labels. Gauges
+ * are created per counter name, so they are "shared" between multiple containers - counter
+ * name remains the same, only labels like ID are changed.
+ * This function creates gauge only once per counter name - every next call only sets gauge
+ * value for specific labels.
+ */
+static int create_gauge(struct flb_in_metrics *ctx, struct cmt_gauge **gauge, flb_sds_t id, flb_sds_t name, flb_sds_t image_name, flb_sds_t metric_prefix,
+ flb_sds_t *fields, flb_sds_t metric_name, flb_sds_t description, flb_sds_t interface, uint64_t value)
+{
+ flb_sds_t *labels;
+ int label_count;
+ if (value == UINT64_MAX) {
+ flb_plg_debug(ctx->ins, "Ignoring invalid gauge for %s, %s_%s_%s", name, COUNTER_PREFIX, metric_prefix, metric_name);
+ return -1;
+ }
+
+ labels = (char *[]){id, name, image_name};
+ label_count = 3;
+
+ /* if gauge was not yet created, it means that this function is called for the first time per counter type */
+ if (*gauge == NULL) {
+ flb_plg_debug(ctx->ins, "Creating gauge for %s, %s_%s_%s", name, COUNTER_PREFIX, metric_prefix, metric_name);
+ *gauge = cmt_gauge_create(ctx->ins->cmt, COUNTER_PREFIX, metric_prefix, metric_name, description, label_count, fields);
+ }
+
+ flb_plg_debug(ctx->ins, "Set gauge for %s, %s_%s_%s: %lu", name, COUNTER_PREFIX, metric_prefix, metric_name, value);
+ if (cmt_gauge_set(*gauge, cfl_time_now(), value, label_count, labels) == -1) {
+ flb_plg_warn(ctx->ins, "Failed to set gauge for %s, %s_%s_%s", name, COUNTER_PREFIX, metric_prefix, metric_name);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Call create_counter for every counter type defined in this plugin.
+ *
+ * Currently supported counters are:
+ * - container_memory_usage_bytes
+ * - container_memory_max_usage_bytes
+ * - container_memory_rss
+ * - container_spec_memory_limit_bytes
+ * - container_cpu_user_seconds_total
+ * - container_cpu_usage_seconds_total
+ * - container_network_receive_bytes_total
+ * - container_network_receive_errors_total
+ * - container_network_transmit_bytes_total
+ * - container_network_transmit_errors_total
+ */
+static int create_counters(struct flb_in_metrics *ctx)
+{
+ struct container *cnt;
+ struct net_iface *iface;
+ struct mk_list *head;
+ struct mk_list *tmp;
+ struct mk_list *inner_head;
+ struct mk_list *inner_tmp;
+
+ mk_list_foreach_safe(head, tmp, &ctx->items)
+ {
+ cnt = mk_list_entry(head, struct container, _head);
+ create_counter(ctx, &ctx->c_memory_usage, cnt->id, cnt->name, cnt->image_name, COUNTER_MEMORY_PREFIX, FIELDS_METRIC, COUNTER_MEMORY_USAGE,
+ DESCRIPTION_MEMORY_USAGE, NULL, cnt->memory_usage);
+ create_counter(ctx, &ctx->c_memory_max_usage, cnt->id, cnt->name, cnt->image_name, COUNTER_MEMORY_PREFIX, FIELDS_METRIC, COUNTER_MEMORY_MAX_USAGE,
+ DESCRIPTION_MEMORY_MAX_USAGE, NULL, cnt->memory_max_usage);
+ create_counter(ctx, &ctx->c_memory_limit, cnt->id, cnt->name, cnt->image_name, COUNTER_SPEC_MEMORY_PREFIX, FIELDS_METRIC, COUNTER_MEMORY_LIMIT,
+ DESCRIPTION_MEMORY_LIMIT, NULL, cnt->memory_limit);
+ create_gauge(ctx, &ctx->g_rss, cnt->id, cnt->name, cnt->image_name, COUNTER_MEMORY_PREFIX, FIELDS_METRIC, GAUGE_MEMORY_RSS,
+ DESCRIPTION_MEMORY_RSS, NULL, cnt->rss);
+ create_counter(ctx, &ctx->c_cpu_user, cnt->id, cnt->name, cnt->image_name, COUNTER_CPU_PREFIX, FIELDS_METRIC, COUNTER_CPU_USER,
+ DESCRIPTION_CPU_USER, NULL, cnt->cpu_user);
+ create_counter(ctx, &ctx->c_cpu, cnt->id, cnt->name, cnt->image_name, COUNTER_CPU_PREFIX, FIELDS_METRIC, COUNTER_CPU,
+ DESCRIPTION_CPU, NULL, cnt->cpu);
+ mk_list_foreach_safe(inner_head, inner_tmp, &cnt->net_data)
+ {
+ iface = mk_list_entry(inner_head, struct net_iface, _head);
+ create_counter(ctx, &ctx->rx_bytes, cnt->id, cnt->name, cnt->image_name, COUNTER_NETWORK_PREFIX, FIELDS_METRIC_WITH_IFACE, COUNTER_RX_BYTES,
+ DESCRIPTION_RX_BYTES, iface->name, iface->rx_bytes);
+ create_counter(ctx, &ctx->rx_errors, cnt->id, cnt->name, cnt->image_name, COUNTER_NETWORK_PREFIX, FIELDS_METRIC_WITH_IFACE, COUNTER_RX_ERRORS,
+ DESCRIPTION_RX_ERRORS, iface->name, iface->rx_errors);
+ create_counter(ctx, &ctx->tx_bytes, cnt->id, cnt->name, cnt->image_name, COUNTER_NETWORK_PREFIX, FIELDS_METRIC_WITH_IFACE, COUNTER_TX_BYTES,
+ DESCRIPTION_TX_BYTES, iface->name, iface->tx_bytes);
+ create_counter(ctx, &ctx->tx_errors, cnt->id, cnt->name, cnt->image_name, COUNTER_NETWORK_PREFIX, FIELDS_METRIC_WITH_IFACE, COUNTER_TX_ERRORS,
+ DESCRIPTION_TX_ERRORS, iface->name, iface->tx_errors);
+ }
+ }
+ return 0;
+}
+
+/* Main function. Destroy (optionally) previous data, gather container data and
+ * create counters.
+ */
+static int scrape_metrics(struct flb_config *config, struct flb_in_metrics *ctx)
+{
+ uint64_t start_ts = cfl_time_now();
+ flb_plg_debug(ctx->ins, "Starting to scrape podman metrics");
+ if (destroy_container_list(ctx) == -1) {
+ flb_plg_error(ctx->ins, "Could not destroy previous container data");
+ return -1;
+ }
+
+ if (collect_container_data(ctx) == -1) {
+ flb_plg_error(ctx->ins, "Could not collect container ids");
+ return -1;
+ }
+
+ if (collect_sysfs_directories(ctx, ctx->sysfs_path) == -1)
+ {
+ flb_plg_error(ctx->ins, "Could not collect sysfs data");
+ return -1;
+ }
+
+ if (ctx->cgroup_version == CGROUP_V1) {
+ if (fill_counters_with_sysfs_data_v1(ctx) == -1) {
+ flb_plg_error(ctx->ins, "Could not collect V1 sysfs data");
+ return -1;
+ }
+ }
+ else if (ctx->cgroup_version == CGROUP_V2) {
+ if (fill_counters_with_sysfs_data_v2(ctx) == -1) {
+ flb_plg_error(ctx->ins, "Could not collect V2 sysfs data");
+ return -1;
+ }
+ }
+
+ if (create_counters(ctx) == -1) {
+ flb_plg_error(ctx->ins, "Could not create container counters");
+ return -1;
+ }
+
+ if (flb_input_metrics_append(ctx->ins, NULL, 0, ctx->ins->cmt) == -1) {
+ flb_plg_error(ctx->ins, "Could not append metrics");
+ return -1;
+ }
+
+ flb_plg_info(ctx->ins, "Scraping metrics took %luns", cfl_time_now() - start_ts);
+ return 0;
+}
+
+/*
+ * Call scrape_metrics function every `scrape interval`.
+ */
+static int cb_metrics_collect_runtime(struct flb_input_instance *ins, struct flb_config *config, void *in_context)
+{
+ return scrape_metrics(config, in_context);
+}
+
+/*
+ * Initialize plugin, setup config file path and (optionally) scrape container
+ * data (if `scrape_at_start` is set).
+ */
+static int in_metrics_init(struct flb_input_instance *in, struct flb_config *config, void *data)
+{
+ struct flb_in_metrics *ctx;
+ int coll_fd_runtime;
+
+ ctx = flb_calloc(1, sizeof(struct flb_in_metrics));
+ if (!ctx) {
+ return -1;
+ }
+ ctx->ins = in;
+
+ ctx->c_memory_usage = NULL;
+ ctx->c_memory_max_usage = NULL;
+ ctx->g_rss = NULL;
+ ctx->c_memory_limit = NULL;
+ ctx->c_cpu_user = NULL;
+ ctx->c_cpu = NULL;
+ ctx->rx_bytes = NULL;
+ ctx->rx_errors = NULL;
+ ctx->tx_bytes = NULL;
+ ctx->tx_errors = NULL;
+
+ if (flb_input_config_map_set(in, (void *) ctx) == -1) {
+ flb_free(ctx);
+ return -1;
+ }
+
+ flb_input_set_context(in, ctx);
+ coll_fd_runtime = flb_input_set_collector_time(in, cb_metrics_collect_runtime, ctx->scrape_interval, 0, config);
+ if (coll_fd_runtime == -1) {
+ flb_plg_error(ctx->ins, "Could not set collector for podman metrics plugin");
+ return -1;
+ }
+ ctx->coll_fd_runtime = coll_fd_runtime;
+
+ if (ctx->podman_config_path) {
+ flb_plg_info(ctx->ins, "Using config file %s", ctx->podman_config_path);
+ ctx->config = flb_sds_create(ctx->podman_config_path);
+ }
+ else {
+ flb_plg_info(ctx->ins, "Using default config file %s", PODMAN_CONFIG_DEFAULT_PATH);
+ ctx->config = flb_sds_create(PODMAN_CONFIG_DEFAULT_PATH);
+ }
+
+ if (get_cgroup_version(ctx) == CGROUP_V2) {
+ flb_plg_info(ctx->ins, "Detected cgroups v2");
+ ctx->cgroup_version = CGROUP_V2;
+ }
+ else {
+ flb_plg_info(ctx->ins, "Detected cgroups v1");
+ ctx->cgroup_version = CGROUP_V1;
+ }
+
+ mk_list_init(&ctx->items);
+ mk_list_init(&ctx->sysfs_items);
+
+ if (ctx->scrape_interval >= 2 && ctx->scrape_on_start) {
+ flb_plg_info(ctx->ins, "Generating podman metrics (initial scrape)");
+ if (scrape_metrics(config, ctx) == -1) {
+ flb_plg_error(ctx->ins, "Could not start collector for podman metrics plugin");
+ flb_sds_destroy(ctx->config);
+ destroy_container_list(ctx);
+ flb_free(ctx);
+ return -1;
+ }
+ }
+
+ flb_plg_info(ctx->ins, "Generating podman metrics");
+
+ return 0;
+}
+
+/*
+ * Function called at plugin exit - destroy collected container data list.
+ */
+static int in_metrics_exit(void *data, struct flb_config *config)
+{
+ struct flb_in_metrics *ctx = data;
+
+ if (!ctx) {
+ return 0;
+ }
+
+ flb_sds_destroy(ctx->config);
+ destroy_container_list(ctx);
+ flb_free(ctx);
+ return 0;
+}
+
+/*
+ * Function called at plugin pause.
+ */
+static void in_metrics_pause(void *data, struct flb_config *config)
+{
+ struct flb_in_metrics *ctx = data;
+ flb_input_collector_pause(ctx->coll_fd_runtime, ctx->ins);
+}
+
+/*
+ * Function called at plugin resume.
+ */
+static void in_metrics_resume(void *data, struct flb_config *config)
+{
+ struct flb_in_metrics *ctx = data;
+ flb_input_collector_resume(ctx->coll_fd_runtime, ctx->ins);
+}
diff --git a/src/fluent-bit/plugins/in_podman_metrics/podman_metrics.h b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics.h
new file mode 100644
index 000000000..3b02d24ed
--- /dev/null
+++ b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_IN_PODMAN_METRICS_H
+#define FLB_IN_PODMAN_METRICS_H
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_input.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_jsmn.h>
+
+#include <monkey/mk_core/mk_list.h>
+
+#include "podman_metrics_config.h"
+
+static int collect_container_data(struct flb_in_metrics *ctx);
+static int add_container_to_list(struct flb_in_metrics *ctx, flb_sds_t id, flb_sds_t name, flb_sds_t image_name);
+static int destroy_container_list(struct flb_in_metrics *ctx);
+
+static int create_counter(struct flb_in_metrics *ctx, struct cmt_counter **counter, flb_sds_t id, flb_sds_t name, flb_sds_t image_name, flb_sds_t metric_prefix,
+ flb_sds_t *fieds, flb_sds_t metric_name, flb_sds_t description, flb_sds_t interface, uint64_t value);
+static int create_gauge(struct flb_in_metrics *ctx, struct cmt_gauge **gauge, flb_sds_t id, flb_sds_t name, flb_sds_t image_name, flb_sds_t metric_prefix,
+ flb_sds_t *fields, flb_sds_t metric_name, flb_sds_t description, flb_sds_t interface, uint64_t value);
+static int create_counters(struct flb_in_metrics *ctx);
+
+static int scrape_metrics(struct flb_config *config, struct flb_in_metrics *ctx);
+
+static int cb_metrics_collect_runtime(struct flb_input_instance *ins, struct flb_config *config, void *in_context);
+static int in_metrics_init(struct flb_input_instance *in, struct flb_config *config, void *data);
+static int in_metrics_exit(void *data, struct flb_config *config);
+static void in_metrics_pause(void *data, struct flb_config *config);
+static void in_metrics_resume(void *data, struct flb_config *config);
+
+
+static struct flb_config_map config_map[] = {
+ {
+ FLB_CONFIG_MAP_TIME, "scrape_interval", "30",
+ 0, FLB_TRUE, offsetof(struct flb_in_metrics, scrape_interval),
+ "Scrape interval to collect the metrics of podman containers"
+ "(defaults to 30s)"
+ },
+
+ {
+ FLB_CONFIG_MAP_BOOL, "scrape_on_start", "false",
+ 0, FLB_TRUE, offsetof(struct flb_in_metrics, scrape_on_start),
+ "Scrape metrics upon start, useful to avoid waiting for 'scrape_interval' "
+ "for the first round of metrics."
+ },
+ {
+ FLB_CONFIG_MAP_STR, "path.config", NULL,
+ 0, FLB_TRUE, offsetof(struct flb_in_metrics, podman_config_path),
+ "Path to podman config file"
+ },
+ {
+ FLB_CONFIG_MAP_STR, "path.sysfs", SYSFS_PATH,
+ 0, FLB_TRUE, offsetof(struct flb_in_metrics, sysfs_path),
+ "Path to sysfs subsystem directory"
+ },
+ {
+ FLB_CONFIG_MAP_STR, "path.procfs", PROCFS_PATH,
+ 0, FLB_TRUE, offsetof(struct flb_in_metrics, procfs_path),
+ "Path to proc subsystem directory"
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_input_plugin in_podman_metrics_plugin = {
+ .name = "podman_metrics",
+ .description = "Podman metrics",
+ .cb_init = in_metrics_init,
+ .cb_pre_run = NULL,
+ .cb_flush_buf = NULL,
+ .config_map = config_map,
+ .cb_pause = in_metrics_pause,
+ .cb_resume = in_metrics_resume,
+ .cb_exit = in_metrics_exit
+};
+
+#endif
diff --git a/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_config.h b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_config.h
new file mode 100644
index 000000000..fabdc0a8d
--- /dev/null
+++ b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_config.h
@@ -0,0 +1,211 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_IN_PODMAN_METRICS_CONFIG_H
+#define FLB_IN_PODMAN_METRICS_CONFIG_H
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_input.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_jsmn.h>
+
+#include <monkey/mk_core/mk_list.h>
+
+/* Buffers and sizes */
+#define JSON_TOKENS 2048
+#define CONTAINER_NAME_SIZE 50
+#define CONTAINER_ID_SIZE 80
+#define CONTAINER_METADATA_SIZE 512
+#define IMAGE_NAME_SIZE 512
+#define PID_BUFFER_SIZE 21
+#define SYSFS_FILE_PATH_SIZE 512
+#define PROCFS_FILE_PATH_SIZE 512
+#define CGROUP_PATH_SIZE 25
+
+/* Special paths for sysfs traversal */
+#define CURRENT_DIR "."
+#define PREV_DIR ".."
+
+/* Ignored network interfaces */
+#define VETH_INTERFACE "veth"
+
+#define JSON_FIELD_NAMES "names"
+#define JSON_FIELD_ID "id"
+#define JSON_FIELD_METADATA "metadata"
+
+#define JSON_SUBFIELD_IMAGE_NAME "image-name\\\":\\\""
+#define JSON_SUBFIELD_SIZE_IMAGE_NAME 15
+
+#define CGROUP_V2_PATH "cgroup.controllers"
+#define CGROUP_V1 1
+#define CGROUP_V2 2
+
+/* Paths in /proc subsystem */
+#define PROCFS_PATH "/proc"
+#define PROC_NET_SUFFIX "net/dev"
+
+/* Paths in /sys subsystem */
+#define SYSFS_PATH "/sys/fs/cgroup"
+#define V1_SYSFS_MEMORY "memory"
+#define V1_SYSFS_CPU "cpuacct"
+#define V1_SYSFS_SYSTEMD "systemd"
+#define SYSFS_CONTAINER_PREFIX "libpod"
+#define SYSFS_LIBPOD_PARENT "libpod_parent"
+#define SYSFS_CONMON "conmon"
+
+/* Default podman config file path, in case of not provided one */
+#define PODMAN_CONFIG_DEFAULT_PATH "/var/lib/containers/storage/overlay-containers/containers.json"
+
+/* Markers of network values in /proc/<pid>/dev/net */
+#define DEV_NET_IGNORE_LINES 2
+#define DEV_NET_NAME 0
+#define DEV_NET_RX_BYTES 1
+#define DEV_NET_RX_ERRORS 3
+#define DEV_NET_TX_BYTES 9
+#define DEV_NET_TX_ERRORS 11
+
+/* Key names in .stat files */
+#define STAT_KEY_RSS "rss"
+#define STAT_KEY_CPU "usage_usec"
+#define STAT_KEY_CPU_USER "user_usec"
+
+/* Static lists of fields in counters or gauges */
+#define FIELDS_METRIC (char*[3]){"id", "name", "image" }
+#define FIELDS_METRIC_WITH_IFACE (char*[4]){"id", "name", "image", "interface" }
+
+/* Files from sysfs containing required data (cgroups v1) */
+#define V1_SYSFS_FILE_MEMORY "memory.usage_in_bytes"
+#define V1_SYSFS_FILE_MAX_MEMORY "memory.max_usage_in_bytes"
+#define V1_SYSFS_FILE_MEMORY_STAT "memory.stat"
+#define V1_SYSFS_FILE_MEMORY_LIMIT "memory.limit_in_bytes"
+#define V1_SYSFS_FILE_CPU_USER "cpuacct.usage_user"
+#define V1_SYSFS_FILE_CPU "cpuacct.usage"
+#define V1_SYSFS_FILE_PIDS "cgroup.procs"
+
+/* Files from sysfs containing required data (cgroups v2) */
+#define V2_SYSFS_FILE_MEMORY "memory.current"
+#define V2_SYSFS_FILE_MAX_MEMORY "memory.peak"
+#define V2_SYSFS_FILE_MEMORY_STAT "memory.stat"
+#define V2_SYSFS_FILE_MEMORY_LIMIT "memory.max"
+#define V2_SYSFS_FILE_CPU_STAT "cpu.stat"
+#define V2_SYSFS_FILE_PIDS "cgroup.procs"
+#define V2_SYSFS_FILE_PIDS_ALT "containers/cgroup.procs"
+
+/* Values used to construct counters/gauges names and descriptions */
+#define COUNTER_PREFIX "container"
+
+#define COUNTER_MEMORY_PREFIX "memory"
+#define COUNTER_SPEC_MEMORY_PREFIX "spec_memory"
+#define COUNTER_MEMORY_USAGE "usage_bytes"
+#define DESCRIPTION_MEMORY_USAGE "Container memory usage in bytes"
+#define COUNTER_MEMORY_MAX_USAGE "max_usage_bytes"
+#define DESCRIPTION_MEMORY_MAX_USAGE "Container max memory usage in bytes"
+#define COUNTER_MEMORY_LIMIT "limit_bytes"
+#define DESCRIPTION_MEMORY_LIMIT "Container memory limit in bytes"
+#define GAUGE_MEMORY_RSS "rss"
+#define DESCRIPTION_MEMORY_RSS "Container RSS in bytes"
+
+#define COUNTER_CPU_PREFIX "cpu"
+#define COUNTER_CPU_USER "user_seconds_total"
+#define DESCRIPTION_CPU_USER "Container cpu usage in seconds in user mode"
+#define COUNTER_CPU "usage_seconds_total"
+#define DESCRIPTION_CPU "Container cpu usage in seconds"
+
+#define COUNTER_NETWORK_PREFIX "network"
+#define COUNTER_RX_BYTES "receive_bytes_total"
+#define DESCRIPTION_RX_BYTES "Network received bytes"
+#define COUNTER_RX_ERRORS "receive_errors_total"
+#define DESCRIPTION_RX_ERRORS "Network received errors"
+#define COUNTER_TX_BYTES "transmit_bytes_total"
+#define DESCRIPTION_TX_BYTES "Network transmited bytes"
+#define COUNTER_TX_ERRORS "transmit_errors_total"
+#define DESCRIPTION_TX_ERRORS "Network transmitedd errors"
+
+
+struct net_iface {
+ flb_sds_t name;
+ uint64_t rx_bytes;
+ uint64_t rx_errors;
+ uint64_t tx_bytes;
+ uint64_t tx_errors;
+ struct mk_list _head;
+};
+
+struct container {
+ flb_sds_t name;
+ flb_sds_t id;
+ flb_sds_t image_name;
+ struct mk_list _head;
+
+ uint64_t memory_usage;
+ uint64_t memory_max_usage;
+ uint64_t memory_limit;
+ uint64_t cpu;
+ uint64_t cpu_user;
+ uint64_t rss;
+
+ struct mk_list net_data;
+};
+
+struct sysfs_path {
+ flb_sds_t path;
+ struct mk_list _head;
+};
+
+struct flb_in_metrics {
+ /* config map options */
+ int scrape_on_start;
+ int scrape_interval;
+ flb_sds_t podman_config_path;
+
+ /* container list */
+ struct mk_list items;
+
+ /* sysfs path list */
+ struct mk_list sysfs_items;
+
+ /* counters */
+ struct cmt_counter *c_memory_usage;
+ struct cmt_counter *c_memory_max_usage;
+ struct cmt_counter *c_memory_limit;
+ struct cmt_gauge *g_rss;
+ struct cmt_counter *c_cpu_user;
+ struct cmt_counter *c_cpu;
+ struct cmt_counter *rx_bytes;
+ struct cmt_counter *rx_errors;
+ struct cmt_counter *tx_bytes;
+ struct cmt_counter *tx_errors;
+
+ /* cgroup version used by host */
+ int cgroup_version;
+
+ /* podman config file path */
+ flb_sds_t config;
+
+ /* proc and sys paths, overwriting mostly for testing */
+ flb_sds_t sysfs_path;
+ flb_sds_t procfs_path;
+
+ /* internal */
+ int coll_fd_runtime;
+ struct flb_input_instance *ins;
+};
+
+#endif
diff --git a/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c
new file mode 100644
index 000000000..e747fe4b8
--- /dev/null
+++ b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c
@@ -0,0 +1,407 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_input_plugin.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_metrics.h>
+#include <fluent-bit/flb_metrics_exporter.h>
+#include <fluent-bit/flb_jsmn.h>
+
+#include <monkey/mk_core/mk_list.h>
+
+#include "podman_metrics_data.h"
+#include "podman_metrics_config.h"
+
+/*
+ * Read uint64_t value from given path. If this function fails, it
+ * returns UINT64_MAX, which will be later interpeted as invalid counter value
+ * (it cannot return 0, because it is a valid value for counter
+ */
+uint64_t read_from_file(struct flb_in_metrics *ctx, flb_sds_t path)
+{
+ int c;
+ uint64_t value = UINT64_MAX;
+ FILE *fp;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ flb_plg_warn(ctx->ins, "Failed to read %s", path);
+ return value;
+ }
+
+ c = fscanf(fp, "%lu", &value);
+ fclose(fp);
+ if (c != 1) {
+ flb_plg_warn(ctx->ins, "Failed to read a number from %s", path);
+ return value;
+ }
+ return value;
+}
+
+/*
+ * Read uint64_t value from given path. Check for key: <VALUE> and return it.
+ * If this function fails, it
+ * returns UINT64_MAX, which will be later interpeted as invalid counter value
+ * (it cannot return 0, because it is a valid value for counter
+ */
+uint64_t read_key_value_from_file(struct flb_in_metrics *ctx, flb_sds_t path, flb_sds_t key)
+{
+ uint64_t value = UINT64_MAX;
+ FILE *fp;
+ flb_sds_t line = NULL;
+ flb_sds_t field = NULL;
+ flb_sds_t line2 = NULL;
+ size_t len = 0;
+ ssize_t read = 0;
+ int key_found = 0;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ flb_plg_warn(ctx->ins, "Failed to read %s", path);
+ return value;
+ }
+
+ while ((read = getline(&line, &len, fp)) != -1) {
+ line2 = line;
+
+ while( (field = strsep(&line2, " :")) != NULL ) {
+ if( *field == '\0' ) {
+ continue;
+ }
+ if (strcmp(field, key) == 0) {
+ key_found = 1;
+ continue;
+ }
+ if (key_found) {
+ value = strtoull(field, NULL, 10);
+ flb_plg_trace(ctx->ins, "Found key %s: %lu", key, value);
+ fclose(fp);
+ flb_free(line);
+ flb_free(line2);
+ return value;
+ }
+
+ }
+ flb_free(line2);
+ }
+ flb_free(line);
+ flb_plg_warn(ctx->ins, "%s not found in %s", key, path);
+ fclose(fp);
+ return value;
+}
+
+/*
+ * Read uint64_t value from path previously picked from sysfs directory list.
+ * If key is not NULL, it will be used to search a file instead of reading single value.
+ */
+uint64_t get_data_from_sysfs(struct flb_in_metrics *ctx, flb_sds_t dir, flb_sds_t name, flb_sds_t key)
+{
+ char path[SYSFS_FILE_PATH_SIZE];
+ uint64_t data = UINT64_MAX;
+ path[0]=0;
+
+ if (dir == NULL) {
+ return data;
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", dir, name);
+
+ if (key == NULL) {
+ data = read_from_file(ctx, path);
+ }
+ else {
+ data = read_key_value_from_file(ctx, path, key);
+ }
+ flb_plg_debug(ctx->ins, "%s: %lu", path, data);
+ return data;
+}
+
+/*
+ * Check if container sysfs data is pressent in previously generated list of sysfs directories.
+ * For cgroups v1, use subsystem (directory, for example memory) to search full path.
+ */
+int get_container_sysfs_subdirectory(struct flb_in_metrics *ctx, flb_sds_t id, flb_sds_t subsystem, flb_sds_t *path)
+{
+ struct sysfs_path *pth;
+ struct mk_list *head;
+ struct mk_list *tmp;
+
+ mk_list_foreach_safe(head, tmp, &ctx->sysfs_items) {
+ pth = mk_list_entry(head, struct sysfs_path, _head);
+ if (strstr(pth->path, id) != 0) {
+ if (subsystem != NULL && strstr(pth->path, subsystem) == 0) {
+ continue;
+ }
+ *path = pth->path;
+ flb_plg_trace(ctx->ins, "Found path for %s: %s", id, pth->path);
+ return 0;
+ }
+ }
+ *path = NULL;
+ return -1;
+}
+
+/*
+* Read data from /proc/ subsystem containing all data about network usage for pid (so, in this case,
+* for container). These fields seem to be in constant positions, so check only specific fields in each
+* row.
+*/
+int get_net_data_from_proc(struct flb_in_metrics *ctx, struct container *cnt, uint64_t pid) {
+ char path[PROCFS_FILE_PATH_SIZE];
+ char pid_buff[PID_BUFFER_SIZE];
+
+ FILE * fp;
+ flb_sds_t line = NULL;
+ flb_sds_t field = NULL;
+ flb_sds_t line2 = NULL;
+
+ size_t len = 0;
+ ssize_t read = 0;
+ int curr_line = 0;
+ int curr_field = 0;
+
+ struct net_iface *iface;
+
+ path[0]=0;
+ sprintf(pid_buff, "%" PRIu64, pid);
+ snprintf(path, sizeof(path), "%s/%s/%s", ctx->procfs_path, pid_buff, PROC_NET_SUFFIX);
+
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ flb_plg_warn(ctx->ins, "Failed to open %s", path);
+ return -1;
+ }
+
+ while ((read = getline(&line, &len, fp)) != -1) {
+ line2 = line;
+ if (curr_line++ <= DEV_NET_IGNORE_LINES) {
+ flb_plg_trace(ctx->ins, "Ignoring line %d in %s", curr_line, path);
+ continue;
+ }
+
+ iface = flb_malloc(sizeof(struct net_iface));
+ if (!iface) {
+ flb_errno();
+ return -1;
+ }
+ iface->name = NULL;
+ iface->rx_bytes = UINT64_MAX;
+ iface->rx_errors = UINT64_MAX;
+ iface->tx_bytes = UINT64_MAX;
+ iface->tx_errors = UINT64_MAX;
+
+
+ while( (field = strsep(&line2, " ")) != NULL ) {
+ if( *field == '\0' ) {
+ continue;
+ }
+ switch (curr_field++)
+ {
+ case DEV_NET_NAME:
+ /* Remove ':' from the end of name */
+ iface->name = flb_sds_create_len(field, strlen(field)-1);
+ flb_plg_trace(ctx->ins, "Reading name from %s: %s", path, iface->name);
+ break;
+
+ case DEV_NET_RX_BYTES:
+ iface->rx_bytes = strtoull(field, NULL, 10);
+ flb_plg_trace(ctx->ins, "Reading rx_bytes from %s: %lu", path, iface->rx_bytes);
+ break;
+
+ case DEV_NET_RX_ERRORS:
+ iface->rx_errors = strtoull(field, NULL, 10);
+ flb_plg_trace(ctx->ins, "Reading rx_errors from %s: %lu", path, iface->rx_errors);
+ break;
+
+ case DEV_NET_TX_BYTES:
+ iface->tx_bytes = strtoull(field, NULL, 10);
+ flb_plg_trace(ctx->ins, "Reading tx_bytes from %s: %lu", path, iface->tx_bytes);
+ break;
+
+ case DEV_NET_TX_ERRORS:
+ iface->tx_errors = strtoull(field, NULL, 10);
+ flb_plg_trace(ctx->ins, "Reading tx_errors from %s: %lu", path, iface->tx_errors);
+ break;
+ }
+ }
+ flb_free(line2);
+ curr_field = 0;
+
+ /* Ignore virtual interfaces connected to podman containers */
+ if (name_starts_with(iface->name, VETH_INTERFACE) == 0) {
+ flb_plg_trace(ctx->ins, "Ignoring virtual interface %s", iface->name);
+ flb_sds_destroy(iface->name);
+ flb_free(iface);
+ continue;
+ }
+ mk_list_add(&iface->_head, &cnt->net_data);
+ }
+
+ flb_free(line);
+ fclose(fp);
+ return 0;
+}
+
+/*
+ * Iterate over directories in sysfs system and collect all libpod-* directories
+ */
+int collect_sysfs_directories(struct flb_in_metrics *ctx, flb_sds_t name)
+{
+ char path[SYSFS_FILE_PATH_SIZE];
+ path[0] = 0;
+ DIR *dir;
+ struct dirent *entry;
+ struct sysfs_path *pth;
+
+ if (!(dir = opendir(name))) {
+ flb_plg_warn(ctx->ins, "Failed to open %s", name);
+ return -1;
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type == DT_DIR) {
+ if (strcmp(entry->d_name, CURRENT_DIR) == 0 || strcmp(entry->d_name, PREV_DIR) == 0) {
+ continue;
+ }
+ snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
+
+ if (name_starts_with(entry->d_name, SYSFS_CONTAINER_PREFIX) == 0 &&
+ strcmp(entry->d_name, SYSFS_LIBPOD_PARENT) != 0 &&
+ strstr(entry->d_name, SYSFS_CONMON) == 0) {
+ pth = flb_malloc(sizeof(struct sysfs_path));
+ if (!pth) {
+ flb_errno();
+ return -1;
+ }
+ pth->path = flb_sds_create(path);
+ flb_plg_debug(ctx->ins, "Collected sysfs directory: %s", pth->path);
+ mk_list_add(&pth->_head, &ctx->sysfs_items);
+ }
+
+ collect_sysfs_directories(ctx, path);
+ }
+ }
+ closedir(dir);
+ return 0;
+}
+
+/*
+ * Iterate over previously created container list. For each entry, generate its
+ * paths in sysfs system directory. From this path, grab data about container metrics
+ * and put it this entry.
+ * This function is used in cgroups v1 - meaning different directories for files.
+ */
+int fill_counters_with_sysfs_data_v1(struct flb_in_metrics *ctx)
+{
+ uint64_t pid;
+ flb_sds_t mem_path;
+ flb_sds_t cpu_path;
+ flb_sds_t systemd_path;
+ struct container *cnt;
+ struct mk_list *head;
+ struct mk_list *tmp;
+
+ mk_list_foreach_safe(head, tmp, &ctx->items) {
+ cnt = mk_list_entry(head, struct container, _head);
+
+ get_container_sysfs_subdirectory(ctx, cnt->id, V1_SYSFS_MEMORY, &mem_path);
+ get_container_sysfs_subdirectory(ctx, cnt->id, V1_SYSFS_CPU, &cpu_path);
+ get_container_sysfs_subdirectory(ctx, cnt->id, V1_SYSFS_SYSTEMD, &systemd_path);
+
+ cnt->memory_usage = get_data_from_sysfs(ctx, mem_path, V1_SYSFS_FILE_MEMORY, NULL);
+ cnt->memory_max_usage = get_data_from_sysfs(ctx, mem_path, V1_SYSFS_FILE_MAX_MEMORY, NULL);
+ cnt->rss = get_data_from_sysfs(ctx, mem_path, V1_SYSFS_FILE_MEMORY_STAT, STAT_KEY_RSS);
+ cnt->memory_limit = get_data_from_sysfs(ctx, mem_path, V1_SYSFS_FILE_MEMORY_LIMIT, NULL);
+ cnt->cpu_user = get_data_from_sysfs(ctx, cpu_path, V1_SYSFS_FILE_CPU_USER, NULL);
+ cnt->cpu = get_data_from_sysfs(ctx, cpu_path, V1_SYSFS_FILE_CPU, NULL);
+ pid = get_data_from_sysfs(ctx, systemd_path, V1_SYSFS_FILE_PIDS, NULL);
+ if (pid && pid != UINT64_MAX) {
+ get_net_data_from_proc(ctx, cnt, pid);
+ }
+ else {
+ flb_plg_warn(ctx->ins, "Failed to collect PID for %s", cnt->name);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Iterate over previously created container list. For each entry, generate its
+ * path in sysfs system directory. From this path, grab data about container metrics
+ * and put it this entry.
+ * This function is used in cgroups v2 - meaning same directory for all files.
+ */
+int fill_counters_with_sysfs_data_v2(struct flb_in_metrics *ctx)
+{
+ uint64_t pid;
+ flb_sds_t path;
+ struct container *cnt;
+ struct mk_list *head;
+ struct mk_list *tmp;
+
+ mk_list_foreach_safe(head, tmp, &ctx->items) {
+ cnt = mk_list_entry(head, struct container, _head);
+
+ get_container_sysfs_subdirectory(ctx, cnt->id, NULL, &path);
+
+ cnt->memory_usage = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_MEMORY, NULL);
+ cnt->memory_max_usage = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_MAX_MEMORY, NULL);
+ cnt->rss = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_MEMORY_STAT, STAT_KEY_RSS);
+ cnt->memory_limit = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_MEMORY_LIMIT, NULL);
+ cnt->cpu_user = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_CPU_STAT, STAT_KEY_CPU_USER);
+ cnt->cpu = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_CPU_STAT, STAT_KEY_CPU);
+ pid = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_PIDS, NULL);
+ if (!pid || pid == UINT64_MAX) {
+ pid = get_data_from_sysfs(ctx, path, V2_SYSFS_FILE_PIDS_ALT, NULL);
+ }
+ if (pid && pid != UINT64_MAX) {
+ get_net_data_from_proc(ctx, cnt, pid);
+ }
+ else {
+ flb_plg_warn(ctx->ins, "Failed to collect PID for %s", cnt->name);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Check if flb_sds_t starts with given string
+ */
+int name_starts_with(flb_sds_t s, const char *str)
+{
+ size_t len = strlen(str);
+ size_t flen = flb_sds_len(s);
+
+ if (s == NULL || len > flen) {
+ return -1;
+ }
+
+ return strncmp(s, str, len);
+}
+
+/*
+ * Calculate which cgroup version is used on host by checing existence of
+ * cgroup.controllers file (if it exists, it is V2).
+ */
+int get_cgroup_version(struct flb_in_metrics *ctx)
+{
+ char path[SYSFS_FILE_PATH_SIZE];
+ snprintf(path, sizeof(path), "%s/%s", ctx->sysfs_path, CGROUP_V2_PATH);
+ return (access(path, F_OK) == 0) ? CGROUP_V2 : CGROUP_V1;
+}
diff --git a/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.h b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.h
new file mode 100644
index 000000000..93fa6de00
--- /dev/null
+++ b/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_IN_PODMAN_METRICS_DATA_H
+#define FLB_IN_PODMAN_METRICS_DATA_H
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_input.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_jsmn.h>
+
+#include <dirent.h>
+#include <monkey/mk_core/mk_list.h>
+
+#include "podman_metrics_config.h"
+
+int destroy_counter(struct flb_in_metrics *ctx, struct cmt_counter **c);
+int destroy_gauge(struct flb_in_metrics *ctx, struct cmt_gauge **g);
+
+uint64_t read_from_file(struct flb_in_metrics *ctx, flb_sds_t path);
+uint64_t read_key_value_from_file(struct flb_in_metrics *ctx, flb_sds_t path, flb_sds_t key);
+uint64_t get_data_from_sysfs(struct flb_in_metrics *ctx, flb_sds_t dir, flb_sds_t name, flb_sds_t key);
+
+int get_container_sysfs_subdirectory(struct flb_in_metrics *ctx, flb_sds_t id, flb_sds_t subsystem, flb_sds_t *path);
+int get_net_data_from_proc(struct flb_in_metrics *ctx, struct container *cnt, uint64_t pid);
+
+int collect_sysfs_directories(struct flb_in_metrics *ctx, flb_sds_t name);
+int fill_counters_with_sysfs_data_v1(struct flb_in_metrics *ctx);
+int fill_counters_with_sysfs_data_v2(struct flb_in_metrics *ctx);
+
+int name_starts_with(flb_sds_t s, const char *str);
+int get_cgroup_version(struct flb_in_metrics *ctx);
+
+#endif