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