VirtualBox

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

Last change on this file since 99600 was 99585, checked in by vboxsync, 19 months ago

Guest Additions/VBoxClient: Implemented "--session-type" parameter and automatic detection of the session type, to detect the current desktop environment at runtime. Makes now use of the VBGHLog facilities. bugref:10427

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 31.7 KB
Line 
1/* $Id: main.cpp 99585 2023-05-03 15:12:56Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions - X11 Client.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 <iprt/process.h>
48#include <iprt/linux/sysfs.h>
49#include <VBox/VBoxGuestLib.h>
50#include <VBox/err.h>
51#include <VBox/version.h>
52#include "VBoxClient.h"
53
54
55/*********************************************************************************************************************************
56* Defines *
57*********************************************************************************************************************************/
58#define VBOXCLIENT_OPT_SERVICES 980
59#define VBOXCLIENT_OPT_CHECKHOSTVERSION VBOXCLIENT_OPT_SERVICES
60#define VBOXCLIENT_OPT_CLIPBOARD VBOXCLIENT_OPT_SERVICES + 1
61#define VBOXCLIENT_OPT_DRAGANDDROP VBOXCLIENT_OPT_SERVICES + 2
62#define VBOXCLIENT_OPT_SEAMLESS VBOXCLIENT_OPT_SERVICES + 3
63#define VBOXCLIENT_OPT_VMSVGA VBOXCLIENT_OPT_SERVICES + 4
64#define VBOXCLIENT_OPT_VMSVGA_SESSION VBOXCLIENT_OPT_SERVICES + 5
65#define VBOXCLIENT_OPT_DISPLAY VBOXCLIENT_OPT_SERVICES + 6
66#define VBOXCLIENT_OPT_SESSION_TYPE VBOXCLIENT_OPT_SERVICES + 7
67
68
69/*********************************************************************************************************************************
70* Local structures *
71*********************************************************************************************************************************/
72/**
73 * The global service state.
74 */
75typedef struct VBCLSERVICESTATE
76{
77 /** Pointer to the service descriptor. */
78 PVBCLSERVICE pDesc;
79 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
80 RTTHREAD Thread;
81 /** Whether Pre-init was called. */
82 bool fPreInited;
83 /** Shutdown indicator. */
84 bool volatile fShutdown;
85 /** Indicator set by the service thread exiting. */
86 bool volatile fStopped;
87 /** Whether the service was started or not. */
88 bool fStarted;
89} VBCLSERVICESTATE;
90/** Pointer to a service state. */
91typedef VBCLSERVICESTATE *PVBCLSERVICESTATE;
92
93
94/*********************************************************************************************************************************
95* Global Variables *
96*********************************************************************************************************************************/
97/** The global service state. */
98VBCLSERVICESTATE g_Service = { 0 };
99
100/** Set by the signal handler when being called. */
101static volatile bool g_fSignalHandlerCalled = false;
102/** Critical section for the signal handler. */
103static RTCRITSECT g_csSignalHandler;
104/** Flag indicating Whether the service starts in daemonized mode or not. */
105bool g_fDaemonized = false;
106/** The name of our pidfile. It is global for the benefit of the cleanup
107 * routine. */
108static char g_szPidFile[RTPATH_MAX] = "";
109/** The file handle of our pidfile. It is global for the benefit of the
110 * cleanup routine. */
111static RTFILE g_hPidFile;
112/** The name of pidfile for parent (control) process. */
113static char g_szControlPidFile[RTPATH_MAX] = "";
114/** The file handle of parent process pidfile. */
115static RTFILE g_hControlPidFile;
116/** The session type to use. */
117static VBGHSESSIONTYPE g_enmSessionType = VBGHSESSIONTYPE_AUTO;
118
119/** Global critical section held during the clean-up routine (to prevent it
120 * being called on multiple threads at once) or things which may not happen
121 * during clean-up (e.g. pausing and resuming the service).
122 */
123static RTCRITSECT g_critSect;
124/** Counter of how often our daemon has been respawned. */
125unsigned g_cRespawn = 0;
126/** Logging verbosity level. */
127unsigned g_cVerbosity = 0;
128/** Absolute path to log file, if any. */
129static char g_szLogFile[RTPATH_MAX + 128] = "";
130/** Set by the signal handler when SIGUSR1 received. */
131static volatile bool g_fProcessReloadRequested = false;
132
133
134/**
135 * Shut down if we get a signal or something.
136 *
137 * This is extern so that we can call it from other compilation units.
138 */
139void VBClShutdown(bool fExit /*=true*/)
140{
141 /* We never release this, as we end up with a call to exit(3) which is not
142 * async-safe. Unless we fix this application properly, we should be sure
143 * never to exit from anywhere except from this method. */
144 int rc = RTCritSectEnter(&g_critSect);
145 if (RT_FAILURE(rc))
146 VBClLogFatalError("Failure while acquiring the global critical section, rc=%Rrc\n", rc);
147
148 /* Ask service to stop. */
149 if (g_Service.pDesc &&
150 g_Service.pDesc->pfnStop)
151 {
152 ASMAtomicWriteBool(&g_Service.fShutdown, true);
153 g_Service.pDesc->pfnStop();
154
155 }
156
157 if (g_szPidFile[0] && g_hPidFile)
158 VbglR3ClosePidFile(g_szPidFile, g_hPidFile);
159
160 VBClLogDestroy();
161
162 if (fExit)
163 exit(RTEXITCODE_SUCCESS);
164}
165
166/**
167 * Returns the current session type.
168 *
169 * @returns The session type.
170 */
171VBGHSESSIONTYPE VBClGetSessionType(void)
172{
173 return g_enmSessionType;
174}
175
176/**
177 * Xlib error handler for certain errors that we can't avoid.
178 */
179static int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
180{
181 char errorText[1024];
182
183 XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
184 VBClLogError("An X Window protocol error occurred: %s (error code %d). Request code: %d, minor code: %d, serial number: %d\n",
185 errorText, pError->error_code, pError->request_code, pError->minor_code, pError->serial);
186 return 0;
187}
188
189/**
190 * Xlib error handler for fatal errors. This often means that the programme is still running
191 * when X exits.
192 */
193static int vboxClientXLibIOErrorHandler(Display *pDisplay)
194{
195 RT_NOREF1(pDisplay);
196 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");
197 VBClShutdown();
198 return 0; /* We should never reach this. */
199}
200
201/**
202 * A standard signal handler which cleans up and exits.
203 */
204static void vboxClientSignalHandler(int iSignal)
205{
206 int rc = RTCritSectEnter(&g_csSignalHandler);
207 if (RT_SUCCESS(rc))
208 {
209 if (g_fSignalHandlerCalled)
210 {
211 RTCritSectLeave(&g_csSignalHandler);
212 return;
213 }
214
215 VBClLogVerbose(2, "Received signal %d\n", iSignal);
216 g_fSignalHandlerCalled = true;
217
218 /* In our internal convention, when VBoxClient process receives SIGUSR1,
219 * this is a trigger for restarting a process with exec() call. Usually
220 * happens as a result of Guest Additions update in order to seamlessly
221 * restart newly installed binaries. */
222 if (iSignal == SIGUSR1)
223 g_fProcessReloadRequested = true;
224
225 /* Leave critical section before stopping the service. */
226 RTCritSectLeave(&g_csSignalHandler);
227
228 if ( g_Service.pDesc
229 && g_Service.pDesc->pfnStop)
230 {
231 VBClLogVerbose(2, "Notifying service to stop ...\n");
232
233 /* Signal the service to stop. */
234 ASMAtomicWriteBool(&g_Service.fShutdown, true);
235
236 g_Service.pDesc->pfnStop();
237
238 VBClLogVerbose(2, "Service notified to stop, waiting on worker thread to stop ...\n");
239 }
240 }
241}
242
243/**
244 * Reset all standard termination signals to call our signal handler.
245 */
246static int vboxClientSignalHandlerInstall(void)
247{
248 struct sigaction sigAction;
249 sigAction.sa_handler = vboxClientSignalHandler;
250 sigemptyset(&sigAction.sa_mask);
251 sigAction.sa_flags = 0;
252 sigaction(SIGHUP, &sigAction, NULL);
253 sigaction(SIGINT, &sigAction, NULL);
254 sigaction(SIGQUIT, &sigAction, NULL);
255 sigaction(SIGPIPE, &sigAction, NULL);
256 sigaction(SIGALRM, &sigAction, NULL);
257 sigaction(SIGTERM, &sigAction, NULL);
258 sigaction(SIGUSR1, &sigAction, NULL);
259 sigaction(SIGUSR2, &sigAction, NULL);
260
261 return RTCritSectInit(&g_csSignalHandler);
262}
263
264/**
265 * Uninstalls a previously installed signal handler.
266 */
267static int vboxClientSignalHandlerUninstall(void)
268{
269 signal(SIGTERM, SIG_DFL);
270#ifdef SIGBREAK
271 signal(SIGBREAK, SIG_DFL);
272#endif
273
274 return RTCritSectDelete(&g_csSignalHandler);
275}
276
277/**
278 * Print out a usage message and exit with success.
279 */
280static void vboxClientUsage(const char *pcszFileName)
281{
282 RTPrintf(VBOX_PRODUCT " VBoxClient "
283 VBOX_VERSION_STRING "\n"
284 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
285
286 RTPrintf("Usage: %s "
287#ifdef VBOX_WITH_SHARED_CLIPBOARD
288 "--clipboard|"
289#endif
290#ifdef VBOX_WITH_DRAG_AND_DROP
291 "--draganddrop|"
292#endif
293#ifdef VBOX_WITH_GUEST_PROPS
294 "--checkhostversion|"
295#endif
296#ifdef VBOX_WITH_SEAMLESS
297 "--seamless|"
298#endif
299#ifdef VBOX_WITH_VMSVGA
300 "--vmsvga|"
301 "--vmsvga-session"
302#endif
303 "\n[-d|--nodaemon]\n", pcszFileName);
304 RTPrintf("\n");
305 RTPrintf("Options:\n");
306#ifdef VBOX_WITH_SHARED_CLIPBOARD
307 RTPrintf(" --clipboard starts the shared clipboard service\n");
308#endif
309#ifdef VBOX_WITH_DRAG_AND_DROP
310 RTPrintf(" --draganddrop starts the drag and drop service\n");
311#endif
312#ifdef VBOX_WITH_GUEST_PROPS
313 RTPrintf(" --checkhostversion starts the host version notifier service\n");
314#endif
315#ifdef VBOX_WITH_SEAMLESS
316 RTPrintf(" --seamless starts the seamless windows service\n");
317#endif
318#ifdef VBOX_WITH_VMSVGA
319 RTPrintf(" --vmsvga starts VMSVGA dynamic resizing for X11/Wayland guests\n");
320#ifdef RT_OS_LINUX
321 RTPrintf(" --vmsvga-session starts Desktop Environment specific screen assistant for X11/Wayland guests\n"
322 " (VMSVGA graphics adapter only)\n");
323#else
324 RTPrintf(" --vmsvga-session an alias for --vmsvga\n");
325#endif
326 RTPrintf(" --display starts VMSVGA dynamic resizing for legacy guests\n");
327#endif
328 RTPrintf(" --session-type specifies the session type to use (auto, x11, wayland)\n");
329 RTPrintf(" -f, --foreground run in the foreground (no daemonizing)\n");
330 RTPrintf(" -d, --nodaemon continues running as a system service\n");
331 RTPrintf(" -h, --help shows this help text\n");
332 RTPrintf(" -l, --logfile <path> enables logging to a file\n");
333 RTPrintf(" -v, --verbose increases logging verbosity level\n");
334 RTPrintf(" -V, --version shows version information\n");
335 RTPrintf("\n");
336}
337
338/**
339 * Complains about seeing more than one service specification.
340 *
341 * @returns RTEXITCODE_SYNTAX.
342 */
343static int vbclSyntaxOnlyOneService(void)
344{
345 RTMsgError("More than one service specified! Only one, please.");
346 return RTEXITCODE_SYNTAX;
347}
348
349/**
350 * The service thread.
351 *
352 * @returns Whatever the worker function returns.
353 * @param ThreadSelf My thread handle.
354 * @param pvUser The service index.
355 */
356static DECLCALLBACK(int) vbclThread(RTTHREAD ThreadSelf, void *pvUser)
357{
358 PVBCLSERVICESTATE pState = (PVBCLSERVICESTATE)pvUser;
359 AssertPtrReturn(pState, VERR_INVALID_POINTER);
360
361#ifndef RT_OS_WINDOWS
362 /*
363 * Block all signals for this thread. Only the main thread will handle signals.
364 */
365 sigset_t signalMask;
366 sigfillset(&signalMask);
367 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
368#endif
369
370 AssertPtrReturn(pState->pDesc->pfnWorker, VERR_INVALID_POINTER);
371 int rc = pState->pDesc->pfnWorker(&pState->fShutdown);
372
373 VBClLogVerbose(2, "Worker loop ended with %Rrc\n", rc);
374
375 ASMAtomicXchgBool(&pState->fShutdown, true);
376 RTThreadUserSignal(ThreadSelf);
377 return rc;
378}
379
380/**
381 * Wait for SIGUSR1 and re-exec.
382 */
383static void vbclHandleUpdateStarted(char *const argv[])
384{
385 /* Context of parent process */
386 sigset_t signalMask;
387 int iSignal;
388 int rc;
389
390 /* Release reference to guest driver. */
391 VbglR3Term();
392
393 sigemptyset(&signalMask);
394 sigaddset(&signalMask, SIGUSR1);
395 rc = pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
396
397 if (rc == 0)
398 {
399 LogRel(("%s: waiting for Guest Additions installation to be completed\n",
400 g_Service.pDesc->pszDesc));
401
402 /* Wait for SIGUSR1. */
403 rc = sigwait(&signalMask, &iSignal);
404 if (rc == 0)
405 {
406 LogRel(("%s: Guest Additions installation to be completed, reloading service\n",
407 g_Service.pDesc->pszDesc));
408
409 /* Release pidfile, otherwise new VBoxClient instance won't be able to quire it. */
410 VBClShutdown(false);
411
412 rc = RTProcCreate(argv[0], argv, RTENV_DEFAULT,
413 RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SEARCH_PATH, NULL);
414 if (RT_SUCCESS(rc))
415 LogRel(("%s: service restarted\n", g_Service.pDesc->pszDesc));
416 else
417 LogRel(("%s: cannot replace running image with %s (%s), automatic service reloading has failed\n",
418 g_Service.pDesc->pszDesc, argv[0], strerror(errno)));
419 }
420 else
421 LogRel(("%s: cannot wait for signal (%s), automatic service reloading has failed\n",
422 g_Service.pDesc->pszDesc, strerror(errno)));
423 }
424 else
425 LogRel(("%s: failed to setup signal handler, automatic service reloading has failed\n",
426 g_Service.pDesc->pszDesc));
427
428 exit(RT_BOOL(rc != 0));
429}
430
431/**
432 * Compose pidfile name.
433 *
434 * @returns IPRT status code.
435 * @param szBuf Buffer to store pidfile name into.
436 * @param cbBuf Size of buffer.
437 * @param szTemplate Null-terminated string which contains pidfile name.
438 * @param fParentProcess Whether pidfile path should be composed for
439 * parent (control) process or for a child (actual
440 * service) process.
441 */
442static int vbclGetPidfileName(char *szBuf, size_t cbBuf, const char *szTemplate,
443 bool fParentProcess)
444{
445 int rc;
446 char pszActiveTTY[128];
447 size_t cchRead;
448
449 RT_ZERO(pszActiveTTY);
450
451 AssertPtrReturn(szBuf, VERR_INVALID_PARAMETER);
452 AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
453 AssertPtrReturn(szTemplate, VERR_INVALID_PARAMETER);
454
455 rc = RTPathUserHome(szBuf, cbBuf);
456 if (RT_FAILURE(rc))
457 VBClLogFatalError("%s: getting home directory failed: %Rrc\n",
458 g_Service.pDesc->pszDesc, rc);
459
460 if (RT_SUCCESS(rc))
461 rc = RTPathAppend(szBuf, cbBuf, szTemplate);
462
463#ifdef RT_OS_LINUX
464 if (RT_SUCCESS(rc))
465 rc = RTLinuxSysFsReadStrFile(pszActiveTTY, sizeof(pszActiveTTY) - 1 /* reserve last byte for string termination */,
466 &cchRead, "class/tty/tty0/active");
467 if (RT_SUCCESS(rc))
468 {
469 RTStrCat(szBuf, cbBuf, "-");
470 RTStrCat(szBuf, cbBuf, pszActiveTTY);
471 }
472 else
473 VBClLogInfo("%s: cannot detect currently active tty device, "
474 "multiple service instances for a single user will not be allowed, rc=%Rrc",
475 g_Service.pDesc->pszDesc, rc);
476#endif /* RT_OS_LINUX */
477
478 if (RT_SUCCESS(rc))
479 RTStrCat(szBuf, cbBuf, fParentProcess ? "-control.pid" : "-service.pid");
480
481 if (RT_FAILURE(rc))
482 VBClLogFatalError("%s: reating PID file path failed: %Rrc\n",
483 g_Service.pDesc->pszDesc, rc);
484
485 return rc;
486}
487
488/**
489 * The main loop for the VBoxClient daemon.
490 */
491int main(int argc, char *argv[])
492{
493 /* Note: No VBClLogXXX calls before actually creating the log. */
494
495 /* Initialize our runtime before all else. */
496 int rc = RTR3InitExe(argc, &argv, 0);
497 if (RT_FAILURE(rc))
498 return RTMsgInitFailure(rc);
499
500 /* A flag which is returned to the parent process when Guest Additions update started. */
501 bool fUpdateStarted = false;
502
503 /* Get our file name for usage info and hints. */
504 const char *pcszFileName = RTPathFilename(argv[0]);
505 if (!pcszFileName)
506 pcszFileName = "VBoxClient";
507
508 /* Parse our option(s). */
509 static const RTGETOPTDEF s_aOptions[] =
510 {
511 { "--nodaemon", 'd', RTGETOPT_REQ_NOTHING },
512 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
513 { "--help", 'h', RTGETOPT_REQ_NOTHING },
514 { "--logfile", 'l', RTGETOPT_REQ_STRING },
515 { "--version", 'V', RTGETOPT_REQ_NOTHING },
516 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
517
518 /* Services */
519#ifdef VBOX_WITH_GUEST_PROPS
520 { "--checkhostversion", VBOXCLIENT_OPT_CHECKHOSTVERSION, RTGETOPT_REQ_NOTHING },
521#endif
522#ifdef VBOX_WITH_SHARED_CLIPBOARD
523 { "--clipboard", VBOXCLIENT_OPT_CLIPBOARD, RTGETOPT_REQ_NOTHING },
524#endif
525#ifdef VBOX_WITH_DRAG_AND_DROP
526 { "--draganddrop", VBOXCLIENT_OPT_DRAGANDDROP, RTGETOPT_REQ_NOTHING },
527#endif
528#ifdef VBOX_WITH_SEAMLESS
529 { "--seamless", VBOXCLIENT_OPT_SEAMLESS, RTGETOPT_REQ_NOTHING },
530#endif
531#ifdef VBOX_WITH_VMSVGA
532 { "--vmsvga", VBOXCLIENT_OPT_VMSVGA, RTGETOPT_REQ_NOTHING },
533 { "--vmsvga-session", VBOXCLIENT_OPT_VMSVGA_SESSION, RTGETOPT_REQ_NOTHING },
534 { "--display", VBOXCLIENT_OPT_DISPLAY, RTGETOPT_REQ_NOTHING },
535#endif
536 { "--session-type", VBOXCLIENT_OPT_SESSION_TYPE, RTGETOPT_REQ_STRING }
537 };
538
539 int ch;
540 RTGETOPTUNION ValueUnion;
541 RTGETOPTSTATE GetState;
542 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
543 if (RT_FAILURE(rc))
544 return RTMsgErrorExitFailure("Failed to parse command line options, rc=%Rrc\n", rc);
545
546 AssertRC(rc);
547
548 bool fDaemonise = true;
549 bool fRespawn = true;
550
551 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
552 {
553 /* For options that require an argument, ValueUnion has received the value. */
554 switch (ch)
555 {
556 case 'd':
557 {
558 fDaemonise = false;
559 break;
560 }
561
562 case 'h':
563 {
564 vboxClientUsage(pcszFileName);
565 return RTEXITCODE_SUCCESS;
566 }
567
568 case 'f':
569 {
570 fDaemonise = false;
571 fRespawn = false;
572 break;
573 }
574
575 case 'l':
576 {
577 rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
578 if (RT_FAILURE(rc))
579 return RTMsgErrorExitFailure("Unable to set log file path, rc=%Rrc\n", rc);
580 break;
581 }
582
583 case 'n':
584 {
585 fRespawn = false;
586 break;
587 }
588
589 case 'v':
590 {
591 g_cVerbosity++;
592 break;
593 }
594
595 case 'V':
596 {
597 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
598 return RTEXITCODE_SUCCESS;
599 }
600
601 /* Services */
602#ifdef VBOX_WITH_GUEST_PROPS
603 case VBOXCLIENT_OPT_CHECKHOSTVERSION:
604 {
605 if (g_Service.pDesc)
606 return vbclSyntaxOnlyOneService();
607 g_Service.pDesc = &g_SvcHostVersion;
608 break;
609 }
610#endif
611#ifdef VBOX_WITH_SHARED_CLIPBOARD
612 case VBOXCLIENT_OPT_CLIPBOARD:
613 {
614 if (g_Service.pDesc)
615 return vbclSyntaxOnlyOneService();
616 g_Service.pDesc = &g_SvcClipboard;
617 break;
618 }
619#endif
620#ifdef VBOX_WITH_DRAG_AND_DROP
621 case VBOXCLIENT_OPT_DRAGANDDROP:
622 {
623 if (g_Service.pDesc)
624 return vbclSyntaxOnlyOneService();
625 g_Service.pDesc = &g_SvcDragAndDrop;
626 break;
627 }
628#endif
629#ifdef VBOX_WITH_SEAMLESS
630 case VBOXCLIENT_OPT_SEAMLESS:
631 {
632 if (g_Service.pDesc)
633 return vbclSyntaxOnlyOneService();
634 g_Service.pDesc = &g_SvcSeamless;
635 break;
636 }
637#endif
638#ifdef VBOX_WITH_VMSVGA
639 case VBOXCLIENT_OPT_VMSVGA:
640 {
641 if (g_Service.pDesc)
642 return vbclSyntaxOnlyOneService();
643 g_Service.pDesc = &g_SvcDisplaySVGA;
644 break;
645 }
646
647 case VBOXCLIENT_OPT_VMSVGA_SESSION:
648 {
649 if (g_Service.pDesc)
650 return vbclSyntaxOnlyOneService();
651# ifdef RT_OS_LINUX
652 g_Service.pDesc = &g_SvcDisplaySVGASession;
653# else
654 g_Service.pDesc = &g_SvcDisplaySVGA;
655# endif
656 break;
657 }
658
659 case VBOXCLIENT_OPT_DISPLAY:
660 {
661 if (g_Service.pDesc)
662 return vbclSyntaxOnlyOneService();
663 g_Service.pDesc = &g_SvcDisplayLegacy;
664 break;
665 }
666#endif
667 case VBOXCLIENT_OPT_SESSION_TYPE:
668 {
669 if (!RTStrICmp(ValueUnion.psz, "x11"))
670 g_enmSessionType = VBGHSESSIONTYPE_X11;
671 else if (!RTStrICmp(ValueUnion.psz, "wayland"))
672 g_enmSessionType = VBGHSESSIONTYPE_WAYLAND;
673 else if (!RTStrICmp(ValueUnion.psz, "none"))
674 g_enmSessionType = VBGHSESSIONTYPE_NONE;
675 else if (!RTStrICmp(ValueUnion.psz, "auto"))
676 g_enmSessionType = VBGHSESSIONTYPE_AUTO;
677 else
678 {
679 RTMsgError("Session type \"%s\" is invalid; defaulting to \"auto\" instead.\n", ValueUnion.psz);
680 g_enmSessionType = VBGHSESSIONTYPE_AUTO;
681 }
682 break;
683 }
684
685 case VINF_GETOPT_NOT_OPTION:
686 break;
687
688 case VERR_GETOPT_UNKNOWN_OPTION:
689 RT_FALL_THROUGH();
690 default:
691 {
692 if ( g_Service.pDesc
693 && g_Service.pDesc->pfnOption)
694 {
695 rc = g_Service.pDesc->pfnOption(NULL, argc, argv, &GetState.iNext);
696 }
697 else /* No service specified yet. */
698 rc = VERR_NOT_FOUND;
699
700 if (RT_FAILURE(rc))
701 {
702 RTMsgError("unrecognized option '%s'", ValueUnion.psz);
703 RTMsgInfo("Try '%s --help' for more information", pcszFileName);
704 return RTEXITCODE_SYNTAX;
705 }
706 break;
707 }
708
709 } /* switch */
710 } /* while RTGetOpt */
711
712 if (!g_Service.pDesc)
713 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No service specified. Quitting because nothing to do!");
714
715 /* Initialize VbglR3 before we do anything else with the logger. */
716 rc = VbglR3InitUser();
717 if (RT_FAILURE(rc))
718 return RTMsgErrorExitFailure("VbglR3InitUser failed: %Rrc", rc);
719
720 rc = VBClLogCreate(g_szLogFile[0] ? g_szLogFile : "");
721 if (RT_FAILURE(rc))
722 return RTMsgErrorExitFailure("Failed to create release log '%s', rc=%Rrc\n",
723 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
724
725 if (!fDaemonise)
726 {
727 /* If the user is running in "no daemon" mode, send critical logging to stdout as well. */
728 PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
729 if (pReleaseLog)
730 {
731 rc = RTLogDestinations(pReleaseLog, "stdout");
732 if (RT_FAILURE(rc))
733 return RTMsgErrorExitFailure("Failed to redivert error output, rc=%Rrc", rc);
734 }
735 }
736
737 VBClLogInfo("VBoxClient %s r%s started. Verbose level = %d\n",
738 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
739
740 /* Try to detect the current session type early on, if needed. */
741 if (g_enmSessionType == VBGHSESSIONTYPE_AUTO)
742 {
743 g_enmSessionType = VBGHSessionTypeDetect();
744 }
745 else
746 VBClLogInfo("Session type was manually set to: %s\n", VBGHSessionTypeToStr(g_enmSessionType));
747
748 VBClLogInfo("Session type is: %s\n", VBGHSessionTypeToStr(g_enmSessionType));
749
750 VBClLogInfo("Service: %s\n", g_Service.pDesc->pszDesc);
751
752 rc = RTCritSectInit(&g_critSect);
753 if (RT_FAILURE(rc))
754 VBClLogFatalError("Initializing critical section failed: %Rrc\n", rc);
755 if (g_Service.pDesc->pszPidFilePathTemplate)
756 {
757 /* Get pidfile name for parent (control) process. */
758 rc = vbclGetPidfileName(g_szControlPidFile, sizeof(g_szControlPidFile), g_Service.pDesc->pszPidFilePathTemplate, true);
759 if (RT_FAILURE(rc))
760 return RTEXITCODE_FAILURE;
761
762 /* Get pidfile name for service process. */
763 rc = vbclGetPidfileName(g_szPidFile, sizeof(g_szPidFile), g_Service.pDesc->pszPidFilePathTemplate, false);
764 if (RT_FAILURE(rc))
765 return RTEXITCODE_FAILURE;
766 }
767
768 if (fDaemonise)
769 {
770 rc = VbglR3DaemonizeEx(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn,
771 true /* fReturnOnUpdate */, &fUpdateStarted, g_szControlPidFile, &g_hControlPidFile);
772 /* This combination only works in context of parent process. */
773 if (RT_SUCCESS(rc) && fUpdateStarted)
774 vbclHandleUpdateStarted(argv);
775 }
776
777 if (RT_FAILURE(rc))
778 VBClLogFatalError("Daemonizing service failed: %Rrc\n", rc);
779
780 if (g_szPidFile[0])
781 {
782 rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
783 if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */
784 {
785 VBClLogInfo("%s: service already running, exitting\n",
786 g_Service.pDesc->pszDesc);
787 return RTEXITCODE_SUCCESS;
788 }
789 if (RT_FAILURE(rc))
790 {
791 VBClLogFatalError("Creating PID file %s failed: %Rrc\n", g_szPidFile, rc);
792 return RTEXITCODE_FAILURE;
793 }
794 }
795
796 if (g_enmSessionType == VBGHSESSIONTYPE_X11)
797 {
798 /* This should never be called twice in one process - in fact one Display
799 * object should probably never be used from multiple threads anyway. */
800 if (!XInitThreads())
801 return RTMsgErrorExitFailure("Failed to initialize X11 threads\n");
802 /* Set an X11 error handler, so that we don't die when we get unavoidable
803 * errors. */
804 XSetErrorHandler(vboxClientXLibErrorHandler);
805 /* Set an X11 I/O error handler, so that we can shutdown properly on
806 * fatal errors. */
807 XSetIOErrorHandler(vboxClientXLibIOErrorHandler);
808 }
809
810 bool fSignalHandlerInstalled = false;
811 if (RT_SUCCESS(rc))
812 {
813 rc = vboxClientSignalHandlerInstall();
814 if (RT_SUCCESS(rc))
815 fSignalHandlerInstalled = true;
816 }
817
818 if ( RT_SUCCESS(rc)
819 && g_Service.pDesc->pfnInit)
820 {
821 VBClLogInfo("Initializing service ...\n");
822 rc = g_Service.pDesc->pfnInit();
823 }
824
825 if (RT_SUCCESS(rc))
826 {
827 VBClLogInfo("Creating worker thread ...\n");
828 rc = RTThreadCreate(&g_Service.Thread, vbclThread, (void *)&g_Service, 0,
829 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_Service.pDesc->pszName);
830 if (RT_FAILURE(rc))
831 {
832 VBClLogError("Creating worker thread failed, rc=%Rrc\n", rc);
833 }
834 else
835 {
836 g_Service.fStarted = true;
837
838 /* Wait for the thread to initialize. */
839 /** @todo There is a race between waiting and checking
840 * the fShutdown flag of a thread here and processing
841 * the thread's actual worker loop. If the thread decides
842 * to exit the loop before we skipped the fShutdown check
843 * below the service will fail to start! */
844 /** @todo This presumably means either a one-shot service or that
845 * something has gone wrong. In the second case treating it as failure
846 * to start is probably right, so we need a way to signal the first
847 * rather than leaving the idle thread hanging around. A flag in the
848 * service description? */
849 RTThreadUserWait(g_Service.Thread, RT_MS_1MIN);
850 if (g_Service.fShutdown)
851 {
852 VBClLogError("Service failed to start!\n");
853 rc = VERR_GENERAL_FAILURE;
854 }
855 else
856 {
857 VBClLogInfo("Service started\n");
858
859 int rcThread;
860 rc = RTThreadWait(g_Service.Thread, RT_INDEFINITE_WAIT, &rcThread);
861 if (RT_SUCCESS(rc))
862 rc = rcThread;
863
864 if (RT_FAILURE(rc))
865 VBClLogError("Waiting on worker thread to stop failed, rc=%Rrc\n", rc);
866
867 if (g_Service.pDesc->pfnTerm)
868 {
869 VBClLogInfo("Terminating service\n");
870
871 int rc2 = g_Service.pDesc->pfnTerm();
872 if (RT_SUCCESS(rc))
873 rc = rc2;
874
875 if (RT_SUCCESS(rc))
876 {
877 VBClLogInfo("Service terminated\n");
878 }
879 else
880 VBClLogError("Service failed to terminate, rc=%Rrc\n", rc);
881 }
882 }
883 }
884 }
885
886 if (RT_FAILURE(rc))
887 {
888 if (rc == VERR_NOT_AVAILABLE)
889 VBClLogInfo("Service is not availabe, skipping\n");
890 else if (rc == VERR_NOT_SUPPORTED)
891 VBClLogInfo("Service is not supported on this platform, skipping\n");
892 else
893 VBClLogError("Service ended with error %Rrc\n", rc);
894 }
895 else
896 VBClLogVerbose(2, "Service ended\n");
897
898 if (fSignalHandlerInstalled)
899 {
900 int rc2 = vboxClientSignalHandlerUninstall();
901 AssertRC(rc2);
902 }
903
904 VBClShutdown(false /*fExit*/);
905
906 /** @todo r=andy Should we return an appropriate exit code if the service failed to init?
907 * Must be tested carefully with our init scripts first. */
908 return g_fProcessReloadRequested ? VBGLR3EXITCODERELOAD : RTEXITCODE_SUCCESS;
909}
910
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