VirtualBox

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

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

scm copyright and license note update

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