VirtualBox

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

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

Additions/VBoxClient: Removed command line options which never were implemented, but added command line options to the (syntax) help which actually *are* there.

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