VirtualBox

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

Last change on this file was 104178, checked in by vboxsync, 6 weeks ago

Guest Control:

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

© 2023 Oracle
ContactPrivacy policyTerms of Use