Hi all,

This patchset enables shmoverride/qubes-guid daemon support
for running multiple X servers concurrently. It allows
having one (or some) appvm GUIs under X server(s) that
runs under other virtual console. The need for this has
come up at mailing list from time to time, e.g., opening
windows VM in a second virtual console [1] or an ability to
connect appvm directly to login screen by having a custom
xsession for that [2]. The custom xsession should also allow
limited multi-user support (however, Qubes RPC support for
that kind of usecase is currently very limited/does not
exits for more than one X server but denying cross-user
RPCs manually might be enough for start).

If anyone has any thought / directions on how / what
should be implement for better Qubes RPC support for
the multi-user/X server case, please share.

This patchset is tested in R3.2. There are two patches for
gui-daemon (the first is just to introduce a variable for
shm.id filename and the second is for the actual logic changes).
The gui-daemon changes in this patchset depends on 3c86bef963
(Move /var/run/shm.id to /var/run/qubes/shm.id) which
is not in R3.2 (I cherrypicked it to R3.2 to make master
integration easier). The DISPLAY value is extracted from
/proc/self/cmdline for the shmoverride constructor which
I hope is an acceptable solution. I'm unsure what the code
should do if there is no DISPLAY found on the cmdline,
perhaps it should just bail out instead of returning :0
as it does in the current changeset?

There are two patches for core-admin. The second patch
for core-admin is not strictly necessary for R3.2 but
in order to make it easier to turn it master compatible
I added the second cludge change for testing in a more
master like codebase that supports migration from one
shm.id path to another. If requested, I can provide the
master based patch with the end result, however, testing
with master/R4.0 does not seem realistic for me at the
moment (is that required?).


--
 i.


[1] https://groups.google.com/forum/#!topic/qubes-devel/6inz6v2ByNo
[2] https://groups.google.com/forum/#!msg/qubes-devel/XnTly2JNfPw/BppN_D5vn4kJ
From c9c082603360e081b671f0d655d41b8014583f60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= <[email protected]>
Date: Fri, 9 Dec 2016 00:15:42 +0200
Subject: [PATCH 1/2] Move shm.id file to variables

Should introduce no functional changes
---
 gui-daemon/xside.c        |  9 ++++++---
 gui-daemon/xside.h        |  1 -
 include/shmid.h           | 23 +++++++++++++++++++++++
 shmoverride/shmoverride.c | 28 ++++++++++++++++++----------
 4 files changed, 47 insertions(+), 14 deletions(-)
 create mode 100644 include/shmid.h

diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c
index 4b2bc99..e5770fc 100644
--- a/gui-daemon/xside.c
+++ b/gui-daemon/xside.c
@@ -56,6 +56,7 @@
 #include "error.h"
 #include "png.h"
 #include "trayicon.h"
+#include "shmid.h"
 
 /* some configuration */
 
@@ -3146,6 +3147,7 @@ int main(int argc, char **argv)
 	int pipe_notify[2];
 	char dbg_log[256];
 	char dbg_log_old[256];
+	char shmid_filename[SHMID_FILENAME_LEN];
 	int logfd;
 	char cmd_tmp[256];
 	struct stat stat_buf;
@@ -3157,6 +3159,7 @@ int main(int argc, char **argv)
 	parse_config(&ghandles);
 	/* parse cmdline, possibly overriding values from config */
 	parse_cmdline(&ghandles, argc, argv);
+	snprintf(shmid_filename, SHMID_FILENAME_LEN, SHMID_FILENAME);
 	get_boot_lock(ghandles.domid);
 
 	if (!ghandles.nofork) {
@@ -3179,11 +3182,11 @@ int main(int argc, char **argv)
 
 	// inside the daemonized process...
 	if (!ghandles.invisible) {
-		f = fopen(GUID_SHMID_FILE, "r");
+		f = fopen(shmid_filename, "r");
 		if (!f) {
 			fprintf(stderr,
 					"Missing %s; run X with preloaded shmoverride\n",
-					GUID_SHMID_FILE);
+					shmid_filename);
 			exit(1);
 		}
 		fscanf(f, "%d", &ghandles.cmd_shmid);
@@ -3193,7 +3196,7 @@ int main(int argc, char **argv)
 			fprintf(stderr,
 					"Invalid or stale shm id 0x%x in %s\n",
 					ghandles.cmd_shmid,
-					GUID_SHMID_FILE);
+					shmid_filename);
 			exit(1);
 		}
 	}
diff --git a/gui-daemon/xside.h b/gui-daemon/xside.h
index 819f005..7659c45 100644
--- a/gui-daemon/xside.h
+++ b/gui-daemon/xside.h
@@ -30,7 +30,6 @@
 #define QREXEC_POLICY_PATH "/usr/lib/qubes/qrexec-policy"
 #define GUID_CONFIG_FILE "/etc/qubes/guid.conf"
 #define GUID_CONFIG_DIR "/etc/qubes"
-#define GUID_SHMID_FILE "/var/run/qubes/shm.id"
 /* this makes any X11 error fatal (i.e. cause exit(1)). This behavior was the
  * case for a long time before introducing this option, so nothing really have
  * changed  */
diff --git a/include/shmid.h b/include/shmid.h
new file mode 100644
index 0000000..d4def66
--- /dev/null
+++ b/include/shmid.h
@@ -0,0 +1,23 @@
+/*
+ * The Qubes OS Project, http://www.qubes-os.org
+ *
+ * Copyright (C) 2016  Ilpo Järvinen  <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#define SHMID_FILENAME	"/var/run/qubes/shm.id"
+#define SHMID_FILENAME_LEN	(sizeof(SHMID_FILENAME_PREFIX))
diff --git a/shmoverride/shmoverride.c b/shmoverride/shmoverride.c
index 494eaf2..e71412b 100644
--- a/shmoverride/shmoverride.c
+++ b/shmoverride/shmoverride.c
@@ -37,6 +37,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include "list.h"
+#include "shmid.h"
 #include <qubes-gui-protocol.h>
 
 static void *(*real_shmat) (int shmid, const void *shmaddr, int shmflg);
@@ -52,6 +53,8 @@ static xc_interface *xc_hnd;
 static int xc_hnd;
 #endif
 static int list_len;
+static char __shmid_filename[SHMID_FILENAME_LEN];
+static char *shmid_filename = NULL;
 
 void *shmat(int shmid, const void *shmaddr, int shmflg)
 {
@@ -116,7 +119,6 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf)
     return 0;
 }
 
-#define SHMID_FILENAME "/var/run/qubes/shm.id"
 int __attribute__ ((constructor)) initfunc()
 {
     int idfd, len;
@@ -141,9 +143,12 @@ int __attribute__ ((constructor)) initfunc()
         perror("shmoverride xc_interface_open");
         return 0;	//allow it to run when not under Xen
     }
-    idfd = open(SHMID_FILENAME, O_WRONLY | O_CREAT | O_EXCL, 0600);
+    snprintf(__shmid_filename, SHMID_FILENAME_LEN, SHMID_FILENAME);
+    shmid_filename = __shmid_filename;
+    idfd = open(shmid_filename, O_WRONLY | O_CREAT | O_EXCL, 0600);
     if (idfd < 0) {
-        perror("shmoverride creating " SHMID_FILENAME);
+        fprintf(stderr, "shmoverride creating %s: %s\n",
+            shmid_filename, strerror(errno));
         xc_interface_close(xc_hnd);
         return 0;
     }
@@ -151,25 +156,27 @@ int __attribute__ ((constructor)) initfunc()
         shmget(IPC_PRIVATE, SHM_CMD_NUM_PAGES * 4096,
                 IPC_CREAT | 0700);
     if (local_shmid == -1) {
-        unlink(SHMID_FILENAME);
+        unlink(shmid_filename);
         perror("shmoverride shmget");
         exit(1);
     }
     sprintf(idbuf, "%d", local_shmid);
     len = strlen(idbuf);
     if (write(idfd, idbuf, len) != len) {
-        unlink(SHMID_FILENAME);
-        perror("shmoverride writing " SHMID_FILENAME);
+        unlink(shmid_filename);
+        fprintf(stderr, "shmoverride writing %s: %s\n",
+            shmid_filename, strerror(errno));
         exit(1);
     }
     if (close(idfd) < 0) {
-        unlink(SHMID_FILENAME);
-        perror("shmoverride closing " SHMID_FILENAME);
+        unlink(shmid_filename);
+        fprintf(stderr, "shmoverride closing %s: %s\n",
+            shmid_filename, strerror(errno));
         exit(1);
     }
     cmd_pages = real_shmat(local_shmid, 0, 0);
     if (!cmd_pages) {
-        unlink(SHMID_FILENAME);
+        unlink(shmid_filename);
         perror("real_shmat");
         exit(1);
     }
@@ -182,7 +189,8 @@ int __attribute__ ((destructor)) descfunc()
     if (cmd_pages) {
         real_shmdt(cmd_pages);
         real_shmctl(local_shmid, IPC_RMID, 0);
-        unlink(SHMID_FILENAME);
+        if (shmid_filename != NULL)
+            unlink(shmid_filename);
     }
     return 0;
 }
-- 
2.5.5

From 45dfeac322cbe07a864db81f1757b1d315401772 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= <[email protected]>
Date: Fri, 9 Dec 2016 00:40:35 +0200
Subject: [PATCH 2/2] Use shm.id.$DISPLAY to allow more than single X server

---
 gui-daemon/xside.c        |  9 +++++++-
 gui-daemon/xside.h        |  2 +-
 include/shmid.h           |  6 ++++--
 shmoverride/README        |  6 +++---
 shmoverride/shmoverride.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c
index e5770fc..3114e88 100644
--- a/gui-daemon/xside.c
+++ b/gui-daemon/xside.c
@@ -3151,6 +3151,7 @@ int main(int argc, char **argv)
 	int logfd;
 	char cmd_tmp[256];
 	struct stat stat_buf;
+	char *display_str;
 
 	load_default_config_values(&ghandles);
 	/* get the VM name to read the right section in config file */
@@ -3159,7 +3160,6 @@ int main(int argc, char **argv)
 	parse_config(&ghandles);
 	/* parse cmdline, possibly overriding values from config */
 	parse_cmdline(&ghandles, argc, argv);
-	snprintf(shmid_filename, SHMID_FILENAME_LEN, SHMID_FILENAME);
 	get_boot_lock(ghandles.domid);
 
 	if (!ghandles.nofork) {
@@ -3182,6 +3182,13 @@ int main(int argc, char **argv)
 
 	// inside the daemonized process...
 	if (!ghandles.invisible) {
+		display_str = getenv("DISPLAY");
+		if (display_str == NULL) {
+			fprintf(stderr, "No DISPLAY in environment\n");
+			exit(1);
+		}
+		snprintf(shmid_filename, SHMID_FILENAME_LEN,
+			 SHMID_FILENAME_PREFIX "%s", display_str);
 		f = fopen(shmid_filename, "r");
 		if (!f) {
 			fprintf(stderr,
diff --git a/gui-daemon/xside.h b/gui-daemon/xside.h
index 7659c45..485eb02 100644
--- a/gui-daemon/xside.h
+++ b/gui-daemon/xside.h
@@ -131,7 +131,7 @@ struct _global_handles {
 	Atom frame_extents; /* Atom: _NET_FRAME_EXTENTS */
 	/* shared memory handling */
 	struct shm_cmd *shmcmd;	/* shared memory with Xorg */
-	uint32_t cmd_shmid;		/* shared memory id - received from shmoverride.so through shm.id file */
+	uint32_t cmd_shmid;		/* shared memory id - received from shmoverride.so through shm.id.$DISPLAY file */
 	int inter_appviewer_lock_fd; /* FD of lock file used to synchronize shared memory access */
 	/* Client VM parameters */
 	libvchan_t *vchan;
diff --git a/include/shmid.h b/include/shmid.h
index d4def66..c0d8840 100644
--- a/include/shmid.h
+++ b/include/shmid.h
@@ -19,5 +19,7 @@
  *
  */
 
-#define SHMID_FILENAME	"/var/run/qubes/shm.id"
-#define SHMID_FILENAME_LEN	(sizeof(SHMID_FILENAME_PREFIX))
+
+#define SHMID_DISPLAY_MAXLEN	20
+#define SHMID_FILENAME_PREFIX	"/var/run/qubes/shm.id."
+#define SHMID_FILENAME_LEN	(sizeof(SHMID_FILENAME_PREFIX) + SHMID_DISPLAY_MAXLEN)
diff --git a/shmoverride/README b/shmoverride/README
index e2a7300..98547be 100644
--- a/shmoverride/README
+++ b/shmoverride/README
@@ -5,9 +5,9 @@ instead of attaching regular shared memory, memory from a foreign domain is
 attached via xc_map_foreign_pages. This mechanism is used to map composition
 buffers from a foreign domain into Xorg server.
 	During its init, shmoverride.so creates a shared memory segment
-(cmd_pages) and writes its shmid to /var/run/qubes/shm.id. All instances of
-qubes_guid map this segment and communicate with shmoverride.so code by
-setting its fields. When qubes_guid wants its
+(cmd_pages) and writes its shmid to /var/run/qubes/shm.id.$DISPLAY. All
+instances of qubes_guid map this segment and communicate with shmoverride.so
+code by setting its fields. When qubes_guid wants its
 XShmAttach(...synth_shmid...) call to be handled by shmoverride.so, it sets the 
 "shmid" field in the cmd_pages shared memory segment to synth_shmid just
 before calling XShmAttach. Function shmat (implemented in shmoverride.so)
diff --git a/shmoverride/shmoverride.c b/shmoverride/shmoverride.c
index e71412b..a196b6b 100644
--- a/shmoverride/shmoverride.c
+++ b/shmoverride/shmoverride.c
@@ -55,6 +55,7 @@ static int xc_hnd;
 static int list_len;
 static char __shmid_filename[SHMID_FILENAME_LEN];
 static char *shmid_filename = NULL;
+static char display_str[SHMID_DISPLAY_MAXLEN+1] = "";
 
 void *shmat(int shmid, const void *shmaddr, int shmflg)
 {
@@ -119,6 +120,52 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf)
     return 0;
 }
 
+int get_display()
+{
+    int fd;
+    ssize_t res;
+    char ch;
+    int in_arg = 0;
+
+    fd = open("/proc/self/cmdline", O_RDONLY);
+    if (fd < 0) {
+        perror("cmdline open");
+        return -1;
+    }
+
+    while(1) {
+        res = read(fd, &ch, 1);
+        if (res < 0) {
+            perror("cmdline read");
+            return -1;
+        }
+        if (res == 0)
+            break;
+
+        if (in_arg == 0 && ch != ':')
+            in_arg = -1;
+        if (in_arg >= 0) {
+            if (in_arg >= SHMID_DISPLAY_MAXLEN)
+                break;
+            if (in_arg > 0 && (ch < '0' || ch > '9'))
+                break;
+            display_str[in_arg++] = ch;
+            display_str[in_arg] = '\0';
+        }
+        if (ch == '\0')
+            in_arg = 0;
+    }
+    close(fd);
+
+    if (display_str[0] != ':') {
+        display_str[0] = ':';
+        display_str[1] = '0';
+        display_str[2] = '\0';
+    }
+
+    return 0;
+}
+
 int __attribute__ ((constructor)) initfunc()
 {
     int idfd, len;
@@ -143,7 +190,11 @@ int __attribute__ ((constructor)) initfunc()
         perror("shmoverride xc_interface_open");
         return 0;	//allow it to run when not under Xen
     }
-    snprintf(__shmid_filename, SHMID_FILENAME_LEN, SHMID_FILENAME);
+
+    if (get_display())
+        exit(1);
+    snprintf(__shmid_filename, SHMID_FILENAME_LEN,
+        SHMID_FILENAME_PREFIX "%s", display_str);
     shmid_filename = __shmid_filename;
     idfd = open(shmid_filename, O_WRONLY | O_CREAT | O_EXCL, 0600);
     if (idfd < 0) {
-- 
2.5.5

From d82eb37aa92c2d2cb431080de73e2188543ba084 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= <[email protected]>
Date: Mon, 19 Dec 2016 22:22:23 +0200
Subject: [PATCH 1/2] shm.id path updated to include $DISPLAY

---
 core-modules/000QubesVm.py          | 11 ++++++-----
 core-modules/01QubesDisposableVm.py |  4 ++--
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/core-modules/000QubesVm.py b/core-modules/000QubesVm.py
index 8e9978a..01f1941 100644
--- a/core-modules/000QubesVm.py
+++ b/core-modules/000QubesVm.py
@@ -2012,16 +2012,17 @@ class QubesVm(object):
             # Run GUI daemon in "invisible" mode, so applications started by
             # prerun script will not disturb the user
             extra_guid_args = ['-I']
-        elif not os.path.exists('/var/run/shm.id'):
-            # Start GUI daemon only when shmoverride is loaded; unless
-            # preparing DispVM, where it isn't needed because of "invisible"
-            # mode
-            start_guid = False
         if start_guid and 'DISPLAY' not in os.environ:
             if verbose:
                 print >> sys.stderr, \
                     "WARNING: not starting GUI, because DISPLAY not set"
             start_guid = False
+        else:
+            if not preparing_dvm and not os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")):
+                # Start GUI daemon only when shmoverride is loaded; unless
+                # preparing DispVM, where it isn't needed because of "invisible"
+                # mode
+                start_guid = False
 
         if start_guid:
             self.start_guid(verbose=verbose, notify_function=notify_function,
diff --git a/core-modules/01QubesDisposableVm.py b/core-modules/01QubesDisposableVm.py
index 4e3ebdd..c3fb963 100644
--- a/core-modules/01QubesDisposableVm.py
+++ b/core-modules/01QubesDisposableVm.py
@@ -222,7 +222,7 @@ class QubesDisposableVm(QubesVm):
         if qmemman_present:
             qmemman_client.close()
 
-        if kwargs.get('start_guid', True) and os.path.exists('/var/run/shm.id'):
+        if kwargs.get('start_guid', True) and 'DISPLAY' in os.environ and os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")):
             self.start_guid(verbose=verbose, before_qrexec=True,
                     notify_function=kwargs.get('notify_function', None))
 
@@ -230,7 +230,7 @@ class QubesDisposableVm(QubesVm):
                 notify_function=kwargs.get('notify_function', None))
         print >>sys.stderr, "time=%s, qrexec done" % (str(time.time()))
 
-        if kwargs.get('start_guid', True) and os.path.exists('/var/run/shm.id'):
+        if kwargs.get('start_guid', True) and 'DISPLAY' in os.environ and os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")):
             self.start_guid(verbose=verbose,
                     notify_function=kwargs.get('notify_function', None))
         print >>sys.stderr, "time=%s, guid done" % (str(time.time()))
-- 
2.5.5

From 6eee39e041da02a8c6a60cfe24b4337a4804ebbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= <[email protected]>
Date: Thu, 22 Dec 2016 11:56:24 +0200
Subject: [PATCH 2/2] Match better to fallback code in master branch

---
 core-modules/000QubesVm.py          | 5 ++++-
 core-modules/01QubesDisposableVm.py | 9 +++++++--
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/core-modules/000QubesVm.py b/core-modules/000QubesVm.py
index 01f1941..b92869b 100644
--- a/core-modules/000QubesVm.py
+++ b/core-modules/000QubesVm.py
@@ -2018,7 +2018,10 @@ class QubesVm(object):
                     "WARNING: not starting GUI, because DISPLAY not set"
             start_guid = False
         else:
-            if not preparing_dvm and not os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")):
+            if not preparing_dvm \
+                    and not os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")) \
+                    and not os.path.exists('/var/run/qubes/shm.id') \
+                    and not os.path.exists('/var/run/shm.id'):
                 # Start GUI daemon only when shmoverride is loaded; unless
                 # preparing DispVM, where it isn't needed because of "invisible"
                 # mode
diff --git a/core-modules/01QubesDisposableVm.py b/core-modules/01QubesDisposableVm.py
index c3fb963..42cb94e 100644
--- a/core-modules/01QubesDisposableVm.py
+++ b/core-modules/01QubesDisposableVm.py
@@ -40,6 +40,7 @@ except ImportError:
     pass
 
 DISPID_STATE_FILE = '/var/run/qubes/dispid'
+GUID_SHMID_FILE = ['/var/run/qubes/shm.id', '/var/run/shm.id']
 
 class QubesDisposableVm(QubesVm):
     """
@@ -222,7 +223,9 @@ class QubesDisposableVm(QubesVm):
         if qmemman_present:
             qmemman_client.close()
 
-        if kwargs.get('start_guid', True) and 'DISPLAY' in os.environ and os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")):
+        if kwargs.get('start_guid', True) and 'DISPLAY' in os.environ and \
+                (os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")) \
+                 or any(os.path.exists(x) for x in GUID_SHMID_FILE)):
             self.start_guid(verbose=verbose, before_qrexec=True,
                     notify_function=kwargs.get('notify_function', None))
 
@@ -230,7 +233,9 @@ class QubesDisposableVm(QubesVm):
                 notify_function=kwargs.get('notify_function', None))
         print >>sys.stderr, "time=%s, qrexec done" % (str(time.time()))
 
-        if kwargs.get('start_guid', True) and 'DISPLAY' in os.environ and os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")):
+        if kwargs.get('start_guid', True) and 'DISPLAY' in os.environ and \
+                (os.path.exists('/var/run/qubes/shm.id.' + os.getenv("DISPLAY")) \
+                 or any(os.path.exists(x) for x in GUID_SHMID_FILE)):
             self.start_guid(verbose=verbose,
                     notify_function=kwargs.get('notify_function', None))
         print >>sys.stderr, "time=%s, guid done" % (str(time.time()))
-- 
2.5.5

Reply via email to