VirtualBox

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

最後變更 在這個檔案從98474是 98474,由 vboxsync 提交於 2 年 前

Additions: X11: Add possibility restart VBoxClient processes during Guest Additions update, bugref:10359.

This commit makes VBoxClient processes to temporary release reference to vboxguest kernel module and then
restart itself. So module can be reloaded and newly started VBoxClient instance will utilize updated module.

When VBoxClient starts in demonized mode, it forks a child process which represents actual service. Parent
process continues to run and its main function is to restart child when it crashes or terminates with exit
status != 0. This commit makes child process to catch SIGUSR1 and terminate with exit
status VBGLR3EXITCODERELOAD (currently equal to 2). Parent process will detect this and in turn will release
its reference to vboxguest kernel module, allowing to reload it. The parent process will then wait for SIGUSR1
itself. Once received, it will restart itself (loading new, updated VBoxClient process image). This is a part
of the procedure to install and reload/restart Guest Additions kernel modules and user services without
requiring guest reboot.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette