VirtualBox

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

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

Build fix.

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