VirtualBox

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

Last change on this file since 45415 was 45415, checked in by vboxsync, 12 years ago

GuestCtrl: Implemented using (public) VirtualBox events instead of own callback mechanisms. Bugfixes for testcases (still work in progress).

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