VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/main.cpp@ 94330

Last change on this file since 94330 was 94306, checked in by vboxsync, 3 years ago

Additions: X11: partly restore legacy X11 resize service removed in r136358, bugref:10199.

In this commit, lagacy X11 resize service 'VBoxClient --display' is restored. This service
will be started as a fallback if 'VBoxClient --vmsvga' service will fail to start. This commit
restores guest screen resize functionality for old X11 guests (currently only Linux) which have
libXrandr older than 1.4 installed in the system.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 24.7 KB
Line 
1/* $Id: main.cpp 94306 2022-03-18 12:15:30Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions - X11 Client.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <sys/wait.h>
23#include <stdlib.h> /* For exit */
24#include <signal.h>
25#include <X11/Xlib.h>
26#include "product-generated.h"
27#include <iprt/asm.h>
28#include <iprt/buildconfig.h>
29#include <iprt/critsect.h>
30#include <iprt/errno.h>
31#include <iprt/getopt.h>
32#include <iprt/initterm.h>
33#include <iprt/message.h>
34#include <iprt/path.h>
35#include <iprt/stream.h>
36#include <iprt/env.h>
37#include <VBox/VBoxGuestLib.h>
38#include <VBox/err.h>
39#include <VBox/version.h>
40#include "VBoxClient.h"
41
42
43/*********************************************************************************************************************************
44* Defines *
45*********************************************************************************************************************************/
46#define VBOXCLIENT_OPT_SERVICES 980
47#define VBOXCLIENT_OPT_CHECKHOSTVERSION VBOXCLIENT_OPT_SERVICES
48#define VBOXCLIENT_OPT_CLIPBOARD VBOXCLIENT_OPT_SERVICES + 1
49#define VBOXCLIENT_OPT_DRAGANDDROP VBOXCLIENT_OPT_SERVICES + 2
50#define VBOXCLIENT_OPT_SEAMLESS VBOXCLIENT_OPT_SERVICES + 3
51#define VBOXCLIENT_OPT_VMSVGA VBOXCLIENT_OPT_SERVICES + 4
52#define VBOXCLIENT_OPT_VMSVGA_SESSION VBOXCLIENT_OPT_SERVICES + 5
53#define VBOXCLIENT_OPT_DISPLAY VBOXCLIENT_OPT_SERVICES + 6
54
55
56/*********************************************************************************************************************************
57* Local structures *
58*********************************************************************************************************************************/
59/**
60 * The global service state.
61 */
62typedef struct VBCLSERVICESTATE
63{
64 /** Pointer to the service descriptor. */
65 PVBCLSERVICE pDesc;
66 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
67 RTTHREAD Thread;
68 /** Whether Pre-init was called. */
69 bool fPreInited;
70 /** Shutdown indicator. */
71 bool volatile fShutdown;
72 /** Indicator set by the service thread exiting. */
73 bool volatile fStopped;
74 /** Whether the service was started or not. */
75 bool fStarted;
76} VBCLSERVICESTATE;
77/** Pointer to a service state. */
78typedef VBCLSERVICESTATE *PVBCLSERVICESTATE;
79
80
81/*********************************************************************************************************************************
82* Global Variables *
83*********************************************************************************************************************************/
84/** The global service state. */
85VBCLSERVICESTATE g_Service = { 0 };
86
87/** Set by the signal handler when being called. */
88static volatile bool g_fSignalHandlerCalled = false;
89/** Critical section for the signal handler. */
90static RTCRITSECT g_csSignalHandler;
91/** Flag indicating Whether the service starts in daemonized mode or not. */
92bool g_fDaemonized = false;
93/** The name of our pidfile. It is global for the benefit of the cleanup
94 * routine. */
95static char g_szPidFile[RTPATH_MAX] = "";
96/** The file handle of our pidfile. It is global for the benefit of the
97 * cleanup routine. */
98static RTFILE g_hPidFile;
99/** Global critical section held during the clean-up routine (to prevent it
100 * being called on multiple threads at once) or things which may not happen
101 * during clean-up (e.g. pausing and resuming the service).
102 */
103static RTCRITSECT g_critSect;
104/** Counter of how often our daemon has been respawned. */
105unsigned g_cRespawn = 0;
106/** Logging verbosity level. */
107unsigned g_cVerbosity = 0;
108/** Absolute path to log file, if any. */
109static char g_szLogFile[RTPATH_MAX + 128] = "";
110
111bool VBClHasWayland(void)
112{
113 return RTEnvGet(VBCL_ENV_WAYLAND_DISPLAY) != NULL;
114}
115
116/**
117 * Shut down if we get a signal or something.
118 *
119 * This is extern so that we can call it from other compilation units.
120 */
121void VBClShutdown(bool fExit /*=true*/)
122{
123 /* We never release this, as we end up with a call to exit(3) which is not
124 * async-safe. Unless we fix this application properly, we should be sure
125 * never to exit from anywhere except from this method. */
126 int rc = RTCritSectEnter(&g_critSect);
127 if (RT_FAILURE(rc))
128 VBClLogFatalError("Failure while acquiring the global critical section, rc=%Rrc\n", rc);
129
130 /* Ask service to stop. */
131 if (g_Service.pDesc &&
132 g_Service.pDesc->pfnStop)
133 {
134 ASMAtomicWriteBool(&g_Service.fShutdown, true);
135 g_Service.pDesc->pfnStop();
136
137 }
138
139 if (g_szPidFile[0] && g_hPidFile)
140 VbglR3ClosePidFile(g_szPidFile, g_hPidFile);
141
142 VBClLogDestroy();
143
144 if (fExit)
145 exit(RTEXITCODE_SUCCESS);
146}
147
148/**
149 * Xlib error handler for certain errors that we can't avoid.
150 */
151static int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
152{
153 char errorText[1024];
154
155 XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
156 VBClLogError("An X Window protocol error occurred: %s (error code %d). Request code: %d, minor code: %d, serial number: %d\n", errorText, pError->error_code, pError->request_code, pError->minor_code, pError->serial);
157 return 0;
158}
159
160/**
161 * Xlib error handler for fatal errors. This often means that the programme is still running
162 * when X exits.
163 */
164static int vboxClientXLibIOErrorHandler(Display *pDisplay)
165{
166 RT_NOREF1(pDisplay);
167 VBClLogError("A fatal guest X Window error occurred. This may just mean that the Window system was shut down while the client was still running\n");
168 VBClShutdown();
169 return 0; /* We should never reach this. */
170}
171
172/**
173 * A standard signal handler which cleans up and exits.
174 */
175static void vboxClientSignalHandler(int iSignal)
176{
177 int rc = RTCritSectEnter(&g_csSignalHandler);
178 if (RT_SUCCESS(rc))
179 {
180 if (g_fSignalHandlerCalled)
181 {
182 RTCritSectLeave(&g_csSignalHandler);
183 return;
184 }
185
186 VBClLogVerbose(2, "Received signal %d\n", iSignal);
187 g_fSignalHandlerCalled = true;
188
189 /* Leave critical section before stopping the service. */
190 RTCritSectLeave(&g_csSignalHandler);
191
192 if ( g_Service.pDesc
193 && g_Service.pDesc->pfnStop)
194 {
195 VBClLogVerbose(2, "Notifying service to stop ...\n");
196
197 /* Signal the service to stop. */
198 ASMAtomicWriteBool(&g_Service.fShutdown, true);
199
200 g_Service.pDesc->pfnStop();
201
202 VBClLogVerbose(2, "Service notified to stop, waiting on worker thread to stop ...\n");
203 }
204 }
205}
206
207/**
208 * Reset all standard termination signals to call our signal handler.
209 */
210static int vboxClientSignalHandlerInstall(void)
211{
212 struct sigaction sigAction;
213 sigAction.sa_handler = vboxClientSignalHandler;
214 sigemptyset(&sigAction.sa_mask);
215 sigAction.sa_flags = 0;
216 sigaction(SIGHUP, &sigAction, NULL);
217 sigaction(SIGINT, &sigAction, NULL);
218 sigaction(SIGQUIT, &sigAction, NULL);
219 sigaction(SIGPIPE, &sigAction, NULL);
220 sigaction(SIGALRM, &sigAction, NULL);
221 sigaction(SIGTERM, &sigAction, NULL);
222 sigaction(SIGUSR1, &sigAction, NULL);
223 sigaction(SIGUSR2, &sigAction, NULL);
224
225 return RTCritSectInit(&g_csSignalHandler);
226}
227
228/**
229 * Uninstalls a previously installed signal handler.
230 */
231static int vboxClientSignalHandlerUninstall(void)
232{
233 signal(SIGTERM, SIG_DFL);
234#ifdef SIGBREAK
235 signal(SIGBREAK, SIG_DFL);
236#endif
237
238 return RTCritSectDelete(&g_csSignalHandler);
239}
240
241/**
242 * Print out a usage message and exit with success.
243 */
244static void vboxClientUsage(const char *pcszFileName)
245{
246 RTPrintf(VBOX_PRODUCT " VBoxClient "
247 VBOX_VERSION_STRING "\n"
248 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
249 "All rights reserved.\n"
250 "\n");
251
252 RTPrintf("Usage: %s "
253#ifdef VBOX_WITH_SHARED_CLIPBOARD
254 "--clipboard|"
255#endif
256#ifdef VBOX_WITH_DRAG_AND_DROP
257 "--draganddrop|"
258#endif
259#ifdef VBOX_WITH_GUEST_PROPS
260 "--checkhostversion|"
261#endif
262#ifdef VBOX_WITH_SEAMLESS
263 "--seamless|"
264#endif
265#ifdef VBOX_WITH_VMSVGA
266 "--vmsvga|"
267 "--vmsvga-session"
268#endif
269 "\n[-d|--nodaemon]\n", pcszFileName);
270 RTPrintf("\n");
271 RTPrintf("Options:\n");
272#ifdef VBOX_WITH_SHARED_CLIPBOARD
273 RTPrintf(" --clipboard starts the shared clipboard service\n");
274#endif
275#ifdef VBOX_WITH_DRAG_AND_DROP
276 RTPrintf(" --draganddrop starts the drag and drop service\n");
277#endif
278#ifdef VBOX_WITH_GUEST_PROPS
279 RTPrintf(" --checkhostversion starts the host version notifier service\n");
280#endif
281#ifdef VBOX_WITH_SEAMLESS
282 RTPrintf(" --seamless starts the seamless windows service\n");
283#endif
284#ifdef VBOX_WITH_VMSVGA
285 RTPrintf(" --vmsvga starts VMSVGA dynamic resizing for X11/Wayland guests\n");
286#ifdef RT_OS_LINUX
287 RTPrintf(" --vmsvga-session starts Desktop Environment specific screen assistant for X11/Wayland guests\n"
288 " (VMSVGA graphics adapter only)\n");
289#else
290 RTPrintf(" --vmsvga-session an alias for --vmsvga\n");
291#endif
292 RTPrintf(" --display starts VMSVGA dynamic resizing for legacy guests\n");
293#endif
294 RTPrintf(" -f, --foreground run in the foreground (no daemonizing)\n");
295 RTPrintf(" -d, --nodaemon continues running as a system service\n");
296 RTPrintf(" -h, --help shows this help text\n");
297 RTPrintf(" -l, --logfile <path> enables logging to a file\n");
298 RTPrintf(" -v, --verbose increases logging verbosity level\n");
299 RTPrintf(" -V, --version shows version information\n");
300 RTPrintf("\n");
301}
302
303/**
304 * Complains about seeing more than one service specification.
305 *
306 * @returns RTEXITCODE_SYNTAX.
307 */
308static int vbclSyntaxOnlyOneService(void)
309{
310 RTMsgError("More than one service specified! Only one, please.");
311 return RTEXITCODE_SYNTAX;
312}
313
314/**
315 * The service thread.
316 *
317 * @returns Whatever the worker function returns.
318 * @param ThreadSelf My thread handle.
319 * @param pvUser The service index.
320 */
321static DECLCALLBACK(int) vbclThread(RTTHREAD ThreadSelf, void *pvUser)
322{
323 PVBCLSERVICESTATE pState = (PVBCLSERVICESTATE)pvUser;
324 AssertPtrReturn(pState, VERR_INVALID_POINTER);
325
326#ifndef RT_OS_WINDOWS
327 /*
328 * Block all signals for this thread. Only the main thread will handle signals.
329 */
330 sigset_t signalMask;
331 sigfillset(&signalMask);
332 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
333#endif
334
335 AssertPtrReturn(pState->pDesc->pfnWorker, VERR_INVALID_POINTER);
336 int rc = pState->pDesc->pfnWorker(&pState->fShutdown);
337
338 VBClLogVerbose(2, "Worker loop ended with %Rrc\n", rc);
339
340 ASMAtomicXchgBool(&pState->fShutdown, true);
341 RTThreadUserSignal(ThreadSelf);
342 return rc;
343}
344
345/**
346 * The main loop for the VBoxClient daemon.
347 */
348int main(int argc, char *argv[])
349{
350 /* Note: No VBClLogXXX calls before actually creating the log. */
351
352 /* Initialize our runtime before all else. */
353 int rc = RTR3InitExe(argc, &argv, 0);
354 if (RT_FAILURE(rc))
355 return RTMsgInitFailure(rc);
356
357 /* This should never be called twice in one process - in fact one Display
358 * object should probably never be used from multiple threads anyway. */
359 if (!XInitThreads())
360 return RTMsgErrorExitFailure("Failed to initialize X11 threads\n");
361
362 /* Get our file name for usage info and hints. */
363 const char *pcszFileName = RTPathFilename(argv[0]);
364 if (!pcszFileName)
365 pcszFileName = "VBoxClient";
366
367 /* Parse our option(s). */
368 static const RTGETOPTDEF s_aOptions[] =
369 {
370 { "--nodaemon", 'd', RTGETOPT_REQ_NOTHING },
371 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
372 { "--help", 'h', RTGETOPT_REQ_NOTHING },
373 { "--logfile", 'l', RTGETOPT_REQ_STRING },
374 { "--version", 'V', RTGETOPT_REQ_NOTHING },
375 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
376
377 /* Services */
378#ifdef VBOX_WITH_GUEST_PROPS
379 { "--checkhostversion", VBOXCLIENT_OPT_CHECKHOSTVERSION, RTGETOPT_REQ_NOTHING },
380#endif
381#ifdef VBOX_WITH_SHARED_CLIPBOARD
382 { "--clipboard", VBOXCLIENT_OPT_CLIPBOARD, RTGETOPT_REQ_NOTHING },
383#endif
384#ifdef VBOX_WITH_DRAG_AND_DROP
385 { "--draganddrop", VBOXCLIENT_OPT_DRAGANDDROP, RTGETOPT_REQ_NOTHING },
386#endif
387#ifdef VBOX_WITH_SEAMLESS
388 { "--seamless", VBOXCLIENT_OPT_SEAMLESS, RTGETOPT_REQ_NOTHING },
389#endif
390#ifdef VBOX_WITH_VMSVGA
391 { "--vmsvga", VBOXCLIENT_OPT_VMSVGA, RTGETOPT_REQ_NOTHING },
392 { "--vmsvga-session", VBOXCLIENT_OPT_VMSVGA_SESSION, RTGETOPT_REQ_NOTHING },
393 { "--display", VBOXCLIENT_OPT_DISPLAY, RTGETOPT_REQ_NOTHING },
394#endif
395 };
396
397 int ch;
398 RTGETOPTUNION ValueUnion;
399 RTGETOPTSTATE GetState;
400 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
401 if (RT_FAILURE(rc))
402 return RTMsgErrorExitFailure("Failed to parse command line options, rc=%Rrc\n", rc);
403
404 AssertRC(rc);
405
406 bool fDaemonise = true;
407 bool fRespawn = true;
408
409 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
410 {
411 /* For options that require an argument, ValueUnion has received the value. */
412 switch (ch)
413 {
414 case 'd':
415 {
416 fDaemonise = false;
417 break;
418 }
419
420 case 'h':
421 {
422 vboxClientUsage(pcszFileName);
423 return RTEXITCODE_SUCCESS;
424 }
425
426 case 'f':
427 {
428 fDaemonise = false;
429 fRespawn = false;
430 break;
431 }
432
433 case 'l':
434 {
435 rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
436 if (RT_FAILURE(rc))
437 return RTMsgErrorExitFailure("Unable to set log file path, rc=%Rrc\n", rc);
438 break;
439 }
440
441 case 'n':
442 {
443 fRespawn = false;
444 break;
445 }
446
447 case 'v':
448 {
449 g_cVerbosity++;
450 break;
451 }
452
453 case 'V':
454 {
455 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
456 return RTEXITCODE_SUCCESS;
457 }
458
459 /* Services */
460#ifdef VBOX_WITH_GUEST_PROPS
461 case VBOXCLIENT_OPT_CHECKHOSTVERSION:
462 {
463 if (g_Service.pDesc)
464 return vbclSyntaxOnlyOneService();
465 g_Service.pDesc = &g_SvcHostVersion;
466 break;
467 }
468#endif
469#ifdef VBOX_WITH_SHARED_CLIPBOARD
470 case VBOXCLIENT_OPT_CLIPBOARD:
471 {
472 if (g_Service.pDesc)
473 return vbclSyntaxOnlyOneService();
474 g_Service.pDesc = &g_SvcClipboard;
475 break;
476 }
477#endif
478#ifdef VBOX_WITH_DRAG_AND_DROP
479 case VBOXCLIENT_OPT_DRAGANDDROP:
480 {
481 if (g_Service.pDesc)
482 return vbclSyntaxOnlyOneService();
483 g_Service.pDesc = &g_SvcDragAndDrop;
484 break;
485 }
486#endif
487#ifdef VBOX_WITH_SEAMLESS
488 case VBOXCLIENT_OPT_SEAMLESS:
489 {
490 if (g_Service.pDesc)
491 return vbclSyntaxOnlyOneService();
492 g_Service.pDesc = &g_SvcSeamless;
493 break;
494 }
495#endif
496#ifdef VBOX_WITH_VMSVGA
497 case VBOXCLIENT_OPT_VMSVGA:
498 {
499 if (g_Service.pDesc)
500 return vbclSyntaxOnlyOneService();
501 g_Service.pDesc = &g_SvcDisplaySVGA;
502 break;
503 }
504
505 case VBOXCLIENT_OPT_VMSVGA_SESSION:
506 {
507 if (g_Service.pDesc)
508 return vbclSyntaxOnlyOneService();
509# ifdef RT_OS_LINUX
510 g_Service.pDesc = &g_SvcDisplaySVGASession;
511# else
512 g_Service.pDesc = &g_SvcDisplaySVGA;
513# endif
514 break;
515 }
516
517 case VBOXCLIENT_OPT_DISPLAY:
518 {
519 if (g_Service.pDesc)
520 return vbclSyntaxOnlyOneService();
521 g_Service.pDesc = &g_SvcDisplayLegacy;
522 break;
523 }
524#endif
525 case VINF_GETOPT_NOT_OPTION:
526 break;
527
528 case VERR_GETOPT_UNKNOWN_OPTION:
529 RT_FALL_THROUGH();
530 default:
531 {
532 if ( g_Service.pDesc
533 && g_Service.pDesc->pfnOption)
534 {
535 rc = g_Service.pDesc->pfnOption(NULL, argc, argv, &GetState.iNext);
536 }
537 else /* No service specified yet. */
538 rc = VERR_NOT_FOUND;
539
540 if (RT_FAILURE(rc))
541 {
542 RTMsgError("unrecognized option '%s'", ValueUnion.psz);
543 RTMsgInfo("Try '%s --help' for more information", pcszFileName);
544 return RTEXITCODE_SYNTAX;
545 }
546 break;
547 }
548
549 } /* switch */
550 } /* while RTGetOpt */
551
552 if (!g_Service.pDesc)
553 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No service specified. Quitting because nothing to do!");
554
555 /* Initialize VbglR3 before we do anything else with the logger. */
556 rc = VbglR3InitUser();
557 if (RT_FAILURE(rc))
558 return RTMsgErrorExitFailure("VbglR3InitUser failed: %Rrc", rc);
559
560 rc = VBClLogCreate(g_szLogFile[0] ? g_szLogFile : "");
561 if (RT_FAILURE(rc))
562 return RTMsgErrorExitFailure("Failed to create release log '%s', rc=%Rrc\n",
563 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
564
565 if (!fDaemonise)
566 {
567 /* If the user is running in "no daemon" mode, send critical logging to stdout as well. */
568 PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
569 if (pReleaseLog)
570 {
571 rc = RTLogDestinations(pReleaseLog, "stdout");
572 if (RT_FAILURE(rc))
573 return RTMsgErrorExitFailure("Failed to redivert error output, rc=%Rrc", rc);
574 }
575 }
576
577 VBClLogInfo("VBoxClient %s r%s started. Verbose level = %d\n", RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
578 VBClLogInfo("Service: %s\n", g_Service.pDesc->pszDesc);
579
580 rc = RTCritSectInit(&g_critSect);
581 if (RT_FAILURE(rc))
582 VBClLogFatalError("Initializing critical section failed: %Rrc\n", rc);
583 if (g_Service.pDesc->pszPidFilePath)
584 {
585 rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
586 if (RT_FAILURE(rc))
587 VBClLogFatalError("Getting home directory failed: %Rrc\n", rc);
588 rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile), g_Service.pDesc->pszPidFilePath);
589 if (RT_FAILURE(rc))
590 VBClLogFatalError("Creating PID file path failed: %Rrc\n", rc);
591 }
592
593 if (fDaemonise)
594 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn);
595 if (RT_FAILURE(rc))
596 VBClLogFatalError("Daemonizing service failed: %Rrc\n", rc);
597
598 if (g_szPidFile[0])
599 {
600 rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
601 if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */
602 return RTEXITCODE_SUCCESS;
603 if (RT_FAILURE(rc))
604 VBClLogFatalError("Creating PID file failed: %Rrc\n", rc);
605 }
606
607#ifndef VBOXCLIENT_WITHOUT_X11
608 /* Set an X11 error handler, so that we don't die when we get unavoidable
609 * errors. */
610 XSetErrorHandler(vboxClientXLibErrorHandler);
611 /* Set an X11 I/O error handler, so that we can shutdown properly on
612 * fatal errors. */
613 XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
614#endif
615
616 bool fSignalHandlerInstalled = false;
617 if (RT_SUCCESS(rc))
618 {
619 rc = vboxClientSignalHandlerInstall();
620 if (RT_SUCCESS(rc))
621 fSignalHandlerInstalled = true;
622 }
623
624 if ( RT_SUCCESS(rc)
625 && g_Service.pDesc->pfnInit)
626 {
627 VBClLogInfo("Initializing service ...\n");
628 rc = g_Service.pDesc->pfnInit();
629 }
630
631 if (RT_SUCCESS(rc))
632 {
633 VBClLogInfo("Creating worker thread ...\n");
634 rc = RTThreadCreate(&g_Service.Thread, vbclThread, (void *)&g_Service, 0,
635 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_Service.pDesc->pszName);
636 if (RT_FAILURE(rc))
637 {
638 VBClLogError("Creating worker thread failed, rc=%Rrc\n", rc);
639 }
640 else
641 {
642 g_Service.fStarted = true;
643
644 /* Wait for the thread to initialize. */
645 /** @todo There is a race between waiting and checking
646 * the fShutdown flag of a thread here and processing
647 * the thread's actual worker loop. If the thread decides
648 * to exit the loop before we skipped the fShutdown check
649 * below the service will fail to start! */
650 /** @todo This presumably means either a one-shot service or that
651 * something has gone wrong. In the second case treating it as failure
652 * to start is probably right, so we need a way to signal the first
653 * rather than leaving the idle thread hanging around. A flag in the
654 * service description? */
655 RTThreadUserWait(g_Service.Thread, RT_MS_1MIN);
656 if (g_Service.fShutdown)
657 {
658 VBClLogError("Service failed to start!\n");
659 rc = VERR_GENERAL_FAILURE;
660 }
661 else
662 {
663 VBClLogInfo("Service started\n");
664
665 int rcThread;
666 rc = RTThreadWait(g_Service.Thread, RT_INDEFINITE_WAIT, &rcThread);
667 if (RT_SUCCESS(rc))
668 rc = rcThread;
669
670 if (RT_FAILURE(rc))
671 VBClLogError("Waiting on worker thread to stop failed, rc=%Rrc\n", rc);
672
673 if (g_Service.pDesc->pfnTerm)
674 {
675 VBClLogInfo("Terminating service\n");
676
677 int rc2 = g_Service.pDesc->pfnTerm();
678 if (RT_SUCCESS(rc))
679 rc = rc2;
680
681 if (RT_SUCCESS(rc))
682 {
683 VBClLogInfo("Service terminated\n");
684 }
685 else
686 VBClLogError("Service failed to terminate, rc=%Rrc\n", rc);
687 }
688 }
689 }
690 }
691
692 if (RT_FAILURE(rc))
693 {
694 if (rc == VERR_NOT_AVAILABLE)
695 VBClLogInfo("Service is not availabe, skipping\n");
696 else if (rc == VERR_NOT_SUPPORTED)
697 VBClLogInfo("Service is not supported on this platform, skipping\n");
698 else
699 VBClLogError("Service ended with error %Rrc\n", rc);
700 }
701 else
702 VBClLogVerbose(2, "Service ended\n");
703
704 if (fSignalHandlerInstalled)
705 {
706 int rc2 = vboxClientSignalHandlerUninstall();
707 AssertRC(rc2);
708 }
709
710 VBClShutdown(false /*fExit*/);
711
712 /** @todo r=andy Should we return an appropriate exit code if the service failed to init?
713 * Must be tested carefully with our init scripts first. */
714 return RTEXITCODE_SUCCESS;
715}
716
Note: See TracBrowser for help on using the repository browser.

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