VirtualBox

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

Last change on this file since 80774 was 79369, checked in by vboxsync, 6 years ago

Main/GuestProcessImpl.cpp: Check flags passed to write* and WaitFor*. bugref:9320

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