VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp@ 73768

Last change on this file since 73768 was 73505, checked in by vboxsync, 6 years ago

Main,Config.kmk: GCC 8.2.0 fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.2 KB
RevLine 
[42084]1/* $Id: GuestProcessImpl.cpp 73505 2018-08-05 13:58:10Z vboxsync $ */
2/** @file
[44863]3 * VirtualBox Main - Guest process handling.
[42084]4 */
5
6/*
[71125]7 * Copyright (C) 2012-2018 Oracle Corporation
[42084]8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
[42507]18/**
19 * Locking rules:
20 * - When the main dispatcher (callbackDispatcher) is called it takes the
21 * WriteLock while dispatching to the various on* methods.
22 * - All other outer functions (accessible by Main) must not own a lock
23 * while waiting for a callback or for an event.
24 * - Only keep Read/WriteLocks as short as possible and only when necessary.
25 */
[42084]26
[57358]27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
[71173]31#define LOG_GROUP LOG_GROUP_MAIN_GUESTPROCESS
[67914]32#include "LoggingNew.h"
33
[55645]34#ifndef VBOX_WITH_GUEST_CONTROL
35# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
36#endif
[42084]37#include "GuestProcessImpl.h"
[42160]38#include "GuestSessionImpl.h"
[42478]39#include "GuestCtrlImplPrivate.h"
[42160]40#include "ConsoleImpl.h"
[45805]41#include "VirtualBoxErrorInfoImpl.h"
[42084]42
43#include "Global.h"
44#include "AutoCaller.h"
[45805]45#include "VBoxEvents.h"
[58521]46#include "ThreadTask.h"
[42084]47
[42162]48#include <memory> /* For auto_ptr. */
49
[42160]50#include <iprt/asm.h>
[45415]51#include <iprt/cpp/utils.h> /* For unconst(). */
[42160]52#include <iprt/getopt.h>
[45415]53
[45780]54#include <VBox/com/listeners.h>
55
[42160]56#include <VBox/com/array.h>
[42084]57
[42160]58
[58521]59class GuestProcessTask : public ThreadTask
[42160]60{
[42897]61public:
62
[42160]63 GuestProcessTask(GuestProcess *pProcess)
[58552]64 : ThreadTask("GenericGuestProcessTask")
65 , mProcess(pProcess)
66 , mRC(VINF_SUCCESS) { }
[42160]67
[42436]68 virtual ~GuestProcessTask(void) { }
[42160]69
[50709]70 int i_rc(void) const { return mRC; }
71 bool i_isOk(void) const { return RT_SUCCESS(mRC); }
72 const ComObjPtr<GuestProcess> &i_process(void) const { return mProcess; }
[42160]73
[42897]74protected:
75
[42160]76 const ComObjPtr<GuestProcess> mProcess;
77 int mRC;
78};
79
[42897]80class GuestProcessStartTask : public GuestProcessTask
[42160]81{
[42897]82public:
83
[42160]84 GuestProcessStartTask(GuestProcess *pProcess)
[58521]85 : GuestProcessTask(pProcess)
86 {
87 m_strTaskName = "gctlPrcStart";
88 }
89
90 void handler()
91 {
[63186]92 GuestProcess::i_startProcessThreadTask(this);
[58521]93 }
[42160]94};
95
[45780]96/**
97 * Internal listener class to serve events in an
98 * active manner, e.g. without polling delays.
99 */
100class GuestProcessListener
101{
102public:
103
104 GuestProcessListener(void)
105 {
106 }
107
[62157]108 virtual ~GuestProcessListener(void)
109 {
110 }
111
[45780]112 HRESULT init(GuestProcess *pProcess)
113 {
[49610]114 AssertPtrReturn(pProcess, E_POINTER);
[45780]115 mProcess = pProcess;
116 return S_OK;
117 }
118
119 void uninit(void)
120 {
[49610]121 mProcess = NULL;
[45780]122 }
123
124 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
125 {
126 switch (aType)
127 {
128 case VBoxEventType_OnGuestProcessStateChanged:
129 case VBoxEventType_OnGuestProcessInputNotify:
130 case VBoxEventType_OnGuestProcessOutput:
131 {
[49610]132 AssertPtrReturn(mProcess, E_POINTER);
[49349]133 int rc2 = mProcess->signalWaitEvent(aType, aEvent);
[63248]134 RT_NOREF(rc2);
135#ifdef LOG_ENABLED
[49504]136 LogFlowThisFunc(("Signalling events of type=%RU32, pProcess=%p resulted in rc=%Rrc\n",
[49350]137 aType, &mProcess, rc2));
[45780]138#endif
139 break;
140 }
141
142 default:
[49504]143 AssertMsgFailed(("Unhandled event %RU32\n", aType));
[45780]144 break;
145 }
146
147 return S_OK;
148 }
149
150private:
151
[49610]152 GuestProcess *mProcess;
[45780]153};
154typedef ListenerImpl<GuestProcessListener, GuestProcess*> GuestProcessListenerImpl;
155
156VBOX_LISTENER_DECLARE(GuestProcessListenerImpl)
157
[42084]158// constructor / destructor
159/////////////////////////////////////////////////////////////////////////////
160
161DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
162
163HRESULT GuestProcess::FinalConstruct(void)
164{
[42354]165 LogFlowThisFuncEnter();
[45415]166 return BaseFinalConstruct();
[42084]167}
168
169void GuestProcess::FinalRelease(void)
170{
171 LogFlowThisFuncEnter();
172 uninit();
173 BaseFinalRelease();
174 LogFlowThisFuncLeave();
175}
176
177// public initializer/uninitializer for internal purposes only
178/////////////////////////////////////////////////////////////////////////////
179
[71406]180int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aObjectID,
[55591]181 const GuestProcessStartupInfo &aProcInfo, const GuestEnvironment *pBaseEnv)
[42084]182{
[71406]183 LogFlowThisFunc(("aConsole=%p, aSession=%p, aObjectID=%RU32, pBaseEnv=%p\n",
184 aConsole, aSession, aObjectID, pBaseEnv));
[42436]185
[44863]186 AssertPtrReturn(aConsole, VERR_INVALID_POINTER);
[42160]187 AssertPtrReturn(aSession, VERR_INVALID_POINTER);
188
[42084]189 /* Enclose the state transition NotReady->InInit->Ready. */
190 AutoInitSpan autoInitSpan(this);
[42105]191 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
[42084]192
[45780]193 HRESULT hr;
194
[71406]195 int vrc = bindToSession(aConsole, aSession, aObjectID);
[44863]196 if (RT_SUCCESS(vrc))
197 {
[47469]198 hr = unconst(mEventSource).createObject();
[45415]199 if (FAILED(hr))
[47469]200 vrc = VERR_NO_MEMORY;
201 else
202 {
[50544]203 hr = mEventSource->init();
[47469]204 if (FAILED(hr))
205 vrc = VERR_COM_UNEXPECTED;
206 }
[45415]207 }
208
209 if (RT_SUCCESS(vrc))
210 {
[45780]211 try
212 {
213 GuestProcessListener *pListener = new GuestProcessListener();
214 ComObjPtr<GuestProcessListenerImpl> thisListener;
215 hr = thisListener.createObject();
216 if (SUCCEEDED(hr))
217 hr = thisListener->init(pListener, this);
218
219 if (SUCCEEDED(hr))
220 {
221 com::SafeArray <VBoxEventType_T> eventTypes;
222 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
223 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
224 eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
[47469]225 hr = mEventSource->RegisterListener(thisListener,
[45780]226 ComSafeArrayAsInParam(eventTypes),
227 TRUE /* Active listener */);
228 if (SUCCEEDED(hr))
229 {
[47469]230 vrc = baseInit();
231 if (RT_SUCCESS(vrc))
232 {
233 mLocalListener = thisListener;
234 }
[45780]235 }
236 else
237 vrc = VERR_COM_UNEXPECTED;
238 }
239 else
240 vrc = VERR_COM_UNEXPECTED;
241 }
242 catch(std::bad_alloc &)
243 {
244 vrc = VERR_NO_MEMORY;
245 }
246 }
247
248 if (RT_SUCCESS(vrc))
249 {
[44863]250 mData.mProcess = aProcInfo;
[55591]251 mData.mpSessionBaseEnv = pBaseEnv;
252 if (pBaseEnv)
253 pBaseEnv->retainConst();
[45426]254 mData.mExitCode = 0;
255 mData.mPID = 0;
[47817]256 mData.mLastError = VINF_SUCCESS;
[45426]257 mData.mStatus = ProcessStatus_Undefined;
[44863]258 /* Everything else will be set by the actual starting routine. */
[42105]259
[44863]260 /* Confirm a successful initialization when it's the case. */
261 autoInitSpan.setSucceeded();
[42234]262
[44863]263 return vrc;
264 }
265
266 autoInitSpan.setFailed();
267 return vrc;
[42084]268}
269
270/**
271 * Uninitializes the instance.
[49504]272 * Called from FinalRelease() or IGuestSession::uninit().
[42084]273 */
274void GuestProcess::uninit(void)
275{
276 /* Enclose the state transition Ready->InUninit->NotReady. */
277 AutoUninitSpan autoUninitSpan(this);
278 if (autoUninitSpan.uninitDone())
279 return;
[42234]280
[55535]281 LogFlowThisFunc(("mExe=%s, PID=%RU32\n", mData.mProcess.mExecutable.c_str(), mData.mPID));
[42897]282
[47469]283 /* Terminate process if not already done yet. */
[71299]284 int rcGuest = VINF_SUCCESS;
285 int vrc = i_terminateProcess(30 * 1000, &rcGuest); /** @todo Make timeouts configurable. */
[47469]286 /* Note: Don't return here yet; first uninit all other stuff in
287 * case of failure. */
288
[55591]289 if (mData.mpSessionBaseEnv)
290 {
291 mData.mpSessionBaseEnv->releaseConst();
292 mData.mpSessionBaseEnv = NULL;
293 }
294
[47469]295 baseUninit();
296
[71406]297 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
[63248]298 RT_NOREF_PV(vrc);
[42084]299}
300
301// implementation of public getters/setters for attributes
302/////////////////////////////////////////////////////////////////////////////
[50709]303HRESULT GuestProcess::getArguments(std::vector<com::Utf8Str> &aArguments)
[42084]304{
[42214]305 LogFlowThisFuncEnter();
306
[42160]307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[52934]308 aArguments = mData.mProcess.mArguments;
[42160]309 return S_OK;
[42084]310}
311
[50709]312HRESULT GuestProcess::getEnvironment(std::vector<com::Utf8Str> &aEnvironment)
[42084]313{
[63245]314#ifndef VBOX_WITH_GUEST_CONTROL
[42084]315 ReturnComNotImplemented();
316#else
[55591]317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /* (Paranoia since both environment objects are immutable.) */
318 HRESULT hrc;
319 if (mData.mpSessionBaseEnv)
320 {
321 int vrc;
322 if (mData.mProcess.mEnvironmentChanges.count() == 0)
323 vrc = mData.mpSessionBaseEnv->queryPutEnvArray(&aEnvironment);
324 else
325 {
326 GuestEnvironment TmpEnv;
327 vrc = TmpEnv.copy(*mData.mpSessionBaseEnv);
328 if (RT_SUCCESS(vrc))
329 {
330 vrc = TmpEnv.applyChanges(mData.mProcess.mEnvironmentChanges);
[63246]331 if (RT_SUCCESS(vrc))
[55591]332 vrc = TmpEnv.queryPutEnvArray(&aEnvironment);
333 }
334 }
335 hrc = Global::vboxStatusCodeToCOM(vrc);
336 }
337 else
[71406]338 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by installed Guest Additions"));
[55591]339 LogFlowThisFuncLeave();
340 return hrc;
341#endif
[42084]342}
343
[50709]344HRESULT GuestProcess::getEventSource(ComPtr<IEventSource> &aEventSource)
[45426]345{
346 LogFlowThisFuncEnter();
347
348 // no need to lock - lifetime constant
[50709]349 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
[45426]350
[47627]351 LogFlowThisFuncLeave();
[45426]352 return S_OK;
353}
354
[50709]355HRESULT GuestProcess::getExecutablePath(com::Utf8Str &aExecutablePath)
[42084]356{
[42214]357 LogFlowThisFuncEnter();
358
[42160]359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
360
[55535]361 aExecutablePath = mData.mProcess.mExecutable;
[42160]362
363 return S_OK;
[42084]364}
365
[50709]366HRESULT GuestProcess::getExitCode(LONG *aExitCode)
[42084]367{
[42214]368 LogFlowThisFuncEnter();
369
[42160]370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
371
372 *aExitCode = mData.mExitCode;
373
374 return S_OK;
[42084]375}
376
[50709]377HRESULT GuestProcess::getName(com::Utf8Str &aName)
[42411]378{
379 LogFlowThisFuncEnter();
380
381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
382
[50709]383 aName = mData.mProcess.mName;
[42411]384
385 return S_OK;
386}
387
[50709]388HRESULT GuestProcess::getPID(ULONG *aPID)
[42084]389{
[42214]390 LogFlowThisFuncEnter();
391
[42160]392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
393
394 *aPID = mData.mPID;
395
396 return S_OK;
[42084]397}
398
[50709]399HRESULT GuestProcess::getStatus(ProcessStatus_T *aStatus)
[42084]400{
[42214]401 LogFlowThisFuncEnter();
402
[42160]403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
404
405 *aStatus = mData.mStatus;
406
407 return S_OK;
[42084]408}
409
[42160]410// private methods
411/////////////////////////////////////////////////////////////////////////////
412
[50618]413int GuestProcess::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
[42160]414{
[44863]415 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
416 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
[42439]417#ifdef DEBUG
[44863]418 LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
419 mData.mPID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
[42439]420#endif
[42272]421
[44863]422 int vrc;
423 switch (pCbCtx->uFunction)
[42461]424 {
425 case GUEST_DISCONNECTED:
[42214]426 {
[50709]427 vrc = i_onGuestDisconnected(pCbCtx, pSvcCb);
[42461]428 break;
429 }
[42214]430
[44863]431 case GUEST_EXEC_STATUS:
[42461]432 {
[50709]433 vrc = i_onProcessStatusChange(pCbCtx, pSvcCb);
[42461]434 break;
435 }
[42214]436
[44863]437 case GUEST_EXEC_OUTPUT:
[42461]438 {
[50709]439 vrc = i_onProcessOutput(pCbCtx, pSvcCb);
[42461]440 break;
441 }
[42214]442
[44863]443 case GUEST_EXEC_INPUT_STATUS:
[42461]444 {
[50709]445 vrc = i_onProcessInputStatus(pCbCtx, pSvcCb);
[42461]446 break;
447 }
[42214]448
[42461]449 default:
450 /* Silently ignore not implemented functions. */
[44863]451 vrc = VERR_NOT_SUPPORTED;
[42461]452 break;
[42214]453 }
454
[42439]455#ifdef DEBUG
[49349]456 LogFlowFuncLeaveRC(vrc);
[42439]457#endif
[42721]458 return vrc;
[42214]459}
460
[42673]461/**
462 * Checks if the current assigned PID matches another PID (from a callback).
463 *
464 * In protocol v1 we don't have the possibility to terminate/kill
[44935]465 * processes so it can happen that a formerly started process A
[42673]466 * (which has the context ID 0 (session=0, process=0, count=0) will
467 * send a delayed message to the host if this process has already
468 * been discarded there and the same context ID was reused by
469 * a process B. Process B in turn then has a different guest PID.
470 *
[49570]471 * Note: This also can happen when restoring from a saved state which
472 * had a guest process running.
473 *
[42673]474 * @return IPRT status code.
475 * @param uPID PID to check.
476 */
[50709]477inline int GuestProcess::i_checkPID(uint32_t uPID)
[42411]478{
[49570]479 int rc = VINF_SUCCESS;
480
[42673]481 /* Was there a PID assigned yet? */
482 if (mData.mPID)
483 {
[49570]484 if (RT_UNLIKELY(mData.mPID != uPID))
[42673]485 {
[50899]486 LogFlowFunc(("Stale guest process (PID=%RU32) sent data to a newly started process (pProcesS=%p, PID=%RU32, status=%RU32)\n",
487 uPID, this, mData.mPID, mData.mStatus));
[49570]488 rc = VERR_NOT_FOUND;
[42673]489 }
490 }
491
[49570]492 return rc;
[42411]493}
494
[43162]495/* static */
[71299]496Utf8Str GuestProcess::i_guestErrorToString(int rcGuest)
[43162]497{
498 Utf8Str strError;
499
500 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
[71299]501 switch (rcGuest)
[43162]502 {
503 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
[71177]504 RT_FALL_THROUGH();
505 case VERR_PATH_NOT_FOUND:
506 strError += Utf8StrFmt(tr("No such file or directory on guest"));
[43162]507 break;
508
509 case VERR_INVALID_VM_HANDLE:
510 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
511 break;
512
513 case VERR_HGCM_SERVICE_NOT_FOUND:
514 strError += Utf8StrFmt(tr("The guest execution service is not available"));
515 break;
516
517 case VERR_BAD_EXE_FORMAT:
518 strError += Utf8StrFmt(tr("The specified file is not an executable format on guest"));
519 break;
520
521 case VERR_AUTHENTICATION_FAILURE:
522 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
523 break;
524
525 case VERR_INVALID_NAME:
526 strError += Utf8StrFmt(tr("The specified file is an invalid name"));
527 break;
528
529 case VERR_TIMEOUT:
530 strError += Utf8StrFmt(tr("The guest did not respond within time"));
531 break;
532
533 case VERR_CANCELLED:
534 strError += Utf8StrFmt(tr("The execution operation was canceled"));
535 break;
536
[57948]537 case VERR_PERMISSION_DENIED: /** @todo r=bird: This is probably completely and utterly misleading. VERR_AUTHENTICATION_FAILURE could have this message. */
[43162]538 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
539 break;
540
[71349]541 case VERR_GSTCTL_MAX_OBJECTS_REACHED:
[45415]542 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
[43162]543 break;
544
545 case VERR_NOT_FOUND:
546 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
547 break;
548
549 default:
[71299]550 strError += Utf8StrFmt("%Rrc", rcGuest);
[43162]551 break;
552 }
553
554 return strError;
555}
556
[61792]557/**
[65088]558 * Returns @c true if the passed in error code indicates an error which came
559 * from the guest side, or @c false if not.
[61792]560 *
[65088]561 * @return bool @c true if the passed in error code indicates an error which came
562 * from the guest side, or @c false if not.
[61792]563 * @param rc Error code to check.
564 */
565/* static */
566bool GuestProcess::i_isGuestError(int rc)
567{
568 return ( rc == VERR_GSTCTL_GUEST_ERROR
[73036]569 || rc == VERR_GSTCTL_PROCESS_EXIT_CODE);
[61792]570}
571
[50709]572inline bool GuestProcess::i_isAlive(void)
[42673]573{
574 return ( mData.mStatus == ProcessStatus_Started
575 || mData.mStatus == ProcessStatus_Paused
576 || mData.mStatus == ProcessStatus_Terminating);
577}
578
[50709]579inline bool GuestProcess::i_hasEnded(void)
[42160]580{
[47627]581 return ( mData.mStatus == ProcessStatus_TerminatedNormally
582 || mData.mStatus == ProcessStatus_TerminatedSignal
583 || mData.mStatus == ProcessStatus_TerminatedAbnormally
584 || mData.mStatus == ProcessStatus_TimedOutKilled
585 || mData.mStatus == ProcessStatus_TimedOutAbnormally
586 || mData.mStatus == ProcessStatus_Down
587 || mData.mStatus == ProcessStatus_Error);
[42160]588}
589
[50709]590int GuestProcess::i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
[42160]591{
[45415]592 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
593 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
[42354]594
[50709]595 int vrc = i_setProcessStatus(ProcessStatus_Down, VINF_SUCCESS);
[42354]596
[49349]597 LogFlowFuncLeaveRC(vrc);
[42721]598 return vrc;
[42160]599}
600
[50709]601int GuestProcess::i_onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
[42214]602{
[45415]603 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
[44863]604 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
[45415]605 /* pCallback is optional. */
[42354]606
[44863]607 if (pSvcCbData->mParms < 5)
608 return VERR_INVALID_PARAMETER;
[42214]609
[44863]610 CALLBACKDATA_PROC_INPUT dataCb;
611 /* pSvcCb->mpaParms[0] always contains the context ID. */
[44945]612 int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uPID);
613 AssertRCReturn(vrc, vrc);
614 vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uStatus);
615 AssertRCReturn(vrc, vrc);
616 vrc = pSvcCbData->mpaParms[3].getUInt32(&dataCb.uFlags);
617 AssertRCReturn(vrc, vrc);
618 vrc = pSvcCbData->mpaParms[4].getUInt32(&dataCb.uProcessed);
619 AssertRCReturn(vrc, vrc);
[44863]620
[45415]621 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32\n",
622 dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed));
[44863]623
[50709]624 vrc = i_checkPID(dataCb.uPID);
[47469]625 if (RT_SUCCESS(vrc))
[42634]626 {
[47469]627 ProcessInputStatus_T inputStatus = ProcessInputStatus_Undefined;
628 switch (dataCb.uStatus)
629 {
630 case INPUT_STS_WRITTEN:
631 inputStatus = ProcessInputStatus_Written;
632 break;
633 case INPUT_STS_ERROR:
634 inputStatus = ProcessInputStatus_Broken;
635 break;
636 case INPUT_STS_TERMINATED:
637 inputStatus = ProcessInputStatus_Broken;
638 break;
639 case INPUT_STS_OVERFLOW:
640 inputStatus = ProcessInputStatus_Overflow;
641 break;
642 case INPUT_STS_UNDEFINED:
643 /* Fall through is intentional. */
644 default:
645 AssertMsg(!dataCb.uProcessed, ("Processed data is not 0 in undefined input state\n"));
646 break;
647 }
[42634]648
[47469]649 if (inputStatus != ProcessInputStatus_Undefined)
650 {
[47627]651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
652
653 /* Copy over necessary data before releasing lock again. */
654 uint32_t uPID = mData.mPID;
655 /** @todo Also handle mSession? */
656
657 alock.release(); /* Release lock before firing off event. */
658
[47469]659 fireGuestProcessInputNotifyEvent(mEventSource, mSession, this,
[47627]660 uPID, 0 /* StdIn */, dataCb.uProcessed, inputStatus);
[47469]661 }
[42634]662 }
[42234]663
[49349]664 LogFlowFuncLeaveRC(vrc);
[42721]665 return vrc;
[42214]666}
667
[50709]668int GuestProcess::i_onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
[42461]669{
[45415]670 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
671 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
[42461]672
[44863]673 return VERR_NOT_IMPLEMENTED;
[42461]674}
675
[50709]676int GuestProcess::i_onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
[42214]677{
[47469]678 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
[44863]679 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
[42354]680
[44863]681 if (pSvcCbData->mParms < 5)
682 return VERR_INVALID_PARAMETER;
[42214]683
[44863]684 CALLBACKDATA_PROC_STATUS dataCb;
685 /* pSvcCb->mpaParms[0] always contains the context ID. */
[44945]686 int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uPID);
687 AssertRCReturn(vrc, vrc);
688 vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uStatus);
689 AssertRCReturn(vrc, vrc);
690 vrc = pSvcCbData->mpaParms[3].getUInt32(&dataCb.uFlags);
691 AssertRCReturn(vrc, vrc);
692 vrc = pSvcCbData->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
693 AssertRCReturn(vrc, vrc);
[44863]694
[45415]695 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32\n",
696 dataCb.uPID, dataCb.uStatus, dataCb.uFlags));
[44863]697
[50709]698 vrc = i_checkPID(dataCb.uPID);
[47469]699 if (RT_SUCCESS(vrc))
[42234]700 {
[47469]701 ProcessStatus_T procStatus = ProcessStatus_Undefined;
702 int procRc = VINF_SUCCESS;
[42234]703
[47469]704 switch (dataCb.uStatus)
[42234]705 {
[47469]706 case PROC_STS_STARTED:
707 {
708 procStatus = ProcessStatus_Started;
[47627]709
710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[47469]711 mData.mPID = dataCb.uPID; /* Set the process PID. */
712 break;
713 }
[42234]714
[47469]715 case PROC_STS_TEN:
716 {
717 procStatus = ProcessStatus_TerminatedNormally;
[47627]718
719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[47469]720 mData.mExitCode = dataCb.uFlags; /* Contains the exit code. */
721 break;
722 }
[42234]723
[47469]724 case PROC_STS_TES:
725 {
726 procStatus = ProcessStatus_TerminatedSignal;
[47627]727
728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[47469]729 mData.mExitCode = dataCb.uFlags; /* Contains the signal. */
730 break;
731 }
[42234]732
[47469]733 case PROC_STS_TEA:
734 {
735 procStatus = ProcessStatus_TerminatedAbnormally;
736 break;
737 }
[42234]738
[47469]739 case PROC_STS_TOK:
740 {
741 procStatus = ProcessStatus_TimedOutKilled;
742 break;
743 }
[42234]744
[47469]745 case PROC_STS_TOA:
746 {
747 procStatus = ProcessStatus_TimedOutAbnormally;
748 break;
749 }
[42234]750
[47469]751 case PROC_STS_DWN:
752 {
753 procStatus = ProcessStatus_Down;
754 break;
755 }
[42234]756
[47469]757 case PROC_STS_ERROR:
758 {
759 procRc = dataCb.uFlags; /* mFlags contains the IPRT error sent from the guest. */
760 procStatus = ProcessStatus_Error;
761 break;
762 }
763
764 case PROC_STS_UNDEFINED:
765 default:
766 {
767 /* Silently skip this request. */
768 procStatus = ProcessStatus_Undefined;
769 break;
770 }
[42354]771 }
[42234]772
[49504]773 LogFlowThisFunc(("Got rc=%Rrc, procSts=%RU32, procRc=%Rrc\n",
[47469]774 vrc, procStatus, procRc));
[42354]775
[47469]776 /* Set the process status. */
[50709]777 int rc2 = i_setProcessStatus(procStatus, procRc);
[47469]778 if (RT_SUCCESS(vrc))
779 vrc = rc2;
780 }
[43162]781
[49349]782 LogFlowFuncLeaveRC(vrc);
[42721]783 return vrc;
[42214]784}
785
[50709]786int GuestProcess::i_onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
[42214]787{
[63248]788 RT_NOREF(pCbCtx);
[44863]789 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
[42354]790
[44863]791 if (pSvcCbData->mParms < 5)
792 return VERR_INVALID_PARAMETER;
[42214]793
[44863]794 CALLBACKDATA_PROC_OUTPUT dataCb;
795 /* pSvcCb->mpaParms[0] always contains the context ID. */
[44945]796 int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uPID);
797 AssertRCReturn(vrc, vrc);
798 vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uHandle);
799 AssertRCReturn(vrc, vrc);
800 vrc = pSvcCbData->mpaParms[3].getUInt32(&dataCb.uFlags);
801 AssertRCReturn(vrc, vrc);
802 vrc = pSvcCbData->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
803 AssertRCReturn(vrc, vrc);
[44863]804
[45415]805 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32\n",
806 dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData));
[44863]807
[50709]808 vrc = i_checkPID(dataCb.uPID);
[47469]809 if (RT_SUCCESS(vrc))
810 {
811 com::SafeArray<BYTE> data((size_t)dataCb.cbData);
812 if (dataCb.cbData)
813 data.initFrom((BYTE*)dataCb.pvData, dataCb.cbData);
[42234]814
[47469]815 fireGuestProcessOutputEvent(mEventSource, mSession, this,
816 mData.mPID, dataCb.uHandle, dataCb.cbData, ComSafeArrayAsInParam(data));
817 }
[42234]818
[49349]819 LogFlowFuncLeaveRC(vrc);
[42721]820 return vrc;
[42214]821}
822
[49504]823/**
[49570]824 * Called by IGuestSession right before this process gets
825 * removed from the public process list.
[49504]826 */
[50618]827int GuestProcess::i_onRemove(void)
[49504]828{
829 LogFlowThisFuncEnter();
830
831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
832
833 int vrc = VINF_SUCCESS;
834
[49570]835 /*
[49504]836 * Note: The event source stuff holds references to this object,
837 * so make sure that this is cleaned up *before* calling uninit().
838 */
839 if (!mEventSource.isNull())
840 {
841 mEventSource->UnregisterListener(mLocalListener);
842
843 mLocalListener.setNull();
844 unconst(mEventSource).setNull();
845 }
846
847 LogFlowFuncLeaveRC(vrc);
848 return vrc;
849}
850
[50709]851int GuestProcess::i_readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
[71299]852 void *pvData, size_t cbData, uint32_t *pcbRead, int *prcGuest)
[42214]853{
[71299]854 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32, prcGuest=%p\n",
855 mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData, prcGuest));
[42214]856 AssertReturn(uSize, VERR_INVALID_PARAMETER);
[42507]857 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
[42214]858 AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
[42461]859 /* pcbRead is optional. */
[42214]860
[42461]861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
862
[45697]863 if ( mData.mStatus != ProcessStatus_Started
864 /* Skip reading if the process wasn't started with the appropriate
865 * flags. */
866 || ( ( uHandle == OUTPUT_HANDLE_ID_STDOUT
867 || uHandle == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED)
868 && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdOut))
869 || ( uHandle == OUTPUT_HANDLE_ID_STDERR
870 && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdErr))
871 )
[42787]872 {
873 if (pcbRead)
874 *pcbRead = 0;
[71299]875 if (prcGuest)
876 *prcGuest = VINF_SUCCESS;
[42716]877 return VINF_SUCCESS; /* Nothing to read anymore. */
[42787]878 }
[42461]879
[45780]880 int vrc;
881
882 GuestWaitEvent *pEvent = NULL;
[49349]883 GuestEventTypes eventTypes;
[45780]884 try
885 {
[47469]886 /*
887 * On Guest Additions < 4.3 there is no guarantee that the process status
888 * change arrives *after* the output event, e.g. if this was the last output
889 * block being read and the process will report status "terminate".
890 * So just skip checking for process status change and only wait for the
891 * output event.
892 */
[50727]893 if (mSession->i_getProtocolVersion() >= 2)
[47469]894 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
[45780]895 eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
896
[47469]897 vrc = registerWaitEvent(eventTypes, &pEvent);
[45780]898 }
[73505]899 catch (std::bad_alloc &)
[45780]900 {
901 vrc = VERR_NO_MEMORY;
902 }
903
904 if (RT_FAILURE(vrc))
905 return vrc;
906
[42721]907 if (RT_SUCCESS(vrc))
[43162]908 {
[45415]909 VBOXHGCMSVCPARM paParms[8];
[42461]910 int i = 0;
[45780]911 paParms[i++].setUInt32(pEvent->ContextID());
[42461]912 paParms[i++].setUInt32(mData.mPID);
913 paParms[i++].setUInt32(uHandle);
914 paParms[i++].setUInt32(0 /* Flags, none set yet. */);
915
[45697]916 alock.release(); /* Drop the write lock before sending. */
917
[42721]918 vrc = sendCommand(HOST_EXEC_GET_OUTPUT, i, paParms);
[42461]919 }
920
[42721]921 if (RT_SUCCESS(vrc))
[50709]922 vrc = i_waitForOutput(pEvent, uHandle, uTimeoutMS,
923 pvData, cbData, pcbRead);
[42461]924
[47469]925 unregisterWaitEvent(pEvent);
[45780]926
[49349]927 LogFlowFuncLeaveRC(vrc);
[42721]928 return vrc;
[42214]929}
930
[42507]931/* Does not do locking; caller is responsible for that! */
[50709]932int GuestProcess::i_setProcessStatus(ProcessStatus_T procStatus, int procRc)
[42160]933{
[47627]934 LogFlowThisFuncEnter();
935
936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
937
[49504]938 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, procRc=%Rrc\n",
[43162]939 mData.mStatus, procStatus, procRc));
[42272]940
[43162]941 if (procStatus == ProcessStatus_Error)
942 {
943 AssertMsg(RT_FAILURE(procRc), ("Guest rc must be an error (%Rrc)\n", procRc));
944 /* Do not allow overwriting an already set error. If this happens
945 * this means we forgot some error checking/locking somewhere. */
[47817]946 AssertMsg(RT_SUCCESS(mData.mLastError), ("Guest rc already set (to %Rrc)\n", mData.mLastError));
[43162]947 }
948 else
949 AssertMsg(RT_SUCCESS(procRc), ("Guest rc must not be an error (%Rrc)\n", procRc));
[42436]950
[47627]951 int rc = VINF_SUCCESS;
952
[47469]953 if (mData.mStatus != procStatus) /* Was there a process status change? */
[45415]954 {
[47817]955 mData.mStatus = procStatus;
956 mData.mLastError = procRc;
[42436]957
[45805]958 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
[45415]959 HRESULT hr = errorInfo.createObject();
960 ComAssertComRC(hr);
[47817]961 if (RT_FAILURE(mData.mLastError))
[45415]962 {
[47817]963 hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, mData.mLastError,
964 COM_IIDOF(IGuestProcess), getComponentName(),
[50709]965 i_guestErrorToString(mData.mLastError));
[47817]966 ComAssertComRC(hr);
[45415]967 }
968
[47627]969 /* Copy over necessary data before releasing lock again. */
970 uint32_t uPID = mData.mPID;
971 /** @todo Also handle mSession? */
972
973 alock.release(); /* Release lock before firing off event. */
974
[45415]975 fireGuestProcessStateChangedEvent(mEventSource, mSession, this,
[47817]976 uPID, procStatus, errorInfo);
[47627]977#if 0
978 /*
979 * On Guest Additions < 4.3 there is no guarantee that outstanding
980 * requests will be delivered to the host after the process has ended,
981 * so just cancel all waiting events here to not let clients run
982 * into timeouts.
983 */
984 if ( mSession->getProtocolVersion() < 2
985 && hasEnded())
986 {
987 LogFlowThisFunc(("Process ended, canceling outstanding wait events ...\n"));
988 rc = cancelWaitEvents();
989 }
990#endif
[45415]991 }
992
[47627]993 return rc;
[42272]994}
995
[43162]996/* static */
[71299]997HRESULT GuestProcess::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
[42436]998{
[43162]999 AssertPtr(pInterface);
[71299]1000 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
[43162]1001
[73003]1002 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestProcess::i_guestErrorToString(rcGuest).c_str());
[42436]1003}
1004
[71299]1005int GuestProcess::i_startProcess(uint32_t cMsTimeout, int *prcGuest)
[42272]1006{
[63153]1007 LogFlowThisFunc(("cMsTimeout=%RU32, procExe=%s, procTimeoutMS=%RU32, procFlags=%x, sessionID=%RU32\n",
1008 cMsTimeout, mData.mProcess.mExecutable.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags,
[50727]1009 mSession->i_getId()));
[42160]1010
[42411]1011 /* Wait until the caller function (if kicked off by a thread)
1012 * has returned and continue operation. */
1013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[42160]1014
[45415]1015 mData.mStatus = ProcessStatus_Starting;
[42160]1016
[45780]1017 int vrc;
1018
1019 GuestWaitEvent *pEvent = NULL;
[49349]1020 GuestEventTypes eventTypes;
[45780]1021 try
[43162]1022 {
[45780]1023 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
[47469]1024 vrc = registerWaitEvent(eventTypes, &pEvent);
[45780]1025 }
[73505]1026 catch (std::bad_alloc &)
[45780]1027 {
1028 vrc = VERR_NO_MEMORY;
1029 }
1030 if (RT_FAILURE(vrc))
1031 return vrc;
[43062]1032
[71299]1033 vrc = i_startProcessInner(cMsTimeout, alock, pEvent, prcGuest);
[63153]1034
1035 unregisterWaitEvent(pEvent);
1036
1037 LogFlowFuncLeaveRC(vrc);
1038 return vrc;
1039}
1040
[71299]1041int GuestProcess::i_startProcessInner(uint32_t cMsTimeout, AutoWriteLock &rLock, GuestWaitEvent *pEvent, int *prcGuest)
[63153]1042{
[45780]1043 GuestSession *pSession = mSession;
1044 AssertPtr(pSession);
[55535]1045 uint32_t const uProtocol = pSession->i_getProtocolVersion();
[43062]1046
[50727]1047 const GuestCredentials &sessionCreds = pSession->i_getCredentials();
[42160]1048
[55535]1049
[45780]1050 /* Prepare arguments. */
1051 size_t cArgs = mData.mProcess.mArguments.size();
[63153]1052 if (cArgs >= 128*1024)
1053 return VERR_BUFFER_OVERFLOW;
[43062]1054
[63153]1055 char *pszArgs = NULL;
1056 int vrc = VINF_SUCCESS;
1057 if (cArgs)
[45780]1058 {
[55535]1059 char const **papszArgv = (char const **)RTMemAlloc((cArgs + 1) * sizeof(papszArgv[0]));
[45780]1060 AssertReturn(papszArgv, VERR_NO_MEMORY);
1061
[55535]1062 for (size_t i = 0; i < cArgs; i++)
[45780]1063 {
[55535]1064 papszArgv[i] = mData.mProcess.mArguments[i].c_str();
1065 AssertPtr(papszArgv[i]);
[42160]1066 }
[45780]1067 papszArgv[cArgs] = NULL;
[42160]1068
[55535]1069 if (uProtocol < UINT32_C(0xdeadbeef) ) /** @todo implement a way of sending argv[0], best idea is a new command. */
1070 vrc = RTGetOptArgvToString(&pszArgs, papszArgv + 1, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1071 else
[55491]1072 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
[43062]1073
[55535]1074 RTMemFree(papszArgv);
[63153]1075 if (RT_FAILURE(vrc))
1076 return vrc;
1077
1078 /* Note! No returns after this. */
[45780]1079 }
[42160]1080
[45780]1081 /* Calculate arguments size (in bytes). */
[63153]1082 size_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
[45697]1083
[55599]1084 /* Prepare environment. The guest service dislikes the empty string at the end, so drop it. */
[55588]1085 size_t cbEnvBlock;
1086 char *pszzEnvBlock;
[63153]1087 vrc = mData.mProcess.mEnvironmentChanges.queryUtf8Block(&pszzEnvBlock, &cbEnvBlock);
[45780]1088 if (RT_SUCCESS(vrc))
1089 {
[55599]1090 Assert(cbEnvBlock > 0);
1091 cbEnvBlock--;
1092
[45780]1093 /* Prepare HGCM call. */
1094 VBOXHGCMSVCPARM paParms[16];
1095 int i = 0;
1096 paParms[i++].setUInt32(pEvent->ContextID());
[55588]1097 paParms[i++].setCppString(mData.mProcess.mExecutable);
[45780]1098 paParms[i++].setUInt32(mData.mProcess.mFlags);
[45927]1099 paParms[i++].setUInt32((uint32_t)mData.mProcess.mArguments.size());
[55588]1100 paParms[i++].setPointer(pszArgs, (uint32_t)cbArgs);
[55591]1101 paParms[i++].setUInt32(mData.mProcess.mEnvironmentChanges.count());
[55588]1102 paParms[i++].setUInt32((uint32_t)cbEnvBlock);
1103 paParms[i++].setPointer(pszzEnvBlock, (uint32_t)cbEnvBlock);
[45780]1104 if (uProtocol < 2)
1105 {
1106 /* In protocol v1 (VBox < 4.3) the credentials were part of the execution
1107 * call. In newer protocols these credentials are part of the opened guest
1108 * session, so not needed anymore here. */
[55588]1109 paParms[i++].setCppString(sessionCreds.mUser);
1110 paParms[i++].setCppString(sessionCreds.mPassword);
[42160]1111 }
[45780]1112 /*
1113 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1114 * until the process was started - the process itself then gets an infinite timeout for execution.
1115 * This is handy when we want to start a process inside a worker thread within a certain timeout
1116 * but let the started process perform lengthly operations then.
1117 */
1118 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1119 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1120 else
[47469]1121 paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
[45780]1122 if (uProtocol >= 2)
1123 {
1124 paParms[i++].setUInt32(mData.mProcess.mPriority);
1125 /* CPU affinity: We only support one CPU affinity block at the moment,
1126 * so that makes up to 64 CPUs total. This can be more in the future. */
1127 paParms[i++].setUInt32(1);
1128 /* The actual CPU affinity blocks. */
[55588]1129 paParms[i++].setPointer((void *)&mData.mProcess.mAffinity, sizeof(mData.mProcess.mAffinity));
[45780]1130 }
[42160]1131
[63153]1132 rLock.release(); /* Drop the write lock before sending. */
[42214]1133
[45780]1134 vrc = sendCommand(HOST_EXEC_CMD, i, paParms);
1135 if (RT_FAILURE(vrc))
[42214]1136 {
[50709]1137 int rc2 = i_setProcessStatus(ProcessStatus_Error, vrc);
[45780]1138 AssertRC(rc2);
[42214]1139 }
[55588]1140
[55591]1141 mData.mProcess.mEnvironmentChanges.freeUtf8Block(pszzEnvBlock);
[42160]1142 }
1143
[63153]1144 RTStrFree(pszArgs);
[45780]1145
1146 if (RT_SUCCESS(vrc))
[63153]1147 vrc = i_waitForStatusChange(pEvent, cMsTimeout,
[71299]1148 NULL /* Process status */, prcGuest);
[42721]1149 return vrc;
[42354]1150}
[42272]1151
[50709]1152int GuestProcess::i_startProcessAsync(void)
[42354]1153{
1154 LogFlowThisFuncEnter();
1155
[58552]1156 int vrc = VINF_SUCCESS;
[58521]1157 HRESULT hr = S_OK;
[42354]1158
[58521]1159 GuestProcessStartTask* pTask = NULL;
[42897]1160 try
[42354]1161 {
[58521]1162 pTask = new GuestProcessStartTask(this);
1163 if (!pTask->i_isOk())
[42897]1164 {
[58521]1165 delete pTask;
[71272]1166 LogFlowThisFunc(("Could not create GuestProcessStartTask object\n"));
[58552]1167 throw VERR_MEMOBJ_INIT_FAILED;
[42897]1168 }
[71272]1169 LogFlowThisFunc(("Successfully created GuestProcessStartTask object\n"));
[58521]1170 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
1171 hr = pTask->createThread();
[42354]1172 }
[42897]1173 catch(std::bad_alloc &)
1174 {
1175 vrc = VERR_NO_MEMORY;
1176 }
[58552]1177 catch(int eVRC)
[58521]1178 {
[58552]1179 vrc = eVRC;
[71272]1180 LogFlowThisFunc(("Could not create thread for GuestProcessStartTask task %Rrc\n", vrc));
[58521]1181 }
[42354]1182
[49349]1183 LogFlowFuncLeaveRC(vrc);
[42721]1184 return vrc;
[42160]1185}
1186
[42897]1187/* static */
[71272]1188int GuestProcess::i_startProcessThreadTask(GuestProcessStartTask *pTask)
[42160]1189{
[63186]1190 LogFlowFunc(("pTask=%p\n", pTask));
[42160]1191
[50709]1192 const ComObjPtr<GuestProcess> pProcess(pTask->i_process());
[42160]1193 Assert(!pProcess.isNull());
1194
[42354]1195 AutoCaller autoCaller(pProcess);
[63186]1196 if (FAILED(autoCaller.rc()))
[71272]1197 return VERR_COM_UNEXPECTED;
[42160]1198
[71272]1199 int vrc = pProcess->i_startProcess(30 * 1000 /* 30s timeout */, NULL /* Guest rc, ignored */);
[43162]1200 /* Nothing to do here anymore. */
[42411]1201
[71272]1202 LogFlowFunc(("pProcess=%p, vrc=%Rrc\n", (GuestProcess *)pProcess, vrc));
1203 return vrc;
[42160]1204}
1205
[71299]1206int GuestProcess::i_terminateProcess(uint32_t uTimeoutMS, int *prcGuest)
[42160]1207{
[71299]1208 /* prcGuest is optional. */
[47469]1209 LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
[42160]1210
[42716]1211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1212
[49504]1213 int vrc = VINF_SUCCESS;
1214
[44863]1215 if (mData.mStatus != ProcessStatus_Started)
[47469]1216 {
[49504]1217 LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
[49440]1218 mData.mStatus));
[47469]1219 }
[49504]1220 else
[45780]1221 {
[49504]1222 AssertPtr(mSession);
1223 /* Note: VBox < 4.3 (aka protocol version 1) does not
1224 * support this, so just skip. */
[50727]1225 if (mSession->i_getProtocolVersion() < 2)
[49504]1226 vrc = VERR_NOT_SUPPORTED;
[44863]1227
[49504]1228 if (RT_SUCCESS(vrc))
[47469]1229 {
[49504]1230 GuestWaitEvent *pEvent = NULL;
1231 GuestEventTypes eventTypes;
1232 try
1233 {
1234 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
[45780]1235
[49504]1236 vrc = registerWaitEvent(eventTypes, &pEvent);
1237 }
[73505]1238 catch (std::bad_alloc &)
[49504]1239 {
1240 vrc = VERR_NO_MEMORY;
1241 }
[45780]1242
[49504]1243 if (RT_FAILURE(vrc))
1244 return vrc;
[45780]1245
[49504]1246 VBOXHGCMSVCPARM paParms[4];
1247 int i = 0;
1248 paParms[i++].setUInt32(pEvent->ContextID());
1249 paParms[i++].setUInt32(mData.mPID);
[45780]1250
[49504]1251 alock.release(); /* Drop the write lock before sending. */
1252
1253 vrc = sendCommand(HOST_EXEC_TERMINATE, i, paParms);
1254 if (RT_SUCCESS(vrc))
[50709]1255 vrc = i_waitForStatusChange(pEvent, uTimeoutMS,
[71299]1256 NULL /* ProcessStatus */, prcGuest);
[49504]1257 unregisterWaitEvent(pEvent);
1258 }
[47469]1259 }
1260
[49349]1261 LogFlowFuncLeaveRC(vrc);
[44863]1262 return vrc;
[42160]1263}
1264
[47469]1265/* static */
[50709]1266ProcessWaitResult_T GuestProcess::i_waitFlagsToResultEx(uint32_t fWaitFlags,
1267 ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
1268 uint32_t uProcFlags, uint32_t uProtocol)
[42160]1269{
[47469]1270 ProcessWaitResult_T waitResult = ProcessWaitResult_None;
[42234]1271
[49349]1272 switch (newStatus)
[42411]1273 {
[49349]1274 case ProcessStatus_TerminatedNormally:
1275 case ProcessStatus_TerminatedSignal:
1276 case ProcessStatus_TerminatedAbnormally:
1277 case ProcessStatus_Down:
[49440]1278 /* Nothing to wait for anymore. */
[49349]1279 waitResult = ProcessWaitResult_Terminate;
1280 break;
[42411]1281
[49349]1282 case ProcessStatus_TimedOutKilled:
1283 case ProcessStatus_TimedOutAbnormally:
[49440]1284 /* Dito. */
[49349]1285 waitResult = ProcessWaitResult_Timeout;
1286 break;
[42411]1287
[49349]1288 case ProcessStatus_Started:
1289 switch (oldStatus)
[42787]1290 {
[49440]1291 case ProcessStatus_Undefined:
[49349]1292 case ProcessStatus_Starting:
[49440]1293 /* Also wait for process start. */
[49349]1294 if (fWaitFlags & ProcessWaitForFlag_Start)
1295 waitResult = ProcessWaitResult_Start;
1296 else
1297 {
1298 /*
[55540]1299 * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
1300 * caller is not interested in getting further process statuses -- so just don't notify
1301 * anything here anymore and return.
1302 */
[49349]1303 if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1304 waitResult = ProcessWaitResult_Start;
1305 }
1306 break;
1307
[49440]1308 case ProcessStatus_Started:
1309 /* Only wait for process start. */
1310 if (fWaitFlags == ProcessWaitForFlag_Start)
1311 waitResult = ProcessWaitResult_Start;
1312 break;
1313
[49349]1314 default:
[49504]1315 AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
[49440]1316 oldStatus));
1317 waitResult = ProcessWaitResult_Start;
[49349]1318 break;
[42787]1319 }
[49349]1320 break;
[42411]1321
[49440]1322 case ProcessStatus_Error:
1323 /* Nothing to wait for anymore. */
1324 waitResult = ProcessWaitResult_Error;
1325 break;
1326
[49349]1327 case ProcessStatus_Undefined:
1328 case ProcessStatus_Starting:
[63153]1329 case ProcessStatus_Terminating:
1330 case ProcessStatus_Paused:
[49440]1331 /* No result available yet, leave wait
1332 * flags untouched. */
[49349]1333 break;
[72973]1334#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1335 case ProcessStatus_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
1336#endif
[42411]1337 }
1338
[49349]1339 if (newStatus == ProcessStatus_Started)
[43162]1340 {
[55540]1341 /**
1342 * Filter out waits which are *not* supported using
[49349]1343 * older guest control Guest Additions.
1344 *
1345 ** @todo ProcessWaitForFlag_Std* flags are not implemented yet.
1346 */
1347 if (uProtocol < 99) /* See @todo above. */
1348 {
1349 if ( waitResult == ProcessWaitResult_None
1350 /* We don't support waiting for stdin, out + err,
1351 * just skip waiting then. */
1352 && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1353 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1354 || (fWaitFlags & ProcessWaitForFlag_StdErr)
1355 )
[43162]1356 )
[49349]1357 {
1358 /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
1359 waitResult = ProcessWaitResult_WaitFlagNotSupported;
1360 }
[43162]1361 }
1362 }
[42436]1363
[49349]1364#ifdef DEBUG
[49504]1365 LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
[49349]1366 oldStatus, newStatus, fWaitFlags, waitResult));
1367#endif
[47469]1368 return waitResult;
1369}
1370
[50709]1371ProcessWaitResult_T GuestProcess::i_waitFlagsToResult(uint32_t fWaitFlags)
[47469]1372{
1373 AssertPtr(mSession);
[50709]1374 return GuestProcess::i_waitFlagsToResultEx(fWaitFlags,
1375 mData.mStatus /* curStatus */, mData.mStatus /* newStatus */,
[50727]1376 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
[47469]1377}
1378
[50709]1379int GuestProcess::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS,
[71299]1380 ProcessWaitResult_T &waitResult, int *prcGuest)
[47469]1381{
1382 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1383
[49349]1384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[47469]1385
[71299]1386 LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, procRc=%Rrc, prcGuest=%p\n",
1387 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, prcGuest));
[47469]1388
1389 /* Did some error occur before? Then skip waiting and return. */
[49349]1390 ProcessStatus_T curStatus = mData.mStatus;
1391 if (curStatus == ProcessStatus_Error)
[47469]1392 {
1393 waitResult = ProcessWaitResult_Error;
[50899]1394 AssertMsg(RT_FAILURE(mData.mLastError),
1395 ("No error rc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
[71299]1396 if (prcGuest)
1397 *prcGuest = mData.mLastError; /* Return last set error. */
1398 LogFlowThisFunc(("Process is in error state (rcGuest=%Rrc)\n", mData.mLastError));
[47469]1399 return VERR_GSTCTL_GUEST_ERROR;
1400 }
1401
[50709]1402 waitResult = i_waitFlagsToResult(fWaitFlags);
[42411]1403
[43162]1404 /* No waiting needed? Return immediately using the last set error. */
1405 if (waitResult != ProcessWaitResult_None)
1406 {
[71299]1407 if (prcGuest)
1408 *prcGuest = mData.mLastError; /* Return last set error (if any). */
1409 LogFlowThisFunc(("Nothing to wait for (rcGuest=%Rrc)\n", mData.mLastError));
[47817]1410 return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
[43162]1411 }
1412
[49405]1413 /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
1414 if (!uTimeoutMS)
1415 uTimeoutMS = RT_INDEFINITE_WAIT;
1416
[45780]1417 int vrc;
1418
1419 GuestWaitEvent *pEvent = NULL;
[49349]1420 GuestEventTypes eventTypes;
[45780]1421 try
1422 {
1423 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1424
[47469]1425 vrc = registerWaitEvent(eventTypes, &pEvent);
[45780]1426 }
[73505]1427 catch (std::bad_alloc &)
[45780]1428 {
1429 vrc = VERR_NO_MEMORY;
1430 }
1431
1432 if (RT_FAILURE(vrc))
1433 return vrc;
1434
[49440]1435 alock.release(); /* Release lock before waiting. */
1436
[47469]1437 /*
1438 * Do the actual waiting.
1439 */
[49349]1440 ProcessStatus_T newStatus = ProcessStatus_Undefined;
[47469]1441 uint64_t u64StartMS = RTTimeMilliTS();
1442 for (;;)
[42897]1443 {
[47905]1444 uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
[47469]1445 if ( uTimeoutMS != RT_INDEFINITE_WAIT
[47905]1446 && u64ElapsedMS >= uTimeoutMS)
[45415]1447 {
[47469]1448 vrc = VERR_TIMEOUT;
1449 break;
1450 }
[45415]1451
[50709]1452 vrc = i_waitForStatusChange(pEvent,
[47469]1453 uTimeoutMS == RT_INDEFINITE_WAIT
[50709]1454 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
[71299]1455 &newStatus, prcGuest);
[47469]1456 if (RT_SUCCESS(vrc))
1457 {
[47470]1458 alock.acquire();
[45415]1459
[50709]1460 waitResult = i_waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
[50727]1461 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
[49349]1462#ifdef DEBUG
[49504]1463 LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
[49349]1464 fWaitFlags, newStatus, waitResult));
1465#endif
[47469]1466 if (ProcessWaitResult_None != waitResult) /* We got a waiting result. */
[45415]1467 break;
1468 }
[47469]1469 else /* Waiting failed, bail out. */
1470 break;
[47627]1471
1472 alock.release(); /* Don't hold lock in next waiting round. */
[42897]1473 }
[45415]1474
[47469]1475 unregisterWaitEvent(pEvent);
[45780]1476
[49504]1477 LogFlowThisFunc(("Returned waitResult=%RU32, newStatus=%RU32, rc=%Rrc\n",
[49349]1478 waitResult, newStatus, vrc));
[45415]1479 return vrc;
1480}
1481
[50709]1482int GuestProcess::i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1483 ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
[45415]1484{
[63248]1485 RT_NOREF(uHandle);
[45780]1486 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
[45482]1487
[45780]1488 VBoxEventType_T evtType;
1489 ComPtr<IEvent> pIEvent;
1490 int vrc = waitForEvent(pEvent, uTimeoutMS,
[49440]1491 &evtType, pIEvent.asOutParam());
[45780]1492 if (RT_SUCCESS(vrc))
[43162]1493 {
[45780]1494 if (evtType == VBoxEventType_OnGuestProcessInputNotify)
1495 {
1496 ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
1497 Assert(!pProcessEvent.isNull());
[42411]1498
[45780]1499 if (pInputStatus)
[45415]1500 {
[45780]1501 HRESULT hr2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
1502 ComAssertComRC(hr2);
[45415]1503 }
[45780]1504 if (pcbProcessed)
[45415]1505 {
[45780]1506 HRESULT hr2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
1507 ComAssertComRC(hr2);
[45415]1508 }
[43162]1509 }
[45780]1510 else
1511 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
[45415]1512 }
[42673]1513
[47469]1514 LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, rc=%Rrc\n",
1515 pEvent, uHandle, vrc));
[45415]1516 return vrc;
1517}
1518
[50709]1519int GuestProcess::i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1520 void *pvData, size_t cbData, uint32_t *pcbRead)
[45482]1521{
[45780]1522 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
[47627]1523 /* pvData is optional. */
1524 /* cbData is optional. */
1525 /* pcbRead is optional. */
[45482]1526
[47627]1527 LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
1528 pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
1529
[45780]1530 int vrc;
[45482]1531
1532 VBoxEventType_T evtType;
[45780]1533 ComPtr<IEvent> pIEvent;
[45482]1534 do
1535 {
[45780]1536 vrc = waitForEvent(pEvent, uTimeoutMS,
[49440]1537 &evtType, pIEvent.asOutParam());
[45780]1538 if (RT_SUCCESS(vrc))
[45415]1539 {
[45780]1540 if (evtType == VBoxEventType_OnGuestProcessOutput)
1541 {
1542 ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
1543 Assert(!pProcessEvent.isNull());
[45415]1544
[45780]1545 ULONG uHandleEvent;
1546 HRESULT hr = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
[47627]1547 if ( SUCCEEDED(hr)
1548 && uHandleEvent == uHandle)
[45415]1549 {
[45780]1550 if (pvData)
[45415]1551 {
[45780]1552 com::SafeArray <BYTE> data;
1553 hr = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1554 ComAssertComRC(hr);
1555 size_t cbRead = data.size();
1556 if (cbRead)
[45697]1557 {
[45780]1558 if (cbRead <= cbData)
1559 {
1560 /* Copy data from event into our buffer. */
1561 memcpy(pvData, data.raw(), data.size());
1562 }
1563 else
1564 vrc = VERR_BUFFER_OVERFLOW;
[49349]1565
1566 LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), rc=%Rrc\n",
1567 cbRead, uHandleEvent, vrc));
[45697]1568 }
[45415]1569 }
[47627]1570
1571 if ( RT_SUCCESS(vrc)
1572 && pcbRead)
[45780]1573 {
1574 ULONG cbRead;
1575 hr = pProcessEvent->COMGETTER(Processed)(&cbRead);
1576 ComAssertComRC(hr);
1577 *pcbRead = (uint32_t)cbRead;
1578 }
1579
1580 break;
[45482]1581 }
[47627]1582 else if (FAILED(hr))
1583 vrc = VERR_COM_UNEXPECTED;
[45415]1584 }
[45780]1585 else
1586 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
[45415]1587 }
1588
[47627]1589 } while (vrc == VINF_SUCCESS);
[45415]1590
[47627]1591 if ( vrc != VINF_SUCCESS
1592 && pcbRead)
1593 {
1594 *pcbRead = 0;
1595 }
1596
[49349]1597 LogFlowFuncLeaveRC(vrc);
[42721]1598 return vrc;
[42673]1599}
1600
[50709]1601int GuestProcess::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
[71299]1602 ProcessStatus_T *pProcessStatus, int *prcGuest)
[45415]1603{
[45780]1604 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
[47469]1605 /* pProcessStatus is optional. */
[71299]1606 /* prcGuest is optional. */
[45415]1607
[45482]1608 VBoxEventType_T evtType;
[45780]1609 ComPtr<IEvent> pIEvent;
1610 int vrc = waitForEvent(pEvent, uTimeoutMS,
1611 &evtType, pIEvent.asOutParam());
1612 if (RT_SUCCESS(vrc))
[45415]1613 {
[45482]1614 Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
[45780]1615 ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
[45482]1616 Assert(!pProcessEvent.isNull());
[45415]1617
[49349]1618 ProcessStatus_T procStatus;
1619 HRESULT hr = pProcessEvent->COMGETTER(Status)(&procStatus);
1620 ComAssertComRC(hr);
[45482]1621 if (pProcessStatus)
[49349]1622 *pProcessStatus = procStatus;
[45415]1623
[45805]1624 ComPtr<IVirtualBoxErrorInfo> errorInfo;
[45482]1625 hr = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
1626 ComAssertComRC(hr);
[45415]1627
[45482]1628 LONG lGuestRc;
[45805]1629 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
[45482]1630 ComAssertComRC(hr);
[47469]1631
[71299]1632 LogFlowThisFunc(("Got procStatus=%RU32, rcGuest=%RI32 (%Rrc)\n",
[49349]1633 procStatus, lGuestRc, lGuestRc));
[47469]1634
[45482]1635 if (RT_FAILURE((int)lGuestRc))
1636 vrc = VERR_GSTCTL_GUEST_ERROR;
[45415]1637
[71299]1638 if (prcGuest)
1639 *prcGuest = (int)lGuestRc;
[45415]1640 }
1641
[49349]1642 LogFlowFuncLeaveRC(vrc);
[45415]1643 return vrc;
1644}
1645
[47469]1646/* static */
[50709]1647bool GuestProcess::i_waitResultImpliesEx(ProcessWaitResult_T waitResult,
1648 ProcessStatus_T procStatus, uint32_t uProcFlags,
1649 uint32_t uProtocol)
[47469]1650{
[63248]1651 /** @todo r=bird: If you subscribe to HN, which the 'u' in 'uProcFlags'
1652 * indicates, you should actually be using 'fProc'! */
1653 RT_NOREF(uProtocol, uProcFlags);
[47469]1654 bool fImplies;
1655
1656 switch (waitResult)
1657 {
1658 case ProcessWaitResult_Start:
1659 fImplies = procStatus == ProcessStatus_Started;
1660 break;
1661
1662 case ProcessWaitResult_Terminate:
1663 fImplies = ( procStatus == ProcessStatus_TerminatedNormally
1664 || procStatus == ProcessStatus_TerminatedSignal
1665 || procStatus == ProcessStatus_TerminatedAbnormally
1666 || procStatus == ProcessStatus_TimedOutKilled
1667 || procStatus == ProcessStatus_TimedOutAbnormally
1668 || procStatus == ProcessStatus_Down
1669 || procStatus == ProcessStatus_Error);
1670 break;
1671
1672 default:
1673 fImplies = false;
1674 break;
1675 }
1676
1677 return fImplies;
1678}
1679
[50709]1680int GuestProcess::i_writeData(uint32_t uHandle, uint32_t uFlags,
[71299]1681 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *prcGuest)
[42160]1682{
[71299]1683 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p, prcGuest=%p\n",
1684 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten, prcGuest));
[42634]1685 /* All is optional. There can be 0 byte writes. */
[42507]1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 if (mData.mStatus != ProcessStatus_Started)
[43162]1689 {
1690 if (puWritten)
1691 *puWritten = 0;
[71299]1692 if (prcGuest)
1693 *prcGuest = VINF_SUCCESS;
[42716]1694 return VINF_SUCCESS; /* Not available for writing (anymore). */
[43162]1695 }
[42507]1696
[45780]1697 int vrc;
1698
1699 GuestWaitEvent *pEvent = NULL;
[49349]1700 GuestEventTypes eventTypes;
[45780]1701 try
[43162]1702 {
[47469]1703 /*
1704 * On Guest Additions < 4.3 there is no guarantee that the process status
1705 * change arrives *after* the input event, e.g. if this was the last input
1706 * block being written and the process will report status "terminate".
1707 * So just skip checking for process status change and only wait for the
1708 * input event.
1709 */
[50727]1710 if (mSession->i_getProtocolVersion() >= 2)
[47469]1711 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
[45780]1712 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
[42507]1713
[47469]1714 vrc = registerWaitEvent(eventTypes, &pEvent);
[45780]1715 }
[73505]1716 catch (std::bad_alloc &)
[45780]1717 {
1718 vrc = VERR_NO_MEMORY;
1719 }
[42507]1720
[45780]1721 if (RT_FAILURE(vrc))
1722 return vrc;
[45697]1723
[45780]1724 VBOXHGCMSVCPARM paParms[5];
1725 int i = 0;
1726 paParms[i++].setUInt32(pEvent->ContextID());
1727 paParms[i++].setUInt32(mData.mPID);
1728 paParms[i++].setUInt32(uFlags);
[45927]1729 paParms[i++].setPointer(pvData, (uint32_t)cbData);
1730 paParms[i++].setUInt32((uint32_t)cbData);
[42507]1731
[45780]1732 alock.release(); /* Drop the write lock before sending. */
1733
[47469]1734 uint32_t cbProcessed = 0;
[45780]1735 vrc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
[42721]1736 if (RT_SUCCESS(vrc))
[42507]1737 {
[45415]1738 ProcessInputStatus_T inputStatus;
[50709]1739 vrc = i_waitForInputNotify(pEvent, uHandle, uTimeoutMS,
1740 &inputStatus, &cbProcessed);
[45415]1741 if (RT_SUCCESS(vrc))
[42507]1742 {
[71299]1743 /** @todo Set rcGuest. */
[42507]1744
[45415]1745 if (puWritten)
1746 *puWritten = cbProcessed;
[42507]1747 }
[45415]1748 /** @todo Error handling. */
[42507]1749 }
1750
[47469]1751 unregisterWaitEvent(pEvent);
[45780]1752
[47469]1753 LogFlowThisFunc(("Returning cbProcessed=%RU32, rc=%Rrc\n",
1754 cbProcessed, vrc));
[42721]1755 return vrc;
[42160]1756}
1757
[42084]1758// implementation of public methods
1759/////////////////////////////////////////////////////////////////////////////
1760
[50709]1761HRESULT GuestProcess::read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
[42084]1762{
[71560]1763 AutoCaller autoCaller(this);
1764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[49440]1765
[42818]1766 if (aToRead == 0)
[42509]1767 return setError(E_INVALIDARG, tr("The size to read is zero"));
[42214]1768
[71560]1769 LogFlowThisFuncEnter();
1770
[52934]1771 aData.resize(aToRead);
[42214]1772
[43162]1773 HRESULT hr = S_OK;
1774
[71299]1775 uint32_t cbRead; int rcGuest;
1776 int vrc = i_readData(aHandle, aToRead, aTimeoutMS, &aData.front(), aToRead, &cbRead, &rcGuest);
[42721]1777 if (RT_SUCCESS(vrc))
[42461]1778 {
[52934]1779 if (aData.size() != cbRead)
1780 aData.resize(cbRead);
[42461]1781 }
[43162]1782 else
1783 {
[52934]1784 aData.resize(0);
1785
[43162]1786 switch (vrc)
1787 {
[45078]1788 case VERR_GSTCTL_GUEST_ERROR:
[71299]1789 hr = GuestProcess::i_setErrorExternal(this, rcGuest);
[43162]1790 break;
[42214]1791
[43162]1792 default:
[73003]1793 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from process \"%s\" (PID %RU32) failed: %Rrc"),
1794 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
[43162]1795 break;
1796 }
1797 }
[42787]1798
[45697]1799 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
[43162]1800
[49349]1801 LogFlowFuncLeaveRC(vrc);
[42214]1802 return hr;
[42084]1803}
1804
[50709]1805HRESULT GuestProcess::terminate()
[42084]1806{
[71560]1807 AutoCaller autoCaller(this);
1808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1809
1810 LogFlowThisFuncEnter();
1811
[42846]1812 HRESULT hr = S_OK;
1813
[71299]1814 int rcGuest;
[71406]1815 int vrc = i_terminateProcess(30 * 1000 /* Timeout in ms */, &rcGuest);
[42846]1816 if (RT_FAILURE(vrc))
1817 {
1818 switch (vrc)
1819 {
[45078]1820 case VERR_GSTCTL_GUEST_ERROR:
[71299]1821 hr = GuestProcess::i_setErrorExternal(this, rcGuest);
[44863]1822 break;
[42846]1823
1824 case VERR_NOT_SUPPORTED:
[73003]1825 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
1826 tr("Terminating process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
1827 mData.mProcess.mExecutable.c_str(), mData.mPID);
[42846]1828 break;
1829
1830 default:
[73003]1831 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Terminating process \"%s\" (PID %RU32) failed: %Rrc"),
1832 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
[42846]1833 break;
1834 }
1835 }
1836
[49504]1837 /* Remove process from guest session list. Now only API clients
1838 * still can hold references to it. */
[45415]1839 AssertPtr(mSession);
[71406]1840 int rc2 = mSession->i_processUnregister(this);
[49638]1841 if (RT_SUCCESS(vrc))
1842 vrc = rc2;
[42897]1843
[49349]1844 LogFlowFuncLeaveRC(vrc);
[42214]1845 return hr;
[42084]1846}
1847
[71560]1848HRESULT GuestProcess::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
[42084]1849{
[71560]1850 AutoCaller autoCaller(this);
1851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1852
1853 LogFlowThisFuncEnter();
1854
[42272]1855 /*
1856 * Note: Do not hold any locks here while waiting!
1857 */
[43162]1858 HRESULT hr = S_OK;
[42354]1859
[71299]1860 int rcGuest;
[55535]1861 ProcessWaitResult_T waitResult;
[71299]1862 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
[42721]1863 if (RT_SUCCESS(vrc))
[42354]1864 {
[43162]1865 *aReason = waitResult;
[42354]1866 }
1867 else
[42436]1868 {
[43162]1869 switch (vrc)
1870 {
[45078]1871 case VERR_GSTCTL_GUEST_ERROR:
[71299]1872 hr = GuestProcess::i_setErrorExternal(this, rcGuest);
[43162]1873 break;
1874
[43288]1875 case VERR_TIMEOUT:
1876 *aReason = ProcessWaitResult_Timeout;
1877 break;
1878
[43162]1879 default:
[73003]1880 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Waiting for process \"%s\" (PID %RU32) failed: %Rrc"),
1881 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
[43162]1882 break;
1883 }
[42436]1884 }
[43162]1885
[49349]1886 LogFlowFuncLeaveRC(vrc);
[42214]1887 return hr;
[42084]1888}
1889
[50874]1890HRESULT GuestProcess::waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
1891 ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
[42411]1892{
1893 uint32_t fWaitFor = ProcessWaitForFlag_None;
[50709]1894 for (size_t i = 0; i < aWaitFor.size(); i++)
1895 fWaitFor |= aWaitFor[i];
[42411]1896
[42461]1897 return WaitFor(fWaitFor, aTimeoutMS, aReason);
[42411]1898}
1899
[50709]1900HRESULT GuestProcess::write(ULONG aHandle, ULONG aFlags, const std::vector<BYTE> &aData,
1901 ULONG aTimeoutMS, ULONG *aWritten)
[42084]1902{
[71560]1903 AutoCaller autoCaller(this);
1904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1905
[42214]1906 LogFlowThisFuncEnter();
1907
[43162]1908 HRESULT hr = S_OK;
[52934]1909
[71299]1910 uint32_t cbWritten; int rcGuest;
[52934]1911 uint32_t cbData = (uint32_t)aData.size();
1912 void *pvData = cbData > 0? (void *)&aData.front(): NULL;
[71299]1913 int vrc = i_writeData(aHandle, aFlags, pvData, cbData, aTimeoutMS, &cbWritten, &rcGuest);
[43162]1914 if (RT_FAILURE(vrc))
1915 {
1916 switch (vrc)
1917 {
[45078]1918 case VERR_GSTCTL_GUEST_ERROR:
[71299]1919 hr = GuestProcess::i_setErrorExternal(this, rcGuest);
[43162]1920 break;
[42787]1921
[43162]1922 default:
[73003]1923 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing to process \"%s\" (PID %RU32) failed: %Rrc"),
1924 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
[43162]1925 break;
1926 }
1927 }
1928
[45697]1929 LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
[43162]1930
[45697]1931 *aWritten = (ULONG)cbWritten;
1932
[49349]1933 LogFlowFuncLeaveRC(vrc);
[42214]1934 return hr;
[42084]1935}
1936
[50709]1937HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFlag_T> &aFlags,
1938 const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
[42507]1939{
1940 LogFlowThisFuncEnter();
1941
1942 ULONG fWrite = ProcessInputFlag_None;
[50709]1943 for (size_t i = 0; i < aFlags.size(); i++)
1944 fWrite |= aFlags[i];
[42507]1945
[50709]1946 return write(aHandle, fWrite, aData, aTimeoutMS, aWritten);
[42507]1947}
1948
[43162]1949///////////////////////////////////////////////////////////////////////////////
1950
1951GuestProcessTool::GuestProcessTool(void)
[49504]1952 : pSession(NULL),
1953 pProcess(NULL)
[43162]1954{
1955}
1956
1957GuestProcessTool::~GuestProcessTool(void)
1958{
[71564]1959 uninit();
[43162]1960}
1961
[71272]1962int GuestProcessTool::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
[71299]1963 bool fAsync, int *prcGuest)
[43162]1964{
[55535]1965 LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n",
1966 pGuestSession, startupInfo.mExecutable.c_str(), fAsync));
[43162]1967
1968 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
[56709]1969 Assert(startupInfo.mArguments[0] == startupInfo.mExecutable);
[43162]1970
[49349]1971 pSession = pGuestSession;
[43162]1972 mStartupInfo = startupInfo;
1973
1974 /* Make sure the process is hidden. */
1975 mStartupInfo.mFlags |= ProcessCreateFlag_Hidden;
1976
[71345]1977 int vrc = pSession->i_processCreateEx(mStartupInfo, pProcess);
[43162]1978 if (RT_SUCCESS(vrc))
[60622]1979 {
[63155]1980 int vrcGuest = VINF_SUCCESS;
[49570]1981 vrc = fAsync
[50709]1982 ? pProcess->i_startProcessAsync()
[63155]1983 : pProcess->i_startProcess(30 * 1000 /* 30s timeout */, &vrcGuest);
[43162]1984
[60622]1985 if ( RT_SUCCESS(vrc)
1986 && !fAsync
[63155]1987 && RT_FAILURE(vrcGuest)
[43162]1988 )
[60622]1989 {
[71299]1990 if (prcGuest)
1991 *prcGuest = vrcGuest;
[60622]1992 vrc = VERR_GSTCTL_GUEST_ERROR;
1993 }
[43162]1994 }
1995
[49349]1996 LogFlowFuncLeaveRC(vrc);
[43162]1997 return vrc;
1998}
1999
[71564]2000void GuestProcessTool::uninit(void)
2001{
[71653]2002 /* Make sure the process is terminated and unregistered from the guest session. */
2003 int rcGuestIgnored;
[71825]2004 terminate(30 * 1000 /* 30s timeout */, &rcGuestIgnored);
[71564]2005
[71825]2006 /* Unregister the process from the process (and the session's object) list. */
2007 if ( pSession
2008 && pProcess)
2009 pSession->i_processUnregister(pProcess);
2010
[71653]2011 /* Release references. */
2012 pProcess.setNull();
2013 pSession.setNull();
[71564]2014}
2015
[71272]2016int GuestProcessTool::getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock)
[43162]2017{
2018 const GuestProcessStream *pStream = NULL;
2019 if (uHandle == OUTPUT_HANDLE_ID_STDOUT)
2020 pStream = &mStdOut;
2021 else if (uHandle == OUTPUT_HANDLE_ID_STDERR)
2022 pStream = &mStdErr;
2023
2024 if (!pStream)
2025 return VERR_INVALID_PARAMETER;
2026
2027 int vrc;
2028 do
2029 {
2030 /* Try parsing the data to see if the current block is complete. */
2031 vrc = mStdOut.ParseBlock(strmBlock);
2032 if (strmBlock.GetCount())
2033 break;
2034 } while (RT_SUCCESS(vrc));
2035
2036 LogFlowThisFunc(("rc=%Rrc, %RU64 pairs\n",
2037 vrc, strmBlock.GetCount()));
2038 return vrc;
2039}
2040
[71272]2041int GuestProcessTool::getRc(void) const
[60622]2042{
[63248]2043 LONG exitCode = -1;
[60622]2044 HRESULT hr = pProcess->COMGETTER(ExitCode(&exitCode));
[63248]2045 AssertComRC(hr);
[60622]2046
[71272]2047 return GuestProcessTool::exitCodeToRc(mStartupInfo, exitCode);
[60622]2048}
2049
[71272]2050bool GuestProcessTool::isRunning(void)
[43162]2051{
[49653]2052 AssertReturn(!pProcess.isNull(), false);
[43162]2053
2054 ProcessStatus_T procStatus = ProcessStatus_Undefined;
2055 HRESULT hr = pProcess->COMGETTER(Status(&procStatus));
[63248]2056 AssertComRC(hr);
[43162]2057
[49504]2058 if ( procStatus == ProcessStatus_Started
2059 || procStatus == ProcessStatus_Paused
2060 || procStatus == ProcessStatus_Terminating)
[43162]2061 {
[49504]2062 return true;
[43162]2063 }
2064
[49504]2065 return false;
[43162]2066}
2067
[71301]2068/**
[73036]2069 * Returns whether the tool has been run correctly or not, based on it's internal process
2070 * status and reported exit status.
2071 *
2072 * @return @c true if the tool has been run correctly (exit status 0), or @c false if some error
2073 * occurred (exit status <> 0 or wrong process state).
2074 */
2075bool GuestProcessTool::isTerminatedOk(void)
2076{
2077 return getTerminationStatus() == VINF_SUCCESS ? true : false;
2078}
2079
2080/**
[71301]2081 * Static helper function to start and wait for a certain toolbox tool.
2082 *
2083 * This function most likely is the one you want to use in the first place if you
2084 * want to just use a toolbox tool and wait for its result. See runEx() if you also
2085 * needs its output.
2086 *
2087 * @return VBox status code.
2088 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2089 * @param startupInfo Startup information about the toolbox tool.
2090 * @param prcGuest Where to store the toolbox tool's specific error code in case
2091 * VERR_GSTCTL_GUEST_ERROR is returned.
2092 */
[49349]2093/* static */
[71272]2094int GuestProcessTool::run( GuestSession *pGuestSession,
[71299]2095 const GuestProcessStartupInfo &startupInfo,
2096 int *prcGuest /* = NULL */)
[49349]2097{
[71299]2098 int rcGuest;
[60622]2099
2100 GuestProcessToolErrorInfo errorInfo;
[71272]2101 int vrc = runErrorInfo(pGuestSession, startupInfo, errorInfo);
[60622]2102 if (RT_SUCCESS(vrc))
2103 {
[71301]2104 /* Make sure to check the error information we got from the guest tool. */
2105 if (GuestProcess::i_isGuestError(errorInfo.rcGuest))
2106 {
[73036]2107 if (errorInfo.rcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */
[71301]2108 rcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2109 else /* At least return something. */
2110 rcGuest = errorInfo.rcGuest;
[60622]2111
[71301]2112 if (prcGuest)
2113 *prcGuest = rcGuest;
2114
2115 vrc = VERR_GSTCTL_GUEST_ERROR;
2116 }
[60622]2117 }
2118
[71301]2119 LogFlowFunc(("Returned rc=%Rrc, rcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.rcGuest, errorInfo.iExitCode));
[60622]2120 return vrc;
[49349]2121}
2122
[61792]2123/**
[71301]2124 * Static helper function to start and wait for a certain toolbox tool, returning
2125 * extended error information from the guest.
[61792]2126 *
[71301]2127 * @return VBox status code.
[61792]2128 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2129 * @param startupInfo Startup information about the toolbox tool.
2130 * @param errorInfo Error information returned for error handling.
2131 */
[60622]2132/* static */
[71272]2133int GuestProcessTool::runErrorInfo( GuestSession *pGuestSession,
[71299]2134 const GuestProcessStartupInfo &startupInfo,
2135 GuestProcessToolErrorInfo &errorInfo)
[60622]2136{
[71272]2137 return runExErrorInfo(pGuestSession, startupInfo,
[71301]2138 NULL /* paStrmOutObjects */, 0 /* cStrmOutObjects */, errorInfo);
[60622]2139}
2140
[61792]2141/**
2142 * Static helper function to start and wait for output of a certain toolbox tool.
2143 *
2144 * @return IPRT status code.
2145 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2146 * @param startupInfo Startup information about the toolbox tool.
2147 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2148 * Optional.
2149 * @param cStrmOutObjects Number of stream objects passed in. Optional.
[71299]2150 * @param prcGuest Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned. Optional.
[61792]2151 */
[60622]2152/* static */
[71272]2153int GuestProcessTool::runEx( GuestSession *pGuestSession,
[71299]2154 const GuestProcessStartupInfo &startupInfo,
2155 GuestCtrlStreamObjects *paStrmOutObjects,
2156 uint32_t cStrmOutObjects,
2157 int *prcGuest /* = NULL */)
[60622]2158{
[71299]2159 int rcGuest;
[60622]2160
2161 GuestProcessToolErrorInfo errorInfo;
[71272]2162 int vrc = GuestProcessTool::runExErrorInfo(pGuestSession, startupInfo, paStrmOutObjects, cStrmOutObjects, errorInfo);
[60622]2163 if (RT_SUCCESS(vrc))
2164 {
[71301]2165 /* Make sure to check the error information we got from the guest tool. */
2166 if (GuestProcess::i_isGuestError(errorInfo.rcGuest))
2167 {
[73036]2168 if (errorInfo.rcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */
[71301]2169 rcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2170 else /* At least return something. */
2171 rcGuest = errorInfo.rcGuest;
[60622]2172
[71301]2173 if (prcGuest)
2174 *prcGuest = rcGuest;
2175
[61792]2176 vrc = VERR_GSTCTL_GUEST_ERROR;
[71301]2177 }
[60622]2178 }
2179
[71301]2180 LogFlowFunc(("Returned rc=%Rrc, rcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.rcGuest, errorInfo.iExitCode));
[60622]2181 return vrc;
2182}
2183
[55613]2184/**
[60642]2185 * Static helper function to start and wait for output of a certain toolbox tool.
[55613]2186 *
[60642]2187 * This is the extended version, which addds the possibility of retrieving parsable so-called guest stream
2188 * objects. Those objects are issued on the guest side as part of VBoxService's toolbox tools (think of a BusyBox-like approach)
2189 * on stdout and can be used on the host side to retrieve more information about the actual command issued on the guest side.
2190 *
[71301]2191 * @return VBox status code.
[60642]2192 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2193 * @param startupInfo Startup information about the toolbox tool.
[61792]2194 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
[60642]2195 * Optional.
2196 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2197 * @param errorInfo Error information returned for error handling.
[55613]2198 */
[49349]2199/* static */
[71272]2200int GuestProcessTool::runExErrorInfo( GuestSession *pGuestSession,
2201 const GuestProcessStartupInfo &startupInfo,
2202 GuestCtrlStreamObjects *paStrmOutObjects,
2203 uint32_t cStrmOutObjects,
2204 GuestProcessToolErrorInfo &errorInfo)
[49349]2205{
[60642]2206 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2207 /* paStrmOutObjects is optional. */
2208
2209 /** @todo Check if this is a valid toolbox. */
2210
[56709]2211 GuestProcessTool procTool;
[71299]2212 int vrc = procTool.init(pGuestSession, startupInfo, false /* Async */, &errorInfo.rcGuest);
[49349]2213 if (RT_SUCCESS(vrc))
2214 {
2215 while (cStrmOutObjects--)
2216 {
2217 try
2218 {
2219 GuestProcessStreamBlock strmBlk;
[71272]2220 vrc = procTool.waitEx( paStrmOutObjects
[71263]2221 ? GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK
[71299]2222 : GUESTPROCESSTOOL_WAIT_FLAG_NONE, &strmBlk, &errorInfo.rcGuest);
[60642]2223 if (paStrmOutObjects)
2224 paStrmOutObjects->push_back(strmBlk);
[49349]2225 }
[73505]2226 catch (std::bad_alloc &)
[49349]2227 {
2228 vrc = VERR_NO_MEMORY;
2229 }
2230 }
2231 }
2232
2233 if (RT_SUCCESS(vrc))
2234 {
2235 /* Make sure the process runs until completion. */
[71299]2236 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &errorInfo.rcGuest);
[49570]2237 if (RT_SUCCESS(vrc))
[73036]2238 errorInfo.rcGuest = procTool.getTerminationStatus(&errorInfo.iExitCode);
[49349]2239 }
2240
[71299]2241 LogFlowFunc(("Returned rc=%Rrc, rcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.rcGuest, errorInfo.iExitCode));
[49349]2242 return vrc;
2243}
2244
[60622]2245/**
2246 * Reports if the tool has been run correctly.
2247 *
[73036]2248 * @return Will return VERR_GSTCTL_PROCESS_EXIT_CODE if the tool process returned an exit code <> 0,
[60622]2249 * VERR_GSTCTL_PROCESS_WRONG_STATE if the tool process is in a wrong state (e.g. still running),
2250 * or VINF_SUCCESS otherwise.
2251 *
[70388]2252 * @param piExitCode Exit code of the tool. Optional.
[60622]2253 */
[73036]2254int GuestProcessTool::getTerminationStatus(int32_t *piExitCode /* = NULL */)
[43162]2255{
2256 Assert(!pProcess.isNull());
2257 /* pExitCode is optional. */
2258
[49349]2259 int vrc;
[71272]2260 if (!isRunning())
[43162]2261 {
[70388]2262 LONG iExitCode = -1;
2263 HRESULT hr = pProcess->COMGETTER(ExitCode(&iExitCode));
[63248]2264 AssertComRC(hr);
[43162]2265
[70388]2266 if (piExitCode)
2267 *piExitCode = iExitCode;
[43162]2268
[73036]2269 vrc = iExitCode != 0 ? VERR_GSTCTL_PROCESS_EXIT_CODE : VINF_SUCCESS;
[43162]2270 }
[49349]2271 else
[60622]2272 vrc = VERR_GSTCTL_PROCESS_WRONG_STATE;
[43162]2273
[49349]2274 LogFlowFuncLeaveRC(vrc);
2275 return vrc;
[43162]2276}
2277
[71299]2278int GuestProcessTool::wait(uint32_t fToolWaitFlags, int *prcGuest)
[43162]2279{
[71299]2280 return waitEx(fToolWaitFlags, NULL /* pStrmBlkOut */, prcGuest);
[43162]2281}
2282
[71299]2283int GuestProcessTool::waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *pStrmBlkOut, int *prcGuest)
[43162]2284{
[71299]2285 LogFlowThisFunc(("fToolWaitFlags=0x%x, pStreamBlock=%p, prcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, prcGuest));
[43162]2286
2287 /* Can we parse the next block without waiting? */
2288 int vrc;
[71263]2289 if (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK)
[43162]2290 {
[49349]2291 AssertPtr(pStrmBlkOut);
[71272]2292 vrc = getCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStrmBlkOut);
[43162]2293 if (RT_SUCCESS(vrc))
2294 return vrc;
[64532]2295 /* else do the waiting below. */
[43162]2296 }
2297
2298 /* Do the waiting. */
[71263]2299 uint32_t fProcWaitForFlags = ProcessWaitForFlag_Terminate;
[43162]2300 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
[71263]2301 fProcWaitForFlags |= ProcessWaitForFlag_StdOut;
[43162]2302 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
[71263]2303 fProcWaitForFlags |= ProcessWaitForFlag_StdErr;
[43162]2304
[47627]2305 /** @todo Decrease timeout while running. */
[49349]2306 uint64_t u64StartMS = RTTimeMilliTS();
[43162]2307 uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
2308
[63155]2309 int vrcGuest = VINF_SUCCESS;
[43162]2310 bool fDone = false;
2311
2312 BYTE byBuf[_64K];
[45482]2313 uint32_t cbRead;
[43162]2314
2315 bool fHandleStdOut = false;
2316 bool fHandleStdErr = false;
2317
[49349]2318 /**
2319 * Updates the elapsed time and checks if a
2320 * timeout happened, then breaking out of the loop.
2321 */
2322#define UPDATE_AND_CHECK_ELAPSED_TIME() \
2323 u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
2324 if ( uTimeoutMS != RT_INDEFINITE_WAIT \
2325 && u64ElapsedMS >= uTimeoutMS) \
2326 { \
2327 vrc = VERR_TIMEOUT; \
2328 break; \
2329 }
2330
2331 /**
2332 * Returns the remaining time (in ms).
2333 */
2334#define GET_REMAINING_TIME \
2335 uTimeoutMS == RT_INDEFINITE_WAIT \
2336 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS \
2337
[63178]2338 ProcessWaitResult_T waitRes = ProcessWaitResult_None;
[43162]2339 do
2340 {
[49349]2341 uint64_t u64ElapsedMS;
2342 UPDATE_AND_CHECK_ELAPSED_TIME();
2343
[71263]2344 vrc = pProcess->i_waitFor(fProcWaitForFlags, GET_REMAINING_TIME, waitRes, &vrcGuest);
[43162]2345 if (RT_FAILURE(vrc))
2346 break;
2347
2348 switch (waitRes)
2349 {
[43201]2350 case ProcessWaitResult_StdIn:
[43301]2351 vrc = VERR_NOT_IMPLEMENTED;
2352 break;
[43201]2353
[43162]2354 case ProcessWaitResult_StdOut:
2355 fHandleStdOut = true;
2356 break;
2357
2358 case ProcessWaitResult_StdErr:
2359 fHandleStdErr = true;
2360 break;
2361
2362 case ProcessWaitResult_WaitFlagNotSupported:
[71263]2363 if (fProcWaitForFlags & ProcessWaitForFlag_StdOut)
[43162]2364 fHandleStdOut = true;
[71263]2365 if (fProcWaitForFlags & ProcessWaitForFlag_StdErr)
[43162]2366 fHandleStdErr = true;
2367 /* Since waiting for stdout / stderr is not supported by the guest,
2368 * wait a bit to not hog the CPU too much when polling for data. */
2369 RTThreadSleep(1); /* Optional, don't check rc. */
2370 break;
2371
2372 case ProcessWaitResult_Error:
[45078]2373 vrc = VERR_GSTCTL_GUEST_ERROR;
[43299]2374 break;
2375
[43162]2376 case ProcessWaitResult_Terminate:
2377 fDone = true;
2378 break;
2379
[43299]2380 case ProcessWaitResult_Timeout:
2381 vrc = VERR_TIMEOUT;
2382 break;
2383
[43301]2384 case ProcessWaitResult_Start:
2385 case ProcessWaitResult_Status:
2386 /* Not used here, just skip. */
2387 break;
2388
[43162]2389 default:
[49570]2390 AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
[43162]2391 break;
2392 }
2393
[49349]2394 if (RT_FAILURE(vrc))
2395 break;
2396
[43162]2397 if (fHandleStdOut)
2398 {
[49349]2399 UPDATE_AND_CHECK_ELAPSED_TIME();
2400
[47627]2401 cbRead = 0;
[50709]2402 vrc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
2403 GET_REMAINING_TIME,
2404 byBuf, sizeof(byBuf),
[63155]2405 &cbRead, &vrcGuest);
[49349]2406 if ( RT_FAILURE(vrc)
2407 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
[43162]2408 break;
2409
2410 if (cbRead)
2411 {
[47627]2412 LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
[43162]2413 vrc = mStdOut.AddData(byBuf, cbRead);
2414
2415 if ( RT_SUCCESS(vrc)
[71263]2416 && (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK))
[43162]2417 {
[49349]2418 AssertPtr(pStrmBlkOut);
[71272]2419 vrc = getCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStrmBlkOut);
[49349]2420
2421 /* When successful, break out of the loop because we're done
2422 * with reading the first stream block. */
[43162]2423 if (RT_SUCCESS(vrc))
2424 fDone = true;
2425 }
2426 }
2427
2428 fHandleStdOut = false;
2429 }
2430
2431 if (fHandleStdErr)
2432 {
[49349]2433 UPDATE_AND_CHECK_ELAPSED_TIME();
2434
[47627]2435 cbRead = 0;
[50709]2436 vrc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDERR, sizeof(byBuf),
2437 GET_REMAINING_TIME,
2438 byBuf, sizeof(byBuf),
[63155]2439 &cbRead, &vrcGuest);
[49349]2440 if ( RT_FAILURE(vrc)
2441 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
[43162]2442 break;
2443
2444 if (cbRead)
2445 {
[47627]2446 LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
[43162]2447 vrc = mStdErr.AddData(byBuf, cbRead);
2448 }
2449
2450 fHandleStdErr = false;
2451 }
2452
2453 } while (!fDone && RT_SUCCESS(vrc));
2454
[49349]2455#undef UPDATE_AND_CHECK_ELAPSED_TIME
2456#undef GET_REMAINING_TIME
2457
[63155]2458 if (RT_FAILURE(vrcGuest))
[49504]2459 vrc = VERR_GSTCTL_GUEST_ERROR;
2460
[63155]2461 LogFlowThisFunc(("Loop ended with rc=%Rrc, vrcGuest=%Rrc, waitRes=%RU32\n",
2462 vrc, vrcGuest, waitRes));
[71299]2463 if (prcGuest)
2464 *prcGuest = vrcGuest;
[43162]2465
[49349]2466 LogFlowFuncLeaveRC(vrc);
[43162]2467 return vrc;
2468}
2469
[71299]2470int GuestProcessTool::terminate(uint32_t uTimeoutMS, int *prcGuest)
[43162]2471{
2472 LogFlowThisFuncEnter();
2473
[71406]2474 int rc;
[43162]2475 if (!pProcess.isNull())
[71299]2476 rc = pProcess->i_terminateProcess(uTimeoutMS, prcGuest);
[47469]2477 else
2478 rc = VERR_NOT_FOUND;
[43162]2479
[47469]2480 LogFlowFuncLeaveRC(rc);
2481 return rc;
[43162]2482}
2483
[60622]2484/**
2485 * Converts a toolbox tool's exit code to an IPRT error code.
2486 *
[70388]2487 * @return int Returned IPRT error for the particular tool.
2488 * @param startupInfo Startup info of the toolbox tool to lookup error code for.
2489 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
[60622]2490 */
2491/* static */
[71272]2492int GuestProcessTool::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode)
[60622]2493{
2494 if (startupInfo.mArguments.size() == 0)
2495 {
2496 AssertFailed();
2497 return VERR_GENERAL_FAILURE; /* Should not happen. */
2498 }
2499
[71272]2500 return exitCodeToRc(startupInfo.mArguments[0].c_str(), iExitCode);
[60622]2501}
2502
2503/**
2504 * Converts a toolbox tool's exit code to an IPRT error code.
2505 *
[65120]2506 * @return Returned IPRT error for the particular tool.
2507 * @param pszTool Name of toolbox tool to lookup error code for.
[70388]2508 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
[60622]2509 */
2510/* static */
[71272]2511int GuestProcessTool::exitCodeToRc(const char *pszTool, int32_t iExitCode)
[60622]2512{
2513 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
2514
[70388]2515 LogFlowFunc(("%s: %d\n", pszTool, iExitCode));
[60622]2516
[70388]2517 if (iExitCode == 0) /* No error? Bail out early. */
[60622]2518 return VINF_SUCCESS;
2519
2520 if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_CAT))
2521 {
[70388]2522 switch (iExitCode)
[60622]2523 {
2524 case VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2525 case VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2526 case VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2527 case VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION: return VERR_SHARING_VIOLATION;
[71176]2528 case VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY: return VERR_IS_A_DIRECTORY;
[71125]2529 default: break;
[60622]2530 }
2531 }
2532 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_STAT))
2533 {
[70388]2534 switch (iExitCode)
[60622]2535 {
[71517]2536 case VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2537 case VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2538 case VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2539 case VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND: return VERR_NET_PATH_NOT_FOUND;
2540 default: break;
[60622]2541 }
2542 }
[70388]2543 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKDIR))
2544 {
2545 switch (iExitCode)
2546 {
[71125]2547 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2548 default: break;
[70388]2549 }
2550 }
[60622]2551
[71518]2552 LogFunc(("Warning: Exit code %d not handled for tool '%s', returning VERR_GENERAL_FAILURE\n", iExitCode, pszTool));
2553
[70388]2554 if (iExitCode == RTEXITCODE_SYNTAX)
2555 return VERR_INTERNAL_ERROR_5;
[60622]2556 return VERR_GENERAL_FAILURE;
2557}
2558
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use