summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c')
-rw-r--r--src/fluent-bit/plugins/in_podman_metrics/podman_metrics_data.c407
1 files changed, 407 insertions, 0 deletions
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;
+}