Changeset 50431 in vbox for trunk/src/VBox/Additions/x11/VBoxClient
- Timestamp:
- Feb 12, 2014 4:29:09 PM (11 years ago)
- svn:sync-xref-src-repo-rev:
- 92220
- Location:
- trunk/src/VBox/Additions/x11/VBoxClient
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h
r50281 r50431 30 30 public: 31 31 /** 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 */ 32 34 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; } 33 38 /** Run the service main loop */ 34 39 virtual int run(bool fDaemonised = false) = 0; 40 /** @todo Note one of these will be called at start-up. */ 35 41 /** 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; } 38 48 /** 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 */ 42 53 virtual void cleanup() = 0; 43 54 /** Virtual destructor. Not used */ -
trunk/src/VBox/Additions/x11/VBoxClient/main.cpp
r50281 r50431 18 18 19 19 #include <sys/types.h> 20 #if 021 #include <sys/vt.h>22 #endif23 20 #include <stdlib.h> /* For exit */ 24 21 #include <stdio.h> … … 47 44 #include "VBoxClient.h" 48 45 49 #define TRACE RTPrintf("%s: %d\n", __PRETTY_FUNCTION__, __LINE__); LogRel(("%s: %d\n", __PRETTY_FUNCTION__, __LINE__))50 51 46 static int (*gpfnOldIOErrorHandler)(Display *) = NULL; 52 47 … … 64 59 * during clean-up (e.g. pausing and resuming the service). 65 60 */ 66 RTCRITSECT g_c leanupCritSect;61 RTCRITSECT g_critSect; 67 62 68 63 /** Clean up if we get a signal or something. This is extern so that we … … 71 66 { 72 67 /* We never release this, as we end up with a call to exit(3) which is not 73 * async-safe. Un tilwe fix this application properly, we should be sure68 * async-safe. Unless we fix this application properly, we should be sure 74 69 * never to exit from anywhere except from this method. */ 75 int rc = RTCritSectEnter(&g_c leanupCritSect);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)); 79 74 abort(); 80 75 } … … 146 141 /** Connect to the X server and return the "XFree86_VT" root window property, 147 142 * or 0 on failure. */ 148 static unsigned long getXOrgVT(void) 149 { 150 Display *pDisplay; 143 static unsigned long getXOrgVT(Display *pDisplay) 144 { 151 145 Atom actualType; 152 146 int actualFormat; … … 154 148 unsigned long *pValue; 155 149 156 pDisplay = XOpenDisplay(NULL);157 if (!pDisplay)158 return VINF_SUCCESS;159 150 XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay), 160 151 XInternAtom(pDisplay, "XFree86_VT", False), 0, 1, False, … … 166 157 XFree(pValue); 167 158 } 168 XCloseDisplay(pDisplay);169 159 return cVT; 170 160 } 171 161 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 else209 g_pService->pause();210 /* If we get caught in a tight loop for some reason try to limit the211 * 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 the239 * first sixteen virtual terminals this way, so if the X server is running on240 * 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 else265 g_pService->pause();266 RTThreadSleep(1000);267 }268 RTFileClose(hFile);269 }270 else271 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 */ 164 static 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. */ 211 static 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. 279 269 * @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 */ 271 static int pfnMonitorThread(RTTHREAD self, void *pvUser) 272 { 273 Display *pDisplay; 288 274 unsigned long cVT; 289 275 RTFILE hFile; 290 276 291 cVT = getXOrgVT();292 if (! cVT)277 pDisplay = XOpenDisplay(NULL); 278 if (!pDisplay) 293 279 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. */ 300 284 return VINF_SUCCESS; 301 285 } … … 303 287 /** 304 288 * 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 */ 296 static int startMonitorThread() 297 { 298 return RTThreadCreate(NULL, pfnMonitorThread, NULL, 0, 299 RTTHREADTYPE_INFREQUENT_POLLER, 0, "MONITOR"); 300 } 315 301 316 302 /** … … 368 354 369 355 /* Initialise our global clean-up critical section */ 370 rc = RTCritSectInit(&g_c leanupCritSect);356 rc = RTCritSectInit(&g_critSect); 371 357 if (RT_FAILURE(rc)) 372 358 { … … 485 471 /* Set an X11 I/O error handler, so that we can shutdown properly on fatal errors. */ 486 472 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", 494 477 rc)); 495 478 VbglR3Term(); 496 479 return 1; 497 480 } 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 } 499 488 g_pService->run(fDaemonise); 500 489 VBoxClient::CleanUp();
Note:
See TracChangeset
for help on using the changeset viewer.