Skip to content
Snippets Groups Projects
Commit 100ec8ba authored by Simon McVittie's avatar Simon McVittie
Browse files

system-info: Add basic container detection


This only detects the container technologies we are most interested in
right now: pressure-vessel, Flatpak and Docker.

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent e7b5aaf6
No related branches found
No related tags found
1 merge request!108system-info: Add basic container detection
Pipeline #2612 passed
...@@ -632,6 +632,46 @@ jsonify_os_release (JsonBuilder *builder, ...@@ -632,6 +632,46 @@ jsonify_os_release (JsonBuilder *builder,
json_builder_end_object (builder); json_builder_end_object (builder);
} }
static void
jsonify_container (JsonBuilder *builder,
SrtSystemInfo *info)
{
SrtContainerType type = srt_system_info_get_container_type (info);
gchar *host_directory = srt_system_info_dup_container_host_directory (info);
gchar **env = NULL;
SrtSystemInfo *host = NULL;
json_builder_set_member_name (builder, "container");
json_builder_begin_object (builder);
{
json_builder_set_member_name (builder, "type");
jsonify_enum (builder, SRT_TYPE_CONTAINER_TYPE, type);
if (type != SRT_CONTAINER_TYPE_NONE)
{
json_builder_set_member_name (builder, "host");
json_builder_begin_object (builder);
{
json_builder_set_member_name (builder, "path");
json_builder_add_string_value (builder, host_directory);
if (host_directory != NULL)
{
host = srt_system_info_new (NULL);
srt_system_info_set_sysroot (host, host_directory);
jsonify_os_release (builder, host);
}
}
json_builder_end_object (builder);
}
}
json_builder_end_object (builder);
g_free (host_directory);
g_clear_object (&host);
g_strfreev (env);
}
static const char * const locales[] = static const char * const locales[] =
{ {
"", "",
...@@ -847,6 +887,7 @@ main (int argc, ...@@ -847,6 +887,7 @@ main (int argc,
json_builder_end_object (builder); json_builder_end_object (builder);
jsonify_os_release (builder, info); jsonify_os_release (builder, info);
jsonify_container (builder, info);
json_builder_set_member_name (builder, "driver_environment"); json_builder_set_member_name (builder, "driver_environment");
json_builder_begin_array (builder); json_builder_begin_array (builder);
......
...@@ -292,6 +292,29 @@ keys: ...@@ -292,6 +292,29 @@ keys:
This is the **VARIANT** from **os-release**(5). This is the **VARIANT** from **os-release**(5).
**container**
: Details of the container we are running in, if any.
The keys are strings:
**type**
: A short machine-readable string identifying the container type,
such as **flatpak**, **pressure-vessel**, **docker**,
**unknown** (if we appear to be in a container but the type is
unknown), or **none** (if we do not appear to be in a container).
**host**
: An object describing the host system. The keys are strings:
**path**
: Absolute path to a directory where important files from the
host system can be found, or **null** if unavailable.
In Flatpak and pressure-vessel, this will be either **/run/host**
or **null*.
**os-release**
: The same as the **os-release** described above, but describing
the host system rather than the container.
**driver_environment** **driver_environment**
: An array of strings, in the form "NAME=VALUE", with the well-known driver environment : An array of strings, in the form "NAME=VALUE", with the well-known driver environment
variables that are currently set. variables that are currently set.
......
...@@ -150,6 +150,12 @@ struct _SrtSystemInfo ...@@ -150,6 +150,12 @@ struct _SrtSystemInfo
gchar **messages_64; gchar **messages_64;
gboolean have_data; gboolean have_data;
} pinned_libs; } pinned_libs;
struct
{
SrtContainerType type;
gchar *host_directory;
gboolean have_data;
} container;
SrtOsRelease os_release; SrtOsRelease os_release;
SrtTestFlags test_flags; SrtTestFlags test_flags;
Tristate can_write_uinput; Tristate can_write_uinput;
...@@ -300,6 +306,8 @@ srt_system_info_init (SrtSystemInfo *self) ...@@ -300,6 +306,8 @@ srt_system_info_init (SrtSystemInfo *self)
self->abis = g_ptr_array_new_full (2, abi_free); self->abis = g_ptr_array_new_full (2, abi_free);
_srt_os_release_init (&self->os_release); _srt_os_release_init (&self->os_release);
self->container.type = SRT_CONTAINER_TYPE_UNKNOWN;
} }
static void static void
...@@ -342,6 +350,17 @@ srt_system_info_set_property (GObject *object, ...@@ -342,6 +350,17 @@ srt_system_info_set_property (GObject *object,
} }
} }
/*
* Forget any cached information about container information.
*/
static void
forget_container_info (SrtSystemInfo *self)
{
g_clear_pointer (&self->container.host_directory, g_free);
self->container.type = SRT_CONTAINER_TYPE_UNKNOWN;
self->container.have_data = FALSE;
}
/* /*
* Forget any cached information about locales. * Forget any cached information about locales.
*/ */
...@@ -431,6 +450,7 @@ srt_system_info_finalize (GObject *object) ...@@ -431,6 +450,7 @@ srt_system_info_finalize (GObject *object)
{ {
SrtSystemInfo *self = SRT_SYSTEM_INFO (object); SrtSystemInfo *self = SRT_SYSTEM_INFO (object);
forget_container_info (self);
forget_icds (self); forget_icds (self);
forget_locales (self); forget_locales (self);
forget_os (self); forget_os (self);
...@@ -1592,6 +1612,7 @@ srt_system_info_set_sysroot (SrtSystemInfo *self, ...@@ -1592,6 +1612,7 @@ srt_system_info_set_sysroot (SrtSystemInfo *self,
{ {
g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); g_return_if_fail (SRT_IS_SYSTEM_INFO (self));
forget_container_info (self);
forget_graphics_modules (self); forget_graphics_modules (self);
forget_libraries (self); forget_libraries (self);
forget_graphics_results (self); forget_graphics_results (self);
...@@ -2742,3 +2763,188 @@ srt_system_info_list_driver_environment (SrtSystemInfo *self) ...@@ -2742,3 +2763,188 @@ srt_system_info_list_driver_environment (SrtSystemInfo *self)
else else
return g_strdupv (self->cached_driver_environment); return g_strdupv (self->cached_driver_environment);
} }
typedef struct
{
SrtContainerType type;
const char *name;
} ContainerTypeName;
static const ContainerTypeName container_types[] =
{
{ SRT_CONTAINER_TYPE_DOCKER, "docker" }
};
static SrtContainerType
container_type_from_name (const char *name)
{
gsize i;
for (i = 0; i < G_N_ELEMENTS (container_types); i++)
{
const ContainerTypeName *entry = &container_types[i];
if (strcmp (entry->name, name) == 0)
return entry->type;
}
return SRT_CONTAINER_TYPE_UNKNOWN;
}
static void
ensure_container_info (SrtSystemInfo *self)
{
const char *sysroot = NULL;
gchar *contents = NULL;
gchar *filename = NULL;
if (self->container.have_data)
return;
g_assert (self->container.host_directory == NULL);
g_assert (self->container.type == SRT_CONTAINER_TYPE_UNKNOWN);
sysroot = self->sysroot;
if (sysroot == NULL)
sysroot = "/";
g_debug ("Finding container info in sysroot %s...", sysroot);
filename = g_build_filename (sysroot, "run", "systemd", "container", NULL);
if (g_file_get_contents (filename, &contents, NULL, NULL))
{
g_strchomp (contents);
self->container.type = container_type_from_name (contents);
g_debug ("Type %d based on %s", self->container.type, filename);
goto out;
}
g_clear_pointer (&filename, g_free);
filename = g_build_filename (sysroot, ".flatpak-info", NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
{
self->container.type = SRT_CONTAINER_TYPE_FLATPAK;
g_debug ("Flatpak based on %s", filename);
goto out;
}
g_clear_pointer (&filename, g_free);
filename = g_build_filename (sysroot, "run", "pressure-vessel", NULL);
if (g_file_test (filename, G_FILE_TEST_IS_DIR))
{
self->container.type = SRT_CONTAINER_TYPE_PRESSURE_VESSEL;
g_debug ("pressure-vessel based on %s", filename);
goto out;
}
g_clear_pointer (&filename, g_free);
filename = g_build_filename (sysroot, ".dockerenv", NULL);
if (g_file_test (filename, G_FILE_TEST_EXISTS))
{
self->container.type = SRT_CONTAINER_TYPE_DOCKER;
g_debug ("Docker based on %s", filename);
goto out;
}
g_clear_pointer (&filename, g_free);
filename = g_build_filename (sysroot, "proc", "1", "cgroup", NULL);
if (g_file_get_contents (filename, &contents, NULL, NULL))
{
if (strstr (contents, "/docker/") != NULL)
self->container.type = SRT_CONTAINER_TYPE_DOCKER;
if (self->container.type != SRT_CONTAINER_TYPE_UNKNOWN)
{
g_debug ("Type %d based on %s", self->container.type, filename);
goto out;
}
}
g_clear_pointer (&filename, g_free);
filename = g_build_filename (sysroot, "run", "host", NULL);
if (g_file_test (filename, G_FILE_TEST_IS_DIR))
{
g_debug ("Unknown container technology based on %s", filename);
self->container.type = SRT_CONTAINER_TYPE_UNKNOWN;
self->container.host_directory = g_steal_pointer (&filename);
goto out;
}
g_clear_pointer (&filename, g_free);
/* We haven't found any particular evidence of being in a container */
g_debug ("Probably not a container");
self->container.type = SRT_CONTAINER_TYPE_NONE;
out:
g_free (contents);
g_free (filename);
switch (self->container.type)
{
case SRT_CONTAINER_TYPE_FLATPAK:
case SRT_CONTAINER_TYPE_PRESSURE_VESSEL:
self->container.host_directory = g_build_filename (sysroot, "run",
"host", NULL);
break;
case SRT_CONTAINER_TYPE_DOCKER:
case SRT_CONTAINER_TYPE_UNKNOWN:
case SRT_CONTAINER_TYPE_NONE:
default:
break;
}
self->container.have_data = TRUE;
}
/**
* srt_system_info_get_container_type:
* @self: The #SrtSystemInfo object
*
* If the program appears to be running in a container, return what sort
* of container it is.
*
* Returns: A recognised container type, or %SRT_CONTAINER_TYPE_NONE
* if a container cannot be detected, or %SRT_CONTAINER_TYPE_UNKNOWN
* if unsure.
*/
SrtContainerType
srt_system_info_get_container_type (SrtSystemInfo *self)
{
g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_CONTAINER_TYPE_UNKNOWN);
ensure_container_info (self);
return self->container.type;
}
/**
* srt_system_info_dup_container_host_directory:
*
* If the program appears to be running in a container, return the
* directory where host files can be found. For example, if this function
* returns `/run/host`, it might be possible to load the host system's
* `/usr/lib/os-release` by reading `/run/host/usr/lib/os-release`.
*
* The returned directory is usually not complete. For example,
* in a Flatpak app, `/run/host` will sometimes contain the host system's
* `/etc` and `/usr`, but only if suitable permissions flags are set.
*
* Returns: A path from which at least some host-system files can be
* loaded, typically `/run/host`, or %NULL if unknown or unavailable
*/
gchar *
srt_system_info_dup_container_host_directory (SrtSystemInfo *self)
{
g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL);
ensure_container_info (self);
return g_strdup (self->container.host_directory);
}
...@@ -71,6 +71,26 @@ typedef enum ...@@ -71,6 +71,26 @@ typedef enum
SRT_DRIVER_FLAGS_NONE = 0 SRT_DRIVER_FLAGS_NONE = 0
} SrtDriverFlags; } SrtDriverFlags;
/**
* SrtContainerType:
* @SRT_CONTAINER_TYPE_UNKNOWN: Unknown container type
* @SRT_CONTAINER_TYPE_NONE: No container detected
* @SRT_CONTAINER_TYPE_FLATPAK: Running in a Flatpak app
* @SRT_CONTAINER_TYPE_PRESSURE_VESSEL: Running in a Steam Runtime container
* using pressure-vessel
* @SRT_CONTAINER_TYPE_DOCKER: Running in a Docker container
*
* A type of container.
*/
typedef enum
{
SRT_CONTAINER_TYPE_NONE = 0,
SRT_CONTAINER_TYPE_FLATPAK,
SRT_CONTAINER_TYPE_PRESSURE_VESSEL,
SRT_CONTAINER_TYPE_DOCKER,
SRT_CONTAINER_TYPE_UNKNOWN = -1
} SrtContainerType;
typedef struct _SrtSystemInfo SrtSystemInfo; typedef struct _SrtSystemInfo SrtSystemInfo;
typedef struct _SrtSystemInfoClass SrtSystemInfoClass; typedef struct _SrtSystemInfoClass SrtSystemInfoClass;
...@@ -87,6 +107,9 @@ SrtSystemInfo *srt_system_info_new (const char *expectations); ...@@ -87,6 +107,9 @@ SrtSystemInfo *srt_system_info_new (const char *expectations);
gboolean srt_system_info_can_run (SrtSystemInfo *self, gboolean srt_system_info_can_run (SrtSystemInfo *self,
const char *multiarch_tuple); const char *multiarch_tuple);
SrtContainerType srt_system_info_get_container_type (SrtSystemInfo *self);
gchar *srt_system_info_dup_container_host_directory (SrtSystemInfo *self);
gboolean srt_system_info_can_write_to_uinput (SrtSystemInfo *self); gboolean srt_system_info_can_write_to_uinput (SrtSystemInfo *self);
SrtLibraryIssues srt_system_info_check_libraries (SrtSystemInfo *self, SrtLibraryIssues srt_system_info_check_libraries (SrtSystemInfo *self,
const gchar *multiarch_tuple, const gchar *multiarch_tuple,
......
whatever
docker
11:perf_event:/docker/9999999999999999999999999999999999999999999999999999999999999999
10:freezer:/docker/9999999999999999999999999999999999999999999999999999999999999999
9:memory:/docker/9999999999999999999999999999999999999999999999999999999999999999
8:rdma:/
7:devices:/docker/9999999999999999999999999999999999999999999999999999999999999999
6:blkio:/docker/9999999999999999999999999999999999999999999999999999999999999999
5:net_cls,net_prio:/docker/9999999999999999999999999999999999999999999999999999999999999999
4:cpu,cpuacct:/docker/9999999999999999999999999999999999999999999999999999999999999999
3:cpuset:/docker/9999999999999999999999999999999999999999999999999999999999999999
2:pids:/docker/9999999999999999999999999999999999999999999999999999999999999999
1:name=systemd:/docker/9999999999999999999999999999999999999999999999999999999999999999
0::/system.slice/docker.service
...@@ -2182,6 +2182,75 @@ driver_environment (Fixture *f, ...@@ -2182,6 +2182,75 @@ driver_environment (Fixture *f,
g_strfreev (envp); g_strfreev (envp);
} }
typedef struct
{
const char *description;
const char *sysroot;
SrtContainerType type;
const char *host_directory;
} ContainerTest;
static const ContainerTest container_tests[] =
{
{ "Has /.dockerenv",
"debian-unstable", SRT_CONTAINER_TYPE_DOCKER, NULL },
{ "Has an unknown value in /run/systemd/container",
"debian10", SRT_CONTAINER_TYPE_UNKNOWN, NULL },
{ "Has 'docker' in /run/systemd/container",
"fedora", SRT_CONTAINER_TYPE_DOCKER, NULL },
{ "Has /.flatpak-info",
"flatpak-example", SRT_CONTAINER_TYPE_FLATPAK, "/run/host" },
{ "Has /run/host",
"invalid-os-release", SRT_CONTAINER_TYPE_UNKNOWN, "/run/host" },
{ "Has no evidence of being a container",
"no-os-release", SRT_CONTAINER_TYPE_NONE, NULL },
{ "Has /run/pressure-vessel",
"steamrt", SRT_CONTAINER_TYPE_PRESSURE_VESSEL, "/run/host" },
{ "Has a Docker-looking /proc/1/cgroup",
"steamrt-unofficial", SRT_CONTAINER_TYPE_DOCKER, NULL },
};
static void
test_containers (Fixture *f,
gconstpointer context)
{
gsize i, j;
for (i = 0; i < G_N_ELEMENTS (container_tests); i++)
{
const ContainerTest *test = &container_tests[i];
SrtSystemInfo *info;
gchar *sysroot, *got, *expected;
g_test_message ("%s: %s", test->sysroot, test->description);
sysroot = g_build_filename (f->srcdir, "sysroots", test->sysroot, NULL);
info = srt_system_info_new (NULL);
g_assert_nonnull (info);
srt_system_info_set_sysroot (info, sysroot);
for (j = 0; j < 2; j++)
{
g_assert_cmpint (srt_system_info_get_container_type (info), ==,
test->type);
got = srt_system_info_dup_container_host_directory (info);
if (test->host_directory == NULL)
expected = NULL;
else
expected = g_build_filename (sysroot, test->host_directory, NULL);
g_assert_cmpstr (got, ==, expected);
g_free (got);
g_free (expected);
}
g_object_unref (info);
g_free (sysroot);
}
}
int int
main (int argc, main (int argc,
char **argv) char **argv)
...@@ -2252,5 +2321,8 @@ main (int argc, ...@@ -2252,5 +2321,8 @@ main (int argc,
g_test_add ("/system-info/driver_environment", Fixture, NULL, g_test_add ("/system-info/driver_environment", Fixture, NULL,
setup, driver_environment, teardown); setup, driver_environment, teardown);
g_test_add ("/system-info/containers", Fixture, NULL,
setup, test_containers, teardown);
return g_test_run (); return g_test_run ();
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment