"Fossies" - the Fresh Open Source Software Archive

Member "flatpak-1.12.2/common/flatpak-run.c" (11 Oct 2021, 164356 Bytes) of package /linux/misc/flatpak-1.12.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "flatpak-run.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.12.1_vs_1.12.2.

    1 /*
    2  * Copyright © 2014-2019 Red Hat, Inc
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU Lesser General Public
    6  * License as published by the Free Software Foundation; either
    7  * version 2.1 of the License, or (at your option) any later version.
    8  *
    9  * This library is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12  * Lesser General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU Lesser General Public
   15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * Authors:
   18  *       Alexander Larsson <alexl@redhat.com>
   19  */
   20 
   21 #include "config.h"
   22 
   23 #include <string.h>
   24 #include <ctype.h>
   25 #include <fcntl.h>
   26 #include <gio/gdesktopappinfo.h>
   27 #include <stdio.h>
   28 #include <unistd.h>
   29 #include <sys/utsname.h>
   30 #include <sys/socket.h>
   31 #include <sys/ioctl.h>
   32 #include <sys/vfs.h>
   33 #include <sys/personality.h>
   34 #include <grp.h>
   35 #include <unistd.h>
   36 #include <gio/gunixfdlist.h>
   37 #ifdef HAVE_DCONF
   38 #include <dconf/dconf.h>
   39 #endif
   40 #ifdef HAVE_LIBMALCONTENT
   41 #include <libmalcontent/malcontent.h>
   42 #endif
   43 
   44 #include "flatpak-syscalls-private.h"
   45 
   46 #ifdef ENABLE_SECCOMP
   47 #include <seccomp.h>
   48 #endif
   49 
   50 #ifdef ENABLE_XAUTH
   51 #include <X11/Xauth.h>
   52 #endif
   53 
   54 #include <glib/gi18n-lib.h>
   55 
   56 #include <gio/gio.h>
   57 #include "libglnx/libglnx.h"
   58 
   59 #include "flatpak-run-private.h"
   60 #include "flatpak-proxy.h"
   61 #include "flatpak-utils-base-private.h"
   62 #include "flatpak-dir-private.h"
   63 #include "flatpak-instance-private.h"
   64 #include "flatpak-systemd-dbus-generated.h"
   65 #include "flatpak-document-dbus-generated.h"
   66 #include "flatpak-error.h"
   67 #include "session-helper/flatpak-session-helper.h"
   68 
   69 #define DEFAULT_SHELL "/bin/sh"
   70 
   71 const char * const abs_usrmerged_dirs[] =
   72 {
   73   "/bin",
   74   "/lib",
   75   "/lib32",
   76   "/lib64",
   77   "/sbin",
   78   NULL
   79 };
   80 const char * const *flatpak_abs_usrmerged_dirs = abs_usrmerged_dirs;
   81 
   82 static char *
   83 extract_unix_path_from_dbus_address (const char *address)
   84 {
   85   const char *path, *path_end;
   86 
   87   if (address == NULL)
   88     return NULL;
   89 
   90   if (!g_str_has_prefix (address, "unix:"))
   91     return NULL;
   92 
   93   path = strstr (address, "path=");
   94   if (path == NULL)
   95     return NULL;
   96   path += strlen ("path=");
   97   path_end = path;
   98   while (*path_end != 0 && *path_end != ',')
   99     path_end++;
  100 
  101   return g_strndup (path, path_end - path);
  102 }
  103 
  104 #ifdef ENABLE_XAUTH
  105 static gboolean
  106 auth_streq (char *str,
  107             char *au_str,
  108             int   au_len)
  109 {
  110   return au_len == strlen (str) && memcmp (str, au_str, au_len) == 0;
  111 }
  112 
  113 static gboolean
  114 xauth_entry_should_propagate (Xauth *xa,
  115                               char  *hostname,
  116                               char  *number)
  117 {
  118   /* ensure entry isn't for remote access */
  119   if (xa->family != FamilyLocal && xa->family != FamilyWild)
  120     return FALSE;
  121 
  122   /* ensure entry is for this machine */
  123   if (xa->family == FamilyLocal && !auth_streq (hostname, xa->address, xa->address_length))
  124     {
  125       /* OpenSUSE inherits the hostname value from DHCP without updating
  126        * its X11 authentication cookie. The old hostname value can still
  127        * be found in the environment variable XAUTHLOCALHOSTNAME.
  128        * For reference:
  129        * https://bugzilla.opensuse.org/show_bug.cgi?id=262309
  130        * For this reason if we have a cookie whose address is equal to the
  131        * variable XAUTHLOCALHOSTNAME, we still need to propagate it, but
  132        * we also need to change its address to `unames.nodename`.
  133        */
  134       const char *xauth_local_hostname;
  135       xauth_local_hostname = g_getenv ("XAUTHLOCALHOSTNAME");
  136       if (xauth_local_hostname == NULL)
  137         return FALSE;
  138 
  139       if (!auth_streq ((char *) xauth_local_hostname, xa->address, xa->address_length))
  140         return FALSE;
  141     }
  142 
  143   /* ensure entry is for this session */
  144   if (xa->number != NULL && !auth_streq (number, xa->number, xa->number_length))
  145     return FALSE;
  146 
  147   return TRUE;
  148 }
  149 
  150 static void
  151 write_xauth (char *number, FILE *output)
  152 {
  153   Xauth *xa, local_xa;
  154   char *filename;
  155   FILE *f;
  156   struct utsname unames;
  157 
  158   if (uname (&unames))
  159     {
  160       g_warning ("uname failed");
  161       return;
  162     }
  163 
  164   filename = XauFileName ();
  165   f = fopen (filename, "rb");
  166   if (f == NULL)
  167     return;
  168 
  169   while (TRUE)
  170     {
  171       xa = XauReadAuth (f);
  172       if (xa == NULL)
  173         break;
  174       if (xauth_entry_should_propagate (xa, unames.nodename, number))
  175         {
  176           local_xa = *xa;
  177           if (local_xa.number)
  178             {
  179               local_xa.number = "99";
  180               local_xa.number_length = 2;
  181             }
  182 
  183           if (local_xa.family == FamilyLocal &&
  184               !auth_streq (unames.nodename, local_xa.address, local_xa.address_length))
  185             {
  186               /* If we decided to propagate this cookie, but its address
  187                * doesn't match `unames.nodename`, we need to change it or
  188                * inside the container it will not work.
  189                */
  190               local_xa.address = unames.nodename;
  191               local_xa.address_length = strlen (local_xa.address);
  192             }
  193 
  194           if (!XauWriteAuth (output, &local_xa))
  195             g_warning ("xauth write error");
  196         }
  197 
  198       XauDisposeAuth (xa);
  199     }
  200 
  201   fclose (f);
  202 }
  203 #endif /* ENABLE_XAUTH */
  204 
  205 static void
  206 flatpak_run_add_x11_args (FlatpakBwrap *bwrap,
  207                           gboolean      allowed)
  208 {
  209   g_autofree char *x11_socket = NULL;
  210   const char *display;
  211 
  212   /* Always cover /tmp/.X11-unix, that way we never see the host one in case
  213    * we have access to the host /tmp. If you request X access we'll put the right
  214    * thing in this anyway.
  215    *
  216    * We need to be a bit careful here, because there are two situations in
  217    * which potentially hostile processes have access to /tmp and could
  218    * create symlinks, which in principle could cause us to create the
  219    * directory and mount the tmpfs at the target of the symlink instead
  220    * of in the intended place:
  221    *
  222    * - With --filesystem=/tmp, it's the host /tmp - but because of the
  223    *   special historical status of /tmp/.X11-unix, we can assume that
  224    *   it is pre-created by the host system before user code gets to run.
  225    *
  226    * - When /tmp is shared between all instances of the same app ID,
  227    *   in principle the app has control over what's in /tmp, but in
  228    *   practice it can't interfere with /tmp/.X11-unix, because we do
  229    *   this unconditionally - therefore by the time app code runs,
  230    *   /tmp/.X11-unix is already a mount point, meaning the app cannot
  231    *   rename or delete it.
  232    */
  233   flatpak_bwrap_add_args (bwrap,
  234                           "--tmpfs", "/tmp/.X11-unix",
  235                           NULL);
  236 
  237   if (!allowed)
  238     {
  239       flatpak_bwrap_unset_env (bwrap, "DISPLAY");
  240       return;
  241     }
  242 
  243   g_debug ("Allowing x11 access");
  244 
  245   display = g_getenv ("DISPLAY");
  246   if (display && display[0] == ':' && g_ascii_isdigit (display[1]))
  247     {
  248       const char *display_nr = &display[1];
  249       const char *display_nr_end = display_nr;
  250       g_autofree char *d = NULL;
  251 
  252       while (g_ascii_isdigit (*display_nr_end))
  253         display_nr_end++;
  254 
  255       d = g_strndup (display_nr, display_nr_end - display_nr);
  256       x11_socket = g_strdup_printf ("/tmp/.X11-unix/X%s", d);
  257 
  258       flatpak_bwrap_add_args (bwrap,
  259                               "--ro-bind", x11_socket, "/tmp/.X11-unix/X99",
  260                               NULL);
  261       flatpak_bwrap_set_env (bwrap, "DISPLAY", ":99.0", TRUE);
  262 
  263 #ifdef ENABLE_XAUTH
  264       g_auto(GLnxTmpfile) xauth_tmpf  = { 0, };
  265 
  266       if (glnx_open_anonymous_tmpfile_full (O_RDWR | O_CLOEXEC, "/tmp", &xauth_tmpf, NULL))
  267         {
  268           FILE *output = fdopen (xauth_tmpf.fd, "wb");
  269           if (output != NULL)
  270             {
  271               /* fd is now owned by output, steal it from the tmpfile */
  272               int tmp_fd = dup (glnx_steal_fd (&xauth_tmpf.fd));
  273               if (tmp_fd != -1)
  274                 {
  275                   static const char dest[] = "/run/flatpak/Xauthority";
  276 
  277                   write_xauth (d, output);
  278                   flatpak_bwrap_add_args_data_fd (bwrap, "--ro-bind-data", tmp_fd, dest);
  279 
  280                   flatpak_bwrap_set_env (bwrap, "XAUTHORITY", dest, TRUE);
  281                 }
  282 
  283               fclose (output);
  284 
  285               if (tmp_fd != -1)
  286                 lseek (tmp_fd, 0, SEEK_SET);
  287             }
  288         }
  289 #endif
  290     }
  291   else
  292     {
  293       flatpak_bwrap_unset_env (bwrap, "DISPLAY");
  294     }
  295 }
  296 
  297 static gboolean
  298 flatpak_run_add_wayland_args (FlatpakBwrap *bwrap)
  299 {
  300   const char *wayland_display;
  301   g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
  302   g_autofree char *wayland_socket = NULL;
  303   g_autofree char *sandbox_wayland_socket = NULL;
  304   gboolean res = FALSE;
  305   struct stat statbuf;
  306 
  307   wayland_display = g_getenv ("WAYLAND_DISPLAY");
  308   if (!wayland_display)
  309     wayland_display = "wayland-0";
  310 
  311   wayland_socket = g_build_filename (user_runtime_dir, wayland_display, NULL);
  312 
  313   if (!g_str_has_prefix (wayland_display, "wayland-") ||
  314       strchr (wayland_display, '/') != NULL)
  315     {
  316       wayland_display = "wayland-0";
  317       flatpak_bwrap_set_env (bwrap, "WAYLAND_DISPLAY", wayland_display, TRUE);
  318     }
  319 
  320   sandbox_wayland_socket = g_strdup_printf ("/run/flatpak/%s", wayland_display);
  321 
  322   if (stat (wayland_socket, &statbuf) == 0 &&
  323       (statbuf.st_mode & S_IFMT) == S_IFSOCK)
  324     {
  325       res = TRUE;
  326       flatpak_bwrap_add_args (bwrap,
  327                               "--ro-bind", wayland_socket, sandbox_wayland_socket,
  328                               NULL);
  329       flatpak_bwrap_add_runtime_dir_member (bwrap, wayland_display);
  330     }
  331   return res;
  332 }
  333 
  334 static void
  335 flatpak_run_add_ssh_args (FlatpakBwrap *bwrap)
  336 {
  337   static const char sandbox_auth_socket[] = "/run/flatpak/ssh-auth";
  338   const char * auth_socket;
  339 
  340   auth_socket = g_getenv ("SSH_AUTH_SOCK");
  341 
  342   if (!auth_socket)
  343     return; /* ssh agent not present */
  344 
  345   if (!g_file_test (auth_socket, G_FILE_TEST_EXISTS))
  346     {
  347       /* Let's clean it up, so that the application will not try to connect */
  348       flatpak_bwrap_unset_env (bwrap, "SSH_AUTH_SOCK");
  349       return;
  350     }
  351 
  352   flatpak_bwrap_add_args (bwrap,
  353                           "--ro-bind", auth_socket, sandbox_auth_socket,
  354                           NULL);
  355   flatpak_bwrap_set_env (bwrap, "SSH_AUTH_SOCK", sandbox_auth_socket, TRUE);
  356 }
  357 
  358 static void
  359 flatpak_run_add_pcsc_args (FlatpakBwrap *bwrap)
  360 {
  361   const char * pcsc_socket;
  362   const char * sandbox_pcsc_socket = "/run/pcscd/pcscd.comm";
  363 
  364   pcsc_socket = g_getenv ("PCSCLITE_CSOCK_NAME");
  365   if (pcsc_socket)
  366     {
  367       if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS))
  368         {
  369           flatpak_bwrap_unset_env (bwrap, "PCSCLITE_CSOCK_NAME");
  370           return;
  371         }
  372     }
  373   else
  374     {
  375       pcsc_socket = "/run/pcscd/pcscd.comm";
  376       if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS))
  377         return;
  378     }
  379 
  380   flatpak_bwrap_add_args (bwrap,
  381                           "--ro-bind", pcsc_socket, sandbox_pcsc_socket,
  382                           NULL);
  383   flatpak_bwrap_set_env (bwrap, "PCSCLITE_CSOCK_NAME", sandbox_pcsc_socket, TRUE);
  384 }
  385 
  386 static gboolean
  387 flatpak_run_cups_check_server_is_socket (const char *server)
  388 {
  389   if (g_str_has_prefix (server, "/") && strstr (server, ":") == NULL)
  390     return TRUE;
  391 
  392   return FALSE;
  393 }
  394 
  395 /* Try to find a default server from a cups confguration file */
  396 static char *
  397 flatpak_run_get_cups_server_name_config (const char *path)
  398 {
  399   g_autoptr(GFile) file = g_file_new_for_path (path);
  400   g_autoptr(GError) my_error = NULL;
  401   g_autoptr(GFileInputStream) input_stream = NULL;
  402   g_autoptr(GDataInputStream) data_stream = NULL;
  403   size_t len;
  404 
  405   input_stream = g_file_read (file, NULL, &my_error);
  406   if (my_error)
  407     {
  408       g_debug ("CUPS configuration file '%s': %s", path, my_error->message);
  409       return NULL;
  410     }
  411 
  412   data_stream = g_data_input_stream_new (G_INPUT_STREAM (input_stream));
  413 
  414   while (TRUE)
  415     {
  416       g_autofree char *line = g_data_input_stream_read_line (data_stream, &len, NULL, NULL);
  417       if (line == NULL)
  418         break;
  419 
  420       g_strchug (line);
  421 
  422       if ((*line  == '\0') || (*line == '#'))
  423         continue;
  424 
  425       g_auto(GStrv) tokens = g_strsplit (line, " ", 2);
  426 
  427       if ((tokens[0] != NULL) && (tokens[1] != NULL))
  428         {
  429           if (strcmp ("ServerName", tokens[0]) == 0)
  430             {
  431               g_strchug (tokens[1]);
  432 
  433               if (flatpak_run_cups_check_server_is_socket (tokens[1]))
  434                 return g_strdup (tokens[1]);
  435             }
  436         }
  437     }
  438 
  439     return NULL;
  440 }
  441 
  442 static char *
  443 flatpak_run_get_cups_server_name (void)
  444 {
  445   g_autofree char * cups_server = NULL;
  446   g_autofree char * cups_config_path = NULL;
  447 
  448   /* TODO
  449    * we don't currently support cups servers located on the network, if such
  450    * server is detected, we simply ignore it and in the worst case we fallback
  451    * to the default socket
  452    */
  453   cups_server = g_strdup (g_getenv ("CUPS_SERVER"));
  454   if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server))
  455     return g_steal_pointer (&cups_server);
  456   g_clear_pointer (&cups_server, g_free);
  457 
  458   cups_config_path = g_build_filename (g_get_home_dir (), ".cups/client.conf", NULL);
  459   cups_server = flatpak_run_get_cups_server_name_config (cups_config_path);
  460   if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server))
  461     return g_steal_pointer (&cups_server);
  462   g_clear_pointer (&cups_server, g_free);
  463 
  464   cups_server = flatpak_run_get_cups_server_name_config ("/etc/cups/client.conf");
  465   if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server))
  466     return g_steal_pointer (&cups_server);
  467 
  468   // Fallback to default socket
  469   return g_strdup ("/var/run/cups/cups.sock");
  470 }
  471 
  472 static void
  473 flatpak_run_add_cups_args (FlatpakBwrap *bwrap)
  474 {
  475   g_autofree char * sandbox_server_name = g_strdup ("/var/run/cups/cups.sock");
  476   g_autofree char * cups_server_name = flatpak_run_get_cups_server_name ();
  477 
  478   if (!g_file_test (cups_server_name, G_FILE_TEST_EXISTS))
  479     {
  480       g_debug ("Could not find CUPS server");
  481       return;
  482     }
  483 
  484   flatpak_bwrap_add_args (bwrap,
  485                           "--ro-bind", cups_server_name, sandbox_server_name,
  486                           NULL);
  487 }
  488 
  489 /* Try to find a default server from a pulseaudio confguration file */
  490 static char *
  491 flatpak_run_get_pulseaudio_server_user_config (const char *path)
  492 {
  493   g_autoptr(GFile) file = g_file_new_for_path (path);
  494   g_autoptr(GError) my_error = NULL;
  495   g_autoptr(GFileInputStream) input_stream = NULL;
  496   g_autoptr(GDataInputStream) data_stream = NULL;
  497   size_t len;
  498 
  499   input_stream = g_file_read (file, NULL, &my_error);
  500   if (my_error)
  501     {
  502       g_debug ("Pulseaudio user configuration file '%s': %s", path, my_error->message);
  503       return NULL;
  504     }
  505 
  506   data_stream = g_data_input_stream_new (G_INPUT_STREAM (input_stream));
  507 
  508   while (TRUE)
  509     {
  510       g_autofree char *line = g_data_input_stream_read_line (data_stream, &len, NULL, NULL);
  511       if (line == NULL)
  512         break;
  513 
  514       g_strchug (line);
  515 
  516       if ((*line  == '\0') || (*line == ';') || (*line == '#'))
  517         continue;
  518 
  519       if (g_str_has_prefix (line, ".include "))
  520         {
  521           g_autofree char *rec_path = g_strdup (line + 9);
  522           g_strstrip (rec_path);
  523           char *found = flatpak_run_get_pulseaudio_server_user_config (rec_path);
  524           if (found)
  525             return found;
  526         }
  527       else if (g_str_has_prefix (line, "["))
  528         {
  529           return NULL;
  530         }
  531       else
  532         {
  533           g_auto(GStrv) tokens = g_strsplit (line, "=", 2);
  534 
  535           if ((tokens[0] != NULL) && (tokens[1] != NULL))
  536             {
  537               g_strchomp (tokens[0]);
  538               if (strcmp ("default-server", tokens[0]) == 0)
  539                 {
  540                   g_strstrip (tokens[1]);
  541                   g_debug ("Found pulseaudio socket from configuration file '%s': %s", path, tokens[1]);
  542                   return g_strdup (tokens[1]);
  543                 }
  544             }
  545         }
  546     }
  547 
  548   return NULL;
  549 }
  550 
  551 static char *
  552 flatpak_run_get_pulseaudio_server (void)
  553 {
  554   const char * pulse_clientconfig;
  555   char *pulse_server;
  556   g_autofree char *pulse_user_config = NULL;
  557 
  558   pulse_server = g_strdup (g_getenv ("PULSE_SERVER"));
  559   if (pulse_server)
  560     return pulse_server;
  561 
  562   pulse_clientconfig = g_getenv ("PULSE_CLIENTCONFIG");
  563   if (pulse_clientconfig)
  564     return flatpak_run_get_pulseaudio_server_user_config (pulse_clientconfig);
  565 
  566   pulse_user_config = g_build_filename (g_get_user_config_dir (), "pulse/client.conf", NULL);
  567   pulse_server = flatpak_run_get_pulseaudio_server_user_config (pulse_user_config);
  568   if (pulse_server)
  569     return pulse_server;
  570 
  571   pulse_server = flatpak_run_get_pulseaudio_server_user_config ("/etc/pulse/client.conf");
  572   if (pulse_server)
  573     return pulse_server;
  574 
  575   return NULL;
  576 }
  577 
  578 static char *
  579 flatpak_run_parse_pulse_server (const char *value)
  580 {
  581   g_auto(GStrv) servers = g_strsplit (value, " ", 0);
  582   gsize i;
  583 
  584   for (i = 0; servers[i] != NULL; i++)
  585     {
  586       const char *server = servers[i];
  587       if (g_str_has_prefix (server, "{"))
  588         {
  589           const char * closing = strstr (server, "}");
  590           if (closing == NULL)
  591             continue;
  592           server = closing + 1;
  593         }
  594       if (g_str_has_prefix (server, "unix:"))
  595         return g_strdup (server + 5);
  596     }
  597 
  598   return NULL;
  599 }
  600 
  601 /*
  602  * Get the machine ID as used by PulseAudio. This is the systemd/D-Bus
  603  * machine ID, or failing that, the hostname.
  604  */
  605 static char *
  606 flatpak_run_get_pulse_machine_id (void)
  607 {
  608   static const char * const machine_ids[] =
  609   {
  610     "/etc/machine-id",
  611     "/var/lib/dbus/machine-id",
  612   };
  613   gsize i;
  614 
  615   for (i = 0; i < G_N_ELEMENTS (machine_ids); i++)
  616     {
  617       g_autofree char *ret = NULL;
  618 
  619       if (g_file_get_contents (machine_ids[i], &ret, NULL, NULL))
  620         {
  621           gsize j;
  622 
  623           g_strstrip (ret);
  624 
  625           for (j = 0; ret[j] != '\0'; j++)
  626             {
  627               if (!g_ascii_isxdigit (ret[j]))
  628                 break;
  629             }
  630 
  631           if (ret[0] != '\0' && ret[j] == '\0')
  632             return g_steal_pointer (&ret);
  633         }
  634     }
  635 
  636   return g_strdup (g_get_host_name ());
  637 }
  638 
  639 /*
  640  * Get the directory used by PulseAudio for its configuration.
  641  */
  642 static char *
  643 flatpak_run_get_pulse_home (void)
  644 {
  645   /* Legacy path ~/.pulse is tried first, for compatibility */
  646   {
  647     const char *parent = g_get_home_dir ();
  648     g_autofree char *ret = g_build_filename (parent, ".pulse", NULL);
  649 
  650     if (g_file_test (ret, G_FILE_TEST_IS_DIR))
  651       return g_steal_pointer (&ret);
  652   }
  653 
  654   /* The more modern path, usually ~/.config/pulse */
  655   {
  656     const char *parent = g_get_user_config_dir ();
  657     /* Usually ~/.config/pulse */
  658     g_autofree char *ret = g_build_filename (parent, "pulse", NULL);
  659 
  660     if (g_file_test (ret, G_FILE_TEST_IS_DIR))
  661       return g_steal_pointer (&ret);
  662   }
  663 
  664   return NULL;
  665 }
  666 
  667 /*
  668  * Get the runtime directory used by PulseAudio for its socket.
  669  */
  670 static char *
  671 flatpak_run_get_pulse_runtime_dir (void)
  672 {
  673   const char *val = NULL;
  674 
  675   val = g_getenv ("PULSE_RUNTIME_PATH");
  676 
  677   if (val != NULL)
  678     return realpath (val, NULL);
  679 
  680   {
  681     const char *user_runtime_dir = g_get_user_runtime_dir ();
  682 
  683     if (user_runtime_dir != NULL)
  684       {
  685         g_autofree char *dir = g_build_filename (user_runtime_dir, "pulse", NULL);
  686 
  687         if (g_file_test (dir, G_FILE_TEST_IS_DIR))
  688           return realpath (dir, NULL);
  689       }
  690   }
  691 
  692   {
  693     g_autofree char *pulse_home = flatpak_run_get_pulse_home ();
  694     g_autofree char *machine_id = flatpak_run_get_pulse_machine_id ();
  695 
  696     if (pulse_home != NULL && machine_id != NULL)
  697       {
  698         /* This is usually a symlink, but we take its realpath() anyway */
  699         g_autofree char *dir = g_strdup_printf ("%s/%s-runtime", pulse_home, machine_id);
  700 
  701         if (g_file_test (dir, G_FILE_TEST_IS_DIR))
  702           return realpath (dir, NULL);
  703       }
  704   }
  705 
  706   return NULL;
  707 }
  708 
  709 static void
  710 flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap)
  711 {
  712   g_autofree char *pulseaudio_server = flatpak_run_get_pulseaudio_server ();
  713   g_autofree char *pulseaudio_socket = NULL;
  714   g_autofree char *pulse_runtime_dir = flatpak_run_get_pulse_runtime_dir ();
  715 
  716   if (pulseaudio_server)
  717     pulseaudio_socket = flatpak_run_parse_pulse_server (pulseaudio_server);
  718 
  719   if (!pulseaudio_socket)
  720     {
  721       pulseaudio_socket = g_build_filename (pulse_runtime_dir, "native", NULL);
  722 
  723       if (!g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS))
  724         g_clear_pointer (&pulseaudio_socket, g_free);
  725     }
  726 
  727   if (!pulseaudio_socket)
  728     {
  729       pulseaudio_socket = realpath ("/var/run/pulse/native", NULL);
  730 
  731       if (pulseaudio_socket && !g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS))
  732         g_clear_pointer (&pulseaudio_socket, g_free);
  733     }
  734 
  735   flatpak_bwrap_unset_env (bwrap, "PULSE_SERVER");
  736 
  737   if (pulseaudio_socket && g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS))
  738     {
  739       static const char sandbox_socket_path[] = "/run/flatpak/pulse/native";
  740       static const char pulse_server[] = "unix:/run/flatpak/pulse/native";
  741       static const char config_path[] = "/run/flatpak/pulse/config";
  742       gboolean share_shm = FALSE; /* TODO: When do we add this? */
  743       g_autofree char *client_config = g_strdup_printf ("enable-shm=%s\n", share_shm ? "yes" : "no");
  744 
  745       /* FIXME - error handling */
  746       if (!flatpak_bwrap_add_args_data (bwrap, "pulseaudio", client_config, -1, config_path, NULL))
  747         return;
  748 
  749       flatpak_bwrap_add_args (bwrap,
  750                               "--ro-bind", pulseaudio_socket, sandbox_socket_path,
  751                               NULL);
  752 
  753       flatpak_bwrap_set_env (bwrap, "PULSE_SERVER", pulse_server, TRUE);
  754       flatpak_bwrap_set_env (bwrap, "PULSE_CLIENTCONFIG", config_path, TRUE);
  755       flatpak_bwrap_add_runtime_dir_member (bwrap, "pulse");
  756     }
  757   else
  758     g_debug ("Could not find pulseaudio socket");
  759 
  760   /* Also allow ALSA access. This was added in 1.8, and is not ideally named. However,
  761    * since the practical permission of ALSA and PulseAudio are essentially the same, and
  762    * since we don't want to add more permissions for something we plan to replace with
  763    * portals/pipewire going forward we reinterpret pulseaudio to also mean ALSA.
  764    */
  765   if (g_file_test ("/dev/snd", G_FILE_TEST_IS_DIR))
  766     flatpak_bwrap_add_args (bwrap, "--dev-bind", "/dev/snd", "/dev/snd", NULL);
  767 }
  768 
  769 static void
  770 flatpak_run_add_resolved_args (FlatpakBwrap *bwrap)
  771 {
  772   const char *resolved_socket = "/run/systemd/resolve/io.systemd.Resolve";
  773 
  774   if (g_file_test (resolved_socket, G_FILE_TEST_EXISTS))
  775     flatpak_bwrap_add_args (bwrap, "--bind", resolved_socket, resolved_socket, NULL);
  776 }
  777 
  778 static void
  779 flatpak_run_add_journal_args (FlatpakBwrap *bwrap)
  780 {
  781   g_autofree char *journal_socket_socket = g_strdup ("/run/systemd/journal/socket");
  782   g_autofree char *journal_stdout_socket = g_strdup ("/run/systemd/journal/stdout");
  783 
  784   if (g_file_test (journal_socket_socket, G_FILE_TEST_EXISTS))
  785     {
  786       flatpak_bwrap_add_args (bwrap,
  787                               "--ro-bind", journal_socket_socket, journal_socket_socket,
  788                               NULL);
  789     }
  790   if (g_file_test (journal_stdout_socket, G_FILE_TEST_EXISTS))
  791     {
  792       flatpak_bwrap_add_args (bwrap,
  793                               "--ro-bind", journal_stdout_socket, journal_stdout_socket,
  794                               NULL);
  795     }
  796 }
  797 
  798 static char *
  799 create_proxy_socket (char *template)
  800 {
  801   g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
  802   g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy", NULL);
  803   g_autofree char *proxy_socket = g_build_filename (proxy_socket_dir, template, NULL);
  804   int fd;
  805 
  806   if (!glnx_shutil_mkdir_p_at (AT_FDCWD, proxy_socket_dir, 0755, NULL, NULL))
  807     return NULL;
  808 
  809   fd = g_mkstemp (proxy_socket);
  810   if (fd == -1)
  811     return NULL;
  812 
  813   close (fd);
  814 
  815   return g_steal_pointer (&proxy_socket);
  816 }
  817 
  818 static gboolean
  819 flatpak_run_add_system_dbus_args (FlatpakBwrap   *app_bwrap,
  820                                   FlatpakBwrap   *proxy_arg_bwrap,
  821                                   FlatpakContext *context,
  822                                   FlatpakRunFlags flags)
  823 {
  824   gboolean unrestricted, no_proxy;
  825   const char *dbus_address = g_getenv ("DBUS_SYSTEM_BUS_ADDRESS");
  826   g_autofree char *real_dbus_address = NULL;
  827   g_autofree char *dbus_system_socket = NULL;
  828 
  829   unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS) != 0;
  830   if (unrestricted)
  831     g_debug ("Allowing system-dbus access");
  832 
  833   no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY) != 0;
  834 
  835   if (dbus_address != NULL)
  836     dbus_system_socket = extract_unix_path_from_dbus_address (dbus_address);
  837   else if (g_file_test ("/var/run/dbus/system_bus_socket", G_FILE_TEST_EXISTS))
  838     dbus_system_socket = g_strdup ("/var/run/dbus/system_bus_socket");
  839 
  840   if (dbus_system_socket != NULL && unrestricted)
  841     {
  842       flatpak_bwrap_add_args (app_bwrap,
  843                               "--ro-bind", dbus_system_socket, "/run/dbus/system_bus_socket",
  844                               NULL);
  845       flatpak_bwrap_set_env (app_bwrap, "DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket", TRUE);
  846 
  847       return TRUE;
  848     }
  849   else if (!no_proxy && flatpak_context_get_needs_system_bus_proxy (context))
  850     {
  851       g_autofree char *proxy_socket = create_proxy_socket ("system-bus-proxy-XXXXXX");
  852 
  853       if (proxy_socket == NULL)
  854         return FALSE;
  855 
  856       if (dbus_address)
  857         real_dbus_address = g_strdup (dbus_address);
  858       else
  859         real_dbus_address = g_strdup_printf ("unix:path=%s", dbus_system_socket);
  860 
  861       flatpak_bwrap_add_args (proxy_arg_bwrap, real_dbus_address, proxy_socket, NULL);
  862 
  863       if (!unrestricted)
  864         flatpak_context_add_bus_filters (context, NULL, FALSE, flags & FLATPAK_RUN_FLAG_SANDBOX, proxy_arg_bwrap);
  865 
  866       if ((flags & FLATPAK_RUN_FLAG_LOG_SYSTEM_BUS) != 0)
  867         flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL);
  868 
  869       flatpak_bwrap_add_args (app_bwrap,
  870                               "--ro-bind", proxy_socket, "/run/dbus/system_bus_socket",
  871                               NULL);
  872       flatpak_bwrap_set_env (app_bwrap, "DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket", TRUE);
  873 
  874       return TRUE;
  875     }
  876   return FALSE;
  877 }
  878 
  879 static gboolean
  880 flatpak_run_add_session_dbus_args (FlatpakBwrap   *app_bwrap,
  881                                    FlatpakBwrap   *proxy_arg_bwrap,
  882                                    FlatpakContext *context,
  883                                    FlatpakRunFlags flags,
  884                                    const char     *app_id)
  885 {
  886   static const char sandbox_socket_path[] = "/run/flatpak/bus";
  887   static const char sandbox_dbus_address[] = "unix:path=/run/flatpak/bus";
  888   gboolean unrestricted, no_proxy;
  889   const char *dbus_address = g_getenv ("DBUS_SESSION_BUS_ADDRESS");
  890   g_autofree char *dbus_session_socket = NULL;
  891 
  892   unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SESSION_BUS) != 0;
  893 
  894   if (dbus_address != NULL)
  895     {
  896       dbus_session_socket = extract_unix_path_from_dbus_address (dbus_address);
  897     }
  898   else
  899     {
  900       g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
  901       struct stat statbuf;
  902 
  903       dbus_session_socket = g_build_filename (user_runtime_dir, "bus", NULL);
  904 
  905       if (stat (dbus_session_socket, &statbuf) < 0
  906           || (statbuf.st_mode & S_IFMT) != S_IFSOCK
  907           || statbuf.st_uid != getuid ())
  908         return FALSE;
  909     }
  910 
  911   if (unrestricted)
  912     g_debug ("Allowing session-dbus access");
  913 
  914   no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY) != 0;
  915 
  916   if (dbus_session_socket != NULL && unrestricted)
  917     {
  918       flatpak_bwrap_add_args (app_bwrap,
  919                               "--ro-bind", dbus_session_socket, sandbox_socket_path,
  920                               NULL);
  921       flatpak_bwrap_set_env (app_bwrap, "DBUS_SESSION_BUS_ADDRESS", sandbox_dbus_address, TRUE);
  922       flatpak_bwrap_add_runtime_dir_member (app_bwrap, "bus");
  923 
  924       return TRUE;
  925     }
  926   else if (!no_proxy && dbus_address != NULL)
  927     {
  928       g_autofree char *proxy_socket = create_proxy_socket ("session-bus-proxy-XXXXXX");
  929 
  930       if (proxy_socket == NULL)
  931         return FALSE;
  932 
  933       flatpak_bwrap_add_args (proxy_arg_bwrap, dbus_address, proxy_socket, NULL);
  934 
  935       if (!unrestricted)
  936         {
  937           flatpak_context_add_bus_filters (context, app_id, TRUE, flags & FLATPAK_RUN_FLAG_SANDBOX, proxy_arg_bwrap);
  938 
  939           /* Allow calling any interface+method on all portals, but only receive broadcasts under /org/desktop/portal */
  940           flatpak_bwrap_add_arg (proxy_arg_bwrap,
  941                                  "--call=org.freedesktop.portal.*=*");
  942           flatpak_bwrap_add_arg (proxy_arg_bwrap,
  943                                  "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*");
  944         }
  945 
  946       if ((flags & FLATPAK_RUN_FLAG_LOG_SESSION_BUS) != 0)
  947         flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL);
  948 
  949       flatpak_bwrap_add_args (app_bwrap,
  950                               "--ro-bind", proxy_socket, sandbox_socket_path,
  951                               NULL);
  952       flatpak_bwrap_set_env (app_bwrap, "DBUS_SESSION_BUS_ADDRESS", sandbox_dbus_address, TRUE);
  953       flatpak_bwrap_add_runtime_dir_member (app_bwrap, "bus");
  954 
  955       return TRUE;
  956     }
  957 
  958   return FALSE;
  959 }
  960 
  961 static gboolean
  962 flatpak_run_add_a11y_dbus_args (FlatpakBwrap   *app_bwrap,
  963                                 FlatpakBwrap   *proxy_arg_bwrap,
  964                                 FlatpakContext *context,
  965                                 FlatpakRunFlags flags)
  966 {
  967   static const char sandbox_socket_path[] = "/run/flatpak/at-spi-bus";
  968   static const char sandbox_dbus_address[] = "unix:path=/run/flatpak/at-spi-bus";
  969   g_autoptr(GDBusConnection) session_bus = NULL;
  970   g_autofree char *a11y_address = NULL;
  971   g_autoptr(GError) local_error = NULL;
  972   g_autoptr(GDBusMessage) reply = NULL;
  973   g_autoptr(GDBusMessage) msg = NULL;
  974   g_autofree char *proxy_socket = NULL;
  975 
  976   if ((flags & FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY) != 0)
  977     return FALSE;
  978 
  979   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
  980   if (session_bus == NULL)
  981     return FALSE;
  982 
  983   msg = g_dbus_message_new_method_call ("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus", "GetAddress");
  984   g_dbus_message_set_body (msg, g_variant_new ("()"));
  985   reply =
  986     g_dbus_connection_send_message_with_reply_sync (session_bus, msg,
  987                                                     G_DBUS_SEND_MESSAGE_FLAGS_NONE,
  988                                                     30000,
  989                                                     NULL,
  990                                                     NULL,
  991                                                     NULL);
  992   if (reply)
  993     {
  994       if (g_dbus_message_to_gerror (reply, &local_error))
  995         {
  996           if (!g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
  997             g_message ("Can't find a11y bus: %s", local_error->message);
  998         }
  999       else
 1000         {
 1001           g_variant_get (g_dbus_message_get_body (reply),
 1002                          "(s)", &a11y_address);
 1003         }
 1004     }
 1005 
 1006   if (!a11y_address)
 1007     return FALSE;
 1008 
 1009   proxy_socket = create_proxy_socket ("a11y-bus-proxy-XXXXXX");
 1010   if (proxy_socket == NULL)
 1011     return FALSE;
 1012 
 1013   flatpak_bwrap_add_args (proxy_arg_bwrap,
 1014                           a11y_address,
 1015                           proxy_socket, "--filter", "--sloppy-names",
 1016                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Embed@/org/a11y/atspi/accessible/root",
 1017                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Unembed@/org/a11y/atspi/accessible/root",
 1018                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.Registry.GetRegisteredEvents@/org/a11y/atspi/registry",
 1019                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetKeystrokeListeners@/org/a11y/atspi/registry/deviceeventcontroller",
 1020                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetDeviceEventListeners@/org/a11y/atspi/registry/deviceeventcontroller",
 1021                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersSync@/org/a11y/atspi/registry/deviceeventcontroller",
 1022                           "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersAsync@/org/a11y/atspi/registry/deviceeventcontroller",
 1023                           NULL);
 1024 
 1025   if ((flags & FLATPAK_RUN_FLAG_LOG_A11Y_BUS) != 0)
 1026     flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL);
 1027 
 1028   flatpak_bwrap_add_args (app_bwrap,
 1029                           "--ro-bind", proxy_socket, sandbox_socket_path,
 1030                           NULL);
 1031   flatpak_bwrap_set_env (app_bwrap, "AT_SPI_BUS_ADDRESS", sandbox_dbus_address, TRUE);
 1032 
 1033   return TRUE;
 1034 }
 1035 
 1036 /* This wraps the argv in a bwrap call, primary to allow the
 1037    command to be run with a proper /.flatpak-info with data
 1038    taken from app_info_path */
 1039 static gboolean
 1040 add_bwrap_wrapper (FlatpakBwrap *bwrap,
 1041                    const char   *app_info_path,
 1042                    GError      **error)
 1043 {
 1044   glnx_autofd int app_info_fd = -1;
 1045   g_auto(GLnxDirFdIterator) dir_iter = { 0 };
 1046   struct dirent *dent;
 1047   g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
 1048   g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy/", NULL);
 1049 
 1050   app_info_fd = open (app_info_path, O_RDONLY | O_CLOEXEC);
 1051   if (app_info_fd == -1)
 1052     return glnx_throw_errno_prefix (error, _("Failed to open app info file"));
 1053 
 1054   if (!glnx_dirfd_iterator_init_at (AT_FDCWD, "/", FALSE, &dir_iter, error))
 1055     return FALSE;
 1056 
 1057   flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ());
 1058 
 1059   while (TRUE)
 1060     {
 1061       glnx_autofd int o_path_fd = -1;
 1062       struct statfs stfs;
 1063 
 1064       if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dir_iter, &dent, NULL, error))
 1065         return FALSE;
 1066 
 1067       if (dent == NULL)
 1068         break;
 1069 
 1070       if (strcmp (dent->d_name, ".flatpak-info") == 0)
 1071         continue;
 1072 
 1073       /* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */
 1074       o_path_fd = openat (dir_iter.fd, dent->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
 1075       if (o_path_fd == -1 || fstatfs (o_path_fd, &stfs) != 0 || stfs.f_type == AUTOFS_SUPER_MAGIC)
 1076         continue; /* AUTOFS mounts are risky and can cause us to block (see issue #1633), so ignore it. Its unlikely the proxy needs such a directory. */
 1077 
 1078       if (dent->d_type == DT_DIR)
 1079         {
 1080           if (strcmp (dent->d_name, "tmp") == 0 ||
 1081               strcmp (dent->d_name, "var") == 0 ||
 1082               strcmp (dent->d_name, "run") == 0)
 1083             flatpak_bwrap_add_arg (bwrap, "--bind");
 1084           else
 1085             flatpak_bwrap_add_arg (bwrap, "--ro-bind");
 1086 
 1087           flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name);
 1088           flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name);
 1089         }
 1090       else if (dent->d_type == DT_LNK)
 1091         {
 1092           g_autofree gchar *target = NULL;
 1093 
 1094           target = glnx_readlinkat_malloc (dir_iter.fd, dent->d_name,
 1095                                            NULL, error);
 1096           if (target == NULL)
 1097             return FALSE;
 1098           flatpak_bwrap_add_args (bwrap, "--symlink", target, NULL);
 1099           flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name);
 1100         }
 1101     }
 1102 
 1103   flatpak_bwrap_add_args (bwrap, "--bind", proxy_socket_dir, proxy_socket_dir, NULL);
 1104 
 1105   /* This is a file rather than a bind mount, because it will then
 1106      not be unmounted from the namespace when the namespace dies. */
 1107   flatpak_bwrap_add_args_data_fd (bwrap, "--file", glnx_steal_fd (&app_info_fd), "/.flatpak-info");
 1108 
 1109   if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
 1110     return FALSE;
 1111 
 1112   return TRUE;
 1113 }
 1114 
 1115 static gboolean
 1116 start_dbus_proxy (FlatpakBwrap *app_bwrap,
 1117                   FlatpakBwrap *proxy_arg_bwrap,
 1118                   const char   *app_info_path,
 1119                   GError      **error)
 1120 {
 1121   char x = 'x';
 1122   const char *proxy;
 1123   g_autofree char *commandline = NULL;
 1124   g_autoptr(FlatpakBwrap) proxy_bwrap = NULL;
 1125   int sync_fds[2] = {-1, -1};
 1126   int proxy_start_index;
 1127 
 1128   proxy_bwrap = flatpak_bwrap_new (NULL);
 1129 
 1130   if (!add_bwrap_wrapper (proxy_bwrap, app_info_path, error))
 1131     return FALSE;
 1132 
 1133   proxy = g_getenv ("FLATPAK_DBUSPROXY");
 1134   if (proxy == NULL)
 1135     proxy = DBUSPROXY;
 1136 
 1137   flatpak_bwrap_add_arg (proxy_bwrap, proxy);
 1138 
 1139   proxy_start_index = proxy_bwrap->argv->len;
 1140 
 1141   if (pipe2 (sync_fds, O_CLOEXEC) < 0)
 1142     {
 1143       g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
 1144                            _("Unable to create sync pipe"));
 1145       return FALSE;
 1146     }
 1147 
 1148   /* read end goes to app */
 1149   flatpak_bwrap_add_args_data_fd (app_bwrap, "--sync-fd", sync_fds[0], NULL);
 1150 
 1151   /* write end goes to proxy */
 1152   flatpak_bwrap_add_fd (proxy_bwrap, sync_fds[1]);
 1153   flatpak_bwrap_add_arg_printf (proxy_bwrap, "--fd=%d", sync_fds[1]);
 1154 
 1155   /* Note: This steals the fds from proxy_arg_bwrap */
 1156   flatpak_bwrap_append_bwrap (proxy_bwrap, proxy_arg_bwrap);
 1157 
 1158   if (!flatpak_bwrap_bundle_args (proxy_bwrap, proxy_start_index, -1, TRUE, error))
 1159     return FALSE;
 1160 
 1161   flatpak_bwrap_finish (proxy_bwrap);
 1162 
 1163   commandline = flatpak_quote_argv ((const char **) proxy_bwrap->argv->pdata, -1);
 1164   g_debug ("Running '%s'", commandline);
 1165 
 1166   /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
 1167   if (!g_spawn_async (NULL,
 1168                       (char **) proxy_bwrap->argv->pdata,
 1169                       NULL,
 1170                       G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
 1171                       flatpak_bwrap_child_setup_cb, proxy_bwrap->fds,
 1172                       NULL, error))
 1173     return FALSE;
 1174 
 1175   /* The write end can be closed now, otherwise the read below will hang of xdg-dbus-proxy
 1176      fails to start. */
 1177   g_clear_pointer (&proxy_bwrap, flatpak_bwrap_free);
 1178 
 1179   /* Sync with proxy, i.e. wait until its listening on the sockets */
 1180   if (read (sync_fds[0], &x, 1) != 1)
 1181     {
 1182       g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
 1183                            _("Failed to sync with dbus proxy"));
 1184       return FALSE;
 1185     }
 1186 
 1187   return TRUE;
 1188 }
 1189 
 1190 static int
 1191 flatpak_extension_compare_by_path (gconstpointer _a,
 1192                                    gconstpointer _b)
 1193 {
 1194   const FlatpakExtension *a = _a;
 1195   const FlatpakExtension *b = _b;
 1196 
 1197   return g_strcmp0 (a->directory, b->directory);
 1198 }
 1199 
 1200 void
 1201 flatpak_run_extend_ld_path (FlatpakBwrap *bwrap,
 1202                             const char *prepend,
 1203                             const char *append)
 1204 {
 1205   g_autoptr(GString) ld_library_path = g_string_new (g_environ_getenv (bwrap->envp, "LD_LIBRARY_PATH"));
 1206 
 1207   if (prepend != NULL && *prepend != '\0')
 1208     {
 1209       if (ld_library_path->len > 0)
 1210         g_string_prepend (ld_library_path, ":");
 1211 
 1212       g_string_prepend (ld_library_path, prepend);
 1213     }
 1214 
 1215   if (append != NULL && *append != '\0')
 1216     {
 1217       if (ld_library_path->len > 0)
 1218         g_string_append (ld_library_path, ":");
 1219 
 1220       g_string_append (ld_library_path, append);
 1221     }
 1222 
 1223   flatpak_bwrap_set_env (bwrap, "LD_LIBRARY_PATH", ld_library_path->str, TRUE);
 1224 }
 1225 
 1226 gboolean
 1227 flatpak_run_add_extension_args (FlatpakBwrap      *bwrap,
 1228                                 GKeyFile          *metakey,
 1229                                 FlatpakDecomposed *ref,
 1230                                 gboolean           use_ld_so_cache,
 1231                                 const char        *target_path,
 1232                                 char             **extensions_out,
 1233                                 char             **ld_path_out,
 1234                                 GCancellable      *cancellable,
 1235                                 GError           **error)
 1236 {
 1237   g_autoptr(GString) used_extensions = g_string_new ("");
 1238   GList *extensions, *path_sorted_extensions, *l;
 1239   g_autoptr(GString) ld_library_path = g_string_new ("");
 1240   int count = 0;
 1241   g_autoptr(GHashTable) mounted_tmpfs =
 1242     g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 1243   g_autoptr(GHashTable) created_symlink =
 1244     g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 1245   g_autofree char *arch = flatpak_decomposed_dup_arch (ref);
 1246   const char *branch = flatpak_decomposed_get_branch (ref);
 1247 
 1248   g_return_val_if_fail (target_path != NULL, FALSE);
 1249 
 1250   extensions = flatpak_list_extensions (metakey, arch, branch);
 1251 
 1252   /* First we apply all the bindings, they are sorted alphabetically in order for parent directory
 1253      to be mounted before child directories */
 1254   path_sorted_extensions = g_list_copy (extensions);
 1255   path_sorted_extensions = g_list_sort (path_sorted_extensions, flatpak_extension_compare_by_path);
 1256 
 1257   for (l = path_sorted_extensions; l != NULL; l = l->next)
 1258     {
 1259       FlatpakExtension *ext = l->data;
 1260       g_autofree char *directory = g_build_filename (target_path, ext->directory, NULL);
 1261       g_autofree char *full_directory = g_build_filename (directory, ext->subdir_suffix, NULL);
 1262       g_autofree char *ref_file = g_build_filename (full_directory, ".ref", NULL);
 1263       g_autofree char *real_ref = g_build_filename (ext->files_path, ext->directory, ".ref", NULL);
 1264 
 1265       if (ext->needs_tmpfs)
 1266         {
 1267           g_autofree char *parent = g_path_get_dirname (directory);
 1268 
 1269           if (g_hash_table_lookup (mounted_tmpfs, parent) == NULL)
 1270             {
 1271               flatpak_bwrap_add_args (bwrap,
 1272                                       "--tmpfs", parent,
 1273                                       NULL);
 1274               g_hash_table_insert (mounted_tmpfs, g_steal_pointer (&parent), "mounted");
 1275             }
 1276         }
 1277 
 1278       flatpak_bwrap_add_args (bwrap,
 1279                               "--ro-bind", ext->files_path, full_directory,
 1280                               NULL);
 1281 
 1282       if (g_file_test (real_ref, G_FILE_TEST_EXISTS))
 1283         flatpak_bwrap_add_args (bwrap,
 1284                                 "--lock-file", ref_file,
 1285                                 NULL);
 1286     }
 1287 
 1288   g_list_free (path_sorted_extensions);
 1289 
 1290   /* Then apply library directories and file merging, in extension prio order */
 1291 
 1292   for (l = extensions; l != NULL; l = l->next)
 1293     {
 1294       FlatpakExtension *ext = l->data;
 1295       g_autofree char *directory = g_build_filename (target_path, ext->directory, NULL);
 1296       g_autofree char *full_directory = g_build_filename (directory, ext->subdir_suffix, NULL);
 1297       int i;
 1298 
 1299       if (used_extensions->len > 0)
 1300         g_string_append (used_extensions, ";");
 1301       g_string_append (used_extensions, ext->installed_id);
 1302       g_string_append (used_extensions, "=");
 1303       if (ext->commit != NULL)
 1304         g_string_append (used_extensions, ext->commit);
 1305       else
 1306         g_string_append (used_extensions, "local");
 1307 
 1308       if (ext->add_ld_path)
 1309         {
 1310           g_autofree char *ld_path = g_build_filename (full_directory, ext->add_ld_path, NULL);
 1311 
 1312           if (use_ld_so_cache)
 1313             {
 1314               g_autofree char *contents = g_strconcat (ld_path, "\n", NULL);
 1315               /* We prepend app or runtime and a counter in order to get the include order correct for the conf files */
 1316               g_autofree char *ld_so_conf_file = g_strdup_printf ("%s-%03d-%s.conf", flatpak_decomposed_get_kind_str (ref), ++count, ext->installed_id);
 1317               g_autofree char *ld_so_conf_file_path = g_build_filename ("/run/flatpak/ld.so.conf.d", ld_so_conf_file, NULL);
 1318 
 1319               if (!flatpak_bwrap_add_args_data (bwrap, "ld-so-conf",
 1320                                                 contents, -1, ld_so_conf_file_path, error))
 1321                 return FALSE;
 1322             }
 1323           else
 1324             {
 1325               if (ld_library_path->len != 0)
 1326                 g_string_append (ld_library_path, ":");
 1327               g_string_append (ld_library_path, ld_path);
 1328             }
 1329         }
 1330 
 1331       for (i = 0; ext->merge_dirs != NULL && ext->merge_dirs[i] != NULL; i++)
 1332         {
 1333           g_autofree char *parent = g_path_get_dirname (directory);
 1334           g_autofree char *merge_dir = g_build_filename (parent, ext->merge_dirs[i], NULL);
 1335           g_autofree char *source_dir = g_build_filename (ext->files_path, ext->merge_dirs[i], NULL);
 1336           g_auto(GLnxDirFdIterator) source_iter = { 0 };
 1337           struct dirent *dent;
 1338 
 1339           if (glnx_dirfd_iterator_init_at (AT_FDCWD, source_dir, TRUE, &source_iter, NULL))
 1340             {
 1341               while (glnx_dirfd_iterator_next_dent (&source_iter, &dent, NULL, NULL) && dent != NULL)
 1342                 {
 1343                   g_autofree char *symlink_path = g_build_filename (merge_dir, dent->d_name, NULL);
 1344                   /* Only create the first, because extensions are listed in prio order */
 1345                   if (g_hash_table_lookup (created_symlink, symlink_path) == NULL)
 1346                     {
 1347                       g_autofree char *symlink = g_build_filename (directory, ext->merge_dirs[i], dent->d_name, NULL);
 1348                       flatpak_bwrap_add_args (bwrap,
 1349                                               "--symlink", symlink, symlink_path,
 1350                                               NULL);
 1351                       g_hash_table_insert (created_symlink, g_steal_pointer (&symlink_path), "created");
 1352                     }
 1353                 }
 1354             }
 1355         }
 1356     }
 1357 
 1358   g_list_free_full (extensions, (GDestroyNotify) flatpak_extension_free);
 1359 
 1360   if (extensions_out)
 1361     *extensions_out = g_string_free (g_steal_pointer (&used_extensions), FALSE);
 1362 
 1363   if (ld_path_out)
 1364     *ld_path_out = g_string_free (g_steal_pointer (&ld_library_path), FALSE);
 1365 
 1366   return TRUE;
 1367 }
 1368 
 1369 /*
 1370  * @per_app_dir_lock_fd: If >= 0, make use of per-app directories in
 1371  *  the host's XDG_RUNTIME_DIR to share /tmp between instances.
 1372  */
 1373 gboolean
 1374 flatpak_run_add_environment_args (FlatpakBwrap    *bwrap,
 1375                                   const char      *app_info_path,
 1376                                   FlatpakRunFlags  flags,
 1377                                   const char      *app_id,
 1378                                   FlatpakContext  *context,
 1379                                   GFile           *app_id_dir,
 1380                                   GPtrArray       *previous_app_id_dirs,
 1381                                   int              per_app_dir_lock_fd,
 1382                                   FlatpakExports **exports_out,
 1383                                   GCancellable    *cancellable,
 1384                                   GError         **error)
 1385 {
 1386   g_autoptr(GError) my_error = NULL;
 1387   g_autoptr(FlatpakExports) exports = NULL;
 1388   g_autoptr(FlatpakBwrap) proxy_arg_bwrap = flatpak_bwrap_new (flatpak_bwrap_empty_env);
 1389   g_autofree char *xdg_dirs_conf = NULL;
 1390   gboolean has_wayland = FALSE;
 1391   gboolean allow_x11 = FALSE;
 1392   gboolean home_access = FALSE;
 1393   gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0;
 1394 
 1395   if ((context->shares & FLATPAK_CONTEXT_SHARED_IPC) == 0)
 1396     {
 1397       g_debug ("Disallowing ipc access");
 1398       flatpak_bwrap_add_args (bwrap, "--unshare-ipc", NULL);
 1399     }
 1400 
 1401   if ((context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0)
 1402     {
 1403       g_debug ("Disallowing network access");
 1404       flatpak_bwrap_add_args (bwrap, "--unshare-net", NULL);
 1405     }
 1406 
 1407   if (context->devices & FLATPAK_CONTEXT_DEVICE_ALL)
 1408     {
 1409       flatpak_bwrap_add_args (bwrap,
 1410                               "--dev-bind", "/dev", "/dev",
 1411                               NULL);
 1412       /* Don't expose the host /dev/shm, just the device nodes, unless explicitly allowed */
 1413       if (g_file_test ("/dev/shm", G_FILE_TEST_IS_DIR))
 1414         {
 1415           if (context->devices & FLATPAK_CONTEXT_DEVICE_SHM)
 1416             {
 1417               /* Don't do anything special: include shm in the
 1418                * shared /dev. The host and all sandboxes and subsandboxes
 1419                * all share /dev/shm */
 1420             }
 1421           else if ((context->features & FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM)
 1422                    && per_app_dir_lock_fd >= 0)
 1423             {
 1424               g_autofree char *shared_dev_shm = NULL;
 1425 
 1426               /* The host and the original sandbox have separate /dev/shm,
 1427                * but we want other instances to be able to share /dev/shm with
 1428                * the first sandbox (except for subsandboxes run with
 1429                * flatpak-spawn --sandbox, which will have their own). */
 1430               if (!flatpak_instance_ensure_per_app_dev_shm (app_id,
 1431                                                             per_app_dir_lock_fd,
 1432                                                             &shared_dev_shm,
 1433                                                             error))
 1434                 return FALSE;
 1435 
 1436               flatpak_bwrap_add_args (bwrap,
 1437                                       "--bind", shared_dev_shm, "/dev/shm",
 1438                                       NULL);
 1439             }
 1440           else
 1441             {
 1442               /* The host, the original sandbox and each subsandbox
 1443                * each have a separate /dev/shm. */
 1444               flatpak_bwrap_add_args (bwrap,
 1445                                       "--tmpfs", "/dev/shm",
 1446                                       NULL);
 1447             }
 1448         }
 1449       else if (g_file_test ("/dev/shm", G_FILE_TEST_IS_SYMLINK))
 1450         {
 1451           g_autofree char *link = flatpak_readlink ("/dev/shm", NULL);
 1452 
 1453           /* On debian (with sysv init) the host /dev/shm is a symlink to /run/shm, so we can't
 1454              mount on top of it. */
 1455           if (g_strcmp0 (link, "/run/shm") == 0)
 1456             {
 1457               if (context->devices & FLATPAK_CONTEXT_DEVICE_SHM &&
 1458                   g_file_test ("/run/shm", G_FILE_TEST_IS_DIR))
 1459                 {
 1460                   flatpak_bwrap_add_args (bwrap,
 1461                                           "--bind", "/run/shm", "/run/shm",
 1462                                           NULL);
 1463                 }
 1464               else if ((context->features & FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM)
 1465                        && per_app_dir_lock_fd >= 0)
 1466                 {
 1467                   g_autofree char *shared_dev_shm = NULL;
 1468 
 1469                   /* The host and the original sandbox have separate /dev/shm,
 1470                    * but we want other instances to be able to share /dev/shm,
 1471                    * except for flatpak-spawn --subsandbox. */
 1472                   if (!flatpak_instance_ensure_per_app_dev_shm (app_id,
 1473                                                                 per_app_dir_lock_fd,
 1474                                                                 &shared_dev_shm,
 1475                                                                 error))
 1476                     return FALSE;
 1477 
 1478                   flatpak_bwrap_add_args (bwrap,
 1479                                           "--bind", shared_dev_shm, "/run/shm",
 1480                                           NULL);
 1481                 }
 1482               else
 1483                 {
 1484                   flatpak_bwrap_add_args (bwrap,
 1485                                           "--dir", "/run/shm",
 1486                                           NULL);
 1487                 }
 1488             }
 1489           else
 1490             g_warning ("Unexpected /dev/shm symlink %s", link);
 1491         }
 1492     }
 1493   else
 1494     {
 1495       flatpak_bwrap_add_args (bwrap,
 1496                               "--dev", "/dev",
 1497                               NULL);
 1498       if (context->devices & FLATPAK_CONTEXT_DEVICE_DRI)
 1499         {
 1500           g_debug ("Allowing dri access");
 1501           int i;
 1502           char *dri_devices[] = {
 1503             "/dev/dri",
 1504             /* mali */
 1505             "/dev/mali",
 1506             "/dev/mali0",
 1507             "/dev/umplock",
 1508             /* nvidia */
 1509             "/dev/nvidiactl",
 1510             "/dev/nvidia-modeset",
 1511             /* nvidia OpenCL/CUDA */
 1512             "/dev/nvidia-uvm",
 1513             "/dev/nvidia-uvm-tools",
 1514           };
 1515 
 1516           for (i = 0; i < G_N_ELEMENTS (dri_devices); i++)
 1517             {
 1518               if (g_file_test (dri_devices[i], G_FILE_TEST_EXISTS))
 1519                 flatpak_bwrap_add_args (bwrap, "--dev-bind", dri_devices[i], dri_devices[i], NULL);
 1520             }
 1521 
 1522           /* Each Nvidia card gets its own device.
 1523              This is a fairly arbitrary limit but ASUS sells mining boards supporting 20 in theory. */
 1524           char nvidia_dev[14]; /* /dev/nvidia plus up to 2 digits */
 1525           for (i = 0; i < 20; i++)
 1526             {
 1527               g_snprintf (nvidia_dev, sizeof (nvidia_dev), "/dev/nvidia%d", i);
 1528               if (g_file_test (nvidia_dev, G_FILE_TEST_EXISTS))
 1529                 flatpak_bwrap_add_args (bwrap, "--dev-bind", nvidia_dev, nvidia_dev, NULL);
 1530             }
 1531         }
 1532 
 1533       if (context->devices & FLATPAK_CONTEXT_DEVICE_KVM)
 1534         {
 1535           g_debug ("Allowing kvm access");
 1536           if (g_file_test ("/dev/kvm", G_FILE_TEST_EXISTS))
 1537             flatpak_bwrap_add_args (bwrap, "--dev-bind", "/dev/kvm", "/dev/kvm", NULL);
 1538         }
 1539 
 1540       if (context->devices & FLATPAK_CONTEXT_DEVICE_SHM)
 1541         {
 1542           /* This is a symlink to /run/shm on debian, so bind to real target */
 1543           g_autofree char *real_dev_shm = realpath ("/dev/shm", NULL);
 1544 
 1545           g_debug ("Allowing /dev/shm access (as %s)", real_dev_shm);
 1546           if (real_dev_shm != NULL)
 1547               flatpak_bwrap_add_args (bwrap, "--bind", real_dev_shm, "/dev/shm", NULL);
 1548         }
 1549       else if ((context->features & FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM)
 1550                && per_app_dir_lock_fd >= 0)
 1551         {
 1552           g_autofree char *shared_dev_shm = NULL;
 1553 
 1554           if (!flatpak_instance_ensure_per_app_dev_shm (app_id,
 1555                                                         per_app_dir_lock_fd,
 1556                                                         &shared_dev_shm,
 1557                                                         error))
 1558             return FALSE;
 1559 
 1560           flatpak_bwrap_add_args (bwrap,
 1561                                   "--bind", shared_dev_shm, "/dev/shm",
 1562                                   NULL);
 1563         }
 1564     }
 1565 
 1566   exports = flatpak_context_get_exports_full (context,
 1567                                               app_id_dir, previous_app_id_dirs,
 1568                                               TRUE, TRUE,
 1569                                               &xdg_dirs_conf, &home_access);
 1570 
 1571   if (flatpak_exports_path_is_visible (exports, "/tmp"))
 1572     {
 1573       /* The original sandbox and any subsandboxes are both already
 1574        * going to share /tmp with the host, so by transitivity they will
 1575        * also share it with each other, and with all other instances. */
 1576     }
 1577   else if (per_app_dir_lock_fd >= 0 && !sandboxed)
 1578     {
 1579       g_autofree char *shared_tmp = NULL;
 1580 
 1581       /* The host and the original sandbox have separate /tmp,
 1582        * but we want other instances to be able to share /tmp with the
 1583        * first sandbox, unless they were created by
 1584        * flatpak-spawn --sandbox.
 1585        *
 1586        * In apply_extra and `flatpak build`, per_app_dir_lock_fd is
 1587        * negative and we skip this. */
 1588       if (!flatpak_instance_ensure_per_app_tmp (app_id,
 1589                                                 per_app_dir_lock_fd,
 1590                                                 &shared_tmp,
 1591                                                 error))
 1592         return FALSE;
 1593 
 1594       flatpak_bwrap_add_args (bwrap,
 1595                               "--bind", shared_tmp, "/tmp",
 1596                               NULL);
 1597     }
 1598 
 1599   flatpak_context_append_bwrap_filesystem (context, bwrap, app_id, app_id_dir,
 1600                                            exports, xdg_dirs_conf, home_access);
 1601 
 1602   if (context->sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND)
 1603     {
 1604       g_debug ("Allowing wayland access");
 1605       has_wayland = flatpak_run_add_wayland_args (bwrap);
 1606     }
 1607 
 1608   if ((context->sockets & FLATPAK_CONTEXT_SOCKET_FALLBACK_X11) != 0)
 1609     allow_x11 = !has_wayland;
 1610   else
 1611     allow_x11 = (context->sockets & FLATPAK_CONTEXT_SOCKET_X11) != 0;
 1612 
 1613   flatpak_run_add_x11_args (bwrap, allow_x11);
 1614 
 1615   if (context->sockets & FLATPAK_CONTEXT_SOCKET_SSH_AUTH)
 1616     {
 1617       flatpak_run_add_ssh_args (bwrap);
 1618     }
 1619 
 1620   if (context->sockets & FLATPAK_CONTEXT_SOCKET_PULSEAUDIO)
 1621     {
 1622       g_debug ("Allowing pulseaudio access");
 1623       flatpak_run_add_pulseaudio_args (bwrap);
 1624     }
 1625 
 1626   if (context->sockets & FLATPAK_CONTEXT_SOCKET_PCSC)
 1627     {
 1628       flatpak_run_add_pcsc_args (bwrap);
 1629     }
 1630 
 1631   if (context->sockets & FLATPAK_CONTEXT_SOCKET_CUPS)
 1632     {
 1633       flatpak_run_add_cups_args (bwrap);
 1634     }
 1635 
 1636   flatpak_run_add_session_dbus_args (bwrap, proxy_arg_bwrap, context, flags, app_id);
 1637   flatpak_run_add_system_dbus_args (bwrap, proxy_arg_bwrap, context, flags);
 1638   flatpak_run_add_a11y_dbus_args (bwrap, proxy_arg_bwrap, context, flags);
 1639 
 1640   /* Must run this before spawning the dbus proxy, to ensure it
 1641      ends up in the app cgroup */
 1642   if (!flatpak_run_in_transient_unit (app_id, &my_error))
 1643     {
 1644       /* We still run along even if we don't get a cgroup, as nothing
 1645          really depends on it. Its just nice to have */
 1646       g_debug ("Failed to run in transient scope: %s", my_error->message);
 1647       g_clear_error (&my_error);
 1648     }
 1649 
 1650   if (!flatpak_bwrap_is_empty (proxy_arg_bwrap) &&
 1651       !start_dbus_proxy (bwrap, proxy_arg_bwrap, app_info_path, error))
 1652     return FALSE;
 1653 
 1654   if (exports_out)
 1655     *exports_out = g_steal_pointer (&exports);
 1656 
 1657   return TRUE;
 1658 }
 1659 
 1660 typedef struct
 1661 {
 1662   const char *env;
 1663   const char *val;
 1664 } ExportData;
 1665 
 1666 static const ExportData default_exports[] = {
 1667   {"PATH", "/app/bin:/usr/bin"},
 1668   /* We always want to unset LD_LIBRARY_PATH to avoid inheriting weird
 1669    * dependencies from the host. But if not using ld.so.cache this is
 1670    * later set. */
 1671   {"LD_LIBRARY_PATH", NULL},
 1672   {"XDG_CONFIG_DIRS", "/app/etc/xdg:/etc/xdg"},
 1673   {"XDG_DATA_DIRS", "/app/share:/usr/share"},
 1674   {"SHELL", "/bin/sh"},
 1675   {"TMPDIR", NULL}, /* Unset TMPDIR as it may not exist in the sandbox */
 1676   /* We always use /run/user/UID, even if the user's XDG_RUNTIME_DIR
 1677    * outside the sandbox is somewhere else. Don't allow a different
 1678    * setting from outside the sandbox to overwrite this. */
 1679   {"XDG_RUNTIME_DIR", NULL},
 1680 
 1681   /* Some env vars are common enough and will affect the sandbox badly
 1682      if set on the host. We clear these always. */
 1683   {"PYTHONPATH", NULL},
 1684   {"PERLLIB", NULL},
 1685   {"PERL5LIB", NULL},
 1686   {"XCURSOR_PATH", NULL},
 1687 };
 1688 
 1689 static const ExportData no_ld_so_cache_exports[] = {
 1690   {"LD_LIBRARY_PATH", "/app/lib"},
 1691 };
 1692 
 1693 static const ExportData devel_exports[] = {
 1694   {"ACLOCAL_PATH", "/app/share/aclocal"},
 1695   {"C_INCLUDE_PATH", "/app/include"},
 1696   {"CPLUS_INCLUDE_PATH", "/app/include"},
 1697   {"LDFLAGS", "-L/app/lib "},
 1698   {"PKG_CONFIG_PATH", "/app/lib/pkgconfig:/app/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig"},
 1699   {"LC_ALL", "en_US.utf8"},
 1700 };
 1701 
 1702 static void
 1703 add_exports (GPtrArray        *env_array,
 1704              const ExportData *exports,
 1705              gsize             n_exports)
 1706 {
 1707   int i;
 1708 
 1709   for (i = 0; i < n_exports; i++)
 1710     {
 1711       if (exports[i].val)
 1712         g_ptr_array_add (env_array, g_strdup_printf ("%s=%s", exports[i].env, exports[i].val));
 1713     }
 1714 }
 1715 
 1716 char **
 1717 flatpak_run_get_minimal_env (gboolean devel, gboolean use_ld_so_cache)
 1718 {
 1719   GPtrArray *env_array;
 1720   static const char * const copy[] = {
 1721     "PWD",
 1722     "GDMSESSION",
 1723     "XDG_CURRENT_DESKTOP",
 1724     "XDG_SESSION_DESKTOP",
 1725     "DESKTOP_SESSION",
 1726     "EMAIL_ADDRESS",
 1727     "HOME",
 1728     "HOSTNAME",
 1729     "LOGNAME",
 1730     "REAL_NAME",
 1731     "TERM",
 1732     "USER",
 1733     "USERNAME",
 1734   };
 1735   static const char * const copy_nodevel[] = {
 1736     "LANG",
 1737     "LANGUAGE",
 1738     "LC_ALL",
 1739     "LC_ADDRESS",
 1740     "LC_COLLATE",
 1741     "LC_CTYPE",
 1742     "LC_IDENTIFICATION",
 1743     "LC_MEASUREMENT",
 1744     "LC_MESSAGES",
 1745     "LC_MONETARY",
 1746     "LC_NAME",
 1747     "LC_NUMERIC",
 1748     "LC_PAPER",
 1749     "LC_TELEPHONE",
 1750     "LC_TIME",
 1751   };
 1752   int i;
 1753 
 1754   env_array = g_ptr_array_new_with_free_func (g_free);
 1755 
 1756   add_exports (env_array, default_exports, G_N_ELEMENTS (default_exports));
 1757 
 1758   if (!use_ld_so_cache)
 1759     add_exports (env_array, no_ld_so_cache_exports, G_N_ELEMENTS (no_ld_so_cache_exports));
 1760 
 1761   if (devel)
 1762     add_exports (env_array, devel_exports, G_N_ELEMENTS (devel_exports));
 1763 
 1764   for (i = 0; i < G_N_ELEMENTS (copy); i++)
 1765     {
 1766       const char *current = g_getenv (copy[i]);
 1767       if (current)
 1768         g_ptr_array_add (env_array, g_strdup_printf ("%s=%s", copy[i], current));
 1769     }
 1770 
 1771   if (!devel)
 1772     {
 1773       for (i = 0; i < G_N_ELEMENTS (copy_nodevel); i++)
 1774         {
 1775           const char *current = g_getenv (copy_nodevel[i]);
 1776           if (current)
 1777             g_ptr_array_add (env_array, g_strdup_printf ("%s=%s", copy_nodevel[i], current));
 1778         }
 1779     }
 1780 
 1781   g_ptr_array_add (env_array, NULL);
 1782   return (char **) g_ptr_array_free (env_array, FALSE);
 1783 }
 1784 
 1785 static char **
 1786 apply_exports (char            **envp,
 1787                const ExportData *exports,
 1788                gsize             n_exports)
 1789 {
 1790   int i;
 1791 
 1792   for (i = 0; i < n_exports; i++)
 1793     {
 1794       const char *value = exports[i].val;
 1795 
 1796       if (value)
 1797         envp = g_environ_setenv (envp, exports[i].env, value, TRUE);
 1798       else
 1799         envp = g_environ_unsetenv (envp, exports[i].env);
 1800     }
 1801 
 1802   return envp;
 1803 }
 1804 
 1805 void
 1806 flatpak_run_apply_env_default (FlatpakBwrap *bwrap, gboolean use_ld_so_cache)
 1807 {
 1808   bwrap->envp = apply_exports (bwrap->envp, default_exports, G_N_ELEMENTS (default_exports));
 1809 
 1810   if (!use_ld_so_cache)
 1811     bwrap->envp = apply_exports (bwrap->envp, no_ld_so_cache_exports, G_N_ELEMENTS (no_ld_so_cache_exports));
 1812 }
 1813 
 1814 static void
 1815 flatpak_run_apply_env_prompt (FlatpakBwrap *bwrap, const char *app_id)
 1816 {
 1817   /* A custom shell prompt. FLATPAK_ID is always set.
 1818    * PS1 can be overwritten by runtime metadata or by --env overrides
 1819    */
 1820   flatpak_bwrap_set_env (bwrap, "FLATPAK_ID", app_id, TRUE);
 1821   flatpak_bwrap_set_env (bwrap, "PS1", "[📦 $FLATPAK_ID \\W]\\$ ", FALSE);
 1822 }
 1823 
 1824 void
 1825 flatpak_run_apply_env_appid (FlatpakBwrap *bwrap,
 1826                              GFile        *app_dir)
 1827 {
 1828   g_autoptr(GFile) app_dir_data = NULL;
 1829   g_autoptr(GFile) app_dir_config = NULL;
 1830   g_autoptr(GFile) app_dir_cache = NULL;
 1831 
 1832   app_dir_data = g_file_get_child (app_dir, "data");
 1833   app_dir_config = g_file_get_child (app_dir, "config");
 1834   app_dir_cache = g_file_get_child (app_dir, "cache");
 1835   flatpak_bwrap_set_env (bwrap, "XDG_DATA_HOME", flatpak_file_get_path_cached (app_dir_data), TRUE);
 1836   flatpak_bwrap_set_env (bwrap, "XDG_CONFIG_HOME", flatpak_file_get_path_cached (app_dir_config), TRUE);
 1837   flatpak_bwrap_set_env (bwrap, "XDG_CACHE_HOME", flatpak_file_get_path_cached (app_dir_cache), TRUE);
 1838 
 1839   if (g_getenv ("XDG_DATA_HOME"))
 1840     flatpak_bwrap_set_env (bwrap, "HOST_XDG_DATA_HOME", g_getenv ("XDG_DATA_HOME"), TRUE);
 1841   if (g_getenv ("XDG_CONFIG_HOME"))
 1842     flatpak_bwrap_set_env (bwrap, "HOST_XDG_CONFIG_HOME", g_getenv ("XDG_CONFIG_HOME"), TRUE);
 1843   if (g_getenv ("XDG_CACHE_HOME"))
 1844     flatpak_bwrap_set_env (bwrap, "HOST_XDG_CACHE_HOME", g_getenv ("XDG_CACHE_HOME"), TRUE);
 1845 }
 1846 
 1847 void
 1848 flatpak_run_apply_env_vars (FlatpakBwrap *bwrap, FlatpakContext *context)
 1849 {
 1850   GHashTableIter iter;
 1851   gpointer key, value;
 1852 
 1853   g_hash_table_iter_init (&iter, context->env_vars);
 1854   while (g_hash_table_iter_next (&iter, &key, &value))
 1855     {
 1856       const char *var = key;
 1857       const char *val = value;
 1858 
 1859       if (val)
 1860         flatpak_bwrap_set_env (bwrap, var, val, TRUE);
 1861       else
 1862         flatpak_bwrap_unset_env (bwrap, var);
 1863     }
 1864 }
 1865 
 1866 GFile *
 1867 flatpak_get_data_dir (const char *app_id)
 1868 {
 1869   g_autoptr(GFile) home = g_file_new_for_path (g_get_home_dir ());
 1870   g_autoptr(GFile) var_app = g_file_resolve_relative_path (home, ".var/app");
 1871 
 1872   return g_file_get_child (var_app, app_id);
 1873 }
 1874 
 1875 gboolean
 1876 flatpak_ensure_data_dir (GFile        *app_id_dir,
 1877                          GCancellable *cancellable,
 1878                          GError      **error)
 1879 {
 1880   g_autoptr(GFile) data_dir = g_file_get_child (app_id_dir, "data");
 1881   g_autoptr(GFile) cache_dir = g_file_get_child (app_id_dir, "cache");
 1882   g_autoptr(GFile) fontconfig_cache_dir = g_file_get_child (cache_dir, "fontconfig");
 1883   g_autoptr(GFile) tmp_dir = g_file_get_child (cache_dir, "tmp");
 1884   g_autoptr(GFile) config_dir = g_file_get_child (app_id_dir, "config");
 1885 
 1886   if (!flatpak_mkdir_p (data_dir, cancellable, error))
 1887     return FALSE;
 1888 
 1889   if (!flatpak_mkdir_p (cache_dir, cancellable, error))
 1890     return FALSE;
 1891 
 1892   if (!flatpak_mkdir_p (fontconfig_cache_dir, cancellable, error))
 1893     return FALSE;
 1894 
 1895   if (!flatpak_mkdir_p (tmp_dir, cancellable, error))
 1896     return FALSE;
 1897 
 1898   if (!flatpak_mkdir_p (config_dir, cancellable, error))
 1899     return FALSE;
 1900 
 1901   return TRUE;
 1902 }
 1903 
 1904 struct JobData
 1905 {
 1906   char      *job;
 1907   GMainLoop *main_loop;
 1908 };
 1909 
 1910 static void
 1911 job_removed_cb (SystemdManager *manager,
 1912                 guint32         id,
 1913                 char           *job,
 1914                 char           *unit,
 1915                 char           *result,
 1916                 struct JobData *data)
 1917 {
 1918   if (strcmp (job, data->job) == 0)
 1919     g_main_loop_quit (data->main_loop);
 1920 }
 1921 
 1922 static gchar *
 1923 systemd_unit_name_escape (const gchar *in)
 1924 {
 1925   /* Adapted from systemd source */
 1926   GString * const str = g_string_sized_new (strlen (in));
 1927 
 1928   for (; *in; in++)
 1929     {
 1930       if (g_ascii_isalnum (*in) || *in == ':' || *in == '_' || *in == '.')
 1931         g_string_append_c (str, *in);
 1932       else
 1933         g_string_append_printf (str, "\\x%02x", *in);
 1934     }
 1935   return g_string_free (str, FALSE);
 1936 }
 1937 
 1938 gboolean
 1939 flatpak_run_in_transient_unit (const char *appid, GError **error)
 1940 {
 1941   g_autoptr(GDBusConnection) conn = NULL;
 1942   g_autofree char *path = NULL;
 1943   g_autofree char *address = NULL;
 1944   g_autofree char *name = NULL;
 1945   g_autofree char *appid_escaped = NULL;
 1946   g_autofree char *job = NULL;
 1947   SystemdManager *manager = NULL;
 1948   GVariantBuilder builder;
 1949   GVariant *properties = NULL;
 1950   GVariant *aux = NULL;
 1951   guint32 pid;
 1952   GMainLoop *main_loop = NULL;
 1953   struct JobData data;
 1954   gboolean res = FALSE;
 1955   g_autoptr(GMainContextPopDefault) main_context = NULL;
 1956 
 1957   path = g_strdup_printf ("/run/user/%d/systemd/private", getuid ());
 1958 
 1959   if (!g_file_test (path, G_FILE_TEST_EXISTS))
 1960     return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED,
 1961                                _("No systemd user session available, cgroups not available"));
 1962 
 1963   main_context = flatpak_main_context_new_default ();
 1964   main_loop = g_main_loop_new (main_context, FALSE);
 1965 
 1966   address = g_strconcat ("unix:path=", path, NULL);
 1967 
 1968   conn = g_dbus_connection_new_for_address_sync (address,
 1969                                                  G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
 1970                                                  NULL,
 1971                                                  NULL, error);
 1972   if (!conn)
 1973     goto out;
 1974 
 1975   manager = systemd_manager_proxy_new_sync (conn,
 1976                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
 1977                                             NULL,
 1978                                             "/org/freedesktop/systemd1",
 1979                                             NULL, error);
 1980   if (!manager)
 1981     goto out;
 1982 
 1983   appid_escaped = systemd_unit_name_escape (appid);
 1984   name = g_strdup_printf ("app-flatpak-%s-%d.scope", appid_escaped, getpid ());
 1985 
 1986   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sv)"));
 1987 
 1988   pid = getpid ();
 1989   g_variant_builder_add (&builder, "(sv)",
 1990                          "PIDs",
 1991                          g_variant_new_fixed_array (G_VARIANT_TYPE ("u"),
 1992                                                     &pid, 1, sizeof (guint32))
 1993                         );
 1994 
 1995   properties = g_variant_builder_end (&builder);
 1996 
 1997   aux = g_variant_new_array (G_VARIANT_TYPE ("(sa(sv))"), NULL, 0);
 1998 
 1999   if (!systemd_manager_call_start_transient_unit_sync (manager,
 2000                                                        name,
 2001                                                        "fail",
 2002                                                        properties,
 2003                                                        aux,
 2004                                                        &job,
 2005                                                        NULL,
 2006                                                        error))
 2007     goto out;
 2008 
 2009   data.job = job;
 2010   data.main_loop = main_loop;
 2011   g_signal_connect (manager, "job-removed", G_CALLBACK (job_removed_cb), &data);
 2012 
 2013   g_main_loop_run (main_loop);
 2014 
 2015   res = TRUE;
 2016 
 2017 out:
 2018   if (main_loop)
 2019     g_main_loop_unref (main_loop);
 2020   if (manager)
 2021     g_object_unref (manager);
 2022 
 2023   return res;
 2024 }
 2025 
 2026 static void
 2027 add_font_path_args (FlatpakBwrap *bwrap)
 2028 {
 2029   g_autoptr(GString) xml_snippet = g_string_new ("");
 2030   gchar *path_build_tmp = NULL;
 2031   g_autoptr(GFile) user_font1 = NULL;
 2032   g_autoptr(GFile) user_font2 = NULL;
 2033   g_autoptr(GFile) user_font_cache = NULL;
 2034   g_auto(GStrv) system_cache_dirs = NULL;
 2035   gboolean found_cache = FALSE;
 2036   int i;
 2037 
 2038 
 2039   g_string_append (xml_snippet,
 2040                    "<?xml version=\"1.0\"?>\n"
 2041                    "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n"
 2042                    "<fontconfig>\n");
 2043 
 2044   if (g_file_test (SYSTEM_FONTS_DIR, G_FILE_TEST_EXISTS))
 2045     {
 2046       flatpak_bwrap_add_args (bwrap,
 2047                               "--ro-bind", SYSTEM_FONTS_DIR, "/run/host/fonts",
 2048                               NULL);
 2049       g_string_append_printf (xml_snippet,
 2050                               "\t<remap-dir as-path=\"%s\">/run/host/fonts</remap-dir>\n",
 2051                               SYSTEM_FONTS_DIR);
 2052     }
 2053 
 2054   if (g_file_test ("/usr/local/share/fonts", G_FILE_TEST_EXISTS))
 2055     {
 2056       flatpak_bwrap_add_args (bwrap,
 2057                               "--ro-bind", "/usr/local/share/fonts", "/run/host/local-fonts",
 2058                               NULL);
 2059       g_string_append_printf (xml_snippet,
 2060                               "\t<remap-dir as-path=\"%s\">/run/host/local-fonts</remap-dir>\n",
 2061                               "/usr/local/share/fonts");
 2062     }
 2063 
 2064   system_cache_dirs = g_strsplit (SYSTEM_FONT_CACHE_DIRS, ":", 0);
 2065   for (i = 0; system_cache_dirs[i] != NULL; i++)
 2066     {
 2067       if (g_file_test (system_cache_dirs[i], G_FILE_TEST_EXISTS))
 2068         {
 2069           flatpak_bwrap_add_args (bwrap,
 2070                                   "--ro-bind", system_cache_dirs[i], "/run/host/fonts-cache",
 2071                                   NULL);
 2072           found_cache = TRUE;
 2073           break;
 2074         }
 2075     }
 2076 
 2077   if (!found_cache)
 2078     {
 2079       /* We ensure these directories are never writable, or fontconfig
 2080          will use them to write the default cache */
 2081       flatpak_bwrap_add_args (bwrap,
 2082                               "--tmpfs", "/run/host/fonts-cache",
 2083                               "--remount-ro", "/run/host/fonts-cache",
 2084                               NULL);
 2085     }
 2086 
 2087   path_build_tmp = g_build_filename (g_get_user_data_dir (), "fonts", NULL);
 2088   user_font1 = g_file_new_for_path (path_build_tmp);
 2089   g_clear_pointer (&path_build_tmp, g_free);
 2090 
 2091   path_build_tmp = g_build_filename (g_get_home_dir (), ".fonts", NULL);
 2092   user_font2 = g_file_new_for_path (path_build_tmp);
 2093   g_clear_pointer (&path_build_tmp, g_free);
 2094 
 2095   if (g_file_query_exists (user_font1, NULL))
 2096     {
 2097       flatpak_bwrap_add_args (bwrap,
 2098                               "--ro-bind", flatpak_file_get_path_cached (user_font1), "/run/host/user-fonts",
 2099                               NULL);
 2100       g_string_append_printf (xml_snippet,
 2101                               "\t<remap-dir as-path=\"%s\">/run/host/user-fonts</remap-dir>\n",
 2102                               flatpak_file_get_path_cached (user_font1));
 2103     }
 2104   else if (g_file_query_exists (user_font2, NULL))
 2105     {
 2106       flatpak_bwrap_add_args (bwrap,
 2107                               "--ro-bind", flatpak_file_get_path_cached (user_font2), "/run/host/user-fonts",
 2108                               NULL);
 2109       g_string_append_printf (xml_snippet,
 2110                               "\t<remap-dir as-path=\"%s\">/run/host/user-fonts</remap-dir>\n",
 2111                               flatpak_file_get_path_cached (user_font2));
 2112     }
 2113 
 2114   path_build_tmp = g_build_filename (g_get_user_cache_dir (), "fontconfig", NULL);
 2115   user_font_cache = g_file_new_for_path (path_build_tmp);
 2116   g_clear_pointer (&path_build_tmp, g_free);
 2117 
 2118   if (g_file_query_exists (user_font_cache, NULL))
 2119     {
 2120       flatpak_bwrap_add_args (bwrap,
 2121                               "--ro-bind", flatpak_file_get_path_cached (user_font_cache), "/run/host/user-fonts-cache",
 2122                               NULL);
 2123     }
 2124   else
 2125     {
 2126       /* We ensure these directories are never writable, or fontconfig
 2127          will use them to write the default cache */
 2128       flatpak_bwrap_add_args (bwrap,
 2129                               "--tmpfs", "/run/host/user-fonts-cache",
 2130                               "--remount-ro", "/run/host/user-fonts-cache",
 2131                               NULL);
 2132     }
 2133 
 2134   g_string_append (xml_snippet,
 2135                    "</fontconfig>\n");
 2136 
 2137   if (!flatpak_bwrap_add_args_data (bwrap, "font-dirs.xml", xml_snippet->str, xml_snippet->len, "/run/host/font-dirs.xml", NULL))
 2138     g_warning ("Unable to add fontconfig data snippet");
 2139 }
 2140 
 2141 static void
 2142 add_icon_path_args (FlatpakBwrap *bwrap)
 2143 {
 2144   g_autofree gchar *user_icons_path = NULL;
 2145   g_autoptr(GFile) user_icons = NULL;
 2146 
 2147   if (g_file_test ("/usr/share/icons", G_FILE_TEST_IS_DIR))
 2148     {
 2149       flatpak_bwrap_add_args (bwrap,
 2150                               "--ro-bind", "/usr/share/icons", "/run/host/share/icons",
 2151                               NULL);
 2152     }
 2153 
 2154   user_icons_path = g_build_filename (g_get_user_data_dir (), "icons", NULL);
 2155   user_icons = g_file_new_for_path (user_icons_path);
 2156   if (g_file_query_exists (user_icons, NULL))
 2157     {
 2158       flatpak_bwrap_add_args (bwrap,
 2159                               "--ro-bind", flatpak_file_get_path_cached (user_icons), "/run/host/user-share/icons",
 2160                               NULL);
 2161     }
 2162 }
 2163 
 2164 FlatpakContext *
 2165 flatpak_app_compute_permissions (GKeyFile *app_metadata,
 2166                                  GKeyFile *runtime_metadata,
 2167                                  GError  **error)
 2168 {
 2169   g_autoptr(FlatpakContext) app_context = NULL;
 2170 
 2171   app_context = flatpak_context_new ();
 2172 
 2173   if (runtime_metadata != NULL)
 2174     {
 2175       if (!flatpak_context_load_metadata (app_context, runtime_metadata, error))
 2176         return NULL;
 2177 
 2178       /* Don't inherit any permissions from the runtime, only things like env vars. */
 2179       flatpak_context_reset_permissions (app_context);
 2180     }
 2181 
 2182   if (app_metadata != NULL &&
 2183       !flatpak_context_load_metadata (app_context, app_metadata, error))
 2184     return NULL;
 2185 
 2186   return g_steal_pointer (&app_context);
 2187 }
 2188 
 2189 #ifdef HAVE_DCONF
 2190 
 2191 static void
 2192 add_dconf_key_to_keyfile (GKeyFile      *keyfile,
 2193                           DConfClient   *client,
 2194                           const char    *key,
 2195                           DConfReadFlags flags)
 2196 {
 2197   g_autofree char *group = g_path_get_dirname (key);
 2198   g_autofree char *k = g_path_get_basename (key);
 2199   GVariant *value = dconf_client_read_full (client, key, flags, NULL);
 2200 
 2201   if (value)
 2202     {
 2203       g_autofree char *val = g_variant_print (value, TRUE);
 2204       g_key_file_set_value (keyfile, group + 1, k, val);
 2205     }
 2206 }
 2207 
 2208 static void
 2209 add_dconf_dir_to_keyfile (GKeyFile      *keyfile,
 2210                           DConfClient   *client,
 2211                           const char    *dir,
 2212                           DConfReadFlags flags)
 2213 {
 2214   g_auto(GStrv) keys = NULL;
 2215   int i;
 2216 
 2217   keys = dconf_client_list (client, dir, NULL);
 2218   for (i = 0; keys[i]; i++)
 2219     {
 2220       g_autofree char *k = g_strconcat (dir, keys[i], NULL);
 2221       if (dconf_is_dir (k, NULL))
 2222         add_dconf_dir_to_keyfile (keyfile, client, k, flags);
 2223       else if (dconf_is_key (k, NULL))
 2224         add_dconf_key_to_keyfile (keyfile, client, k, flags);
 2225     }
 2226 }
 2227 
 2228 static void
 2229 add_dconf_locks_to_list (GString     *s,
 2230                          DConfClient *client,
 2231                          const char  *dir)
 2232 {
 2233   g_auto(GStrv) locks = NULL;
 2234   int i;
 2235 
 2236   locks = dconf_client_list_locks (client, dir, NULL);
 2237   for (i = 0; locks[i]; i++)
 2238     {
 2239       g_string_append (s, locks[i]);
 2240       g_string_append_c (s, '\n');
 2241     }
 2242 }
 2243 
 2244 #endif /* HAVE_DCONF */
 2245 
 2246 static void
 2247 get_dconf_data (const char  *app_id,
 2248                 const char **paths,
 2249                 const char  *migrate_path,
 2250                 char       **defaults,
 2251                 gsize       *defaults_size,
 2252                 char       **values,
 2253                 gsize       *values_size,
 2254                 char       **locks,
 2255                 gsize       *locks_size)
 2256 {
 2257 #ifdef HAVE_DCONF
 2258   DConfClient *client = NULL;
 2259   g_autofree char *prefix = NULL;
 2260 #endif
 2261   g_autoptr(GKeyFile) defaults_data = NULL;
 2262   g_autoptr(GKeyFile) values_data = NULL;
 2263   g_autoptr(GString) locks_data = NULL;
 2264 
 2265   defaults_data = g_key_file_new ();
 2266   values_data = g_key_file_new ();
 2267   locks_data = g_string_new ("");
 2268 
 2269 #ifdef HAVE_DCONF
 2270 
 2271   client = dconf_client_new ();
 2272 
 2273   prefix = flatpak_dconf_path_for_app_id (app_id);
 2274 
 2275   if (migrate_path)
 2276     {
 2277       g_debug ("Add values in dir '%s', prefix is '%s'", migrate_path, prefix);
 2278       if (flatpak_dconf_path_is_similar (migrate_path, prefix))
 2279         add_dconf_dir_to_keyfile (values_data, client, migrate_path, DCONF_READ_USER_VALUE);
 2280       else
 2281         g_warning ("Ignoring D-Conf migrate-path setting %s", migrate_path);
 2282     }
 2283 
 2284   g_debug ("Add defaults in dir %s", prefix);
 2285   add_dconf_dir_to_keyfile (defaults_data, client, prefix, DCONF_READ_DEFAULT_VALUE);
 2286 
 2287   g_debug ("Add locks in dir %s", prefix);
 2288   add_dconf_locks_to_list (locks_data, client, prefix);
 2289 
 2290   /* We allow extra paths for defaults and locks, but not for user values */
 2291   if (paths)
 2292     {
 2293       int i;
 2294       for (i = 0; paths[i]; i++)
 2295         {
 2296           if (dconf_is_dir (paths[i], NULL))
 2297             {
 2298               g_debug ("Add defaults in dir %s", paths[i]);
 2299               add_dconf_dir_to_keyfile (defaults_data, client, paths[i], DCONF_READ_DEFAULT_VALUE);
 2300 
 2301               g_debug ("Add locks in dir %s", paths[i]);
 2302               add_dconf_locks_to_list (locks_data, client, paths[i]);
 2303             }
 2304           else if (dconf_is_key (paths[i], NULL))
 2305             {
 2306               g_debug ("Add individual key %s", paths[i]);
 2307               add_dconf_key_to_keyfile (defaults_data, client, paths[i], DCONF_READ_DEFAULT_VALUE);
 2308               add_dconf_key_to_keyfile (values_data, client, paths[i], DCONF_READ_USER_VALUE);
 2309             }
 2310           else
 2311             {
 2312               g_warning ("Ignoring settings path '%s': neither dir nor key", paths[i]);
 2313             }
 2314         }
 2315     }
 2316 #endif
 2317 
 2318   *defaults = g_key_file_to_data (defaults_data, defaults_size, NULL);
 2319   *values = g_key_file_to_data (values_data, values_size, NULL);
 2320   *locks_size = locks_data->len;
 2321   *locks = g_string_free (g_steal_pointer (&locks_data), FALSE);
 2322 
 2323 #ifdef HAVE_DCONF
 2324   g_object_unref (client);
 2325 #endif
 2326 }
 2327 
 2328 static gboolean
 2329 flatpak_run_add_dconf_args (FlatpakBwrap *bwrap,
 2330                             const char   *app_id,
 2331                             GKeyFile     *metakey,
 2332                             GError      **error)
 2333 {
 2334   g_auto(GStrv) paths = NULL;
 2335   g_autofree char *migrate_path = NULL;
 2336   g_autofree char *defaults = NULL;
 2337   g_autofree char *values = NULL;
 2338   g_autofree char *locks = NULL;
 2339   gsize defaults_size;
 2340   gsize values_size;
 2341   gsize locks_size;
 2342 
 2343   if (metakey)
 2344     {
 2345       paths = g_key_file_get_string_list (metakey,
 2346                                           FLATPAK_METADATA_GROUP_DCONF,
 2347                                           FLATPAK_METADATA_KEY_DCONF_PATHS,
 2348                                           NULL, NULL);
 2349       migrate_path = g_key_file_get_string (metakey,
 2350                                             FLATPAK_METADATA_GROUP_DCONF,
 2351                                             FLATPAK_METADATA_KEY_DCONF_MIGRATE_PATH,
 2352                                             NULL);
 2353     }
 2354 
 2355   get_dconf_data (app_id,
 2356                   (const char **) paths,
 2357                   migrate_path,
 2358                   &defaults, &defaults_size,
 2359                   &values, &values_size,
 2360                   &locks, &locks_size);
 2361 
 2362   if (defaults_size != 0 &&
 2363       !flatpak_bwrap_add_args_data (bwrap,
 2364                                     "dconf-defaults",
 2365                                     defaults, defaults_size,
 2366                                     "/etc/glib-2.0/settings/defaults",
 2367                                     error))
 2368     return FALSE;
 2369 
 2370   if (locks_size != 0 &&
 2371       !flatpak_bwrap_add_args_data (bwrap,
 2372                                     "dconf-locks",
 2373                                     locks, locks_size,
 2374                                     "/etc/glib-2.0/settings/locks",
 2375                                     error))
 2376     return FALSE;
 2377 
 2378   /* We do a one-time conversion of existing dconf settings to a keyfile.
 2379    * Only do that once the app stops requesting dconf access.
 2380    */
 2381   if (migrate_path)
 2382     {
 2383       g_autofree char *filename = NULL;
 2384 
 2385       filename = g_build_filename (g_get_home_dir (),
 2386                                    ".var/app", app_id,
 2387                                    "config/glib-2.0/settings/keyfile",
 2388                                    NULL);
 2389 
 2390       g_debug ("writing D-Conf values to %s", filename);
 2391 
 2392       if (values_size != 0 && !g_file_test (filename, G_FILE_TEST_EXISTS))
 2393         {
 2394           g_autofree char *dir = g_path_get_dirname (filename);
 2395 
 2396           if (g_mkdir_with_parents (dir, 0700) == -1)
 2397             {
 2398               g_warning ("failed creating dirs for %s", filename);
 2399               return FALSE;
 2400             }
 2401 
 2402           if (!g_file_set_contents (filename, values, values_size, error))
 2403             {
 2404               g_warning ("failed writing %s", filename);
 2405               return FALSE;
 2406             }
 2407         }
 2408     }
 2409 
 2410   return TRUE;
 2411 }
 2412 
 2413 gboolean
 2414 flatpak_run_add_app_info_args (FlatpakBwrap       *bwrap,
 2415                                GFile              *app_files,
 2416                                GFile              *original_app_files,
 2417                                GBytes             *app_deploy_data,
 2418                                const char         *app_extensions,
 2419                                GFile              *runtime_files,
 2420                                GFile              *original_runtime_files,
 2421                                GBytes             *runtime_deploy_data,
 2422                                const char         *runtime_extensions,
 2423                                const char         *app_id,
 2424                                const char         *app_branch,
 2425                                FlatpakDecomposed  *runtime_ref,
 2426                                GFile              *app_id_dir,
 2427                                FlatpakContext     *final_app_context,
 2428                                FlatpakContext     *cmdline_context,
 2429                                gboolean            sandbox,
 2430                                gboolean            build,
 2431                                gboolean            devel,
 2432                                char              **app_info_path_out,
 2433                                int                 instance_id_fd,
 2434                                char              **instance_id_host_dir_out,
 2435                                GError             **error)
 2436 {
 2437   g_autofree char *info_path = NULL;
 2438   g_autofree char *bwrapinfo_path = NULL;
 2439   int fd, fd2, fd3;
 2440   g_autoptr(GKeyFile) keyfile = NULL;
 2441   g_autofree char *runtime_path = NULL;
 2442   const char *group;
 2443   g_autofree char *instance_id = NULL;
 2444   glnx_autofd int lock_fd = -1;
 2445   g_autofree char *instance_id_host_dir = NULL;
 2446   g_autofree char *instance_id_sandbox_dir = NULL;
 2447   g_autofree char *instance_id_lock_file = NULL;
 2448   g_autofree char *arch = flatpak_decomposed_dup_arch (runtime_ref);
 2449 
 2450   g_return_val_if_fail (app_id != NULL, FALSE);
 2451 
 2452   instance_id = flatpak_instance_allocate_id (&instance_id_host_dir, &lock_fd);
 2453   if (instance_id == NULL)
 2454     return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Unable to allocate instance id"));
 2455 
 2456   instance_id_sandbox_dir = g_strdup_printf ("/run/flatpak/.flatpak/%s", instance_id);
 2457   instance_id_lock_file = g_build_filename (instance_id_sandbox_dir, ".ref", NULL);
 2458 
 2459   flatpak_bwrap_add_args (bwrap,
 2460                           "--ro-bind",
 2461                           instance_id_host_dir,
 2462                           instance_id_sandbox_dir,
 2463                           "--lock-file",
 2464                           instance_id_lock_file,
 2465                           NULL);
 2466   flatpak_bwrap_add_runtime_dir_member (bwrap, ".flatpak");
 2467   /* Keep the .ref lock held until we've started bwrap to avoid races */
 2468   flatpak_bwrap_add_noinherit_fd (bwrap, glnx_steal_fd (&lock_fd));
 2469 
 2470   info_path = g_build_filename (instance_id_host_dir, "info", NULL);
 2471 
 2472   keyfile = g_key_file_new ();
 2473 
 2474   if (original_app_files)
 2475     group = FLATPAK_METADATA_GROUP_APPLICATION;
 2476   else
 2477     group = FLATPAK_METADATA_GROUP_RUNTIME;
 2478 
 2479   g_key_file_set_string (keyfile, group, FLATPAK_METADATA_KEY_NAME, app_id);
 2480   g_key_file_set_string (keyfile, group, FLATPAK_METADATA_KEY_RUNTIME,
 2481                          flatpak_decomposed_get_ref (runtime_ref));
 2482 
 2483   g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2484                          FLATPAK_METADATA_KEY_INSTANCE_ID, instance_id);
 2485   if (app_id_dir)
 2486     {
 2487       g_autofree char *instance_path = g_file_get_path (app_id_dir);
 2488       g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2489                              FLATPAK_METADATA_KEY_INSTANCE_PATH, instance_path);
 2490     }
 2491 
 2492   if (app_files)
 2493     {
 2494       g_autofree char *app_path = g_file_get_path (app_files);
 2495       g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2496                              FLATPAK_METADATA_KEY_APP_PATH, app_path);
 2497     }
 2498 
 2499   if (original_app_files != NULL && original_app_files != app_files)
 2500     {
 2501       g_autofree char *app_path = g_file_get_path (original_app_files);
 2502       g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2503                              FLATPAK_METADATA_KEY_ORIGINAL_APP_PATH, app_path);
 2504     }
 2505 
 2506   if (app_deploy_data)
 2507     g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2508                            FLATPAK_METADATA_KEY_APP_COMMIT, flatpak_deploy_data_get_commit (app_deploy_data));
 2509   if (app_extensions && *app_extensions != 0)
 2510     g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2511                            FLATPAK_METADATA_KEY_APP_EXTENSIONS, app_extensions);
 2512   runtime_path = g_file_get_path (runtime_files);
 2513   g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2514                          FLATPAK_METADATA_KEY_RUNTIME_PATH, runtime_path);
 2515 
 2516   if (runtime_files != original_runtime_files)
 2517     {
 2518       g_autofree char *path = g_file_get_path (original_runtime_files);
 2519       g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2520                              FLATPAK_METADATA_KEY_ORIGINAL_RUNTIME_PATH, path);
 2521     }
 2522 
 2523   if (runtime_deploy_data)
 2524     g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2525                            FLATPAK_METADATA_KEY_RUNTIME_COMMIT, flatpak_deploy_data_get_commit (runtime_deploy_data));
 2526   if (runtime_extensions && *runtime_extensions != 0)
 2527     g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2528                            FLATPAK_METADATA_KEY_RUNTIME_EXTENSIONS, runtime_extensions);
 2529   if (app_branch != NULL)
 2530     g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2531                            FLATPAK_METADATA_KEY_BRANCH, app_branch);
 2532   g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2533                          FLATPAK_METADATA_KEY_ARCH, arch);
 2534 
 2535   g_key_file_set_string (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2536                          FLATPAK_METADATA_KEY_FLATPAK_VERSION, PACKAGE_VERSION);
 2537 
 2538   if ((final_app_context->sockets & FLATPAK_CONTEXT_SOCKET_SESSION_BUS) == 0)
 2539     g_key_file_set_boolean (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2540                             FLATPAK_METADATA_KEY_SESSION_BUS_PROXY, TRUE);
 2541 
 2542   if ((final_app_context->sockets & FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS) == 0)
 2543     g_key_file_set_boolean (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2544                             FLATPAK_METADATA_KEY_SYSTEM_BUS_PROXY, TRUE);
 2545 
 2546   if (sandbox)
 2547     g_key_file_set_boolean (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2548                             FLATPAK_METADATA_KEY_SANDBOX, TRUE);
 2549   if (build)
 2550     g_key_file_set_boolean (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2551                             FLATPAK_METADATA_KEY_BUILD, TRUE);
 2552   if (devel)
 2553     g_key_file_set_boolean (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2554                             FLATPAK_METADATA_KEY_DEVEL, TRUE);
 2555 
 2556   if (cmdline_context)
 2557     {
 2558       g_autoptr(GPtrArray) cmdline_args = g_ptr_array_new_with_free_func (g_free);
 2559       flatpak_context_to_args (cmdline_context, cmdline_args);
 2560       if (cmdline_args->len > 0)
 2561         {
 2562           g_key_file_set_string_list (keyfile, FLATPAK_METADATA_GROUP_INSTANCE,
 2563                                       FLATPAK_METADATA_KEY_EXTRA_ARGS,
 2564                                       (const char * const *) cmdline_args->pdata,
 2565                                       cmdline_args->len);
 2566         }
 2567     }
 2568 
 2569   flatpak_context_save_metadata (final_app_context, TRUE, keyfile);
 2570 
 2571   if (!g_key_file_save_to_file (keyfile, info_path, error))
 2572     return FALSE;
 2573 
 2574   /* We want to create a file on /.flatpak-info that the app cannot modify, which
 2575      we do by creating a read-only bind mount. This way one can openat()
 2576      /proc/$pid/root, and if that succeeds use openat via that to find the
 2577      unfakable .flatpak-info file. However, there is a tiny race in that if
 2578      you manage to open /proc/$pid/root, but then the pid dies, then
 2579      every mount but the root is unmounted in the namespace, so the
 2580      .flatpak-info will be empty. We fix this by first creating a real file
 2581      with the real info in, then bind-mounting on top of that, the same info.
 2582      This way even if the bind-mount is unmounted we can find the real data.
 2583    */
 2584 
 2585   fd = open (info_path, O_RDONLY);
 2586   if (fd == -1)
 2587     {
 2588       int errsv = errno;
 2589       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 2590                    _("Failed to open flatpak-info file: %s"), g_strerror (errsv));
 2591       return FALSE;
 2592     }
 2593 
 2594   fd2 = open (info_path, O_RDONLY);
 2595   if (fd2 == -1)
 2596     {
 2597       close (fd);
 2598       int errsv = errno;
 2599       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 2600                    _("Failed to open flatpak-info file: %s"), g_strerror (errsv));
 2601       return FALSE;
 2602     }
 2603 
 2604   flatpak_bwrap_add_args_data_fd (bwrap,
 2605                                   "--file", fd, "/.flatpak-info");
 2606   flatpak_bwrap_add_args_data_fd (bwrap,
 2607                                   "--ro-bind-data", fd2, "/.flatpak-info");
 2608 
 2609   /* Tell the application that it's running under Flatpak in a generic way. */
 2610   flatpak_bwrap_add_args (bwrap,
 2611                           "--setenv", "container", "flatpak",
 2612                           NULL);
 2613   if (!flatpak_bwrap_add_args_data (bwrap,
 2614                                     "container-manager",
 2615                                     "flatpak\n", -1,
 2616                                     "/run/host/container-manager",
 2617                                     error))
 2618     return FALSE;
 2619 
 2620   bwrapinfo_path = g_build_filename (instance_id_host_dir, "bwrapinfo.json", NULL);
 2621   fd3 = open (bwrapinfo_path, O_RDWR | O_CREAT, 0644);
 2622   if (fd3 == -1)
 2623     {
 2624       close (fd);
 2625       close (fd2);
 2626       int errsv = errno;
 2627       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 2628                    _("Failed to open bwrapinfo.json file: %s"), g_strerror (errsv));
 2629       return FALSE;
 2630     }
 2631 
 2632   /* NOTE: It is important that this takes place after bwrapinfo.json is created,
 2633      otherwise start notifications in the portal may not work. */
 2634   if (instance_id_fd != -1)
 2635     {
 2636       gsize instance_id_position = 0;
 2637       gsize instance_id_size = strlen (instance_id);
 2638 
 2639       while (instance_id_size > 0)
 2640         {
 2641           gssize bytes_written = write (instance_id_fd, instance_id + instance_id_position, instance_id_size);
 2642           if (G_UNLIKELY (bytes_written <= 0))
 2643             {
 2644               int errsv = bytes_written == -1 ? errno : ENOSPC;
 2645               if (errsv == EINTR)
 2646                 continue;
 2647 
 2648               close (fd);
 2649               close (fd2);
 2650               close (fd3);
 2651 
 2652               g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 2653                            _("Failed to write to instance id fd: %s"), g_strerror (errsv));
 2654               return FALSE;
 2655             }
 2656 
 2657           instance_id_position += bytes_written;
 2658           instance_id_size -= bytes_written;
 2659         }
 2660 
 2661       close (instance_id_fd);
 2662     }
 2663 
 2664   flatpak_bwrap_add_args_data_fd (bwrap, "--info-fd", fd3, NULL);
 2665 
 2666   if (app_info_path_out != NULL)
 2667     *app_info_path_out = g_strdup_printf ("/proc/self/fd/%d", fd);
 2668 
 2669   if (instance_id_host_dir_out != NULL)
 2670     *instance_id_host_dir_out = g_steal_pointer (&instance_id_host_dir);
 2671 
 2672   return TRUE;
 2673 }
 2674 
 2675 static void
 2676 add_tzdata_args (FlatpakBwrap *bwrap,
 2677                  GFile *runtime_files)
 2678 {
 2679   g_autofree char *raw_timezone = flatpak_get_timezone ();
 2680   g_autofree char *timezone_content = g_strdup_printf ("%s\n", raw_timezone);
 2681   g_autofree char *localtime_content = g_strconcat ("../usr/share/zoneinfo/", raw_timezone, NULL);
 2682   g_autoptr(GFile) runtime_zoneinfo = NULL;
 2683 
 2684   if (runtime_files)
 2685     runtime_zoneinfo = g_file_resolve_relative_path (runtime_files, "share/zoneinfo");
 2686 
 2687   /* Check for runtime /usr/share/zoneinfo */
 2688   if (runtime_zoneinfo != NULL && g_file_query_exists (runtime_zoneinfo, NULL))
 2689     {
 2690       /* Check for host /usr/share/zoneinfo */
 2691       if (g_file_test ("/usr/share/zoneinfo", G_FILE_TEST_IS_DIR))
 2692         {
 2693           /* Here we assume the host timezone file exist in the host data */
 2694           flatpak_bwrap_add_args (bwrap,
 2695                                   "--ro-bind", "/usr/share/zoneinfo", "/usr/share/zoneinfo",
 2696                                   "--symlink", localtime_content, "/etc/localtime",
 2697                                   NULL);
 2698         }
 2699       else
 2700         {
 2701           g_autoptr(GFile) runtime_tzfile = g_file_resolve_relative_path (runtime_zoneinfo, raw_timezone);
 2702 
 2703           /* Check if host timezone file exist in the runtime tzdata */
 2704           if (g_file_query_exists (runtime_tzfile, NULL))
 2705             flatpak_bwrap_add_args (bwrap,
 2706                                     "--symlink", localtime_content, "/etc/localtime",
 2707                                     NULL);
 2708         }
 2709     }
 2710 
 2711   flatpak_bwrap_add_args_data (bwrap, "timezone",
 2712                                timezone_content, -1, "/etc/timezone",
 2713                                NULL);
 2714 }
 2715 
 2716 static void
 2717 add_monitor_path_args (gboolean      use_session_helper,
 2718                        FlatpakBwrap *bwrap)
 2719 {
 2720   g_autoptr(AutoFlatpakSessionHelper) session_helper = NULL;
 2721   g_autofree char *monitor_path = NULL;
 2722   g_autofree char *pkcs11_socket_path = NULL;
 2723   g_autoptr(GVariant) session_data = NULL;
 2724 
 2725   if (use_session_helper)
 2726     {
 2727       session_helper =
 2728         flatpak_session_helper_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
 2729                                                        G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
 2730                                                        FLATPAK_SESSION_HELPER_BUS_NAME,
 2731                                                        FLATPAK_SESSION_HELPER_PATH,
 2732                                                        NULL, NULL);
 2733     }
 2734 
 2735   if (session_helper &&
 2736       flatpak_session_helper_call_request_session_sync (session_helper,
 2737                                                         &session_data,
 2738                                                         NULL, NULL))
 2739     {
 2740       if (g_variant_lookup (session_data, "path", "s", &monitor_path))
 2741         flatpak_bwrap_add_args (bwrap,
 2742                                 "--ro-bind", monitor_path, "/run/host/monitor",
 2743                                 "--symlink", "/run/host/monitor/resolv.conf", "/etc/resolv.conf",
 2744                                 "--symlink", "/run/host/monitor/host.conf", "/etc/host.conf",
 2745                                 "--symlink", "/run/host/monitor/hosts", "/etc/hosts",
 2746                                 "--symlink", "/run/host/monitor/gai.conf", "/etc/gai.conf",
 2747                                 NULL);
 2748 
 2749       if (g_variant_lookup (session_data, "pkcs11-socket", "s", &pkcs11_socket_path))
 2750         {
 2751           static const char sandbox_pkcs11_socket_path[] = "/run/flatpak/p11-kit/pkcs11";
 2752           const char *trusted_module_contents =
 2753             "# This overrides the runtime p11-kit-trusted module with a client one talking to the trust module on the host\n"
 2754             "module: p11-kit-client.so\n";
 2755 
 2756           if (flatpak_bwrap_add_args_data (bwrap, "p11-kit-trust.module",
 2757                                            trusted_module_contents, -1,
 2758                                            "/etc/pkcs11/modules/p11-kit-trust.module", NULL))
 2759             {
 2760               flatpak_bwrap_add_args (bwrap,
 2761                                       "--ro-bind", pkcs11_socket_path, sandbox_pkcs11_socket_path,
 2762                                       NULL);
 2763               flatpak_bwrap_unset_env (bwrap, "P11_KIT_SERVER_ADDRESS");
 2764               flatpak_bwrap_add_runtime_dir_member (bwrap, "p11-kit");
 2765             }
 2766         }
 2767     }
 2768   else
 2769     {
 2770       if (g_file_test ("/etc/resolv.conf", G_FILE_TEST_EXISTS))
 2771         flatpak_bwrap_add_args (bwrap,
 2772                                 "--ro-bind", "/etc/resolv.conf", "/etc/resolv.conf",
 2773                                 NULL);
 2774       if (g_file_test ("/etc/host.conf", G_FILE_TEST_EXISTS))
 2775         flatpak_bwrap_add_args (bwrap,
 2776                                 "--ro-bind", "/etc/host.conf", "/etc/host.conf",
 2777                                 NULL);
 2778       if (g_file_test ("/etc/hosts", G_FILE_TEST_EXISTS))
 2779         flatpak_bwrap_add_args (bwrap,
 2780                                 "--ro-bind", "/etc/hosts", "/etc/hosts",
 2781                                 NULL);
 2782       if (g_file_test ("/etc/gai.conf", G_FILE_TEST_EXISTS))
 2783         flatpak_bwrap_add_args (bwrap,
 2784                                 "--ro-bind", "/etc/gai.conf", "/etc/gai.conf",
 2785                                 NULL);
 2786     }
 2787 }
 2788 
 2789 static void
 2790 add_document_portal_args (FlatpakBwrap *bwrap,
 2791                           const char   *app_id,
 2792                           char        **out_mount_path)
 2793 {
 2794   g_autoptr(GDBusConnection) session_bus = NULL;
 2795   g_autofree char *doc_mount_path = NULL;
 2796 
 2797   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
 2798   if (session_bus)
 2799     {
 2800       g_autoptr(GError) local_error = NULL;
 2801       g_autoptr(GDBusMessage) reply = NULL;
 2802       g_autoptr(GDBusMessage) msg =
 2803         g_dbus_message_new_method_call ("org.freedesktop.portal.Documents",
 2804                                         "/org/freedesktop/portal/documents",
 2805                                         "org.freedesktop.portal.Documents",
 2806                                         "GetMountPoint");
 2807       g_dbus_message_set_body (msg, g_variant_new ("()"));
 2808       reply =
 2809         g_dbus_connection_send_message_with_reply_sync (session_bus, msg,
 2810                                                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
 2811                                                         30000,
 2812                                                         NULL,
 2813                                                         NULL,
 2814                                                         NULL);
 2815       if (reply)
 2816         {
 2817           if (g_dbus_message_to_gerror (reply, &local_error))
 2818             {
 2819               if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
 2820                 g_debug ("Document portal not available, not mounting /run/flatpak/doc");
 2821               else
 2822                 g_message ("Can't get document portal: %s", local_error->message);
 2823             }
 2824           else
 2825             {
 2826               static const char dst_path[] = "/run/flatpak/doc";
 2827               g_autofree char *src_path = NULL;
 2828               g_variant_get (g_dbus_message_get_body (reply),
 2829                              "(^ay)", &doc_mount_path);
 2830 
 2831               src_path = g_strdup_printf ("%s/by-app/%s",
 2832                                           doc_mount_path, app_id);
 2833               flatpak_bwrap_add_args (bwrap, "--bind", src_path, dst_path, NULL);
 2834               flatpak_bwrap_add_runtime_dir_member (bwrap, "doc");
 2835             }
 2836         }
 2837     }
 2838 
 2839   *out_mount_path = g_steal_pointer (&doc_mount_path);
 2840 }
 2841 
 2842 #ifdef ENABLE_SECCOMP
 2843 static const uint32_t seccomp_x86_64_extra_arches[] = { SCMP_ARCH_X86, 0, };
 2844 
 2845 #ifdef SCMP_ARCH_AARCH64
 2846 static const uint32_t seccomp_aarch64_extra_arches[] = { SCMP_ARCH_ARM, 0 };
 2847 #endif
 2848 
 2849 /*
 2850  * @negative_errno: Result code as returned by libseccomp functions
 2851  *
 2852  * Translate a libseccomp error code into an error message. libseccomp
 2853  * mostly returns negative `errno` values such as `-ENOMEM`, but some
 2854  * standard `errno` values are used for non-standard purposes where their
 2855  * `strerror()` would be misleading.
 2856  *
 2857  * Returns: a string version of @negative_errno if possible
 2858  */
 2859 static const char *
 2860 flatpak_seccomp_strerror (int negative_errno)
 2861 {
 2862   g_return_val_if_fail (negative_errno < 0, "Non-negative error value from libseccomp?");
 2863   g_return_val_if_fail (negative_errno > INT_MIN, "Out of range error value from libseccomp?");
 2864 
 2865   switch (negative_errno)
 2866     {
 2867       case -EDOM:
 2868         return "Architecture specific failure";
 2869 
 2870       case -EFAULT:
 2871         return "Internal libseccomp failure (unknown syscall?)";
 2872 
 2873       case -ECANCELED:
 2874         return "System failure beyond the control of libseccomp";
 2875     }
 2876 
 2877   /* e.g. -ENOMEM: the result of strerror() is good enough */
 2878   return g_strerror (-negative_errno);
 2879 }
 2880 
 2881 static inline void
 2882 cleanup_seccomp (void *p)
 2883 {
 2884   scmp_filter_ctx *pp = (scmp_filter_ctx *) p;
 2885 
 2886   if (*pp)
 2887     seccomp_release (*pp);
 2888 }
 2889 
 2890 static gboolean
 2891 setup_seccomp (FlatpakBwrap   *bwrap,
 2892                const char     *arch,
 2893                gulong          allowed_personality,
 2894                FlatpakRunFlags run_flags,
 2895                GError        **error)
 2896 {
 2897   gboolean multiarch = (run_flags & FLATPAK_RUN_FLAG_MULTIARCH) != 0;
 2898   gboolean devel = (run_flags & FLATPAK_RUN_FLAG_DEVEL) != 0;
 2899 
 2900   __attribute__((cleanup (cleanup_seccomp))) scmp_filter_ctx seccomp = NULL;
 2901 
 2902   /**** BEGIN NOTE ON CODE SHARING
 2903    *
 2904    * There are today a number of different Linux container
 2905    * implementations.  That will likely continue for long into the
 2906    * future.  But we can still try to share code, and it's important
 2907    * to do so because it affects what library and application writers
 2908    * can do, and we should support code portability between different
 2909    * container tools.
 2910    *
 2911    * This syscall blocklist is copied from linux-user-chroot, which was in turn
 2912    * clearly influenced by the Sandstorm.io blocklist.
 2913    *
 2914    * If you make any changes here, I suggest sending the changes along
 2915    * to other sandbox maintainers.  Using the libseccomp list is also
 2916    * an appropriate venue:
 2917    * https://groups.google.com/forum/#!forum/libseccomp
 2918    *
 2919    * A non-exhaustive list of links to container tooling that might
 2920    * want to share this blocklist:
 2921    *
 2922    *  https://github.com/sandstorm-io/sandstorm
 2923    *    in src/sandstorm/supervisor.c++
 2924    *  https://github.com/flatpak/flatpak.git
 2925    *    in common/flatpak-run.c
 2926    *  https://git.gnome.org/browse/linux-user-chroot
 2927    *    in src/setup-seccomp.c
 2928    *
 2929    * Other useful resources:
 2930    * https://github.com/systemd/systemd/blob/HEAD/src/shared/seccomp-util.c
 2931    * https://github.com/moby/moby/blob/HEAD/profiles/seccomp/default.json
 2932    *
 2933    **** END NOTE ON CODE SHARING
 2934    */
 2935   struct
 2936   {
 2937     int                  scall;
 2938     int                  errnum;
 2939     struct scmp_arg_cmp *arg;
 2940   } syscall_blocklist[] = {
 2941     /* Block dmesg */
 2942     {SCMP_SYS (syslog), EPERM},
 2943     /* Useless old syscall */
 2944     {SCMP_SYS (uselib), EPERM},
 2945     /* Don't allow disabling accounting */
 2946     {SCMP_SYS (acct), EPERM},
 2947     /* 16-bit code is unnecessary in the sandbox, and modify_ldt is a
 2948        historic source of interesting information leaks. */
 2949     {SCMP_SYS (modify_ldt), EPERM},
 2950     /* Don't allow reading current quota use */
 2951     {SCMP_SYS (quotactl), EPERM},
 2952 
 2953     /* Don't allow access to the kernel keyring */
 2954     {SCMP_SYS (add_key), EPERM},
 2955     {SCMP_SYS (keyctl), EPERM},
 2956     {SCMP_SYS (request_key), EPERM},
 2957 
 2958     /* Scary VM/NUMA ops */
 2959     {SCMP_SYS (move_pages), EPERM},
 2960     {SCMP_SYS (mbind), EPERM},
 2961     {SCMP_SYS (get_mempolicy), EPERM},
 2962     {SCMP_SYS (set_mempolicy), EPERM},
 2963     {SCMP_SYS (migrate_pages), EPERM},
 2964 
 2965     /* Don't allow subnamespace setups: */
 2966     {SCMP_SYS (unshare), EPERM},
 2967     {SCMP_SYS (setns), EPERM},
 2968     {SCMP_SYS (mount), EPERM},
 2969     {SCMP_SYS (umount), EPERM},
 2970     {SCMP_SYS (umount2), EPERM},
 2971     {SCMP_SYS (pivot_root), EPERM},
 2972     {SCMP_SYS (chroot), EPERM},
 2973 #if defined(__s390__) || defined(__s390x__) || defined(__CRIS__)
 2974     /* Architectures with CONFIG_CLONE_BACKWARDS2: the child stack
 2975      * and flags arguments are reversed so the flags come second */
 2976     {SCMP_SYS (clone), EPERM, &SCMP_A1 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
 2977 #else
 2978     /* Normally the flags come first */
 2979     {SCMP_SYS (clone), EPERM, &SCMP_A0 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
 2980 #endif
 2981 
 2982     /* Don't allow faking input to the controlling tty (CVE-2017-5226) */
 2983     {SCMP_SYS (ioctl), EPERM, &SCMP_A1 (SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int) TIOCSTI)},
 2984 
 2985     /* seccomp can't look into clone3()'s struct clone_args to check whether
 2986      * the flags are OK, so we have no choice but to block clone3().
 2987      * Return ENOSYS so user-space will fall back to clone().
 2988      * (GHSA-67h7-w3jq-vh4q; see also https://github.com/moby/moby/commit/9f6b562d) */
 2989     {SCMP_SYS (clone3), ENOSYS},
 2990 
 2991     /* New mount manipulation APIs can also change our VFS. There's no
 2992      * legitimate reason to do these in the sandbox, so block all of them
 2993      * rather than thinking about which ones might be dangerous.
 2994      * (GHSA-67h7-w3jq-vh4q) */
 2995     {SCMP_SYS (open_tree), ENOSYS},
 2996     {SCMP_SYS (move_mount), ENOSYS},
 2997     {SCMP_SYS (fsopen), ENOSYS},
 2998     {SCMP_SYS (fsconfig), ENOSYS},
 2999     {SCMP_SYS (fsmount), ENOSYS},
 3000     {SCMP_SYS (fspick), ENOSYS},
 3001     {SCMP_SYS (mount_setattr), ENOSYS},
 3002   };
 3003 
 3004   struct
 3005   {
 3006     int                  scall;
 3007     int                  errnum;
 3008     struct scmp_arg_cmp *arg;
 3009   } syscall_nondevel_blocklist[] = {
 3010     /* Profiling operations; we expect these to be done by tools from outside
 3011      * the sandbox.  In particular perf has been the source of many CVEs.
 3012      */
 3013     {SCMP_SYS (perf_event_open), EPERM},
 3014     /* Don't allow you to switch to bsd emulation or whatnot */
 3015     {SCMP_SYS (personality), EPERM, &SCMP_A0 (SCMP_CMP_NE, allowed_personality)},
 3016     {SCMP_SYS (ptrace), EPERM}
 3017   };
 3018   /* Blocklist all but unix, inet, inet6 and netlink */
 3019   struct
 3020   {
 3021     int             family;
 3022     FlatpakRunFlags flags_mask;
 3023   } socket_family_allowlist[] = {
 3024     /* NOTE: Keep in numerical order */
 3025     { AF_UNSPEC, 0 },
 3026     { AF_LOCAL, 0 },
 3027     { AF_INET, 0 },
 3028     { AF_INET6, 0 },
 3029     { AF_NETLINK, 0 },
 3030     { AF_CAN, FLATPAK_RUN_FLAG_CANBUS },
 3031     { AF_BLUETOOTH, FLATPAK_RUN_FLAG_BLUETOOTH },
 3032   };
 3033   int last_allowed_family;
 3034   int i, r;
 3035   g_auto(GLnxTmpfile) seccomp_tmpf  = { 0, };
 3036 
 3037   seccomp = seccomp_init (SCMP_ACT_ALLOW);
 3038   if (!seccomp)
 3039     return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Initialize seccomp failed"));
 3040 
 3041   if (arch != NULL)
 3042     {
 3043       uint32_t arch_id = 0;
 3044       const uint32_t *extra_arches = NULL;
 3045 
 3046       if (strcmp (arch, "i386") == 0)
 3047         {
 3048           arch_id = SCMP_ARCH_X86;
 3049         }
 3050       else if (strcmp (arch, "x86_64") == 0)
 3051         {
 3052           arch_id = SCMP_ARCH_X86_64;
 3053           extra_arches = seccomp_x86_64_extra_arches;
 3054         }
 3055       else if (strcmp (arch, "arm") == 0)
 3056         {
 3057           arch_id = SCMP_ARCH_ARM;
 3058         }
 3059 #ifdef SCMP_ARCH_AARCH64
 3060       else if (strcmp (arch, "aarch64") == 0)
 3061         {
 3062           arch_id = SCMP_ARCH_AARCH64;
 3063           extra_arches = seccomp_aarch64_extra_arches;
 3064         }
 3065 #endif
 3066 
 3067       /* We only really need to handle arches on multiarch systems.
 3068        * If only one arch is supported the default is fine */
 3069       if (arch_id != 0)
 3070         {
 3071           /* This *adds* the target arch, instead of replacing the
 3072              native one. This is not ideal, because we'd like to only
 3073              allow the target arch, but we can't really disallow the
 3074              native arch at this point, because then bubblewrap
 3075              couldn't continue running. */
 3076           r = seccomp_arch_add (seccomp, arch_id);
 3077           if (r < 0 && r != -EEXIST)
 3078             return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to add architecture to seccomp filter: %s"), flatpak_seccomp_strerror (r));
 3079 
 3080           if (multiarch && extra_arches != NULL)
 3081             {
 3082               for (i = 0; extra_arches[i] != 0; i++)
 3083                 {
 3084                   r = seccomp_arch_add (seccomp, extra_arches[i]);
 3085                   if (r < 0 && r != -EEXIST)
 3086                     return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to add multiarch architecture to seccomp filter: %s"), flatpak_seccomp_strerror (r));
 3087                 }
 3088             }
 3089         }
 3090     }
 3091 
 3092   /* TODO: Should we filter the kernel keyring syscalls in some way?
 3093    * We do want them to be used by desktop apps, but they could also perhaps
 3094    * leak system stuff or secrets from other apps.
 3095    */
 3096 
 3097   for (i = 0; i < G_N_ELEMENTS (syscall_blocklist); i++)
 3098     {
 3099       int scall = syscall_blocklist[i].scall;
 3100       int errnum = syscall_blocklist[i].errnum;
 3101 
 3102       g_return_val_if_fail (errnum == EPERM || errnum == ENOSYS, FALSE);
 3103 
 3104       if (syscall_blocklist[i].arg)
 3105         r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 1, *syscall_blocklist[i].arg);
 3106       else
 3107         r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 0);
 3108 
 3109       /* EFAULT means "internal libseccomp error", but in practice we get
 3110        * this for syscall numbers added via flatpak-syscalls-private.h
 3111        * when trying to filter them on a non-native architecture, because
 3112        * libseccomp cannot map the syscall number to a name and back to a
 3113        * number for the non-native architecture. */
 3114       if (r == -EFAULT)
 3115         flatpak_debug2 ("Unable to block syscall %d: syscall not known to libseccomp?",
 3116                         scall);
 3117       else if (r < 0)
 3118         return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d: %s"), scall, flatpak_seccomp_strerror (r));
 3119     }
 3120 
 3121   if (!devel)
 3122     {
 3123       for (i = 0; i < G_N_ELEMENTS (syscall_nondevel_blocklist); i++)
 3124         {
 3125           int scall = syscall_nondevel_blocklist[i].scall;
 3126           int errnum = syscall_nondevel_blocklist[i].errnum;
 3127 
 3128           g_return_val_if_fail (errnum == EPERM || errnum == ENOSYS, FALSE);
 3129 
 3130           if (syscall_nondevel_blocklist[i].arg)
 3131             r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 1, *syscall_nondevel_blocklist[i].arg);
 3132           else
 3133             r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 0);
 3134 
 3135           /* See above for the meaning of EFAULT. */
 3136           if (r == -EFAULT)
 3137             flatpak_debug2 ("Unable to block syscall %d: syscall not known to libseccomp?",
 3138                             scall);
 3139           else if (r < 0)
 3140             return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d: %s"), scall, flatpak_seccomp_strerror (r));
 3141         }
 3142     }
 3143 
 3144   /* Socket filtering doesn't work on e.g. i386, so ignore failures here
 3145    * However, we need to user seccomp_rule_add_exact to avoid libseccomp doing
 3146    * something else: https://github.com/seccomp/libseccomp/issues/8 */
 3147   last_allowed_family = -1;
 3148   for (i = 0; i < G_N_ELEMENTS (socket_family_allowlist); i++)
 3149     {
 3150       int family = socket_family_allowlist[i].family;
 3151       int disallowed;
 3152 
 3153       if (socket_family_allowlist[i].flags_mask != 0 &&
 3154           (socket_family_allowlist[i].flags_mask & run_flags) != socket_family_allowlist[i].flags_mask)
 3155         continue;
 3156 
 3157       for (disallowed = last_allowed_family + 1; disallowed < family; disallowed++)
 3158         {
 3159           /* Blocklist the in-between valid families */
 3160           seccomp_rule_add_exact (seccomp, SCMP_ACT_ERRNO (EAFNOSUPPORT), SCMP_SYS (socket), 1, SCMP_A0 (SCMP_CMP_EQ, disallowed));
 3161         }
 3162       last_allowed_family = family;
 3163     }
 3164   /* Blocklist the rest */
 3165   seccomp_rule_add_exact (seccomp, SCMP_ACT_ERRNO (EAFNOSUPPORT), SCMP_SYS (socket), 1, SCMP_A0 (SCMP_CMP_GE, last_allowed_family + 1));
 3166 
 3167   if (!glnx_open_anonymous_tmpfile_full (O_RDWR | O_CLOEXEC, "/tmp", &seccomp_tmpf, error))
 3168     return FALSE;
 3169 
 3170   r = seccomp_export_bpf (seccomp, seccomp_tmpf.fd);
 3171 
 3172   if (r != 0)
 3173     return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to export bpf: %s"), flatpak_seccomp_strerror (r));
 3174 
 3175   lseek (seccomp_tmpf.fd, 0, SEEK_SET);
 3176 
 3177   flatpak_bwrap_add_args_data_fd (bwrap,
 3178                                   "--seccomp", glnx_steal_fd (&seccomp_tmpf.fd), NULL);
 3179 
 3180   return TRUE;
 3181 }
 3182 #endif
 3183 
 3184 static void
 3185 flatpak_run_setup_usr_links (FlatpakBwrap *bwrap,
 3186                              GFile        *runtime_files,
 3187                              const char   *sysroot)
 3188 {
 3189   int i;
 3190 
 3191   if (runtime_files == NULL)
 3192     return;
 3193 
 3194   for (i = 0; flatpak_abs_usrmerged_dirs[i] != NULL; i++)
 3195     {
 3196       const char *subdir = flatpak_abs_usrmerged_dirs[i];
 3197       g_autoptr(GFile) runtime_subdir = NULL;
 3198 
 3199       g_assert (subdir[0] == '/');
 3200       /* Skip the '/' when using as a subdirectory of the runtime */
 3201       runtime_subdir = g_file_get_child (runtime_files, subdir + 1);
 3202 
 3203       if (g_file_query_exists (runtime_subdir, NULL))
 3204         {
 3205           g_autofree char *link = g_strconcat ("usr", subdir, NULL);
 3206           g_autofree char *create = NULL;
 3207 
 3208           if (sysroot != NULL)
 3209             create = g_strconcat (sysroot, subdir, NULL);
 3210           else
 3211             create = g_strdup (subdir);
 3212 
 3213           flatpak_bwrap_add_args (bwrap,
 3214                                   "--symlink", link, create,
 3215                                   NULL);
 3216         }
 3217       else
 3218         {
 3219           g_debug ("%s does not exist",
 3220                    flatpak_file_get_path_cached (runtime_subdir));
 3221         }
 3222     }
 3223 }
 3224 
 3225 gboolean
 3226 flatpak_run_setup_base_argv (FlatpakBwrap   *bwrap,
 3227                              GFile          *runtime_files,
 3228                              GFile          *app_id_dir,
 3229                              const char     *arch,
 3230                              FlatpakRunFlags flags,
 3231                              GError        **error)
 3232 {
 3233   g_autofree char *run_dir = NULL;
 3234   g_autofree char *passwd_contents = NULL;
 3235   g_autoptr(GString) group_contents = NULL;
 3236   const char *pkcs11_conf_contents = NULL;
 3237   struct group *g;
 3238   gulong pers;
 3239   gid_t gid = getgid ();
 3240   g_autoptr(GFile) etc = NULL;
 3241 
 3242   run_dir = g_strdup_printf ("/run/user/%d", getuid ());
 3243 
 3244   passwd_contents = g_strdup_printf ("%s:x:%d:%d:%s:%s:%s\n"
 3245                                      "nfsnobody:x:65534:65534:Unmapped user:/:/sbin/nologin\n",
 3246                                      g_get_user_name (),
 3247                                      getuid (), gid,
 3248                                      g_get_real_name (),
 3249                                      g_get_home_dir (),
 3250                                      DEFAULT_SHELL);
 3251 
 3252   group_contents = g_string_new ("");
 3253   g = getgrgid (gid);
 3254   /* if NULL, the primary group is not known outside the container, so
 3255    * it might as well stay unknown inside the container... */
 3256   if (g != NULL)
 3257     g_string_append_printf (group_contents, "%s:x:%d:%s\n",
 3258                             g->gr_name, gid, g_get_user_name ());
 3259   g_string_append (group_contents, "nfsnobody:x:65534:\n");
 3260 
 3261   pkcs11_conf_contents =
 3262     "# Disable user pkcs11 config, because the host modules don't work in the runtime\n"
 3263     "user-config: none\n";
 3264 
 3265   if ((flags & FLATPAK_RUN_FLAG_NO_PROC) == 0)
 3266     flatpak_bwrap_add_args (bwrap,
 3267                             "--proc", "/proc",
 3268                             NULL);
 3269 
 3270   if (!(flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS))
 3271     flatpak_bwrap_add_arg (bwrap, "--unshare-pid");
 3272 
 3273   flatpak_bwrap_add_args (bwrap,
 3274                           "--dir", "/tmp",
 3275                           "--dir", "/var/tmp",
 3276                           "--dir", "/run/host",
 3277                           "--dir", run_dir,
 3278                           "--setenv", "XDG_RUNTIME_DIR", run_dir,
 3279                           "--symlink", "../run", "/var/run",
 3280                           "--ro-bind", "/sys/block", "/sys/block",
 3281                           "--ro-bind", "/sys/bus", "/sys/bus",
 3282                           "--ro-bind", "/sys/class", "/sys/class",
 3283                           "--ro-bind", "/sys/dev", "/sys/dev",
 3284                           "--ro-bind", "/sys/devices", "/sys/devices",
 3285                           "--ro-bind-try", "/proc/self/ns/user", "/run/.userns",
 3286                           /* glib uses this like /etc/timezone */
 3287                           "--symlink", "/etc/timezone", "/var/db/zoneinfo",
 3288                           NULL);
 3289 
 3290   if (flags & FLATPAK_RUN_FLAG_DIE_WITH_PARENT)
 3291     flatpak_bwrap_add_args (bwrap,
 3292                             "--die-with-parent",
 3293                             NULL);
 3294 
 3295   if (flags & FLATPAK_RUN_FLAG_WRITABLE_ETC)
 3296     flatpak_bwrap_add_args (bwrap,
 3297                             "--dir", "/usr/etc",
 3298                             "--symlink", "usr/etc", "/etc",
 3299                             NULL);
 3300 
 3301   if (!flatpak_bwrap_add_args_data (bwrap, "passwd", passwd_contents, -1, "/etc/passwd", error))
 3302     return FALSE;
 3303 
 3304   if (!flatpak_bwrap_add_args_data (bwrap, "group", group_contents->str, -1, "/etc/group", error))
 3305     return FALSE;
 3306 
 3307   if (!flatpak_bwrap_add_args_data (bwrap, "pkcs11.conf", pkcs11_conf_contents, -1, "/etc/pkcs11/pkcs11.conf", error))
 3308     return FALSE;
 3309 
 3310   if (g_file_test ("/etc/machine-id", G_FILE_TEST_EXISTS))
 3311     flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/machine-id", "/etc/machine-id", NULL);
 3312   else if (g_file_test ("/var/lib/dbus/machine-id", G_FILE_TEST_EXISTS))
 3313     flatpak_bwrap_add_args (bwrap, "--ro-bind", "/var/lib/dbus/machine-id", "/etc/machine-id", NULL);
 3314 
 3315   if (runtime_files)
 3316     etc = g_file_get_child (runtime_files, "etc");
 3317   if (etc != NULL &&
 3318       (flags & FLATPAK_RUN_FLAG_WRITABLE_ETC) == 0 &&
 3319       g_file_query_exists (etc, NULL))
 3320     {
 3321       g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
 3322       struct dirent *dent;
 3323       gboolean inited;
 3324 
 3325       inited = glnx_dirfd_iterator_init_at (AT_FDCWD, flatpak_file_get_path_cached (etc), FALSE, &dfd_iter, NULL);
 3326 
 3327       while (inited)
 3328         {
 3329           g_autofree char *src = NULL;
 3330           g_autofree char *dest = NULL;
 3331 
 3332           if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, NULL, NULL) || dent == NULL)
 3333             break;
 3334 
 3335           if (strcmp (dent->d_name, "passwd") == 0 ||
 3336               strcmp (dent->d_name, "group") == 0 ||
 3337               strcmp (dent->d_name, "machine-id") == 0 ||
 3338               strcmp (dent->d_name, "resolv.conf") == 0 ||
 3339               strcmp (dent->d_name, "host.conf") == 0 ||
 3340               strcmp (dent->d_name, "hosts") == 0 ||
 3341               strcmp (dent->d_name, "gai.conf") == 0 ||
 3342               strcmp (dent->d_name, "localtime") == 0 ||
 3343               strcmp (dent->d_name, "timezone") == 0 ||
 3344               strcmp (dent->d_name, "pkcs11") == 0)
 3345             continue;
 3346 
 3347           src = g_build_filename (flatpak_file_get_path_cached (etc), dent->d_name, NULL);
 3348           dest = g_build_filename ("/etc", dent->d_name, NULL);
 3349           if (dent->d_type == DT_LNK)
 3350             {
 3351               g_autofree char *target = NULL;
 3352 
 3353               target = glnx_readlinkat_malloc (dfd_iter.fd, dent->d_name,
 3354                                                NULL, error);
 3355               if (target == NULL)
 3356                 return FALSE;
 3357 
 3358               flatpak_bwrap_add_args (bwrap, "--symlink", target, dest, NULL);
 3359             }
 3360           else
 3361             {
 3362               flatpak_bwrap_add_args (bwrap, "--ro-bind", src, dest, NULL);
 3363             }
 3364         }
 3365     }
 3366 
 3367   if (app_id_dir != NULL)
 3368     {
 3369       g_autoptr(GFile) app_cache_dir = g_file_get_child (app_id_dir, "cache");
 3370       g_autoptr(GFile) app_tmp_dir = g_file_get_child (app_cache_dir, "tmp");
 3371       g_autoptr(GFile) app_data_dir = g_file_get_child (app_id_dir, "data");
 3372       g_autoptr(GFile) app_config_dir = g_file_get_child (app_id_dir, "config");
 3373 
 3374       flatpak_bwrap_add_args (bwrap,
 3375                               /* These are nice to have as a fixed path */
 3376                               "--bind", flatpak_file_get_path_cached (app_cache_dir), "/var/cache",
 3377                               "--bind", flatpak_file_get_path_cached (app_data_dir), "/var/data",
 3378                               "--bind", flatpak_file_get_path_cached (app_config_dir), "/var/config",
 3379                               "--bind", flatpak_file_get_path_cached (app_tmp_dir), "/var/tmp",
 3380                               NULL);
 3381     }
 3382 
 3383   flatpak_run_setup_usr_links (bwrap, runtime_files, NULL);
 3384 
 3385   add_tzdata_args (bwrap, runtime_files);
 3386 
 3387   pers = PER_LINUX;
 3388 
 3389   if ((flags & FLATPAK_RUN_FLAG_SET_PERSONALITY) &&
 3390       flatpak_is_linux32_arch (arch))
 3391     {
 3392       g_debug ("Setting personality linux32");
 3393       pers = PER_LINUX32;
 3394     }
 3395 
 3396   /* Always set the personallity, and clear all weird flags */
 3397   personality (pers);
 3398 
 3399 #ifdef ENABLE_SECCOMP
 3400   if (!setup_seccomp (bwrap, arch, pers, flags, error))
 3401     return FALSE;
 3402 #endif
 3403 
 3404   if ((flags & FLATPAK_RUN_FLAG_WRITABLE_ETC) == 0)
 3405     add_monitor_path_args ((flags & FLATPAK_RUN_FLAG_NO_SESSION_HELPER) == 0, bwrap);
 3406 
 3407   return TRUE;
 3408 }
 3409 
 3410 static gboolean
 3411 forward_file (XdpDbusDocuments *documents,
 3412               const char       *app_id,
 3413               const char       *file,
 3414               char            **out_doc_id,
 3415               GError          **error)
 3416 {
 3417   int fd, fd_id;
 3418   g_autofree char *doc_id = NULL;
 3419   g_autoptr(GUnixFDList) fd_list = NULL;
 3420   const char *perms[] = { "read", "write", NULL };
 3421 
 3422   fd = open (file, O_PATH | O_CLOEXEC);
 3423   if (fd == -1)
 3424     return flatpak_fail (error, _("Failed to open ‘%s’"), file);
 3425 
 3426   fd_list = g_unix_fd_list_new ();
 3427   fd_id = g_unix_fd_list_append (fd_list, fd, error);
 3428   close (fd);
 3429 
 3430   if (!xdp_dbus_documents_call_add_sync (documents,
 3431                                          g_variant_new ("h", fd_id),
 3432                                          TRUE, /* reuse */
 3433                                          FALSE, /* not persistent */
 3434                                          fd_list,
 3435                                          &doc_id,
 3436                                          NULL,
 3437                                          NULL,
 3438                                          error))
 3439     {
 3440       if (error)
 3441         g_dbus_error_strip_remote_error (*error);
 3442       return FALSE;
 3443     }
 3444 
 3445   if (!xdp_dbus_documents_call_grant_permissions_sync (documents,
 3446                                                        doc_id,
 3447                                                        app_id,
 3448                                                        perms,
 3449                                                        NULL,
 3450                                                        error))
 3451     {
 3452       if (error)
 3453         g_dbus_error_strip_remote_error (*error);
 3454       return FALSE;
 3455     }
 3456 
 3457   *out_doc_id = g_steal_pointer (&doc_id);
 3458 
 3459   return TRUE;
 3460 }
 3461 
 3462 static gboolean
 3463 add_rest_args (FlatpakBwrap   *bwrap,
 3464                const char     *app_id,
 3465                FlatpakExports *exports,
 3466                gboolean        file_forwarding,
 3467                const char     *doc_mount_path,
 3468                char           *args[],
 3469                int             n_args,
 3470                GError        **error)
 3471 {
 3472   g_autoptr(XdpDbusDocuments) documents = NULL;
 3473   gboolean forwarding = FALSE;
 3474   gboolean forwarding_uri = FALSE;
 3475   gboolean can_forward = TRUE;
 3476   int i;
 3477 
 3478   if (file_forwarding && doc_mount_path == NULL)
 3479     {
 3480       g_message ("Can't get document portal mount path");
 3481       can_forward = FALSE;
 3482     }
 3483   else if (file_forwarding)
 3484     {
 3485       g_autoptr(GError) local_error = NULL;
 3486 
 3487       documents = xdp_dbus_documents_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, 0,
 3488                                                              "org.freedesktop.portal.Documents",
 3489                                                              "/org/freedesktop/portal/documents",
 3490                                                              NULL,
 3491                                                              &local_error);
 3492       if (documents == NULL)
 3493         {
 3494           g_message ("Can't get document portal: %s", local_error->message);
 3495           can_forward = FALSE;
 3496         }
 3497     }
 3498 
 3499   for (i = 0; i < n_args; i++)
 3500     {
 3501       g_autoptr(GFile) file = NULL;
 3502 
 3503       if (file_forwarding &&
 3504           (strcmp (args[i], "@@") == 0 ||
 3505            strcmp (args[i], "@@u") == 0))
 3506         {
 3507           forwarding_uri = strcmp (args[i], "@@u") == 0;
 3508           forwarding = !forwarding;
 3509           continue;
 3510         }
 3511 
 3512       if (can_forward && forwarding)
 3513         {
 3514           if (forwarding_uri)
 3515             {
 3516               if (g_str_has_prefix (args[i], "file:"))
 3517                 file = g_file_new_for_uri (args[i]);
 3518               else if (G_IS_DIR_SEPARATOR (args[i][0]))
 3519                 file = g_file_new_for_path (args[i]);
 3520             }
 3521           else
 3522             file = g_file_new_for_path (args[i]);
 3523         }
 3524 
 3525       if (file && !flatpak_exports_path_is_visible (exports,
 3526                                                     flatpak_file_get_path_cached (file)))
 3527         {
 3528           g_autofree char *doc_id = NULL;
 3529           g_autofree char *basename = NULL;
 3530           g_autofree char *doc_path = NULL;
 3531           if (!forward_file (documents, app_id, flatpak_file_get_path_cached (file),
 3532                              &doc_id, error))
 3533             return FALSE;
 3534 
 3535           basename = g_file_get_basename (file);
 3536           doc_path = g_build_filename (doc_mount_path, doc_id, basename, NULL);
 3537 
 3538           if (forwarding_uri)
 3539             {
 3540               g_autofree char *path = doc_path;
 3541               doc_path = g_filename_to_uri (path, NULL, NULL);
 3542               /* This should never fail */
 3543               g_assert (doc_path != NULL);
 3544             }
 3545 
 3546           g_debug ("Forwarding file '%s' as '%s' to %s", args[i], doc_path, app_id);
 3547           flatpak_bwrap_add_arg (bwrap, doc_path);
 3548         }
 3549       else
 3550         flatpak_bwrap_add_arg (bwrap, args[i]);
 3551     }
 3552 
 3553   return TRUE;
 3554 }
 3555 
 3556 FlatpakContext *
 3557 flatpak_context_load_for_deploy (FlatpakDeploy *deploy,
 3558                                  GError       **error)
 3559 {
 3560   g_autoptr(FlatpakContext) context = NULL;
 3561   g_autoptr(FlatpakContext) overrides = NULL;
 3562   g_autoptr(GKeyFile) metakey = NULL;
 3563 
 3564   metakey = flatpak_deploy_get_metadata (deploy);
 3565   context = flatpak_app_compute_permissions (metakey, NULL, error);
 3566   if (context == NULL)
 3567     return NULL;
 3568 
 3569   overrides = flatpak_deploy_get_overrides (deploy);
 3570   flatpak_context_merge (context, overrides);
 3571 
 3572   return g_steal_pointer (&context);
 3573 }
 3574 
 3575 static char *
 3576 calculate_ld_cache_checksum (GBytes   *app_deploy_data,
 3577                              GBytes   *runtime_deploy_data,
 3578                              const char *app_extensions,
 3579                              const char *runtime_extensions)
 3580 {
 3581   g_autoptr(GChecksum) ld_so_checksum = g_checksum_new (G_CHECKSUM_SHA256);
 3582   if (app_deploy_data)
 3583     g_checksum_update (ld_so_checksum, (guchar *) flatpak_deploy_data_get_commit (app_deploy_data), -1);
 3584   g_checksum_update (ld_so_checksum, (guchar *) flatpak_deploy_data_get_commit (runtime_deploy_data), -1);
 3585   if (app_extensions)
 3586     g_checksum_update (ld_so_checksum, (guchar *) app_extensions, -1);
 3587   if (runtime_extensions)
 3588     g_checksum_update (ld_so_checksum, (guchar *) runtime_extensions, -1);
 3589 
 3590   return g_strdup (g_checksum_get_string (ld_so_checksum));
 3591 }
 3592 
 3593 static gboolean
 3594 add_ld_so_conf (FlatpakBwrap *bwrap,
 3595                 GError      **error)
 3596 {
 3597   const char *contents =
 3598     "include /run/flatpak/ld.so.conf.d/app-*.conf\n"
 3599     "include /app/etc/ld.so.conf\n"
 3600     "/app/lib\n"
 3601     "include /run/flatpak/ld.so.conf.d/runtime-*.conf\n";
 3602 
 3603   return flatpak_bwrap_add_args_data (bwrap, "ld-so-conf",
 3604                                       contents, -1, "/etc/ld.so.conf", error);
 3605 }
 3606 
 3607 static int
 3608 regenerate_ld_cache (GPtrArray    *base_argv_array,
 3609                      GArray       *base_fd_array,
 3610                      GFile        *app_id_dir,
 3611                      const char   *checksum,
 3612                      GFile        *runtime_files,
 3613                      gboolean      generate_ld_so_conf,
 3614                      GCancellable *cancellable,
 3615                      GError      **error)
 3616 {
 3617   g_autoptr(FlatpakBwrap) bwrap = NULL;
 3618   g_autoptr(GArray) combined_fd_array = NULL;
 3619   g_autoptr(GFile) ld_so_cache = NULL;
 3620   g_autoptr(GFile) ld_so_cache_tmp = NULL;
 3621   g_autofree char *sandbox_cache_path = NULL;
 3622   g_autofree char *tmp_basename = NULL;
 3623   g_auto(GStrv) minimal_envp = NULL;
 3624   g_autofree char *commandline = NULL;
 3625   int exit_status;
 3626   glnx_autofd int ld_so_fd = -1;
 3627   g_autoptr(GFile) ld_so_dir = NULL;
 3628 
 3629   if (app_id_dir)
 3630     ld_so_dir = g_file_get_child (app_id_dir, ".ld.so");
 3631   else
 3632     {
 3633       g_autoptr(GFile) base_dir = g_file_new_for_path (g_get_user_cache_dir ());
 3634       ld_so_dir = g_file_resolve_relative_path (base_dir, "flatpak/ld.so");
 3635     }
 3636 
 3637   ld_so_cache = g_file_get_child (ld_so_dir, checksum);
 3638   ld_so_fd = open (flatpak_file_get_path_cached (ld_so_cache), O_RDONLY);
 3639   if (ld_so_fd >= 0)
 3640     return glnx_steal_fd (&ld_so_fd);
 3641 
 3642   g_debug ("Regenerating ld.so.cache %s", flatpak_file_get_path_cached (ld_so_cache));
 3643 
 3644   if (!flatpak_mkdir_p (ld_so_dir, cancellable, error))
 3645     return FALSE;
 3646 
 3647   minimal_envp = flatpak_run_get_minimal_env (FALSE, FALSE);
 3648   bwrap = flatpak_bwrap_new (minimal_envp);
 3649 
 3650   flatpak_bwrap_append_args (bwrap, base_argv_array);
 3651 
 3652   flatpak_run_setup_usr_links (bwrap, runtime_files, NULL);
 3653 
 3654   if (generate_ld_so_conf)
 3655     {
 3656       if (!add_ld_so_conf (bwrap, error))
 3657         return -1;
 3658     }
 3659   else
 3660     flatpak_bwrap_add_args (bwrap,
 3661                             "--symlink", "../usr/etc/ld.so.conf", "/etc/ld.so.conf",
 3662                             NULL);
 3663 
 3664   tmp_basename = g_strconcat (checksum, ".XXXXXX", NULL);
 3665   glnx_gen_temp_name (tmp_basename);
 3666 
 3667   sandbox_cache_path = g_build_filename ("/run/ld-so-cache-dir", tmp_basename, NULL);
 3668   ld_so_cache_tmp = g_file_get_child (ld_so_dir, tmp_basename);
 3669 
 3670   flatpak_bwrap_add_args (bwrap,
 3671                           "--unshare-pid",
 3672                           "--unshare-ipc",
 3673                           "--unshare-net",
 3674                           "--proc", "/proc",
 3675                           "--dev", "/dev",
 3676                           "--bind", flatpak_file_get_path_cached (ld_so_dir), "/run/ld-so-cache-dir",
 3677                           NULL);
 3678   flatpak_bwrap_sort_envp (bwrap);
 3679   flatpak_bwrap_envp_to_args (bwrap);
 3680 
 3681   if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
 3682     return -1;
 3683 
 3684   flatpak_bwrap_add_args (bwrap,
 3685                           "ldconfig", "-X", "-C", sandbox_cache_path, NULL);
 3686 
 3687   flatpak_bwrap_finish (bwrap);
 3688 
 3689   commandline = flatpak_quote_argv ((const char **) bwrap->argv->pdata, -1);
 3690   g_debug ("Running: '%s'", commandline);
 3691 
 3692   combined_fd_array = g_array_new (FALSE, TRUE, sizeof (int));
 3693   g_array_append_vals (combined_fd_array, base_fd_array->data, base_fd_array->len);
 3694   g_array_append_vals (combined_fd_array, bwrap->fds->data, bwrap->fds->len);
 3695 
 3696   /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
 3697   if (!g_spawn_sync (NULL,
 3698                      (char **) bwrap->argv->pdata,
 3699                      bwrap->envp,
 3700                      G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
 3701                      flatpak_bwrap_child_setup_cb, combined_fd_array,
 3702                      NULL, NULL,
 3703                      &exit_status,
 3704                      error))
 3705     return -1;
 3706 
 3707   if (!WIFEXITED (exit_status) || WEXITSTATUS (exit_status) != 0)
 3708     {
 3709       flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED,
 3710                           _("ldconfig failed, exit status %d"), exit_status);
 3711       return -1;
 3712     }
 3713 
 3714   ld_so_fd = open (flatpak_file_get_path_cached (ld_so_cache_tmp), O_RDONLY);
 3715   if (ld_so_fd < 0)
 3716     {
 3717       flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Can't open generated ld.so.cache"));
 3718       return -1;
 3719     }
 3720 
 3721   if (app_id_dir == NULL)
 3722     {
 3723       /* For runs without an app id dir we always regenerate the ld.so.cache */
 3724       unlink (flatpak_file_get_path_cached (ld_so_cache_tmp));
 3725     }
 3726   else
 3727     {
 3728       g_autoptr(GFile) active = g_file_get_child (ld_so_dir, "active");
 3729 
 3730       /* For app-dirs we keep one checksum alive, by pointing the active symlink to it */
 3731 
 3732       /* Rename to known name, possibly overwriting existing ref if race */
 3733       if (rename (flatpak_file_get_path_cached (ld_so_cache_tmp), flatpak_file_get_path_cached (ld_so_cache)) == -1)
 3734         {
 3735           glnx_set_error_from_errno (error);
 3736           return -1;
 3737         }
 3738 
 3739       if (!flatpak_switch_symlink_and_remove (flatpak_file_get_path_cached (active),
 3740                                               checksum, error))
 3741         return -1;
 3742     }
 3743 
 3744   return glnx_steal_fd (&ld_so_fd);
 3745 }
 3746 
 3747 /* Check that this user is actually allowed to run this app. When running
 3748  * from the gnome-initial-setup session, an app filter might not be available. */
 3749 static gboolean
 3750 check_parental_controls (FlatpakDecomposed *app_ref,
 3751                          FlatpakDeploy     *deploy,
 3752                          GCancellable      *cancellable,
 3753                          GError           **error)
 3754 {
 3755 #ifdef HAVE_LIBMALCONTENT
 3756   g_autoptr(MctManager) manager = NULL;
 3757   g_autoptr(MctAppFilter) app_filter = NULL;
 3758   g_autoptr(GDBusConnection) system_bus = NULL;
 3759   g_autoptr(GError) local_error = NULL;
 3760   g_autoptr(GDesktopAppInfo) app_info = NULL;
 3761   gboolean allowed = FALSE;
 3762 
 3763   system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
 3764   if (system_bus == NULL)
 3765     return FALSE;
 3766 
 3767   manager = mct_manager_new (system_bus);
 3768   app_filter = mct_manager_get_app_filter (manager, getuid (),
 3769                                            MCT_GET_APP_FILTER_FLAGS_INTERACTIVE,
 3770                                            cancellable, &local_error);
 3771   if (g_error_matches (local_error, MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_DISABLED))
 3772     {
 3773       g_debug ("Skipping parental controls check for %s since parental "
 3774                "controls are disabled globally", flatpak_decomposed_get_ref (app_ref));
 3775       return TRUE;
 3776     }
 3777   else if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) ||
 3778            g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
 3779     {
 3780       g_debug ("Skipping parental controls check for %s since a required "
 3781                "service was not found", flatpak_decomposed_get_ref (app_ref));
 3782       return TRUE;
 3783     }
 3784   else if (local_error != NULL)
 3785     {
 3786       g_propagate_error (error, g_steal_pointer (&local_error));
 3787       return FALSE;
 3788     }
 3789 
 3790   /* Always filter by app ID. Additionally, filter by app info (which runs
 3791    * multiple checks, including whether the app ID, executable path and
 3792    * content types are allowed) if available. If the flatpak contains
 3793    * multiple .desktop files, we use the main one. The app ID check is
 3794    * always done, as the binary executed by `flatpak run` isn’t necessarily
 3795    * extracted from a .desktop file. */
 3796   allowed = mct_app_filter_is_flatpak_ref_allowed (app_filter, flatpak_decomposed_get_ref (app_ref));
 3797 
 3798   /* Look up the app’s main .desktop file. */
 3799   if (deploy != NULL && allowed)
 3800     {
 3801       g_autoptr(GFile) deploy_dir = NULL;
 3802       const char *deploy_path;
 3803       g_autofree char *desktop_file_name = NULL;
 3804       g_autofree char *desktop_file_path = NULL;
 3805       g_autofree char *app_id = flatpak_decomposed_dup_id (app_ref);
 3806 
 3807       deploy_dir = flatpak_deploy_get_dir (deploy);
 3808       deploy_path = flatpak_file_get_path_cached (deploy_dir);
 3809 
 3810       desktop_file_name = g_strconcat (app_id, ".desktop", NULL);
 3811       desktop_file_path = g_build_path (G_DIR_SEPARATOR_S,
 3812                                         deploy_path,
 3813                                         "export",
 3814                                         "share",
 3815                                         "applications",
 3816                                         desktop_file_name,
 3817                                         NULL);
 3818       app_info = g_desktop_app_info_new_from_filename (desktop_file_path);
 3819     }
 3820 
 3821   if (app_info != NULL)
 3822     allowed = allowed && mct_app_filter_is_appinfo_allowed (app_filter,
 3823                                                             G_APP_INFO (app_info));
 3824 
 3825   if (!allowed)
 3826     return flatpak_fail_error (error, FLATPAK_ERROR_PERMISSION_DENIED,
 3827                                /* Translators: The placeholder is for an app ref. */
 3828                                _("Running %s is not allowed by the policy set by your administrator"),
 3829                                flatpak_decomposed_get_ref (app_ref));
 3830 #endif  /* HAVE_LIBMALCONTENT */
 3831 
 3832   return TRUE;
 3833 }
 3834 
 3835 static int
 3836 open_namespace_fd_if_needed (const char *path,
 3837                              const char *other_path) {
 3838   struct stat s, other_s;
 3839 
 3840   if (stat (path, &s) != 0)
 3841     return -1; /* No such namespace, ignore */
 3842 
 3843   if (stat (other_path, &other_s) != 0)
 3844     return -1; /* No such namespace, ignore */
 3845 
 3846   /* setns calls fail if the process is already in the desired namespace, hence the
 3847      check here to ensure the namespaces are different. */
 3848   if (s.st_ino != other_s.st_ino)
 3849     return open (path, O_RDONLY|O_CLOEXEC);
 3850 
 3851   return -1;
 3852 }
 3853 
 3854 static gboolean
 3855 check_sudo (GError **error)
 3856 {
 3857   const char *sudo_command_env = g_getenv ("SUDO_COMMAND");
 3858   g_auto(GStrv) split_command = NULL;
 3859 
 3860   /* This check exists to stop accidental usage of `sudo flatpak run`
 3861      and is not to prevent running as root.
 3862    */
 3863 
 3864   if (!sudo_command_env)
 3865     return TRUE;
 3866 
 3867   /* SUDO_COMMAND could be a value like `/usr/bin/flatpak run foo` */
 3868   split_command = g_strsplit (sudo_command_env, " ", 2);
 3869   if (g_str_has_suffix (split_command[0], "flatpak"))
 3870     return flatpak_fail_error (error, FLATPAK_ERROR, _("\"flatpak run\" is not intended to be run as `sudo flatpak run`, use `sudo -i` or `su -l` instead and invoke \"flatpak run\" from inside the new shell"));
 3871 
 3872   return TRUE;
 3873 }
 3874 
 3875 gboolean
 3876 flatpak_run_app (FlatpakDecomposed *app_ref,
 3877                  FlatpakDeploy     *app_deploy,
 3878                  const char        *custom_app_path,
 3879                  FlatpakContext    *extra_context,
 3880                  const char        *custom_runtime,
 3881                  const char        *custom_runtime_version,
 3882                  const char        *custom_runtime_commit,
 3883                  const char        *custom_usr_path,
 3884                  int                parent_pid,
 3885                  FlatpakRunFlags    flags,
 3886                  const char        *cwd,
 3887                  const char        *custom_command,
 3888                  char              *args[],
 3889                  int                n_args,
 3890                  int                instance_id_fd,
 3891                  char             **instance_dir_out,
 3892                  GCancellable      *cancellable,
 3893                  GError           **error)
 3894 {
 3895   g_autoptr(FlatpakDeploy) runtime_deploy = NULL;
 3896   g_autoptr(GBytes) runtime_deploy_data = NULL;
 3897   g_autoptr(GBytes) app_deploy_data = NULL;
 3898   g_autoptr(GFile) app_files = NULL;
 3899   g_autoptr(GFile) original_app_files = NULL;
 3900   g_autoptr(GFile) runtime_files = NULL;
 3901   g_autoptr(GFile) original_runtime_files = NULL;
 3902   g_autoptr(GFile) bin_ldconfig = NULL;
 3903   g_autoptr(GFile) app_id_dir = NULL;
 3904   g_autoptr(GFile) real_app_id_dir = NULL;
 3905   g_autofree char *default_runtime_pref = NULL;
 3906   g_autoptr(FlatpakDecomposed) default_runtime = NULL;
 3907   g_autofree char *default_command = NULL;
 3908   g_autoptr(GKeyFile) metakey = NULL;
 3909   g_autoptr(GKeyFile) runtime_metakey = NULL;
 3910   g_autoptr(FlatpakBwrap) bwrap = NULL;
 3911   const char *command = "/bin/sh";
 3912   g_autoptr(GError) my_error = NULL;
 3913   g_autoptr(FlatpakDecomposed) runtime_ref = NULL;
 3914   int i;
 3915   g_autoptr(GPtrArray) previous_app_id_dirs = NULL;
 3916   g_autofree char *app_id = NULL;
 3917   g_autofree char *app_arch = NULL;
 3918   g_autofree char *app_info_path = NULL;
 3919   g_autofree char *app_ld_path = NULL;
 3920   g_autofree char *instance_id_host_dir = NULL;
 3921   g_autoptr(FlatpakContext) app_context = NULL;
 3922   g_autoptr(FlatpakContext) overrides = NULL;
 3923   g_autoptr(FlatpakExports) exports = NULL;
 3924   g_autofree char *commandline = NULL;
 3925   g_autofree char *doc_mount_path = NULL;
 3926   g_autofree char *app_extensions = NULL;
 3927   g_autofree char *runtime_extensions = NULL;
 3928   g_autofree char *runtime_ld_path = NULL;
 3929   g_autofree char *checksum = NULL;
 3930   glnx_autofd int per_app_dir_lock_fd = -1;
 3931   g_autofree char *per_app_dir_lock_path = NULL;
 3932   g_autofree char *shared_xdg_runtime_dir = NULL;
 3933   int ld_so_fd = -1;
 3934   g_autoptr(GFile) runtime_ld_so_conf = NULL;
 3935   gboolean generate_ld_so_conf = TRUE;
 3936   gboolean use_ld_so_cache = TRUE;
 3937   gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0;
 3938   gboolean parent_expose_pids = (flags & FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS) != 0;
 3939   gboolean parent_share_pids = (flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS) != 0;
 3940   const char *app_target_path = "/app";
 3941   const char *runtime_target_path = "/usr";
 3942   struct stat s;
 3943 
 3944   g_return_val_if_fail (app_ref != NULL, FALSE);
 3945 
 3946   if (!check_sudo (error))
 3947     return FALSE;
 3948 
 3949   app_id = flatpak_decomposed_dup_id (app_ref);
 3950   g_return_val_if_fail (app_id != NULL, FALSE);
 3951   app_arch = flatpak_decomposed_dup_arch (app_ref);
 3952   g_return_val_if_fail (app_arch != NULL, FALSE);
 3953 
 3954   /* Check the user is allowed to run this flatpak. */
 3955   if (!check_parental_controls (app_ref, app_deploy, cancellable, error))
 3956     return FALSE;
 3957 
 3958   /* Construct the bwrap context. */
 3959   bwrap = flatpak_bwrap_new (NULL);
 3960   flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ());
 3961 
 3962   if (app_deploy == NULL)
 3963     {
 3964       g_assert (flatpak_decomposed_is_runtime (app_ref));
 3965       default_runtime_pref = flatpak_decomposed_dup_pref (app_ref);
 3966     }
 3967   else
 3968     {
 3969       const gchar *key;
 3970 
 3971       app_deploy_data = flatpak_deploy_get_deploy_data (app_deploy, FLATPAK_DEPLOY_VERSION_ANY, cancellable, error);
 3972       if (app_deploy_data == NULL)
 3973         return FALSE;
 3974 
 3975       if ((flags & FLATPAK_RUN_FLAG_DEVEL) != 0)
 3976         key = FLATPAK_METADATA_KEY_SDK;
 3977       else
 3978         key = FLATPAK_METADATA_KEY_RUNTIME;
 3979 
 3980       metakey = flatpak_deploy_get_metadata (app_deploy);
 3981       default_runtime_pref = g_key_file_get_string (metakey,
 3982                                                     FLATPAK_METADATA_GROUP_APPLICATION,
 3983                                                     key, &my_error);
 3984       if (my_error)
 3985         {
 3986           g_propagate_error (error, g_steal_pointer (&my_error));
 3987           return FALSE;
 3988         }
 3989     }
 3990 
 3991   default_runtime = flatpak_decomposed_new_from_pref (FLATPAK_KINDS_RUNTIME, default_runtime_pref, error);
 3992   if (default_runtime == NULL)
 3993     return FALSE;
 3994 
 3995   if (custom_runtime != NULL || custom_runtime_version != NULL)
 3996     {
 3997       g_auto(GStrv) custom_runtime_parts = NULL;
 3998       const char *custom_runtime_id = NULL;
 3999       const char *custom_runtime_arch = NULL;
 4000 
 4001       if (custom_runtime)
 4002         {
 4003           custom_runtime_parts = g_strsplit (custom_runtime, "/", 0);
 4004           for (i = 0; i < 3 && custom_runtime_parts[i] != NULL; i++)
 4005             {
 4006               if (strlen (custom_runtime_parts[i]) > 0)
 4007                 {
 4008                   if (i == 0)
 4009                     custom_runtime_id = custom_runtime_parts[i];
 4010                   if (i == 1)
 4011                     custom_runtime_arch = custom_runtime_parts[i];
 4012 
 4013                   if (i == 2 && custom_runtime_version == NULL)
 4014                     custom_runtime_version = custom_runtime_parts[i];
 4015                 }
 4016             }
 4017         }
 4018 
 4019       runtime_ref = flatpak_decomposed_new_from_decomposed (default_runtime,
 4020                                                             FLATPAK_KINDS_RUNTIME,
 4021                                                             custom_runtime_id,
 4022                                                             custom_runtime_arch,
 4023                                                             custom_runtime_version,
 4024                                                             error);
 4025       if (runtime_ref == NULL)
 4026         return FALSE;
 4027     }
 4028   else
 4029     runtime_ref = flatpak_decomposed_ref (default_runtime);
 4030 
 4031   runtime_deploy = flatpak_find_deploy_for_ref (flatpak_decomposed_get_ref (runtime_ref), custom_runtime_commit, NULL, cancellable, error);
 4032   if (runtime_deploy == NULL)
 4033     return FALSE;
 4034 
 4035   runtime_deploy_data = flatpak_deploy_get_deploy_data (runtime_deploy, FLATPAK_DEPLOY_VERSION_ANY, cancellable, error);
 4036   if (runtime_deploy_data == NULL)
 4037     return FALSE;
 4038 
 4039   runtime_metakey = flatpak_deploy_get_metadata (runtime_deploy);
 4040 
 4041   app_context = flatpak_app_compute_permissions (metakey, runtime_metakey, error);
 4042   if (app_context == NULL)
 4043     return FALSE;
 4044 
 4045   if (app_deploy != NULL)
 4046     {
 4047       overrides = flatpak_deploy_get_overrides (app_deploy);
 4048       flatpak_context_merge (app_context, overrides);
 4049     }
 4050 
 4051   if (sandboxed)
 4052     flatpak_context_make_sandboxed (app_context);
 4053 
 4054   if (extra_context)
 4055     flatpak_context_merge (app_context, extra_context);
 4056 
 4057   original_runtime_files = flatpak_deploy_get_files (runtime_deploy);
 4058 
 4059   if (custom_usr_path != NULL)
 4060     {
 4061       runtime_files = g_file_new_for_path (custom_usr_path);
 4062       /* Mount the original runtime below here instead of /usr */
 4063       runtime_target_path = "/run/parent/usr";
 4064     }
 4065   else
 4066     {
 4067       runtime_files = g_object_ref (original_runtime_files);
 4068     }
 4069 
 4070   bin_ldconfig = g_file_resolve_relative_path (runtime_files, "bin/ldconfig");
 4071   if (!g_file_query_exists (bin_ldconfig, NULL))
 4072     use_ld_so_cache = FALSE;
 4073 
 4074   /* We can't use the ld.so cache if we are using a custom /usr or /app,
 4075    * because we don't have a unique ID for the /usr or /app, so we can't
 4076    * do cache-invalidation correctly. The caller can either build their
 4077    * own ld.so.cache before supplying us with the runtime, or supply
 4078    * their own LD_LIBRARY_PATH. */
 4079   if (custom_usr_path != NULL || custom_app_path != NULL)
 4080     use_ld_so_cache = FALSE;
 4081 
 4082   if (app_deploy != NULL)
 4083     {
 4084       g_autofree const char **previous_ids = NULL;
 4085       gsize len = 0;
 4086       gboolean do_migrate;
 4087 
 4088       real_app_id_dir = flatpak_get_data_dir (app_id);
 4089       original_app_files = flatpak_deploy_get_files (app_deploy);
 4090 
 4091       previous_app_id_dirs = g_ptr_array_new_with_free_func (g_object_unref);
 4092       previous_ids = flatpak_deploy_data_get_previous_ids (app_deploy_data, &len);
 4093 
 4094       do_migrate = !g_file_query_exists (real_app_id_dir, cancellable);
 4095 
 4096       /* When migrating, find most recent old existing source and rename that to
 4097        * the new name.
 4098        *
 4099        * We ignore other names than that. For more recent names that don't exist
 4100        * we never ran them so nothing will even reference them. For older names
 4101        * either they were not used, or they were used but then the more recent
 4102        * name was used and a symlink to it was created.
 4103        *
 4104        * This means we may end up with a chain of symlinks: oldest -> old -> current.
 4105        * This is unfortunate but not really a problem, but for robustness reasons we
 4106        * don't want to mess with user files unnecessary. For example, the app dir could
 4107        * actually be a symlink for other reasons. Imagine for instance that you want to put the
 4108        * steam games somewhere else so you leave the app dir as a symlink to /mnt/steam.
 4109        */
 4110       for (i = len - 1; i >= 0; i--)
 4111         {
 4112           g_autoptr(GFile) previous_app_id_dir = NULL;
 4113           g_autoptr(GFileInfo) previous_app_id_dir_info = NULL;
 4114           g_autoptr(GError) local_error = NULL;
 4115 
 4116           previous_app_id_dir = flatpak_get_data_dir (previous_ids[i]);
 4117           previous_app_id_dir_info = g_file_query_info (previous_app_id_dir,
 4118                                                         G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
 4119                                                         G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
 4120                                                         G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
 4121                                                         cancellable,
 4122                                                         &local_error);
 4123           /* Warn about the migration failures, but don't make them fatal, then you can never run the app */
 4124           if (previous_app_id_dir_info == NULL)
 4125             {
 4126               if  (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && do_migrate)
 4127                 {
 4128                   g_warning (_("Failed to migrate from %s: %s"), flatpak_file_get_path_cached (previous_app_id_dir),
 4129                              local_error->message);
 4130                   do_migrate = FALSE; /* Don't migrate older things, they are likely symlinks to the thing that we failed on */
 4131                 }
 4132 
 4133               g_clear_error (&local_error);
 4134               continue;
 4135             }
 4136 
 4137           if (do_migrate)
 4138             {
 4139               do_migrate = FALSE; /* Don't migrate older things, they are likely symlinks to this dir */
 4140 
 4141               if (!flatpak_file_rename (previous_app_id_dir, real_app_id_dir, cancellable, &local_error))
 4142                 {
 4143                   g_warning (_("Failed to migrate old app data directory %s to new name %s: %s"),
 4144                              flatpak_file_get_path_cached (previous_app_id_dir), app_id,
 4145                              local_error->message);
 4146                 }
 4147               else
 4148                 {
 4149                   /* Leave a symlink in place of the old data dir */
 4150                   if (!g_file_make_symbolic_link (previous_app_id_dir, app_id, cancellable, &local_error))
 4151                     {
 4152                       g_warning (_("Failed to create symlink while migrating %s: %s"),
 4153                                  flatpak_file_get_path_cached (previous_app_id_dir),
 4154                                  local_error->message);
 4155                     }
 4156                 }
 4157             }
 4158 
 4159           /* Give app access to this old dir */
 4160           g_ptr_array_add (previous_app_id_dirs, g_steal_pointer (&previous_app_id_dir));
 4161         }
 4162 
 4163       if (!flatpak_ensure_data_dir (real_app_id_dir, cancellable, error))
 4164         return FALSE;
 4165 
 4166       if (!sandboxed)
 4167         app_id_dir = g_object_ref (real_app_id_dir);
 4168     }
 4169 
 4170   if (custom_app_path != NULL)
 4171     {
 4172       if (strcmp (custom_app_path, "") == 0)
 4173         app_files = NULL;
 4174       else
 4175         app_files = g_file_new_for_path (custom_app_path);
 4176 
 4177       /* Mount the original app below here */
 4178       app_target_path = "/run/parent/app";
 4179     }
 4180   else if (original_app_files != NULL)
 4181     {
 4182       app_files = g_object_ref (original_app_files);
 4183     }
 4184 
 4185   flatpak_run_apply_env_default (bwrap, use_ld_so_cache);
 4186   flatpak_run_apply_env_vars (bwrap, app_context);
 4187   flatpak_run_apply_env_prompt (bwrap, app_id);
 4188 
 4189   if (real_app_id_dir)
 4190     {
 4191       g_autoptr(GFile) sandbox_dir = g_file_get_child (real_app_id_dir, "sandbox");
 4192       flatpak_bwrap_set_env (bwrap, "FLATPAK_SANDBOX_DIR", flatpak_file_get_path_cached (sandbox_dir), TRUE);
 4193     }
 4194 
 4195   flatpak_bwrap_add_args (bwrap,
 4196                           "--ro-bind", flatpak_file_get_path_cached (runtime_files), "/usr",
 4197                           NULL);
 4198 
 4199   if (runtime_files == original_runtime_files)
 4200     {
 4201       /* All true Flatpak runtimes have files/.ref */
 4202       flatpak_bwrap_add_args (bwrap,
 4203                               "--lock-file", "/usr/.ref",
 4204                               NULL);
 4205     }
 4206   else
 4207     {
 4208       g_autoptr(GFile) runtime_child = NULL;
 4209 
 4210       runtime_child = g_file_get_child (runtime_files, ".ref");
 4211 
 4212       /* Lock ${usr}/.ref if it exists */
 4213       if (g_file_query_exists (runtime_child, NULL))
 4214         flatpak_bwrap_add_args (bwrap,
 4215                                 "--lock-file", "/usr/.ref",
 4216                                 NULL);
 4217 
 4218       /* Put the real Flatpak runtime in /run/parent, so that the
 4219        * replacement /usr can have symlinks into /run/parent in order
 4220        * to use the Flatpak runtime's graphics drivers etc. if desired */
 4221       flatpak_bwrap_add_args (bwrap,
 4222                               "--ro-bind",
 4223                               flatpak_file_get_path_cached (original_runtime_files),
 4224                               "/run/parent/usr",
 4225                               "--lock-file", "/run/parent/usr/.ref",
 4226                               NULL);
 4227       flatpak_run_setup_usr_links (bwrap, original_runtime_files,
 4228                                    "/run/parent");
 4229 
 4230       g_clear_object (&runtime_child);
 4231       runtime_child = g_file_get_child (original_runtime_files, "etc");
 4232 
 4233       if (g_file_query_exists (runtime_child, NULL))
 4234         flatpak_bwrap_add_args (bwrap,
 4235                                 "--symlink", "usr/etc", "/run/parent/etc",
 4236                                 NULL);
 4237     }
 4238 
 4239   if (app_files != NULL)
 4240     {
 4241       flatpak_bwrap_add_args (bwrap,
 4242                               "--ro-bind", flatpak_file_get_path_cached (app_files), "/app",
 4243                               NULL);
 4244 
 4245       if (app_files == original_app_files)
 4246         {
 4247           /* All true Flatpak apps have files/.ref */
 4248           flatpak_bwrap_add_args (bwrap,
 4249                                   "--lock-file", "/app/.ref",
 4250                                   NULL);
 4251         }
 4252       else
 4253         {
 4254           g_autoptr(GFile) app_child = NULL;
 4255 
 4256           app_child = g_file_get_child (app_files, ".ref");
 4257 
 4258           /* Lock ${app}/.ref if it exists */
 4259           if (g_file_query_exists (app_child, NULL))
 4260             flatpak_bwrap_add_args (bwrap,
 4261                                     "--lock-file", "/app/.ref",
 4262                                     NULL);
 4263         }
 4264     }
 4265   else
 4266     {
 4267       flatpak_bwrap_add_args (bwrap,
 4268                               "--dir", "/app",
 4269                               NULL);
 4270     }
 4271 
 4272   if (original_app_files != NULL && app_files != original_app_files)
 4273     {
 4274       /* Put the real Flatpak app in /run/parent/app */
 4275       flatpak_bwrap_add_args (bwrap,
 4276                               "--ro-bind",
 4277                               flatpak_file_get_path_cached (original_app_files),
 4278                               "/run/parent/app",
 4279                               "--lock-file", "/run/parent/app/.ref",
 4280                               NULL);
 4281     }
 4282 
 4283   if (metakey != NULL &&
 4284       !flatpak_run_add_extension_args (bwrap, metakey, app_ref,
 4285                                        use_ld_so_cache, app_target_path,
 4286                                        &app_extensions, &app_ld_path,
 4287                                        cancellable, error))
 4288     return FALSE;
 4289 
 4290   if (!flatpak_run_add_extension_args (bwrap, runtime_metakey, runtime_ref,
 4291                                        use_ld_so_cache, runtime_target_path,
 4292                                        &runtime_extensions, &runtime_ld_path,
 4293                                        cancellable, error))
 4294     return FALSE;
 4295 
 4296   if (custom_usr_path == NULL)
 4297     flatpak_run_extend_ld_path (bwrap, NULL, runtime_ld_path);
 4298 
 4299   if (custom_app_path == NULL)
 4300     flatpak_run_extend_ld_path (bwrap, app_ld_path, NULL);
 4301 
 4302   runtime_ld_so_conf = g_file_resolve_relative_path (runtime_files, "etc/ld.so.conf");
 4303   if (lstat (flatpak_file_get_path_cached (runtime_ld_so_conf), &s) == 0)
 4304     generate_ld_so_conf = S_ISREG (s.st_mode) && s.st_size == 0;
 4305 
 4306   /* At this point we have the minimal argv set up, with just the app, runtime and extensions.
 4307      We can reuse this to generate the ld.so.cache (if needed) */
 4308   if (use_ld_so_cache)
 4309     {
 4310       checksum = calculate_ld_cache_checksum (app_deploy_data, runtime_deploy_data,
 4311                                               app_extensions, runtime_extensions);
 4312       ld_so_fd = regenerate_ld_cache (bwrap->argv,
 4313                                       bwrap->fds,
 4314                                       app_id_dir,
 4315                                       checksum,
 4316                                       runtime_files,
 4317                                       generate_ld_so_conf,
 4318                                       cancellable, error);
 4319       if (ld_so_fd == -1)
 4320         return FALSE;
 4321       flatpak_bwrap_add_fd (bwrap, ld_so_fd);
 4322     }
 4323 
 4324   flags |= flatpak_context_get_run_flags (app_context);
 4325 
 4326   if (!flatpak_run_setup_base_argv (bwrap, runtime_files, app_id_dir, app_arch, flags, error))
 4327     return FALSE;
 4328 
 4329   if (generate_ld_so_conf)
 4330     {
 4331       if (!add_ld_so_conf (bwrap, error))
 4332         return FALSE;
 4333     }
 4334 
 4335   if (ld_so_fd != -1)
 4336     {
 4337       /* Don't add to fd_array, its already there */
 4338       flatpak_bwrap_add_arg (bwrap, "--ro-bind-data");
 4339       flatpak_bwrap_add_arg_printf (bwrap, "%d", ld_so_fd);
 4340       flatpak_bwrap_add_arg (bwrap, "/etc/ld.so.cache");
 4341     }
 4342 
 4343   if (!flatpak_run_add_app_info_args (bwrap,
 4344                                       app_files, original_app_files, app_deploy_data, app_extensions,
 4345                                       runtime_files, original_runtime_files, runtime_deploy_data, runtime_extensions,
 4346                                       app_id, flatpak_decomposed_get_branch (app_ref),
 4347                                       runtime_ref, app_id_dir, app_context, extra_context,
 4348                                       sandboxed, FALSE, flags & FLATPAK_RUN_FLAG_DEVEL,
 4349                                       &app_info_path, instance_id_fd, &instance_id_host_dir,
 4350                                       error))
 4351     return FALSE;
 4352 
 4353   if (!sandboxed)
 4354     {
 4355       if (!flatpak_instance_ensure_per_app_dir (app_id,
 4356                                                 &per_app_dir_lock_fd,
 4357                                                 &per_app_dir_lock_path,
 4358                                                 error))
 4359         return FALSE;
 4360 
 4361       if (!flatpak_instance_ensure_per_app_xdg_runtime_dir (app_id,
 4362                                                             per_app_dir_lock_fd,
 4363                                                             &shared_xdg_runtime_dir,
 4364                                                             error))
 4365         return FALSE;
 4366 
 4367       flatpak_bwrap_add_arg (bwrap, "--bind");
 4368       flatpak_bwrap_add_arg (bwrap, shared_xdg_runtime_dir);
 4369       flatpak_bwrap_add_arg_printf (bwrap, "/run/user/%d", getuid ());
 4370     }
 4371 
 4372   if (!flatpak_run_add_dconf_args (bwrap, app_id, metakey, error))
 4373     return FALSE;
 4374 
 4375   if (!sandboxed && !(flags & FLATPAK_RUN_FLAG_NO_DOCUMENTS_PORTAL))
 4376     add_document_portal_args (bwrap, app_id, &doc_mount_path);
 4377 
 4378   if (!flatpak_run_add_environment_args (bwrap, app_info_path, flags,
 4379                                          app_id, app_context, app_id_dir, previous_app_id_dirs,
 4380                                          per_app_dir_lock_fd,
 4381                                          &exports, cancellable, error))
 4382     return FALSE;
 4383 
 4384   if (per_app_dir_lock_path != NULL)
 4385     {
 4386       static const char lock[] = "/run/flatpak/per-app-dirs-ref";
 4387 
 4388       flatpak_bwrap_add_args (bwrap,
 4389                               "--ro-bind", per_app_dir_lock_path, lock,
 4390                               "--lock-file", lock,
 4391                               NULL);
 4392     }
 4393 
 4394   if ((app_context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) != 0)
 4395     flatpak_run_add_resolved_args (bwrap);
 4396 
 4397   flatpak_run_add_journal_args (bwrap);
 4398   add_font_path_args (bwrap);
 4399   add_icon_path_args (bwrap);
 4400 
 4401   flatpak_bwrap_add_args (bwrap,
 4402                           /* Not in base, because we don't want this for flatpak build */
 4403                           "--symlink", "/app/lib/debug/source", "/run/build",
 4404                           "--symlink", "/usr/lib/debug/source", "/run/build-runtime",
 4405                           NULL);
 4406 
 4407   if (cwd)
 4408     flatpak_bwrap_add_args (bwrap, "--chdir", cwd, NULL);
 4409 
 4410   if (parent_expose_pids || parent_share_pids)
 4411     {
 4412       g_autofree char *userns_path = NULL;
 4413       g_autofree char *pidns_path = NULL;
 4414       g_autofree char *userns2_path = NULL;
 4415       int userns_fd, userns2_fd, pidns_fd;
 4416 
 4417       if (parent_pid == 0)
 4418         return flatpak_fail (error, "No parent pid specified");
 4419 
 4420       userns_path = g_strdup_printf ("/proc/%d/root/run/.userns", parent_pid);
 4421 
 4422       userns_fd = open_namespace_fd_if_needed (userns_path, "/proc/self/ns/user");
 4423       if (userns_fd != -1)
 4424         {
 4425           flatpak_bwrap_add_args_data_fd (bwrap, "--userns", userns_fd, NULL);
 4426 
 4427           userns2_path = g_strdup_printf ("/proc/%d/ns/user", parent_pid);
 4428           userns2_fd = open_namespace_fd_if_needed (userns2_path, userns_path);
 4429           if (userns2_fd != -1)
 4430             flatpak_bwrap_add_args_data_fd (bwrap, "--userns2", userns2_fd, NULL);
 4431         }
 4432 
 4433       pidns_path = g_strdup_printf ("/proc/%d/ns/pid", parent_pid);
 4434       pidns_fd = open (pidns_path, O_RDONLY|O_CLOEXEC);
 4435       if (pidns_fd != -1)
 4436         flatpak_bwrap_add_args_data_fd (bwrap, "--pidns", pidns_fd, NULL);
 4437     }
 4438 
 4439   flatpak_bwrap_populate_runtime_dir (bwrap, shared_xdg_runtime_dir);
 4440 
 4441   if (custom_command)
 4442     {
 4443       command = custom_command;
 4444     }
 4445   else if (metakey)
 4446     {
 4447       default_command = g_key_file_get_string (metakey,
 4448                                                FLATPAK_METADATA_GROUP_APPLICATION,
 4449                                                FLATPAK_METADATA_KEY_COMMAND,
 4450                                                &my_error);
 4451       if (my_error)
 4452         {
 4453           g_propagate_error (error, g_steal_pointer (&my_error));
 4454           return FALSE;
 4455         }
 4456       command = default_command;
 4457     }
 4458 
 4459   flatpak_bwrap_sort_envp (bwrap);
 4460   flatpak_bwrap_envp_to_args (bwrap);
 4461 
 4462   if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
 4463     return FALSE;
 4464 
 4465   flatpak_bwrap_add_arg (bwrap, command);
 4466 
 4467   if (!add_rest_args (bwrap, app_id,
 4468                       exports, (flags & FLATPAK_RUN_FLAG_FILE_FORWARDING) != 0,
 4469                       doc_mount_path,
 4470                       args, n_args, error))
 4471     return FALSE;
 4472 
 4473   /* Hold onto the lock until we execute bwrap */
 4474   flatpak_bwrap_add_noinherit_fd (bwrap, glnx_steal_fd (&per_app_dir_lock_fd));
 4475 
 4476   flatpak_bwrap_finish (bwrap);
 4477 
 4478   commandline = flatpak_quote_argv ((const char **) bwrap->argv->pdata, -1);
 4479   g_debug ("Running '%s'", commandline);
 4480 
 4481   if ((flags & FLATPAK_RUN_FLAG_BACKGROUND) != 0)
 4482     {
 4483       GPid child_pid;
 4484       char pid_str[64];
 4485       g_autofree char *pid_path = NULL;
 4486       GSpawnFlags spawn_flags;
 4487 
 4488       spawn_flags = G_SPAWN_SEARCH_PATH;
 4489       if (flags & FLATPAK_RUN_FLAG_DO_NOT_REAP)
 4490         spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD;
 4491 
 4492       /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
 4493       spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
 4494 
 4495       /* flatpak_bwrap_envp_to_args() moved the environment variables to
 4496        * be set into --setenv instructions in argv, so the environment
 4497        * in which the bwrap command runs must be empty. */
 4498       g_assert (bwrap->envp != NULL);
 4499       g_assert (bwrap->envp[0] == NULL);
 4500 
 4501       if (!g_spawn_async (NULL,
 4502                           (char **) bwrap->argv->pdata,
 4503                           bwrap->envp,
 4504                           spawn_flags,
 4505                           flatpak_bwrap_child_setup_cb, bwrap->fds,
 4506                           &child_pid,
 4507                           error))
 4508         return FALSE;
 4509 
 4510       g_snprintf (pid_str, sizeof (pid_str), "%d", child_pid);
 4511       pid_path = g_build_filename (instance_id_host_dir, "pid", NULL);
 4512       g_file_set_contents (pid_path, pid_str, -1, NULL);
 4513     }
 4514   else
 4515     {
 4516       char pid_str[64];
 4517       g_autofree char *pid_path = NULL;
 4518 
 4519       g_snprintf (pid_str, sizeof (pid_str), "%d", getpid ());
 4520       pid_path = g_build_filename (instance_id_host_dir, "pid", NULL);
 4521       g_file_set_contents (pid_path, pid_str, -1, NULL);
 4522 
 4523       /* Ensure we unset O_CLOEXEC for marked fds and rewind fds as needed.
 4524        * Note that this does not close fds that are not already marked O_CLOEXEC, because
 4525        * we do want to allow inheriting fds into flatpak run. */
 4526       flatpak_bwrap_child_setup (bwrap->fds, FALSE);
 4527 
 4528       /* flatpak_bwrap_envp_to_args() moved the environment variables to
 4529        * be set into --setenv instructions in argv, so the environment
 4530        * in which the bwrap command runs must be empty. */
 4531       g_assert (bwrap->envp != NULL);
 4532       g_assert (bwrap->envp[0] == NULL);
 4533 
 4534       if (execvpe (flatpak_get_bwrap (), (char **) bwrap->argv->pdata, bwrap->envp) == -1)
 4535         {
 4536           g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
 4537                                _("Unable to start app"));
 4538           return FALSE;
 4539         }
 4540       /* Not actually reached... */
 4541     }
 4542 
 4543   if (instance_dir_out)
 4544     *instance_dir_out = g_steal_pointer (&instance_id_host_dir);
 4545 
 4546   return TRUE;
 4547 }