VBoxServiceExec.cpp revision 1bf88a54387e004678654cf5158075f59e3be168
af062818b47340eef15700d2f0211576ba3506eevboxsync * VBoxServiceExec - Host-driven Command Execution.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Copyright (C) 2009 Sun Microsystems, Inc.
af062818b47340eef15700d2f0211576ba3506eevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
af062818b47340eef15700d2f0211576ba3506eevboxsync * available from http://www.virtualbox.org. This file is free software;
af062818b47340eef15700d2f0211576ba3506eevboxsync * you can redistribute it and/or modify it under the terms of the GNU
af062818b47340eef15700d2f0211576ba3506eevboxsync * General Public License (GPL) as published by the Free Software
af062818b47340eef15700d2f0211576ba3506eevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
af062818b47340eef15700d2f0211576ba3506eevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
af062818b47340eef15700d2f0211576ba3506eevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
af062818b47340eef15700d2f0211576ba3506eevboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
af062818b47340eef15700d2f0211576ba3506eevboxsync * additional information or have any questions.
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync/*******************************************************************************
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync* Header Files *
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync*******************************************************************************/
af062818b47340eef15700d2f0211576ba3506eevboxsync/*******************************************************************************
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync* Global Variables *
af062818b47340eef15700d2f0211576ba3506eevboxsync*******************************************************************************/
af062818b47340eef15700d2f0211576ba3506eevboxsync/** The vminfo interval (millseconds). */
af062818b47340eef15700d2f0211576ba3506eevboxsync/** The semaphore we're blocking on. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsyncstatic RTSEMEVENTMULTI g_hExecEvent = NIL_RTSEMEVENTMULTI;
af062818b47340eef15700d2f0211576ba3506eevboxsync/** The guest property service client ID. */
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @copydoc VBOXSERVICE::pfnPreInit */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic DECLCALLBACK(int) VBoxServiceExecPreInit(void)
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @copydoc VBOXSERVICE::pfnOption */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic DECLCALLBACK(int) VBoxServiceExecOption(const char **ppszShort, int argc, char **argv, int *pi)
af062818b47340eef15700d2f0211576ba3506eevboxsync /* no short options */;
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VBoxServiceArgUInt32(argc, argv, "", pi, &g_cMsExecInterval, 1, UINT32_MAX - 1);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync/** @copydoc VBOXSERVICE::pfnInit */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * If not specified, find the right interval default.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * Then create the event sem to block on.
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VbglR3GuestPropConnect(&g_uExecGuestPropSvcClientID);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(3, "Exec: Property Service Client ID: %#x\n", g_uExecGuestPropSvcClientID);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Failed to connect to the guest property service! Error: %Rrc\n", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync * Validates flags for executable guest properties.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @returns VBox status code. Success means they are valid.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pszFlags Pointer to flags to be checked.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic int VBoxServiceExecValidateFlags(const char *pszFlags)
af062818b47340eef15700d2f0211576ba3506eevboxsync * Reads a host transient property.
af062818b47340eef15700d2f0211576ba3506eevboxsync * This will validate the flags to make sure it is a transient property that can
af062818b47340eef15700d2f0211576ba3506eevboxsync * only be change by the host.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @returns VBox status code, fully bitched.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pszPropName The property name.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * @param ppszValue Where to return the value. This is always set
af062818b47340eef15700d2f0211576ba3506eevboxsync * to NULL. Free it using RTStrFree().
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param puTimestamp Where to return the timestamp. This is only set
af062818b47340eef15700d2f0211576ba3506eevboxsync * on success. Optional.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic int VBoxServiceExecReadHostProp(const char *pszPropName, char **ppszValue, uint64_t *puTimestamp)
af062818b47340eef15700d2f0211576ba3506eevboxsync * (Re-)Allocate the buffer and try read the property.
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Failed to allocate %zu bytes\n", cbBuf);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VbglR3GuestPropRead(g_uExecGuestPropSvcClientID, pszPropName,
af062818b47340eef15700d2f0211576ba3506eevboxsync /* try again with a bigger buffer. */
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(2, "Exec: %s not found\n", pszPropName);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Failed to query \"%s\": %Rrc\n", pszPropName, rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync * Validate it and set return values on success.
ee6bcfc59fe3b0230aad85e2ef63d0402b7719b2vboxsync VBoxServiceError("Exec: Flag validation failed for \"%s\": %Rrc; flags=\"%s\"\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(2, "Exec: Read \"%s\" = \"%s\", timestamp %RU64n\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: RTStrDup failed for \"%s\"\n", pszValue);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync break; /* done */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * Frees an argument vector constructed by VBoxServiceExecCreateArgV.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * @param papszArgs The vector to free.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic void VBoxServiceExecFreeArgV(char **papszArgs)
af062818b47340eef15700d2f0211576ba3506eevboxsync * Creates an argument vector out of an executable name and a string containing
af062818b47340eef15700d2f0211576ba3506eevboxsync * the arguments separated by spaces.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @returns VBox status code. Not bitched.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pszExec The executable name.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pszArgs The string containging the arguments.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param ppapszArgs Where to return the argument vector. Not set on
af062818b47340eef15700d2f0211576ba3506eevboxsync * failure. Use VBoxServiceExecFreeArgV to free.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @todo Quoted strings. Do it unix (bourne shell) fashion.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic int VBoxServiceExecCreateArgV(const char *pszExec, const char *pszArgs, char ***ppapszArgs)
af062818b47340eef15700d2f0211576ba3506eevboxsync char **papszArgs = (char **)RTMemAlloc(sizeof(char *) * (cAlloc + 1));
af062818b47340eef15700d2f0211576ba3506eevboxsync * Start by adding the executable name first.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note! We keep the papszArgs fully terminated at all times to keep cleanup simple.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * Parse the argument string and add any arguments found in it.
af062818b47340eef15700d2f0211576ba3506eevboxsync /* skip leading spaces */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* find the of the current word. Quoting is ignored atm. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* resize the vector. */
af062818b47340eef15700d2f0211576ba3506eevboxsync void *pvNew = RTMemRealloc(papszArgs, sizeof(char *) * (cAlloc + 1));
af062818b47340eef15700d2f0211576ba3506eevboxsync /* add it */
af062818b47340eef15700d2f0211576ba3506eevboxsync papszArgs[cUsed++] = RTStrDupN(pszArgs, (uintptr_t)pszEnd - (uintptr_t)pszArgs);
af062818b47340eef15700d2f0211576ba3506eevboxsync /* advance */
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @copydoc VBOXSERVICE::pfnWorker */
af062818b47340eef15700d2f0211576ba3506eevboxsyncDECLCALLBACK(int) VBoxServiceExecWorker(bool volatile *pfShutdown)
af062818b47340eef15700d2f0211576ba3506eevboxsync * Tell the control thread that it can continue
af062818b47340eef15700d2f0211576ba3506eevboxsync * spawning services.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Execution loop.
af062818b47340eef15700d2f0211576ba3506eevboxsync * The thread at the moment does nothing but checking for one specific guest property
af062818b47340eef15700d2f0211576ba3506eevboxsync * for triggering a hard coded sysprep command with parameters given by the host. This
af062818b47340eef15700d2f0211576ba3506eevboxsync * feature was required by the VDI guys.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Later this thread could become a general host->guest executor.. there are some
af062818b47340eef15700d2f0211576ba3506eevboxsync * sketches for this in the code.
af062818b47340eef15700d2f0211576ba3506eevboxsync bool fSysprepDone = false;
af062818b47340eef15700d2f0211576ba3506eevboxsync#if 0 /** @todo r=bird: This code needs reviewing and testing before it can be enabled. */
af062818b47340eef15700d2f0211576ba3506eevboxsync * Get the sysprep command and arguments.
af062818b47340eef15700d2f0211576ba3506eevboxsync * The sysprep executable location is either retrieved from the host
af062818b47340eef15700d2f0211576ba3506eevboxsync * or is in a hard coded location depending on the Windows version.
af062818b47340eef15700d2f0211576ba3506eevboxsync int rc = VBoxServiceExecReadHostProp("/VirtualBox/HostGuest/SysprepExec", &pszSysprepExec, NULL);
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Predefined sys. */
af062818b47340eef15700d2f0211576ba3506eevboxsync char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysPrepCmd, sizeof(szSysPrepCmd), NULL);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = RTPathAppend(szSysPrepCmd, sizeof(szSysPrepCmd), "system32\\sysprep\\sysprep.exe");
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VBoxServiceExecReadHostProp("/VirtualBox/HostGuest/SysprepArgs", &pszSysprepArgs, NULL);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VBoxServiceExecCreateArgV(pszSysprepExec, pszSysprepArgs, &papszArgs);
af062818b47340eef15700d2f0211576ba3506eevboxsync * Execute it synchronously and store the result.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note that RTProcWait should never fail here and
af062818b47340eef15700d2f0211576ba3506eevboxsync * that (the host is screwed if it does though).
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(3, "Exec: Executing sysprep ...\n");
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(3, "Exec: sysprep argv[%u]: \"%s\"\n", i, papszArgs[i]);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = RTProcCreate(pszSysprepExec, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &pid);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, &Status);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(1, "Sysprep returned: %d (reason %d)\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @todo r=bird: Figure out whether you should try re-execute sysprep if it
af062818b47340eef15700d2f0211576ba3506eevboxsync * fails or not. This is not mentioned in the defect. */
af062818b47340eef15700d2f0211576ba3506eevboxsync * Store the result in Set return value so the host knows what happend.
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VbglR3GuestPropWriteValueF(g_uExecGuestPropSvcClientID,
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Failed to write SysprepRet: rc=%Rrc\n", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: RTProcWait failed for sysprep: %Rrc\n", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: VBoxServiceExecCreateArgV: %Rrc\n", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Sysprep executable not found! Search path=%s\n", pszSysprepExec);
af062818b47340eef15700d2f0211576ba3506eevboxsync * Only continue polling if the guest property value is empty/missing
af062818b47340eef15700d2f0211576ba3506eevboxsync * or if the sysprep command is missing.
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceVerbose(1, "Exec: Stopping sysprep processing (rc=%Rrc)\n", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VbglR3GuestPropWriteValueF(g_uExecGuestPropSvcClientID, "/VirtualBox/HostGuest/SysprepVBoxRC", "%d", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Failed to write SysprepVBoxRC: rc=%Rrc\n", rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* temporarily disabled. */
af062818b47340eef15700d2f0211576ba3506eevboxsync 2. Check that the flags indicates that the guest cannot write to it and that it's transient.
af062818b47340eef15700d2f0211576ba3506eevboxsync 3. Check if the timestamp changed.
af062818b47340eef15700d2f0211576ba3506eevboxsync 4. Get the arguments and other stuff.
af062818b47340eef15700d2f0211576ba3506eevboxsync 5. Execute it. This may involve grabbing the output (stderr and/or stdout) and pushing into
af062818b47340eef15700d2f0211576ba3506eevboxsync values afterwards. It may also entail redirecting input to a file containing text from a guest prop value.
af062818b47340eef15700d2f0211576ba3506eevboxsync 6. Set the result values (there will be three, one IPRT style one for everything up to
af062818b47340eef15700d2f0211576ba3506eevboxsync and including RTProcWait and two that mirrors Status.iStatus and Status.enmReason (stringified)).
af062818b47340eef15700d2f0211576ba3506eevboxsync * Block for a while.
af062818b47340eef15700d2f0211576ba3506eevboxsync * The event semaphore takes care of ignoring interruptions and it
af062818b47340eef15700d2f0211576ba3506eevboxsync * allows us to implement service wakeup later.
af062818b47340eef15700d2f0211576ba3506eevboxsync if (*pfShutdown)
af062818b47340eef15700d2f0211576ba3506eevboxsync#ifdef FULL_FEATURED_EXEC
af062818b47340eef15700d2f0211576ba3506eevboxsync Wait for changes to the command value. If that fails for some reason other than timeout / interrupt, fall back on the semaphore.
af062818b47340eef15700d2f0211576ba3506eevboxsync int rc2 = RTSemEventMultiWait(g_hExecEvent, g_cMsExecInterval);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (*pfShutdown)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
af062818b47340eef15700d2f0211576ba3506eevboxsync VBoxServiceError("Exec: Service terminating - RTSemEventMultiWait: %Rrc\n", rc2);
af062818b47340eef15700d2f0211576ba3506eevboxsync rcRet = rc2;
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSemEventMultiDestroy(g_hExecEvent);
af062818b47340eef15700d2f0211576ba3506eevboxsync g_hExecEvent = NIL_RTSEMEVENTMULTI;
af062818b47340eef15700d2f0211576ba3506eevboxsync return rcRet;
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @copydoc VBOXSERVICE::pfnStop */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic DECLCALLBACK(void) VBoxServiceExecStop(void)
af062818b47340eef15700d2f0211576ba3506eevboxsync /** @todo Later, figure what to do if we're in RTProcWait(). it's a very
af062818b47340eef15700d2f0211576ba3506eevboxsync * annoying call since doesn't support timeouts in the posix world. */
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @copydoc VBOXSERVICE::pfnTerm */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Nothing here yet. */
af062818b47340eef15700d2f0211576ba3506eevboxsync VbglR3GuestPropDisconnect(g_uExecGuestPropSvcClientID);
af062818b47340eef15700d2f0211576ba3506eevboxsync * The 'vminfo' service description.
af062818b47340eef15700d2f0211576ba3506eevboxsync /* pszName. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* pszDescription. */
af062818b47340eef15700d2f0211576ba3506eevboxsync "Host-driven Command Execution",
af062818b47340eef15700d2f0211576ba3506eevboxsync /* pszUsage. */
af062818b47340eef15700d2f0211576ba3506eevboxsync "[--exec-interval <ms>]"
af062818b47340eef15700d2f0211576ba3506eevboxsync /* pszOptions. */
af062818b47340eef15700d2f0211576ba3506eevboxsync " --exec-interval Specifies the interval at which to check for new\n"
af062818b47340eef15700d2f0211576ba3506eevboxsync " remote execution commands. The default is 10000 ms.\n"
af062818b47340eef15700d2f0211576ba3506eevboxsync /* methods */