/* $Id: VBoxManageGuestCtrl.cpp 85220 2020-07-11 15:42:34Z vboxsync $ */ /** @file * VBoxManage - Implementation of guestcontrol command. */ /* * Copyright (C) 2010-2020 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 /* For RTProcSelf(). */ #include #include #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() \ { "--user", GCTLCMD_COMMON_OPT_USER, RTGETOPT_REQ_STRING }, \ { "--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; /** Event semaphore used for wait notifications. * Also being used for the listener implementations in VBoxManageGuestCtrlListener.cpp. */ RTSEMEVENT g_SemEventGuestCtrlCanceled = NIL_RTSEMEVENT; /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Listener declarations. */ VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl) VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl) VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl) VBOX_LISTENER_DECLARE(GuestEventListenerImpl) VBOX_LISTENER_DECLARE(GuestAdditionsRunlevelListener) /** * 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 sub-command scope flags. */ uint64_t fSubcommandScope; /** 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; /** * 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 }; #endif /* VBOX_ONLY_DOCS */ void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint64_t fSubcommandScope) { const uint64_t fAnonSubCmds = HELP_SCOPE_GSTCTRL_CLOSESESSION | HELP_SCOPE_GSTCTRL_LIST | HELP_SCOPE_GSTCTRL_CLOSEPROCESS | HELP_SCOPE_GSTCTRL_CLOSESESSION | HELP_SCOPE_GSTCTRL_UPDATEGA | HELP_SCOPE_GSTCTRL_WATCH | HELP_SCOPE_GSTCTRL_WAITRUNLEVEL; /* 0 1 2 3 4 5 6 7 8XXXXXXXXXX */ /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ if (~fAnonSubCmds & fSubcommandScope) RTStrmPrintf(pStrm, "%s guestcontrol %s [--verbose|-v] [--quiet|-q]\n" " [--user[name] ] [--domain ]\n" " [--passwordfile | --password ]\n%s", pcszSep1, pcszSep2, (fSubcommandScope & RTMSGREFENTRYSTR_SCOPE_MASK) == RTMSGREFENTRYSTR_SCOPE_GLOBAL ? "\n" : ""); if (fSubcommandScope & HELP_SCOPE_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 (fSubcommandScope & HELP_SCOPE_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 (fSubcommandScope & HELP_SCOPE_GSTCTRL_COPYFROM) RTStrmPrintf(pStrm, " copyfrom [common-options]\n" " [--follow] [-R|--recursive]\n" " [guest-src1 [...]] \n" "\n" " copyfrom [common-options]\n" " [--follow] [-R|--recursive]\n" " [--target-directory ]\n" " [guest-src1 [...]]\n" "\n"); if (fSubcommandScope & HELP_SCOPE_GSTCTRL_COPYTO) RTStrmPrintf(pStrm, " copyto [common-options]\n" " [--follow] [-R|--recursive]\n" " [host-src1 [...]] \n" "\n" " copyto [common-options]\n" " [--follow] [-R|--recursive]\n" " [--target-directory ]\n" " [host-src1 [...]]\n" "\n"); if (fSubcommandScope & HELP_SCOPE_GSTCTRL_MKDIR) RTStrmPrintf(pStrm, " mkdir|createdir[ectory] [common-options]\n" " [--parents] [--mode ]\n" " [...]\n" "\n"); if (fSubcommandScope & HELP_SCOPE_GSTCTRL_RMDIR) RTStrmPrintf(pStrm, " rmdir|removedir[ectory] [common-options]\n" " [-R|--recursive]\n" " [...]\n" "\n"); if (fSubcommandScope & HELP_SCOPE_GSTCTRL_RM) RTStrmPrintf(pStrm, " removefile|rm [common-options] [-f|--force]\n" " [...]\n" "\n"); if (fSubcommandScope & HELP_SCOPE_GSTCTRL_MV) RTStrmPrintf(pStrm, " mv|move|ren[ame] [common-options]\n" " [source1 [...]] \n" "\n"); if (fSubcommandScope & HELP_SCOPE_GSTCTRL_MKTEMP) RTStrmPrintf(pStrm, " mktemp|createtemp[orary] [common-options]\n" " [--secure] [--mode ] [--tmpdir ]\n" "