proc: discover filesystem root for podman containers

For processes we find in a podman container, we can sniff the libpod
cgroup scope. Using that we can translate into the podman layer that
contains the files.

With that, future work could find the proper .so when resolving based on
alternate roots for the process.
This commit is contained in:
Christian Hergert
2021-02-24 18:40:41 -08:00
parent b5790be7ad
commit b22899474d
2 changed files with 158 additions and 1 deletions

View File

@ -113,6 +113,7 @@ endif
libsysprof_pkg_deps = [
dependency('gio-2.0', version: glib_req_version),
dependency('gio-unix-2.0', version: glib_req_version),
dependency('json-glib-1.0'),
polkit_dep,
libsysprof_capture_deps,
]

View File

@ -39,6 +39,7 @@
#include "config.h"
#include <json-glib/json-glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -53,9 +54,16 @@ struct _SysprofProcSource
SysprofCaptureWriter *writer;
GArray *pids;
SysprofMountinfo *mountinfo;
GHashTable *podman;
guint is_ready : 1;
};
typedef struct
{
char *id;
char **layers;
} PodmanInfo;
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofProcSource, sysprof_proc_source, G_TYPE_OBJECT, 0,
@ -161,6 +169,147 @@ pid_is_interesting (SysprofProcSource *self,
return FALSE;
}
static void
podman_info_free (gpointer data)
{
PodmanInfo *info = data;
g_free (info->id);
g_strfreev (info->layers);
g_free (info);
}
static void
update_containers_json (SysprofProcSource *self)
{
g_autoptr(JsonParser) parser = NULL;
g_autofree gchar *path = NULL;
JsonNode *root;
JsonArray *ar;
guint n_items;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
parser = json_parser_new ();
path = g_build_filename (g_get_user_data_dir (),
"containers",
"storage",
"overlay-containers",
"containers.json",
NULL);
if (!json_parser_load_from_file (parser, path, NULL) ||
!(root = json_parser_get_root (parser)) ||
!JSON_NODE_HOLDS_ARRAY (root) ||
!(ar = json_node_get_array (root)))
return;
n_items = json_array_get_length (ar);
for (guint i = 0; i < n_items; i++)
{
JsonObject *item = json_array_get_object_element (ar, i);
PodmanInfo *info;
const char *id;
const char *layer;
if (item == NULL)
continue;
id = json_object_get_string_member (item, "id");
layer = json_object_get_string_member (item, "layer");
if (id == NULL || layer == NULL)
continue;
info = g_new0 (PodmanInfo, 1);
info->id = g_strdup (id);
info->layers = g_new0 (char *, 2);
info->layers[0] = g_strdup (layer);
info->layers[1] = NULL;
g_hash_table_insert (self->podman, info->id, info);
}
}
static void
sysprof_proc_source_populate_pid_podman (SysprofProcSource *self,
GPid pid,
const char *container)
{
PodmanInfo *info;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (container != NULL);
if (!(info = g_hash_table_lookup (self->podman, container)))
{
update_containers_json (self);
info = g_hash_table_lookup (self->podman, container);
}
if (info != NULL)
{
for (guint i = 0; info->layers[i]; i++)
{
g_autofree char *path = g_build_filename (g_get_user_data_dir (),
"containers",
"storage",
"overlay",
info->layers[i],
"diff",
NULL);
sysprof_capture_writer_add_pid_root (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
pid,
path,
i);
}
}
}
static void
sysprof_proc_source_populate_pid_root (SysprofProcSource *self,
GPid pid,
const char *cgroup)
{
static GRegex *regex;
GMatchInfo *match_info = NULL;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (cgroup != NULL);
/* This function tries to discover the podman container that contains the
* process identified on the host as @pid. We can only do anything with this
* if the pids are in containers that the running user controls (so that we
* can actually access the overlays).
*
* This stuff, and I want to emphasize, is a giant hack. Just like containers
* are on Linux. But if we are really careful, we can make this work for the
* one particular use case I care about, which is podman/toolbox on Fedora
* Workstation/Silverblue.
*
* -- Christian
*/
if (regex == NULL)
{
regex = g_regex_new ("libpod-([a-z0-9]{64})\\.scope", G_REGEX_OPTIMIZE, 0, NULL);
g_assert (regex != NULL);
}
if (g_regex_match (regex, cgroup, 0, &match_info))
{
char *word = g_match_info_fetch (match_info, 1);
sysprof_proc_source_populate_pid_podman (self, pid, word);
g_free (word);
}
g_match_info_free (match_info);
}
static void
sysprof_proc_source_populate (SysprofProcSource *self,
GVariant *info)
@ -191,6 +340,7 @@ sysprof_proc_source_populate (SysprofProcSource *self,
const gchar *comm;
const gchar *mountinfo;
const gchar *maps;
const gchar *cgroup;
gint32 pid;
g_variant_dict_init (&dict, pidinfo);
@ -211,8 +361,12 @@ sysprof_proc_source_populate (SysprofProcSource *self,
if (!g_variant_dict_lookup (&dict, "maps", "&s", &maps))
maps = "";
if (!g_variant_dict_lookup (&dict, "cgroup", "&s", &cgroup))
cgroup = "";
sysprof_proc_source_populate_process (self, pid, *cmdline ? cmdline : comm);
sysprof_proc_source_populate_maps (self, pid, maps, mountinfo);
sysprof_proc_source_populate_pid_root (self, pid, cgroup);
skip:
g_variant_dict_clear (&dict);
@ -253,7 +407,7 @@ sysprof_proc_source_start (SysprofSource *source)
g_assert (self->writer != NULL);
sysprof_helpers_get_process_info_async (helpers,
"pid,maps,mountinfo,cmdline,comm",
"pid,maps,mountinfo,cmdline,comm,cgroup",
NULL,
sysprof_proc_source_get_process_info_cb,
g_object_ref (self));
@ -362,6 +516,7 @@ sysprof_proc_source_finalize (GObject *object)
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->pids, g_array_unref);
g_clear_pointer (&self->mountinfo, sysprof_mountinfo_free);
g_clear_pointer (&self->podman, g_hash_table_unref);
G_OBJECT_CLASS (sysprof_proc_source_parent_class)->finalize (object);
}
@ -379,6 +534,7 @@ sysprof_proc_source_init (SysprofProcSource *self)
{
self->pids = g_array_new (FALSE, FALSE, sizeof (GPid));
self->mountinfo = sysprof_mountinfo_new ();
self->podman = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, podman_info_free);
}
SysprofSource *