VirtualBox

Ignore:
Timestamp:
Oct 17, 2013 2:48:13 PM (12 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
90036
Message:

FE/VBoxManage/GuestCtrl: Unified a lot of command line handling, added support for remove file and remove directory, separated event listeners.

Location:
trunk/src/VBox/Frontends/VBoxManage
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/Makefile.kmk

    r48406 r49165  
    55
    66#
    7 # Copyright (C) 2006-2012 Oracle Corporation
     7# Copyright (C) 2006-2013 Oracle Corporation
    88#
    99# This file is part of VirtualBox Open Source Edition (OSE), as
     
    4545        VBoxManageDisk.cpp \
    4646        $(if $(VBOX_WITH_GUEST_CONTROL),VBoxManageGuestCtrl.cpp) \
     47        $(if $(VBOX_WITH_GUEST_CONTROL),VBoxManageGuestCtrlListener.cpp) \
    4748        $(if $(VBOX_WITH_GUEST_PROPS),VBoxManageGuestProp.cpp) \
    4849        VBoxManageHelp.cpp \
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r48014 r49165  
    2121*******************************************************************************/
    2222#include "VBoxManage.h"
     23#include "VBoxManageGuestCtrl.h"
    2324
    2425#ifndef VBOX_ONLY_DOCS
     
    6263using namespace com;
    6364
    64 /** @todo Move this into a helper module. */
    65 static const char *ctrlFileStatusToText(FileStatus_T enmStatus);
    66 static const char *ctrlProcessStatusToText(ProcessStatus_T enmStatus);
    67 static const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus);
    68 
    69 class GuestFileEventListener;
    70 typedef ListenerImpl<GuestFileEventListener> GuestFileEventListenerImpl;
    71 VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl)
    72 
    73 class GuestProcessEventListener;
    74 typedef ListenerImpl<GuestProcessEventListener> GuestProcessEventListenerImpl;
    75 VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl)
    76 
    77 class GuestSessionEventListener;
    78 typedef ListenerImpl<GuestSessionEventListener> GuestSessionEventListenerImpl;
    79 VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl)
    80 
    81 /** Simple statistics class for binding locally
    82  *  held data to a specific guest object. */
    83 class GuestEventStats
    84 {
    85 
    86 public:
    87 
    88     GuestEventStats(void)
    89         : uLastUpdatedMS(RTTimeMilliTS())
    90     {
    91     }
    92 
    93     /** @todo Make this more a class than a structure. */
    94 public:
    95 
    96     uint64_t uLastUpdatedMS;
    97 };
    98 
    99 class GuestFileStats : public GuestEventStats
    100 {
    101 
    102 public:
    103 
    104     GuestFileStats(void) { }
    105 
    106     GuestFileStats(ComObjPtr<GuestFileEventListenerImpl> pListenerImpl)
    107         : mListener(pListenerImpl)
    108     {
    109     }
    110 
    111 public: /** @todo */
    112 
    113     ComObjPtr<GuestFileEventListenerImpl> mListener;
    114 };
    115 
    116 class GuestProcStats : public GuestEventStats
    117 {
    118 
    119 public:
    120 
    121     GuestProcStats(void) { }
    122 
    123     GuestProcStats(ComObjPtr<GuestProcessEventListenerImpl> pListenerImpl)
    124         : mListener(pListenerImpl)
    125     {
    126     }
    127 
    128 public: /** @todo */
    129 
    130     ComObjPtr<GuestProcessEventListenerImpl> mListener;
    131 };
    132 
    133 class GuestSessionStats : public GuestEventStats
    134 {
    135 
    136 public:
    137 
    138     GuestSessionStats(void) { }
    139 
    140     GuestSessionStats(ComObjPtr<GuestSessionEventListenerImpl> pListenerImpl)
    141         : mListener(pListenerImpl)
    142     {
    143     }
    144 
    145 public: /** @todo */
    146 
    147     ComObjPtr<GuestSessionEventListenerImpl> mListener;
    148 };
    149 
    150 /** Map containing all watched guest files. */
    151 typedef std::map< ComPtr<IGuestFile>, GuestFileStats > GuestEventFiles;
    152 /** Map containing all watched guest processes. */
    153 typedef std::map< ComPtr<IGuestProcess>, GuestProcStats > GuestEventProcs;
    154 /** Map containing all watched guest sessions. */
    155 typedef std::map< ComPtr<IGuestSession>, GuestSessionStats > GuestEventSessions;
    156 
    157 class GuestListenerBase
    158 {
    159 public:
    160 
    161     GuestListenerBase(void)
    162         : mfVerbose(false)
    163     {
    164     }
    165 
    166     virtual ~GuestListenerBase(void)
    167     {
    168     }
    169 
    170     HRESULT init(bool fVerbose = false)
    171     {
    172         mfVerbose = fVerbose;
    173         return S_OK;
    174     }
    175 
    176 protected:
    177 
    178     /** Verbose flag. */
    179     bool mfVerbose;
    180 };
    181 
    182 /**
    183  *  Handler for guest process events.
    184  */
    185 class GuestFileEventListener : public GuestListenerBase
    186 {
    187 public:
    188 
    189     GuestFileEventListener(void)
    190     {
    191     }
    192 
    193     virtual ~GuestFileEventListener(void)
    194     {
    195     }
    196 
    197     void uninit(void)
    198     {
    199 
    200     }
    201 
    202     STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    203     {
    204         switch (aType)
    205         {
    206             case VBoxEventType_OnGuestFileStateChanged:
    207             {
    208                 HRESULT rc;
    209                 do
    210                 {
    211                     ComPtr<IGuestFileStateChangedEvent> pEvent = aEvent;
    212                     Assert(!pEvent.isNull());
    213 
    214                     ComPtr<IGuestFile> pProcess;
    215                     CHECK_ERROR_BREAK(pEvent, COMGETTER(File)(pProcess.asOutParam()));
    216                     AssertBreak(!pProcess.isNull());
    217                     FileStatus_T fileSts;
    218                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Status)(&fileSts));
    219                     Bstr strPath;
    220                     CHECK_ERROR_BREAK(pProcess, COMGETTER(FileName)(strPath.asOutParam()));
    221                     ULONG uID;
    222                     CHECK_ERROR_BREAK(pProcess, COMGETTER(Id)(&uID));
    223 
    224                     RTPrintf("File ID=%RU32 \"%s\" changed status to [%s]\n",
    225                              uID, Utf8Str(strPath).c_str(),
    226                              ctrlFileStatusToText(fileSts));
    227 
    228                 } while (0);
    229                 break;
    230             }
    231 
    232             default:
    233                 AssertFailed();
    234         }
    235 
    236         return S_OK;
    237     }
    238 
    239 protected:
    240 
    241 };
    242 
    243 /**
    244  *  Handler for guest process events.
    245  */
    246 class GuestProcessEventListener : public GuestListenerBase
    247 {
    248 public:
    249 
    250     GuestProcessEventListener(void)
    251     {
    252     }
    253 
    254     virtual ~GuestProcessEventListener(void)
    255     {
    256     }
    257 
    258     void uninit(void)
    259     {
    260 
    261     }
    262 
    263     STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    264     {
    265         switch (aType)
    266         {
    267             case VBoxEventType_OnGuestProcessStateChanged:
    268             {
    269                 HRESULT rc;
    270                 do
    271                 {
    272                     ComPtr<IGuestProcessStateChangedEvent> pEvent = aEvent;
    273                     Assert(!pEvent.isNull());
    274 
    275                     ComPtr<IGuestProcess> pProcess;
    276                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Process)(pProcess.asOutParam()));
    277                     AssertBreak(!pProcess.isNull());
    278                     ProcessStatus_T procSts;
    279                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Status)(&procSts));
    280                     Bstr strPath;
    281                     CHECK_ERROR_BREAK(pProcess, COMGETTER(ExecutablePath)(strPath.asOutParam()));
    282                     ULONG uPID;
    283                     CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID));
    284 
    285                     RTPrintf("Process PID=%RU32 \"%s\" changed status to [%s]\n",
    286                              uPID, Utf8Str(strPath).c_str(),
    287                              ctrlProcessStatusToText(procSts));
    288 
    289                 } while (0);
    290                 break;
    291             }
    292 
    293             default:
    294                 AssertFailed();
    295         }
    296 
    297         return S_OK;
    298     }
    299 
    300 protected:
    301 
    302 };
    303 
    304 /**
    305  *  Handler for guest session events.
    306  */
    307 class GuestSessionEventListener : public GuestListenerBase
    308 {
    309 public:
    310 
    311     GuestSessionEventListener(void)
    312     {
    313     }
    314 
    315     virtual ~GuestSessionEventListener(void)
    316     {
    317     }
    318 
    319     void uninit(void)
    320     {
    321         GuestEventProcs::iterator itProc = mProcs.begin();
    322         while (itProc != mProcs.end())
    323         {
    324             if (!itProc->first.isNull())
    325             {
    326                 HRESULT rc;
    327                 do
    328                 {
    329                     /* Listener unregistration. */
    330                     ComPtr<IEventSource> pES;
    331                     CHECK_ERROR_BREAK(itProc->first, COMGETTER(EventSource)(pES.asOutParam()));
    332                     if (!pES.isNull())
    333                         CHECK_ERROR_BREAK(pES, UnregisterListener(itProc->second.mListener));
    334                 } while (0);
    335                 itProc->first->Release();
    336             }
    337 
    338             itProc++;
    339         }
    340         mProcs.clear();
    341 
    342         GuestEventFiles::iterator itFile = mFiles.begin();
    343         while (itFile != mFiles.end())
    344         {
    345             if (!itFile->first.isNull())
    346             {
    347                 HRESULT rc;
    348                 do
    349                 {
    350                     /* Listener unregistration. */
    351                     ComPtr<IEventSource> pES;
    352                     CHECK_ERROR_BREAK(itFile->first, COMGETTER(EventSource)(pES.asOutParam()));
    353                     if (!pES.isNull())
    354                         CHECK_ERROR_BREAK(pES, UnregisterListener(itFile->second.mListener));
    355                 } while (0);
    356                 itFile->first->Release();
    357             }
    358 
    359             itFile++;
    360         }
    361         mFiles.clear();
    362     }
    363 
    364     STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    365     {
    366         switch (aType)
    367         {
    368             case VBoxEventType_OnGuestFileRegistered:
    369             {
    370                 HRESULT rc;
    371                 do
    372                 {
    373                     ComPtr<IGuestFileRegisteredEvent> pEvent = aEvent;
    374                     Assert(!pEvent.isNull());
    375 
    376                     ComPtr<IGuestFile> pFile;
    377                     CHECK_ERROR_BREAK(pEvent, COMGETTER(File)(pFile.asOutParam()));
    378                     AssertBreak(!pFile.isNull());
    379                     BOOL fRegistered;
    380                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Registered)(&fRegistered));
    381                     Bstr strPath;
    382                     CHECK_ERROR_BREAK(pFile, COMGETTER(FileName)(strPath.asOutParam()));
    383 
    384                     RTPrintf("File \"%s\" %s\n",
    385                              Utf8Str(strPath).c_str(),
    386                              fRegistered ? "registered" : "unregistered");
    387                     if (fRegistered)
    388                     {
    389                         if (mfVerbose)
    390                             RTPrintf("Registering ...\n");
    391 
    392                         /* Register for IGuestFile events. */
    393                         ComObjPtr<GuestFileEventListenerImpl> pListener;
    394                         pListener.createObject();
    395                         CHECK_ERROR_BREAK(pListener, init(new GuestFileEventListener()));
    396 
    397                         ComPtr<IEventSource> es;
    398                         CHECK_ERROR_BREAK(pFile, COMGETTER(EventSource)(es.asOutParam()));
    399                         com::SafeArray<VBoxEventType_T> eventTypes;
    400                         eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
    401                         CHECK_ERROR_BREAK(es, RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes),
    402                                                                true /* Active listener */));
    403 
    404                         GuestFileStats fileStats(pListener);
    405                         mFiles[pFile] = fileStats;
    406                     }
    407                     else
    408                     {
    409                         GuestEventFiles::iterator itFile = mFiles.find(pFile);
    410                         if (itFile != mFiles.end())
    411                         {
    412                             if (mfVerbose)
    413                                 RTPrintf("Unregistering file ...\n");
    414 
    415                             if (!itFile->first.isNull())
    416                             {
    417                                 /* Listener unregistration. */
    418                                 ComPtr<IEventSource> pES;
    419                                 CHECK_ERROR(itFile->first, COMGETTER(EventSource)(pES.asOutParam()));
    420                                 if (!pES.isNull())
    421                                     CHECK_ERROR(pES, UnregisterListener(itFile->second.mListener));
    422                                 itFile->first->Release();
    423                             }
    424 
    425                             mFiles.erase(itFile);
    426                         }
    427                     }
    428 
    429                 } while (0);
    430                 break;
    431             }
    432 
    433             case VBoxEventType_OnGuestProcessRegistered:
    434             {
    435                 HRESULT rc;
    436                 do
    437                 {
    438                     ComPtr<IGuestProcessRegisteredEvent> pEvent = aEvent;
    439                     Assert(!pEvent.isNull());
    440 
    441                     ComPtr<IGuestProcess> pProcess;
    442                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Process)(pProcess.asOutParam()));
    443                     AssertBreak(!pProcess.isNull());
    444                     BOOL fRegistered;
    445                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Registered)(&fRegistered));
    446                     Bstr strPath;
    447                     CHECK_ERROR_BREAK(pProcess, COMGETTER(ExecutablePath)(strPath.asOutParam()));
    448 
    449                     RTPrintf("Process \"%s\" %s\n",
    450                              Utf8Str(strPath).c_str(),
    451                              fRegistered ? "registered" : "unregistered");
    452                     if (fRegistered)
    453                     {
    454                         if (mfVerbose)
    455                             RTPrintf("Registering ...\n");
    456 
    457                         /* Register for IGuestProcess events. */
    458                         ComObjPtr<GuestProcessEventListenerImpl> pListener;
    459                         pListener.createObject();
    460                         CHECK_ERROR_BREAK(pListener, init(new GuestProcessEventListener()));
    461 
    462                         ComPtr<IEventSource> es;
    463                         CHECK_ERROR_BREAK(pProcess, COMGETTER(EventSource)(es.asOutParam()));
    464                         com::SafeArray<VBoxEventType_T> eventTypes;
    465                         eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
    466                         CHECK_ERROR_BREAK(es, RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes),
    467                                                                true /* Active listener */));
    468 
    469                         GuestProcStats procStats(pListener);
    470                         mProcs[pProcess] = procStats;
    471                     }
    472                     else
    473                     {
    474                         GuestEventProcs::iterator itProc = mProcs.find(pProcess);
    475                         if (itProc != mProcs.end())
    476                         {
    477                             if (mfVerbose)
    478                                 RTPrintf("Unregistering process ...\n");
    479 
    480                             if (!itProc->first.isNull())
    481                             {
    482                                 /* Listener unregistration. */
    483                                 ComPtr<IEventSource> pES;
    484                                 CHECK_ERROR(itProc->first, COMGETTER(EventSource)(pES.asOutParam()));
    485                                 if (!pES.isNull())
    486                                     CHECK_ERROR(pES, UnregisterListener(itProc->second.mListener));
    487                                 itProc->first->Release();
    488                             }
    489 
    490                             mProcs.erase(itProc);
    491                         }
    492                     }
    493 
    494                 } while (0);
    495                 break;
    496             }
    497 
    498             case VBoxEventType_OnGuestSessionStateChanged:
    499             {
    500                 HRESULT rc;
    501                 do
    502                 {
    503                     ComPtr<IGuestSessionStateChangedEvent> pEvent = aEvent;
    504                     Assert(!pEvent.isNull());
    505                     ComPtr<IGuestSession> pSession;
    506                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Session)(pSession.asOutParam()));
    507                     AssertBreak(!pSession.isNull());
    508 
    509                     GuestSessionStatus_T sessSts;
    510                     CHECK_ERROR_BREAK(pSession, COMGETTER(Status)(&sessSts));
    511                     ULONG uID;
    512                     CHECK_ERROR_BREAK(pSession, COMGETTER(Id)(&uID));
    513                     Bstr strName;
    514                     CHECK_ERROR_BREAK(pSession, COMGETTER(Name)(strName.asOutParam()));
    515 
    516                     RTPrintf("Session ID=%RU32 \"%s\" changed status to [%s]\n",
    517                              uID, Utf8Str(strName).c_str(),
    518                              ctrlSessionStatusToText(sessSts));
    519 
    520                 } while (0);
    521                 break;
    522             }
    523 
    524             default:
    525                 AssertFailed();
    526         }
    527 
    528         return S_OK;
    529     }
    530 
    531 protected:
    532 
    533     GuestEventFiles mFiles;
    534     GuestEventProcs mProcs;
    535 };
    536 
    537 /**
    538  *  Handler for guest events.
    539  */
    540 class GuestEventListener : public GuestListenerBase
    541 {
    542 public:
    543     GuestEventListener(void)
    544     {
    545     }
    546 
    547     virtual ~GuestEventListener(void)
    548     {
    549     }
    550 
    551     void uninit(void)
    552     {
    553         GuestEventSessions::iterator itSession = mSessions.begin();
    554         while (itSession != mSessions.end())
    555         {
    556             if (!itSession->first.isNull())
    557             {
    558                 HRESULT rc;
    559                 do
    560                 {
    561                     /* Listener unregistration. */
    562                     ComPtr<IEventSource> pES;
    563                     CHECK_ERROR_BREAK(itSession->first, COMGETTER(EventSource)(pES.asOutParam()));
    564                     if (!pES.isNull())
    565                         CHECK_ERROR_BREAK(pES, UnregisterListener(itSession->second.mListener));
    566 
    567                 } while (0);
    568                 itSession->first->Release();
    569             }
    570 
    571             itSession++;
    572         }
    573         mSessions.clear();
    574     }
    575 
    576     STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    577     {
    578         switch (aType)
    579         {
    580             case VBoxEventType_OnGuestSessionRegistered:
    581             {
    582                 HRESULT rc;
    583                 do
    584                 {
    585                     ComPtr<IGuestSessionRegisteredEvent> pEvent = aEvent;
    586                     Assert(!pEvent.isNull());
    587 
    588                     ComPtr<IGuestSession> pSession;
    589                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Session)(pSession.asOutParam()));
    590                     AssertBreak(!pSession.isNull());
    591                     BOOL fRegistered;
    592                     CHECK_ERROR_BREAK(pEvent, COMGETTER(Registered)(&fRegistered));
    593                     Bstr strName;
    594                     CHECK_ERROR_BREAK(pSession, COMGETTER(Name)(strName.asOutParam()));
    595                     ULONG uID;
    596                     CHECK_ERROR_BREAK(pSession, COMGETTER(Id)(&uID));
    597 
    598                     RTPrintf("Session ID=%RU32 \"%s\" %s\n",
    599                              uID, Utf8Str(strName).c_str(),
    600                              fRegistered ? "registered" : "unregistered");
    601                     if (fRegistered)
    602                     {
    603                         if (mfVerbose)
    604                             RTPrintf("Registering ...\n");
    605 
    606                         /* Register for IGuestSession events. */
    607                         ComObjPtr<GuestSessionEventListenerImpl> pListener;
    608                         pListener.createObject();
    609                         CHECK_ERROR_BREAK(pListener, init(new GuestSessionEventListener()));
    610 
    611                         ComPtr<IEventSource> es;
    612                         CHECK_ERROR_BREAK(pSession, COMGETTER(EventSource)(es.asOutParam()));
    613                         com::SafeArray<VBoxEventType_T> eventTypes;
    614                         eventTypes.push_back(VBoxEventType_OnGuestFileRegistered);
    615                         eventTypes.push_back(VBoxEventType_OnGuestProcessRegistered);
    616                         CHECK_ERROR_BREAK(es, RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes),
    617                                                                true /* Active listener */));
    618 
    619                         GuestSessionStats sessionStats(pListener);
    620                         mSessions[pSession] = sessionStats;
    621                     }
    622                     else
    623                     {
    624                         GuestEventSessions::iterator itSession = mSessions.find(pSession);
    625                         if (itSession != mSessions.end())
    626                         {
    627                             if (mfVerbose)
    628                                 RTPrintf("Unregistering ...\n");
    629 
    630                             if (!itSession->first.isNull())
    631                             {
    632                                 /* Listener unregistration. */
    633                                 ComPtr<IEventSource> pES;
    634                                 CHECK_ERROR_BREAK(itSession->first, COMGETTER(EventSource)(pES.asOutParam()));
    635                                 if (!pES.isNull())
    636                                     CHECK_ERROR_BREAK(pES, UnregisterListener(itSession->second.mListener));
    637                                 itSession->first->Release();
    638                             }
    639 
    640                             mSessions.erase(itSession);
    641                         }
    642                     }
    643 
    644                 } while (0);
    645                 break;
    646             }
    647 
    648             default:
    649                 AssertFailed();
    650         }
    651 
    652         return S_OK;
    653     }
    654 
    655 protected:
    656 
    657     GuestEventSessions mSessions;
    658 };
    659 typedef ListenerImpl<GuestEventListener> GuestEventListenerImpl;
    660 VBOX_LISTENER_DECLARE(GuestEventListenerImpl)
    661 
    66265/** Set by the signal handler. */
    66366static volatile bool         g_fGuestCtrlCanceled = false;
     
    66669static ComPtr<IGuestSession> g_pGuestSession;
    66770
     71/**
     72 * Command context flags.
     73 */
     74/** No flags set. */
     75#define CTLCMDCTX_FLAGS_NONE                0
     76/** Don't install a signal handler (CTRL+C trap). */
     77#define CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER   RT_BIT(0)
     78/** No guest session needed. */
     79#define CTLCMDCTX_FLAGS_SESSION_ANONYMOUS   RT_BIT(1)
     80/** Detach the guest session. That is, don't close the
     81 *  guest session automatically on exit. */
     82#define CTLCMDCTX_FLAGS_SESSION_DETACH      RT_BIT(2)
     83
     84/**
     85 * Context for handling a specific command.
     86 */
     87typedef struct GCTLCMDCTX
     88{
     89    HandlerArg handlerArg;
     90    /** Command-specific argument count. */
     91    int iArgc;
     92    /** Command-specific argument vector. */
     93    char **ppaArgv;
     94    /** First argv to start parsing with. */
     95    int iFirstArgc;
     96    /** Command context flags. */
     97    uint32_t uFlags;
     98    /** Verbose flag. */
     99    bool fVerbose;
     100    /** User name. */
     101    Utf8Str strUsername;
     102    /** Password. */
     103    Utf8Str strPassword;
     104    /** Domain. */
     105    Utf8Str strDomain;
     106    /** Pointer to the IGuest interface. */
     107    ComPtr<IGuest> pGuest;
     108    /** Pointer to the to be used guest session. */
     109    ComPtr<IGuestSession> pGuestSession;
     110
     111} GCTLCMDCTX, *PGCTLCMDCTX;
     112
     113typedef struct GCTLCMD
     114{
     115    /**
     116     * Actual command handler callback.
     117     *
     118     * @param   pCtx            Pointer to command context to use.
     119     */
     120    DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (PGCTLCMDCTX pCtx));
     121
     122} GCTLCMD, *PGCTLCMD;
     123
    668124typedef struct COPYCONTEXT
    669125{
    670     COPYCONTEXT() : fVerbose(false), fDryRun(false), fHostToGuest(false)
    671     {
    672     }
    673 
     126    COPYCONTEXT()
     127        : fDryRun(false),
     128          fHostToGuest(false)
     129    {
     130    }
     131
     132    PGCTLCMDCTX pCmdCtx;
    674133    ComPtr<IGuestSession> pGuestSession;
    675     bool fVerbose;
    676134    bool fDryRun;
    677135    bool fHostToGuest;
     136
    678137} COPYCONTEXT, *PCOPYCONTEXT;
    679138
     
    772231};
    773232
     233/*
     234 * Common getopt definitions, starting at 1000.
     235 * Specific command definitions will start all at 2000.
     236 */
     237enum GETOPTDEF_COMMON
     238{
     239    GETOPTDEF_COMMON_PASSWORD = 1000
     240};
     241
    774242/**
    775243 * RTGetOpt-IDs for the guest execution control command line.
     
    782250    GETOPTDEF_EXEC_DOS2UNIX,
    783251    GETOPTDEF_EXEC_UNIX2DOS,
    784     GETOPTDEF_EXEC_PASSWORD,
    785252    GETOPTDEF_EXEC_WAITFOREXIT,
    786253    GETOPTDEF_EXEC_WAITFORSTDOUT,
     
    792259    GETOPTDEF_COPY_DRYRUN = 1000,
    793260    GETOPTDEF_COPY_FOLLOW,
    794     GETOPTDEF_COPY_PASSWORD,
    795261    GETOPTDEF_COPY_TARGETDIR
    796262};
     
    798264enum GETOPTDEF_MKDIR
    799265{
    800     GETOPTDEF_MKDIR_PASSWORD = 1000
    801266};
    802267
     268enum GETOPTDEF_RM
     269{
     270};
     271
     272enum GETOPTDEF_RMDIR
     273{
     274    GETOPTDEF_RMDIR_RECURSIVE = 2000
     275};
     276
    803277enum GETOPTDEF_SESSIONCLOSE
    804278{
    805     GETOPTDEF_SESSIONCLOSE_ALL = 1000
     279    GETOPTDEF_SESSIONCLOSE_ALL = 2000
    806280};
    807281
    808282enum GETOPTDEF_STAT
    809283{
    810     GETOPTDEF_STAT_PASSWORD = 1000
    811284};
    812285
     
    854327                 "                            [--domain <domain>] [--verbose]\n"
    855328                 "                            [--parents] [--mode <mode>]\n"
     329                 "\n"
     330                 "                            removedir[ectory]|rmdir|rm\n"
     331                 "                            <guest directory>... --username <name>\n"
     332                 "                            [--passwordfile <file> | --password <password>]\n"
     333                 "                            [--domain <domain>] [--verbose]\n"
     334                 "                            [--recursive|-R|-r]\n"
     335                 "\n"
     336                 "                            removefile|rm\n"
     337                 "                            <guest file>... --username <name>\n"
     338                 "                            [--passwordfile <file> | --password <password>]\n"
     339                 "                            [--domain <domain>] [--verbose]\n"
    856340                 "\n"
    857341                 "                            createtemp[orary]|mktemp\n"
     
    983467 * string.
    984468 */
    985 static const char *ctrlProcessStatusToText(ProcessStatus_T enmStatus)
     469const char *ctrlProcessStatusToText(ProcessStatus_T enmStatus)
    986470{
    987471    switch (enmStatus)
     
    1066550 * string.
    1067551 */
    1068 static const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus)
     552const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus)
    1069553{
    1070554    switch (enmStatus)
     
    1096580 * string.
    1097581 */
    1098 static const char *ctrlFileStatusToText(FileStatus_T enmStatus)
     582const char *ctrlFileStatusToText(FileStatus_T enmStatus)
    1099583{
    1100584    switch (enmStatus)
     
    1173657/**
    1174658 * Un-initializes the VM after guest control usage.
     659 * @param   pCmdCtx                 Pointer to command context.
     660 * @param   uFlags                  Command context flags.
    1175661 */
    1176 static void ctrlUninitVM(HandlerArg *pArg)
    1177 {
    1178     AssertPtrReturnVoid(pArg);
    1179     if (pArg->session)
    1180         pArg->session->UnlockMachine();
     662static void ctrlUninitVM(PGCTLCMDCTX pCtx, uint32_t uFlags)
     663{
     664    AssertPtrReturnVoid(pCtx);
     665
     666    if (!(pCtx->uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER))
     667        ctrlSignalHandlerUninstall();
     668
     669    HRESULT rc;
     670
     671    do
     672    {
     673        if (!pCtx->pGuestSession.isNull())
     674        {
     675            if (   !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)
     676                && !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH))
     677            {
     678                if (pCtx->fVerbose)
     679                    RTPrintf("Closing guest session ...\n");
     680
     681                CHECK_ERROR(pCtx->pGuestSession, Close());
     682                /* Keep going - don't break here. Try to unlock the
     683                 * machine down below. */
     684            }
     685            else if (pCtx->fVerbose)
     686                RTPrintf("Guest session detached\n");
     687        }
     688
     689        if (pCtx->handlerArg.session)
     690            CHECK_ERROR(pCtx->handlerArg.session, UnlockMachine());
     691
     692    } while (0);
     693
     694    for (int i = 0; i < pCtx->iArgc; i++)
     695        RTStrFree(pCtx->ppaArgv[i]);
     696    RTMemFree(pCtx->ppaArgv);
    1181697}
    1182698
     
    1185701 *
    1186702 * That is, checks whether it's up and running, if it can be locked (shared
    1187  * only) and returns a valid IGuest pointer on success.
     703 * only) and returns a valid IGuest pointer on success. Also, it does some
     704 * basic command line processing and opens a guest session, if required.
    1188705 *
    1189  * @return  IPRT status code.
    1190  * @param   pArg            Our command line argument structure.
    1191  * @param   pszNameOrId     The VM's name or UUID.
    1192  * @param   pGuest          Where to return the IGuest interface pointer.
     706 * @return  RTEXITCODE status code.
     707 * @param   pArg                    Pointer to command line argument structure.
     708 * @param   pCmdCtx                 Pointer to command context.
     709 * @param   uFlags                  Command context flags.
    1193710 */
    1194 static int ctrlInitVM(HandlerArg *pArg, const char *pszNameOrId, ComPtr<IGuest> *pGuest)
    1195 {
    1196     AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
    1197     AssertPtrReturn(pszNameOrId, VERR_INVALID_PARAMETER);
     711static RTEXITCODE ctrlInitVM(HandlerArg *pArg,
     712                             PGCTLCMDCTX pCtx, uint32_t uFlags)
     713{
     714    AssertPtrReturn(pArg, RTEXITCODE_FAILURE);
     715    AssertReturn(pArg->argc > 1, RTEXITCODE_FAILURE);
     716    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     717
     718    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
     719
     720    const char *pszNameOrId = pArg->argv[0];
     721    const char *pszCmd = pArg->argv[1];
    1198722
    1199723    /* Lookup VM. */
     
    1203727    CHECK_ERROR(pArg->virtualBox, FindMachine(Bstr(pszNameOrId).raw(),
    1204728                                              machine.asOutParam()));
    1205     if (FAILED(rc))
    1206         return VERR_NOT_FOUND;
    1207 
    1208     /* Machine is running? */
    1209     MachineState_T machineState;
    1210     CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), 1);
    1211     if (machineState != MachineState_Running)
    1212     {
    1213         RTMsgError("Machine \"%s\" is not running (currently %s)!\n",
    1214                    pszNameOrId, machineStateToName(machineState, false));
    1215         return VERR_VM_INVALID_VM_STATE;
    1216     }
    1217 
    1218     do
    1219     {
    1220         /* Open a session for the VM. */
    1221         CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared));
    1222         /* Get the associated console. */
    1223         ComPtr<IConsole> console;
    1224         CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam()));
    1225         /* ... and session machine. */
    1226         ComPtr<IMachine> sessionMachine;
    1227         CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
    1228         /* Get IGuest interface. */
    1229         CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest->asOutParam()));
    1230     } while (0);
    1231 
    1232     if (FAILED(rc))
    1233         ctrlUninitVM(pArg);
    1234     return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
     729    if (SUCCEEDED(rc))
     730    {
     731        /* Machine is running? */
     732        MachineState_T machineState;
     733        CHECK_ERROR(machine, COMGETTER(State)(&machineState));
     734        if (   SUCCEEDED(rc)
     735            && (machineState != MachineState_Running))
     736            rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine \"%s\" is not running (currently %s)!\n",
     737                                    pszNameOrId, machineStateToName(machineState, false));
     738    }
     739    else
     740        rcExit = RTEXITCODE_FAILURE;
     741
     742    if (rcExit == RTEXITCODE_SUCCESS)
     743    {
     744        /*
     745         * Process standard options which are served by all commands.
     746         */
     747        static const RTGETOPTDEF s_aOptions[] =
     748        {
     749            { "--username",            'u',                             RTGETOPT_REQ_STRING  },
     750            { "--passwordfile",        'p',                             RTGETOPT_REQ_STRING  },
     751            { "--password",            GETOPTDEF_COMMON_PASSWORD,       RTGETOPT_REQ_STRING  },
     752            { "--domain",              'd',                             RTGETOPT_REQ_STRING  },
     753            { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     754        };
     755
     756        /*
     757         * Allocate per-command argv. This then only contains the specific arguments
     758         * the command needs.
     759         */
     760        pCtx->ppaArgv = (char**)RTMemAlloc(pArg->argc * sizeof(char*) + 1);
     761        if (!pCtx->ppaArgv)
     762        {
     763            rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Not enough memory for per-command argv\n");
     764        }
     765        else
     766        {
     767            pCtx->iArgc = 0;
     768
     769            int ch;
     770            RTGETOPTUNION ValueUnion;
     771            RTGETOPTSTATE GetState;
     772            RTGetOptInit(&GetState, pArg->argc, pArg->argv,
     773                         s_aOptions, RT_ELEMENTS(s_aOptions),
     774                         2, /* Skip VM name and guest control command */
     775                         RTGETOPTINIT_FLAGS_OPTS_FIRST);
     776
     777            while (   (ch = RTGetOpt(&GetState, &ValueUnion))
     778                   && (rcExit == RTEXITCODE_SUCCESS))
     779            {
     780                /* For options that require an argument, ValueUnion has received the value. */
     781                switch (ch)
     782                {
     783                    case 'u': /* User name */
     784                        if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))
     785                            pCtx->strUsername = ValueUnion.psz;
     786                        break;
     787
     788                    case GETOPTDEF_COMMON_PASSWORD: /* Password */
     789                        if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))
     790                        {
     791                            if (pCtx->strPassword.isEmpty())
     792                                pCtx->strPassword = ValueUnion.psz;
     793                        }
     794                        break;
     795
     796                    case 'p': /* Password file */
     797                    {
     798                        if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))
     799                            rcExit = readPasswordFile(ValueUnion.psz, &pCtx->strPassword);
     800                        break;
     801                    }
     802
     803                    case 'd': /* domain */
     804                        if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))
     805                            pCtx->strDomain = ValueUnion.psz;
     806                        break;
     807
     808                    case 'v': /* Verbose */
     809                        pCtx->fVerbose = true;
     810                        break;
     811
     812                    case VINF_GETOPT_NOT_OPTION:
     813                        /* Fall through is intentional. */
     814                    default:
     815                    {
     816                        Assert(GetState.iNext);
     817                        char *pszArg = RTStrDup(pArg->argv[GetState.iNext - 1]);
     818                        if (!pszArg)
     819                        {
     820                            rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
     821                                                    "Not enough memory for command line handling\n");
     822                            break;
     823                        }
     824                        pCtx->ppaArgv[pCtx->iArgc] = pszArg;
     825                        pCtx->iArgc++;
     826                        break;
     827                    }
     828
     829                } /* switch */
     830            } /* while RTGetOpt */
     831        }
     832    }
     833
     834    /*
     835     * Check for mandatory stuff.
     836     */
     837    if (rcExit == RTEXITCODE_SUCCESS)
     838    {
     839#if 0
     840        RTPrintf("argc=%d\n", pCtx->iArgc);
     841        for (int i = 0; i < pCtx->iArgc; i++)
     842            RTPrintf("argv[%d]=%s\n", i, pCtx->ppaArgv[i]);
     843#endif
     844        if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))
     845        {
     846            if (pCtx->strUsername.isEmpty())
     847                rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
     848        }
     849    }
     850
     851    if (rcExit == RTEXITCODE_SUCCESS)
     852    {
     853        /*
     854         * Build up a reasonable guest session name. Useful for identifying
     855         * a specific session when listing / searching for them.
     856         */
     857        char *pszSessionName;
     858        if (0 >= RTStrAPrintf(&pszSessionName,
     859                              "[%RU32] VBoxManage Guest Control [%s] - %s",
     860                              RTProcSelf(), pszNameOrId, pszCmd))
     861            return RTMsgErrorExit(RTEXITCODE_FAILURE, "No enough memory for session name\n");
     862
     863        do
     864        {
     865            /* Open a session for the VM. */
     866            CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared));
     867            /* Get the associated console. */
     868            ComPtr<IConsole> console;
     869            CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam()));
     870            /* ... and session machine. */
     871            ComPtr<IMachine> sessionMachine;
     872            CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
     873            /* Get IGuest interface. */
     874            CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pCtx->pGuest.asOutParam()));
     875            if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))
     876            {
     877                if (pCtx->fVerbose)
     878                    RTPrintf("Opening guest session as user '%s' ...\n", pCtx->strUsername.c_str());
     879
     880                /* Open a guest session. */
     881                Assert(!pCtx->pGuest.isNull());
     882                CHECK_ERROR_BREAK(pCtx->pGuest, CreateSession(Bstr(pCtx->strUsername).raw(),
     883                                                              Bstr(pCtx->strPassword).raw(),
     884                                                              Bstr(pCtx->strDomain).raw(),
     885                                                              Bstr(pszSessionName).raw(),
     886                                                              pCtx->pGuestSession.asOutParam()));
     887            }
     888
     889            if (!(uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER))
     890                ctrlSignalHandlerInstall();
     891
     892        } while (0);
     893
     894        if (FAILED(rc))
     895            rcExit = RTEXITCODE_FAILURE;
     896
     897        RTStrFree(pszSessionName);
     898    }
     899
     900    if (rcExit == RTEXITCODE_SUCCESS)
     901    {
     902        pCtx->handlerArg = *pArg;
     903        pCtx->uFlags = uFlags;
     904    }
     905    else /* Clean up on failure. */
     906        ctrlUninitVM(pCtx, uFlags);
     907
     908    return rcExit;
    1235909}
    1236910
     
    13301004}
    13311005
    1332 /* <Missing documentation> */
    1333 static RTEXITCODE handleCtrlProcessExec(ComPtr<IGuest> pGuest, HandlerArg *pArg)
    1334 {
    1335     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
     1006static DECLCALLBACK(RTEXITCODE) handleCtrlProcessExec(PGCTLCMDCTX pCtx)
     1007{
     1008    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    13361009
    13371010    /*
     
    13461019        { "--image",                        'i',                                      RTGETOPT_REQ_STRING  },
    13471020        { "--no-profile",                   GETOPTDEF_EXEC_NO_PROFILE,                RTGETOPT_REQ_NOTHING },
    1348         { "--username",                     'u',                                      RTGETOPT_REQ_STRING  },
    1349         { "--passwordfile",                 'p',                                      RTGETOPT_REQ_STRING  },
    1350         { "--password",                     GETOPTDEF_EXEC_PASSWORD,                  RTGETOPT_REQ_STRING  },
    1351         { "--domain",                       'd',                                      RTGETOPT_REQ_STRING  },
    13521021        { "--timeout",                      't',                                      RTGETOPT_REQ_UINT32  },
    13531022        { "--unix2dos",                     GETOPTDEF_EXEC_UNIX2DOS,                  RTGETOPT_REQ_NOTHING },
    1354         { "--verbose",                      'v',                                      RTGETOPT_REQ_NOTHING },
    13551023        { "--wait-exit",                    GETOPTDEF_EXEC_WAITFOREXIT,               RTGETOPT_REQ_NOTHING },
    13561024        { "--wait-stdout",                  GETOPTDEF_EXEC_WAITFORSTDOUT,             RTGETOPT_REQ_NOTHING },
     
    13611029    RTGETOPTUNION           ValueUnion;
    13621030    RTGETOPTSTATE           GetState;
    1363     RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
     1031    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, 0);
    13641032
    13651033    Utf8Str                              strCmd;
     
    13681036    com::SafeArray<IN_BSTR>              aArgs;
    13691037    com::SafeArray<IN_BSTR>              aEnv;
    1370     Utf8Str                              strUsername;
    1371     Utf8Str                              strPassword;
    1372     Utf8Str                              strDomain;
    13731038    RTMSINTERVAL                         cMsTimeout      = 0;
    13741039    OUTPUTTYPE                           eOutputType     = OUTPUTTYPE_UNDEFINED;
    13751040    bool                                 fDetached       = true;
    1376     bool                                 fVerbose        = false;
    13771041    int                                  vrc             = VINF_SUCCESS;
    13781042
     
    14241088                /** @todo Add a hidden flag. */
    14251089
    1426                 case 'u': /* User name */
    1427                     strUsername = ValueUnion.psz;
    1428                     break;
    1429 
    1430                 case GETOPTDEF_EXEC_PASSWORD: /* Password */
    1431                     strPassword = ValueUnion.psz;
    1432                     break;
    1433 
    1434                 case 'p': /* Password file */
    1435                 {
    1436                     RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
    1437                     if (rcExit != RTEXITCODE_SUCCESS)
    1438                         return rcExit;
    1439                     break;
    1440                 }
    1441 
    1442                 case 'd': /* domain */
    1443                     strDomain = ValueUnion.psz;
    1444                     break;
    1445 
    14461090                case 't': /* Timeout */
    14471091                    cMsTimeout = ValueUnion.u32;
     
    14541098                    break;
    14551099
    1456                 case 'v': /* Verbose */
    1457                     fVerbose = true;
    1458                     break;
    1459 
    14601100                case GETOPTDEF_EXEC_WAITFOREXIT:
    14611101                    aWaitFlags.push_back(ProcessWaitForFlag_Terminate);
     
    14761116
    14771117                case VINF_GETOPT_NOT_OPTION:
    1478                 {
    14791118                    if (aArgs.size() == 0 && strCmd.isEmpty())
    14801119                        strCmd = ValueUnion.psz;
     
    14821121                        aArgs.push_back(Bstr(ValueUnion.psz).raw());
    14831122                    break;
    1484                 }
    14851123
    14861124                default:
    14871125                    return RTGetOptPrintError(ch, &ValueUnion);
     1126                    break;
    14881127
    14891128            } /* switch */
     
    15001139    if (strCmd.isEmpty())
    15011140        return errorSyntax(USAGE_GUESTCONTROL, "No command to execute specified!");
    1502 
    1503     if (strUsername.isEmpty())
    1504         return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
    15051141
    15061142    /** @todo Any output conversion not supported yet! */
     
    15081144        return errorSyntax(USAGE_GUESTCONTROL, "Output conversion not implemented yet!");
    15091145
    1510     ctrlSignalHandlerInstall();
    1511 
    1512     /*
    1513      * Start with the real work.
    1514      */
    1515     HRESULT rc = S_OK;
    1516     if (fVerbose)
    1517         RTPrintf("Opening guest session as user '%s' ...\n", strUsername.c_str());
    1518 
    15191146    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
    1520 
    1521     /** @todo This eventually needs a bit of revamping so that a valid session gets passed
    1522      *        into this function already so that we don't need to mess around with closing
    1523      *        the session all over the places below again. Later. */
     1147    HRESULT rc;
    15241148
    15251149    try
     
    15271151        do
    15281152        {
    1529             Utf8Str strVBoxManage;
    1530             strVBoxManage.printf("VBoxManage Guest Control (PID %RU32)", RTProcSelf());
    1531 
    1532             CHECK_ERROR_BREAK(pGuest, CreateSession(Bstr(strUsername).raw(),
    1533                                       Bstr(strPassword).raw(),
    1534                                       Bstr(strDomain).raw(),
    1535                                       Bstr(strVBoxManage).raw(),
    1536                                       g_pGuestSession.asOutParam()));
    1537 
    15381153            /* Adjust process creation flags if we don't want to wait for process termination. */
    15391154            if (fDetached)
     
    15461161             * Wait for guest session to start.
    15471162             */
    1548             if (fVerbose)
     1163            if (pCtx->fVerbose)
    15491164            {
    15501165                if (cMsTimeout == 0)
     
    15661181                || sessionWaitResult == GuestSessionWaitResult_WaitFlagNotSupported)
    15671182            {
    1568                 if (fVerbose)
     1183                if (pCtx->fVerbose)
    15691184                    RTPrintf("Guest session (ID %RU32) has been started\n", uSessionID);
    15701185            }
     
    15751190            }
    15761191
    1577             if (fVerbose)
     1192            if (pCtx->fVerbose)
    15781193            {
    15791194                if (cMsTimeout == 0)
     
    16211236                        ULONG uPID = 0;
    16221237                        CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID));
    1623                         if (fVerbose)
     1238                        if (pCtx->fVerbose)
    16241239                        {
    16251240                            RTPrintf("Process '%s' (PID %RU32) started\n",
     
    17081323                    LONG exitCode;
    17091324                    CHECK_ERROR_BREAK(pProcess, COMGETTER(ExitCode)(&exitCode));
    1710                     if (fVerbose)
     1325                    if (pCtx->fVerbose)
    17111326                        RTPrintf("Exit code=%u (Status=%u [%s])\n",
    17121327                                 exitCode, procStatus, ctrlProcessStatusToText(procStatus));
     
    17141329                    rcExit = (RTEXITCODE)ctrlExecProcessStatusToExitCode(procStatus, exitCode);
    17151330                }
    1716                 else if (fVerbose)
     1331                else if (pCtx->fVerbose)
    17171332                    RTPrintf("Process now is in status [%s]\n", ctrlProcessStatusToText(procStatus));
    17181333            }
    17191334            else
    17201335            {
    1721                 if (fVerbose)
     1336                if (pCtx->fVerbose)
    17221337                    RTPrintf("Process execution aborted!\n");
    17231338
     
    17311346        rc = E_OUTOFMEMORY;
    17321347    }
    1733 
    1734     ctrlSignalHandlerUninstall();
    17351348
    17361349    bool fCloseSession = false;
     
    17471360        fCloseSession = true;
    17481361
    1749     if (   fCloseSession
    1750         && !g_pGuestSession.isNull())
    1751     {
    1752         if (fVerbose)
    1753             RTPrintf("Closing guest session ...\n");
    1754         rc = g_pGuestSession->Close();
    1755     }
    1756     else if (!fCloseSession && fVerbose)
    1757         RTPrintf("Guest session detached\n");
     1362    if (!fCloseSession)
     1363        pCtx->uFlags |= CTLCMDCTX_FLAGS_SESSION_DETACH;
    17581364
    17591365    if (   rcExit == RTEXITCODE_SUCCESS
     
    17721378 *
    17731379 * @return  IPRT status code.
    1774  * @param   pGuest                  Pointer to IGuest interface to use.
    1775  * @param   fVerbose                Flag indicating if we want to run in verbose mode.
     1380 * @param   pCtx                    Pointer to command context.
    17761381 * @param   fDryRun                 Flag indicating if we want to run a dry run only.
    17771382 * @param   fHostToGuest            Flag indicating if we want to copy from host to guest
    17781383 *                                  or vice versa.
    1779  * @param   strUsername             Username of account to use on the guest side.
    1780  * @param   strPassword             Password of account to use.
    1781  * @param   strDomain               Domain of account to use.
    17821384 * @param   strSessionName          Session name (only for identification purposes).
    17831385 * @param   ppContext               Pointer which receives the allocated copy context.
    17841386 */
    1785 static int ctrlCopyContextCreate(IGuest *pGuest, bool fVerbose, bool fDryRun,
    1786                                  bool fHostToGuest, const Utf8Str &strUsername,
    1787                                  const Utf8Str &strPassword, const Utf8Str &strDomain,
     1387static int ctrlCopyContextCreate(PGCTLCMDCTX pCtx, bool fDryRun, bool fHostToGuest,
    17881388                                 const Utf8Str &strSessionName,
    17891389                                 PCOPYCONTEXT *ppContext)
    17901390{
    1791     AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
     1391    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    17921392
    17931393    PCOPYCONTEXT pContext = new COPYCONTEXT();
    17941394    AssertPtrReturn(pContext, VERR_NO_MEMORY); /**< @todo r=klaus cannot happen with new */
    17951395    ComPtr<IGuestSession> pGuestSession;
    1796     HRESULT rc = pGuest->CreateSession(Bstr(strUsername).raw(),
    1797                                        Bstr(strPassword).raw(),
    1798                                        Bstr(strDomain).raw(),
    1799                                        Bstr(strSessionName).raw(),
    1800                                        pGuestSession.asOutParam());
     1396    HRESULT rc = pCtx->pGuest->CreateSession(Bstr(pCtx->strUsername).raw(),
     1397                                             Bstr(pCtx->strPassword).raw(),
     1398                                             Bstr(pCtx->strDomain).raw(),
     1399                                             Bstr(strSessionName).raw(),
     1400                                             pGuestSession.asOutParam());
    18011401    if (FAILED(rc))
    1802         return ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    1803 
    1804     pContext->fVerbose = fVerbose;
     1402        return ctrlPrintError(pCtx->pGuest, COM_IIDOF(IGuest));
     1403
     1404    pContext->pCmdCtx = pCtx;
    18051405    pContext->fDryRun = fDryRun;
    18061406    pContext->fHostToGuest = fHostToGuest;
     
    19781578        && fDirExists)
    19791579    {
    1980         if (pContext->fVerbose)
     1580        if (pContext->pCmdCtx->fVerbose)
    19811581            RTPrintf("Directory \"%s\" already exists\n", pszDir);
    19821582        return VINF_SUCCESS;
     
    19881588        return vrc;
    19891589
    1990     if (pContext->fVerbose)
     1590    if (pContext->pCmdCtx->fVerbose)
    19911591        RTPrintf("Creating directory \"%s\" ...\n", pszDir);
    19921592
     
    21641764    AssertReturn(!fFlags, VERR_INVALID_POINTER); /* No flags supported yet. */
    21651765
    2166     if (pContext->fVerbose)
     1766    if (pContext->pCmdCtx->fVerbose)
    21671767        RTPrintf("Copying \"%s\" to \"%s\" ...\n",
    21681768                 pszFileSource, pszFileDest);
     
    21961796    else
    21971797    {
    2198         if (pContext->fVerbose)
     1798        if (pContext->pCmdCtx->fVerbose)
    21991799            rc = showProgress(pProgress);
    22001800        else
     
    22391839        vrc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
    22401840
    2241     if (pContext->fVerbose)
     1841    if (pContext->pCmdCtx->fVerbose)
    22421842        RTPrintf("Processing host directory: %s\n", szCurDir);
    22431843
     
    22841884                        break;
    22851885
    2286                     if (pContext->fVerbose)
     1886                    if (pContext->pCmdCtx->fVerbose)
    22871887                        RTPrintf("Directory: %s\n", DirEntry.szName);
    22881888
     
    23281928                    }
    23291929
    2330                     if (pContext->fVerbose)
     1930                    if (pContext->pCmdCtx->fVerbose)
    23311931                        RTPrintf("File: %s\n", DirEntry.szName);
    23321932
     
    24112011        return vrc;
    24122012
    2413     if (pContext->fVerbose)
     2013    if (pContext->pCmdCtx->fVerbose)
    24142014        RTPrintf("Processing guest directory: %s\n", szCurDir);
    24152015
     
    24482048                    break;
    24492049
    2450                 if (pContext->fVerbose)
     2050                if (pContext->pCmdCtx->fVerbose)
    24512051                {
    24522052                    Utf8Str strDir(strName);
     
    24982098                }
    24992099
    2500                 if (pContext->fVerbose)
     2100                if (pContext->pCmdCtx->fVerbose)
    25012101                    RTPrintf("File: %s\n", strFile.c_str());
    25022102
     
    26552255}
    26562256
    2657 static RTEXITCODE handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg,
    2658                                  bool fHostToGuest)
    2659 {
    2660     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
     2257static RTEXITCODE handleCtrlCopy(PGCTLCMDCTX pCtx, bool fHostToGuest)
     2258{
     2259    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    26612260
    26622261    /** @todo r=bird: This command isn't very unix friendly in general. mkdir
     
    26782277        { "--dryrun",              GETOPTDEF_COPY_DRYRUN,           RTGETOPT_REQ_NOTHING },
    26792278        { "--follow",              GETOPTDEF_COPY_FOLLOW,           RTGETOPT_REQ_NOTHING },
    2680         { "--username",            'u',                             RTGETOPT_REQ_STRING  },
    2681         { "--passwordfile",        'p',                             RTGETOPT_REQ_STRING  },
    2682         { "--password",            GETOPTDEF_COPY_PASSWORD,         RTGETOPT_REQ_STRING  },
    2683         { "--domain",              'd',                             RTGETOPT_REQ_STRING  },
    26842279        { "--recursive",           'R',                             RTGETOPT_REQ_NOTHING },
    2685         { "--target-directory",    GETOPTDEF_COPY_TARGETDIR,        RTGETOPT_REQ_STRING  },
    2686         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     2280        { "--target-directory",    GETOPTDEF_COPY_TARGETDIR,        RTGETOPT_REQ_STRING  }
    26872281    };
    26882282
     
    26902284    RTGETOPTUNION ValueUnion;
    26912285    RTGETOPTSTATE GetState;
    2692     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    2693                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2286    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     2287                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    26942288
    26952289    Utf8Str strSource;
    26962290    Utf8Str strDest;
    2697     Utf8Str strUsername;
    2698     Utf8Str strPassword;
    2699     Utf8Str strDomain;
    27002291    uint32_t fFlags = CopyFileFlag_None;
    2701     bool fVerbose = false;
    27022292    bool fCopyRecursive = false;
    27032293    bool fDryRun = false;
     
    27192309                break;
    27202310
    2721             case 'u': /* User name */
    2722                 strUsername = ValueUnion.psz;
    2723                 break;
    2724 
    2725             case GETOPTDEF_COPY_PASSWORD: /* Password */
    2726                 strPassword = ValueUnion.psz;
    2727                 break;
    2728 
    2729             case 'p': /* Password file */
    2730             {
    2731                 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
    2732                 if (rcExit != RTEXITCODE_SUCCESS)
    2733                     return rcExit;
    2734                 break;
    2735             }
    2736 
    2737             case 'd': /* domain */
    2738                 strDomain = ValueUnion.psz;
    2739                 break;
    2740 
    27412311            case 'R': /* Recursive processing */
    27422312                fFlags |= CopyFileFlag_Recursive;
     
    27452315            case GETOPTDEF_COPY_TARGETDIR:
    27462316                strDest = ValueUnion.psz;
    2747                 break;
    2748 
    2749             case 'v': /* Verbose */
    2750                 fVerbose = true;
    27512317                break;
    27522318
     
    27562322                 * --target-directory yet? Then use the current
    27572323                 * (= last) argument as destination. */
    2758                 if (   pArg->argc == GetState.iNext
     2324                if (  pCtx->iArgc == GetState.iNext
    27592325                    && strDest.isEmpty())
    27602326                {
     
    27712337            default:
    27722338                return RTGetOptPrintError(ch, &ValueUnion);
     2339                break;
    27732340        }
    27742341    }
     
    27822349                           "No destination specified!");
    27832350
    2784     if (strUsername.isEmpty())
    2785         return errorSyntax(USAGE_GUESTCONTROL,
    2786                            "No user name specified!");
    2787 
    27882351    /*
    27892352     * Done parsing arguments, do some more preparations.
    27902353     */
    2791     if (fVerbose)
     2354    if (pCtx->fVerbose)
    27922355    {
    27932356        if (fHostToGuest)
     
    28022365     * the routines need to know when handling the actual copying. */
    28032366    PCOPYCONTEXT pContext = NULL;
    2804     vrc = ctrlCopyContextCreate(guest, fVerbose, fDryRun, fHostToGuest,
    2805                                 strUsername, strPassword, strDomain,
     2367    vrc = ctrlCopyContextCreate(pCtx, fDryRun, fHostToGuest,
    28062368                                "VBoxManage Guest Control Copy", &pContext);
    28072369    if (RT_FAILURE(vrc))
     
    28622424            }
    28632425
    2864             if (fVerbose)
     2426            if (pCtx->fVerbose)
    28652427                RTPrintf("Source: %s\n", pszSource);
    28662428
     
    29492511}
    29502512
    2951 static RTEXITCODE handleCtrlCreateDirectory(ComPtr<IGuest> pGuest, HandlerArg *pArg)
    2952 {
    2953     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
     2513static DECLCALLBACK(RTEXITCODE) handleCtrlCopyFrom(PGCTLCMDCTX pCtx)
     2514{
     2515    return handleCtrlCopy(pCtx, false /* Guest to host */);
     2516}
     2517
     2518static DECLCALLBACK(RTEXITCODE) handleCtrlCopyTo(PGCTLCMDCTX pCtx)
     2519{
     2520    return handleCtrlCopy(pCtx, true /* Host to guest */);
     2521}
     2522
     2523static DECLCALLBACK(RTEXITCODE) handleCtrlCreateDirectory(PGCTLCMDCTX pCtx)
     2524{
     2525    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    29542526
    29552527    /*
     
    29622534    {
    29632535        { "--mode",                'm',                             RTGETOPT_REQ_UINT32  },
    2964         { "--parents",             'P',                             RTGETOPT_REQ_NOTHING },
    2965         { "--username",            'u',                             RTGETOPT_REQ_STRING  },
    2966         { "--passwordfile",        'p',                             RTGETOPT_REQ_STRING  },
    2967         { "--password",            GETOPTDEF_MKDIR_PASSWORD,        RTGETOPT_REQ_STRING  },
    2968         { "--domain",              'd',                             RTGETOPT_REQ_STRING  },
    2969         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     2536        { "--parents",             'P',                             RTGETOPT_REQ_NOTHING }
    29702537    };
    29712538
     
    29732540    RTGETOPTUNION ValueUnion;
    29742541    RTGETOPTSTATE GetState;
    2975     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    2976                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    2977 
    2978     Utf8Str strUsername;
    2979     Utf8Str strPassword;
    2980     Utf8Str strDomain;
     2542    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     2543                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2544
    29812545    SafeArray<DirectoryCreateFlag_T> dirCreateFlags;
    29822546    uint32_t fDirMode = 0; /* Default mode. */
    2983     bool fVerbose = false;
    2984 
    29852547    DESTDIRMAP mapDirs;
    29862548
     
    29982560                break;
    29992561
    3000             case 'u': /* User name */
    3001                 strUsername = ValueUnion.psz;
    3002                 break;
    3003 
    3004             case GETOPTDEF_MKDIR_PASSWORD: /* Password */
    3005                 strPassword = ValueUnion.psz;
    3006                 break;
    3007 
    3008             case 'p': /* Password file */
    3009             {
    3010                 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
    3011                 if (rcExit != RTEXITCODE_SUCCESS)
    3012                     return rcExit;
    3013                 break;
    3014             }
    3015 
    3016             case 'd': /* domain */
    3017                 strDomain = ValueUnion.psz;
    3018                 break;
    3019 
    3020             case 'v': /* Verbose */
    3021                 fVerbose = true;
    3022                 break;
    3023 
    30242562            case VINF_GETOPT_NOT_OPTION:
    3025             {
    30262563                mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
    30272564                break;
    3028             }
    30292565
    30302566            default:
    30312567                return RTGetOptPrintError(ch, &ValueUnion);
     2568                break;
    30322569        }
    30332570    }
     
    30372574        return errorSyntax(USAGE_GUESTCONTROL, "No directory to create specified!");
    30382575
    3039     if (strUsername.isEmpty())
    3040         return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
    3041 
    30422576    /*
    30432577     * Create the directories.
    30442578     */
    3045     HRESULT hrc = S_OK;
    3046     if (fVerbose && cDirs)
    3047         RTPrintf("Creating %u directories ...\n", cDirs);
    3048 
    3049     ComPtr<IGuestSession> pGuestSession;
    3050     hrc = pGuest->CreateSession(Bstr(strUsername).raw(),
    3051                                 Bstr(strPassword).raw(),
    3052                                 Bstr(strDomain).raw(),
    3053                                 Bstr("VBoxManage Guest Control MkDir").raw(),
    3054                                 pGuestSession.asOutParam());
    3055     if (FAILED(hrc))
    3056     {
    3057         ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    3058         return RTEXITCODE_FAILURE;
    3059     }
     2579    HRESULT rc = S_OK;
     2580    if (pCtx->fVerbose && cDirs)
     2581        RTPrintf("Creating %RU32 directories ...\n", cDirs);
    30602582
    30612583    DESTDIRMAPITER it = mapDirs.begin();
    3062     while (it != mapDirs.end())
    3063     {
    3064         if (fVerbose)
     2584    while (   (it != mapDirs.end())
     2585           && !g_fGuestCtrlCanceled)
     2586    {
     2587        if (pCtx->fVerbose)
    30652588            RTPrintf("Creating directory \"%s\" ...\n", it->first.c_str());
    30662589
    3067         hrc = pGuestSession->DirectoryCreate(Bstr(it->first).raw(), fDirMode, ComSafeArrayAsInParam(dirCreateFlags));
    3068         if (FAILED(hrc))
    3069         {
    3070             ctrlPrintError(pGuest, COM_IIDOF(IGuestSession)); /* Return code ignored, save original rc. */
    3071             break;
    3072         }
    3073 
     2590        CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryCreate(Bstr(it->first).raw(),
     2591                                               fDirMode, ComSafeArrayAsInParam(dirCreateFlags)));
    30742592        it++;
    30752593    }
    30762594
    3077     if (!pGuestSession.isNull())
    3078         pGuestSession->Close();
    3079 
    3080     return FAILED(hrc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
    3081 }
    3082 
    3083 static RTEXITCODE handleCtrlCreateTemp(ComPtr<IGuest> pGuest, HandlerArg *pArg)
    3084 {
    3085     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
     2595    return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
     2596}
     2597
     2598static DECLCALLBACK(RTEXITCODE) handleCtrlRemoveDirectory(PGCTLCMDCTX pCtx)
     2599{
     2600    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    30862601
    30872602    /*
     
    30932608    static const RTGETOPTDEF s_aOptions[] =
    30942609    {
     2610        { "--recursive",           GETOPTDEF_RMDIR_RECURSIVE,       RTGETOPT_REQ_NOTHING }
     2611    };
     2612
     2613    int ch;
     2614    RTGETOPTUNION ValueUnion;
     2615    RTGETOPTSTATE GetState;
     2616    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     2617                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2618
     2619    bool fRecursive = false;
     2620    DESTDIRMAP mapDirs;
     2621
     2622    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     2623    {
     2624        /* For options that require an argument, ValueUnion has received the value. */
     2625        switch (ch)
     2626        {
     2627            case GETOPTDEF_RMDIR_RECURSIVE:
     2628                fRecursive = true;
     2629                break;
     2630
     2631            case VINF_GETOPT_NOT_OPTION:
     2632                mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
     2633                break;
     2634
     2635            default:
     2636                return RTGetOptPrintError(ch, &ValueUnion);
     2637                break;
     2638        }
     2639    }
     2640
     2641    uint32_t cDirs = mapDirs.size();
     2642    if (!cDirs)
     2643        return errorSyntax(USAGE_GUESTCONTROL, "No directory to remove specified!");
     2644
     2645    /*
     2646     * Remove the directories.
     2647     */
     2648    HRESULT rc = S_OK;
     2649    if (pCtx->fVerbose && cDirs)
     2650        RTPrintf("Removing %RU32 directories ...\n", cDirs);
     2651
     2652    DESTDIRMAPITER it = mapDirs.begin();
     2653    while (   (it != mapDirs.end())
     2654           && !g_fGuestCtrlCanceled)
     2655    {
     2656        if (pCtx->fVerbose)
     2657            RTPrintf("%s directory \"%s\" ...\n",
     2658                     fRecursive ? "Recursively removing" : "Removing",
     2659                     it->first.c_str());
     2660        try
     2661        {
     2662            if (fRecursive)
     2663            {
     2664                com::SafeArray<DirectoryRemoveRecFlag_T> aRemRecFlags;
     2665                /** @todo Make flags configurable. */
     2666                aRemRecFlags.push_back(DirectoryRemoveRecFlag_ContentAndDir);
     2667
     2668                ComPtr<IProgress> pProgress;
     2669                CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRemoveRecursive(Bstr(it->first).raw(),
     2670                                                                                ComSafeArrayAsInParam(aRemRecFlags),
     2671                                                                                pProgress.asOutParam()));
     2672                if (pCtx->fVerbose)
     2673                    rc = showProgress(pProgress);
     2674                else
     2675                    rc = pProgress->WaitForCompletion(-1 /* No timeout */);
     2676                if (SUCCEEDED(rc))
     2677                    CHECK_PROGRESS_ERROR(pProgress, ("Directory deletion failed"));
     2678            }
     2679            else
     2680                CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRemove(Bstr(it->first).raw()));
     2681        }
     2682        catch (std::bad_alloc)
     2683        {
     2684            rc = E_OUTOFMEMORY;
     2685            break;
     2686        }
     2687
     2688        it++;
     2689    }
     2690
     2691    return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
     2692}
     2693
     2694static DECLCALLBACK(RTEXITCODE) handleCtrlRemoveFile(PGCTLCMDCTX pCtx)
     2695{
     2696    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     2697
     2698    /*
     2699     * Parse arguments.
     2700     *
     2701     * Note! No direct returns here, everyone must go thru the cleanup at the
     2702     *       end of this function.
     2703     */
     2704    static const RTGETOPTDEF s_aOptions[] = { 0 };
     2705
     2706    int ch;
     2707    RTGETOPTUNION ValueUnion;
     2708    RTGETOPTSTATE GetState;
     2709    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     2710                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2711
     2712    DESTDIRMAP mapDirs;
     2713
     2714    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     2715    {
     2716        /* For options that require an argument, ValueUnion has received the value. */
     2717        switch (ch)
     2718        {
     2719            case VINF_GETOPT_NOT_OPTION:
     2720                mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
     2721                break;
     2722
     2723            default:
     2724                return RTGetOptPrintError(ch, &ValueUnion);
     2725                break;
     2726        }
     2727    }
     2728
     2729    uint32_t cFiles = mapDirs.size();
     2730    if (!cFiles)
     2731        return errorSyntax(USAGE_GUESTCONTROL, "No file to remove specified!");
     2732
     2733    /*
     2734     * Create the directories.
     2735     */
     2736    HRESULT rc = S_OK;
     2737    if (pCtx->fVerbose && cFiles)
     2738        RTPrintf("Removing %RU32 file(s) ...\n", cFiles);
     2739
     2740    DESTDIRMAPITER it = mapDirs.begin();
     2741    while (   (it != mapDirs.end())
     2742           && !g_fGuestCtrlCanceled)
     2743    {
     2744        if (pCtx->fVerbose)
     2745            RTPrintf("Removing file \"%s\" ...\n", it->first.c_str());
     2746
     2747        CHECK_ERROR_BREAK(pCtx->pGuestSession, FileRemove(Bstr(it->first).raw()));
     2748
     2749        it++;
     2750    }
     2751
     2752    return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
     2753}
     2754
     2755static DECLCALLBACK(RTEXITCODE) handleCtrlCreateTemp(PGCTLCMDCTX pCtx)
     2756{
     2757    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     2758
     2759    /*
     2760     * Parse arguments.
     2761     *
     2762     * Note! No direct returns here, everyone must go thru the cleanup at the
     2763     *       end of this function.
     2764     */
     2765    static const RTGETOPTDEF s_aOptions[] =
     2766    {
    30952767        { "--mode",                'm',                             RTGETOPT_REQ_UINT32  },
    30962768        { "--directory",           'D',                             RTGETOPT_REQ_NOTHING },
    30972769        { "--secure",              's',                             RTGETOPT_REQ_NOTHING },
    3098         { "--tmpdir",              't',                             RTGETOPT_REQ_STRING  },
    3099         { "--username",            'u',                             RTGETOPT_REQ_STRING  },
    3100         { "--passwordfile",        'p',                             RTGETOPT_REQ_STRING  },
    3101         { "--password",            GETOPTDEF_MKDIR_PASSWORD,        RTGETOPT_REQ_STRING  },
    3102         { "--domain",              'd',                             RTGETOPT_REQ_STRING  },
    3103         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     2770        { "--tmpdir",              't',                             RTGETOPT_REQ_STRING  }
    31042771    };
    31052772
     
    31072774    RTGETOPTUNION ValueUnion;
    31082775    RTGETOPTSTATE GetState;
    3109     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    3110                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    3111 
    3112     Utf8Str strUsername;
    3113     Utf8Str strPassword;
    3114     Utf8Str strDomain;
     2776    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     2777                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2778
    31152779    Utf8Str strTemplate;
    31162780    uint32_t fMode = 0; /* Default mode. */
     
    31182782    bool fSecure = false;
    31192783    Utf8Str strTempDir;
    3120     bool fVerbose = false;
    31212784
    31222785    DESTDIRMAP mapDirs;
     
    31412804            case 't': /* Temp directory */
    31422805                strTempDir = ValueUnion.psz;
    3143                 break;
    3144 
    3145             case 'u': /* User name */
    3146                 strUsername = ValueUnion.psz;
    3147                 break;
    3148 
    3149             case GETOPTDEF_MKDIR_PASSWORD: /* Password */
    3150                 strPassword = ValueUnion.psz;
    3151                 break;
    3152 
    3153             case 'p': /* Password file */
    3154             {
    3155                 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
    3156                 if (rcExit != RTEXITCODE_SUCCESS)
    3157                     return rcExit;
    3158                 break;
    3159             }
    3160 
    3161             case 'd': /* domain */
    3162                 strDomain = ValueUnion.psz;
    3163                 break;
    3164 
    3165             case 'v': /* Verbose */
    3166                 fVerbose = true;
    31672806                break;
    31682807
     
    31792818            default:
    31802819                return RTGetOptPrintError(ch, &ValueUnion);
     2820                break;
    31812821        }
    31822822    }
     
    31842824    if (strTemplate.isEmpty())
    31852825        return errorSyntax(USAGE_GUESTCONTROL, "No template specified!");
    3186 
    3187     if (strUsername.isEmpty())
    3188         return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
    31892826
    31902827    if (!fDirectory)
     
    31942831     * Create the directories.
    31952832     */
    3196     HRESULT hrc = S_OK;
    3197     if (fVerbose)
     2833    if (pCtx->fVerbose)
    31982834    {
    31992835        if (fDirectory && !strTempDir.isEmpty())
     
    32112847    }
    32122848
    3213     ComPtr<IGuestSession> pGuestSession;
    3214     hrc = pGuest->CreateSession(Bstr(strUsername).raw(),
    3215                                 Bstr(strPassword).raw(),
    3216                                 Bstr(strDomain).raw(),
    3217                                 Bstr("VBoxManage Guest Control MkTemp").raw(),
    3218                                 pGuestSession.asOutParam());
    3219     if (FAILED(hrc))
    3220     {
    3221         ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    3222         return RTEXITCODE_FAILURE;
    3223     }
    3224 
     2849    HRESULT rc = S_OK;
    32252850    if (fDirectory)
    32262851    {
    32272852        Bstr directory;
    3228         hrc = pGuestSession->DirectoryCreateTemp(Bstr(strTemplate).raw(),
    3229                                                  fMode, Bstr(strTempDir).raw(),
    3230                                                  fSecure,
    3231                                                  directory.asOutParam());
    3232         if (SUCCEEDED(hrc))
     2853        CHECK_ERROR(pCtx->pGuestSession, DirectoryCreateTemp(Bstr(strTemplate).raw(),
     2854                                                             fMode, Bstr(strTempDir).raw(),
     2855                                                             fSecure,
     2856                                                             directory.asOutParam()));
     2857        if (SUCCEEDED(rc))
    32332858            RTPrintf("Directory name: %ls\n", directory.raw());
    32342859    }
    32352860    // else - temporary file not yet implemented
    3236     if (FAILED(hrc))
    3237         ctrlPrintError(pGuest, COM_IIDOF(IGuestSession)); /* Return code ignored, save original rc. */
    3238 
    3239     if (!pGuestSession.isNull())
    3240         pGuestSession->Close();
    3241 
    3242     return FAILED(hrc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
    3243 }
    3244 
    3245 static int handleCtrlStat(ComPtr<IGuest> pGuest, HandlerArg *pArg)
    3246 {
    3247     AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     2861
     2862    return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
     2863}
     2864
     2865static DECLCALLBACK(RTEXITCODE) handleCtrlStat(PGCTLCMDCTX pCtx)
     2866{
     2867    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    32482868
    32492869    static const RTGETOPTDEF s_aOptions[] =
     
    32522872        { "--file-system",         'f',                             RTGETOPT_REQ_NOTHING },
    32532873        { "--format",              'c',                             RTGETOPT_REQ_STRING },
    3254         { "--username",            'u',                             RTGETOPT_REQ_STRING  },
    3255         { "--passwordfile",        'p',                             RTGETOPT_REQ_STRING  },
    3256         { "--password",            GETOPTDEF_STAT_PASSWORD,         RTGETOPT_REQ_STRING  },
    3257         { "--domain",              'd',                             RTGETOPT_REQ_STRING  },
    3258         { "--terse",               't',                             RTGETOPT_REQ_NOTHING },
    3259         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     2874        { "--terse",               't',                             RTGETOPT_REQ_NOTHING }
    32602875    };
    32612876
     
    32632878    RTGETOPTUNION ValueUnion;
    32642879    RTGETOPTSTATE GetState;
    3265     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    3266                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    3267 
    3268     Utf8Str strUsername;
    3269     Utf8Str strPassword;
    3270     Utf8Str strDomain;
    3271 
    3272     bool fVerbose = false;
     2880    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     2881                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2882
    32732883    DESTDIRMAP mapObjs;
    32742884
    32752885    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    32762886    {
     2887                        RTPrintf("val: %s=%d\n", ValueUnion.psz, ch);
     2888
    32772889        /* For options that require an argument, ValueUnion has received the value. */
    32782890        switch (ch)
    32792891        {
    3280             case 'u': /* User name */
    3281                 strUsername = ValueUnion.psz;
    3282                 break;
    3283 
    3284             case GETOPTDEF_STAT_PASSWORD: /* Password */
    3285                 strPassword = ValueUnion.psz;
    3286                 break;
    3287 
    3288             case 'p': /* Password file */
    3289             {
    3290                 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
    3291                 if (rcExit != RTEXITCODE_SUCCESS)
    3292                     return rcExit;
    3293                 break;
    3294             }
    3295 
    3296             case 'd': /* domain */
    3297                 strDomain = ValueUnion.psz;
    3298                 break;
    3299 
    33002892            case 'L': /* Dereference */
    33012893            case 'f': /* File-system */
     
    33062898                break; /* Never reached. */
    33072899
    3308             case 'v': /* Verbose */
    3309                 fVerbose = true;
    3310                 break;
    3311 
    33122900            case VINF_GETOPT_NOT_OPTION:
    3313             {
    33142901                mapObjs[ValueUnion.psz]; /* Add element to check to map. */
    33152902                break;
    3316             }
    33172903
    33182904            default:
    33192905                return RTGetOptPrintError(ch, &ValueUnion);
     2906                break;
    33202907        }
    33212908    }
     
    33252912        return errorSyntax(USAGE_GUESTCONTROL, "No element(s) to check specified!");
    33262913
    3327     if (strUsername.isEmpty())
    3328         return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
    3329 
    3330     ComPtr<IGuestSession> pGuestSession;
    3331     HRESULT hrc = pGuest->CreateSession(Bstr(strUsername).raw(),
    3332                                         Bstr(strPassword).raw(),
    3333                                         Bstr(strDomain).raw(),
    3334                                         Bstr("VBoxManage Guest Control Stat").raw(),
    3335                                         pGuestSession.asOutParam());
    3336     if (FAILED(hrc))
    3337         return ctrlPrintError(pGuest, COM_IIDOF(IGuest));
     2914    HRESULT rc;
    33382915
    33392916    /*
    3340      * Create the directories.
     2917     * Doing the checks.
    33412918     */
    33422919    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
     
    33442921    while (it != mapObjs.end())
    33452922    {
    3346         if (fVerbose)
     2923        if (pCtx->fVerbose)
    33472924            RTPrintf("Checking for element \"%s\" ...\n", it->first.c_str());
    33482925
    33492926        ComPtr<IGuestFsObjInfo> pFsObjInfo;
    3350         hrc = pGuestSession->FileQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam());
    3351         if (FAILED(hrc))
    3352             hrc = pGuestSession->DirectoryQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam());
    3353 
    3354         if (FAILED(hrc))
     2927        rc = pCtx->pGuestSession->FileQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam());
     2928        if (FAILED(rc))
     2929            rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam());
     2930
     2931        if (FAILED(rc))
    33552932        {
    33562933            /* If there's at least one element which does not exist on the guest,
    33572934             * drop out with exitcode 1. */
    3358             if (fVerbose)
     2935            if (pCtx->fVerbose)
    33592936                RTPrintf("Cannot stat for element \"%s\": No such element\n",
    33602937                         it->first.c_str());
     
    33642941        {
    33652942            FsObjType_T objType;
    3366             hrc = pFsObjInfo->COMGETTER(Type)(&objType);
    3367             if (FAILED(hrc))
    3368                 return ctrlPrintError(pGuest, COM_IIDOF(IGuestFsObjInfo));
     2943            pFsObjInfo->COMGETTER(Type)(&objType);
    33692944            switch (objType)
    33702945            {
     
    33922967    }
    33932968
    3394     if (!pGuestSession.isNull())
    3395         pGuestSession->Close();
    3396 
    33972969    return rcExit;
    33982970}
    33992971
    3400 static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg)
    3401 {
    3402     AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     2972static DECLCALLBACK(RTEXITCODE) handleCtrlUpdateAdditions(PGCTLCMDCTX pCtx)
     2973{
     2974    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    34032975
    34042976    /*
     
    34082980    Utf8Str strSource;
    34092981    com::SafeArray<IN_BSTR> aArgs;
    3410     bool fVerbose = false;
    34112982    bool fWaitStartOnly = false;
    34122983
     
    34142985    {
    34152986        { "--source",              's',         RTGETOPT_REQ_STRING  },
    3416         { "--verbose",             'v',         RTGETOPT_REQ_NOTHING },
    34172987        { "--wait-start",          'w',         RTGETOPT_REQ_NOTHING }
    34182988    };
     
    34212991    RTGETOPTUNION ValueUnion;
    34222992    RTGETOPTSTATE GetState;
    3423     RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
     2993    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, 0);
    34242994
    34252995    int vrc = VINF_SUCCESS;
     
    34333003                break;
    34343004
    3435             case 'v':
    3436                 fVerbose = true;
    3437                 break;
    3438 
    34393005            case 'w':
    34403006                fWaitStartOnly = true;
     
    34423008
    34433009            case VINF_GETOPT_NOT_OPTION:
    3444             {
    34453010                if (aArgs.size() == 0 && strSource.isEmpty())
    34463011                    strSource = ValueUnion.psz;
     
    34483013                    aArgs.push_back(Bstr(ValueUnion.psz).raw());
    34493014                break;
    3450             }
    34513015
    34523016            default:
    34533017                return RTGetOptPrintError(ch, &ValueUnion);
    3454         }
    3455     }
    3456 
    3457     if (fVerbose)
     3018                break;
     3019        }
     3020    }
     3021
     3022    if (pCtx->fVerbose)
    34583023        RTPrintf("Updating Guest Additions ...\n");
    34593024
     
    34623027    {
    34633028        ComPtr<ISystemProperties> pProperties;
    3464         CHECK_ERROR_BREAK(pArg->virtualBox, COMGETTER(SystemProperties)(pProperties.asOutParam()));
     3029        CHECK_ERROR_BREAK(pCtx->handlerArg.virtualBox, COMGETTER(SystemProperties)(pProperties.asOutParam()));
    34653030        Bstr strISO;
    34663031        CHECK_ERROR_BREAK(pProperties, COMGETTER(DefaultAdditionsISO)(strISO.asOutParam()));
     
    34833048    if (RT_SUCCESS(vrc))
    34843049    {
    3485         if (fVerbose)
     3050        if (pCtx->fVerbose)
    34863051            RTPrintf("Using source: %s\n", strSource.c_str());
    34873052
     
    34903055        {
    34913056            aUpdateFlags.push_back(AdditionsUpdateFlag_WaitForUpdateStartOnly);
    3492             if (fVerbose)
     3057            if (pCtx->fVerbose)
    34933058                RTPrintf("Preparing and waiting for Guest Additions installer to start ...\n");
    34943059        }
    34953060
    34963061        ComPtr<IProgress> pProgress;
    3497         CHECK_ERROR(guest, UpdateGuestAdditions(Bstr(strSource).raw(),
     3062        CHECK_ERROR(pCtx->pGuest, UpdateGuestAdditions(Bstr(strSource).raw(),
    34983063                                                ComSafeArrayAsInParam(aArgs),
    34993064                                                /* Wait for whole update process to complete. */
     
    35013066                                                pProgress.asOutParam()));
    35023067        if (FAILED(rc))
    3503             vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
     3068            vrc = ctrlPrintError(pCtx->pGuest, COM_IIDOF(IGuest));
    35043069        else
    35053070        {
    3506             if (fVerbose)
     3071            if (pCtx->fVerbose)
    35073072                rc = showProgress(pProgress);
    35083073            else
     
    35133078            vrc = ctrlPrintProgressError(pProgress);
    35143079            if (   RT_SUCCESS(vrc)
    3515                 && fVerbose)
     3080                && pCtx->fVerbose)
    35163081            {
    35173082                RTPrintf("Guest Additions update successful\n");
     
    35233088}
    35243089
    3525 static RTEXITCODE handleCtrlList(ComPtr<IGuest> guest, HandlerArg *pArg)
    3526 {
    3527     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
    3528 
    3529     if (pArg->argc < 1)
     3090static DECLCALLBACK(RTEXITCODE) handleCtrlList(PGCTLCMDCTX pCtx)
     3091{
     3092    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     3093
     3094    if (pCtx->iArgc < 1)
    35303095        return errorSyntax(USAGE_GUESTCONTROL, "Must specify a listing category");
    35313096
     
    35383103    bool fListProcesses = false;
    35393104    bool fListFiles = false;
    3540     if (   !RTStrICmp(pArg->argv[0], "sessions")
    3541         || !RTStrICmp(pArg->argv[0], "sess"))
     3105    if (   !RTStrICmp(pCtx->ppaArgv[0], "sessions")
     3106        || !RTStrICmp(pCtx->ppaArgv[0], "sess"))
    35423107        fListSessions = true;
    3543     else if (   !RTStrICmp(pArg->argv[0], "processes")
    3544              || !RTStrICmp(pArg->argv[0], "procs"))
     3108    else if (   !RTStrICmp(pCtx->ppaArgv[0], "processes")
     3109             || !RTStrICmp(pCtx->ppaArgv[0], "procs"))
    35453110        fListSessions = fListProcesses = true; /* Showing processes implies showing sessions. */
    3546     else if (   !RTStrICmp(pArg->argv[0], "files"))
     3111    else if (   !RTStrICmp(pCtx->ppaArgv[0], "files"))
    35473112        fListSessions = fListFiles = true;     /* Showing files implies showing sessions. */
    3548     else if (!RTStrICmp(pArg->argv[0], "all"))
     3113    else if (!RTStrICmp(pCtx->ppaArgv[0], "all"))
    35493114        fListAll = true;
    35503115
     
    35623127
    35633128            SafeIfaceArray <IGuestSession> collSessions;
    3564             CHECK_ERROR_BREAK(guest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
     3129            CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
    35653130            size_t cSessions = collSessions.size();
    35663131
     
    36523217    }
    36533218    else
    3654         return errorSyntax(USAGE_GUESTCONTROL, "Invalid listing category '%s", pArg->argv[0]);
     3219        return errorSyntax(USAGE_GUESTCONTROL, "Invalid listing category '%s", pCtx->ppaArgv[0]);
    36553220
    36563221    return rcExit;
    36573222}
    36583223
    3659 static RTEXITCODE handleCtrlProcessClose(ComPtr<IGuest> guest, HandlerArg *pArg)
    3660 {
    3661     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
    3662 
    3663     if (pArg->argc < 1)
     3224static DECLCALLBACK(RTEXITCODE) handleCtrlProcessClose(PGCTLCMDCTX pCtx)
     3225{
     3226    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     3227
     3228    if (pCtx->iArgc < 1)
    36643229        return errorSyntax(USAGE_GUESTCONTROL, "Must specify at least a PID to close");
    36653230
     
    36733238    {
    36743239        { "--session-id",          'i',                             RTGETOPT_REQ_UINT32  },
    3675         { "--session-name",        'n',                             RTGETOPT_REQ_STRING  },
    3676         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     3240        { "--session-name",        'n',                             RTGETOPT_REQ_STRING  }
    36773241    };
    36783242
     
    36803244    RTGETOPTUNION ValueUnion;
    36813245    RTGETOPTSTATE GetState;
    3682     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    3683                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3246    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     3247                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    36843248
    36853249    std::vector < uint32_t > vecPID;
    36863250    ULONG ulSessionID = UINT32_MAX;
    36873251    Utf8Str strSessionName;
    3688     bool fVerbose = false;
    36893252
    36903253    int vrc = VINF_SUCCESS;
     
    37043267                break;
    37053268
    3706             case 'v': /* Verbose */
    3707                 fVerbose = true;
    3708                 break;
    3709 
    37103269            case VINF_GETOPT_NOT_OPTION:
    3711                 if (pArg->argc == GetState.iNext)
     3270                if (pCtx->iArgc == GetState.iNext)
    37123271                {
    37133272                    /* Treat every else specified as a PID to kill. */
     
    37303289            default:
    37313290                return RTGetOptPrintError(ch, &ValueUnion);
     3291                break;
    37323292        }
    37333293    }
     
    37593319
    37603320        SafeIfaceArray <IGuestSession> collSessions;
    3761         CHECK_ERROR_BREAK(guest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
     3321        CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
    37623322        size_t cSessions = collSessions.size();
    37633323
     
    38103370                    if (fProcFound)
    38113371                    {
    3812                         if (fVerbose)
     3372                        if (pCtx->fVerbose)
    38133373                            RTPrintf("Terminating process (PID %RU32) (session ID %RU32) ...\n",
    38143374                                     uPID, uID);
     
    38453405}
    38463406
    3847 static RTEXITCODE handleCtrlProcess(ComPtr<IGuest> guest, HandlerArg *pArg)
    3848 {
    3849     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
    3850 
    3851     if (pArg->argc < 1)
     3407static DECLCALLBACK(RTEXITCODE) handleCtrlProcess(PGCTLCMDCTX pCtx)
     3408{
     3409    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     3410
     3411    if (pCtx->iArgc < 1)
    38523412        return errorSyntax(USAGE_GUESTCONTROL, "Must specify an action");
    38533413
    38543414    /** Use RTGetOpt here when handling command line args gets more complex. */
    38553415
    3856     HandlerArg argSub = *pArg;
    3857     argSub.argc = pArg->argc - 1; /* Skip session action. */
    3858     argSub.argv = pArg->argv + 1; /* Same here. */
    3859 
    3860     if (   !RTStrICmp(pArg->argv[0], "close")
    3861         || !RTStrICmp(pArg->argv[0], "kill")
    3862         || !RTStrICmp(pArg->argv[0], "terminate"))
    3863     {
    3864         return handleCtrlProcessClose(guest, &argSub);
    3865     }
    3866 
    3867     return errorSyntax(USAGE_GUESTCONTROL, "Invalid process action '%s'", pArg->argv[0]);
    3868 }
    3869 
    3870 static RTEXITCODE handleCtrlSessionClose(ComPtr<IGuest> guest, HandlerArg *pArg)
    3871 {
    3872     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
    3873 
    3874     if (pArg->argc < 1)
     3416    if (   !RTStrICmp(pCtx->ppaArgv[0], "close")
     3417        || !RTStrICmp(pCtx->ppaArgv[0], "kill")
     3418        || !RTStrICmp(pCtx->ppaArgv[0], "terminate"))
     3419    {
     3420        pCtx->iFirstArgc++; /* Skip process action. */
     3421        return handleCtrlProcessClose(pCtx);
     3422    }
     3423
     3424    return errorSyntax(USAGE_GUESTCONTROL, "Invalid process action '%s'", pCtx->ppaArgv[0]);
     3425}
     3426
     3427static DECLCALLBACK(RTEXITCODE) handleCtrlSessionClose(PGCTLCMDCTX pCtx)
     3428{
     3429    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     3430
     3431    if (pCtx->iArgc < 1)
    38753432        return errorSyntax(USAGE_GUESTCONTROL, "Must specify at least a session ID to close");
    38763433
     
    38853442        { "--all",                 GETOPTDEF_SESSIONCLOSE_ALL,      RTGETOPT_REQ_NOTHING  },
    38863443        { "--session-id",          'i',                             RTGETOPT_REQ_UINT32  },
    3887         { "--session-name",        'n',                             RTGETOPT_REQ_STRING  },
    3888         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
     3444        { "--session-name",        'n',                             RTGETOPT_REQ_STRING  }
    38893445    };
    38903446
     
    38923448    RTGETOPTUNION ValueUnion;
    38933449    RTGETOPTSTATE GetState;
    3894     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    3895                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3450    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     3451                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    38963452
    38973453    ULONG ulSessionID = UINT32_MAX;
    38983454    Utf8Str strSessionName;
    3899     bool fVerbose = false;
    39003455
    39013456    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     
    39123467                break;
    39133468
    3914             case 'v': /* Verbose */
    3915                 fVerbose = true;
    3916                 break;
    3917 
    39183469            case GETOPTDEF_SESSIONCLOSE_ALL:
    39193470                strSessionName = "*";
     
    39263477            default:
    39273478                return RTGetOptPrintError(ch, &ValueUnion);
     3479                break;
    39283480        }
    39293481    }
     
    39493501
    39503502        SafeIfaceArray <IGuestSession> collSessions;
    3951         CHECK_ERROR_BREAK(guest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
     3503        CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
    39523504        size_t cSessions = collSessions.size();
    39533505
     
    39783530
    39793531                Assert(!pSession.isNull());
    3980                 if (fVerbose)
     3532                if (pCtx->fVerbose)
    39813533                    RTPrintf("Closing guest session ID=#%RU32 \"%s\" ...\n",
    39823534                             uID, strNameUtf8.c_str());
    39833535                CHECK_ERROR_BREAK(pSession, Close());
    3984                 if (fVerbose)
     3536                if (pCtx->fVerbose)
    39853537                    RTPrintf("Guest session successfully closed\n");
    39863538
     
    40003552}
    40013553
    4002 static RTEXITCODE handleCtrlSession(ComPtr<IGuest> guest, HandlerArg *pArg)
    4003 {
    4004     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
    4005 
    4006     if (pArg->argc < 1)
     3554static DECLCALLBACK(RTEXITCODE) handleCtrlSession(PGCTLCMDCTX pCtx)
     3555{
     3556    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     3557
     3558    if (pCtx->iArgc < 1)
    40073559        return errorSyntax(USAGE_GUESTCONTROL, "Must specify an action");
    40083560
    40093561    /** Use RTGetOpt here when handling command line args gets more complex. */
    40103562
    4011     HandlerArg argSub = *pArg;
    4012     argSub.argc = pArg->argc - 1; /* Skip session action. */
    4013     argSub.argv = pArg->argv + 1; /* Same here. */
    4014 
    4015     if (   !RTStrICmp(pArg->argv[0], "close")
    4016         || !RTStrICmp(pArg->argv[0], "kill")
    4017         || !RTStrICmp(pArg->argv[0], "terminate"))
    4018     {
    4019         return handleCtrlSessionClose(guest, &argSub);
    4020     }
    4021 
    4022     return errorSyntax(USAGE_GUESTCONTROL, "Invalid session action '%s'", pArg->argv[0]);
    4023 }
    4024 
    4025 static RTEXITCODE handleCtrlWatch(ComPtr<IGuest> guest, HandlerArg *pArg)
    4026 {
    4027     AssertPtrReturn(pArg, RTEXITCODE_SYNTAX);
     3563    if (   !RTStrICmp(pCtx->ppaArgv[0], "close")
     3564        || !RTStrICmp(pCtx->ppaArgv[0], "kill")
     3565        || !RTStrICmp(pCtx->ppaArgv[0], "terminate"))
     3566    {
     3567        pCtx->iFirstArgc++; /* Skip session action. */
     3568        return handleCtrlSessionClose(pCtx);
     3569    }
     3570
     3571    return errorSyntax(USAGE_GUESTCONTROL, "Invalid session action '%s'", pCtx->ppaArgv[0]);
     3572}
     3573
     3574static DECLCALLBACK(RTEXITCODE) handleCtrlWatch(PGCTLCMDCTX pCtx)
     3575{
     3576    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    40283577
    40293578    /*
    40303579     * Parse arguments.
    40313580     */
    4032     static const RTGETOPTDEF s_aOptions[] =
    4033     {
    4034         { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING }
    4035     };
     3581    static const RTGETOPTDEF s_aOptions[] = { 0 };
    40363582
    40373583    int ch;
    40383584    RTGETOPTUNION ValueUnion;
    40393585    RTGETOPTSTATE GetState;
    4040     RTGetOptInit(&GetState, pArg->argc, pArg->argv,
    4041                  s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    4042 
    4043     bool fVerbose = false;
     3586    RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv,
     3587                 s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    40443588
    40453589    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     
    40483592        switch (ch)
    40493593        {
    4050             case 'v': /* Verbose */
    4051                 fVerbose = true;
    4052                 break;
    4053 
    40543594            case VINF_GETOPT_NOT_OPTION:
    40553595                break;
     
    40573597            default:
    40583598                return RTGetOptPrintError(ch, &ValueUnion);
     3599                break;
    40593600        }
    40603601    }
     
    40763617            /* Register for IGuest events. */
    40773618            ComPtr<IEventSource> es;
    4078             CHECK_ERROR_BREAK(guest, COMGETTER(EventSource)(es.asOutParam()));
     3619            CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(EventSource)(es.asOutParam()));
    40793620            com::SafeArray<VBoxEventType_T> eventTypes;
    40803621            eventTypes.push_back(VBoxEventType_OnGuestSessionRegistered);
     
    40873628        } while (0);
    40883629
    4089         ctrlSignalHandlerInstall();
    4090 
    4091         if (fVerbose)
     3630        if (pCtx->fVerbose)
    40923631            RTPrintf("Waiting for events ...\n");
    40933632
     
    40983637        }
    40993638
    4100         if (fVerbose)
     3639        if (pCtx->fVerbose)
    41013640            RTPrintf("Signal caught, exiting ...\n");
    4102 
    4103         ctrlSignalHandlerUninstall();
    41043641
    41053642        if (!pGuestListener.isNull())
     
    41073644            /* Guest callback unregistration. */
    41083645            ComPtr<IEventSource> pES;
    4109             CHECK_ERROR(guest, COMGETTER(EventSource)(pES.asOutParam()));
     3646            CHECK_ERROR(pCtx->pGuest, COMGETTER(EventSource)(pES.asOutParam()));
    41103647            if (!pES.isNull())
    41113648                CHECK_ERROR(pES, UnregisterListener(pGuestListener));
     
    41293666int handleGuestControl(HandlerArg *pArg)
    41303667{
    4131     AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     3668    AssertPtrReturn(pArg, VERR_INVALID_POINTER);
    41323669
    41333670#ifdef DEBUG_andy_disabled
     
    41363673#endif
    41373674
    4138     HandlerArg arg = *pArg;
    4139     arg.argc = pArg->argc - 2; /* Skip VM name and sub command. */
    4140     arg.argv = pArg->argv + 2; /* Same here. */
    4141 
    4142     ComPtr<IGuest> guest;
    4143     int vrc = ctrlInitVM(pArg, pArg->argv[0] /* VM Name */, &guest);
    4144     if (RT_SUCCESS(vrc))
    4145     {
    4146         int rcExit;
    4147         if (pArg->argc < 2)
    4148             rcExit = errorSyntax(USAGE_GUESTCONTROL, "No sub command specified!");
    4149         else if (   !RTStrICmp(pArg->argv[1], "exec")
    4150                  || !RTStrICmp(pArg->argv[1], "execute"))
    4151             rcExit = handleCtrlProcessExec(guest, &arg);
    4152         else if (!RTStrICmp(pArg->argv[1], "copyfrom"))
    4153             rcExit = handleCtrlCopy(guest, &arg, false /* Guest to host */);
    4154         else if (   !RTStrICmp(pArg->argv[1], "copyto")
    4155                  || !RTStrICmp(pArg->argv[1], "cp"))
    4156             rcExit = handleCtrlCopy(guest, &arg, true /* Host to guest */);
    4157         else if (   !RTStrICmp(pArg->argv[1], "createdirectory")
    4158                  || !RTStrICmp(pArg->argv[1], "createdir")
    4159                  || !RTStrICmp(pArg->argv[1], "mkdir")
    4160                  || !RTStrICmp(pArg->argv[1], "md"))
    4161             rcExit = handleCtrlCreateDirectory(guest, &arg);
    4162         else if (   !RTStrICmp(pArg->argv[1], "createtemporary")
    4163                  || !RTStrICmp(pArg->argv[1], "createtemp")
    4164                  || !RTStrICmp(pArg->argv[1], "mktemp"))
    4165             rcExit = handleCtrlCreateTemp(guest, &arg);
    4166         else if (   !RTStrICmp(pArg->argv[1], "kill")    /* Linux. */
    4167                  || !RTStrICmp(pArg->argv[1], "pkill")   /* Solaris / *BSD. */
    4168                  || !RTStrICmp(pArg->argv[1], "pskill")) /* SysInternals version. */
    4169         {
    4170             /** @todo What about "taskkill" on Windows? */
    4171             rcExit = handleCtrlProcessClose(guest, &arg);
    4172         }
    4173         /** @todo Implement "killall"? */
    4174         else if (   !RTStrICmp(pArg->argv[1], "stat"))
    4175             rcExit = handleCtrlStat(guest, &arg);
    4176         else if (   !RTStrICmp(pArg->argv[1], "updateadditions")
    4177                  || !RTStrICmp(pArg->argv[1], "updateadds"))
    4178             rcExit = handleCtrlUpdateAdditions(guest, &arg);
    4179         else if (   !RTStrICmp(pArg->argv[1], "list"))
    4180             rcExit = handleCtrlList(guest, &arg);
    4181         else if (   !RTStrICmp(pArg->argv[1], "session"))
    4182             rcExit = handleCtrlSession(guest, &arg);
    4183         else if (   !RTStrICmp(pArg->argv[1], "process"))
    4184             rcExit = handleCtrlProcess(guest, &arg);
    4185         else if (   !RTStrICmp(pArg->argv[1], "watch"))
    4186             rcExit = handleCtrlWatch(guest, &arg);
    4187         else
    4188             rcExit = errorSyntax(USAGE_GUESTCONTROL, "Unknown sub command '%s' specified!", pArg->argv[1]);
    4189 
    4190         ctrlUninitVM(pArg);
     3675    /* pArg->argv[0] contains the VM name. */
     3676    /* pArg->argv[1] contains the guest control command. */
     3677    if (pArg->argc < 2)
     3678        return errorSyntax(USAGE_GUESTCONTROL, "No sub command specified!");
     3679
     3680    uint32_t uCmdCtxFlags = 0;
     3681    GCTLCMD gctlCmd;
     3682    if (   !RTStrICmp(pArg->argv[1], "exec")
     3683        || !RTStrICmp(pArg->argv[1], "execute"))
     3684        gctlCmd.pfnHandler = handleCtrlProcessExec;
     3685    else if (!RTStrICmp(pArg->argv[1], "copyfrom"))
     3686        gctlCmd.pfnHandler = handleCtrlCopyFrom;
     3687    else if (   !RTStrICmp(pArg->argv[1], "copyto")
     3688             || !RTStrICmp(pArg->argv[1], "cp"))
     3689        gctlCmd.pfnHandler = handleCtrlCopyTo;
     3690    else if (   !RTStrICmp(pArg->argv[1], "createdirectory")
     3691             || !RTStrICmp(pArg->argv[1], "createdir")
     3692             || !RTStrICmp(pArg->argv[1], "mkdir")
     3693             || !RTStrICmp(pArg->argv[1], "md"))
     3694        gctlCmd.pfnHandler = handleCtrlCreateDirectory;
     3695    else if (   !RTStrICmp(pArg->argv[1], "removedirectory")
     3696             || !RTStrICmp(pArg->argv[1], "removedir")
     3697             || !RTStrICmp(pArg->argv[1], "rmdir"))
     3698        gctlCmd.pfnHandler = handleCtrlRemoveDirectory;
     3699    else if (   !RTStrICmp(pArg->argv[1], "rm")
     3700             || !RTStrICmp(pArg->argv[1], "removefile"))
     3701        gctlCmd.pfnHandler = handleCtrlRemoveFile;
     3702    else if (   !RTStrICmp(pArg->argv[1], "createtemporary")
     3703             || !RTStrICmp(pArg->argv[1], "createtemp")
     3704             || !RTStrICmp(pArg->argv[1], "mktemp"))
     3705        gctlCmd.pfnHandler = handleCtrlCreateTemp;
     3706    else if (   !RTStrICmp(pArg->argv[1], "kill")    /* Linux. */
     3707             || !RTStrICmp(pArg->argv[1], "pkill")   /* Solaris / *BSD. */
     3708             || !RTStrICmp(pArg->argv[1], "pskill")) /* SysInternals version. */
     3709    {
     3710        /** @todo What about "taskkill" on Windows? */
     3711        uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS
     3712                     | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER;
     3713        gctlCmd.pfnHandler = handleCtrlProcessClose;
     3714    }
     3715    /** @todo Implement "killall"? */
     3716    else if (   !RTStrICmp(pArg->argv[1], "stat"))
     3717        gctlCmd.pfnHandler = handleCtrlStat;
     3718    else if (   !RTStrICmp(pArg->argv[1], "updateadditions")
     3719             || !RTStrICmp(pArg->argv[1], "updateadds"))
     3720    {
     3721        uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS
     3722                     | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER;
     3723        gctlCmd.pfnHandler = handleCtrlUpdateAdditions;
     3724    }
     3725    else if (   !RTStrICmp(pArg->argv[1], "list"))
     3726    {
     3727        uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS
     3728                     | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER;
     3729        gctlCmd.pfnHandler = handleCtrlList;
     3730    }
     3731    else if (   !RTStrICmp(pArg->argv[1], "session"))
     3732    {
     3733        uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS
     3734                     | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER;
     3735        gctlCmd.pfnHandler = handleCtrlSession;
     3736    }
     3737    else if (   !RTStrICmp(pArg->argv[1], "process"))
     3738    {
     3739        uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS
     3740                     | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER;
     3741        gctlCmd.pfnHandler = handleCtrlProcess;
     3742    }
     3743    else if (   !RTStrICmp(pArg->argv[1], "watch"))
     3744    {
     3745        uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS
     3746                     | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER;
     3747        gctlCmd.pfnHandler = handleCtrlWatch;
     3748    }
     3749    else
     3750        return errorSyntax(USAGE_GUESTCONTROL, "Unknown sub command '%s' specified!", pArg->argv[1]);
     3751
     3752    GCTLCMDCTX cmdCtx;
     3753    RT_ZERO(cmdCtx);
     3754
     3755    RTEXITCODE rcExit = ctrlInitVM(pArg, &cmdCtx, uCmdCtxFlags);
     3756    if (rcExit == RTEXITCODE_SUCCESS)
     3757    {
     3758        /* Kick off the actual command handler. */
     3759        rcExit = gctlCmd.pfnHandler(&cmdCtx);
     3760
     3761        ctrlUninitVM(&cmdCtx, cmdCtx.uFlags);
    41913762        return rcExit;
    41923763    }
    4193     return RTEXITCODE_FAILURE;
    4194 }
    4195 
     3764
     3765    return rcExit;
     3766}
    41963767#endif /* !VBOX_ONLY_DOCS */
    41973768
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette