VirtualBox

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

Last change on this file since 49498 was 49440, checked in by vboxsync, 11 years ago

Main/GuestCtrl: Bugfixes:

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