VirtualBox

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

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

remove changes that break up log and error strings, introduced in 92972

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