VirtualBox

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

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

Additions: X11: main: prevent deadlock on service termination, bugref:10032.

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