VirtualBox

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

Last change on this file since 53062 was 52934, checked in by vboxsync, 10 years ago

Main: safearray cleanup, removed unnecessary SafeArray<->vector conversions

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