VirtualBox

Ignore:
Timestamp:
Feb 12, 2014 4:29:09 PM (11 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
92220
Message:

Additions/x11/VBoxClient: clean-up and make the VT monitor thread also monitor for X server terminations.

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

Legend:

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

    r50281 r50431  
    3030public:
    3131    /** Get the services default path to pidfile, relative to $HOME */
     32    /** @todo Should this also have a component relative to the X server number?
     33     */
    3234    virtual const char *getPidFilePath() = 0;
     35    /** Special initialisation, if needed.  @a pause and @a resume are
     36     * guaranteed not to be called until after this returns. */
     37    virtual int init() { return VINF_SUCCESS; }
    3338    /** Run the service main loop */
    3439    virtual int run(bool fDaemonised = false) = 0;
     40    /** @todo Note one of these will be called at start-up. */
    3541    /** 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() { }
     42     * and potentially before @a run is or after it exits.
     43     * This is called by the VT monitoring thread to allow the service to disable
     44     * itself when the X server is switched out.  If the monitoring functionality
     45     * is available then @a pause or @a resume will be called as soon as it starts
     46     * up. */
     47    virtual int pause() { return VINF_SUCCESS; }
    3848    /** 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. */
     49    virtual int resume() { return VINF_SUCCESS; }
     50    /** Clean up any global resources before we shut down hard.  The last calls
     51     * to @a pause and @a resume are guaranteed to finish before this is called.
     52     */
    4253    virtual void cleanup() = 0;
    4354    /** Virtual destructor.  Not used */
  • trunk/src/VBox/Additions/x11/VBoxClient/main.cpp

    r50281 r50431  
    1818
    1919#include <sys/types.h>
    20 #if 0
    21 #include <sys/vt.h>
    22 #endif
    2320#include <stdlib.h>       /* For exit */
    2421#include <stdio.h>
     
    4744#include "VBoxClient.h"
    4845
    49 #define TRACE RTPrintf("%s: %d\n", __PRETTY_FUNCTION__, __LINE__); LogRel(("%s: %d\n", __PRETTY_FUNCTION__, __LINE__))
    50 
    5146static int (*gpfnOldIOErrorHandler)(Display *) = NULL;
    5247
     
    6459 * during clean-up (e.g. pausing and resuming the service).
    6560 */
    66 RTCRITSECT g_cleanupCritSect;
     61RTCRITSECT g_critSect;
    6762
    6863/** Clean up if we get a signal or something.  This is extern so that we
     
    7166{
    7267    /* We never release this, as we end up with a call to exit(3) which is not
    73      * async-safe.  Until we fix this application properly, we should be sure
     68     * async-safe.  Unless we fix this application properly, we should be sure
    7469     * never to exit from anywhere except from this method. */
    75     int rc = RTCritSectEnter(&g_cleanupCritSect);
    76     if (RT_FAILURE(rc))
    77     {
    78         RTPrintf("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc);
     70    int rc = RTCritSectEnter(&g_critSect);
     71    if (RT_FAILURE(rc))
     72    {
     73        LogRel(("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc));
    7974        abort();
    8075    }
     
    146141/** Connect to the X server and return the "XFree86_VT" root window property,
    147142 * or 0 on failure. */
    148 static unsigned long getXOrgVT(void)
    149 {
    150     Display *pDisplay;
     143static unsigned long getXOrgVT(Display *pDisplay)
     144{
    151145    Atom actualType;
    152146    int actualFormat;
     
    154148    unsigned long *pValue;
    155149
    156     pDisplay = XOpenDisplay(NULL);
    157     if (!pDisplay)
    158         return VINF_SUCCESS;
    159150    XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
    160151                       XInternAtom(pDisplay, "XFree86_VT", False), 0, 1, False,
     
    166157        XFree(pValue);
    167158    }
    168     XCloseDisplay(pDisplay);
    169159    return cVT;
    170160}
    171161
    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. */
    177 static 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. */
    241 static 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.
     162/** Check whether the current virtual terminal is the one running the X server.
     163 */
     164static void checkVTSysfs(RTFILE hFile, uint32_t cVT)
     165{
     166    char szTTY[7] = "";
     167    uint32_t cTTY;
     168    size_t cbRead;
     169    int rc;
     170    const char *pcszStage;
     171
     172    do {
     173        pcszStage = "reading /sys/class/tty/tty0/active";
     174        rc = RTFileReadAt(hFile, 0, (void *)szTTY, sizeof(szTTY), &cbRead);
     175        if (RT_FAILURE(rc))
     176            break;
     177        szTTY[cbRead - 1] = '\0';
     178        pcszStage = "getting VT number from sysfs file";
     179        rc = RTStrToUInt32Full(&szTTY[3], 10, &cTTY);
     180        if (RT_FAILURE(rc))
     181            break;
     182        pcszStage = "entering critical section";
     183        rc = RTCritSectEnter(&g_critSect);
     184        if (RT_FAILURE(rc))
     185            break;
     186        pcszStage = "asking service to pause or resume";
     187        if (cTTY == cVT)
     188            rc = g_pService->resume();
     189        else
     190            rc = g_pService->pause();
     191        if (RT_FAILURE(rc))
     192            break;
     193        pcszStage = "leaving critical section";
     194        rc = RTCritSectLeave(&g_critSect);
     195    } while(false);
     196    if (RT_FAILURE(rc))
     197    {
     198        LogRelFunc(("VBoxClient: failed at stage: \"%s\" rc: %Rrc cVT: %d szTTY: %s.\n",
     199                    pcszStage, rc, (int) cVT, szTTY));
     200        if (RTCritSectIsOwner(&g_critSect))
     201            RTCritSectLeave(&g_critSect);
     202        VBoxClient::CleanUp();
     203    }
     204}
     205
     206/** Poll for TTY changes using sysfs and for X server disconnection.
     207 * Reading from the start of the pollable file "/sys/class/tty/tty0/active"
     208 * returns the currently active TTY as a string of the form "tty<n>", with n
     209 * greater than zero.  Polling for POLLPRI returns when the TTY changes.
     210 * @a cVT should be zero if we do not know the X server's VT. */
     211static void pollTTYAndXServer(Display *pDisplay, uint32_t cVT)
     212{
     213    RTFILE hFile = NIL_RTFILE;
     214    struct pollfd pollFD[2];
     215    unsigned cPollFD = 1;
     216    int rc;
     217
     218    pollFD[1].fd = -1;
     219    pollFD[1].revents = 0;
     220    /* This block could be Linux-only, but keeping it on Solaris too, where it
     221     * should just fail gracefully, gives us more code path coverage. */
     222    if (cVT)
     223    {
     224        rc = RTFileOpen(&hFile, "/sys/class/tty/tty0/active",
     225                        RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
     226        if (RT_SUCCESS(rc))
     227        {
     228            pollFD[1].fd = RTFileToNative(hFile);
     229            pollFD[1].events = POLLPRI;
     230            cPollFD = 2;
     231        }
     232    }
     233    AssertRelease(pollFD[1].fd >= 0 || cPollFD == 1);
     234    pollFD[0].fd = ConnectionNumber(pDisplay);
     235    pollFD[0].events = POLLIN;
     236    while (true)
     237    {
     238        if (hFile)
     239            checkVTSysfs(hFile, cVT);
     240        /* The only point of this loop is to trigger the I/O error handler if
     241         * appropriate. */
     242        while (XPending(pDisplay))
     243        {
     244            XEvent ev;
     245
     246            XNextEvent(pDisplay, &ev);
     247        }
     248        /* If we get caught in a tight loop for some reason try to limit the
     249         * damage. */
     250        if (poll(pollFD, cPollFD, 0) > 0)
     251        {
     252            LogRel(("Monitor thread: unexpectedly fast event, revents=0x%x, 0x%x.\n",
     253                    pollFD[0].revents, pollFD[1].revents));
     254            RTThreadYield();
     255        }
     256        if (   (poll(pollFD, cPollFD, -1) < 0 && errno != EINTR)
     257            || pollFD[0].revents & POLLNVAL
     258            || pollFD[1].revents & POLLNVAL)
     259        {
     260            LogRel(("Monitor thread: poll failed, stopping.\n"));
     261            VBoxClient::CleanUp();
     262        }
     263    }
     264}
     265
     266/**
     267 * Thread which notifies the service when we switch to a different VT or back
     268 * and cleans up when the X server exits.
    279269 * @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  */
    286 static int pfnVTMonitorThread(RTTHREAD self, void *pvUser)
    287 {
     270 */
     271static int pfnMonitorThread(RTTHREAD self, void *pvUser)
     272{
     273    Display *pDisplay;
    288274    unsigned long cVT;
    289275    RTFILE hFile;
    290276   
    291     cVT = getXOrgVT();
    292     if (!cVT)
     277    pDisplay = XOpenDisplay(NULL);
     278    if (!pDisplay)
    293279        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
     280    cVT = getXOrgVT(pDisplay);
     281    /* Note: cVT will be 0 if we failed to get it.  This is valid. */
     282    pollTTYAndXServer(pDisplay, (uint32_t) cVT);
     283    /* Should never get here. */
    300284    return VINF_SUCCESS;
    301285}
     
    303287/**
    304288 * 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  */
    309 static int startVTMonitorThread()
    310 {
    311     return RTThreadCreate(NULL, pfnVTMonitorThread, NULL, 0,
    312                           RTTHREADTYPE_INFREQUENT_POLLER, 0, "VT_MONITOR");
    313 }
    314 #endif /* defined RT_OS_LINUX */
     289 * VT or back, and terminates us when the X server exits.  The first is best
     290 * effort functionality: XFree86 4.3 and older do not report their VT via the
     291 * "XFree86_VT" root window property at all, and pre-2.6.38 Linux does not
     292 * provide the interface in "sysfs" which we use.  If there is a need for this
     293 * to work with pre-2.6.38 Linux we can send the VT_GETSTATE ioctl to
     294 * /dev/console at regular intervals.
     295 */
     296static int startMonitorThread()
     297{
     298    return RTThreadCreate(NULL, pfnMonitorThread, NULL, 0,
     299                          RTTHREADTYPE_INFREQUENT_POLLER, 0, "MONITOR");
     300}
    315301
    316302/**
     
    368354
    369355    /* Initialise our global clean-up critical section */
    370     rc = RTCritSectInit(&g_cleanupCritSect);
     356    rc = RTCritSectInit(&g_critSect);
    371357    if (RT_FAILURE(rc))
    372358    {
     
    485471    /* Set an X11 I/O error handler, so that we can shutdown properly on fatal errors. */
    486472    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",
     473    rc = g_pService->init();
     474    if (RT_FAILURE(rc))
     475    {
     476        LogRel(("VBoxClient: failed to initialise the service (%Rrc).  Exiting.\n",
    494477                 rc));
    495478        VbglR3Term();
    496479        return 1;
    497480    }
    498 #endif /* defined RT_OS_LINUX */
     481    rc = startMonitorThread();
     482    if (RT_FAILURE(rc))
     483    {
     484        LogRel(("Failed to start the monitor thread (%Rrc).  Exiting.\n",
     485                 rc));
     486        VBoxClient::CleanUp();
     487    }
    499488    g_pService->run(fDaemonise);
    500489    VBoxClient::CleanUp();
Note: See TracChangeset for help on using the changeset viewer.

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