From 447a8d053789aaeeaa8a00109dadb315a118ca5d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 1 Feb 2018 10:29:12 +0100 Subject: [PATCH] Extract FlatpakContext to a separate file This is basically a code motion only, no changes to behaviour. Closes: #1374 Approved by: alexlarsson --- common/Makefile.am.inc | 2 + common/flatpak-common-types.h | 1 - common/flatpak-context.c | 1654 +++++++++++++++++++++++++++++++++ common/flatpak-context.h | 116 +++ common/flatpak-dir.h | 1 + common/flatpak-run.c | 1502 ------------------------------ common/flatpak-run.h | 40 +- 7 files changed, 1774 insertions(+), 1542 deletions(-) create mode 100644 common/flatpak-context.c create mode 100644 common/flatpak-context.h diff --git a/common/Makefile.am.inc b/common/Makefile.am.inc index 8e9b9162..dacdf477 100644 --- a/common/Makefile.am.inc +++ b/common/Makefile.am.inc @@ -39,6 +39,8 @@ libflatpak_common_la_SOURCES = \ common/flatpak-dir.h \ common/flatpak-run.c \ common/flatpak-run.h \ + common/flatpak-context.c \ + common/flatpak-context.h \ common/flatpak-portal-error.c \ common/flatpak-portal-error.h \ common/flatpak-utils.c \ diff --git a/common/flatpak-common-types.h b/common/flatpak-common-types.h index 22657850..f6ce9e16 100644 --- a/common/flatpak-common-types.h +++ b/common/flatpak-common-types.h @@ -28,7 +28,6 @@ typedef enum { typedef struct FlatpakDir FlatpakDir; typedef struct FlatpakDeploy FlatpakDeploy; -typedef struct FlatpakContext FlatpakContext; typedef struct FlatpakOciRegistry FlatpakOciRegistry; typedef struct _FlatpakOciManifest FlatpakOciManifest; diff --git a/common/flatpak-context.c b/common/flatpak-context.c new file mode 100644 index 00000000..02e0b5e3 --- /dev/null +++ b/common/flatpak-context.c @@ -0,0 +1,1654 @@ +/* + * Copyright © 2014-2018 Red Hat, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: + * Alexander Larsson + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "libglnx/libglnx.h" + +#include "flatpak-run.h" +#include "flatpak-proxy.h" +#include "flatpak-utils.h" +#include "flatpak-dir.h" +#include "flatpak-systemd-dbus.h" +#include "document-portal/xdp-dbus.h" +#include "lib/flatpak-error.h" + +/* Same order as enum */ +const char *flatpak_context_shares[] = { + "network", + "ipc", + NULL +}; + +/* Same order as enum */ +const char *flatpak_context_sockets[] = { + "x11", + "wayland", + "pulseaudio", + "session-bus", + "system-bus", + NULL +}; + +const char *flatpak_context_devices[] = { + "dri", + "all", + "kvm", + NULL +}; + +const char *flatpak_context_features[] = { + "devel", + "multiarch", + NULL +}; + +FlatpakContext * +flatpak_context_new (void) +{ + FlatpakContext *context; + + context = g_slice_new0 (FlatpakContext); + context->env_vars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + context->persistent = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + context->filesystems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + context->session_bus_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + context->system_bus_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + context->generic_policy = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify)g_strfreev); + + return context; +} + +void +flatpak_context_free (FlatpakContext *context) +{ + g_hash_table_destroy (context->env_vars); + g_hash_table_destroy (context->persistent); + g_hash_table_destroy (context->filesystems); + g_hash_table_destroy (context->session_bus_policy); + g_hash_table_destroy (context->system_bus_policy); + g_hash_table_destroy (context->generic_policy); + g_slice_free (FlatpakContext, context); +} + +static guint32 +flatpak_context_bitmask_from_string (const char *name, const char **names) +{ + guint32 i; + + for (i = 0; names[i] != NULL; i++) + { + if (strcmp (names[i], name) == 0) + return 1 << i; + } + + return 0; +} + +static char ** +flatpak_context_bitmask_to_string (guint32 enabled, guint32 valid, const char **names) +{ + guint32 i; + GPtrArray *array; + + array = g_ptr_array_new (); + + for (i = 0; names[i] != NULL; i++) + { + guint32 bitmask = 1 << i; + if (valid & bitmask) + { + if (enabled & bitmask) + g_ptr_array_add (array, g_strdup (names[i])); + else + g_ptr_array_add (array, g_strdup_printf ("!%s", names[i])); + } + } + + g_ptr_array_add (array, NULL); + return (char **) g_ptr_array_free (array, FALSE); +} + +static void +flatpak_context_bitmask_to_args (guint32 enabled, guint32 valid, const char **names, + const char *enable_arg, const char *disable_arg, + GPtrArray *args) +{ + guint32 i; + + for (i = 0; names[i] != NULL; i++) + { + guint32 bitmask = 1 << i; + if (valid & bitmask) + { + if (enabled & bitmask) + g_ptr_array_add (args, g_strdup_printf ("%s=%s", enable_arg, names[i])); + else + g_ptr_array_add (args, g_strdup_printf ("%s=%s", disable_arg, names[i])); + } + } +} + + +static FlatpakContextShares +flatpak_context_share_from_string (const char *string, GError **error) +{ + FlatpakContextShares shares = flatpak_context_bitmask_from_string (string, flatpak_context_shares); + + if (shares == 0) + { + g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_shares); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Unknown share type %s, valid types are: %s"), string, values); + } + + return shares; +} + +static char ** +flatpak_context_shared_to_string (FlatpakContextShares shares, FlatpakContextShares valid) +{ + return flatpak_context_bitmask_to_string (shares, valid, flatpak_context_shares); +} + +static void +flatpak_context_shared_to_args (FlatpakContextShares shares, + FlatpakContextShares valid, + GPtrArray *args) +{ + return flatpak_context_bitmask_to_args (shares, valid, flatpak_context_shares, "--share", "--unshare", args); +} + +static FlatpakPolicy +flatpak_policy_from_string (const char *string, GError **error) +{ + const char *policies[] = { "none", "see", "filtered", "talk", "own", NULL }; + int i; + g_autofree char *values = NULL; + + for (i = 0; policies[i]; i++) + { + if (strcmp (string, policies[i]) == 0) + return i; + } + + values = g_strjoinv (", ", (char **)policies); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Unknown policy type %s, valid types are: %s"), string, values); + + return -1; +} + +static const char * +flatpak_policy_to_string (FlatpakPolicy policy) +{ + if (policy == FLATPAK_POLICY_SEE) + return "see"; + if (policy == FLATPAK_POLICY_TALK) + return "talk"; + if (policy == FLATPAK_POLICY_OWN) + return "own"; + + return "none"; +} + +static gboolean +flatpak_verify_dbus_name (const char *name, GError **error) +{ + const char *name_part; + g_autofree char *tmp = NULL; + + if (g_str_has_suffix (name, ".*")) + { + tmp = g_strndup (name, strlen (name) - 2); + name_part = tmp; + } + else + { + name_part = name; + } + + if (g_dbus_is_name (name_part) && !g_dbus_is_unique_name (name_part)) + return TRUE; + + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Invalid dbus name %s\n"), name); + return FALSE; +} + +static FlatpakContextSockets +flatpak_context_socket_from_string (const char *string, GError **error) +{ + FlatpakContextSockets sockets = flatpak_context_bitmask_from_string (string, flatpak_context_sockets); + + if (sockets == 0) + { + g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_sockets); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Unknown socket type %s, valid types are: %s"), string, values); + } + + return sockets; +} + +static char ** +flatpak_context_sockets_to_string (FlatpakContextSockets sockets, FlatpakContextSockets valid) +{ + return flatpak_context_bitmask_to_string (sockets, valid, flatpak_context_sockets); +} + +static void +flatpak_context_sockets_to_args (FlatpakContextSockets sockets, + FlatpakContextSockets valid, + GPtrArray *args) +{ + return flatpak_context_bitmask_to_args (sockets, valid, flatpak_context_sockets, "--socket", "--nosocket", args); +} + +static FlatpakContextDevices +flatpak_context_device_from_string (const char *string, GError **error) +{ + FlatpakContextDevices devices = flatpak_context_bitmask_from_string (string, flatpak_context_devices); + + if (devices == 0) + { + g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_devices); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Unknown device type %s, valid types are: %s"), string, values); + } + return devices; +} + +static char ** +flatpak_context_devices_to_string (FlatpakContextDevices devices, FlatpakContextDevices valid) +{ + return flatpak_context_bitmask_to_string (devices, valid, flatpak_context_devices); +} + +static void +flatpak_context_devices_to_args (FlatpakContextDevices devices, + FlatpakContextDevices valid, + GPtrArray *args) +{ + return flatpak_context_bitmask_to_args (devices, valid, flatpak_context_devices, "--device", "--nodevice", args); +} + +static FlatpakContextFeatures +flatpak_context_feature_from_string (const char *string, GError **error) +{ + FlatpakContextFeatures feature = flatpak_context_bitmask_from_string (string, flatpak_context_features); + + if (feature == 0) + { + g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_features); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Unknown feature type %s, valid types are: %s"), string, values); + } + + return feature; +} + +static char ** +flatpak_context_features_to_string (FlatpakContextFeatures features, FlatpakContextFeatures valid) +{ + return flatpak_context_bitmask_to_string (features, valid, flatpak_context_features); +} + +static void +flatpak_context_features_to_args (FlatpakContextFeatures features, + FlatpakContextFeatures valid, + GPtrArray *args) +{ + return flatpak_context_bitmask_to_args (features, valid, flatpak_context_features, "--allow", "--disallow", args); +} + +static void +flatpak_context_add_shares (FlatpakContext *context, + FlatpakContextShares shares) +{ + context->shares_valid |= shares; + context->shares |= shares; +} + +static void +flatpak_context_remove_shares (FlatpakContext *context, + FlatpakContextShares shares) +{ + context->shares_valid |= shares; + context->shares &= ~shares; +} + +static void +flatpak_context_add_sockets (FlatpakContext *context, + FlatpakContextSockets sockets) +{ + context->sockets_valid |= sockets; + context->sockets |= sockets; +} + +static void +flatpak_context_remove_sockets (FlatpakContext *context, + FlatpakContextSockets sockets) +{ + context->sockets_valid |= sockets; + context->sockets &= ~sockets; +} + +static void +flatpak_context_add_devices (FlatpakContext *context, + FlatpakContextDevices devices) +{ + context->devices_valid |= devices; + context->devices |= devices; +} + +static void +flatpak_context_remove_devices (FlatpakContext *context, + FlatpakContextDevices devices) +{ + context->devices_valid |= devices; + context->devices &= ~devices; +} + +static void +flatpak_context_add_features (FlatpakContext *context, + FlatpakContextFeatures features) +{ + context->features_valid |= features; + context->features |= features; +} + +static void +flatpak_context_remove_features (FlatpakContext *context, + FlatpakContextFeatures features) +{ + context->features_valid |= features; + context->features &= ~features; +} + +static void +flatpak_context_set_env_var (FlatpakContext *context, + const char *name, + const char *value) +{ + g_hash_table_insert (context->env_vars, g_strdup (name), g_strdup (value)); +} + +void +flatpak_context_set_session_bus_policy (FlatpakContext *context, + const char *name, + FlatpakPolicy policy) +{ + g_hash_table_insert (context->session_bus_policy, g_strdup (name), GINT_TO_POINTER (policy)); +} + +void +flatpak_context_set_system_bus_policy (FlatpakContext *context, + const char *name, + FlatpakPolicy policy) +{ + g_hash_table_insert (context->system_bus_policy, g_strdup (name), GINT_TO_POINTER (policy)); +} + +static void +flatpak_context_apply_generic_policy (FlatpakContext *context, + const char *key, + const char *value) +{ + GPtrArray *new = g_ptr_array_new (); + const char **old_v; + int i; + + g_assert (strchr (key, '.') != NULL); + + old_v = g_hash_table_lookup (context->generic_policy, key); + for (i = 0; old_v != NULL && old_v[i] != NULL; i++) + { + const char *old = old_v[i]; + const char *cmp1 = old; + const char *cmp2 = value; + if (*cmp1 == '!') + cmp1++; + if (*cmp2 == '!') + cmp2++; + if (strcmp (cmp1, cmp2) != 0) + g_ptr_array_add (new, g_strdup (old)); + } + + g_ptr_array_add (new, g_strdup (value)); + g_ptr_array_add (new, NULL); + + g_hash_table_insert (context->generic_policy, g_strdup (key), + g_ptr_array_free (new, FALSE)); +} + +static void +flatpak_context_set_persistent (FlatpakContext *context, + const char *path) +{ + g_hash_table_insert (context->persistent, g_strdup (path), GINT_TO_POINTER (1)); +} + +static gboolean +get_xdg_dir_from_prefix (const char *prefix, + const char **where, + const char **dir) +{ + if (strcmp (prefix, "xdg-data") == 0) + { + if (where) + *where = "data"; + if (dir) + *dir = g_get_user_data_dir (); + return TRUE; + } + if (strcmp (prefix, "xdg-cache") == 0) + { + if (where) + *where = "cache"; + if (dir) + *dir = g_get_user_cache_dir (); + return TRUE; + } + if (strcmp (prefix, "xdg-config") == 0) + { + if (where) + *where = "config"; + if (dir) + *dir = g_get_user_config_dir (); + return TRUE; + } + return FALSE; +} + +static gboolean +get_xdg_user_dir_from_string (const char *filesystem, + const char **config_key, + const char **suffix, + const char **dir) +{ + char *slash; + const char *rest; + g_autofree char *prefix = NULL; + gsize len; + + slash = strchr (filesystem, '/'); + + if (slash) + len = slash - filesystem; + else + len = strlen (filesystem); + + rest = filesystem + len; + while (*rest == '/') + rest++; + + if (suffix) + *suffix = rest; + + prefix = g_strndup (filesystem, len); + + if (strcmp (prefix, "xdg-desktop") == 0) + { + if (config_key) + *config_key = "XDG_DESKTOP_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); + return TRUE; + } + if (strcmp (prefix, "xdg-documents") == 0) + { + if (config_key) + *config_key = "XDG_DOCUMENTS_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS); + return TRUE; + } + if (strcmp (prefix, "xdg-download") == 0) + { + if (config_key) + *config_key = "XDG_DOWNLOAD_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD); + return TRUE; + } + if (strcmp (prefix, "xdg-music") == 0) + { + if (config_key) + *config_key = "XDG_MUSIC_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC); + return TRUE; + } + if (strcmp (prefix, "xdg-pictures") == 0) + { + if (config_key) + *config_key = "XDG_PICTURES_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); + return TRUE; + } + if (strcmp (prefix, "xdg-public-share") == 0) + { + if (config_key) + *config_key = "XDG_PUBLICSHARE_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE); + return TRUE; + } + if (strcmp (prefix, "xdg-templates") == 0) + { + if (config_key) + *config_key = "XDG_TEMPLATES_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES); + return TRUE; + } + if (strcmp (prefix, "xdg-videos") == 0) + { + if (config_key) + *config_key = "XDG_VIDEOS_DIR"; + if (dir) + *dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS); + return TRUE; + } + if (get_xdg_dir_from_prefix (prefix, NULL, dir)) + { + if (config_key) + *config_key = NULL; + return TRUE; + } + /* Don't support xdg-run without suffix, because that doesn't work */ + if (strcmp (prefix, "xdg-run") == 0 && + *rest != 0) + { + if (config_key) + *config_key = NULL; + if (dir) + *dir = g_get_user_runtime_dir (); + return TRUE; + } + + return FALSE; +} + +static char * +parse_filesystem_flags (const char *filesystem, FlatpakFilesystemMode *mode) +{ + gsize len = strlen (filesystem); + + if (mode) + *mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE; + + if (g_str_has_suffix (filesystem, ":ro")) + { + len -= 3; + if (mode) + *mode = FLATPAK_FILESYSTEM_MODE_READ_ONLY; + } + else if (g_str_has_suffix (filesystem, ":rw")) + { + len -= 3; + if (mode) + *mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE; + } + else if (g_str_has_suffix (filesystem, ":create")) + { + len -= 7; + if (mode) + *mode = FLATPAK_FILESYSTEM_MODE_CREATE; + } + + return g_strndup (filesystem, len); +} + +static gboolean +flatpak_context_verify_filesystem (const char *filesystem_and_mode, + GError **error) +{ + g_autofree char *filesystem = parse_filesystem_flags (filesystem_and_mode, NULL); + + if (strcmp (filesystem, "host") == 0) + return TRUE; + if (strcmp (filesystem, "home") == 0) + return TRUE; + if (get_xdg_user_dir_from_string (filesystem, NULL, NULL, NULL)) + return TRUE; + if (g_str_has_prefix (filesystem, "~/")) + return TRUE; + if (g_str_has_prefix (filesystem, "/")) + return TRUE; + + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Unknown filesystem location %s, valid locations are: host, home, xdg-*[/...], ~/dir, /dir"), filesystem); + return FALSE; +} + +static void +flatpak_context_add_filesystem (FlatpakContext *context, + const char *what) +{ + FlatpakFilesystemMode mode; + char *fs = parse_filesystem_flags (what, &mode); + + g_hash_table_insert (context->filesystems, fs, GINT_TO_POINTER (mode)); +} + +static void +flatpak_context_remove_filesystem (FlatpakContext *context, + const char *what) +{ + g_hash_table_insert (context->filesystems, + parse_filesystem_flags (what, NULL), + NULL); +} + +void +flatpak_context_merge (FlatpakContext *context, + FlatpakContext *other) +{ + GHashTableIter iter; + gpointer key, value; + + context->shares &= ~other->shares_valid; + context->shares |= other->shares; + context->shares_valid |= other->shares_valid; + context->sockets &= ~other->sockets_valid; + context->sockets |= other->sockets; + context->sockets_valid |= other->sockets_valid; + context->devices &= ~other->devices_valid; + context->devices |= other->devices; + context->devices_valid |= other->devices_valid; + context->features &= ~other->features_valid; + context->features |= other->features; + context->features_valid |= other->features_valid; + + g_hash_table_iter_init (&iter, other->env_vars); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->env_vars, g_strdup (key), g_strdup (value)); + + g_hash_table_iter_init (&iter, other->persistent); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->persistent, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->filesystems); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->filesystems, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->session_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->session_bus_policy, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->system_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->system_bus_policy, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->system_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (context->system_bus_policy, g_strdup (key), value); + + g_hash_table_iter_init (&iter, other->generic_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char **policy_values = (const char **)value; + int i; + + for (i = 0; policy_values[i] != NULL; i++) + flatpak_context_apply_generic_policy (context, (char *)key, policy_values[i]); + } + +} + +static gboolean +option_share_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextShares share; + + share = flatpak_context_share_from_string (value, error); + if (share == 0) + return FALSE; + + flatpak_context_add_shares (context, share); + + return TRUE; +} + +static gboolean +option_unshare_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextShares share; + + share = flatpak_context_share_from_string (value, error); + if (share == 0) + return FALSE; + + flatpak_context_remove_shares (context, share); + + return TRUE; +} + +static gboolean +option_socket_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextSockets socket; + + socket = flatpak_context_socket_from_string (value, error); + if (socket == 0) + return FALSE; + + flatpak_context_add_sockets (context, socket); + + return TRUE; +} + +static gboolean +option_nosocket_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextSockets socket; + + socket = flatpak_context_socket_from_string (value, error); + if (socket == 0) + return FALSE; + + flatpak_context_remove_sockets (context, socket); + + return TRUE; +} + +static gboolean +option_device_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextDevices device; + + device = flatpak_context_device_from_string (value, error); + if (device == 0) + return FALSE; + + flatpak_context_add_devices (context, device); + + return TRUE; +} + +static gboolean +option_nodevice_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextDevices device; + + device = flatpak_context_device_from_string (value, error); + if (device == 0) + return FALSE; + + flatpak_context_remove_devices (context, device); + + return TRUE; +} + +static gboolean +option_allow_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextFeatures feature; + + feature = flatpak_context_feature_from_string (value, error); + if (feature == 0) + return FALSE; + + flatpak_context_add_features (context, feature); + + return TRUE; +} + +static gboolean +option_disallow_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + FlatpakContextFeatures feature; + + feature = flatpak_context_feature_from_string (value, error); + if (feature == 0) + return FALSE; + + flatpak_context_remove_features (context, feature); + + return TRUE; +} + +static gboolean +option_filesystem_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + if (!flatpak_context_verify_filesystem (value, error)) + return FALSE; + + flatpak_context_add_filesystem (context, value); + return TRUE; +} + +static gboolean +option_nofilesystem_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + if (!flatpak_context_verify_filesystem (value, error)) + return FALSE; + + flatpak_context_remove_filesystem (context, value); + return TRUE; +} + +static gboolean +option_env_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + g_auto(GStrv) split = g_strsplit (value, "=", 2); + + if (split == NULL || split[0] == NULL || split[0][0] == 0 || split[1] == NULL) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Invalid env format %s"), value); + return FALSE; + } + + flatpak_context_set_env_var (context, split[0], split[1]); + return TRUE; +} + +static gboolean +option_own_name_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + if (!flatpak_verify_dbus_name (value, error)) + return FALSE; + + flatpak_context_set_session_bus_policy (context, value, FLATPAK_POLICY_OWN); + return TRUE; +} + +static gboolean +option_talk_name_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + if (!flatpak_verify_dbus_name (value, error)) + return FALSE; + + flatpak_context_set_session_bus_policy (context, value, FLATPAK_POLICY_TALK); + return TRUE; +} + +static gboolean +option_system_own_name_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + if (!flatpak_verify_dbus_name (value, error)) + return FALSE; + + flatpak_context_set_system_bus_policy (context, value, FLATPAK_POLICY_OWN); + return TRUE; +} + +static gboolean +option_system_talk_name_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + if (!flatpak_verify_dbus_name (value, error)) + return FALSE; + + flatpak_context_set_system_bus_policy (context, value, FLATPAK_POLICY_TALK); + return TRUE; +} + +static gboolean +option_add_generic_policy_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + char *t; + g_autofree char *key = NULL; + const char *policy_value; + + t = strchr (value, '='); + if (t == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + policy_value = t + 1; + key = g_strndup (value, t - value); + if (strchr (key, '.') == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + + if (policy_value[0] == '!') + return flatpak_fail (error, "--policy values can't start with \"!\""); + + flatpak_context_apply_generic_policy (context, key, policy_value); + + return TRUE; +} + +static gboolean +option_remove_generic_policy_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + char *t; + g_autofree char *key = NULL; + const char *policy_value; + g_autofree char *extended_value = NULL; + + t = strchr (value, '='); + if (t == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + policy_value = t + 1; + key = g_strndup (value, t - value); + if (strchr (key, '.') == NULL) + return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); + + if (policy_value[0] == '!') + return flatpak_fail (error, "--policy values can't start with \"!\""); + + extended_value = g_strconcat ("!", policy_value, NULL); + + flatpak_context_apply_generic_policy (context, key, extended_value); + + return TRUE; +} + +static gboolean +option_persist_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + FlatpakContext *context = data; + + flatpak_context_set_persistent (context, value); + return TRUE; +} + +static gboolean option_no_desktop_deprecated; + +static GOptionEntry context_options[] = { + { "share", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_share_cb, N_("Share with host"), N_("SHARE") }, + { "unshare", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_unshare_cb, N_("Unshare with host"), N_("SHARE") }, + { "socket", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_socket_cb, N_("Expose socket to app"), N_("SOCKET") }, + { "nosocket", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nosocket_cb, N_("Don't expose socket to app"), N_("SOCKET") }, + { "device", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_device_cb, N_("Expose device to app"), N_("DEVICE") }, + { "nodevice", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nodevice_cb, N_("Don't expose device to app"), N_("DEVICE") }, + { "allow", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_allow_cb, N_("Allow feature"), N_("FEATURE") }, + { "disallow", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_disallow_cb, N_("Don't allow feature"), N_("FEATURE") }, + { "filesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_filesystem_cb, N_("Expose filesystem to app (:ro for read-only)"), N_("FILESYSTEM[:ro]") }, + { "nofilesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nofilesystem_cb, N_("Don't expose filesystem to app"), N_("FILESYSTEM") }, + { "env", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_env_cb, N_("Set environment variable"), N_("VAR=VALUE") }, + { "own-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_own_name_cb, N_("Allow app to own name on the session bus"), N_("DBUS_NAME") }, + { "talk-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_talk_name_cb, N_("Allow app to talk to name on the session bus"), N_("DBUS_NAME") }, + { "system-own-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_system_own_name_cb, N_("Allow app to own name on the system bus"), N_("DBUS_NAME") }, + { "system-talk-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_system_talk_name_cb, N_("Allow app to talk to name on the system bus"), N_("DBUS_NAME") }, + { "add-policy", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_add_generic_policy_cb, N_("Add generic policy option"), N_("SUBSYSTEM.KEY=VALUE") }, + { "remove-policy", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_remove_generic_policy_cb, N_("Remove generic policy option"), N_("SUBSYSTEM.KEY=VALUE") }, + { "persist", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_persist_cb, N_("Persist home directory"), N_("FILENAME") }, + /* This is not needed/used anymore, so hidden, but we accept it for backwards compat */ + { "no-desktop", 0, G_OPTION_FLAG_IN_MAIN | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &option_no_desktop_deprecated, N_("Don't require a running session (no cgroups creation)"), NULL }, + { NULL } +}; + +void +flatpak_context_complete (FlatpakContext *context, FlatpakCompletion *completion) +{ + flatpak_complete_options (completion, context_options); +} + +GOptionGroup * +flatpak_context_get_options (FlatpakContext *context) +{ + GOptionGroup *group; + + group = g_option_group_new ("environment", + "Runtime Environment", + "Runtime Environment", + context, + NULL); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); + + g_option_group_add_entries (group, context_options); + + return group; +} + +static const char * +parse_negated (const char *option, gboolean *negated) +{ + if (option[0] == '!') + { + option++; + *negated = TRUE; + } + else + { + *negated = FALSE; + } + return option; +} + +/* + * Merge the FLATPAK_METADATA_GROUP_CONTEXT, + * FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, + * FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY and + * FLATPAK_METADATA_GROUP_ENVIRONMENT groups, and all groups starting + * with FLATPAK_METADATA_GROUP_PREFIX_POLICY, from metakey into context. + * + * This is a merge, not a replace! + */ +gboolean +flatpak_context_load_metadata (FlatpakContext *context, + GKeyFile *metakey, + GError **error) +{ + gboolean remove; + g_auto(GStrv) groups = NULL; + int i; + + if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_SHARED, NULL)) + { + g_auto(GStrv) shares = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_SHARED, NULL, error); + if (shares == NULL) + return FALSE; + + for (i = 0; shares[i] != NULL; i++) + { + FlatpakContextShares share; + + share = flatpak_context_share_from_string (parse_negated (shares[i], &remove), NULL); + if (share == 0) + g_debug ("Unknown share type %s", shares[i]); + else + { + if (remove) + flatpak_context_remove_shares (context, share); + else + flatpak_context_add_shares (context, share); + } + } + } + + if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_SOCKETS, NULL)) + { + g_auto(GStrv) sockets = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_SOCKETS, NULL, error); + if (sockets == NULL) + return FALSE; + + for (i = 0; sockets[i] != NULL; i++) + { + FlatpakContextSockets socket = flatpak_context_socket_from_string (parse_negated (sockets[i], &remove), NULL); + if (socket == 0) + g_debug ("Unknown socket type %s", sockets[i]); + else + { + if (remove) + flatpak_context_remove_sockets (context, socket); + else + flatpak_context_add_sockets (context, socket); + } + } + } + + if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_DEVICES, NULL)) + { + g_auto(GStrv) devices = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_DEVICES, NULL, error); + if (devices == NULL) + return FALSE; + + + for (i = 0; devices[i] != NULL; i++) + { + FlatpakContextDevices device = flatpak_context_device_from_string (parse_negated (devices[i], &remove), NULL); + if (device == 0) + g_debug ("Unknown device type %s", devices[i]); + else + { + if (remove) + flatpak_context_remove_devices (context, device); + else + flatpak_context_add_devices (context, device); + } + } + } + + if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_FEATURES, NULL)) + { + g_auto(GStrv) features = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_FEATURES, NULL, error); + if (features == NULL) + return FALSE; + + + for (i = 0; features[i] != NULL; i++) + { + FlatpakContextFeatures feature = flatpak_context_feature_from_string (parse_negated (features[i], &remove), NULL); + if (feature == 0) + g_debug ("Unknown feature type %s", features[i]); + else + { + if (remove) + flatpak_context_remove_features (context, feature); + else + flatpak_context_add_features (context, feature); + } + } + } + + if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_FILESYSTEMS, NULL)) + { + g_auto(GStrv) filesystems = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_FILESYSTEMS, NULL, error); + if (filesystems == NULL) + return FALSE; + + for (i = 0; filesystems[i] != NULL; i++) + { + const char *fs = parse_negated (filesystems[i], &remove); + if (!flatpak_context_verify_filesystem (fs, NULL)) + g_debug ("Unknown filesystem type %s", filesystems[i]); + else + { + if (remove) + flatpak_context_remove_filesystem (context, fs); + else + flatpak_context_add_filesystem (context, fs); + } + } + } + + if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_PERSISTENT, NULL)) + { + g_auto(GStrv) persistent = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_PERSISTENT, NULL, error); + if (persistent == NULL) + return FALSE; + + for (i = 0; persistent[i] != NULL; i++) + flatpak_context_set_persistent (context, persistent[i]); + } + + if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY)) + { + g_auto(GStrv) keys = NULL; + gsize i, keys_count; + + keys = g_key_file_get_keys (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, &keys_count, NULL); + for (i = 0; i < keys_count; i++) + { + const char *key = keys[i]; + g_autofree char *value = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, key, NULL); + FlatpakPolicy policy; + + if (!flatpak_verify_dbus_name (key, error)) + return FALSE; + + policy = flatpak_policy_from_string (value, NULL); + if ((int) policy != -1) + flatpak_context_set_session_bus_policy (context, key, policy); + } + } + + if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY)) + { + g_auto(GStrv) keys = NULL; + gsize i, keys_count; + + keys = g_key_file_get_keys (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, &keys_count, NULL); + for (i = 0; i < keys_count; i++) + { + const char *key = keys[i]; + g_autofree char *value = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, key, NULL); + FlatpakPolicy policy; + + if (!flatpak_verify_dbus_name (key, error)) + return FALSE; + + policy = flatpak_policy_from_string (value, NULL); + if ((int) policy != -1) + flatpak_context_set_system_bus_policy (context, key, policy); + } + } + + if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT)) + { + g_auto(GStrv) keys = NULL; + gsize i, keys_count; + + keys = g_key_file_get_keys (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT, &keys_count, NULL); + for (i = 0; i < keys_count; i++) + { + const char *key = keys[i]; + g_autofree char *value = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT, key, NULL); + + flatpak_context_set_env_var (context, key, value); + } + } + + groups = g_key_file_get_groups (metakey, NULL); + for (i = 0; groups[i] != NULL; i++) + { + const char *group = groups[i]; + const char *subsystem; + int j; + + if (g_str_has_prefix (group, FLATPAK_METADATA_GROUP_PREFIX_POLICY)) + { + g_auto(GStrv) keys = NULL; + subsystem = group + strlen (FLATPAK_METADATA_GROUP_PREFIX_POLICY); + keys = g_key_file_get_keys (metakey, group, NULL, NULL); + for (j = 0; keys != NULL && keys[j] != NULL; j++) + { + const char *key = keys[j]; + g_autofree char *policy_key = g_strdup_printf ("%s.%s", subsystem, key); + g_auto(GStrv) values = NULL; + int k; + + values = g_key_file_get_string_list (metakey, group, key, NULL, NULL); + for (k = 0; values != NULL && values[k] != NULL; k++) + flatpak_context_apply_generic_policy (context, policy_key, + values[k]); + } + } + } + + return TRUE; +} + +/* + * Save the FLATPAK_METADATA_GROUP_CONTEXT, + * FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, + * FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY and + * FLATPAK_METADATA_GROUP_ENVIRONMENT groups, and all groups starting + * with FLATPAK_METADATA_GROUP_PREFIX_POLICY, into metakey + */ +void +flatpak_context_save_metadata (FlatpakContext *context, + gboolean flatten, + GKeyFile *metakey) +{ + g_auto(GStrv) shared = NULL; + g_auto(GStrv) sockets = NULL; + g_auto(GStrv) devices = NULL; + g_auto(GStrv) features = NULL; + GHashTableIter iter; + gpointer key, value; + FlatpakContextShares shares_mask = context->shares; + FlatpakContextShares shares_valid = context->shares_valid; + FlatpakContextSockets sockets_mask = context->sockets; + FlatpakContextSockets sockets_valid = context->sockets_valid; + FlatpakContextDevices devices_mask = context->devices; + FlatpakContextDevices devices_valid = context->devices_valid; + FlatpakContextFeatures features_mask = context->features; + FlatpakContextFeatures features_valid = context->features; + g_auto(GStrv) groups = NULL; + int i; + + if (flatten) + { + /* A flattened format means we don't expect this to be merged on top of + another context. In that case we never need to negate any flags. + We calculate this by removing the zero parts of the mask from the valid set. + */ + /* First we make sure only the valid parts of the mask are set, in case we + got some leftover */ + shares_mask &= shares_valid; + sockets_mask &= sockets_valid; + devices_mask &= devices_valid; + features_mask &= features_valid; + + /* Then just set the valid set to be the mask set */ + shares_valid = shares_mask; + sockets_valid = sockets_mask; + devices_valid = devices_mask; + features_valid = features_mask; + } + + shared = flatpak_context_shared_to_string (shares_mask, shares_valid); + sockets = flatpak_context_sockets_to_string (sockets_mask, sockets_valid); + devices = flatpak_context_devices_to_string (devices_mask, devices_valid); + features = flatpak_context_features_to_string (features_mask, features_valid); + + if (shared[0] != NULL) + { + g_key_file_set_string_list (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_SHARED, + (const char * const *) shared, g_strv_length (shared)); + } + else + { + g_key_file_remove_key (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_SHARED, + NULL); + } + + if (sockets[0] != NULL) + { + g_key_file_set_string_list (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_SOCKETS, + (const char * const *) sockets, g_strv_length (sockets)); + } + else + { + g_key_file_remove_key (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_SOCKETS, + NULL); + } + + if (devices[0] != NULL) + { + g_key_file_set_string_list (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_DEVICES, + (const char * const *) devices, g_strv_length (devices)); + } + else + { + g_key_file_remove_key (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_DEVICES, + NULL); + } + + if (features[0] != NULL) + { + g_key_file_set_string_list (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_FEATURES, + (const char * const *) features, g_strv_length (features)); + } + else + { + g_key_file_remove_key (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_FEATURES, + NULL); + } + + if (g_hash_table_size (context->filesystems) > 0) + { + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + + g_hash_table_iter_init (&iter, context->filesystems); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + FlatpakFilesystemMode mode = GPOINTER_TO_INT (value); + + if (mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) + g_ptr_array_add (array, g_strconcat (key, ":ro", NULL)); + else if (mode == FLATPAK_FILESYSTEM_MODE_CREATE) + g_ptr_array_add (array, g_strconcat (key, ":create", NULL)); + else if (value != NULL) + g_ptr_array_add (array, g_strdup (key)); + else + g_ptr_array_add (array, g_strconcat ("!", key, NULL)); + } + + g_key_file_set_string_list (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_FILESYSTEMS, + (const char * const *) array->pdata, array->len); + } + else + { + g_key_file_remove_key (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_FILESYSTEMS, + NULL); + } + + if (g_hash_table_size (context->persistent) > 0) + { + g_autofree char **keys = (char **) g_hash_table_get_keys_as_array (context->persistent, NULL); + + g_key_file_set_string_list (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_PERSISTENT, + (const char * const *) keys, g_strv_length (keys)); + } + else + { + g_key_file_remove_key (metakey, + FLATPAK_METADATA_GROUP_CONTEXT, + FLATPAK_METADATA_KEY_PERSISTENT, + NULL); + } + + g_key_file_remove_group (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, NULL); + g_hash_table_iter_init (&iter, context->session_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + FlatpakPolicy policy = GPOINTER_TO_INT (value); + if (policy > 0) + g_key_file_set_string (metakey, + FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, + (char *) key, flatpak_policy_to_string (policy)); + } + + g_key_file_remove_group (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, NULL); + g_hash_table_iter_init (&iter, context->system_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + FlatpakPolicy policy = GPOINTER_TO_INT (value); + if (policy > 0) + g_key_file_set_string (metakey, + FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, + (char *) key, flatpak_policy_to_string (policy)); + } + + g_key_file_remove_group (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT, NULL); + g_hash_table_iter_init (&iter, context->env_vars); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + g_key_file_set_string (metakey, + FLATPAK_METADATA_GROUP_ENVIRONMENT, + (char *) key, (char *) value); + } + + + groups = g_key_file_get_groups (metakey, NULL); + for (i = 0; groups[i] != NULL; i++) + { + const char *group = groups[i]; + if (g_str_has_prefix (group, FLATPAK_METADATA_GROUP_PREFIX_POLICY)) + g_key_file_remove_group (metakey, group, NULL); + } + + g_hash_table_iter_init (&iter, context->generic_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + g_auto(GStrv) parts = g_strsplit ((const char *)key, ".", 2); + g_autofree char *group = NULL; + g_assert (parts[1] != NULL); + const char **policy_values = (const char **)value; + g_autoptr(GPtrArray) new = g_ptr_array_new (); + + for (i = 0; policy_values[i] != NULL; i++) + { + const char *policy_value = policy_values[i]; + + if (!flatten || policy_value[0] != '!') + g_ptr_array_add (new, (char *)policy_value); + } + + if (new->len > 0) + { + group = g_strconcat (FLATPAK_METADATA_GROUP_PREFIX_POLICY, + parts[0], NULL); + g_key_file_set_string_list (metakey, group, parts[1], + (const char * const*)new->pdata, + new->len); + } + } +} + +void +flatpak_context_allow_host_fs (FlatpakContext *context) +{ + flatpak_context_add_filesystem (context, "host"); +} + +gboolean +flatpak_context_get_needs_session_bus_proxy (FlatpakContext *context) +{ + return g_hash_table_size (context->session_bus_policy) > 0; +} + +gboolean +flatpak_context_get_needs_system_bus_proxy (FlatpakContext *context) +{ + return g_hash_table_size (context->system_bus_policy) > 0; +} + +void +flatpak_context_to_args (FlatpakContext *context, + GPtrArray *args) +{ + GHashTableIter iter; + gpointer key, value; + + flatpak_context_shared_to_args (context->shares, context->shares_valid, args); + flatpak_context_sockets_to_args (context->sockets, context->sockets_valid, args); + flatpak_context_devices_to_args (context->devices, context->devices_valid, args); + flatpak_context_features_to_args (context->features, context->features_valid, args); + + g_hash_table_iter_init (&iter, context->env_vars); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_ptr_array_add (args, g_strdup_printf ("--env=%s=%s", (char *)key, (char *)value)); + + g_hash_table_iter_init (&iter, context->persistent); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_ptr_array_add (args, g_strdup_printf ("--persist=%s", (char *)key)); + + g_hash_table_iter_init (&iter, context->session_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char *name = key; + FlatpakPolicy policy = GPOINTER_TO_INT (value); + + g_ptr_array_add (args, g_strdup_printf ("--%s-name=%s", flatpak_policy_to_string (policy), name)); + } + + g_hash_table_iter_init (&iter, context->system_bus_policy); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char *name = key; + FlatpakPolicy policy = GPOINTER_TO_INT (value); + + g_ptr_array_add (args, g_strdup_printf ("--system-%s-name=%s", flatpak_policy_to_string (policy), name)); + } + + g_hash_table_iter_init (&iter, context->filesystems); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + FlatpakFilesystemMode mode = GPOINTER_TO_INT (value); + + if (mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) + g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s:ro", (char *)key)); + else if (mode == FLATPAK_FILESYSTEM_MODE_READ_WRITE) + g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s", (char *)key)); + else if (mode == FLATPAK_FILESYSTEM_MODE_CREATE) + g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s:create", (char *)key)); + else + g_ptr_array_add (args, g_strdup_printf ("--nofilesystem=%s", (char *)key)); + } +} diff --git a/common/flatpak-context.h b/common/flatpak-context.h new file mode 100644 index 00000000..178a57c9 --- /dev/null +++ b/common/flatpak-context.h @@ -0,0 +1,116 @@ +/* + * Copyright © 2014-2018 Red Hat, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: + * Alexander Larsson + */ + +#ifndef __FLATPAK_CONTEXT_H__ +#define __FLATPAK_CONTEXT_H__ + +#include "libglnx/libglnx.h" +#include "dbus-proxy/flatpak-proxy.h" +#include "flatpak-utils.h" + +typedef struct FlatpakContext FlatpakContext; + +typedef enum { + FLATPAK_CONTEXT_SHARED_NETWORK = 1 << 0, + FLATPAK_CONTEXT_SHARED_IPC = 1 << 1, +} FlatpakContextShares; + +/* In numerical order of more privs */ +typedef enum { + FLATPAK_FILESYSTEM_MODE_READ_ONLY = 1, + FLATPAK_FILESYSTEM_MODE_READ_WRITE = 2, + FLATPAK_FILESYSTEM_MODE_CREATE = 3, +} FlatpakFilesystemMode; + +typedef enum { + FLATPAK_CONTEXT_SOCKET_X11 = 1 << 0, + FLATPAK_CONTEXT_SOCKET_WAYLAND = 1 << 1, + FLATPAK_CONTEXT_SOCKET_PULSEAUDIO = 1 << 2, + FLATPAK_CONTEXT_SOCKET_SESSION_BUS = 1 << 3, + FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS = 1 << 4, +} FlatpakContextSockets; + +typedef enum { + FLATPAK_CONTEXT_DEVICE_DRI = 1 << 0, + FLATPAK_CONTEXT_DEVICE_ALL = 1 << 1, + FLATPAK_CONTEXT_DEVICE_KVM = 1 << 2, +} FlatpakContextDevices; + +typedef enum { + FLATPAK_CONTEXT_FEATURE_DEVEL = 1 << 0, + FLATPAK_CONTEXT_FEATURE_MULTIARCH = 1 << 1, +} FlatpakContextFeatures; + +struct FlatpakContext +{ + FlatpakContextShares shares; + FlatpakContextShares shares_valid; + FlatpakContextSockets sockets; + FlatpakContextSockets sockets_valid; + FlatpakContextDevices devices; + FlatpakContextDevices devices_valid; + FlatpakContextFeatures features; + FlatpakContextFeatures features_valid; + GHashTable *env_vars; + GHashTable *persistent; + GHashTable *filesystems; + GHashTable *session_bus_policy; + GHashTable *system_bus_policy; + GHashTable *generic_policy; +}; + +extern const char *flatpak_context_sockets[]; +extern const char *flatpak_context_devices[]; +extern const char *flatpak_context_features[]; +extern const char *flatpak_context_shares[]; + +FlatpakContext *flatpak_context_new (void); +void flatpak_context_free (FlatpakContext *context); +void flatpak_context_merge (FlatpakContext *context, + FlatpakContext *other); +GOptionGroup *flatpak_context_get_options (FlatpakContext *context); +void flatpak_context_complete (FlatpakContext *context, + FlatpakCompletion *completion); +gboolean flatpak_context_load_metadata (FlatpakContext *context, + GKeyFile *metakey, + GError **error); +void flatpak_context_save_metadata (FlatpakContext *context, + gboolean flatten, + GKeyFile *metakey); +void flatpak_context_allow_host_fs (FlatpakContext *context); +void flatpak_context_set_session_bus_policy (FlatpakContext *context, + const char *name, + FlatpakPolicy policy); +void flatpak_context_set_system_bus_policy (FlatpakContext *context, + const char *name, + FlatpakPolicy policy); +void flatpak_context_to_args (FlatpakContext *context, + GPtrArray *args); +gboolean flatpak_context_get_needs_session_bus_proxy (FlatpakContext *context); +gboolean flatpak_context_get_needs_system_bus_proxy (FlatpakContext *context); + +FlatpakContext *flatpak_context_load_for_deploy (FlatpakDeploy *deploy, + GError **error); +FlatpakContext *flatpak_context_load_for_app (const char *app_id, + GError **error); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakContext, flatpak_context_free) + +#endif /* __FLATPAK_CONTEXT_H__ */ diff --git a/common/flatpak-dir.h b/common/flatpak-dir.h index fd6102bc..9a44e7f1 100644 --- a/common/flatpak-dir.h +++ b/common/flatpak-dir.h @@ -25,6 +25,7 @@ #include "libglnx/libglnx.h" #include +#include #define FLATPAK_TYPE_DIR flatpak_dir_get_type () #define FLATPAK_DIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLATPAK_TYPE_DIR, FlatpakDir)) diff --git a/common/flatpak-run.c b/common/flatpak-run.c index bd4ddeed..6e4977a4 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -55,44 +55,6 @@ #define DEFAULT_SHELL "/bin/sh" -typedef enum { - FLATPAK_CONTEXT_SHARED_NETWORK = 1 << 0, - FLATPAK_CONTEXT_SHARED_IPC = 1 << 1, -} FlatpakContextShares; - -/* In numerical order of more privs */ -typedef enum { - FLATPAK_FILESYSTEM_MODE_READ_ONLY = 1, - FLATPAK_FILESYSTEM_MODE_READ_WRITE = 2, - FLATPAK_FILESYSTEM_MODE_CREATE = 3, -} FlatpakFilesystemMode; - - -/* Same order as enum */ -const char *flatpak_context_shares[] = { - "network", - "ipc", - NULL -}; - -typedef enum { - FLATPAK_CONTEXT_SOCKET_X11 = 1 << 0, - FLATPAK_CONTEXT_SOCKET_WAYLAND = 1 << 1, - FLATPAK_CONTEXT_SOCKET_PULSEAUDIO = 1 << 2, - FLATPAK_CONTEXT_SOCKET_SESSION_BUS = 1 << 3, - FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS = 1 << 4, -} FlatpakContextSockets; - -/* Same order as enum */ -const char *flatpak_context_sockets[] = { - "x11", - "wayland", - "pulseaudio", - "session-bus", - "system-bus", - NULL -}; - const char *dont_mount_in_root[] = { ".", "..", "lib", "lib32", "lib64", "bin", "sbin", "usr", "boot", "root", "tmp", "etc", "app", "run", "proc", "sys", "dev", "var", NULL @@ -104,30 +66,6 @@ const char *dont_export_in[] = { "/lib", "/lib32", "/lib64", "/bin", "/sbin", "/usr", "/etc", "/app", "/dev", NULL }; -typedef enum { - FLATPAK_CONTEXT_DEVICE_DRI = 1 << 0, - FLATPAK_CONTEXT_DEVICE_ALL = 1 << 1, - FLATPAK_CONTEXT_DEVICE_KVM = 1 << 2, -} FlatpakContextDevices; - -const char *flatpak_context_devices[] = { - "dri", - "all", - "kvm", - NULL -}; - -typedef enum { - FLATPAK_CONTEXT_FEATURE_DEVEL = 1 << 0, - FLATPAK_CONTEXT_FEATURE_MULTIARCH = 1 << 1, -} FlatpakContextFeatures; - -const char *flatpak_context_features[] = { - "devel", - "multiarch", - NULL -}; - static gboolean add_dbus_proxy_args (GPtrArray *argv_array, GPtrArray *session_dbus_proxy_argv, @@ -140,161 +78,6 @@ add_dbus_proxy_args (GPtrArray *argv_array, const char *app_info_path, GError **error); -struct FlatpakContext -{ - FlatpakContextShares shares; - FlatpakContextShares shares_valid; - FlatpakContextSockets sockets; - FlatpakContextSockets sockets_valid; - FlatpakContextDevices devices; - FlatpakContextDevices devices_valid; - FlatpakContextFeatures features; - FlatpakContextFeatures features_valid; - GHashTable *env_vars; - GHashTable *persistent; - GHashTable *filesystems; - GHashTable *session_bus_policy; - GHashTable *system_bus_policy; - GHashTable *generic_policy; -}; - -FlatpakContext * -flatpak_context_new (void) -{ - FlatpakContext *context; - - context = g_slice_new0 (FlatpakContext); - context->env_vars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - context->persistent = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - context->filesystems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - context->session_bus_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - context->system_bus_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - context->generic_policy = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify)g_strfreev); - - return context; -} - -void -flatpak_context_free (FlatpakContext *context) -{ - g_hash_table_destroy (context->env_vars); - g_hash_table_destroy (context->persistent); - g_hash_table_destroy (context->filesystems); - g_hash_table_destroy (context->session_bus_policy); - g_hash_table_destroy (context->system_bus_policy); - g_hash_table_destroy (context->generic_policy); - g_slice_free (FlatpakContext, context); -} - -static guint32 -flatpak_context_bitmask_from_string (const char *name, const char **names) -{ - guint32 i; - - for (i = 0; names[i] != NULL; i++) - { - if (strcmp (names[i], name) == 0) - return 1 << i; - } - - return 0; -} - -static char ** -flatpak_context_bitmask_to_string (guint32 enabled, guint32 valid, const char **names) -{ - guint32 i; - GPtrArray *array; - - array = g_ptr_array_new (); - - for (i = 0; names[i] != NULL; i++) - { - guint32 bitmask = 1 << i; - if (valid & bitmask) - { - if (enabled & bitmask) - g_ptr_array_add (array, g_strdup (names[i])); - else - g_ptr_array_add (array, g_strdup_printf ("!%s", names[i])); - } - } - - g_ptr_array_add (array, NULL); - return (char **) g_ptr_array_free (array, FALSE); -} - -static void -flatpak_context_bitmask_to_args (guint32 enabled, guint32 valid, const char **names, - const char *enable_arg, const char *disable_arg, - GPtrArray *args) -{ - guint32 i; - - for (i = 0; names[i] != NULL; i++) - { - guint32 bitmask = 1 << i; - if (valid & bitmask) - { - if (enabled & bitmask) - g_ptr_array_add (args, g_strdup_printf ("%s=%s", enable_arg, names[i])); - else - g_ptr_array_add (args, g_strdup_printf ("%s=%s", disable_arg, names[i])); - } - } -} - - -static FlatpakContextShares -flatpak_context_share_from_string (const char *string, GError **error) -{ - FlatpakContextShares shares = flatpak_context_bitmask_from_string (string, flatpak_context_shares); - - if (shares == 0) - { - g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_shares); - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Unknown share type %s, valid types are: %s"), string, values); - } - - return shares; -} - -static char ** -flatpak_context_shared_to_string (FlatpakContextShares shares, FlatpakContextShares valid) -{ - return flatpak_context_bitmask_to_string (shares, valid, flatpak_context_shares); -} - -static void -flatpak_context_shared_to_args (FlatpakContextShares shares, - FlatpakContextShares valid, - GPtrArray *args) -{ - return flatpak_context_bitmask_to_args (shares, valid, flatpak_context_shares, "--share", "--unshare", args); -} - -static FlatpakPolicy -flatpak_policy_from_string (const char *string, GError **error) -{ - const char *policies[] = { "none", "see", "filtered", "talk", "own", NULL }; - int i; - g_autofree char *values = NULL; - - for (i = 0; policies[i]; i++) - { - if (strcmp (string, policies[i]) == 0) - return i; - } - - values = g_strjoinv (", ", (char **)policies); - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Unknown policy type %s, valid types are: %s"), string, values); - - return -1; -} - static const char * flatpak_policy_to_string (FlatpakPolicy policy) { @@ -308,243 +91,6 @@ flatpak_policy_to_string (FlatpakPolicy policy) return "none"; } -static gboolean -flatpak_verify_dbus_name (const char *name, GError **error) -{ - const char *name_part; - g_autofree char *tmp = NULL; - - if (g_str_has_suffix (name, ".*")) - { - tmp = g_strndup (name, strlen (name) - 2); - name_part = tmp; - } - else - { - name_part = name; - } - - if (g_dbus_is_name (name_part) && !g_dbus_is_unique_name (name_part)) - return TRUE; - - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Invalid dbus name %s\n"), name); - return FALSE; -} - -static FlatpakContextSockets -flatpak_context_socket_from_string (const char *string, GError **error) -{ - FlatpakContextSockets sockets = flatpak_context_bitmask_from_string (string, flatpak_context_sockets); - - if (sockets == 0) - { - g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_sockets); - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Unknown socket type %s, valid types are: %s"), string, values); - } - - return sockets; -} - -static char ** -flatpak_context_sockets_to_string (FlatpakContextSockets sockets, FlatpakContextSockets valid) -{ - return flatpak_context_bitmask_to_string (sockets, valid, flatpak_context_sockets); -} - -static void -flatpak_context_sockets_to_args (FlatpakContextSockets sockets, - FlatpakContextSockets valid, - GPtrArray *args) -{ - return flatpak_context_bitmask_to_args (sockets, valid, flatpak_context_sockets, "--socket", "--nosocket", args); -} - -static FlatpakContextDevices -flatpak_context_device_from_string (const char *string, GError **error) -{ - FlatpakContextDevices devices = flatpak_context_bitmask_from_string (string, flatpak_context_devices); - - if (devices == 0) - { - g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_devices); - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Unknown device type %s, valid types are: %s"), string, values); - } - return devices; -} - -static char ** -flatpak_context_devices_to_string (FlatpakContextDevices devices, FlatpakContextDevices valid) -{ - return flatpak_context_bitmask_to_string (devices, valid, flatpak_context_devices); -} - -static void -flatpak_context_devices_to_args (FlatpakContextDevices devices, - FlatpakContextDevices valid, - GPtrArray *args) -{ - return flatpak_context_bitmask_to_args (devices, valid, flatpak_context_devices, "--device", "--nodevice", args); -} - -static FlatpakContextFeatures -flatpak_context_feature_from_string (const char *string, GError **error) -{ - FlatpakContextFeatures feature = flatpak_context_bitmask_from_string (string, flatpak_context_features); - - if (feature == 0) - { - g_autofree char *values = g_strjoinv (", ", (char **)flatpak_context_features); - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Unknown feature type %s, valid types are: %s"), string, values); - } - - return feature; -} - -static char ** -flatpak_context_features_to_string (FlatpakContextFeatures features, FlatpakContextFeatures valid) -{ - return flatpak_context_bitmask_to_string (features, valid, flatpak_context_features); -} - -static void -flatpak_context_features_to_args (FlatpakContextFeatures features, - FlatpakContextFeatures valid, - GPtrArray *args) -{ - return flatpak_context_bitmask_to_args (features, valid, flatpak_context_features, "--allow", "--disallow", args); -} - -static void -flatpak_context_add_shares (FlatpakContext *context, - FlatpakContextShares shares) -{ - context->shares_valid |= shares; - context->shares |= shares; -} - -static void -flatpak_context_remove_shares (FlatpakContext *context, - FlatpakContextShares shares) -{ - context->shares_valid |= shares; - context->shares &= ~shares; -} - -static void -flatpak_context_add_sockets (FlatpakContext *context, - FlatpakContextSockets sockets) -{ - context->sockets_valid |= sockets; - context->sockets |= sockets; -} - -static void -flatpak_context_remove_sockets (FlatpakContext *context, - FlatpakContextSockets sockets) -{ - context->sockets_valid |= sockets; - context->sockets &= ~sockets; -} - -static void -flatpak_context_add_devices (FlatpakContext *context, - FlatpakContextDevices devices) -{ - context->devices_valid |= devices; - context->devices |= devices; -} - -static void -flatpak_context_remove_devices (FlatpakContext *context, - FlatpakContextDevices devices) -{ - context->devices_valid |= devices; - context->devices &= ~devices; -} - -static void -flatpak_context_add_features (FlatpakContext *context, - FlatpakContextFeatures features) -{ - context->features_valid |= features; - context->features |= features; -} - -static void -flatpak_context_remove_features (FlatpakContext *context, - FlatpakContextFeatures features) -{ - context->features_valid |= features; - context->features &= ~features; -} - -static void -flatpak_context_set_env_var (FlatpakContext *context, - const char *name, - const char *value) -{ - g_hash_table_insert (context->env_vars, g_strdup (name), g_strdup (value)); -} - -void -flatpak_context_set_session_bus_policy (FlatpakContext *context, - const char *name, - FlatpakPolicy policy) -{ - g_hash_table_insert (context->session_bus_policy, g_strdup (name), GINT_TO_POINTER (policy)); -} - -void -flatpak_context_set_system_bus_policy (FlatpakContext *context, - const char *name, - FlatpakPolicy policy) -{ - g_hash_table_insert (context->system_bus_policy, g_strdup (name), GINT_TO_POINTER (policy)); -} - -static void -flatpak_context_apply_generic_policy (FlatpakContext *context, - const char *key, - const char *value) -{ - GPtrArray *new = g_ptr_array_new (); - const char **old_v; - int i; - - g_assert (strchr (key, '.') != NULL); - - old_v = g_hash_table_lookup (context->generic_policy, key); - for (i = 0; old_v != NULL && old_v[i] != NULL; i++) - { - const char *old = old_v[i]; - const char *cmp1 = old; - const char *cmp2 = value; - if (*cmp1 == '!') - cmp1++; - if (*cmp2 == '!') - cmp2++; - if (strcmp (cmp1, cmp2) != 0) - g_ptr_array_add (new, g_strdup (old)); - } - - g_ptr_array_add (new, g_strdup (value)); - g_ptr_array_add (new, NULL); - - g_hash_table_insert (context->generic_policy, g_strdup (key), - g_ptr_array_free (new, FALSE)); -} - -static void -flatpak_context_set_persistent (FlatpakContext *context, - const char *path) -{ - g_hash_table_insert (context->persistent, g_strdup (path), GINT_TO_POINTER (1)); -} - static gboolean get_xdg_dir_from_prefix (const char *prefix, const char **where, @@ -723,1054 +269,6 @@ get_xdg_user_dir_from_string (const char *filesystem, return FALSE; } -static char * -parse_filesystem_flags (const char *filesystem, FlatpakFilesystemMode *mode) -{ - gsize len = strlen (filesystem); - - if (mode) - *mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE; - - if (g_str_has_suffix (filesystem, ":ro")) - { - len -= 3; - if (mode) - *mode = FLATPAK_FILESYSTEM_MODE_READ_ONLY; - } - else if (g_str_has_suffix (filesystem, ":rw")) - { - len -= 3; - if (mode) - *mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE; - } - else if (g_str_has_suffix (filesystem, ":create")) - { - len -= 7; - if (mode) - *mode = FLATPAK_FILESYSTEM_MODE_CREATE; - } - - return g_strndup (filesystem, len); -} - -static gboolean -flatpak_context_verify_filesystem (const char *filesystem_and_mode, - GError **error) -{ - g_autofree char *filesystem = parse_filesystem_flags (filesystem_and_mode, NULL); - - if (strcmp (filesystem, "host") == 0) - return TRUE; - if (strcmp (filesystem, "home") == 0) - return TRUE; - if (get_xdg_user_dir_from_string (filesystem, NULL, NULL, NULL)) - return TRUE; - if (g_str_has_prefix (filesystem, "~/")) - return TRUE; - if (g_str_has_prefix (filesystem, "/")) - return TRUE; - - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Unknown filesystem location %s, valid locations are: host, home, xdg-*[/...], ~/dir, /dir"), filesystem); - return FALSE; -} - -static void -flatpak_context_add_filesystem (FlatpakContext *context, - const char *what) -{ - FlatpakFilesystemMode mode; - char *fs = parse_filesystem_flags (what, &mode); - - g_hash_table_insert (context->filesystems, fs, GINT_TO_POINTER (mode)); -} - -static void -flatpak_context_remove_filesystem (FlatpakContext *context, - const char *what) -{ - g_hash_table_insert (context->filesystems, - parse_filesystem_flags (what, NULL), - NULL); -} - -void -flatpak_context_merge (FlatpakContext *context, - FlatpakContext *other) -{ - GHashTableIter iter; - gpointer key, value; - - context->shares &= ~other->shares_valid; - context->shares |= other->shares; - context->shares_valid |= other->shares_valid; - context->sockets &= ~other->sockets_valid; - context->sockets |= other->sockets; - context->sockets_valid |= other->sockets_valid; - context->devices &= ~other->devices_valid; - context->devices |= other->devices; - context->devices_valid |= other->devices_valid; - context->features &= ~other->features_valid; - context->features |= other->features; - context->features_valid |= other->features_valid; - - g_hash_table_iter_init (&iter, other->env_vars); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (context->env_vars, g_strdup (key), g_strdup (value)); - - g_hash_table_iter_init (&iter, other->persistent); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (context->persistent, g_strdup (key), value); - - g_hash_table_iter_init (&iter, other->filesystems); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (context->filesystems, g_strdup (key), value); - - g_hash_table_iter_init (&iter, other->session_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (context->session_bus_policy, g_strdup (key), value); - - g_hash_table_iter_init (&iter, other->system_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (context->system_bus_policy, g_strdup (key), value); - - g_hash_table_iter_init (&iter, other->system_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (context->system_bus_policy, g_strdup (key), value); - - g_hash_table_iter_init (&iter, other->generic_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - const char **policy_values = (const char **)value; - int i; - - for (i = 0; policy_values[i] != NULL; i++) - flatpak_context_apply_generic_policy (context, (char *)key, policy_values[i]); - } - -} - -static gboolean -option_share_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextShares share; - - share = flatpak_context_share_from_string (value, error); - if (share == 0) - return FALSE; - - flatpak_context_add_shares (context, share); - - return TRUE; -} - -static gboolean -option_unshare_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextShares share; - - share = flatpak_context_share_from_string (value, error); - if (share == 0) - return FALSE; - - flatpak_context_remove_shares (context, share); - - return TRUE; -} - -static gboolean -option_socket_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextSockets socket; - - socket = flatpak_context_socket_from_string (value, error); - if (socket == 0) - return FALSE; - - flatpak_context_add_sockets (context, socket); - - return TRUE; -} - -static gboolean -option_nosocket_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextSockets socket; - - socket = flatpak_context_socket_from_string (value, error); - if (socket == 0) - return FALSE; - - flatpak_context_remove_sockets (context, socket); - - return TRUE; -} - -static gboolean -option_device_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextDevices device; - - device = flatpak_context_device_from_string (value, error); - if (device == 0) - return FALSE; - - flatpak_context_add_devices (context, device); - - return TRUE; -} - -static gboolean -option_nodevice_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextDevices device; - - device = flatpak_context_device_from_string (value, error); - if (device == 0) - return FALSE; - - flatpak_context_remove_devices (context, device); - - return TRUE; -} - -static gboolean -option_allow_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextFeatures feature; - - feature = flatpak_context_feature_from_string (value, error); - if (feature == 0) - return FALSE; - - flatpak_context_add_features (context, feature); - - return TRUE; -} - -static gboolean -option_disallow_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - FlatpakContextFeatures feature; - - feature = flatpak_context_feature_from_string (value, error); - if (feature == 0) - return FALSE; - - flatpak_context_remove_features (context, feature); - - return TRUE; -} - -static gboolean -option_filesystem_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - if (!flatpak_context_verify_filesystem (value, error)) - return FALSE; - - flatpak_context_add_filesystem (context, value); - return TRUE; -} - -static gboolean -option_nofilesystem_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - if (!flatpak_context_verify_filesystem (value, error)) - return FALSE; - - flatpak_context_remove_filesystem (context, value); - return TRUE; -} - -static gboolean -option_env_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - g_auto(GStrv) split = g_strsplit (value, "=", 2); - - if (split == NULL || split[0] == NULL || split[0][0] == 0 || split[1] == NULL) - { - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Invalid env format %s"), value); - return FALSE; - } - - flatpak_context_set_env_var (context, split[0], split[1]); - return TRUE; -} - -static gboolean -option_own_name_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - if (!flatpak_verify_dbus_name (value, error)) - return FALSE; - - flatpak_context_set_session_bus_policy (context, value, FLATPAK_POLICY_OWN); - return TRUE; -} - -static gboolean -option_talk_name_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - if (!flatpak_verify_dbus_name (value, error)) - return FALSE; - - flatpak_context_set_session_bus_policy (context, value, FLATPAK_POLICY_TALK); - return TRUE; -} - -static gboolean -option_system_own_name_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - if (!flatpak_verify_dbus_name (value, error)) - return FALSE; - - flatpak_context_set_system_bus_policy (context, value, FLATPAK_POLICY_OWN); - return TRUE; -} - -static gboolean -option_system_talk_name_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - if (!flatpak_verify_dbus_name (value, error)) - return FALSE; - - flatpak_context_set_system_bus_policy (context, value, FLATPAK_POLICY_TALK); - return TRUE; -} - -static gboolean -option_add_generic_policy_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - char *t; - g_autofree char *key = NULL; - const char *policy_value; - - t = strchr (value, '='); - if (t == NULL) - return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); - policy_value = t + 1; - key = g_strndup (value, t - value); - if (strchr (key, '.') == NULL) - return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); - - if (policy_value[0] == '!') - return flatpak_fail (error, "--policy values can't start with \"!\""); - - flatpak_context_apply_generic_policy (context, key, policy_value); - - return TRUE; -} - -static gboolean -option_remove_generic_policy_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - char *t; - g_autofree char *key = NULL; - const char *policy_value; - g_autofree char *extended_value = NULL; - - t = strchr (value, '='); - if (t == NULL) - return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); - policy_value = t + 1; - key = g_strndup (value, t - value); - if (strchr (key, '.') == NULL) - return flatpak_fail (error, "--policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE"); - - if (policy_value[0] == '!') - return flatpak_fail (error, "--policy values can't start with \"!\""); - - extended_value = g_strconcat ("!", policy_value, NULL); - - flatpak_context_apply_generic_policy (context, key, extended_value); - - return TRUE; -} - -static gboolean -option_persist_cb (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - FlatpakContext *context = data; - - flatpak_context_set_persistent (context, value); - return TRUE; -} - -static gboolean option_no_desktop_deprecated; - -static GOptionEntry context_options[] = { - { "share", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_share_cb, N_("Share with host"), N_("SHARE") }, - { "unshare", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_unshare_cb, N_("Unshare with host"), N_("SHARE") }, - { "socket", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_socket_cb, N_("Expose socket to app"), N_("SOCKET") }, - { "nosocket", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nosocket_cb, N_("Don't expose socket to app"), N_("SOCKET") }, - { "device", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_device_cb, N_("Expose device to app"), N_("DEVICE") }, - { "nodevice", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nodevice_cb, N_("Don't expose device to app"), N_("DEVICE") }, - { "allow", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_allow_cb, N_("Allow feature"), N_("FEATURE") }, - { "disallow", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_disallow_cb, N_("Don't allow feature"), N_("FEATURE") }, - { "filesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_filesystem_cb, N_("Expose filesystem to app (:ro for read-only)"), N_("FILESYSTEM[:ro]") }, - { "nofilesystem", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_nofilesystem_cb, N_("Don't expose filesystem to app"), N_("FILESYSTEM") }, - { "env", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_env_cb, N_("Set environment variable"), N_("VAR=VALUE") }, - { "own-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_own_name_cb, N_("Allow app to own name on the session bus"), N_("DBUS_NAME") }, - { "talk-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_talk_name_cb, N_("Allow app to talk to name on the session bus"), N_("DBUS_NAME") }, - { "system-own-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_system_own_name_cb, N_("Allow app to own name on the system bus"), N_("DBUS_NAME") }, - { "system-talk-name", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_system_talk_name_cb, N_("Allow app to talk to name on the system bus"), N_("DBUS_NAME") }, - { "add-policy", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_add_generic_policy_cb, N_("Add generic policy option"), N_("SUBSYSTEM.KEY=VALUE") }, - { "remove-policy", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_remove_generic_policy_cb, N_("Remove generic policy option"), N_("SUBSYSTEM.KEY=VALUE") }, - { "persist", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_CALLBACK, &option_persist_cb, N_("Persist home directory"), N_("FILENAME") }, - /* This is not needed/used anymore, so hidden, but we accept it for backwards compat */ - { "no-desktop", 0, G_OPTION_FLAG_IN_MAIN | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &option_no_desktop_deprecated, N_("Don't require a running session (no cgroups creation)"), NULL }, - { NULL } -}; - -void -flatpak_context_complete (FlatpakContext *context, FlatpakCompletion *completion) -{ - flatpak_complete_options (completion, context_options); -} - -GOptionGroup * -flatpak_context_get_options (FlatpakContext *context) -{ - GOptionGroup *group; - - group = g_option_group_new ("environment", - "Runtime Environment", - "Runtime Environment", - context, - NULL); - g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); - - g_option_group_add_entries (group, context_options); - - return group; -} - -static const char * -parse_negated (const char *option, gboolean *negated) -{ - if (option[0] == '!') - { - option++; - *negated = TRUE; - } - else - { - *negated = FALSE; - } - return option; -} - -/* - * Merge the FLATPAK_METADATA_GROUP_CONTEXT, - * FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, - * FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY and - * FLATPAK_METADATA_GROUP_ENVIRONMENT groups, and all groups starting - * with FLATPAK_METADATA_GROUP_PREFIX_POLICY, from metakey into context. - * - * This is a merge, not a replace! - */ -gboolean -flatpak_context_load_metadata (FlatpakContext *context, - GKeyFile *metakey, - GError **error) -{ - gboolean remove; - g_auto(GStrv) groups = NULL; - int i; - - if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_SHARED, NULL)) - { - g_auto(GStrv) shares = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_SHARED, NULL, error); - if (shares == NULL) - return FALSE; - - for (i = 0; shares[i] != NULL; i++) - { - FlatpakContextShares share; - - share = flatpak_context_share_from_string (parse_negated (shares[i], &remove), NULL); - if (share == 0) - g_debug ("Unknown share type %s", shares[i]); - else - { - if (remove) - flatpak_context_remove_shares (context, share); - else - flatpak_context_add_shares (context, share); - } - } - } - - if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_SOCKETS, NULL)) - { - g_auto(GStrv) sockets = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_SOCKETS, NULL, error); - if (sockets == NULL) - return FALSE; - - for (i = 0; sockets[i] != NULL; i++) - { - FlatpakContextSockets socket = flatpak_context_socket_from_string (parse_negated (sockets[i], &remove), NULL); - if (socket == 0) - g_debug ("Unknown socket type %s", sockets[i]); - else - { - if (remove) - flatpak_context_remove_sockets (context, socket); - else - flatpak_context_add_sockets (context, socket); - } - } - } - - if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_DEVICES, NULL)) - { - g_auto(GStrv) devices = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_DEVICES, NULL, error); - if (devices == NULL) - return FALSE; - - - for (i = 0; devices[i] != NULL; i++) - { - FlatpakContextDevices device = flatpak_context_device_from_string (parse_negated (devices[i], &remove), NULL); - if (device == 0) - g_debug ("Unknown device type %s", devices[i]); - else - { - if (remove) - flatpak_context_remove_devices (context, device); - else - flatpak_context_add_devices (context, device); - } - } - } - - if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_FEATURES, NULL)) - { - g_auto(GStrv) features = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_FEATURES, NULL, error); - if (features == NULL) - return FALSE; - - - for (i = 0; features[i] != NULL; i++) - { - FlatpakContextFeatures feature = flatpak_context_feature_from_string (parse_negated (features[i], &remove), NULL); - if (feature == 0) - g_debug ("Unknown feature type %s", features[i]); - else - { - if (remove) - flatpak_context_remove_features (context, feature); - else - flatpak_context_add_features (context, feature); - } - } - } - - if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_FILESYSTEMS, NULL)) - { - g_auto(GStrv) filesystems = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_FILESYSTEMS, NULL, error); - if (filesystems == NULL) - return FALSE; - - for (i = 0; filesystems[i] != NULL; i++) - { - const char *fs = parse_negated (filesystems[i], &remove); - if (!flatpak_context_verify_filesystem (fs, NULL)) - g_debug ("Unknown filesystem type %s", filesystems[i]); - else - { - if (remove) - flatpak_context_remove_filesystem (context, fs); - else - flatpak_context_add_filesystem (context, fs); - } - } - } - - if (g_key_file_has_key (metakey, FLATPAK_METADATA_GROUP_CONTEXT, FLATPAK_METADATA_KEY_PERSISTENT, NULL)) - { - g_auto(GStrv) persistent = g_key_file_get_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_PERSISTENT, NULL, error); - if (persistent == NULL) - return FALSE; - - for (i = 0; persistent[i] != NULL; i++) - flatpak_context_set_persistent (context, persistent[i]); - } - - if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY)) - { - g_auto(GStrv) keys = NULL; - gsize i, keys_count; - - keys = g_key_file_get_keys (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, &keys_count, NULL); - for (i = 0; i < keys_count; i++) - { - const char *key = keys[i]; - g_autofree char *value = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, key, NULL); - FlatpakPolicy policy; - - if (!flatpak_verify_dbus_name (key, error)) - return FALSE; - - policy = flatpak_policy_from_string (value, NULL); - if ((int) policy != -1) - flatpak_context_set_session_bus_policy (context, key, policy); - } - } - - if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY)) - { - g_auto(GStrv) keys = NULL; - gsize i, keys_count; - - keys = g_key_file_get_keys (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, &keys_count, NULL); - for (i = 0; i < keys_count; i++) - { - const char *key = keys[i]; - g_autofree char *value = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, key, NULL); - FlatpakPolicy policy; - - if (!flatpak_verify_dbus_name (key, error)) - return FALSE; - - policy = flatpak_policy_from_string (value, NULL); - if ((int) policy != -1) - flatpak_context_set_system_bus_policy (context, key, policy); - } - } - - if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT)) - { - g_auto(GStrv) keys = NULL; - gsize i, keys_count; - - keys = g_key_file_get_keys (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT, &keys_count, NULL); - for (i = 0; i < keys_count; i++) - { - const char *key = keys[i]; - g_autofree char *value = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT, key, NULL); - - flatpak_context_set_env_var (context, key, value); - } - } - - groups = g_key_file_get_groups (metakey, NULL); - for (i = 0; groups[i] != NULL; i++) - { - const char *group = groups[i]; - const char *subsystem; - int j; - - if (g_str_has_prefix (group, FLATPAK_METADATA_GROUP_PREFIX_POLICY)) - { - g_auto(GStrv) keys = NULL; - subsystem = group + strlen (FLATPAK_METADATA_GROUP_PREFIX_POLICY); - keys = g_key_file_get_keys (metakey, group, NULL, NULL); - for (j = 0; keys != NULL && keys[j] != NULL; j++) - { - const char *key = keys[j]; - g_autofree char *policy_key = g_strdup_printf ("%s.%s", subsystem, key); - g_auto(GStrv) values = NULL; - int k; - - values = g_key_file_get_string_list (metakey, group, key, NULL, NULL); - for (k = 0; values != NULL && values[k] != NULL; k++) - flatpak_context_apply_generic_policy (context, policy_key, - values[k]); - } - } - } - - return TRUE; -} - -/* - * Save the FLATPAK_METADATA_GROUP_CONTEXT, - * FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, - * FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY and - * FLATPAK_METADATA_GROUP_ENVIRONMENT groups, and all groups starting - * with FLATPAK_METADATA_GROUP_PREFIX_POLICY, into metakey - */ -void -flatpak_context_save_metadata (FlatpakContext *context, - gboolean flatten, - GKeyFile *metakey) -{ - g_auto(GStrv) shared = NULL; - g_auto(GStrv) sockets = NULL; - g_auto(GStrv) devices = NULL; - g_auto(GStrv) features = NULL; - GHashTableIter iter; - gpointer key, value; - FlatpakContextShares shares_mask = context->shares; - FlatpakContextShares shares_valid = context->shares_valid; - FlatpakContextSockets sockets_mask = context->sockets; - FlatpakContextSockets sockets_valid = context->sockets_valid; - FlatpakContextDevices devices_mask = context->devices; - FlatpakContextDevices devices_valid = context->devices_valid; - FlatpakContextFeatures features_mask = context->features; - FlatpakContextFeatures features_valid = context->features; - g_auto(GStrv) groups = NULL; - int i; - - if (flatten) - { - /* A flattened format means we don't expect this to be merged on top of - another context. In that case we never need to negate any flags. - We calculate this by removing the zero parts of the mask from the valid set. - */ - /* First we make sure only the valid parts of the mask are set, in case we - got some leftover */ - shares_mask &= shares_valid; - sockets_mask &= sockets_valid; - devices_mask &= devices_valid; - features_mask &= features_valid; - - /* Then just set the valid set to be the mask set */ - shares_valid = shares_mask; - sockets_valid = sockets_mask; - devices_valid = devices_mask; - features_valid = features_mask; - } - - shared = flatpak_context_shared_to_string (shares_mask, shares_valid); - sockets = flatpak_context_sockets_to_string (sockets_mask, sockets_valid); - devices = flatpak_context_devices_to_string (devices_mask, devices_valid); - features = flatpak_context_features_to_string (features_mask, features_valid); - - if (shared[0] != NULL) - { - g_key_file_set_string_list (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_SHARED, - (const char * const *) shared, g_strv_length (shared)); - } - else - { - g_key_file_remove_key (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_SHARED, - NULL); - } - - if (sockets[0] != NULL) - { - g_key_file_set_string_list (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_SOCKETS, - (const char * const *) sockets, g_strv_length (sockets)); - } - else - { - g_key_file_remove_key (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_SOCKETS, - NULL); - } - - if (devices[0] != NULL) - { - g_key_file_set_string_list (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_DEVICES, - (const char * const *) devices, g_strv_length (devices)); - } - else - { - g_key_file_remove_key (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_DEVICES, - NULL); - } - - if (features[0] != NULL) - { - g_key_file_set_string_list (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_FEATURES, - (const char * const *) features, g_strv_length (features)); - } - else - { - g_key_file_remove_key (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_FEATURES, - NULL); - } - - if (g_hash_table_size (context->filesystems) > 0) - { - g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); - - g_hash_table_iter_init (&iter, context->filesystems); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - FlatpakFilesystemMode mode = GPOINTER_TO_INT (value); - - if (mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) - g_ptr_array_add (array, g_strconcat (key, ":ro", NULL)); - else if (mode == FLATPAK_FILESYSTEM_MODE_CREATE) - g_ptr_array_add (array, g_strconcat (key, ":create", NULL)); - else if (value != NULL) - g_ptr_array_add (array, g_strdup (key)); - else - g_ptr_array_add (array, g_strconcat ("!", key, NULL)); - } - - g_key_file_set_string_list (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_FILESYSTEMS, - (const char * const *) array->pdata, array->len); - } - else - { - g_key_file_remove_key (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_FILESYSTEMS, - NULL); - } - - if (g_hash_table_size (context->persistent) > 0) - { - g_autofree char **keys = (char **) g_hash_table_get_keys_as_array (context->persistent, NULL); - - g_key_file_set_string_list (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_PERSISTENT, - (const char * const *) keys, g_strv_length (keys)); - } - else - { - g_key_file_remove_key (metakey, - FLATPAK_METADATA_GROUP_CONTEXT, - FLATPAK_METADATA_KEY_PERSISTENT, - NULL); - } - - g_key_file_remove_group (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, NULL); - g_hash_table_iter_init (&iter, context->session_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - FlatpakPolicy policy = GPOINTER_TO_INT (value); - if (policy > 0) - g_key_file_set_string (metakey, - FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY, - (char *) key, flatpak_policy_to_string (policy)); - } - - g_key_file_remove_group (metakey, FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, NULL); - g_hash_table_iter_init (&iter, context->system_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - FlatpakPolicy policy = GPOINTER_TO_INT (value); - if (policy > 0) - g_key_file_set_string (metakey, - FLATPAK_METADATA_GROUP_SYSTEM_BUS_POLICY, - (char *) key, flatpak_policy_to_string (policy)); - } - - g_key_file_remove_group (metakey, FLATPAK_METADATA_GROUP_ENVIRONMENT, NULL); - g_hash_table_iter_init (&iter, context->env_vars); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - g_key_file_set_string (metakey, - FLATPAK_METADATA_GROUP_ENVIRONMENT, - (char *) key, (char *) value); - } - - - groups = g_key_file_get_groups (metakey, NULL); - for (i = 0; groups[i] != NULL; i++) - { - const char *group = groups[i]; - if (g_str_has_prefix (group, FLATPAK_METADATA_GROUP_PREFIX_POLICY)) - g_key_file_remove_group (metakey, group, NULL); - } - - g_hash_table_iter_init (&iter, context->generic_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - g_auto(GStrv) parts = g_strsplit ((const char *)key, ".", 2); - g_autofree char *group = NULL; - g_assert (parts[1] != NULL); - const char **policy_values = (const char **)value; - g_autoptr(GPtrArray) new = g_ptr_array_new (); - - for (i = 0; policy_values[i] != NULL; i++) - { - const char *policy_value = policy_values[i]; - - if (!flatten || policy_value[0] != '!') - g_ptr_array_add (new, (char *)policy_value); - } - - if (new->len > 0) - { - group = g_strconcat (FLATPAK_METADATA_GROUP_PREFIX_POLICY, - parts[0], NULL); - g_key_file_set_string_list (metakey, group, parts[1], - (const char * const*)new->pdata, - new->len); - } - } -} - -void -flatpak_context_allow_host_fs (FlatpakContext *context) -{ - flatpak_context_add_filesystem (context, "host"); -} - -gboolean -flatpak_context_get_needs_session_bus_proxy (FlatpakContext *context) -{ - return g_hash_table_size (context->session_bus_policy) > 0; -} - -gboolean -flatpak_context_get_needs_system_bus_proxy (FlatpakContext *context) -{ - return g_hash_table_size (context->system_bus_policy) > 0; -} - -void -flatpak_context_to_args (FlatpakContext *context, - GPtrArray *args) -{ - GHashTableIter iter; - gpointer key, value; - - flatpak_context_shared_to_args (context->shares, context->shares_valid, args); - flatpak_context_sockets_to_args (context->sockets, context->sockets_valid, args); - flatpak_context_devices_to_args (context->devices, context->devices_valid, args); - flatpak_context_features_to_args (context->features, context->features_valid, args); - - g_hash_table_iter_init (&iter, context->env_vars); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_ptr_array_add (args, g_strdup_printf ("--env=%s=%s", (char *)key, (char *)value)); - - g_hash_table_iter_init (&iter, context->persistent); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_ptr_array_add (args, g_strdup_printf ("--persist=%s", (char *)key)); - - g_hash_table_iter_init (&iter, context->session_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - const char *name = key; - FlatpakPolicy policy = GPOINTER_TO_INT (value); - - g_ptr_array_add (args, g_strdup_printf ("--%s-name=%s", flatpak_policy_to_string (policy), name)); - } - - g_hash_table_iter_init (&iter, context->system_bus_policy); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - const char *name = key; - FlatpakPolicy policy = GPOINTER_TO_INT (value); - - g_ptr_array_add (args, g_strdup_printf ("--system-%s-name=%s", flatpak_policy_to_string (policy), name)); - } - - g_hash_table_iter_init (&iter, context->filesystems); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - FlatpakFilesystemMode mode = GPOINTER_TO_INT (value); - - if (mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) - g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s:ro", (char *)key)); - else if (mode == FLATPAK_FILESYSTEM_MODE_READ_WRITE) - g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s", (char *)key)); - else if (mode == FLATPAK_FILESYSTEM_MODE_CREATE) - g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s:create", (char *)key)); - else - g_ptr_array_add (args, g_strdup_printf ("--nofilesystem=%s", (char *)key)); - } -} - static char * extract_unix_path_from_dbus_address (const char *address) { diff --git a/common/flatpak-run.h b/common/flatpak-run.h index 519f7e57..e9d7ae54 100644 --- a/common/flatpak-run.h +++ b/common/flatpak-run.h @@ -22,8 +22,8 @@ #define __FLATPAK_RUN_H__ #include "libglnx/libglnx.h" -#include "dbus-proxy/flatpak-proxy.h" #include "flatpak-common-types.h" +#include "flatpak-context.h" #include "flatpak-utils.h" gboolean flatpak_run_in_transient_unit (const char *app_id, @@ -95,11 +95,6 @@ gboolean flatpak_run_in_transient_unit (const char *app_id, #define FLATPAK_METADATA_KEY_PRIORITY "priority" #define FLATPAK_METADATA_KEY_REF "ref" -extern const char *flatpak_context_sockets[]; -extern const char *flatpak_context_devices[]; -extern const char *flatpak_context_features[]; -extern const char *flatpak_context_shares[]; - typedef struct { GPtrArray *argv; GArray *fds; @@ -138,39 +133,6 @@ void flatpak_bwrap_add_bind_arg (FlatpakBwrap *bwrap, G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakBwrap, flatpak_bwrap_free) - -FlatpakContext *flatpak_context_new (void); -void flatpak_context_free (FlatpakContext *context); -void flatpak_context_merge (FlatpakContext *context, - FlatpakContext *other); -GOptionGroup *flatpak_context_get_options (FlatpakContext *context); -void flatpak_context_complete (FlatpakContext *context, - FlatpakCompletion *completion); -gboolean flatpak_context_load_metadata (FlatpakContext *context, - GKeyFile *metakey, - GError **error); -void flatpak_context_save_metadata (FlatpakContext *context, - gboolean flatten, - GKeyFile *metakey); -void flatpak_context_allow_host_fs (FlatpakContext *context); -void flatpak_context_set_session_bus_policy (FlatpakContext *context, - const char *name, - FlatpakPolicy policy); -void flatpak_context_set_system_bus_policy (FlatpakContext *context, - const char *name, - FlatpakPolicy policy); -void flatpak_context_to_args (FlatpakContext *context, - GPtrArray *args); -gboolean flatpak_context_get_needs_session_bus_proxy (FlatpakContext *context); -gboolean flatpak_context_get_needs_system_bus_proxy (FlatpakContext *context); - -FlatpakContext *flatpak_context_load_for_deploy (FlatpakDeploy *deploy, - GError **error); -FlatpakContext *flatpak_context_load_for_app (const char *app_id, - GError **error); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakContext, flatpak_context_free) - typedef enum { FLATPAK_RUN_FLAG_DEVEL = (1 << 0), FLATPAK_RUN_FLAG_BACKGROUND = (1 << 1),