/* $Id: VBoxManageGuestCtrl.cpp 52842 2014-09-24 13:44:42Z vboxsync $ */ /** @file * VBoxManage - Implementation of guestcontrol command. */ /* * Copyright (C) 2010-2013 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 #ifdef USE_XPCOM_QUEUE # include # include #endif #include #ifdef RT_OS_DARWIN # include #endif using namespace com; /** Set by the signal handler when current guest control * action shall be aborted. */ static volatile bool g_fGuestCtrlCanceled = false; /** * Listener declarations. */ VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl) VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl) VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl) VBOX_LISTENER_DECLARE(GuestEventListenerImpl) /** * Command context flags. */ /** No flags set. */ #define CTLCMDCTX_FLAGS_NONE 0 /** Don't install a signal handler (CTRL+C trap). */ #define CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER RT_BIT(0) /** No guest session needed. */ #define CTLCMDCTX_FLAGS_SESSION_ANONYMOUS RT_BIT(1) /** Detach the guest session. That is, don't close the * guest session automatically on exit. */ #define CTLCMDCTX_FLAGS_SESSION_DETACH RT_BIT(2) /** * Context for handling a specific command. */ typedef struct GCTLCMDCTX { HandlerArg handlerArg; /** Command-specific argument count. */ int iArgc; /** Command-specific argument vector. */ char **ppaArgv; /** First argv to start parsing with. */ int iFirstArgc; /** Command context flags. */ uint32_t uFlags; /** Verbose flag. */ bool fVerbose; /** 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 GCTLCMD { /** * Actual command handler callback. * * @param pCtx Pointer to command context to use. */ DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (PGCTLCMDCTX pCtx)); } GCTLCMD, *PGCTLCMD; 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 entires, whereas the key is the destination * directory and the mapped value is a vector holding all elements for this directoy. */ typedef std::map< Utf8Str, std::vector > DESTDIRMAP, *PDESTDIRMAP; typedef std::map< Utf8Str, std::vector >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER; /** * Special exit codes for returning errors/information of a * started guest process to the command line VBoxManage was started from. * Useful for e.g. scripting. * * @note These are frozen as of 4.1.0. */ enum EXITCODEEXEC { EXITCODEEXEC_SUCCESS = RTEXITCODE_SUCCESS, /* Process exited normally but with an exit code <> 0. */ EXITCODEEXEC_CODE = 16, EXITCODEEXEC_FAILED = 17, EXITCODEEXEC_TERM_SIGNAL = 18, EXITCODEEXEC_TERM_ABEND = 19, EXITCODEEXEC_TIMEOUT = 20, EXITCODEEXEC_DOWN = 21, EXITCODEEXEC_CANCELED = 22 }; /* * Common getopt definitions, starting at 1000. * Specific command definitions will start all at 2000. */ enum GETOPTDEF_COMMON { GETOPTDEF_COMMON_PASSWORD = 1000 }; /** * 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 GETOPTDEF_COPY { GETOPTDEF_COPY_DRYRUN = 1000, GETOPTDEF_COPY_FOLLOW, GETOPTDEF_COPY_TARGETDIR }; enum GETOPTDEF_MKDIR { }; enum GETOPTDEF_RM { }; enum GETOPTDEF_RMDIR { }; enum GETOPTDEF_SESSIONCLOSE { GETOPTDEF_SESSIONCLOSE_ALL = 2000 }; enum GETOPTDEF_STAT { }; enum OUTPUTTYPE { OUTPUTTYPE_UNDEFINED = 0, OUTPUTTYPE_DOS2UNIX = 10, OUTPUTTYPE_UNIX2DOS = 20 }; static int ctrlCopyDirExists(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) { RTStrmPrintf(pStrm, "%s guestcontrol %s \n%s", pcszSep1, pcszSep2, uSubCmd == ~0U ? "\n" : ""); if (uSubCmd & USAGE_GSTCTRL_EXEC) RTStrmPrintf(pStrm, " exec[ute]\n" " --image --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose] [--timeout ]\n" " [--environment \"= [=]\"]\n" " [--wait-exit] [--wait-stdout] [--wait-stderr]\n" " [--dos2unix] [--unix2dos]\n" " [-- [] ... []]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_COPYFROM) RTStrmPrintf(pStrm, " copyfrom\n" " --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose]\n" " [--dryrun] [--follow] [--recursive]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_COPYTO) RTStrmPrintf(pStrm, " copyto|cp\n" " --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose]\n" " [--dryrun] [--follow] [--recursive]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_CREATEDIR) RTStrmPrintf(pStrm, " createdir[ectory]|mkdir|md\n" " ... --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose]\n" " [--parents] [--mode ]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_REMOVEDIR) RTStrmPrintf(pStrm, " removedir[ectory]|rmdir\n" " ... --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose]\n" " [--recursive|-R|-r]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_REMOVEFILE) RTStrmPrintf(pStrm, " removefile|rm\n" " ... --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_RENAME) RTStrmPrintf(pStrm, " ren[ame]|mv\n" " ... --username \n" " [--passwordfile | --password ]\n" " [--domain ] [--verbose]\n" "\n"); if (uSubCmd & USAGE_GSTCTRL_CREATETEMP) RTStrmPrintf(pStrm, " createtemp[orary]|mktemp\n" "