VirtualBox

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

Last change on this file since 100246 was 100246, checked in by vboxsync, 11 months ago

Additions: Linux: Added a skeleton for Wayland service, bugref:10194.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use