flatpak_run_app (FlatpakDecomposed *app_ref,
                 FlatpakDeploy     *app_deploy,
                 const char        *custom_app_path,
                 FlatpakContext    *extra_context,
                 const char        *custom_runtime,
                 const char        *custom_runtime_version,
                 const char        *custom_runtime_commit,
                 const char        *custom_usr_path,
                 int                parent_pid,
                 FlatpakRunFlags    flags,
                 const char        *cwd,
                 const char        *custom_command,
                 char              *args[],
                 int                n_args,
                 int                instance_id_fd,
                 char             **instance_dir_out,
                 GCancellable      *cancellable,
                 GError           **error)
{
  g_autoptr(FlatpakDeploy) runtime_deploy = NULL;
  g_autoptr(GBytes) runtime_deploy_data = NULL;
  g_autoptr(GBytes) app_deploy_data = NULL;
  g_autoptr(GFile) app_files = NULL;
  g_autoptr(GFile) original_app_files = NULL;
  g_autoptr(GFile) runtime_files = NULL;
  g_autoptr(GFile) original_runtime_files = NULL;
  g_autoptr(GFile) bin_ldconfig = NULL;
  g_autoptr(GFile) app_id_dir = NULL;
  g_autoptr(GFile) real_app_id_dir = NULL;
  g_autofree char *default_runtime_pref = NULL;
  g_autoptr(FlatpakDecomposed) default_runtime = NULL;
  g_autofree char *default_command = NULL;
  g_autoptr(GKeyFile) metakey = NULL;
  g_autoptr(GKeyFile) runtime_metakey = NULL;
  g_autoptr(FlatpakBwrap) bwrap = NULL;
  const char *command = "/bin/sh";
  g_autoptr(GError) my_error = NULL;
  g_autoptr(FlatpakDecomposed) runtime_ref = NULL;
  int i;
  g_autoptr(GPtrArray) previous_app_id_dirs = NULL;
  g_autofree char *app_id = NULL;
  g_autofree char *app_arch = NULL;
  g_autofree char *app_info_path = NULL;
  g_autofree char *app_ld_path = NULL;
  g_autofree char *instance_id_host_dir = NULL;
  g_autoptr(FlatpakContext) app_context = NULL;
  g_autoptr(FlatpakContext) overrides = NULL;
  g_autoptr(FlatpakExports) exports = NULL;
  g_autofree char *commandline = NULL;
  g_autofree char *doc_mount_path = NULL;
  g_autofree char *app_extensions = NULL;
  g_autofree char *runtime_extensions = NULL;
  g_autofree char *runtime_ld_path = NULL;
  g_autofree char *checksum = NULL;
  glnx_autofd int per_app_dir_lock_fd = -1;
  g_autofree char *per_app_dir_lock_path = NULL;
  g_autofree char *shared_xdg_runtime_dir = NULL;
  int ld_so_fd = -1;
  g_autoptr(GFile) runtime_ld_so_conf = NULL;
  gboolean generate_ld_so_conf = TRUE;
  gboolean use_ld_so_cache = TRUE;
  gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0;
  gboolean parent_expose_pids = (flags & FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS) != 0;
  gboolean parent_share_pids = (flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS) != 0;
  const char *app_target_path = "/app";
  const char *runtime_target_path = "/usr";
  struct stat s;

  g_return_val_if_fail (app_ref != NULL, FALSE);

  if (!check_sudo (error))
    return FALSE;

  app_id = flatpak_decomposed_dup_id (app_ref);
  g_return_val_if_fail (app_id != NULL, FALSE);
  app_arch = flatpak_decomposed_dup_arch (app_ref);
  g_return_val_if_fail (app_arch != NULL, FALSE);

  /* Check the user is allowed to run this flatpak. */
  if (!check_parental_controls (app_ref, app_deploy, cancellable, error))
    return FALSE;

  /* Construct the bwrap context. */
  bwrap = flatpak_bwrap_new (NULL);
  flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ());

  if (app_deploy == NULL)
    {
      g_assert (flatpak_decomposed_is_runtime (app_ref));
      default_runtime_pref = flatpak_decomposed_dup_pref (app_ref);
    }
  else
    {
      const gchar *key;

      app_deploy_data = flatpak_deploy_get_deploy_data (app_deploy, FLATPAK_DEPLOY_VERSION_ANY, cancellable, error);
      if (app_deploy_data == NULL)
        return FALSE;

      if ((flags & FLATPAK_RUN_FLAG_DEVEL) != 0)
        key = FLATPAK_METADATA_KEY_SDK;
      else
        key = FLATPAK_METADATA_KEY_RUNTIME;

      metakey = flatpak_deploy_get_metadata (app_deploy);
      default_runtime_pref = g_key_file_get_string (metakey,
                                                    FLATPAK_METADATA_GROUP_APPLICATION,
                                                    key, &my_error);
      if (my_error)
        {
          g_propagate_error (error, g_steal_pointer (&my_error));
          return FALSE;
        }
    }

  default_runtime = flatpak_decomposed_new_from_pref (FLATPAK_KINDS_RUNTIME, default_runtime_pref, error);
  if (default_runtime == NULL)
    return FALSE;

  if (custom_runtime != NULL || custom_runtime_version != NULL)
    {
      g_auto(GStrv) custom_runtime_parts = NULL;
      const char *custom_runtime_id = NULL;
      const char *custom_runtime_arch = NULL;

      if (custom_runtime)
        {
          custom_runtime_parts = g_strsplit (custom_runtime, "/", 0);
          for (i = 0; i < 3 && custom_runtime_parts[i] != NULL; i++)
            {
              if (strlen (custom_runtime_parts[i]) > 0)
                {
                  if (i == 0)
                    custom_runtime_id = custom_runtime_parts[i];
                  if (i == 1)
                    custom_runtime_arch = custom_runtime_parts[i];

                  if (i == 2 && custom_runtime_version == NULL)
                    custom_runtime_version = custom_runtime_parts[i];
                }
            }
        }

      runtime_ref = flatpak_decomposed_new_from_decomposed (default_runtime,
                                                            FLATPAK_KINDS_RUNTIME,
                                                            custom_runtime_id,
                                                            custom_runtime_arch,
                                                            custom_runtime_version,
                                                            error);
      if (runtime_ref == NULL)
        return FALSE;
    }
  else
    runtime_ref = flatpak_decomposed_ref (default_runtime);

  runtime_deploy = flatpak_find_deploy_for_ref (flatpak_decomposed_get_ref (runtime_ref), custom_runtime_commit, NULL, cancellable, error);
  if (runtime_deploy == NULL)
    return FALSE;

  runtime_deploy_data = flatpak_deploy_get_deploy_data (runtime_deploy, FLATPAK_DEPLOY_VERSION_ANY, cancellable, error);
  if (runtime_deploy_data == NULL)
    return FALSE;

  runtime_metakey = flatpak_deploy_get_metadata (runtime_deploy);

  app_context = flatpak_app_compute_permissions (metakey, runtime_metakey, error);
  if (app_context == NULL)
    return FALSE;

  if (app_deploy != NULL)
    {
      overrides = flatpak_deploy_get_overrides (app_deploy);
      flatpak_context_merge (app_context, overrides);
    }

  if (sandboxed)
    flatpak_context_make_sandboxed (app_context);

  if (extra_context)
    flatpak_context_merge (app_context, extra_context);

  original_runtime_files = flatpak_deploy_get_files (runtime_deploy);

  if (custom_usr_path != NULL)
    {
      runtime_files = g_file_new_for_path (custom_usr_path);
      /* Mount the original runtime below here instead of /usr */
      runtime_target_path = "/run/parent/usr";
    }
  else
    {
      runtime_files = g_object_ref (original_runtime_files);
    }

  bin_ldconfig = g_file_resolve_relative_path (runtime_files, "bin/ldconfig");
  if (!g_file_query_exists (bin_ldconfig, NULL))
    use_ld_so_cache = FALSE;

  /* We can't use the ld.so cache if we are using a custom /usr or /app,
   * because we don't have a unique ID for the /usr or /app, so we can't
   * do cache-invalidation correctly. The caller can either build their
   * own ld.so.cache before supplying us with the runtime, or supply
   * their own LD_LIBRARY_PATH. */
  if (custom_usr_path != NULL || custom_app_path != NULL)
    use_ld_so_cache = FALSE;

  if (app_deploy != NULL)
    {
      g_autofree const char **previous_ids = NULL;
      gsize len = 0;
      gboolean do_migrate;

      real_app_id_dir = flatpak_get_data_dir (app_id);
      original_app_files = flatpak_deploy_get_files (app_deploy);

      previous_app_id_dirs = g_ptr_array_new_with_free_func (g_object_unref);
      previous_ids = flatpak_deploy_data_get_previous_ids (app_deploy_data, &len);

      do_migrate = !g_file_query_exists (real_app_id_dir, cancellable);

      /* When migrating, find most recent old existing source and rename that to
       * the new name.
       *
       * We ignore other names than that. For more recent names that don't exist
       * we never ran them so nothing will even reference them. For older names
       * either they were not used, or they were used but then the more recent
       * name was used and a symlink to it was created.
       *
       * This means we may end up with a chain of symlinks: oldest -> old -> current.
       * This is unfortunate but not really a problem, but for robustness reasons we
       * don't want to mess with user files unnecessary. For example, the app dir could
       * actually be a symlink for other reasons. Imagine for instance that you want to put the
       * steam games somewhere else so you leave the app dir as a symlink to /mnt/steam.
       */
      for (i = len - 1; i >= 0; i--)
        {
          g_autoptr(GFile) previous_app_id_dir = NULL;
          g_autoptr(GFileInfo) previous_app_id_dir_info = NULL;
          g_autoptr(GError) local_error = NULL;

          previous_app_id_dir = flatpak_get_data_dir (previous_ids[i]);
          previous_app_id_dir_info = g_file_query_info (previous_app_id_dir,
                                                        G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
                                                        G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
                                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                                        cancellable,
                                                        &local_error);
          /* Warn about the migration failures, but don't make them fatal, then you can never run the app */
          if (previous_app_id_dir_info == NULL)
            {
              if  (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && do_migrate)
                {
                  g_warning (_("Failed to migrate from %s: %s"), flatpak_file_get_path_cached (previous_app_id_dir),
                             local_error->message);
                  do_migrate = FALSE; /* Don't migrate older things, they are likely symlinks to the thing that we failed on */
                }

              g_clear_error (&local_error);
              continue;
            }

          if (do_migrate)
            {
              do_migrate = FALSE; /* Don't migrate older things, they are likely symlinks to this dir */

              if (!flatpak_file_rename (previous_app_id_dir, real_app_id_dir, cancellable, &local_error))
                {
                  g_warning (_("Failed to migrate old app data directory %s to new name %s: %s"),
                             flatpak_file_get_path_cached (previous_app_id_dir), app_id,
                             local_error->message);
                }
              else
                {
                  /* Leave a symlink in place of the old data dir */
                  if (!g_file_make_symbolic_link (previous_app_id_dir, app_id, cancellable, &local_error))
                    {
                      g_warning (_("Failed to create symlink while migrating %s: %s"),
                                 flatpak_file_get_path_cached (previous_app_id_dir),
                                 local_error->message);
                    }
                }
            }

          /* Give app access to this old dir */
          g_ptr_array_add (previous_app_id_dirs, g_steal_pointer (&previous_app_id_dir));
        }

      if (!flatpak_ensure_data_dir (real_app_id_dir, cancellable, error))
        return FALSE;

      if (!sandboxed)
        app_id_dir = g_object_ref (real_app_id_dir);
    }

  if (custom_app_path != NULL)
    {
      if (strcmp (custom_app_path, "") == 0)
        app_files = NULL;
      else
        app_files = g_file_new_for_path (custom_app_path);

      /* Mount the original app below here */
      app_target_path = "/run/parent/app";
    }
  else if (original_app_files != NULL)
    {
      app_files = g_object_ref (original_app_files);
    }

  flatpak_run_apply_env_default (bwrap, use_ld_so_cache);
  flatpak_run_apply_env_vars (bwrap, app_context);
  flatpak_run_apply_env_prompt (bwrap, app_id);

  if (real_app_id_dir)
    {
      g_autoptr(GFile) sandbox_dir = g_file_get_child (real_app_id_dir, "sandbox");
      flatpak_bwrap_set_env (bwrap, "FLATPAK_SANDBOX_DIR", flatpak_file_get_path_cached (sandbox_dir), TRUE);
    }

  flatpak_bwrap_add_args (bwrap,
                          "--ro-bind", flatpak_file_get_path_cached (runtime_files), "/usr",
                          NULL);

  if (runtime_files == original_runtime_files)
    {
      /* All true Flatpak runtimes have files/.ref */
      flatpak_bwrap_add_args (bwrap,
                              "--lock-file", "/usr/.ref",
                              NULL);
    }
  else
    {
      g_autoptr(GFile) runtime_child = NULL;

      runtime_child = g_file_get_child (runtime_files, ".ref");

      /* Lock ${usr}/.ref if it exists */
      if (g_file_query_exists (runtime_child, NULL))
        flatpak_bwrap_add_args (bwrap,
                                "--lock-file", "/usr/.ref",
                                NULL);

      /* Put the real Flatpak runtime in /run/parent, so that the
       * replacement /usr can have symlinks into /run/parent in order
       * to use the Flatpak runtime's graphics drivers etc. if desired */
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind",
                              flatpak_file_get_path_cached (original_runtime_files),
                              "/run/parent/usr",
                              "--lock-file", "/run/parent/usr/.ref",
                              NULL);
      flatpak_run_setup_usr_links (bwrap, original_runtime_files,
                                   "/run/parent");

      g_clear_object (&runtime_child);
      runtime_child = g_file_get_child (original_runtime_files, "etc");

      if (g_file_query_exists (runtime_child, NULL))
        flatpak_bwrap_add_args (bwrap,
                                "--symlink", "usr/etc", "/run/parent/etc",
                                NULL);
    }

  if (app_files != NULL)
    {
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", flatpak_file_get_path_cached (app_files), "/app",
                              NULL);

      if (app_files == original_app_files)
        {
          /* All true Flatpak apps have files/.ref */
          flatpak_bwrap_add_args (bwrap,
                                  "--lock-file", "/app/.ref",
                                  NULL);
        }
      else
        {
          g_autoptr(GFile) app_child = NULL;

          app_child = g_file_get_child (app_files, ".ref");

          /* Lock ${app}/.ref if it exists */
          if (g_file_query_exists (app_child, NULL))
            flatpak_bwrap_add_args (bwrap,
                                    "--lock-file", "/app/.ref",
                                    NULL);
        }
    }
  else
    {
      flatpak_bwrap_add_args (bwrap,
                              "--dir", "/app",
                              NULL);
    }

  if (original_app_files != NULL && app_files != original_app_files)
    {
      /* Put the real Flatpak app in /run/parent/app */
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind",
                              flatpak_file_get_path_cached (original_app_files),
                              "/run/parent/app",
                              "--lock-file", "/run/parent/app/.ref",
                              NULL);
    }

  if (metakey != NULL &&
      !flatpak_run_add_extension_args (bwrap, metakey, app_ref,
                                       use_ld_so_cache, app_target_path,
                                       &app_extensions, &app_ld_path,
                                       cancellable, error))
    return FALSE;

  if (!flatpak_run_add_extension_args (bwrap, runtime_metakey, runtime_ref,
                                       use_ld_so_cache, runtime_target_path,
                                       &runtime_extensions, &runtime_ld_path,
                                       cancellable, error))
    return FALSE;

  if (custom_usr_path == NULL)
    flatpak_run_extend_ld_path (bwrap, NULL, runtime_ld_path);

  if (custom_app_path == NULL)
    flatpak_run_extend_ld_path (bwrap, app_ld_path, NULL);

  runtime_ld_so_conf = g_file_resolve_relative_path (runtime_files, "etc/ld.so.conf");
  if (lstat (flatpak_file_get_path_cached (runtime_ld_so_conf), &s) == 0)
    generate_ld_so_conf = S_ISREG (s.st_mode) && s.st_size == 0;

  /* At this point we have the minimal argv set up, with just the app, runtime and extensions.
     We can reuse this to generate the ld.so.cache (if needed) */
  if (use_ld_so_cache)
    {
      checksum = calculate_ld_cache_checksum (app_deploy_data, runtime_deploy_data,
                                              app_extensions, runtime_extensions);
      ld_so_fd = regenerate_ld_cache (bwrap->argv,
                                      bwrap->fds,
                                      app_id_dir,
                                      checksum,
                                      runtime_files,
                                      generate_ld_so_conf,
                                      cancellable, error);
      if (ld_so_fd == -1)
        return FALSE;
      flatpak_bwrap_add_fd (bwrap, ld_so_fd);
    }

  flags |= flatpak_context_get_run_flags (app_context);

  if (!flatpak_run_setup_base_argv (bwrap, runtime_files, app_id_dir, app_arch, flags, error))
    return FALSE;

  if (generate_ld_so_conf)
    {
      if (!add_ld_so_conf (bwrap, error))
        return FALSE;
    }

  if (ld_so_fd != -1)
    {
      /* Don't add to fd_array, its already there */
      flatpak_bwrap_add_arg (bwrap, "--ro-bind-data");
      flatpak_bwrap_add_arg_printf (bwrap, "%d", ld_so_fd);
      flatpak_bwrap_add_arg (bwrap, "/etc/ld.so.cache");
    }

  if (!flatpak_run_add_app_info_args (bwrap,
                                      app_files, original_app_files, app_deploy_data, app_extensions,
                                      runtime_files, original_runtime_files, runtime_deploy_data, runtime_extensions,
                                      app_id, flatpak_decomposed_get_branch (app_ref),
                                      runtime_ref, app_id_dir, app_context, extra_context,
                                      sandboxed, FALSE, flags & FLATPAK_RUN_FLAG_DEVEL,
                                      &app_info_path, instance_id_fd, &instance_id_host_dir,
                                      error))
    return FALSE;

  if (!sandboxed)
    {
      if (!flatpak_instance_ensure_per_app_dir (app_id,
                                                &per_app_dir_lock_fd,
                                                &per_app_dir_lock_path,
                                                error))
        return FALSE;

      if (!flatpak_instance_ensure_per_app_xdg_runtime_dir (app_id,
                                                            per_app_dir_lock_fd,
                                                            &shared_xdg_runtime_dir,
                                                            error))
        return FALSE;

      flatpak_bwrap_add_arg (bwrap, "--bind");
      flatpak_bwrap_add_arg (bwrap, shared_xdg_runtime_dir);
      flatpak_bwrap_add_arg_printf (bwrap, "/run/user/%d", getuid ());
    }

  if (!flatpak_run_add_dconf_args (bwrap, app_id, metakey, error))
    return FALSE;

  if (!sandboxed && !(flags & FLATPAK_RUN_FLAG_NO_DOCUMENTS_PORTAL))
    add_document_portal_args (bwrap, app_id, &doc_mount_path);

  if (!flatpak_run_add_environment_args (bwrap, app_info_path, flags,
                                         app_id, app_context, app_id_dir, previous_app_id_dirs,
                                         per_app_dir_lock_fd,
                                         &exports, cancellable, error))
    return FALSE;

  if (per_app_dir_lock_path != NULL)
    {
      static const char lock[] = "/run/flatpak/per-app-dirs-ref";

      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", per_app_dir_lock_path, lock,
                              "--lock-file", lock,
                              NULL);
    }

  if ((app_context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) != 0)
    flatpak_run_add_resolved_args (bwrap);

  flatpak_run_add_journal_args (bwrap);
  add_font_path_args (bwrap);
  add_icon_path_args (bwrap);

  flatpak_bwrap_add_args (bwrap,
                          /* Not in base, because we don't want this for flatpak build */
                          "--symlink", "/app/lib/debug/source", "/run/build",
                          "--symlink", "/usr/lib/debug/source", "/run/build-runtime",
                          NULL);

  if (cwd)
    flatpak_bwrap_add_args (bwrap, "--chdir", cwd, NULL);

  if (parent_expose_pids || parent_share_pids)
    {
      g_autofree char *userns_path = NULL;
      g_autofree char *pidns_path = NULL;
      g_autofree char *userns2_path = NULL;
      int userns_fd, userns2_fd, pidns_fd;

      if (parent_pid == 0)
        return flatpak_fail (error, "No parent pid specified");

      userns_path = g_strdup_printf ("/proc/%d/root/run/.userns", parent_pid);

      userns_fd = open_namespace_fd_if_needed (userns_path, "/proc/self/ns/user");
      if (userns_fd != -1)
        {
          flatpak_bwrap_add_args_data_fd (bwrap, "--userns", userns_fd, NULL);

          userns2_path = g_strdup_printf ("/proc/%d/ns/user", parent_pid);
          userns2_fd = open_namespace_fd_if_needed (userns2_path, userns_path);
          if (userns2_fd != -1)
            flatpak_bwrap_add_args_data_fd (bwrap, "--userns2", userns2_fd, NULL);
        }

      pidns_path = g_strdup_printf ("/proc/%d/ns/pid", parent_pid);
      pidns_fd = open (pidns_path, O_RDONLY|O_CLOEXEC);
      if (pidns_fd != -1)
        flatpak_bwrap_add_args_data_fd (bwrap, "--pidns", pidns_fd, NULL);
    }

  flatpak_bwrap_populate_runtime_dir (bwrap, shared_xdg_runtime_dir);

  if (custom_command)
    {
      command = custom_command;
    }
  else if (metakey)
    {
      default_command = g_key_file_get_string (metakey,
                                               FLATPAK_METADATA_GROUP_APPLICATION,
                                               FLATPAK_METADATA_KEY_COMMAND,
                                               &my_error);
      if (my_error)
        {
          g_propagate_error (error, g_steal_pointer (&my_error));
          return FALSE;
        }
      command = default_command;
    }

  flatpak_bwrap_sort_envp (bwrap);
  flatpak_bwrap_envp_to_args (bwrap);

  if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
    return FALSE;

  flatpak_bwrap_add_arg (bwrap, command);

  if (!add_rest_args (bwrap, app_id,
                      exports, (flags & FLATPAK_RUN_FLAG_FILE_FORWARDING) != 0,
                      doc_mount_path,
                      args, n_args, error))
    return FALSE;

  /* Hold onto the lock until we execute bwrap */
  flatpak_bwrap_add_noinherit_fd (bwrap, glnx_steal_fd (&per_app_dir_lock_fd));

  flatpak_bwrap_finish (bwrap);

  commandline = flatpak_quote_argv ((const char **) bwrap->argv->pdata, -1);
  g_debug ("Running '%s'", commandline);

  if ((flags & FLATPAK_RUN_FLAG_BACKGROUND) != 0)
    {
      GPid child_pid;
      char pid_str[64];
      g_autofree char *pid_path = NULL;
      GSpawnFlags spawn_flags;

      spawn_flags = G_SPAWN_SEARCH_PATH;
      if (flags & FLATPAK_RUN_FLAG_DO_NOT_REAP)
        spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD;

      /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
      spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN;

      /* flatpak_bwrap_envp_to_args() moved the environment variables to
       * be set into --setenv instructions in argv, so the environment
       * in which the bwrap command runs must be empty. */
      g_assert (bwrap->envp != NULL);
      g_assert (bwrap->envp[0] == NULL);

      if (!g_spawn_async (NULL,
                          (char **) bwrap->argv->pdata,
                          bwrap->envp,
                          spawn_flags,
                          flatpak_bwrap_child_setup_cb, bwrap->fds,
                          &child_pid,
                          error))
        return FALSE;

      g_snprintf (pid_str, sizeof (pid_str), "%d", child_pid);
      pid_path = g_build_filename (instance_id_host_dir, "pid", NULL);
      g_file_set_contents (pid_path, pid_str, -1, NULL);
    }
  else
    {
      char pid_str[64];
      g_autofree char *pid_path = NULL;

      g_snprintf (pid_str, sizeof (pid_str), "%d", getpid ());
      pid_path = g_build_filename (instance_id_host_dir, "pid", NULL);
      g_file_set_contents (pid_path, pid_str, -1, NULL);

      /* Ensure we unset O_CLOEXEC for marked fds and rewind fds as needed.
       * Note that this does not close fds that are not already marked O_CLOEXEC, because
       * we do want to allow inheriting fds into flatpak run. */
      flatpak_bwrap_child_setup (bwrap->fds, FALSE);

      /* flatpak_bwrap_envp_to_args() moved the environment variables to
       * be set into --setenv instructions in argv, so the environment
       * in which the bwrap command runs must be empty. */
      g_assert (bwrap->envp != NULL);
      g_assert (bwrap->envp[0] == NULL);

      if (execvpe (flatpak_get_bwrap (), (char **) bwrap->argv->pdata, bwrap->envp) == -1)
        {
          g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
                               _("Unable to start app"));
          return FALSE;
        }
      /* Not actually reached... */
    }

  if (instance_dir_out)
    *instance_dir_out = g_steal_pointer (&instance_id_host_dir);

  return TRUE;
}