VirtualBox

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

Last change on this file since 91516 was 91516, checked in by vboxsync, 3 years ago

Main: Fixed continuation indent for a bunch of places now using GuestErrorInfo as a variable. Fixed two no-else-after-return cases too. bugref:1909

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

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