VirtualBox

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

Last change on this file since 94521 was 93720, checked in by vboxsync, 2 years ago

Guest Control/Main: Return an error in IGuestProcess::terminate() if the guest process is not in a running state (anymore). Useful for testcases.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use