From e71023ff2bea7b9b120092204ad4aadb27871b99 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 1 Oct 2020 20:47:52 +0100
Subject: [PATCH] launcher: Always set PWD to the command's actual working
 directory

pressure-vessel-wrap unsets PWD, but we don't want the command to inherit
a value of PWD from the launcher. In particular, when running in session
mode, the launcher's $PWD is the Steam installation, typically
~/.local/share/Steam, and the setup commands are also run with that
working directory, but the actual game is run with the current working
directory set to its own game directory (which we didn't necessarily
even know at the time that the launcher was started).

Consumers of $PWD should really check that it is equivalent to the
actual current working directory and ignore it if it does not, like
GNU get_current_dir_name(3) and our pv_get_current_dirs() do, but
blindly believing $PWD is a common shell-scripting mistake, and I
wouldn't be surprised if there are some games whose launcher scripts
will believe $PWD even when it doesn't coincide with the real working
directory.

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 pressure-vessel/launch.1.md   |  8 ++++++++
 pressure-vessel/launcher.1.md |  6 ++++++
 pressure-vessel/launcher.c    | 19 +++++++++++++++++++
 pressure-vessel/wrap.1.md     |  7 +++++++
 4 files changed, 40 insertions(+)

diff --git a/pressure-vessel/launch.1.md b/pressure-vessel/launch.1.md
index 9eb38f7ed..70aff48ee 100644
--- a/pressure-vessel/launch.1.md
+++ b/pressure-vessel/launch.1.md
@@ -114,6 +114,14 @@ it set, and pass through **FONTS** from the caller.
     **env -u** *VAR* *COMMAND* *ARGUMENTS...*
     as the command.
 
+# ENVIRONMENT
+
+`PWD`
+:   **pressure-vessel-launcher**(1) sets this to the current working
+    directory (as specified by **--directory**, or inherited from the
+    launcher) for each command executed inside the container,
+    overriding the environment options shown above.
+
 # OUTPUT
 
 The standard output from *COMMAND* is printed on standard output.
diff --git a/pressure-vessel/launcher.1.md b/pressure-vessel/launcher.1.md
index 632c03a60..f4c4b32dc 100644
--- a/pressure-vessel/launcher.1.md
+++ b/pressure-vessel/launcher.1.md
@@ -74,6 +74,12 @@ D-Bus session bus, and executes arbitrary commands as subprocesses.
 **--verbose**
 :   Be more verbose.
 
+# ENVIRONMENT
+
+`PWD`
+:   Set to the current working directory for each command executed
+    inside the container, overriding **--lock-env-from-fd**.
+
 # OUTPUT
 
 **pressure-vessel-launcher** prints zero or more lines of
diff --git a/pressure-vessel/launcher.c b/pressure-vessel/launcher.c
index 69cf1de4d..0a2d3ef76 100644
--- a/pressure-vessel/launcher.c
+++ b/pressure-vessel/launcher.c
@@ -66,6 +66,7 @@ static GHashTable *client_pid_data_hash = NULL;
 static guint name_owner_id = 0;
 static GMainLoop *main_loop;
 static PvLauncher1 *launcher;
+static gchar *original_cwd_l = NULL;
 
 /*
  * Close the --info-fd, and also close standard output (if different).
@@ -382,6 +383,11 @@ handle_launch (PvLauncher1           *object,
       const char *val = NULL;
       g_variant_get_child (arg_envs, i, "{&s&s}", &var, &val);
 
+      /* Ignore PWD: we special-case that later, and the debug message
+       * if it is locked would be confusing */
+      if (g_strcmp0 (var, "PWD") == 0)
+        continue;
+
       if (g_hash_table_contains (lock_env_hash, var))
         {
           const gchar *locked_val = g_environ_getenv (env, var);
@@ -398,6 +404,10 @@ handle_launch (PvLauncher1           *object,
 
   for (i = 0; unset_env != NULL && unset_env[i] != NULL; i++)
     {
+      /* Again ignore PWD */
+      if (g_strcmp0 (unset_env[i], "PWD") == 0)
+        continue;
+
       if (g_hash_table_contains (lock_env_hash, unset_env[i]))
         {
           const gchar *locked_val = g_environ_getenv (env, unset_env[i]);
@@ -411,6 +421,11 @@ handle_launch (PvLauncher1           *object,
       env = g_environ_unsetenv (env, unset_env[i]);
     }
 
+  if (arg_cwd_path == NULL)
+    env = g_environ_setenv (env, "PWD", original_cwd_l, TRUE);
+  else
+    env = g_environ_setenv (env, "PWD", arg_cwd_path, TRUE);
+
   /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
   if (!g_spawn_async_with_pipes (arg_cwd_path,
                                  (gchar **) arg_argv,
@@ -1102,6 +1117,8 @@ main (int argc,
 
   my_pid = getpid ();
 
+  pv_get_current_dirs (NULL, &original_cwd_l);
+
   setlocale (LC_ALL, "");
 
   g_set_prgname ("pressure-vessel-launcher");
@@ -1422,6 +1439,8 @@ out:
   g_clear_error (&local_error);
   close_info_fh ();
 
+  g_free (original_cwd_l);
+
   g_debug ("Exiting with status %d", ret);
   return ret;
 }
diff --git a/pressure-vessel/wrap.1.md b/pressure-vessel/wrap.1.md
index 128c0271f..96400f30d 100644
--- a/pressure-vessel/wrap.1.md
+++ b/pressure-vessel/wrap.1.md
@@ -262,6 +262,10 @@ The following environment variables (among others) are read by
 `PULSE_SERVER`
 :   Used to locate a PulseAudio server.
 
+`PWD`
+:   Used to choose between logically equivalent names for the current
+    working directory (see **get_current_dir_name**(3)).
+
 `STEAM_COMPAT_APP_ID` (integer)
 :   Equivalent to `--steam-app-id="$STEAM_COMPAT_APP_ID"`.
 
@@ -379,6 +383,9 @@ The following environment variables are set by **pressure-vessel-wrap**(1).
 `PULSE_SERVER`
 :   Set to the address of a PulseAudio server socket.
 
+`PWD`
+:   Set to the current working directory inside the container.
+
 `VDPAU_DRIVER_PATH`
 :   Set to a search path for VDPAU drivers
     if `--runtime` and `--with-host-graphics` are active.
-- 
GitLab