Skip to content
Snippets Groups Projects
wrap.c 78.4 KiB
Newer Older
  flatpak_bwrap_add_args (bwrap,
  g_hash_table_add (extra_locked_vars_to_unset, g_strdup ("PWD"));

  /* Put Steam Runtime environment variables back, if /usr is mounted
   * from the host. */
  if (runtime == NULL)
    {
      g_debug ("Making Steam Runtime available...");

      /* We need libraries from the Steam Runtime, so make sure that's
       * visible (it should never need to be read/write though) */
      if (opt_env_if_host != NULL)
        {
          for (i = 0; opt_env_if_host[i] != NULL; i++)
            {
              char *equals = strchr (opt_env_if_host[i], '=');

              g_assert (equals != NULL);

              if (g_str_has_prefix (opt_env_if_host[i], "STEAM_RUNTIME=/"))
                flatpak_exports_add_path_expose (exports,
                                                 FLATPAK_FILESYSTEM_MODE_READ_ONLY,
                                                 equals + 1);

              flatpak_bwrap_set_env (bwrap, opt_env_if_host[i], equals + 1, TRUE);

  /* Convert the exported directories into extra bubblewrap arguments */
  exports_bwrap = flatpak_bwrap_new (flatpak_bwrap_empty_env);
  flatpak_exports_append_bwrap_args (exports, exports_bwrap);
  adjust_exports (exports_bwrap, home);
  flatpak_bwrap_append_bwrap (bwrap, exports_bwrap);

  flatpak_run_add_font_path_args (bwrap);

  /* We need to set up IPC rendezvous points relatively late, so that
   * even if we are sharing /tmp via --filesystem=/tmp, we'll still
   * mount our own /tmp/.X11-unix over the top of the OS's. */
  if (runtime != NULL)
    {
      flatpak_run_add_wayland_args (bwrap);

      /* When in a Flatpak container the "DISPLAY" env is equal to ":99.0",
       * but it might be different on the host system. As a workaround we simply
       * bind the whole "/tmp/.X11-unix" directory and later unset the container
       * "DISPLAY" env.
       */
      if (is_flatpak_env)
        {
          flatpak_bwrap_add_args (bwrap,
                                  "--ro-bind", "/tmp/.X11-unix", "/tmp/.X11-unix",
                                  NULL);
        }
      else
        {
          flatpak_run_add_x11_args (bwrap, extra_locked_vars_to_unset, TRUE);
        }

      flatpak_run_add_pulseaudio_args (bwrap, extra_locked_vars_to_unset);
      flatpak_run_add_session_dbus_args (bwrap);
      flatpak_run_add_system_dbus_args (bwrap);
    }

  if (is_flatpak_env)
    {
      /* These are the environment variables that will be wrong, or useless,
       * in the new container that will be created. */
      g_hash_table_add (extra_locked_vars_to_unset, g_strdup ("FLATPAK_ID"));
      g_hash_table_add (extra_locked_vars_to_unset, g_strdup ("FLATPAK_SANDBOX_DIR"));
      g_hash_table_add (extra_locked_vars_to_unset, g_strdup ("LD_AUDIT"));

      /* These are the environment variables that might differ in the host
       * system. However from inside a container we are not able to know the
       * host's value. So we allow them to inherit the value from the host. */
      g_hash_table_add (extra_locked_vars_to_inherit, g_strdup ("DBUS_SESSION_BUS_ADDRESS"));
      g_hash_table_add (extra_locked_vars_to_inherit, g_strdup ("DBUS_SYSTEM_BUS_ADDRESS"));
      g_hash_table_add (extra_locked_vars_to_inherit, g_strdup ("DISPLAY"));
      g_hash_table_add (extra_locked_vars_to_inherit, g_strdup ("XDG_RUNTIME_DIR"));

      /* The bwrap envp will be completely ignored when calling
       * `flatpak-spawn --host`. For this reason we convert them to
       * `--setenv`. */
      for (i = 0; bwrap->envp != NULL && bwrap->envp[i] != NULL; i++)
        {
          g_auto(GStrv) split = g_strsplit (bwrap->envp[i], "=", 2);

          g_assert (split != NULL);
          g_assert (split[0] != NULL);
          g_assert (split[1] != NULL);

          flatpak_bwrap_add_args (bwrap,
                                  "--setenv", split[0], split[1],
                                  NULL);
        }
    }

  for (i = 0; bwrap->envp != NULL && bwrap->envp[i] != NULL; i++)
    {
      /* List only the variables names and not their values */
      char *equals = strchr (bwrap->envp[i], '=');

      g_assert (equals != NULL);

      /* Never lock LD_PRELOAD, otherwise, for example, we might miss the
       * overlay renderer library that the Steam client adds to LD_PRELOAD */
      if (g_str_has_prefix (bwrap->envp[i], "LD_PRELOAD="))
        continue;

      *equals = '\0';

      g_string_append (lock_env, bwrap->envp[i]);

      *equals = '=';

      g_string_append_c (lock_env, '\0');
    }

  g_hash_table_iter_init (&iter, extra_locked_vars_to_unset);
  while (g_hash_table_iter_next (&iter, &k, NULL))
    {
      env_var = k;
      g_string_append (lock_env, env_var);
      g_string_append_c (lock_env, '\0');

      flatpak_bwrap_add_args (bwrap,
                              "--unsetenv", env_var,
                              NULL);
    }

  g_hash_table_iter_init (&iter, extra_locked_vars_to_inherit);
  while (g_hash_table_iter_next (&iter, &k, NULL))
    {
      /* To inherit the variable value, we just use the lock
       * mechanism without `--setenv` or '--unsetenv'. In this way, when we
       * execute `launcher` with `--host`, it will inherit the value from the
       * host system. */
      env_var = k;
      g_string_append (lock_env, env_var);
      g_string_append_c (lock_env, '\0');
    }

  /* Populate the bwrap environ with the variables that are not in the
   * `extra_locked_vars_to_unset` list or `extra_locked_vars_to_inherit` list,
   * and without overriding the values that are already present in
   * bwrap->envp.
   * We skip this if we are in a Flatpak environment, because in that case
   * we already used `--setenv` for all the variables that we care about and
   * the bwrap envp will be ignored anyway. */
  if (!is_flatpak_env)
    {
      for (i = 0; original_environ[i] != NULL; i++)
        {
          char *eq = strchr (original_environ[i], '=');
          if (eq)
            {
              g_autofree gchar *key = g_strndup (original_environ[i],
                                                 eq - original_environ[i]);
              if (!g_hash_table_contains (extra_locked_vars_to_unset, key)
                  && !g_hash_table_contains (extra_locked_vars_to_inherit, key))
                flatpak_bwrap_set_env (bwrap, key, eq + 1, FALSE);
            }
        }

      /* The setuid bwrap will filter out some of the environment variables,
       * so we have to go via --setenv */
      for (i = 0; unsecure_environment_variables[i] != NULL; i++)
        {
          const gchar *val = g_environ_getenv (bwrap->envp, unsecure_environment_variables[i]);
          if (val != NULL)
            {
              flatpak_bwrap_add_args (bwrap, "--setenv",
                                      unsecure_environment_variables[i], val, NULL);
            }
        }
    }

  if (!flatpak_buffer_to_sealed_memfd_or_tmpfile (&lock_env_tmpf, "lock-env",
                                                  lock_env->str, lock_env->len,
                                                  error))
    goto out;

  lock_env_fd = g_strdup_printf ("%d", lock_env_tmpf.fd);
  flatpak_bwrap_add_fd (bwrap, glnx_steal_fd (&lock_env_tmpf.fd));

  if (opt_verbose)
    {
      g_message ("%s options before bundling:", bwrap_executable);

      for (i = 0; i < bwrap->argv->len; i++)
        {
          g_autofree gchar *quoted = NULL;

          quoted = g_shell_quote (g_ptr_array_index (bwrap->argv, i));
          g_message ("\t%s", quoted);
        }
    }

  if (!opt_only_prepare)
    {
      if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
        goto out;
    }
  adverb_args = flatpak_bwrap_new (flatpak_bwrap_empty_env);
  if (runtime != NULL)
    adverb_in_container = pv_runtime_get_adverb (runtime, adverb_args);

  if (opt_terminate_timeout >= 0.0)
    {
      if (opt_terminate_idle_timeout > 0.0)
        flatpak_bwrap_add_arg_printf (adverb_args,
                                      "--terminate-idle-timeout=%f",
                                      opt_terminate_idle_timeout);

      flatpak_bwrap_add_arg_printf (adverb_args,
                                    "--terminate-timeout=%f",
                                    opt_terminate_timeout);
    }

  /* If not using a runtime, the adverb in the container has the
   * same path as outside */
  if (adverb_in_container == NULL)
    adverb_in_container = g_build_filename (tools_dir,
                                            "pressure-vessel-adverb",
                                            NULL);
  flatpak_bwrap_add_args (bwrap,
                          adverb_in_container,
                          "--exit-with-parent",
                          "--subreaper",
                          NULL);
  if (opt_pass_fds != NULL)
    {
      for (i = 0; i < opt_pass_fds->len; i++)
        {
          int fd = g_array_index (opt_pass_fds, int, i);

          flatpak_bwrap_add_fd (bwrap, fd);
          flatpak_bwrap_add_arg_printf (bwrap, "--pass-fd=%d", fd);
        }
    }

  flatpak_bwrap_add_arg_printf (bwrap, "--pass-fd=%s", lock_env_fd);

  switch (opt_shell)
    {
      case PV_SHELL_AFTER:
        flatpak_bwrap_add_arg (bwrap, "--shell=after");
        break;

      case PV_SHELL_FAIL:
        flatpak_bwrap_add_arg (bwrap, "--shell=fail");
        break;

      case PV_SHELL_INSTEAD:
        flatpak_bwrap_add_arg (bwrap, "--shell=instead");
        break;

      case PV_SHELL_NONE:
        flatpak_bwrap_add_arg (bwrap, "--shell=none");
        break;

      default:
        g_warn_if_reached ();
    }

  switch (opt_terminal)
    {
      case PV_TERMINAL_AUTO:
        flatpak_bwrap_add_arg (bwrap, "--terminal=auto");
        break;

      case PV_TERMINAL_NONE:
        flatpak_bwrap_add_arg (bwrap, "--terminal=none");
        break;

      case PV_TERMINAL_TTY:
        flatpak_bwrap_add_arg (bwrap, "--terminal=tty");
        break;

      case PV_TERMINAL_XTERM:
        flatpak_bwrap_add_arg (bwrap, "--terminal=xterm");
        break;

      default:
        g_warn_if_reached ();
        break;
    }

  if (opt_verbose)
    flatpak_bwrap_add_arg (bwrap, "--verbose");
  flatpak_bwrap_append_bwrap (bwrap, adverb_args);
  flatpak_bwrap_add_arg (bwrap, "--");
  if (opt_launcher)
    {
      g_autofree gchar *pressure_vessel_launcher = g_build_filename (tools_dir,
                                                                     "pressure-vessel-launcher",
                                                                     NULL);
      g_debug ("Adding pressure-vessel-launcher '%s'...", pressure_vessel_launcher);
      flatpak_bwrap_add_arg (bwrap, pressure_vessel_launcher);

      g_debug ("Adding locked environment variables...");
      flatpak_bwrap_add_args (bwrap, "--lock-env-from-fd", lock_env_fd, NULL);
  g_debug ("Adding wrapped command...");
  flatpak_bwrap_append_args (bwrap, wrapped_command->argv);
  if (is_flatpak_env)
    {
      /* Just use the envp from @bwrap */
      g_autoptr(FlatpakBwrap) flatpak_spawn = flatpak_bwrap_new (flatpak_bwrap_empty_env);
      flatpak_bwrap_add_arg (flatpak_spawn, spawn_executable);
      flatpak_bwrap_add_arg (flatpak_spawn, "--host");

      for (i = 0; i < bwrap->fds->len; i++)
        {
          g_autofree char *fd_str = g_strdup_printf ("--forward-fd=%d",
                                                     g_array_index (bwrap->fds, int, i));
          flatpak_bwrap_add_arg (flatpak_spawn, fd_str);
        }
      /* Change the current working directory where flatpak-spawn will run.
       * Bwrap will then set its directory by itself. For this reason here
       * we just need a directory that it's known to exist. */
      flatpak_bwrap_add_arg (flatpak_spawn, "--directory=/");

      flatpak_bwrap_append_bwrap (flatpak_spawn, bwrap);
      g_clear_pointer (&bwrap, flatpak_bwrap_free);
      bwrap = g_steal_pointer (&flatpak_spawn);
    }

      g_message ("Final %s options:", bwrap_executable);
      for (i = 0; i < bwrap->argv->len; i++)
        {
          g_autofree gchar *quoted = NULL;

          quoted = g_shell_quote (g_ptr_array_index (bwrap->argv, i));
          g_message ("\t%s", quoted);
        }

      g_message ("%s environment:", bwrap_executable);

      for (i = 0; bwrap->envp != NULL && bwrap->envp[i] != NULL; i++)
        {
          g_autofree gchar *quoted = NULL;

          quoted = g_shell_quote (bwrap->envp[i]);
          g_message ("\t%s", quoted);
        }
  /* Clean up temporary directory before running our long-running process */
  if (runtime != NULL)
    pv_runtime_cleanup (runtime);
  if (opt_write_bwrap != NULL)
    {
      FILE *file = fopen (opt_write_bwrap, "w");
      if (file == NULL)
        {
          g_warning ("An error occurred trying to write the bwrap arguments: %s",
                    g_strerror (errno));
          /* This is not a fatal error, try to continue */
        }
      else
        {
          for (i = 0; i < bwrap->argv->len; i++)
            fprintf (file, "%s%c", (gchar *)g_ptr_array_index (bwrap->argv, i), '\0');

          fclose (file);
        }
    }

  if (opt_only_prepare)
    ret = 0;
  else
    pv_bwrap_execve (bwrap, fileno (original_stdout), error);

out:
  if (local_error != NULL)
    g_warning ("%s", local_error->message);

  g_clear_pointer (&opt_ld_preload, g_ptr_array_unref);
  g_clear_pointer (&opt_env_if_host, g_strfreev);
  g_clear_pointer (&opt_freedesktop_app_id, g_free);
  g_clear_pointer (&opt_steam_app_id, g_free);
  g_clear_pointer (&opt_home, g_free);
  g_clear_pointer (&opt_fake_home, g_free);
  g_clear_pointer (&opt_runtime_base, g_free);
  g_clear_pointer (&opt_runtime, g_free);
  g_clear_pointer (&opt_pass_fds, g_array_unref);
  g_debug ("Exiting with status %d", ret);