/* $Id: VBoxManageGuestCtrl.cpp 63567 2016-08-16 14:06:54Z vboxsync $ */ /** @file * VBoxManage - Implementation of guestcontrol command. */ /* * Copyright (C) 2010-2016 Oracle Corporation * * 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. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include "VBoxManage.h" #include "VBoxManageGuestCtrl.h" #ifndef VBOX_ONLY_DOCS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For RTProcSelf(). */ #include #include #include #include #ifdef USE_XPCOM_QUEUE # include # include #endif #include #ifdef RT_OS_DARWIN # include #endif using namespace com; /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ #define GCTLCMD_COMMON_OPT_USER 999 /**< The --username option number. */ #define GCTLCMD_COMMON_OPT_PASSWORD 998 /**< The --password option number. */ #define GCTLCMD_COMMON_OPT_PASSWORD_FILE 997 /**< The --password-file option number. */ #define GCTLCMD_COMMON_OPT_DOMAIN 996 /**< The --domain option number. */ /** Common option definitions. */ #define GCTLCMD_COMMON_OPTION_DEFS() \ { "--username", GCTLCMD_COMMON_OPT_USER, RTGETOPT_REQ_STRING }, \ { "--passwordfile", GCTLCMD_COMMON_OPT_PASSWORD_FILE, RTGETOPT_REQ_STRING }, \ { "--password", GCTLCMD_COMMON_OPT_PASSWORD, RTGETOPT_REQ_STRING }, \ { "--domain", GCTLCMD_COMMON_OPT_DOMAIN, RTGETOPT_REQ_STRING }, \ { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, \ { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, /** Handles common options in the typical option parsing switch. */ #define GCTLCMD_COMMON_OPTION_CASES(a_pCtx, a_ch, a_pValueUnion) \ case 'v': \ case 'q': \ case GCTLCMD_COMMON_OPT_USER: \ case GCTLCMD_COMMON_OPT_DOMAIN: \ case GCTLCMD_COMMON_OPT_PASSWORD: \ case GCTLCMD_COMMON_OPT_PASSWORD_FILE: \ { \ RTEXITCODE rcExitCommon = gctlCtxSetOption(a_pCtx, a_ch, a_pValueUnion); \ if (RT_UNLIKELY(rcExitCommon != RTEXITCODE_SUCCESS)) \ return rcExitCommon; \ } break /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** Set by the signal handler when current guest control * action shall be aborted. */ static volatile bool g_fGuestCtrlCanceled = false; /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Listener declarations. */ VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl) VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl) VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl) VBOX_LISTENER_DECLARE(GuestEventListenerImpl) /** * Definition of a guestcontrol command, with handler and various flags. */ typedef struct GCTLCMDDEF { /** The command name. */ const char *pszName; /** * Actual command handler callback. * * @param pCtx Pointer to command context to use. */ DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct GCTLCMDCTX *pCtx, int argc, char **argv)); /** The command usage flags. */ uint32_t fCmdUsage; /** Command context flags (GCTLCMDCTX_F_XXX). */ uint32_t fCmdCtx; } GCTLCMD; /** Pointer to a const guest control command definition. */ typedef GCTLCMDDEF const *PCGCTLCMDDEF; /** @name GCTLCMDCTX_F_XXX - Command context flags. * @{ */ /** No flags set. */ #define GCTLCMDCTX_F_NONE 0 /** Don't install a signal handler (CTRL+C trap). */ #define GCTLCMDCTX_F_NO_SIGNAL_HANDLER RT_BIT(0) /** No guest session needed. */ #define GCTLCMDCTX_F_SESSION_ANONYMOUS RT_BIT(1) /** @} */ /** * Context for handling a specific command. */ typedef struct GCTLCMDCTX { HandlerArg *pArg; /** Pointer to the command definition. */ PCGCTLCMDDEF pCmdDef; /** The VM name or UUID. */ const char *pszVmNameOrUuid; /** Whether we've done the post option parsing init already. */ bool fPostOptionParsingInited; /** Whether we've locked the VM session. */ bool fLockedVmSession; /** Whether to detach (@c true) or close the session. */ bool fDetachGuestSession; /** Set if we've installed the signal handler. */ bool fInstalledSignalHandler; /** The verbosity level. */ uint32_t cVerbose; /** User name. */ Utf8Str strUsername; /** Password. */ Utf8Str strPassword; /** Domain. */ Utf8Str strDomain; /** Pointer to the IGuest interface. */ ComPtr pGuest; /** Pointer to the to be used guest session. */ ComPtr pGuestSession; /** The guest session ID. */ ULONG uSessionID; } GCTLCMDCTX, *PGCTLCMDCTX; typedef struct COPYCONTEXT { COPYCONTEXT() : fDryRun(false), fHostToGuest(false) { } PGCTLCMDCTX pCmdCtx; bool fDryRun; bool fHostToGuest; } COPYCONTEXT, *PCOPYCONTEXT; /** * An entry for a source element, including an optional DOS-like wildcard (*,?). */ class SOURCEFILEENTRY { public: SOURCEFILEENTRY(const char *pszSource, const char *pszFilter) : mSource(pszSource), mFilter(pszFilter) {} SOURCEFILEENTRY(const char *pszSource) : mSource(pszSource) { Parse(pszSource); } const char* GetSource() const { return mSource.c_str(); } const char* GetFilter() const { return mFilter.c_str(); } private: int Parse(const char *pszPath) { AssertPtrReturn(pszPath, VERR_INVALID_POINTER); if ( !RTFileExists(pszPath) && !RTDirExists(pszPath)) { /* No file and no directory -- maybe a filter? */ char *pszFilename = RTPathFilename(pszPath); if ( pszFilename && strpbrk(pszFilename, "*?")) { /* Yep, get the actual filter part. */ mFilter = RTPathFilename(pszPath); /* Remove the filter from actual sourcec directory name. */ RTPathStripFilename(mSource.mutableRaw()); mSource.jolt(); } } return VINF_SUCCESS; /** @todo */ } private: Utf8Str mSource; Utf8Str mFilter; }; typedef std::vector SOURCEVEC, *PSOURCEVEC; /** * An entry for an element which needs to be copied/created to/on the guest. */ typedef struct DESTFILEENTRY { DESTFILEENTRY(Utf8Str strFileName) : mFileName(strFileName) {} Utf8Str mFileName; } DESTFILEENTRY, *PDESTFILEENTRY; /* * Map for holding destination entries, whereas the key is the destination * directory and the mapped value is a vector holding all elements for this directory. */ typedef std::map< Utf8Str, std::vector > DESTDIRMAP, *PDESTDIRMAP; typedef std::map< Utf8Str, std::vector >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER; /** * RTGetOpt-IDs for the guest execution control command line. */ enum GETOPTDEF_EXEC { GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES = 1000, GETOPTDEF_EXEC_NO_PROFILE, GETOPTDEF_EXEC_OUTPUTFORMAT, GETOPTDEF_EXEC_DOS2UNIX, GETOPTDEF_EXEC_UNIX2DOS, GETOPTDEF_EXEC_WAITFOREXIT, GETOPTDEF_EXEC_WAITFORSTDOUT, GETOPTDEF_EXEC_WAITFORSTDERR }; enum kStreamTransform { kStreamTransform_None = 0, kStreamTransform_Dos2Unix, kStreamTransform_Unix2Dos }; /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ static int gctlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest, const char *pszDir, bool *fExists); #endif /* VBOX_ONLY_DOCS */ void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t uSubCmd) { const uint32_t fAnonSubCmds = USAGE_GSTCTRL_CLOSESESSION | USAGE_GSTCTRL_LIST | USAGE_GSTCTRL_CLOSEPROCESS | USAGE_GSTCTRL_CLOSESESSION | USAGE_GSTCTRL_UPDATEGA | USAGE_GSTCTRL_WATCH; /* 0 1 2 3 4 5 6 7 8XXXXXXXXXX */ /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ if (~fAnonSubCmds & uSubCmd) RTStrmPrintf(pStrm, "%s guestcontrol %s [--verbose|-v] [--quiet|-q]\n" " [--username ] [--domain ]\n" " [--passwordfile | --password ]\n%s", pcszSep1, pcszSep2, uSubCmd == ~0U ? "\n" : ""); if (uSubCmd & USAGE_GSTCTRL_RUN) RTStrmPrintf(pStrm, " run [common-options]\n" " [--exe ] [--timeout ]\n" " [-E|--putenv [=]] [--unquoted-args]\n" " [--ignore-operhaned-processes] [--profile]\n" " [--no-wait-stdout|--wait-stdout]\n" " [--no-wait-stderr|--wait-stderr]\n" " [--dos2unix] [--unix2dos]\n" " -- [argument1] ... [argumentN]]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_START) RTStrmPrintf(pStrm, " start [common-options]\n" " [--exe ] [--timeout ]\n" " [-E|--putenv [=]] [--unquoted-args]\n" " [--ignore-operhaned-processes] [--profile]\n" " -- [argument1] ... [argumentN]]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_COPYFROM) RTStrmPrintf(pStrm, " copyfrom [common-options]\n" " [--dryrun] [--follow] [-R|--recursive]\n" " [guest-src1 [...]] \n" "\n" " copyfrom [common-options]\n" " [--dryrun] [--follow] [-R|--recursive]\n" " [--target-directory ]\n" " [guest-src1 [...]]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_COPYTO) RTStrmPrintf(pStrm, " copyto [common-options]\n" " [--dryrun] [--follow] [-R|--recursive]\n" " [host-src1 [...]] \n" "\n" " copyto [common-options]\n" " [--dryrun] [--follow] [-R|--recursive]\n" " [--target-directory ]\n" " [host-src1 [...]]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_MKDIR) RTStrmPrintf(pStrm, " mkdir|createdir[ectory] [common-options]\n" " [--parents] [--mode ]\n" " [...]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_RMDIR) RTStrmPrintf(pStrm, " rmdir|removedir[ectory] [common-options]\n" " [-R|--recursive]\n" " [...]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_RM) RTStrmPrintf(pStrm, " removefile|rm [common-options] [-f|--force]\n" " [...]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_MV) RTStrmPrintf(pStrm, " mv|move|ren[ame] [common-options]\n" " [source1 [...]] \n" "\n"); if (uSubCmd & USAGE_GSTCTRL_MKTEMP) RTStrmPrintf(pStrm, " mktemp|createtemp[orary] [common-options]\n" " [--secure] [--mode ] [--tmpdir ]\n" "