/** @file * * VirtualBox Guest Service: * Linux guest. */ /* * Copyright (C) 2006-2007 Sun Microsystems, Inc. * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 USA or visit http://www.sun.com if you need * additional information or have any questions. */ #include #include #include #include #include #include #include /* For exit */ #include #include #include #include #include #include #include "clipboard.h" #ifdef DYNAMIC_RESIZE # include "displaychange.h" # ifdef SEAMLESS_GUEST # include "seamless.h" # endif #endif #define TRACE RTPrintf("%s: %d\n", __PRETTY_FUNCTION__, __LINE__); Log(("%s: %d\n", __PRETTY_FUNCTION__, __LINE__)) static int (*gpfnOldIOErrorHandler)(Display *) = NULL; /* Make these global so that the destructors are called if we make an "emergency exit", i.e. a (handled) signal or an X11 error. */ #ifdef DYNAMIC_RESIZE VBoxGuestDisplayChangeMonitor gDisplayChange; # ifdef SEAMLESS_GUEST /** Our instance of the seamless class. This only makes sense if dynamic resizing is enabled. */ VBoxGuestSeamless gSeamless; # endif /* SEAMLESS_GUEST defined */ #endif /* DYNAMIC_RESIZE */ #ifdef VBOX_X11_CLIPBOARD VBoxGuestClipboard gClipboard; #endif /** * Drop the programmes privileges to the caller's. * @returns IPRT status code * @todo move this into the R3 guest library */ int vboxClientDropPrivileges(void) { int rc = VINF_SUCCESS; int rcSystem, rcErrno; LogFlowFunc(("\n")); #ifdef _POSIX_SAVED_IDS rcSystem = setuid(getuid()); #else rcSystem = setreuid(-1, getuid()); #endif if (rcSystem < 0) { rcErrno = errno; rc = RTErrConvertFromErrno(rcErrno); LogRel(("VBoxClient: failed to drop privileges, error %Rrc.\n", rc)); } LogFlowFunc(("returning %Rrc\n", rc)); return rc; } /** * Xlib error handler for certain errors that we can't avoid. */ int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError) { char errorText[1024]; if (pError->error_code == BadAtom) { /* This can be triggered in debug builds if a guest application passes a bad atom in its list of supported clipboard formats. As such it is harmless. */ Log(("VBoxClient: ignoring BadAtom error and returning\n")); return 0; } if (pError->error_code == BadWindow) { /* This can be triggered if a guest application destroys a window before we notice. */ Log(("VBoxClient: ignoring BadWindow error and returning\n")); return 0; } XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText)); LogRel(("VBoxClient: 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)); /** Disable seamless mode */ VbglR3SeamlessSetCap(false); VbglR3Term(); exit(1); } /** * Xlib error handler for fatal errors. This often means that the programme is still running * when X exits. */ int vboxClientXLibIOErrorHandler(Display *pDisplay) { Log(("VBoxClient: 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")); /** Disable seamless mode */ VbglR3SeamlessSetCap(false); VbglR3Term(); return gpfnOldIOErrorHandler(pDisplay); } /** * A standard signal handler which cleans up and exits. Our global static objects will * be cleaned up properly as we exit using "exit". */ void vboxClientSignalHandler(int cSignal) { Log(("VBoxClient: terminated with signal %d\n", cSignal)); /** Disable seamless mode */ VbglR3SeamlessSetCap(false); RTPrintf(("VBoxClient: terminating...\n")); /* don't call VbglR3Term() here otherwise the /dev/vboxadd filehandle is closed */ /* Our pause() call will now return and exit. */ } /** * Reset all standard termination signals to call our signal handler, which cleans up * and exits. */ void vboxClientSetSignalHandlers(void) { struct sigaction sigAction; LogFlowFunc(("\n")); sigAction.sa_handler = vboxClientSignalHandler; sigemptyset(&sigAction.sa_mask); sigAction.sa_flags = 0; sigaction(SIGHUP, &sigAction, NULL); sigaction(SIGINT, &sigAction, NULL); sigaction(SIGQUIT, &sigAction, NULL); sigaction(SIGABRT, &sigAction, NULL); sigaction(SIGPIPE, &sigAction, NULL); sigaction(SIGALRM, &sigAction, NULL); sigaction(SIGTERM, &sigAction, NULL); sigaction(SIGUSR1, &sigAction, NULL); sigaction(SIGUSR2, &sigAction, NULL); LogFlowFunc(("returning\n")); } /** * Print out a usage message and exit with success. */ void vboxClientUsage(const char *pcszFileName) { RTPrintf("Usage: %s [-d|--nodaemon]\n", pcszFileName); RTPrintf("Start the VirtualBox X Window System guest services.\n\n"); RTPrintf("Options:\n"); RTPrintf(" -d, --nodaemon do not lower privileges and continue running as a system\n"); RTPrintf(" service\n"); RTPrintf("\n"); exit(0); } /** * The main loop for the VBoxClient daemon. */ int main(int argc, char *argv[]) { int rcClipboard, rc = VINF_SUCCESS; const char *pszFileName = RTPathFilename(argv[0]); bool fDaemonise = true; if (NULL == pszFileName) pszFileName = "VBoxClient"; /* Initialise our runtime before all else. */ RTR3Init(); /* Parse our option(s) */ /** @todo Use RTGetOpt() if the arguments become more complex. */ for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--nodaemon")) fDaemonise = false; else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { vboxClientUsage(pszFileName); exit(0); } else { RTPrintf("%s: unrecognized option `%s'\n", pszFileName, argv[i]); RTPrintf("Try `%s --help' for more information\n", pszFileName); exit(1); } } if (fDaemonise) { rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, NULL); if (RT_FAILURE(rc)) { RTPrintf("VBoxClient: failed to daemonize. exiting.\n"); #ifdef DEBUG RTPrintf("Error %Rrc\n", rc); #endif return 1; } } /* Initialise the guest library. */ if (RT_FAILURE(VbglR3Init())) { RTPrintf("Failed to connect to the VirtualBox kernel service\n"); return 1; } if (fDaemonise && RT_FAILURE(vboxClientDropPrivileges())) return 1; LogRel(("VBoxClient: starting...\n")); /* Initialise threading in X11 and in Xt. */ if (!XInitThreads() || !XtToolkitThreadInitialize()) { LogRel(("VBoxClient: error initialising threads in X11, exiting.\n")); return 1; } /* Set an X11 error handler, so that we don't die when we get unavoidable errors. */ XSetErrorHandler(vboxClientXLibErrorHandler); /* Set an X11 I/O error handler, so that we can shutdown properly on fatal errors. */ gpfnOldIOErrorHandler = XSetIOErrorHandler(vboxClientXLibIOErrorHandler); vboxClientSetSignalHandlers(); try { #ifdef VBOX_X11_CLIPBOARD /* Connect to the host clipboard. */ LogRel(("VBoxClient: starting clipboard Guest Additions...\n")); rcClipboard = gClipboard.init(); if (RT_FAILURE(rcClipboard)) { LogRel(("VBoxClient: vboxClipboardConnect failed with rc = %Rrc\n", rcClipboard)); } #endif /* VBOX_X11_CLIPBOARD defined */ #ifdef DYNAMIC_RESIZE LogRel(("VBoxClient: starting dynamic guest resizing...\n")); rc = gDisplayChange.init(); if (RT_FAILURE(rc)) { LogRel(("VBoxClient: failed to start dynamic guest resizing, rc = %Rrc\n", rc)); } # ifdef SEAMLESS_GUEST if (RT_SUCCESS(rc)) { LogRel(("VBoxClient: starting seamless Guest Additions...\n")); rc = gSeamless.init(); if (RT_FAILURE(rc)) { LogRel(("VBoxClient: failed to start seamless Additions, rc = %Rrc\n", rc)); } } # endif /* SEAMLESS_GUEST defined */ #endif /* DYNAMIC_RESIZE defined */ } catch (std::exception e) { LogRel(("VBoxClient: failed to initialise Guest Additions - caught exception: %s\n", e.what())); rc = VERR_UNRESOLVED_ERROR; } catch (...) { LogRel(("VBoxClient: failed to initialise Guest Additions - caught unknown exception.\n")); rc = VERR_UNRESOLVED_ERROR; } LogRel(("VBoxClient: sleeping...\n")); pause(); LogRel(("VBoxClient: exiting...\n")); try { /* r=frank: Why all these 2s delays? What are we waiting for? */ #ifdef DYNAMIC_RESIZE # ifdef SEAMLESS_GUEST LogRel(("VBoxClient: shutting down seamless Guest Additions...\n")); gSeamless.uninit(2000); # endif /* SEAMLESS_GUEST defined */ LogRel(("VBoxClient: shutting down dynamic guest resizing...\n")); gDisplayChange.uninit(2000); #endif /* DYNAMIC_RESIZE defined */ #ifdef VBOX_X11_CLIPBOARD /* Connect to the host clipboard. */ LogRel(("VBoxClient: shutting down clipboard Guest Additions...\n")); gClipboard.uninit(2000); #endif /* VBOX_X11_CLIPBOARD defined */ } catch (std::exception e) { LogRel(("VBoxClient: failed to shut down Guest Additions - caught exception: %s\n", e.what())); rc = VERR_UNRESOLVED_ERROR; } catch (...) { LogRel(("VBoxClient: failed to shut down Guest Additions - caught unknown exception.\n")); rc = VERR_UNRESOLVED_ERROR; } VbglR3Term(); return RT_SUCCESS(rc) ? 0 : 1; }