VirtualBox

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

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

Additions: X11: VBoxClient: move Wayland detection function into common place, bugref:10134.

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

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