Skip to content
Snippets Groups Projects
wrap.c 68.8 KiB
Newer Older
          /* We have the beginnings of infrastructure to set a LD_PRELOAD
           * from inside the container, but currently the only thing we
           * support is it coming from the host. */
          g_assert (g_str_has_prefix (preload, "host:"));
          preload = preload + 5;

          if (g_file_test (preload, G_FILE_TEST_EXISTS))
            {
              if (opt_runtime != NULL
                  && (g_str_has_prefix (preload, "/usr/")
                      || g_str_has_prefix (preload, "/lib")))
                {
                  g_autofree gchar *in_run_host = g_build_filename ("/run/host",
                                                                    preload,
                                                                    NULL);

                  /* When using a runtime we can't write to /usr/ or /libQUAL/,
                   * so redirect this preloaded module to the corresponding
                   * location in /run/host. */
                  search_path_append (adjusted_ld_preload, in_run_host);
                }
              else
                {
                  flatpak_bwrap_add_args (bwrap,
                                          "--ro-bind", preload, preload,
                                          NULL);
                  search_path_append (adjusted_ld_preload, preload);
                }
            }
          else
            {
              g_debug ("LD_PRELOAD module '%s' does not exist", preload);
            }
        }
    }

  /* Put the caller's LD_PRELOAD back.
   * This would be filtered out by a setuid bwrap, so we have to go
   * via --setenv. */

  if (adjusted_ld_preload->len != 0)
      flatpak_bwrap_add_args (bwrap,
                              "--setenv", "LD_PRELOAD",
                              adjusted_ld_preload->str,
                              NULL);
  else
      flatpak_bwrap_add_args (bwrap,
                              "--unsetenv", "LD_PRELOAD",
                              NULL);

  /* Make sure the current working directory (the game we are going to
   * run) is available. Some games write here. */

  g_debug ("Making home directory available...");

  if (pv_is_same_file (home, cwd_p))
    {
      g_debug ("Not making physical working directory \"%s\" available to "
               "container because it is the home directory",
               cwd_p);
    }
  else
    {
      flatpak_bwrap_add_args (bwrap,
                              "--bind", cwd_p, cwd_p,
                              NULL);
    }

  flatpak_bwrap_add_args (bwrap,
                          "--chdir", cwd_p,
                          "--unsetenv", "PWD",
                          NULL);

  /* TODO: Potential future expansion: use --unshare-pid for more isolation */

  /* Put Steam Runtime environment variables back, if /usr is mounted
   * from the host. */
  if (opt_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_bwrap_add_args (bwrap,
                                          "--ro-bind", equals + 1,
                                          equals + 1,
                                          NULL);
                }

              *equals = '\0';
              /* We do this via --setenv instead of flatpak_bwrap_set_env()
               * to make sure they aren't filtered out by a setuid bwrap. */
              flatpak_bwrap_add_args (bwrap,
                                      "--setenv", opt_env_if_host[i],
                                      equals + 1,
                                      NULL);
              *equals = '=';
            }
        }
    }

  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 (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
    goto out;

  g_debug ("Adding wrapped command...");
  flatpak_bwrap_append_args (bwrap, wrapped_command->argv);
      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);
        }
  /* flatpak_bwrap_finish did this */
  g_assert (g_ptr_array_index (bwrap->argv, bwrap->argv->len - 1) == NULL);

  g_debug ("Launching child process...");

  if (opt_interactive)
    spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN;

  if (!g_spawn_async (NULL,   /* cwd */
                      (gchar **) bwrap->argv->pdata,
                      bwrap->envp,
                      spawn_flags,
                      flatpak_bwrap_child_setup_cb,
                      bwrap->fds,
                      &child_pid,
                      error))
    {
      ret = 127;
      goto out;
    }

  g_child_watch_add (child_pid, child_exited_cb, &child_exited_closure);

  while (!child_exited_closure.exited)
    g_main_context_iteration (NULL, TRUE);

  if (opt_verbose)
    {
      if (WIFEXITED (child_exited_closure.wait_status))
        g_message ("Command exited with status %d",
                    WEXITSTATUS (child_exited_closure.wait_status));
      else if (WIFSIGNALED (child_exited_closure.wait_status))
        g_message ("Command killed by signal %d",
                   WTERMSIG (child_exited_closure.wait_status));
      else
        g_message ("Command terminated in an unknown way");
    }

  if (WIFEXITED (child_exited_closure.wait_status))
    ret = WEXITSTATUS (child_exited_closure.wait_status);
  else if (WIFSIGNALED (child_exited_closure.wait_status))
    ret = 128 + WTERMSIG (child_exited_closure.wait_status);
  else
    ret = 126;

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

  if (tmpdir != NULL &&
      !glnx_shutil_rm_rf_at (-1, tmpdir, NULL, error))
    {
      g_warning ("Unable to delete temporary directory: %s",
                 local_error->message);
      g_clear_error (&local_error);
    }

  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, g_free);

  return ret;
}