VirtualBox

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

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

Main/src-client/GuestProcessImpl.cpp: fix compiler warnings on XPCOM, as it generates strange enums

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.3 KB
Line 
1
2/* $Id: GuestProcessImpl.cpp 42920 2012-08-22 13:12:39Z vboxsync $ */
3/** @file
4 * VirtualBox Main - XXX.
5 */
6
7/*
8 * Copyright (C) 2012 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#include "VMMDev.h"
39
40#include <memory> /* For auto_ptr. */
41
42#include <iprt/asm.h>
43#include <iprt/getopt.h>
44#include <VBox/VMMDev.h>
45#include <VBox/com/array.h>
46
47#ifdef LOG_GROUP
48 #undef LOG_GROUP
49#endif
50#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
51#include <VBox/log.h>
52
53
54class GuestProcessTask
55{
56public:
57
58 GuestProcessTask(GuestProcess *pProcess)
59 : mProcess(pProcess),
60 mRC(VINF_SUCCESS) { }
61
62 virtual ~GuestProcessTask(void) { }
63
64 int rc(void) const { return mRC; }
65 bool isOk(void) const { return RT_SUCCESS(mRC); }
66 const ComObjPtr<GuestProcess> &Process(void) const { return mProcess; }
67
68protected:
69
70 const ComObjPtr<GuestProcess> mProcess;
71 int mRC;
72};
73
74class GuestProcessStartTask : public GuestProcessTask
75{
76public:
77
78 GuestProcessStartTask(GuestProcess *pProcess)
79 : GuestProcessTask(pProcess) { }
80};
81
82
83// constructor / destructor
84/////////////////////////////////////////////////////////////////////////////
85
86DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
87
88HRESULT GuestProcess::FinalConstruct(void)
89{
90 LogFlowThisFuncEnter();
91
92 mData.mExitCode = 0;
93 mData.mNextContextID = 0;
94 mData.mPID = 0;
95 mData.mProcessID = 0;
96 mData.mRC = VINF_SUCCESS;
97 mData.mStatus = ProcessStatus_Undefined;
98
99 mData.mWaitCount = 0;
100 mData.mWaitEvent = NULL;
101
102 HRESULT hr = BaseFinalConstruct();
103 return hr;
104}
105
106void GuestProcess::FinalRelease(void)
107{
108 LogFlowThisFuncEnter();
109 uninit();
110 BaseFinalRelease();
111 LogFlowThisFuncLeave();
112}
113
114// public initializer/uninitializer for internal purposes only
115/////////////////////////////////////////////////////////////////////////////
116
117int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aProcessID, const GuestProcessStartupInfo &aProcInfo)
118{
119 LogFlowThisFunc(("aConsole=%p, aSession=%p, aProcessID=%RU32\n",
120 aConsole, aSession, aProcessID));
121
122 AssertPtrReturn(aSession, VERR_INVALID_POINTER);
123
124 /* Enclose the state transition NotReady->InInit->Ready. */
125 AutoInitSpan autoInitSpan(this);
126 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
127
128 mData.mConsole = aConsole;
129 mData.mParent = aSession;
130 mData.mProcessID = aProcessID;
131 mData.mProcess = aProcInfo;
132 /* Everything else will be set by the actual starting routine. */
133
134 /* Confirm a successful initialization when it's the case. */
135 autoInitSpan.setSucceeded();
136
137 return VINF_SUCCESS;
138}
139
140/**
141 * Uninitializes the instance.
142 * Called from FinalRelease().
143 */
144void GuestProcess::uninit(void)
145{
146 LogFlowThisFunc(("mCmd=%s, PID=%RU32\n",
147 mData.mProcess.mCommand.c_str(), mData.mPID));
148
149 /* Enclose the state transition Ready->InUninit->NotReady. */
150 AutoUninitSpan autoUninitSpan(this);
151 if (autoUninitSpan.uninitDone())
152 return;
153
154 int vrc = VINF_SUCCESS;
155
156#ifdef VBOX_WITH_GUEST_CONTROL
157 /*
158 * Cancel all callbacks + waiters.
159 * Note: Deleting them is the job of the caller!
160 */
161 for (GuestCtrlCallbacks::iterator itCallbacks = mData.mCallbacks.begin();
162 itCallbacks != mData.mCallbacks.end(); ++itCallbacks)
163 {
164 GuestCtrlCallback *pCallback = itCallbacks->second;
165 AssertPtr(pCallback);
166 int rc2 = pCallback->Cancel();
167 if (RT_SUCCESS(vrc))
168 vrc = rc2;
169 }
170 mData.mCallbacks.clear();
171
172 if (mData.mWaitEvent)
173 {
174 int rc2 = mData.mWaitEvent->Cancel();
175 if (RT_SUCCESS(vrc))
176 vrc = rc2;
177 }
178
179 mData.mStatus = ProcessStatus_Down; /** @todo Correct? */
180#endif
181
182 LogFlowFuncLeaveRC(vrc);
183}
184
185// implementation of public getters/setters for attributes
186/////////////////////////////////////////////////////////////////////////////
187
188STDMETHODIMP GuestProcess::COMGETTER(Arguments)(ComSafeArrayOut(BSTR, aArguments))
189{
190#ifndef VBOX_WITH_GUEST_CONTROL
191 ReturnComNotImplemented();
192#else
193 LogFlowThisFuncEnter();
194
195 CheckComArgOutSafeArrayPointerValid(aArguments);
196
197 AutoCaller autoCaller(this);
198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
199
200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
201
202 com::SafeArray<BSTR> collection(mData.mProcess.mArguments.size());
203 size_t s = 0;
204 for (ProcessArguments::const_iterator it = mData.mProcess.mArguments.begin();
205 it != mData.mProcess.mArguments.end();
206 it++, s++)
207 {
208 Bstr tmp = *it;
209 tmp.cloneTo(&collection[s]);
210 }
211
212 collection.detachTo(ComSafeArrayOutArg(aArguments));
213
214 return S_OK;
215#endif /* VBOX_WITH_GUEST_CONTROL */
216}
217
218STDMETHODIMP GuestProcess::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnvironment))
219{
220#ifndef VBOX_WITH_GUEST_CONTROL
221 ReturnComNotImplemented();
222#else
223 LogFlowThisFuncEnter();
224
225 CheckComArgOutSafeArrayPointerValid(aEnvironment);
226
227 AutoCaller autoCaller(this);
228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
229
230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
231
232 com::SafeArray<BSTR> arguments(mData.mProcess.mEnvironment.Size());
233 for (size_t i = 0; i < arguments.size(); i++)
234 {
235 Bstr tmp = mData.mProcess.mEnvironment.Get(i);
236 tmp.cloneTo(&arguments[i]);
237 }
238 arguments.detachTo(ComSafeArrayOutArg(aEnvironment));
239
240 return S_OK;
241#endif /* VBOX_WITH_GUEST_CONTROL */
242}
243
244STDMETHODIMP GuestProcess::COMGETTER(ExecutablePath)(BSTR *aExecutablePath)
245{
246#ifndef VBOX_WITH_GUEST_CONTROL
247 ReturnComNotImplemented();
248#else
249 LogFlowThisFuncEnter();
250
251 CheckComArgOutPointerValid(aExecutablePath);
252
253 AutoCaller autoCaller(this);
254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
255
256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
257
258 mData.mProcess.mCommand.cloneTo(aExecutablePath);
259
260 return S_OK;
261#endif /* VBOX_WITH_GUEST_CONTROL */
262}
263
264STDMETHODIMP GuestProcess::COMGETTER(ExitCode)(LONG *aExitCode)
265{
266#ifndef VBOX_WITH_GUEST_CONTROL
267 ReturnComNotImplemented();
268#else
269 LogFlowThisFuncEnter();
270
271 CheckComArgOutPointerValid(aExitCode);
272
273 AutoCaller autoCaller(this);
274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
275
276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
277
278 *aExitCode = mData.mExitCode;
279
280 return S_OK;
281#endif /* VBOX_WITH_GUEST_CONTROL */
282}
283
284STDMETHODIMP GuestProcess::COMGETTER(Name)(BSTR *aName)
285{
286#ifndef VBOX_WITH_GUEST_CONTROL
287 ReturnComNotImplemented();
288#else
289 LogFlowThisFuncEnter();
290
291 CheckComArgOutPointerValid(aName);
292
293 AutoCaller autoCaller(this);
294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
295
296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
297
298 mData.mProcess.mName.cloneTo(aName);
299
300 return S_OK;
301#endif /* VBOX_WITH_GUEST_CONTROL */
302}
303
304STDMETHODIMP GuestProcess::COMGETTER(PID)(ULONG *aPID)
305{
306#ifndef VBOX_WITH_GUEST_CONTROL
307 ReturnComNotImplemented();
308#else
309 LogFlowThisFuncEnter();
310
311 CheckComArgOutPointerValid(aPID);
312
313 AutoCaller autoCaller(this);
314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
315
316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
317
318 *aPID = mData.mPID;
319
320 return S_OK;
321#endif /* VBOX_WITH_GUEST_CONTROL */
322}
323
324STDMETHODIMP GuestProcess::COMGETTER(Status)(ProcessStatus_T *aStatus)
325{
326#ifndef VBOX_WITH_GUEST_CONTROL
327 ReturnComNotImplemented();
328#else
329 LogFlowThisFuncEnter();
330
331 AutoCaller autoCaller(this);
332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
333
334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
335
336 *aStatus = mData.mStatus;
337
338 return S_OK;
339#endif /* VBOX_WITH_GUEST_CONTROL */
340}
341
342// private methods
343/////////////////////////////////////////////////////////////////////////////
344
345inline int GuestProcess::callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID)
346{
347 const ComObjPtr<GuestSession> pSession(mData.mParent);
348 Assert(!pSession.isNull());
349 ULONG uSessionID = 0;
350 HRESULT hr = pSession->COMGETTER(Id)(&uSessionID);
351 ComAssertComRC(hr);
352
353 /* Create a new context ID and assign it. */
354 int vrc = VERR_NOT_FOUND;
355
356 ULONG uCount = mData.mNextContextID++;
357 ULONG uNewContextID = 0;
358 ULONG uTries = 0;
359 for (;;)
360 {
361 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
362 uCount = 0;
363
364 /* Create a new context ID ... */
365 uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID,
366 mData.mProcessID, uCount);
367
368 /* Is the context ID already used? Try next ID ... */
369 if (!callbackExists(uCount))
370 {
371 /* Callback with context ID was not found. This means
372 * we can use this context ID for our new callback we want
373 * to add below. */
374 vrc = VINF_SUCCESS;
375 break;
376 }
377
378 uCount++;
379 if (++uTries == UINT32_MAX)
380 break; /* Don't try too hard. */
381 }
382
383 if (RT_SUCCESS(vrc))
384 {
385 /* Add callback with new context ID to our callback map.
386 * Note: This is *not* uNewContextID (which also includes
387 * the session + process ID), just the context count
388 * will be used here. */
389 mData.mCallbacks[uCount] = pCallback;
390 Assert(mData.mCallbacks.size());
391
392 /* Report back new context ID. */
393 if (puContextID)
394 *puContextID = uNewContextID;
395
396 LogFlowThisFunc(("Added new callback (Session: %RU32, Process: %RU32, Count=%RU32) CID=%RU32\n",
397 uSessionID, mData.mProcessID, uCount, uNewContextID));
398 }
399
400 return vrc;
401}
402
403int GuestProcess::callbackDispatcher(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
404{
405#ifdef DEBUG
406 LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uFunction=%RU32, pvData=%p, cbData=%RU32\n",
407 mData.mPID, uContextID, uFunction, pvData, cbData));
408#endif
409
410 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
411 AssertReturn(cbData, VERR_INVALID_PARAMETER);
412
413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
414
415 int vrc;
416
417 /* Get the optional callback associated to this context ID.
418 * The callback may not be around anymore if just kept locally by the caller when
419 * doing the actual HGCM sending stuff. */
420 GuestCtrlCallback *pCallback = NULL;
421 GuestCtrlCallbacks::const_iterator it
422 = mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
423 if (it != mData.mCallbacks.end())
424 {
425 pCallback = it->second;
426 AssertPtr(pCallback);
427#ifdef DEBUG
428 LogFlowThisFunc(("pCallback=%p\n", pCallback));
429#endif
430 }
431
432 switch (uFunction)
433 {
434 case GUEST_DISCONNECTED:
435 {
436 PCALLBACKDATACLIENTDISCONNECTED pCallbackData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvData);
437 AssertPtr(pCallbackData);
438 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbData, VERR_INVALID_PARAMETER);
439 AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
440
441 vrc = onGuestDisconnected(pCallback, pCallbackData); /* Affects all callbacks. */
442 break;
443 }
444
445 case GUEST_EXEC_SEND_STATUS:
446 {
447 PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvData);
448 AssertPtr(pCallbackData);
449 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbData, VERR_INVALID_PARAMETER);
450 AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
451
452 vrc = onProcessStatusChange(pCallback, pCallbackData);
453 break;
454 }
455
456 case GUEST_EXEC_SEND_OUTPUT:
457 {
458 PCALLBACKDATAEXECOUT pCallbackData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvData);
459 AssertPtr(pCallbackData);
460 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbData, VERR_INVALID_PARAMETER);
461 AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
462
463 vrc = onProcessOutput(pCallback, pCallbackData);
464 break;
465 }
466
467 case GUEST_EXEC_SEND_INPUT_STATUS:
468 {
469 PCALLBACKDATAEXECINSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvData);
470 AssertPtr(pCallbackData);
471 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbData, VERR_INVALID_PARAMETER);
472 AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
473
474 vrc = onProcessInputStatus(pCallback, pCallbackData);
475 break;
476 }
477
478 default:
479 /* Silently ignore not implemented functions. */
480 vrc = VERR_NOT_IMPLEMENTED;
481 break;
482 }
483
484#ifdef DEBUG
485 LogFlowFuncLeaveRC(vrc);
486#endif
487 return vrc;
488}
489
490inline bool GuestProcess::callbackExists(uint32_t uContextID)
491{
492 GuestCtrlCallbacks::const_iterator it =
493 mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
494 return (it == mData.mCallbacks.end()) ? false : true;
495}
496
497inline int GuestProcess::callbackRemove(uint32_t uContextID)
498{
499 GuestCtrlCallbacks::iterator it =
500 mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
501 if (it != mData.mCallbacks.end())
502 {
503 delete it->second;
504 mData.mCallbacks.erase(it);
505
506 return VINF_SUCCESS;
507 }
508
509 return VERR_NOT_FOUND;
510}
511
512/**
513 * Checks if the current assigned PID matches another PID (from a callback).
514 *
515 * In protocol v1 we don't have the possibility to terminate/kill
516 * processes so it can happen that a formerly start process A
517 * (which has the context ID 0 (session=0, process=0, count=0) will
518 * send a delayed message to the host if this process has already
519 * been discarded there and the same context ID was reused by
520 * a process B. Process B in turn then has a different guest PID.
521 *
522 * @return IPRT status code.
523 * @param uPID PID to check.
524 */
525inline int GuestProcess::checkPID(uint32_t uPID)
526{
527 /* Was there a PID assigned yet? */
528 if (mData.mPID)
529 {
530 /*
531
532 */
533 if (mData.mParent->getProtocolVersion() < 2)
534 {
535 /* Simply ignore the stale requests. */
536 return (mData.mPID == uPID)
537 ? VINF_SUCCESS : VERR_NOT_FOUND;
538 }
539 /* This should never happen! */
540 AssertReleaseMsg(mData.mPID == uPID, ("Unterminated guest process (PID %RU32) sent data to a newly started process (PID %RU32)\n",
541 uPID, mData.mPID));
542 }
543
544 return VINF_SUCCESS;
545}
546
547inline bool GuestProcess::isAlive(void)
548{
549 return ( mData.mStatus == ProcessStatus_Started
550 || mData.mStatus == ProcessStatus_Paused
551 || mData.mStatus == ProcessStatus_Terminating);
552}
553
554bool GuestProcess::isReady(void)
555{
556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
557
558 if (mData.mStatus == ProcessStatus_Started)
559 {
560 Assert(mData.mPID); /* PID must not be 0. */
561 return true;
562 }
563
564 return false;
565}
566
567int GuestProcess::onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData)
568{
569 /* pCallback is optional. */
570 AssertPtrReturn(pData, VERR_INVALID_POINTER);
571
572 LogFlowThisFunc(("uPID=%RU32, pCallback=%p, pData=%p\n", mData.mPID, pCallback, pData));
573
574 mData.mStatus = ProcessStatus_Down;
575
576 /* First, signal callback in every case. */
577 if (pCallback)
578 pCallback->Signal();
579
580 /* Do we need to report a termination? */
581 ProcessWaitResult_T waitRes;
582 if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
583 waitRes = ProcessWaitResult_Status; /* No, just report a status. */
584 else
585 waitRes = ProcessWaitResult_Terminate;
586
587 /* Signal in any case. */
588 int vrc = signalWaiters(waitRes);
589 AssertRC(vrc);
590
591 LogFlowFuncLeaveRC(vrc);
592 return vrc;
593}
594
595int GuestProcess::onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData)
596{
597 /* pCallback is optional. */
598 AssertPtrReturn(pData, VERR_INVALID_POINTER);
599
600 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, cbProcessed=%RU32, pCallback=%p, pData=%p\n",
601 mData.mPID, pData->u32Status, pData->u32Flags, pData->cbProcessed, pCallback, pData));
602
603 int vrc = checkPID(pData->u32PID);
604 if (RT_FAILURE(vrc))
605 return vrc;
606
607 /* First, signal callback in every case (if available). */
608 if (pCallback)
609 {
610 vrc = pCallback->SetData(pData, sizeof(CALLBACKDATAEXECINSTATUS));
611
612 int rc2 = pCallback->Signal();
613 if (RT_SUCCESS(vrc))
614 vrc = rc2;
615 }
616
617 /* Then do the WaitFor signalling stuff. */
618 uint32_t uWaitFlags = mData.mWaitEvent
619 ? mData.mWaitEvent->GetWaitFlags() : 0;
620 if (uWaitFlags & ProcessWaitForFlag_StdIn)
621 {
622 int rc2 = signalWaiters(ProcessWaitResult_StdIn);
623 if (RT_SUCCESS(vrc))
624 vrc = rc2;
625 }
626
627 LogFlowFuncLeaveRC(vrc);
628 return vrc;
629}
630
631int GuestProcess::onProcessNotifyIO(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
632{
633 /* pCallback is optional. */
634 AssertPtrReturn(pData, VERR_INVALID_POINTER);
635
636 return 0;
637}
638
639int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
640{
641 /* pCallback is optional. */
642 AssertPtrReturn(pData, VERR_INVALID_POINTER);
643
644 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, pCallback=%p, pData=%p\n",
645 pData->u32PID, pData->u32Status, pData->u32Flags, pCallback, pData));
646
647 int vrc = checkPID(pData->u32PID);
648 if (RT_FAILURE(vrc))
649 return vrc;
650
651 BOOL fSignal = FALSE;
652 ProcessWaitResult_T waitRes;
653 uint32_t uWaitFlags = mData.mWaitEvent
654 ? mData.mWaitEvent->GetWaitFlags() : 0;
655 switch (pData->u32Status)
656 {
657 case PROC_STS_STARTED:
658 {
659 fSignal = (uWaitFlags & ProcessWaitForFlag_Start);
660 waitRes = ProcessWaitResult_Start;
661
662 mData.mStatus = ProcessStatus_Started;
663 mData.mPID = pData->u32PID;
664 break;
665 }
666
667 case PROC_STS_TEN:
668 {
669 fSignal = TRUE; /* Signal in any case. */
670 waitRes = ProcessWaitResult_Terminate;
671
672 mData.mStatus = ProcessStatus_TerminatedNormally;
673 mData.mExitCode = pData->u32Flags; /* Contains the exit code. */
674 break;
675 }
676
677 case PROC_STS_TES:
678 {
679 fSignal = TRUE; /* Signal in any case. */
680 waitRes = ProcessWaitResult_Terminate;
681
682 mData.mStatus = ProcessStatus_TerminatedSignal;
683 mData.mExitCode = pData->u32Flags; /* Contains the signal. */
684 break;
685 }
686
687 case PROC_STS_TEA:
688 {
689 fSignal = TRUE; /* Signal in any case. */
690 waitRes = ProcessWaitResult_Terminate;
691
692 mData.mStatus = ProcessStatus_TerminatedAbnormally;
693 break;
694 }
695
696 case PROC_STS_TOK:
697 {
698 fSignal = TRUE; /* Signal in any case. */
699 waitRes = ProcessWaitResult_Timeout;
700
701 mData.mStatus = ProcessStatus_TimedOutKilled;
702 break;
703 }
704
705 case PROC_STS_TOA:
706 {
707 fSignal = TRUE; /* Signal in any case. */
708 waitRes = ProcessWaitResult_Timeout;
709
710 mData.mStatus = ProcessStatus_TimedOutAbnormally;
711 break;
712 }
713
714 case PROC_STS_DWN:
715 {
716 fSignal = TRUE; /* Signal in any case. */
717 /* Do we need to report termination? */
718 if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
719 waitRes = ProcessWaitResult_Status;
720 else
721 waitRes = ProcessWaitResult_Terminate;
722
723 mData.mStatus = ProcessStatus_Down;
724 break;
725 }
726
727 case PROC_STS_ERROR:
728 {
729 fSignal = TRUE; /* Signal in any case. */
730 waitRes = ProcessWaitResult_Error;
731
732 mData.mStatus = ProcessStatus_Error;
733
734 Utf8Str strError = Utf8StrFmt(tr("Guest process \"%s\" could not be started: "), mData.mProcess.mCommand.c_str());
735
736 /* Note: It's not required that the process has been started before. */
737 if (mData.mPID)
738 {
739 strError += Utf8StrFmt(tr("Error vrc=%Rrc occured (PID %RU32)"), vrc, mData.mPID);
740 }
741 else
742 {
743 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
744 switch (pData->u32Flags) /* pData->u32Flags contains the IPRT error code from guest side. */
745 {
746 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
747 strError += Utf8StrFmt(tr("The specified file was not found on guest"));
748 break;
749
750 case VERR_PATH_NOT_FOUND:
751 strError += Utf8StrFmt(tr("Could not resolve path to specified file was not found on guest"));
752 break;
753
754 case VERR_BAD_EXE_FORMAT:
755 strError += Utf8StrFmt(tr("The specified file is not an executable format on guest"));
756 break;
757
758 case VERR_AUTHENTICATION_FAILURE:
759 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
760 break;
761
762 case VERR_INVALID_NAME:
763 strError += Utf8StrFmt(tr("The specified file is an invalid name"));
764 break;
765
766 case VERR_TIMEOUT:
767 strError += Utf8StrFmt(tr("The guest did not respond within time"));
768 break;
769
770 case VERR_CANCELLED:
771 strError += Utf8StrFmt(tr("The execution operation was canceled"));
772 break;
773
774 case VERR_PERMISSION_DENIED:
775 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
776 break;
777
778 case VERR_MAX_PROCS_REACHED:
779 strError += Utf8StrFmt(tr("Maximum number of parallel guest processes has been reached"));
780 break;
781
782 default:
783 strError += Utf8StrFmt(tr("Reported error %Rrc"), pData->u32Flags);
784 break;
785 }
786 }
787
788 vrc = setErrorInternal(pData->u32Flags, strError);
789 AssertRC(vrc);
790 break;
791 }
792
793 case PROC_STS_UNDEFINED:
794 default:
795 {
796 /* Silently skip this request. */
797 fSignal = TRUE; /* Signal in any case. */
798 waitRes = ProcessWaitResult_Status;
799
800 mData.mStatus = ProcessStatus_Undefined;
801 break;
802 }
803 }
804
805 LogFlowThisFunc(("Got vrc=%Rrc, waitResult=%d\n", vrc, waitRes));
806
807 /*
808 * Now do the signalling stuff.
809 */
810 if (pCallback)
811 vrc = pCallback->Signal();
812
813 if (fSignal)
814 {
815 int rc2 = signalWaiters(waitRes);
816 if (RT_SUCCESS(vrc))
817 vrc = rc2;
818 }
819
820 LogFlowFuncLeaveRC(vrc);
821 return vrc;
822}
823
824int GuestProcess::onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECOUT pData)
825{
826 /* pCallback is optional. */
827 AssertPtrReturn(pData, VERR_INVALID_POINTER);
828
829 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, pCallback=%p, pData=%p\n",
830 mData.mPID, pData->u32HandleId, pData->u32Flags, pData->pvData, pData->cbData, pCallback, pData));
831
832 int vrc = checkPID(pData->u32PID);
833 if (RT_FAILURE(vrc))
834 return vrc;
835
836 /* First, signal callback in every case (if available). */
837 if (pCallback)
838 {
839 vrc = pCallback->SetData(pData, sizeof(CALLBACKDATAEXECOUT));
840
841 int rc2 = pCallback->Signal();
842 if (RT_SUCCESS(vrc))
843 vrc = rc2;
844 }
845
846 /* Then do the WaitFor signalling stuff. */
847 BOOL fSignal = FALSE;
848 uint32_t uWaitFlags = mData.mWaitEvent
849 ? mData.mWaitEvent->GetWaitFlags() : 0;
850
851 if ( (uWaitFlags & ProcessWaitForFlag_StdOut)
852 || (uWaitFlags & ProcessWaitForFlag_StdErr))
853 {
854 fSignal = TRUE;
855 }
856 else if ( (uWaitFlags & ProcessWaitForFlag_StdOut)
857 && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT))
858 {
859 fSignal = TRUE;
860 }
861 else if ( (uWaitFlags & ProcessWaitForFlag_StdErr)
862 && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDERR))
863 {
864 fSignal = TRUE;
865 }
866
867 if (fSignal)
868 {
869 int rc2;
870 if (pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT)
871 rc2 = signalWaiters(ProcessWaitResult_StdOut);
872 else
873 rc2 = signalWaiters(ProcessWaitResult_StdErr);
874 if (RT_SUCCESS(vrc))
875 vrc = rc2;
876 }
877
878 LogFlowFuncLeaveRC(vrc);
879 return vrc;
880}
881
882int GuestProcess::readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
883 void *pvData, size_t cbData, size_t *pcbRead)
884{
885 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32\n",
886 mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData));
887 AssertReturn(uSize, VERR_INVALID_PARAMETER);
888 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
889 AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
890 /* pcbRead is optional. */
891
892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
893
894 if (mData.mStatus != ProcessStatus_Started)
895 {
896 if (pcbRead)
897 *pcbRead = 0;
898 return VINF_SUCCESS; /* Nothing to read anymore. */
899 }
900
901 uint32_t uContextID = 0;
902 GuestCtrlCallback *pCallbackRead = new GuestCtrlCallback();
903 if (!pCallbackRead)
904 return VERR_NO_MEMORY;
905
906 /* Create callback and add it to the map. */
907 int vrc = pCallbackRead->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT);
908 if (RT_SUCCESS(vrc))
909 vrc = callbackAdd(pCallbackRead, &uContextID);
910
911 alock.release(); /* Drop the write lock again. */
912
913 if (RT_SUCCESS(vrc))
914 {
915 VBOXHGCMSVCPARM paParms[5];
916
917 int i = 0;
918 paParms[i++].setUInt32(uContextID);
919 paParms[i++].setUInt32(mData.mPID);
920 paParms[i++].setUInt32(uHandle);
921 paParms[i++].setUInt32(0 /* Flags, none set yet. */);
922
923 vrc = sendCommand(HOST_EXEC_GET_OUTPUT, i, paParms);
924 }
925
926 if (RT_SUCCESS(vrc))
927 {
928 /*
929 * Let's wait for the process being started.
930 * Note: Be sure not keeping a AutoRead/WriteLock here.
931 */
932 LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
933 vrc = pCallbackRead->Wait(uTimeoutMS);
934 if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
935 {
936 vrc = pCallbackRead->GetResultCode();
937 LogFlowThisFunc(("Callback returned vrc=%Rrc, cbData=%RU32\n", vrc, pCallbackRead->GetDataSize()));
938
939 if (RT_SUCCESS(vrc))
940 {
941 Assert(pCallbackRead->GetDataSize() == sizeof(CALLBACKDATAEXECOUT));
942 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)pCallbackRead->GetDataRaw();
943 AssertPtr(pData);
944
945 size_t cbRead = pData->cbData;
946 if (cbRead)
947 {
948 Assert(cbData >= cbRead);
949 memcpy(pvData, pData->pvData, cbRead);
950 }
951
952 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
953
954 if (pcbRead)
955 *pcbRead = cbRead;
956 }
957 }
958 else
959 vrc = VERR_TIMEOUT;
960 }
961
962 alock.acquire();
963
964 AssertPtr(pCallbackRead);
965 int rc2 = callbackRemove(uContextID);
966 if (RT_SUCCESS(vrc))
967 vrc = rc2;
968
969 LogFlowFuncLeaveRC(vrc);
970 return vrc;
971}
972
973int GuestProcess::sendCommand(uint32_t uFunction,
974 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
975{
976 LogFlowThisFuncEnter();
977
978 ComObjPtr<Console> pConsole = mData.mConsole;
979 Assert(!pConsole.isNull());
980
981 /* Forward the information to the VMM device. */
982 VMMDev *pVMMDev = pConsole->getVMMDev();
983 AssertPtr(pVMMDev);
984
985 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
986 int vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", uFunction, uParms, paParms);
987 if (RT_FAILURE(vrc))
988 {
989 int rc2;
990 if (vrc == VERR_INVALID_VM_HANDLE)
991 rc2 = setErrorInternal(vrc, tr("VMM device is not available (is the VM running?)"));
992 else if (vrc == VERR_NOT_FOUND)
993 rc2 = setErrorInternal(vrc, tr("The guest execution service is not ready (yet)"));
994 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
995 rc2 = setErrorInternal(vrc, tr("The guest execution service is not available"));
996 else
997 rc2 = setErrorInternal(vrc, Utf8StrFmt(tr("The HGCM call failed with error %Rrc"), vrc));
998 AssertRC(rc2);
999 }
1000
1001 LogFlowFuncLeaveRC(vrc);
1002 return vrc;
1003}
1004
1005/* Does not do locking; caller is responsible for that! */
1006int GuestProcess::setErrorInternal(int vrc, const Utf8Str &strMessage)
1007{
1008 LogFlowThisFunc(("vrc=%Rrc, strMsg=%s\n", vrc, strMessage.c_str()));
1009
1010 Assert(RT_FAILURE(vrc));
1011 Assert(!strMessage.isEmpty());
1012
1013#ifdef DEBUG
1014 /* Do not allow overwriting an already set error. If this happens
1015 * this means we forgot some error checking/locking somewhere. */
1016 Assert(RT_SUCCESS(mData.mRC));
1017 Assert(mData.mErrorMsg.isEmpty());
1018#endif
1019
1020 mData.mStatus = ProcessStatus_Error;
1021 mData.mRC = vrc;
1022 mData.mErrorMsg = strMessage;
1023
1024 int rc2 = signalWaiters(ProcessWaitResult_Error);
1025 LogFlowFuncLeaveRC(rc2);
1026 return rc2;
1027}
1028
1029HRESULT GuestProcess::setErrorExternal(void)
1030{
1031 return RT_SUCCESS(mData.mRC)
1032 ? S_OK : setError(VBOX_E_IPRT_ERROR, "%s", mData.mErrorMsg.c_str());
1033}
1034
1035int GuestProcess::signalWaiters(ProcessWaitResult_T enmWaitResult)
1036{
1037 LogFlowThisFunc(("enmWaitResult=%d, mWaitCount=%RU32, mWaitEvent=%p\n",
1038 enmWaitResult, mData.mWaitCount, mData.mWaitEvent));
1039
1040 /* Note: No write locking here -- already done in the caller. */
1041
1042 int vrc = VINF_SUCCESS;
1043 if (mData.mWaitEvent)
1044 vrc = mData.mWaitEvent->Signal(enmWaitResult);
1045 LogFlowFuncLeaveRC(vrc);
1046 return vrc;
1047}
1048
1049int GuestProcess::startProcess(void)
1050{
1051 LogFlowThisFunc(("aCmd=%s, aTimeoutMS=%RU32, fFlags=%x\n",
1052 mData.mProcess.mCommand.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags));
1053
1054 /* Wait until the caller function (if kicked off by a thread)
1055 * has returned and continue operation. */
1056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1057
1058 int vrc;
1059 uint32_t uContextID = 0;
1060 GuestCtrlCallback *pCallbackStart = new GuestCtrlCallback();
1061 if (!pCallbackStart)
1062 return VERR_NO_MEMORY;
1063
1064 mData.mStatus = ProcessStatus_Starting;
1065
1066 /* Create callback and add it to the map. */
1067 vrc = pCallbackStart->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START);
1068 if (RT_SUCCESS(vrc))
1069 vrc = callbackAdd(pCallbackStart, &uContextID);
1070
1071 if (RT_SUCCESS(vrc))
1072 {
1073 GuestSession *pSession = mData.mParent;
1074 AssertPtr(pSession);
1075
1076 const GuestCredentials &sessionCreds = pSession->getCredentials();
1077
1078 /* Prepare arguments. */
1079 char *pszArgs = NULL;
1080 size_t cArgs = mData.mProcess.mArguments.size();
1081 if (cArgs)
1082 {
1083 char **papszArgv = (char**)RTMemAlloc(sizeof(char*) * (cArgs + 1));
1084 AssertReturn(papszArgv, VERR_NO_MEMORY);
1085 for (size_t i = 0; RT_SUCCESS(vrc) && i < cArgs; i++)
1086 vrc = RTStrDupEx(&papszArgv[i], mData.mProcess.mArguments[i].c_str());
1087 papszArgv[cArgs] = NULL;
1088
1089 if (RT_SUCCESS(vrc))
1090 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1091 }
1092 size_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1093
1094 /* Prepare environment. */
1095 void *pvEnv = NULL;
1096 size_t cbEnv = 0;
1097 vrc = mData.mProcess.mEnvironment.BuildEnvironmentBlock(&pvEnv, &cbEnv, NULL /* cEnv */);
1098
1099 if (RT_SUCCESS(vrc))
1100 {
1101 /* Prepare HGCM call. */
1102 VBOXHGCMSVCPARM paParms[15];
1103 int i = 0;
1104 paParms[i++].setUInt32(uContextID);
1105 paParms[i++].setPointer((void*)mData.mProcess.mCommand.c_str(),
1106 (ULONG)mData.mProcess.mCommand.length() + 1);
1107 paParms[i++].setUInt32(mData.mProcess.mFlags);
1108 paParms[i++].setUInt32(mData.mProcess.mArguments.size());
1109 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1110 paParms[i++].setUInt32(mData.mProcess.mEnvironment.Size());
1111 paParms[i++].setUInt32(cbEnv);
1112 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1113 paParms[i++].setPointer((void*)sessionCreds.mUser.c_str(), (ULONG)sessionCreds.mUser.length() + 1);
1114 paParms[i++].setPointer((void*)sessionCreds.mPassword.c_str(), (ULONG)sessionCreds.mPassword.length() + 1);
1115 /** @todo New command needs the domain as well! */
1116
1117 /*
1118 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1119 * until the process was started - the process itself then gets an infinite timeout for execution.
1120 * This is handy when we want to start a process inside a worker thread within a certain timeout
1121 * but let the started process perform lengthly operations then.
1122 */
1123 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1124 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1125 else
1126 paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
1127
1128 /* Note: Don't hold the write lock in here, because setErrorInternal */
1129 vrc = sendCommand(HOST_EXEC_CMD, i, paParms);
1130 }
1131
1132 GuestEnvironment::FreeEnvironmentBlock(pvEnv);
1133 if (pszArgs)
1134 RTStrFree(pszArgs);
1135
1136 uint32_t uTimeoutMS = mData.mProcess.mTimeoutMS;
1137
1138 /* Drop the write lock again before waiting. */
1139 alock.release();
1140
1141 if (RT_SUCCESS(vrc))
1142 {
1143 /*
1144 * Let's wait for the process being started.
1145 * Note: Be sure not keeping a AutoRead/WriteLock here.
1146 */
1147 LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
1148 vrc = pCallbackStart->Wait(uTimeoutMS);
1149 if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
1150 {
1151 vrc = pCallbackStart->GetResultCode();
1152 LogFlowThisFunc(("Callback returned vrc=%Rrc\n", vrc));
1153 }
1154 else
1155 vrc = VERR_TIMEOUT;
1156 }
1157
1158 AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
1159
1160 AssertPtr(pCallbackStart);
1161 int rc2 = callbackRemove(uContextID);
1162 if (RT_SUCCESS(vrc))
1163 vrc = rc2;
1164 }
1165
1166 LogFlowFuncLeaveRC(vrc);
1167 return vrc;
1168}
1169
1170int GuestProcess::startProcessAsync(void)
1171{
1172 LogFlowThisFuncEnter();
1173
1174 int vrc;
1175
1176 try
1177 {
1178 /* Asynchronously start the process on the guest by kicking off a
1179 * worker thread. */
1180 std::auto_ptr<GuestProcessStartTask> pTask(new GuestProcessStartTask(this));
1181 AssertReturn(pTask->isOk(), pTask->rc());
1182
1183 vrc = RTThreadCreate(NULL, GuestProcess::startProcessThread,
1184 (void *)pTask.get(), 0,
1185 RTTHREADTYPE_MAIN_WORKER, 0,
1186 "gctlPrcStart");
1187 if (RT_SUCCESS(vrc))
1188 {
1189 /* pTask is now owned by startProcessThread(), so release it. */
1190 pTask.release();
1191 }
1192 }
1193 catch(std::bad_alloc &)
1194 {
1195 vrc = VERR_NO_MEMORY;
1196 }
1197
1198 LogFlowFuncLeaveRC(vrc);
1199 return vrc;
1200}
1201
1202/* static */
1203DECLCALLBACK(int) GuestProcess::startProcessThread(RTTHREAD Thread, void *pvUser)
1204{
1205 LogFlowFunc(("pvUser=%p\n", pvUser));
1206
1207 std::auto_ptr<GuestProcessStartTask> pTask(static_cast<GuestProcessStartTask*>(pvUser));
1208 AssertPtr(pTask.get());
1209
1210 const ComObjPtr<GuestProcess> pProcess(pTask->Process());
1211 Assert(!pProcess.isNull());
1212
1213 AutoCaller autoCaller(pProcess);
1214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1215
1216 int vrc = pProcess->startProcess();
1217 if (RT_FAILURE(vrc))
1218 {
1219 /** @todo What now? */
1220 }
1221
1222 LogFlowFuncLeaveRC(vrc);
1223 return vrc;
1224}
1225
1226int GuestProcess::terminateProcess(void)
1227{
1228 LogFlowThisFuncEnter();
1229
1230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 if (mData.mParent->getProtocolVersion() < 2)
1233 return VERR_NOT_SUPPORTED;
1234
1235 LogFlowThisFuncLeave();
1236 return VERR_NOT_IMPLEMENTED;
1237}
1238
1239int GuestProcess::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestProcessWaitResult &waitRes)
1240{
1241 LogFlowThisFuncEnter();
1242
1243 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1244
1245 LogFlowThisFunc(("fWaitFlags=%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p\n",
1246 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent));
1247
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 ProcessStatus_T curStatus = mData.mStatus;
1251
1252 /* Did some error occur before? Then skip waiting and return. */
1253 if (curStatus == ProcessStatus_Error)
1254 {
1255 waitRes.mResult = ProcessWaitResult_Error;
1256 return VINF_SUCCESS;
1257 }
1258
1259 waitRes.mResult = ProcessWaitResult_None;
1260 waitRes.mRC = VINF_SUCCESS;
1261
1262 if ( (fWaitFlags & ProcessWaitForFlag_Terminate)
1263 || (fWaitFlags & ProcessWaitForFlag_StdIn)
1264 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1265 || (fWaitFlags & ProcessWaitForFlag_StdErr))
1266 {
1267 switch (mData.mStatus)
1268 {
1269 case ProcessStatus_TerminatedNormally:
1270 case ProcessStatus_TerminatedSignal:
1271 case ProcessStatus_TerminatedAbnormally:
1272 case ProcessStatus_Down:
1273 waitRes.mResult = ProcessWaitResult_Terminate;
1274 waitRes.mRC = mData.mRC;
1275 break;
1276
1277 case ProcessStatus_TimedOutKilled:
1278 case ProcessStatus_TimedOutAbnormally:
1279 waitRes.mResult = ProcessWaitResult_Timeout;
1280 waitRes.mRC = mData.mRC;
1281 break;
1282
1283 case ProcessStatus_Error:
1284 waitRes.mResult = ProcessWaitResult_Error;
1285 waitRes.mRC = mData.mRC;
1286 break;
1287
1288 case ProcessStatus_Started:
1289 {
1290 /* Filter out waits which are *not* supported using
1291 * older guest control Guest Additions. */
1292 if (mData.mParent->getProtocolVersion() < 2)
1293 {
1294 /* We don't support waiting for stdin, out + err,
1295 * just skip waiting then. */
1296 if ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1297 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1298 || (fWaitFlags & ProcessWaitForFlag_StdErr))
1299 {
1300 /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
1301 waitRes.mResult = ProcessWaitResult_WaitFlagNotSupported;
1302 }
1303 }
1304
1305 break;
1306 }
1307
1308 case ProcessStatus_Undefined:
1309 case ProcessStatus_Starting:
1310 /* Do the waiting below. */
1311 break;
1312
1313 default:
1314 AssertMsgFailed(("Unhandled process status %ld\n", mData.mStatus));
1315 return VERR_NOT_IMPLEMENTED;
1316 }
1317 }
1318 else if (fWaitFlags & ProcessWaitForFlag_Start)
1319 {
1320 switch (mData.mStatus)
1321 {
1322 case ProcessStatus_Started:
1323 case ProcessStatus_Paused:
1324 case ProcessStatus_Terminating:
1325 case ProcessStatus_TerminatedNormally:
1326 case ProcessStatus_TerminatedSignal:
1327 case ProcessStatus_TerminatedAbnormally:
1328 case ProcessStatus_Down:
1329 waitRes.mResult = ProcessWaitResult_Start;
1330 break;
1331
1332 case ProcessStatus_Error:
1333 waitRes.mResult = ProcessWaitResult_Error;
1334 waitRes.mRC = mData.mRC;
1335 break;
1336
1337 case ProcessStatus_TimedOutKilled:
1338 case ProcessStatus_TimedOutAbnormally:
1339 waitRes.mResult = ProcessWaitResult_Timeout;
1340 waitRes.mRC = mData.mRC;
1341 break;
1342
1343 case ProcessStatus_Undefined:
1344 case ProcessStatus_Starting:
1345 /* Do the waiting below. */
1346 break;
1347
1348 default:
1349 AssertMsgFailed(("Unhandled process status %ld\n", mData.mStatus));
1350 return VERR_NOT_IMPLEMENTED;
1351 }
1352 }
1353
1354 LogFlowThisFunc(("waitResult=%ld, waitRC=%Rrc\n", waitRes.mResult, waitRes.mRC));
1355
1356 /* No waiting needed? Return immediately. */
1357 if (waitRes.mResult != ProcessWaitResult_None)
1358 return VINF_SUCCESS;
1359
1360 if (mData.mWaitCount > 0)
1361 return VERR_ALREADY_EXISTS;
1362 mData.mWaitCount++;
1363
1364 Assert(mData.mWaitEvent == NULL);
1365 mData.mWaitEvent = new GuestProcessEvent(fWaitFlags);
1366 AssertPtrReturn(mData.mWaitEvent, VERR_NO_MEMORY);
1367
1368 alock.release(); /* Release lock before waiting. */
1369
1370 int vrc = mData.mWaitEvent->Wait(uTimeoutMS);
1371 if (RT_SUCCESS(vrc))
1372 {
1373 waitRes = mData.mWaitEvent->GetResult();
1374 }
1375 else if (vrc == VERR_TIMEOUT)
1376 {
1377 waitRes.mRC = VINF_SUCCESS;
1378 waitRes.mResult = ProcessWaitResult_Timeout;
1379
1380 vrc = VINF_SUCCESS;
1381 }
1382
1383 alock.acquire(); /* Get the lock again. */
1384
1385 /* Note: The caller always is responsible of deleting the
1386 * stuff it created before. See close() for more information. */
1387 delete mData.mWaitEvent;
1388 mData.mWaitEvent = NULL;
1389
1390 mData.mWaitCount--;
1391
1392 LogFlowFuncLeaveRC(vrc);
1393 return vrc;
1394}
1395
1396int GuestProcess::waitForStart(uint32_t uTimeoutMS)
1397{
1398 LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
1399
1400 int vrc = VINF_SUCCESS;
1401
1402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1403 if (mData.mStatus != ProcessStatus_Started)
1404 {
1405 alock.release();
1406
1407 GuestProcessWaitResult waitRes;
1408 vrc = waitFor(ProcessWaitForFlag_Start, uTimeoutMS, waitRes);
1409 if ( RT_FAILURE(vrc)
1410 || waitRes.mResult == ProcessWaitResult_Start)
1411 {
1412 if (RT_SUCCESS(vrc))
1413 vrc = waitRes.mRC;
1414 }
1415 /** @todo More error handling needed. */
1416 }
1417
1418 LogFlowFuncLeaveRC(vrc);
1419 return vrc;
1420}
1421
1422int GuestProcess::writeData(uint32_t uHandle, uint32_t uFlags,
1423 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten)
1424{
1425 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p\n",
1426 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten));
1427 /* All is optional. There can be 0 byte writes. */
1428
1429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1430
1431 if (mData.mStatus != ProcessStatus_Started)
1432 return VINF_SUCCESS; /* Not available for writing (anymore). */
1433
1434 uint32_t uContextID = 0;
1435 GuestCtrlCallback *pCallbackWrite = new GuestCtrlCallback();
1436 if (!pCallbackWrite)
1437 return VERR_NO_MEMORY;
1438
1439 /* Create callback and add it to the map. */
1440 int vrc = pCallbackWrite->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS);
1441 if (RT_SUCCESS(vrc))
1442 vrc = callbackAdd(pCallbackWrite, &uContextID);
1443
1444 alock.release(); /* Drop the write lock again. */
1445
1446 if (RT_SUCCESS(vrc))
1447 {
1448 VBOXHGCMSVCPARM paParms[5];
1449
1450 int i = 0;
1451 paParms[i++].setUInt32(uContextID);
1452 paParms[i++].setUInt32(mData.mPID);
1453 paParms[i++].setUInt32(uFlags);
1454 paParms[i++].setPointer(pvData, cbData);
1455 paParms[i++].setUInt32(cbData);
1456
1457 vrc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
1458 }
1459
1460 if (RT_SUCCESS(vrc))
1461 {
1462 /*
1463 * Let's wait for the process being started.
1464 * Note: Be sure not keeping a AutoRead/WriteLock here.
1465 */
1466 LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
1467 vrc = pCallbackWrite->Wait(uTimeoutMS);
1468 if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
1469 {
1470 vrc = pCallbackWrite->GetResultCode();
1471 LogFlowThisFunc(("Callback returned vrc=%Rrc, cbData=%RU32\n", vrc, pCallbackWrite->GetDataSize()));
1472
1473 if (RT_SUCCESS(vrc))
1474 {
1475 Assert(pCallbackWrite->GetDataSize() == sizeof(CALLBACKDATAEXECINSTATUS));
1476 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)pCallbackWrite->GetDataRaw();
1477 AssertPtr(pData);
1478
1479 uint32_t cbWritten = 0;
1480 switch (pData->u32Status)
1481 {
1482 case INPUT_STS_WRITTEN:
1483 cbWritten = pData->cbProcessed;
1484 break;
1485
1486 case INPUT_STS_ERROR:
1487 vrc = pData->u32Flags; /** @todo Fix int vs. uint32_t! */
1488 break;
1489
1490 case INPUT_STS_TERMINATED:
1491 vrc = VERR_CANCELLED;
1492 break;
1493
1494 case INPUT_STS_OVERFLOW:
1495 vrc = VERR_BUFFER_OVERFLOW;
1496 break;
1497
1498 default:
1499 /* Silently skip unknown errors. */
1500 break;
1501 }
1502
1503 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
1504
1505 if (puWritten)
1506 *puWritten = cbWritten;
1507 }
1508 }
1509 else
1510 vrc = VERR_TIMEOUT;
1511 }
1512
1513 alock.acquire();
1514
1515 AssertPtr(pCallbackWrite);
1516 int rc2 = callbackRemove(uContextID);
1517 if (RT_SUCCESS(vrc))
1518 vrc = rc2;
1519
1520 LogFlowFuncLeaveRC(vrc);
1521 return vrc;
1522}
1523
1524// implementation of public methods
1525/////////////////////////////////////////////////////////////////////////////
1526
1527STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
1528{
1529#ifndef VBOX_WITH_GUEST_CONTROL
1530 ReturnComNotImplemented();
1531#else
1532 if (aToRead == 0)
1533 return setError(E_INVALIDARG, tr("The size to read is zero"));
1534 CheckComArgOutSafeArrayPointerValid(aData);
1535
1536 AutoCaller autoCaller(this);
1537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1538
1539 com::SafeArray<BYTE> data((size_t)aToRead);
1540 Assert(data.size() >= aToRead);
1541
1542 size_t cbRead;
1543 int vrc = readData(aHandle, aToRead, aTimeoutMS, data.raw(), aToRead, &cbRead);
1544 if (RT_SUCCESS(vrc))
1545 {
1546 if (data.size() != cbRead)
1547 data.resize(cbRead);
1548 data.detachTo(ComSafeArrayOutArg(aData));
1549 }
1550
1551 LogFlowThisFunc(("readData returned %Rrc, cbRead=%RU64\n", vrc, cbRead));
1552
1553 /** @todo Do setError() here. */
1554 HRESULT hr = RT_SUCCESS(vrc) ? S_OK : VBOX_E_IPRT_ERROR;
1555 LogFlowFuncLeaveRC(vrc);
1556
1557 return hr;
1558#endif /* VBOX_WITH_GUEST_CONTROL */
1559}
1560
1561STDMETHODIMP GuestProcess::Terminate(void)
1562{
1563#ifndef VBOX_WITH_GUEST_CONTROL
1564 ReturnComNotImplemented();
1565#else
1566 LogFlowThisFuncEnter();
1567
1568 AutoCaller autoCaller(this);
1569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1570
1571 HRESULT hr = S_OK;
1572
1573 int vrc = terminateProcess();
1574 if (RT_FAILURE(vrc))
1575 {
1576 switch (vrc)
1577 {
1578 case VERR_NOT_IMPLEMENTED:
1579 ReturnComNotImplemented();
1580 break; /* Never reached. */
1581
1582 case VERR_NOT_SUPPORTED:
1583 hr = setError(VBOX_E_IPRT_ERROR,
1584 tr("Terminating process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
1585 mData.mProcess.mCommand.c_str(), mData.mPID);
1586 break;
1587
1588 default:
1589 hr = setError(VBOX_E_IPRT_ERROR,
1590 tr("Terminating process \"%s\" (PID %RU32) failed: %Rrc"),
1591 mData.mProcess.mCommand.c_str(), mData.mPID, vrc);
1592 break;
1593 }
1594 }
1595
1596 AssertPtr(mData.mParent);
1597 mData.mParent->processRemoveFromList(this);
1598
1599 /*
1600 * Release autocaller before calling uninit.
1601 */
1602 autoCaller.release();
1603
1604 uninit();
1605
1606 LogFlowFuncLeaveRC(vrc);
1607 return hr;
1608#endif /* VBOX_WITH_GUEST_CONTROL */
1609}
1610
1611STDMETHODIMP GuestProcess::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
1612{
1613#ifndef VBOX_WITH_GUEST_CONTROL
1614 ReturnComNotImplemented();
1615#else
1616 LogFlowThisFuncEnter();
1617
1618 CheckComArgOutPointerValid(aReason);
1619
1620 AutoCaller autoCaller(this);
1621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1622
1623 /*
1624 * Note: Do not hold any locks here while waiting!
1625 */
1626 HRESULT hr;
1627
1628 GuestProcessWaitResult waitRes;
1629 int vrc = waitFor(aWaitFlags, aTimeoutMS, waitRes);
1630 if (RT_SUCCESS(vrc))
1631 {
1632 *aReason = waitRes.mResult;
1633 hr = setErrorExternal();
1634 }
1635 else
1636 {
1637 hr = setError(VBOX_E_IPRT_ERROR,
1638 tr("Waiting for process \"%s\" (PID %RU32) failed with vrc=%Rrc"),
1639 mData.mProcess.mCommand.c_str(), mData.mPID, vrc);
1640 }
1641 LogFlowFuncLeaveRC(vrc);
1642 return hr;
1643#endif /* VBOX_WITH_GUEST_CONTROL */
1644}
1645
1646STDMETHODIMP GuestProcess::WaitForArray(ComSafeArrayIn(ProcessWaitForFlag_T, aFlags), ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
1647{
1648#ifndef VBOX_WITH_GUEST_CONTROL
1649 ReturnComNotImplemented();
1650#else
1651 LogFlowThisFuncEnter();
1652
1653 CheckComArgOutPointerValid(aReason);
1654
1655 AutoCaller autoCaller(this);
1656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1657
1658 /*
1659 * Note: Do not hold any locks here while waiting!
1660 */
1661 uint32_t fWaitFor = ProcessWaitForFlag_None;
1662 com::SafeArray<ProcessWaitForFlag_T> flags(ComSafeArrayInArg(aFlags));
1663 for (size_t i = 0; i < flags.size(); i++)
1664 fWaitFor |= flags[i];
1665
1666 return WaitFor(fWaitFor, aTimeoutMS, aReason);
1667#endif /* VBOX_WITH_GUEST_CONTROL */
1668}
1669
1670STDMETHODIMP GuestProcess::Write(ULONG aHandle, ULONG aFlags,
1671 ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1672{
1673#ifndef VBOX_WITH_GUEST_CONTROL
1674 ReturnComNotImplemented();
1675#else
1676 LogFlowThisFuncEnter();
1677
1678 CheckComArgOutPointerValid(aWritten);
1679
1680 AutoCaller autoCaller(this);
1681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1682
1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1684
1685 com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
1686 int vrc = writeData(aHandle, aFlags, data.raw(), data.size(), aTimeoutMS, (uint32_t*)aWritten);
1687
1688 LogFlowThisFunc(("writeData returned %Rrc, aWritten=%RU32\n", vrc, aWritten));
1689
1690 /** @todo Do setError() here. */
1691 HRESULT hr = RT_SUCCESS(vrc) ? S_OK : VBOX_E_IPRT_ERROR;
1692 LogFlowFuncLeaveRC(vrc);
1693
1694 return hr;
1695#endif /* VBOX_WITH_GUEST_CONTROL */
1696}
1697
1698STDMETHODIMP GuestProcess::WriteArray(ULONG aHandle, ComSafeArrayIn(ProcessInputFlag_T, aFlags),
1699 ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1700{
1701#ifndef VBOX_WITH_GUEST_CONTROL
1702 ReturnComNotImplemented();
1703#else
1704 LogFlowThisFuncEnter();
1705
1706 CheckComArgOutPointerValid(aWritten);
1707
1708 AutoCaller autoCaller(this);
1709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1710
1711 /*
1712 * Note: Do not hold any locks here while writing!
1713 */
1714 ULONG fWrite = ProcessInputFlag_None;
1715 com::SafeArray<ProcessInputFlag_T> flags(ComSafeArrayInArg(aFlags));
1716 for (size_t i = 0; i < flags.size(); i++)
1717 fWrite |= flags[i];
1718
1719 return Write(aHandle, fWrite, ComSafeArrayInArg(aData), aTimeoutMS, aWritten);
1720#endif /* VBOX_WITH_GUEST_CONTROL */
1721}
1722
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