VirtualBox

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

Last change on this file since 86872 was 86872, checked in by vboxsync, 4 years ago

Additions/VBoxClient: Big revamp of the internal service handling and termination fixes. A service now runs as part of a worker thread, while the main thread is used for initialization / shutdown and signal handling [build fix].

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