VirtualBox

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

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

GuestCtrl: More code for guest session infrastructure handling (untested, work in progress).

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