VirtualBox

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

Last change on this file since 97744 was 96878, checked in by vboxsync, 2 years ago

Additions: X11: Print out if Wayland environment detected or not, bugref:10134.

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