VirtualBox

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

最後變更 在這個檔案從93423是 93385,由 vboxsync 提交於 3 年 前

Additions: make VBoxDRMClient and VBoxClient --vmsvga-session available only from Linux, bugref:10185.

For the rest of platforms (Solaris, FreeBSD) --vmsvga-session is an alias to --vmsvga.

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

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