1 /* gbp-flatpak-runner.c 2 * 3 * Copyright 2016-2019 Christian Hergert <chergert@redhat.com> 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 * SPDX-License-Identifier: GPL-3.0-or-later 19 */ 20 21 #define G_LOG_DOMAIN "gbp-flatpak-runner" 22 #define G_SETTINGS_ENABLE_BACKEND 23 24 #include <errno.h> 25 #include <gio/gsettingsbackend.h> 26 #include <glib/gi18n.h> 27 #include <stdlib.h> 28 #include <unistd.h> 29 30 #include "gbp-flatpak-aux.h" 31 #include "gbp-flatpak-manifest.h" 32 #include "gbp-flatpak-runner.h" 33 #include "gbp-flatpak-util.h" 34 35 struct _GbpFlatpakRunner 36 { 37 IdeRunner parent_instance; 38 39 gchar *build_path; 40 gchar *manifest_command; 41 }; 42 43 G_DEFINE_FINAL_TYPE (GbpFlatpakRunner, gbp_flatpak_runner, IDE_TYPE_RUNNER) 44 45 static IdeSubprocessLauncher * 46 gbp_flatpak_runner_create_launcher (IdeRunner *runner) 47 { 48 const gchar *cwd = ide_runner_get_cwd (runner); 49 50 return g_object_new (IDE_TYPE_SUBPROCESS_LAUNCHER, 51 "flags", 0, 52 "cwd", cwd, 53 NULL); 54 } 55 56 static gboolean 57 contains_argv (IdeSubprocessLauncher *launcher, 58 const gchar *arg) 59 { 60 const gchar * const *args; 61 62 if (arg == NULL) 63 return TRUE; 64 65 if (!(args = ide_subprocess_launcher_get_argv (launcher))) 66 return FALSE; 67 68 return g_strv_contains (args, arg); 69 } 70 71 static gchar * 72 get_project_build_dir (GbpFlatpakRunner *self) 73 { 74 IdeContext *context; 75 76 g_assert (GBP_IS_FLATPAK_RUNNER (self)); 77 78 context = ide_object_get_context (IDE_OBJECT (self)); 79 return ide_context_cache_filename (context, NULL, NULL); 80 } 81 82 static void 83 gbp_flatpak_runner_fixup_launcher (IdeRunner *runner, 84 IdeSubprocessLauncher *launcher) 85 { 86 const gchar *config_dir = gbp_flatpak_get_config_dir (); 87 GbpFlatpakRunner *self = (GbpFlatpakRunner *)runner; 88 g_autofree gchar *doc_portal = NULL; 89 g_autofree gchar *project_build_dir = NULL; 90 g_autofree gchar *project_build_dir_param = NULL; 91 IdeConfigManager *config_manager; 92 IdeConfig *config; 93 IdeEnvironment *env; 94 g_auto(GStrv) environ_ = NULL; 95 const gchar *app_id; 96 IdeContext *context; 97 guint i = 0; 98 99 g_assert (GBP_IS_FLATPAK_RUNNER (runner)); 100 g_assert (IDE_IS_SUBPROCESS_LAUNCHER (launcher)); 101 102 context = ide_object_get_context (IDE_OBJECT (self)); 103 config_manager = ide_config_manager_from_context (context); 104 config = ide_config_manager_get_current (config_manager); 105 app_id = ide_config_get_app_id (config); 106 107 doc_portal = g_strdup_printf ("--bind-mount=/run/user/%u/doc=/run/user/%u/doc/by-app/%s", 108 getuid (), getuid (), app_id); 109 110 /* Get access to override installations */ 111 ide_subprocess_launcher_setenv (launcher, "FLATPAK_CONFIG_DIR", config_dir, TRUE); 112 113 ide_subprocess_launcher_insert_argv (launcher, i++, "flatpak"); 114 ide_subprocess_launcher_insert_argv (launcher, i++, "build"); 115 ide_subprocess_launcher_insert_argv (launcher, i++, "--with-appdir"); 116 ide_subprocess_launcher_insert_argv (launcher, i++, "--allow=devel"); 117 ide_subprocess_launcher_insert_argv (launcher, i++, doc_portal); 118 119 i += gbp_flatpak_aux_apply (launcher, i); 120 121 if (GBP_IS_FLATPAK_MANIFEST (config)) 122 { 123 const gchar * const *finish_args; 124 125 /* 126 * We cannot rely on flatpak-builder --run because it filters out 127 * some finish-args that we really care about. (Primarily so that 128 * it can preserve the integrity of the build results). So instead, 129 * we just mimic what it would do for us and ensure we pass along 130 * all the finish-args we know about. 131 */ 132 133 finish_args = gbp_flatpak_manifest_get_finish_args (GBP_FLATPAK_MANIFEST (config)); 134 135 if (finish_args != NULL) 136 { 137 for (guint j = 0; finish_args[j] != NULL; j++) 138 { 139 const gchar *arg = finish_args[j]; 140 141 if (g_str_has_prefix (arg, "--allow") || 142 g_str_has_prefix (arg, "--share") || 143 g_str_has_prefix (arg, "--socket") || 144 g_str_has_prefix (arg, "--filesystem") || 145 g_str_has_prefix (arg, "--device") || 146 g_str_has_prefix (arg, "--env") || 147 g_str_has_prefix (arg, "--system-talk") || 148 g_str_has_prefix (arg, "--own-name") || 149 g_str_has_prefix (arg, "--talk-name") || 150 g_str_has_prefix (arg, "--add-policy")) 151 ide_subprocess_launcher_insert_argv (launcher, i++, arg); 152 } 153 } 154 } 155 else 156 { 157 ide_subprocess_launcher_insert_argv (launcher, i++, "--share=ipc"); 158 ide_subprocess_launcher_insert_argv (launcher, i++, "--share=network"); 159 ide_subprocess_launcher_insert_argv (launcher, i++, "--socket=x11"); 160 ide_subprocess_launcher_insert_argv (launcher, i++, "--socket=wayland"); 161 } 162 163 ide_subprocess_launcher_insert_argv (launcher, i++, "--talk-name=org.freedesktop.portal.*"); 164 ide_subprocess_launcher_insert_argv (launcher, i++, "--talk-name=org.a11y.Bus"); 165 166 /* Make sure we have access to the build directory */ 167 project_build_dir = get_project_build_dir (self); 168 project_build_dir_param = g_strdup_printf ("--filesystem=%s", project_build_dir); 169 ide_subprocess_launcher_insert_argv (launcher, i++, project_build_dir_param); 170 171 /* Proxy environment stuff to the launcher */ 172 if ((env = ide_runner_get_environment (runner)) && 173 (environ_ = ide_environment_get_environ (env))) 174 { 175 for (guint j = 0; environ_[j]; j++) 176 { 177 g_autofree gchar *arg = g_strdup_printf ("--env=%s", environ_[j]); 178 179 if (!contains_argv (launcher, arg)) 180 ide_subprocess_launcher_insert_argv (launcher, i++, arg); 181 } 182 } 183 184 /* Disable G_MESSAGES_DEBUG as it could cause 'flatpak build' to spew info 185 * and mess up systems that need a clean stdin/stdout/stderr. 186 */ 187 ide_subprocess_launcher_setenv (launcher, "G_MESSAGES_DEBUG", NULL, TRUE); 188 189 ide_subprocess_launcher_insert_argv (launcher, i++, self->build_path); 190 } 191 192 GbpFlatpakRunner * 193 gbp_flatpak_runner_new (IdeContext *context, 194 const gchar *build_path, 195 IdeBuildTarget *build_target, 196 const gchar *manifest_command) 197 { 198 GbpFlatpakRunner *self; 199 200 g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL); 201 g_return_val_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target), NULL); 202 g_return_val_if_fail (build_target || manifest_command, NULL); 203 204 self = g_object_new (GBP_TYPE_FLATPAK_RUNNER, NULL); 205 self->build_path = g_strdup (build_path); 206 self->manifest_command = g_strdup (manifest_command); 207 208 if (build_target == NULL) 209 { 210 ide_runner_append_argv (IDE_RUNNER (self), manifest_command); 211 } 212 else 213 { 214 g_auto(GStrv) argv = ide_build_target_get_argv (build_target); 215 216 if (argv != NULL) 217 { 218 for (guint i = 0; argv[i]; i++) 219 ide_runner_append_argv (IDE_RUNNER (self), argv[i]); 220 } 221 222 ide_runner_set_build_target (IDE_RUNNER (self), build_target); 223 } 224 225 return g_steal_pointer (&self); 226 } 227 228 static void 229 override_settings (GbpFlatpakRunner *self) 230 { 231 g_autoptr(GSettingsBackend) backend = NULL; 232 g_autoptr(GSettings) settings = NULL; 233 g_autofree char *filename = NULL; 234 IdeConfigManager *config_manager; 235 IdeContext *context; 236 const char *app_id; 237 IdeConfig *config; 238 239 g_assert (GBP_IS_FLATPAK_RUNNER (self)); 240 241 if (!(context = ide_object_get_context (IDE_OBJECT (self))) || 242 !(config_manager = ide_config_manager_from_context (context)) || 243 !(config = ide_config_manager_get_current (config_manager)) || 244 !(app_id = ide_config_get_app_id (config))) 245 return; 246 247 filename = g_build_filename (g_get_home_dir (), ".var", "app", app_id, 248 "config", "glib-2.0", "settings", "keyfile", 249 NULL); 250 backend = g_keyfile_settings_backend_new (filename, "/", NULL); 251 settings = g_settings_new_with_backend ("org.gtk.Settings.Debug", backend); 252 253 g_settings_set_boolean (settings, "enable-inspector-keybinding", TRUE); 254 } 255 256 static void 257 gbp_flatpak_runner_run_async (IdeRunner *runner, 258 GCancellable *cancellable, 259 GAsyncReadyCallback callback, 260 gpointer user_data) 261 { 262 GbpFlatpakRunner *self = (GbpFlatpakRunner *)runner; 263 264 g_assert (GBP_IS_FLATPAK_RUNNER (self)); 265 g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); 266 267 override_settings (self); 268 269 IDE_RUNNER_CLASS (gbp_flatpak_runner_parent_class)->run_async (runner, cancellable, callback, user_data); 270 } 271 272 static void 273 gbp_flatpak_runner_finalize (GObject *object) 274 { 275 GbpFlatpakRunner *self = (GbpFlatpakRunner *)object; 276 277 g_clear_pointer (&self->build_path, g_free); 278 g_clear_pointer (&self->manifest_command, g_free); 279 280 G_OBJECT_CLASS (gbp_flatpak_runner_parent_class)->finalize (object); 281 } 282 283 static void 284 gbp_flatpak_runner_class_init (GbpFlatpakRunnerClass *klass) 285 { 286 GObjectClass *object_class = G_OBJECT_CLASS (klass); 287 IdeRunnerClass *runner_class = IDE_RUNNER_CLASS (klass); 288 289 object_class->finalize = gbp_flatpak_runner_finalize; 290 291 runner_class->create_launcher = gbp_flatpak_runner_create_launcher; 292 runner_class->fixup_launcher = gbp_flatpak_runner_fixup_launcher; 293 runner_class->run_async = gbp_flatpak_runner_run_async; 294 } 295 296 static void 297 gbp_flatpak_runner_init (GbpFlatpakRunner *self) 298 { 299 ide_runner_set_run_on_host (IDE_RUNNER (self), TRUE); 300 ide_runner_set_clear_env (IDE_RUNNER (self), FALSE); 301 } 302