/* sysprof-podman.c * * Copyright 2019 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * SPDX-License-Identifier: GPL-3.0-or-later */ #define G_LOG_DOMAIN "sysprof-podman" #include "config.h" #include #include "sysprof-podman.h" static const char *debug_dirs[] = { "/usr/lib/debug", "/usr/lib32/debug", "/usr/lib64/debug", }; void _sysprof_podman_debug_dirs (GPtrArray *dirs) { g_autofree gchar *base_path = NULL; g_autoptr(GDir) dir = NULL; const gchar *name; g_assert (dirs != NULL); base_path = g_build_filename (g_get_user_data_dir (), "containers", "storage", "overlay", NULL); if (!(dir = g_dir_open (base_path, 0, NULL))) return; while ((name = g_dir_read_name (dir))) { for (guint i = 0; i < G_N_ELEMENTS (debug_dirs); i++) { g_autofree gchar *debug_path = g_build_filename (base_path, name, "diff", debug_dirs[i], NULL); if (g_file_test (debug_path, G_FILE_TEST_IS_DIR)) g_ptr_array_add (dirs, g_steal_pointer (&debug_path)); } } } gchar ** sysprof_podman_debug_dirs (void) { GPtrArray *dirs = g_ptr_array_new (); _sysprof_podman_debug_dirs (dirs); g_ptr_array_add (dirs, NULL); return (gchar **)g_ptr_array_free (dirs, FALSE); } struct _SysprofPodman { JsonParser *containers_parser; JsonParser *layers_parser; JsonParser *images_parser; }; void sysprof_podman_free (SysprofPodman *self) { g_clear_object (&self->containers_parser); g_clear_object (&self->layers_parser); g_clear_object (&self->images_parser); g_slice_free (SysprofPodman, self); } static void load_containers (SysprofPodman *self) { g_autofree char *path = NULL; g_assert (self != NULL); path = g_build_filename (g_get_user_data_dir (), "containers", "storage", "overlay-containers", "containers.json", NULL); json_parser_load_from_file (self->containers_parser, path, NULL); } static void load_layers (SysprofPodman *self) { g_autofree char *path = NULL; g_assert (self != NULL); path = g_build_filename (g_get_user_data_dir (), "containers", "storage", "overlay-layers", "layers.json", NULL); json_parser_load_from_file (self->layers_parser, path, NULL); } static void load_images (SysprofPodman *self) { g_autofree char *path = NULL; g_assert (self != NULL); path = g_build_filename (g_get_user_data_dir (), "containers", "storage", "overlay-images", "images.json", NULL); json_parser_load_from_file (self->images_parser, path, NULL); } SysprofPodman * sysprof_podman_snapshot_current_user (void) { SysprofPodman *self; self = g_slice_new0 (SysprofPodman); self->containers_parser = json_parser_new (); self->layers_parser = json_parser_new (); self->images_parser = json_parser_new (); load_containers (self); load_layers (self); load_images (self); return self; } static const char * find_image_layer (JsonParser *parser, const char *image) { JsonNode *root; JsonArray *ar; guint n_items; g_assert (JSON_IS_PARSER (parser)); g_assert (image != NULL); if (!(root = json_parser_get_root (parser)) || !JSON_NODE_HOLDS_ARRAY (root) || !(ar = json_node_get_array (root))) return NULL; n_items = json_array_get_length (ar); for (guint i = 0; i < n_items; i++) { JsonObject *item = json_array_get_object_element (ar, i); const char *id; const char *layer; if (item == NULL || !json_object_has_member (item, "id") || !json_object_has_member (item, "layer") || !(id = json_object_get_string_member (item, "id")) || strcmp (id, image) != 0 || !(layer = json_object_get_string_member (item, "layer"))) continue; return layer; } return NULL; } static const char * find_parent_layer (JsonParser *parser, const char *layer, GHashTable *seen) { JsonNode *root; JsonArray *ar; guint n_items; g_assert (JSON_IS_PARSER (parser)); g_assert (layer != NULL); g_assert (seen != NULL); if (!(root = json_parser_get_root (parser)) || !JSON_NODE_HOLDS_ARRAY (root) || !(ar = json_node_get_array (root))) return NULL; n_items = json_array_get_length (ar); for (guint i = 0; i < n_items; i++) { JsonObject *item = json_array_get_object_element (ar, i); const char *parent; const char *id; if (item == NULL || !json_object_has_member (item, "id") || !json_object_has_member (item, "parent") || !(id = json_object_get_string_member (item, "id")) || strcmp (id, layer) != 0 || !(parent = json_object_get_string_member (item, "parent"))) continue; if (g_hash_table_contains (seen, parent)) return NULL; return parent; } return NULL; } static char * get_layer_dir (const char *layer) { /* We don't use XDG data dir because this might be in a container * or flatpak environment that doesn't match. And generally, it's * always .local. */ return g_build_filename (g_get_home_dir (), ".local", "share", "containers", "storage", "overlay", layer, "diff", NULL); } gchar ** sysprof_podman_get_layers (SysprofPodman *self, const char *container) { const char *layer = NULL; const char *image = NULL; GHashTable *layers; JsonNode *root; JsonArray *ar; const char **keys; char **ret; guint n_items; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (container != NULL, NULL); if (!(root = json_parser_get_root (self->containers_parser)) || !JSON_NODE_HOLDS_ARRAY (root) || !(ar = json_node_get_array (root))) return NULL; n_items = json_array_get_length (ar); /* First try to locate the "layer" identifier for the container * in question. */ for (guint i = 0; i < n_items; i++) { JsonObject *item = json_array_get_object_element (ar, i); const char *item_layer; const char *id; if (item == NULL || !(id = json_object_get_string_member (item, "id")) || strcmp (id, container) != 0 || !(item_layer = json_object_get_string_member (item, "layer"))) continue; layer = item_layer; image = json_object_get_string_member (item, "image"); break; } layers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); /* Add all our known layers starting from current layer */ do g_hash_table_add (layers, get_layer_dir (layer)); while ((layer = find_parent_layer (self->layers_parser, layer, layers))); /* If an image was specified, add its layer */ if ((layer = find_image_layer (self->images_parser, image))) { do g_hash_table_add (layers, get_layer_dir (layer)); while ((layer = find_parent_layer (self->layers_parser, layer, layers))); } keys = (const char **)g_hash_table_get_keys_as_array (layers, NULL); ret = g_strdupv ((char **)keys); g_hash_table_unref (layers); g_free (keys); return ret; }