VirtualBox

Changeset 50281 in vbox for trunk


Ignore:
Timestamp:
Jan 30, 2014 10:43:54 AM (11 years ago)
Author:
vboxsync
Message:

Additions/x11/VBoxClient: detect VT switches on Linux 2.6.38 and higher and notify the service.

Location:
trunk/src/VBox/Additions/x11/VBoxClient
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h

    r44528 r50281  
    3333    /** Run the service main loop */
    3434    virtual int run(bool fDaemonised = false) = 0;
    35     /** Clean up any global resources before we shut down hard */
     35    /** Pause the service loop.  This must be safe to call on a different thread
     36     * and potentially before @run is or after it exits. */
     37    virtual void pause() { }
     38    /** Resume after pausing.  The same applies here as for @a pause. */
     39    virtual void resume() { }
     40    /** Clean up any global resources before we shut down hard.  Calling
     41     *  @a pause or @a resume later than @a cleanup must not cause errors. */
    3642    virtual void cleanup() = 0;
    3743    /** Virtual destructor.  Not used */
  • trunk/src/VBox/Additions/x11/VBoxClient/main.cpp

    r48945 r50281  
    1818
    1919#include <sys/types.h>
     20#if 0
     21#include <sys/vt.h>
     22#endif
    2023#include <stdlib.h>       /* For exit */
    2124#include <stdio.h>
     
    2326#include <unistd.h>
    2427#include <errno.h>
     28#include <poll.h>
    2529#include <signal.h>
    2630
    2731#include <X11/Xlib.h>
     32#include <X11/Xatom.h>
    2833
    2934#include <iprt/critsect.h>
    3035#include <iprt/env.h>
     36#include <iprt/file.h>
    3137#include <iprt/initterm.h>
    3238#include <iprt/message.h>
     
    3541#include <iprt/stream.h>
    3642#include <iprt/string.h>
     43#include <iprt/types.h>
    3744#include <VBox/VBoxGuestLib.h>
    3845#include <VBox/log.h>
     
    5360 * cleanup routine. */
    5461static RTFILE g_hPidFile;
    55 /** Global critical section used to protect the clean-up routine, which can be
    56  * called from different threads.
    57  */
    58 RTCRITSECT g_critSect;
     62/** Global critical section held during the clean-up routine (to prevent it
     63 * being called on multiple threads at once) or things which may not happen
     64 * during clean-up (e.g. pausing and resuming the service).
     65 */
     66RTCRITSECT g_cleanupCritSect;
    5967
    6068/** Clean up if we get a signal or something.  This is extern so that we
     
    6573     * async-safe.  Until we fix this application properly, we should be sure
    6674     * never to exit from anywhere except from this method. */
    67     int rc = RTCritSectEnter(&g_critSect);
     75    int rc = RTCritSectEnter(&g_cleanupCritSect);
    6876    if (RT_FAILURE(rc))
    6977    {
     
    136144}
    137145
     146/** Connect to the X server and return the "XFree86_VT" root window property,
     147 * or 0 on failure. */
     148static unsigned long getXOrgVT(void)
     149{
     150    Display *pDisplay;
     151    Atom actualType;
     152    int actualFormat;
     153    unsigned long cItems, cbLeft, cVT = 0;
     154    unsigned long *pValue;
     155
     156    pDisplay = XOpenDisplay(NULL);
     157    if (!pDisplay)
     158        return VINF_SUCCESS;
     159    XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
     160                       XInternAtom(pDisplay, "XFree86_VT", False), 0, 1, False,
     161                       XA_INTEGER, &actualType, &actualFormat, &cItems, &cbLeft,
     162                       (unsigned char **)&pValue);
     163    if (cItems && actualFormat == 32)
     164    {
     165        cVT = *pValue;
     166        XFree(pValue);
     167    }
     168    XCloseDisplay(pDisplay);
     169    return cVT;
     170}
     171
     172#ifdef RT_OS_LINUX
     173/** Poll for TTY changes using sysfs. Reading from the start of the pollable
     174 * file "/sys/class/tty/tty0/active" returns the currently active TTY as a
     175 * string of the form "tty<n>", with n greater than zero.  Polling for POLLPRI
     176 * returns when the TTY changes. */
     177static bool pollTTYSysfs(uint32_t cVT)
     178{
     179    RTFILE hFile;
     180    struct pollfd pollFD;
     181
     182    if (RT_SUCCESS(RTFileOpen(&hFile, "/sys/class/tty/tty0/active",
     183                          RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN)))
     184    {
     185        pollFD.fd = RTFileToNative(hFile);
     186        pollFD.events = POLLPRI;
     187        while (true)
     188        {
     189            char szTTY[7];
     190            uint32_t cTTY;
     191            size_t cbRead;
     192
     193            if (RT_FAILURE(RTFileReadAt(hFile, 0, (void *)szTTY, sizeof(szTTY),
     194                           &cbRead)))
     195            {
     196                LogRel(("VT monitor thread: read failed, stopping.\n"));
     197                break;
     198            }
     199            szTTY[6] = '\0';
     200            cTTY = RTStrToUInt32(&szTTY[3]);
     201            if (!cTTY)
     202            {
     203                LogRel(("VT monitor thread: failed to read the TTY, stopping.\n"));
     204                break;
     205            }
     206            if (cTTY == cVT)
     207                g_pService->resume();
     208            else
     209                g_pService->pause();
     210            /* If we get caught in a tight loop for some reason try to limit the
     211             * damage. */
     212            if (poll(&pollFD, 1, 0) > 0)
     213            {
     214                LogRel(("VT monitor thread: unexpectedly fast event, revents=0x%x.\n",
     215                        pollFD.revents));
     216                RTThreadYield();
     217            }
     218            if (   (poll(&pollFD, 1, -1) < 0 && errno != EINVAL)
     219                || pollFD.revents & POLLNVAL)
     220            {
     221                LogRel(("VT monitor thread: poll failed, stopping.\n"));
     222                break;
     223            }
     224        }
     225        RTFileClose(hFile);
     226    }
     227    else
     228        return false;
     229    return true;
     230}
     231#endif /* defined RT_OS_LINUX */
     232
     233#if 0
     234/** @note This is disabled because I believe that our existing code works well
     235 *        enough on the pre-Linux 2.6.38 systems where it would be needed.
     236 *        I will leave the code in place in case that proves to be wrong. */
     237/** Here we monitor the active VT by performing a VT_STATE ioctl on
     238 * /dev/console at regular intervals.  Unfortunately we can only monitor the
     239 * first sixteen virtual terminals this way, so if the X server is running on
     240 * a higher one we do not even try. */
     241static bool pollTTYDevConsole(unsigned long cVT)
     242{
     243    RTFILE hFile;
     244
     245    if (cVT >= 16)
     246        return false;
     247    if (RT_SUCCESS(RTFileOpen(&hFile, "/dev/console",
     248                      RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN)))
     249    {
     250        while (true)
     251        {
     252            struct vt_stat vtStat;
     253            int rc;
     254
     255            if (   RT_FAILURE(RTFileIoCtl(hFile, VT_GETSTATE, &vtStat,
     256                              sizeof(vtStat), &rc))
     257                || rc)
     258            {
     259                LogRel(("VT monitor thread: ioctl failed, stopping.\n"));
     260                break;
     261            }
     262            if (vtStat.v_active == cVT)
     263                g_pService->resume();
     264            else
     265                g_pService->pause();
     266            RTThreadSleep(1000);
     267        }
     268        RTFileClose(hFile);
     269    }
     270    else
     271        return false;
     272    return true;
     273}
     274#endif
     275
     276#ifdef RT_OS_LINUX
     277/**
     278 * Thread which notifies the service when we switch to a different VT or back.
     279 * @note runs until programme exit.
     280 * @todo take "g_cleanupCritSect" while pausing/resuming the service to ensure
     281 *       that they do not happen after clean-up starts.  We actually want to
     282 *       prevent racing with the static destructor for the service object, as
     283 *       there is (I think) no guarantee that the VT thread will stop before the
     284 *       destructor is called on programme exit.
     285 */
     286static int pfnVTMonitorThread(RTTHREAD self, void *pvUser)
     287{
     288    unsigned long cVT;
     289    RTFILE hFile;
     290   
     291    cVT = getXOrgVT();
     292    if (!cVT)
     293        return VINF_SUCCESS;
     294    if (!pollTTYSysfs((uint32_t) cVT))
     295#if 0  /* Probably not needed, see comment before function. */
     296        pollTTYDevConsole(cVT);
     297#else
     298        true;
     299#endif
     300    return VINF_SUCCESS;
     301}
     302
     303/**
     304 * Start the thread which notifies the service when we switch to a different
     305 * VT or back.  This is best effort functionality (XFree86 4.3 and older do not
     306 * report their VT via the "XFree86_VT" root window property at all), so we
     307 * only fail if actual thread creation fails.
     308 */
     309static int startVTMonitorThread()
     310{
     311    return RTThreadCreate(NULL, pfnVTMonitorThread, NULL, 0,
     312                          RTTHREADTYPE_INFREQUENT_POLLER, 0, "VT_MONITOR");
     313}
     314#endif /* defined RT_OS_LINUX */
     315
    138316/**
    139317 * Print out a usage message and exit with success.
     
    190368
    191369    /* Initialise our global clean-up critical section */
    192     rc = RTCritSectInit(&g_critSect);
     370    rc = RTCritSectInit(&g_cleanupCritSect);
    193371    if (RT_FAILURE(rc))
    194372    {
     
    307485    /* Set an X11 I/O error handler, so that we can shutdown properly on fatal errors. */
    308486    XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
     487#ifdef RT_OS_LINUX
     488    rc = startVTMonitorThread();
     489    if (RT_FAILURE(rc))
     490    {
     491        RTPrintf("Failed to start the VT monitor thread (%Rrc).  Exiting.\n",
     492                 rc);
     493        LogRel(("Failed to start the VT monitor thread (%Rrc).  Exiting.\n",
     494                 rc));
     495        VbglR3Term();
     496        return 1;
     497    }
     498#endif /* defined RT_OS_LINUX */
    309499    g_pService->run(fDaemonise);
    310500    VBoxClient::CleanUp();
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette