Index: /trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h
===================================================================
--- /trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h	(revision 50430)
+++ /trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h	(revision 50431)
@@ -30,14 +30,25 @@
 public:
     /** Get the services default path to pidfile, relative to $HOME */
+    /** @todo Should this also have a component relative to the X server number?
+     */
     virtual const char *getPidFilePath() = 0;
+    /** Special initialisation, if needed.  @a pause and @a resume are
+     * guaranteed not to be called until after this returns. */
+    virtual int init() { return VINF_SUCCESS; }
     /** Run the service main loop */
     virtual int run(bool fDaemonised = false) = 0;
+    /** @todo Note one of these will be called at start-up. */
     /** Pause the service loop.  This must be safe to call on a different thread
-     * and potentially before @run is or after it exits. */
-    virtual void pause() { }
+     * and potentially before @a run is or after it exits.
+     * This is called by the VT monitoring thread to allow the service to disable
+     * itself when the X server is switched out.  If the monitoring functionality
+     * is available then @a pause or @a resume will be called as soon as it starts
+     * up. */
+    virtual int pause() { return VINF_SUCCESS; }
     /** Resume after pausing.  The same applies here as for @a pause. */
-    virtual void resume() { }
-    /** Clean up any global resources before we shut down hard.  Calling
-     *  @a pause or @a resume later than @a cleanup must not cause errors. */
+    virtual int resume() { return VINF_SUCCESS; }
+    /** Clean up any global resources before we shut down hard.  The last calls
+     * to @a pause and @a resume are guaranteed to finish before this is called.
+     */
     virtual void cleanup() = 0;
     /** Virtual destructor.  Not used */
Index: /trunk/src/VBox/Additions/x11/VBoxClient/main.cpp
===================================================================
--- /trunk/src/VBox/Additions/x11/VBoxClient/main.cpp	(revision 50430)
+++ /trunk/src/VBox/Additions/x11/VBoxClient/main.cpp	(revision 50431)
@@ -18,7 +18,4 @@
 
 #include <sys/types.h>
-#if 0
-#include <sys/vt.h>
-#endif
 #include <stdlib.h>       /* For exit */
 #include <stdio.h>
@@ -47,6 +44,4 @@
 #include "VBoxClient.h"
 
-#define TRACE RTPrintf("%s: %d\n", __PRETTY_FUNCTION__, __LINE__); LogRel(("%s: %d\n", __PRETTY_FUNCTION__, __LINE__))
-
 static int (*gpfnOldIOErrorHandler)(Display *) = NULL;
 
@@ -64,5 +59,5 @@
  * during clean-up (e.g. pausing and resuming the service).
  */
-RTCRITSECT g_cleanupCritSect;
+RTCRITSECT g_critSect;
 
 /** Clean up if we get a signal or something.  This is extern so that we
@@ -71,10 +66,10 @@
 {
     /* We never release this, as we end up with a call to exit(3) which is not
-     * async-safe.  Until we fix this application properly, we should be sure
+     * async-safe.  Unless we fix this application properly, we should be sure
      * never to exit from anywhere except from this method. */
-    int rc = RTCritSectEnter(&g_cleanupCritSect);
-    if (RT_FAILURE(rc))
-    {
-        RTPrintf("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc);
+    int rc = RTCritSectEnter(&g_critSect);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc));
         abort();
     }
@@ -146,7 +141,6 @@
 /** Connect to the X server and return the "XFree86_VT" root window property,
  * or 0 on failure. */
-static unsigned long getXOrgVT(void)
-{
-    Display *pDisplay;
+static unsigned long getXOrgVT(Display *pDisplay)
+{
     Atom actualType;
     int actualFormat;
@@ -154,7 +148,4 @@
     unsigned long *pValue;
 
-    pDisplay = XOpenDisplay(NULL);
-    if (!pDisplay)
-        return VINF_SUCCESS;
     XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
                        XInternAtom(pDisplay, "XFree86_VT", False), 0, 1, False,
@@ -166,136 +157,129 @@
         XFree(pValue);
     }
-    XCloseDisplay(pDisplay);
     return cVT;
 }
 
-#ifdef RT_OS_LINUX
-/** Poll for TTY changes using sysfs. Reading from the start of the pollable
- * file "/sys/class/tty/tty0/active" returns the currently active TTY as a
- * string of the form "tty<n>", with n greater than zero.  Polling for POLLPRI
- * returns when the TTY changes. */
-static bool pollTTYSysfs(uint32_t cVT)
-{
-    RTFILE hFile;
-    struct pollfd pollFD;
-
-    if (RT_SUCCESS(RTFileOpen(&hFile, "/sys/class/tty/tty0/active",
-                          RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN)))
-    {
-        pollFD.fd = RTFileToNative(hFile);
-        pollFD.events = POLLPRI;
-        while (true)
-        {
-            char szTTY[7];
-            uint32_t cTTY;
-            size_t cbRead;
-
-            if (RT_FAILURE(RTFileReadAt(hFile, 0, (void *)szTTY, sizeof(szTTY),
-                           &cbRead)))
-            {
-                LogRel(("VT monitor thread: read failed, stopping.\n"));
-                break;
-            }
-            szTTY[6] = '\0';
-            cTTY = RTStrToUInt32(&szTTY[3]);
-            if (!cTTY)
-            {
-                LogRel(("VT monitor thread: failed to read the TTY, stopping.\n"));
-                break;
-            }
-            if (cTTY == cVT)
-                g_pService->resume();
-            else
-                g_pService->pause();
-            /* If we get caught in a tight loop for some reason try to limit the
-             * damage. */
-            if (poll(&pollFD, 1, 0) > 0)
-            {
-                LogRel(("VT monitor thread: unexpectedly fast event, revents=0x%x.\n",
-                        pollFD.revents));
-                RTThreadYield();
-            }
-            if (   (poll(&pollFD, 1, -1) < 0 && errno != EINVAL)
-                || pollFD.revents & POLLNVAL)
-            {
-                LogRel(("VT monitor thread: poll failed, stopping.\n"));
-                break;
-            }
-        }
-        RTFileClose(hFile);
-    }
-    else
-        return false;
-    return true;
-}
-#endif /* defined RT_OS_LINUX */
-
-#if 0
-/** @note This is disabled because I believe that our existing code works well
- *        enough on the pre-Linux 2.6.38 systems where it would be needed. 
- *        I will leave the code in place in case that proves to be wrong. */
-/** Here we monitor the active VT by performing a VT_STATE ioctl on
- * /dev/console at regular intervals.  Unfortunately we can only monitor the
- * first sixteen virtual terminals this way, so if the X server is running on
- * a higher one we do not even try. */
-static bool pollTTYDevConsole(unsigned long cVT)
-{
-    RTFILE hFile;
-
-    if (cVT >= 16)
-        return false;
-    if (RT_SUCCESS(RTFileOpen(&hFile, "/dev/console",
-                      RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN)))
-    {
-        while (true)
-        {
-            struct vt_stat vtStat;
-            int rc;
-
-            if (   RT_FAILURE(RTFileIoCtl(hFile, VT_GETSTATE, &vtStat,
-                              sizeof(vtStat), &rc))
-                || rc)
-            {
-                LogRel(("VT monitor thread: ioctl failed, stopping.\n"));
-                break;
-            }
-            if (vtStat.v_active == cVT)
-                g_pService->resume();
-            else
-                g_pService->pause();
-            RTThreadSleep(1000);
-        }
-        RTFileClose(hFile);
-    }
-    else
-        return false;
-    return true;
-}
-#endif
-
-#ifdef RT_OS_LINUX
-/**
- * Thread which notifies the service when we switch to a different VT or back.
+/** Check whether the current virtual terminal is the one running the X server.
+ */
+static void checkVTSysfs(RTFILE hFile, uint32_t cVT)
+{
+    char szTTY[7] = "";
+    uint32_t cTTY;
+    size_t cbRead;
+    int rc;
+    const char *pcszStage;
+
+    do {
+        pcszStage = "reading /sys/class/tty/tty0/active";
+        rc = RTFileReadAt(hFile, 0, (void *)szTTY, sizeof(szTTY), &cbRead);
+        if (RT_FAILURE(rc))
+            break;
+        szTTY[cbRead - 1] = '\0';
+        pcszStage = "getting VT number from sysfs file";
+        rc = RTStrToUInt32Full(&szTTY[3], 10, &cTTY);
+        if (RT_FAILURE(rc))
+            break;
+        pcszStage = "entering critical section";
+        rc = RTCritSectEnter(&g_critSect);
+        if (RT_FAILURE(rc))
+            break;
+        pcszStage = "asking service to pause or resume";
+        if (cTTY == cVT)
+            rc = g_pService->resume();
+        else
+            rc = g_pService->pause();
+        if (RT_FAILURE(rc))
+            break;
+        pcszStage = "leaving critical section";
+        rc = RTCritSectLeave(&g_critSect);
+    } while(false);
+    if (RT_FAILURE(rc))
+    {
+        LogRelFunc(("VBoxClient: failed at stage: \"%s\" rc: %Rrc cVT: %d szTTY: %s.\n",
+                    pcszStage, rc, (int) cVT, szTTY));
+        if (RTCritSectIsOwner(&g_critSect))
+            RTCritSectLeave(&g_critSect);
+        VBoxClient::CleanUp();
+    }
+}
+
+/** Poll for TTY changes using sysfs and for X server disconnection.
+ * Reading from the start of the pollable file "/sys/class/tty/tty0/active"
+ * returns the currently active TTY as a string of the form "tty<n>", with n
+ * greater than zero.  Polling for POLLPRI returns when the TTY changes.
+ * @a cVT should be zero if we do not know the X server's VT. */
+static void pollTTYAndXServer(Display *pDisplay, uint32_t cVT)
+{
+    RTFILE hFile = NIL_RTFILE;
+    struct pollfd pollFD[2];
+    unsigned cPollFD = 1;
+    int rc;
+
+    pollFD[1].fd = -1;
+    pollFD[1].revents = 0;
+    /* This block could be Linux-only, but keeping it on Solaris too, where it
+     * should just fail gracefully, gives us more code path coverage. */
+    if (cVT)
+    {
+        rc = RTFileOpen(&hFile, "/sys/class/tty/tty0/active",
+                        RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+        if (RT_SUCCESS(rc))
+        {
+            pollFD[1].fd = RTFileToNative(hFile);
+            pollFD[1].events = POLLPRI;
+            cPollFD = 2;
+        }
+    }
+    AssertRelease(pollFD[1].fd >= 0 || cPollFD == 1);
+    pollFD[0].fd = ConnectionNumber(pDisplay);
+    pollFD[0].events = POLLIN;
+    while (true)
+    {
+        if (hFile)
+            checkVTSysfs(hFile, cVT);
+        /* The only point of this loop is to trigger the I/O error handler if
+         * appropriate. */
+        while (XPending(pDisplay))
+        {
+            XEvent ev;
+
+            XNextEvent(pDisplay, &ev);
+        }
+        /* If we get caught in a tight loop for some reason try to limit the
+         * damage. */
+        if (poll(pollFD, cPollFD, 0) > 0)
+        {
+            LogRel(("Monitor thread: unexpectedly fast event, revents=0x%x, 0x%x.\n",
+                    pollFD[0].revents, pollFD[1].revents));
+            RTThreadYield();
+        }
+        if (   (poll(pollFD, cPollFD, -1) < 0 && errno != EINTR)
+            || pollFD[0].revents & POLLNVAL
+            || pollFD[1].revents & POLLNVAL)
+        {
+            LogRel(("Monitor thread: poll failed, stopping.\n"));
+            VBoxClient::CleanUp();
+        }
+    }
+}
+
+/**
+ * Thread which notifies the service when we switch to a different VT or back
+ * and cleans up when the X server exits.
  * @note runs until programme exit.
- * @todo take "g_cleanupCritSect" while pausing/resuming the service to ensure
- *       that they do not happen after clean-up starts.  We actually want to
- *       prevent racing with the static destructor for the service object, as
- *       there is (I think) no guarantee that the VT thread will stop before the
- *       destructor is called on programme exit.
- */
-static int pfnVTMonitorThread(RTTHREAD self, void *pvUser)
-{
+ */
+static int pfnMonitorThread(RTTHREAD self, void *pvUser)
+{
+    Display *pDisplay;
     unsigned long cVT;
     RTFILE hFile;
     
-    cVT = getXOrgVT();
-    if (!cVT)
+    pDisplay = XOpenDisplay(NULL);
+    if (!pDisplay)
         return VINF_SUCCESS;
-    if (!pollTTYSysfs((uint32_t) cVT))
-#if 0  /* Probably not needed, see comment before function. */
-        pollTTYDevConsole(cVT);
-#else
-        true;
-#endif
+    cVT = getXOrgVT(pDisplay);
+    /* Note: cVT will be 0 if we failed to get it.  This is valid. */
+    pollTTYAndXServer(pDisplay, (uint32_t) cVT);
+    /* Should never get here. */
     return VINF_SUCCESS;
 }
@@ -303,14 +287,16 @@
 /**
  * Start the thread which notifies the service when we switch to a different
- * VT or back.  This is best effort functionality (XFree86 4.3 and older do not
- * report their VT via the "XFree86_VT" root window property at all), so we
- * only fail if actual thread creation fails.
- */
-static int startVTMonitorThread()
-{
-    return RTThreadCreate(NULL, pfnVTMonitorThread, NULL, 0,
-                          RTTHREADTYPE_INFREQUENT_POLLER, 0, "VT_MONITOR");
-}
-#endif /* defined RT_OS_LINUX */
+ * VT or back, and terminates us when the X server exits.  The first is best
+ * effort functionality: XFree86 4.3 and older do not report their VT via the
+ * "XFree86_VT" root window property at all, and pre-2.6.38 Linux does not
+ * provide the interface in "sysfs" which we use.  If there is a need for this
+ * to work with pre-2.6.38 Linux we can send the VT_GETSTATE ioctl to
+ * /dev/console at regular intervals.
+ */
+static int startMonitorThread()
+{
+    return RTThreadCreate(NULL, pfnMonitorThread, NULL, 0,
+                          RTTHREADTYPE_INFREQUENT_POLLER, 0, "MONITOR");
+}
 
 /**
@@ -368,5 +354,5 @@
 
     /* Initialise our global clean-up critical section */
-    rc = RTCritSectInit(&g_cleanupCritSect);
+    rc = RTCritSectInit(&g_critSect);
     if (RT_FAILURE(rc))
     {
@@ -485,16 +471,19 @@
     /* Set an X11 I/O error handler, so that we can shutdown properly on fatal errors. */
     XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
-#ifdef RT_OS_LINUX
-    rc = startVTMonitorThread();
-    if (RT_FAILURE(rc))
-    {
-        RTPrintf("Failed to start the VT monitor thread (%Rrc).  Exiting.\n",
-                 rc);
-        LogRel(("Failed to start the VT monitor thread (%Rrc).  Exiting.\n",
+    rc = g_pService->init();
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("VBoxClient: failed to initialise the service (%Rrc).  Exiting.\n",
                  rc));
         VbglR3Term();
         return 1;
     }
-#endif /* defined RT_OS_LINUX */
+    rc = startMonitorThread();
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("Failed to start the monitor thread (%Rrc).  Exiting.\n",
+                 rc));
+        VBoxClient::CleanUp();
+    }
     g_pService->run(fDaemonise);
     VBoxClient::CleanUp();
