From 9f9f953d5321bbed1862e0e36752ba8a75dfe8c4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:57:41 -0700 Subject: [PATCH] libsysprof-profile: import podman utils This comes from libsysprof, which we hope to remove as part of this move to libsysprof-profile vs libsysprof-analyze. --- src/libsysprof-profile/meson.build | 2 + .../sysprof-podman-private.h | 36 +++ src/libsysprof-profile/sysprof-podman.c | 274 ++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-podman-private.h create mode 100644 src/libsysprof-profile/sysprof-podman.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index fb22f182..c87581f9 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -8,6 +8,7 @@ libsysprof_profile_public_sources = [ libsysprof_profile_private_sources = [ 'mapped-ring-buffer-source.c', 'sysprof-controlfd-instrument.c', + 'sysprof-podman.c', 'sysprof-polkit.c', ] @@ -31,6 +32,7 @@ libsysprof_profile_deps = [ dependency('gio-unix-2.0', version: glib_req_version, required: host_machine.system() != 'windows'), + dependency('json-glib-1.0'), dependency('libdex-1', version: dex_req_version), dependency('polkit-gobject-1', version: polkit_req_version), diff --git a/src/libsysprof-profile/sysprof-podman-private.h b/src/libsysprof-profile/sysprof-podman-private.h new file mode 100644 index 00000000..95d67350 --- /dev/null +++ b/src/libsysprof-profile/sysprof-podman-private.h @@ -0,0 +1,36 @@ +/* sysprof-podman-private.h + * + * Copyright 2020 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 + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +typedef struct _SysprofPodman SysprofPodman; + +SysprofPodman *sysprof_podman_snapshot_current_user (void); +gchar **sysprof_podman_get_layers (SysprofPodman *self, + const char *container); +void sysprof_podman_free (SysprofPodman *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPodman, sysprof_podman_free) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-podman.c b/src/libsysprof-profile/sysprof-podman.c new file mode 100644 index 00000000..81076e8c --- /dev/null +++ b/src/libsysprof-profile/sysprof-podman.c @@ -0,0 +1,274 @@ +/* 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-private.h" + +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; +}