VirtualBox

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

Last change on this file since 42657 was 42634, checked in by vboxsync, 13 years ago

Guest Control 2.0: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.2 KB
Line 
1
2/* $Id: GuestProcessImpl.cpp 42634 2012-08-06 17:29:38Z 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
500inline bool GuestProcess::isAlive(void)
501{
502 return ( mData.mStatus == ProcessStatus_Started
503 || mData.mStatus == ProcessStatus_Paused
504 || mData.mStatus == ProcessStatus_Terminating);
505}
506
507/* Do not hold any locks here because the lock validator will be unhappy
508 * when being in uninit(). */
509void GuestProcess::close(void)
510{
511 LogFlowThisFuncEnter();
512
513 uninit();
514
515 LogFlowThisFuncLeave();
516}
517
518bool GuestProcess::isReady(void)
519{
520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
521
522 if (mData.mStatus == ProcessStatus_Started)
523 {
524 Assert(mData.mPID); /* PID must not be 0. */
525 return true;
526 }
527
528 return false;
529}
530
531int GuestProcess::onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData)
532{
533 /* pCallback is optional. */
534 AssertPtrReturn(pData, VERR_INVALID_POINTER);
535
536 LogFlowThisFunc(("uPID=%RU32, pCallback=%p, pData=%p\n", mData.mPID, pCallback, pData));
537
538 mData.mStatus = ProcessStatus_Down;
539
540 /* First, signal callback in every case. */
541 if (pCallback)
542 pCallback->Signal();
543
544 /* Do we need to report a termination? */
545 ProcessWaitResult_T waitRes;
546 if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
547 waitRes = ProcessWaitResult_Status; /* No, just report a status. */
548 else
549 waitRes = ProcessWaitResult_Terminate;
550
551 /* Signal in any case. */
552 int rc = signalWaiters(waitRes);
553 AssertRC(rc);
554
555 LogFlowFuncLeaveRC(rc);
556 return rc;
557}
558
559int GuestProcess::onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData)
560{
561 /* pCallback is optional. */
562 AssertPtrReturn(pData, VERR_INVALID_POINTER);
563
564 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, cbProcessed=%RU32, pCallback=%p, pData=%p\n",
565 mData.mPID, pData->u32Status, pData->u32Flags, pData->cbProcessed, pCallback, pData));
566
567 int rc = VINF_SUCCESS;
568
569 /* First, signal callback in every case (if available). */
570 if (pCallback)
571 {
572 rc = pCallback->SetData(pData, sizeof(CALLBACKDATAEXECINSTATUS));
573
574 int rc2 = pCallback->Signal();
575 if (RT_SUCCESS(rc))
576 rc = rc2;
577 }
578
579 /* Then do the WaitFor signalling stuff. */
580 uint32_t uWaitFlags = mData.mWaitEvent
581 ? mData.mWaitEvent->GetWaitFlags() : 0;
582 if (uWaitFlags & ProcessWaitForFlag_StdIn)
583 {
584 int rc2 = signalWaiters(ProcessWaitResult_StdIn);
585 if (RT_SUCCESS(rc))
586 rc = rc2;
587 }
588
589 LogFlowFuncLeaveRC(rc);
590 return rc;
591}
592
593int GuestProcess::onProcessNotifyIO(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
594{
595 /* pCallback is optional. */
596 AssertPtrReturn(pData, VERR_INVALID_POINTER);
597
598 return 0;
599}
600
601int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
602{
603 /* pCallback is optional. */
604 AssertPtrReturn(pData, VERR_INVALID_POINTER);
605
606 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, pCallback=%p, pData=%p\n",
607 pData->u32PID, pData->u32Status, pData->u32Flags, pCallback, pData));
608
609 int rc = VINF_SUCCESS;
610
611 /* Get data from the callback payload. */
612 if (mData.mPID)
613 Assert(mData.mPID == pData->u32PID);
614
615 BOOL fSignal = FALSE;
616 ProcessWaitResult_T waitRes;
617 uint32_t uWaitFlags = mData.mWaitEvent
618 ? mData.mWaitEvent->GetWaitFlags() : 0;
619 switch (pData->u32Status)
620 {
621 case PROC_STS_STARTED:
622 {
623 fSignal = (uWaitFlags & ProcessWaitForFlag_Start);
624 waitRes = ProcessWaitResult_Status;
625
626 mData.mStatus = ProcessStatus_Started;
627 mData.mPID = pData->u32PID;
628 break;
629 }
630
631 case PROC_STS_TEN:
632 {
633 fSignal = TRUE; /* Signal in any case. */
634 waitRes = ProcessWaitResult_Terminate;
635
636 mData.mStatus = ProcessStatus_TerminatedNormally;
637 mData.mExitCode = pData->u32Flags; /* Contains the exit code. */
638 break;
639 }
640
641 case PROC_STS_TES:
642 {
643 fSignal = TRUE; /* Signal in any case. */
644 waitRes = ProcessWaitResult_Terminate;
645
646 mData.mStatus = ProcessStatus_TerminatedSignal;
647 mData.mExitCode = pData->u32Flags; /* Contains the signal. */
648 break;
649 }
650
651 case PROC_STS_TEA:
652 {
653 fSignal = TRUE; /* Signal in any case. */
654 waitRes = ProcessWaitResult_Terminate;
655
656 mData.mStatus = ProcessStatus_TerminatedAbnormally;
657 break;
658 }
659
660 case PROC_STS_TOK:
661 {
662 fSignal = TRUE; /* Signal in any case. */
663 waitRes = ProcessWaitResult_Timeout;
664
665 mData.mStatus = ProcessStatus_TimedOutKilled;
666 break;
667 }
668
669 case PROC_STS_TOA:
670 {
671 fSignal = TRUE; /* Signal in any case. */
672 waitRes = ProcessWaitResult_Timeout;
673
674 mData.mStatus = ProcessStatus_TimedOutAbnormally;
675 break;
676 }
677
678 case PROC_STS_DWN:
679 {
680 fSignal = TRUE; /* Signal in any case. */
681 waitRes = (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
682 ? ProcessWaitResult_Terminate : ProcessWaitResult_Status;
683
684 mData.mStatus = ProcessStatus_Down;
685 break;
686 }
687
688 case PROC_STS_ERROR:
689 {
690 fSignal = TRUE; /* Signal in any case. */
691 waitRes = ProcessWaitResult_Error;
692
693 mData.mStatus = ProcessStatus_Error;
694
695 Utf8Str strError = Utf8StrFmt(tr("Guest process \"%s\" could not be started: "), mData.mProcess.mCommand.c_str());
696
697 /* Note: It's not required that the process has been started before. */
698 if (mData.mPID)
699 {
700 strError += Utf8StrFmt(tr("Error rc=%Rrc occured (PID %RU32)"), rc, mData.mPID);
701 }
702 else
703 {
704 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
705 switch (pData->u32Flags) /* pData->u32Flags contains the IPRT error code from guest side. */
706 {
707 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
708 strError += Utf8StrFmt(tr("The specified file was not found on guest"));
709 break;
710
711 case VERR_PATH_NOT_FOUND:
712 strError += Utf8StrFmt(tr("Could not resolve path to specified file was not found on guest"));
713 break;
714
715 case VERR_BAD_EXE_FORMAT:
716 strError += Utf8StrFmt(tr("The specified file is not an executable format on guest"));
717 break;
718
719 case VERR_AUTHENTICATION_FAILURE:
720 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
721 break;
722
723 case VERR_INVALID_NAME:
724 strError += Utf8StrFmt(tr("The specified file is an invalid name"));
725 break;
726
727 case VERR_TIMEOUT:
728 strError += Utf8StrFmt(tr("The guest did not respond within time"));
729 break;
730
731 case VERR_CANCELLED:
732 strError += Utf8StrFmt(tr("The execution operation was canceled"));
733 break;
734
735 case VERR_PERMISSION_DENIED:
736 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
737 break;
738
739 case VERR_MAX_PROCS_REACHED:
740 strError += Utf8StrFmt(tr("Maximum number of parallel guest processes has been reached"));
741 break;
742
743 default:
744 strError += Utf8StrFmt(tr("Reported error %Rrc"), pData->u32Flags);
745 break;
746 }
747 }
748
749 rc = setErrorInternal(pData->u32Flags, strError);
750 AssertRC(rc);
751 break;
752 }
753
754 case PROC_STS_UNDEFINED:
755 default:
756 {
757 /* Silently skip this request. */
758 fSignal = TRUE; /* Signal in any case. */
759 waitRes = ProcessWaitResult_Status;
760
761 mData.mStatus = ProcessStatus_Undefined;
762 break;
763 }
764 }
765
766 LogFlowThisFunc(("Got rc=%Rrc, waitResult=%d\n", rc, waitRes));
767
768 /*
769 * Now do the signalling stuff.
770 */
771 if (pCallback)
772 rc = pCallback->Signal();
773
774 if (fSignal)
775 {
776 int rc2 = signalWaiters(waitRes);
777 if (RT_SUCCESS(rc))
778 rc = rc2;
779 }
780
781 LogFlowFuncLeaveRC(rc);
782 return rc;
783}
784
785int GuestProcess::onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECOUT pData)
786{
787 /* pCallback is optional. */
788 AssertPtrReturn(pData, VERR_INVALID_POINTER);
789
790 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, pCallback=%p, pData=%p\n",
791 mData.mPID, pData->u32HandleId, pData->u32Flags, pData->pvData, pData->cbData, pCallback, pData));
792
793 /* Copy data into callback (if any). */
794 int rc = VINF_SUCCESS;
795
796 /* First, signal callback in every case (if available). */
797 if (pCallback)
798 {
799 rc = pCallback->SetData(pData, sizeof(CALLBACKDATAEXECOUT));
800
801 int rc2 = pCallback->Signal();
802 if (RT_SUCCESS(rc))
803 rc = rc2;
804 }
805
806 /* Then do the WaitFor signalling stuff. */
807 BOOL fSignal = FALSE;
808 uint32_t uWaitFlags = mData.mWaitEvent
809 ? mData.mWaitEvent->GetWaitFlags() : 0;
810
811 if ( (uWaitFlags & ProcessWaitForFlag_StdOut)
812 || (uWaitFlags & ProcessWaitForFlag_StdErr))
813 {
814 fSignal = TRUE;
815 }
816 else if ( (uWaitFlags & ProcessWaitForFlag_StdOut)
817 && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT))
818 {
819 fSignal = TRUE;
820 }
821 else if ( (uWaitFlags & ProcessWaitForFlag_StdErr)
822 && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDERR))
823 {
824 fSignal = TRUE;
825 }
826
827 if (fSignal)
828 {
829 int rc2 = signalWaiters( pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT
830 ? ProcessWaitResult_StdOut : ProcessWaitResult_StdErr);
831 if (RT_SUCCESS(rc))
832 rc = rc2;
833 }
834
835 LogFlowFuncLeaveRC(rc);
836 return rc;
837}
838
839int GuestProcess::readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
840 void *pvData, size_t cbData, size_t *pcbRead)
841{
842 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32\n",
843 mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData));
844 AssertReturn(uSize, VERR_INVALID_PARAMETER);
845 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
846 AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
847 /* pcbRead is optional. */
848
849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
850
851 if (mData.mStatus != ProcessStatus_Started)
852 return VERR_NOT_AVAILABLE;
853
854 uint32_t uContextID = 0;
855 GuestCtrlCallback *pCallbackRead = new GuestCtrlCallback();
856 if (!pCallbackRead)
857 return VERR_NO_MEMORY;
858
859 /* Create callback and add it to the map. */
860 int rc = pCallbackRead->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT);
861 if (RT_SUCCESS(rc))
862 rc = callbackAdd(pCallbackRead, &uContextID);
863
864 alock.release(); /* Drop the write lock again. */
865
866 if (RT_SUCCESS(rc))
867 {
868 VBOXHGCMSVCPARM paParms[5];
869
870 int i = 0;
871 paParms[i++].setUInt32(uContextID);
872 paParms[i++].setUInt32(mData.mPID);
873 paParms[i++].setUInt32(uHandle);
874 paParms[i++].setUInt32(0 /* Flags, none set yet. */);
875
876 rc = sendCommand(HOST_EXEC_GET_OUTPUT, i, paParms);
877 }
878
879 if (RT_SUCCESS(rc))
880 {
881 /*
882 * Let's wait for the process being started.
883 * Note: Be sure not keeping a AutoRead/WriteLock here.
884 */
885 LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
886 rc = pCallbackRead->Wait(uTimeoutMS);
887 if (RT_SUCCESS(rc)) /* Wait was successful, check for supplied information. */
888 {
889 rc = pCallbackRead->GetResultCode();
890 LogFlowThisFunc(("Callback returned rc=%Rrc, cbData=%RU32\n", rc, pCallbackRead->GetDataSize()));
891
892 if (RT_SUCCESS(rc))
893 {
894 Assert(pCallbackRead->GetDataSize() == sizeof(CALLBACKDATAEXECOUT));
895 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)pCallbackRead->GetDataRaw();
896 AssertPtr(pData);
897
898 size_t cbRead = pData->cbData;
899 if (cbRead)
900 {
901 Assert(cbData >= cbRead);
902 memcpy(pvData, pData->pvData, cbRead);
903 }
904
905 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
906
907 if (pcbRead)
908 *pcbRead = cbRead;
909 }
910 }
911 else
912 rc = VERR_TIMEOUT;
913 }
914
915 alock.acquire();
916
917 AssertPtr(pCallbackRead);
918 int rc2 = callbackRemove(uContextID);
919 if (RT_SUCCESS(rc))
920 rc = rc2;
921
922 LogFlowFuncLeaveRC(rc);
923 return rc;
924}
925
926int GuestProcess::sendCommand(uint32_t uFunction,
927 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
928{
929 LogFlowThisFuncEnter();
930
931 ComObjPtr<Console> pConsole = mData.mConsole;
932 Assert(!pConsole.isNull());
933
934 /* Forward the information to the VMM device. */
935 VMMDev *pVMMDev = pConsole->getVMMDev();
936 AssertPtr(pVMMDev);
937
938 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
939 int rc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", uFunction, uParms, paParms);
940 if (RT_FAILURE(rc))
941 {
942 int rc2;
943 if (rc == VERR_INVALID_VM_HANDLE)
944 rc2 = setErrorInternal(rc, tr("VMM device is not available (is the VM running?)"));
945 else if (rc == VERR_NOT_FOUND)
946 rc2 = setErrorInternal(rc, tr("The guest execution service is not ready (yet)"));
947 else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
948 rc2 = setErrorInternal(rc, tr("The guest execution service is not available"));
949 else
950 rc2 = setErrorInternal(rc, Utf8StrFmt(tr("The HGCM call failed with error %Rrc"), rc));
951 AssertRC(rc2);
952 }
953
954 LogFlowFuncLeaveRC(rc);
955 return rc;
956}
957
958/* Does not do locking; caller is responsible for that! */
959int GuestProcess::setErrorInternal(int rc, const Utf8Str &strMessage)
960{
961 LogFlowThisFunc(("rc=%Rrc, strMsg=%s\n", rc, strMessage.c_str()));
962
963 Assert(RT_FAILURE(rc));
964 Assert(!strMessage.isEmpty());
965
966#ifdef DEBUG
967 /* Do not allow overwriting an already set error. If this happens
968 * this means we forgot some error checking/locking somewhere. */
969 Assert(RT_SUCCESS(mData.mRC));
970 Assert(mData.mErrorMsg.isEmpty());
971#endif
972
973 mData.mStatus = ProcessStatus_Error;
974 mData.mRC = rc;
975 mData.mErrorMsg = strMessage;
976
977 int rc2 = signalWaiters(ProcessWaitResult_Error);
978 LogFlowFuncLeaveRC(rc2);
979 return rc2;
980}
981
982int GuestProcess::setErrorExternal(void)
983{
984 return RT_SUCCESS(mData.mRC)
985 ? S_OK : setError(VBOX_E_IPRT_ERROR, "%s", mData.mErrorMsg.c_str());
986}
987
988int GuestProcess::signalWaiters(ProcessWaitResult_T enmWaitResult)
989{
990 LogFlowThisFunc(("enmWaitResult=%d, mWaitCount=%RU32, mWaitEvent=%p\n",
991 enmWaitResult, mData.mWaitCount, mData.mWaitEvent));
992
993 /* Note: No write locking here -- already done in the caller. */
994
995 int rc = VINF_SUCCESS;
996 if (mData.mWaitEvent)
997 rc = mData.mWaitEvent->Signal(enmWaitResult);
998 LogFlowFuncLeaveRC(rc);
999 return rc;
1000}
1001
1002int GuestProcess::startProcess(void)
1003{
1004 LogFlowThisFunc(("aCmd=%s, aTimeoutMS=%RU32, fFlags=%x\n",
1005 mData.mProcess.mCommand.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags));
1006
1007 /* Wait until the caller function (if kicked off by a thread)
1008 * has returned and continue operation. */
1009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 int rc;
1012 uint32_t uContextID = 0;
1013 GuestCtrlCallback *pCallbackStart = new GuestCtrlCallback();
1014 if (!pCallbackStart)
1015 return VERR_NO_MEMORY;
1016
1017 mData.mStatus = ProcessStatus_Starting;
1018
1019 /* Create callback and add it to the map. */
1020 rc = pCallbackStart->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START);
1021 if (RT_SUCCESS(rc))
1022 rc = callbackAdd(pCallbackStart, &uContextID);
1023
1024 if (RT_SUCCESS(rc))
1025 {
1026 GuestSession *pSession = mData.mParent;
1027 AssertPtr(pSession);
1028
1029 const GuestCredentials &sessionCreds = pSession->getCredentials();
1030
1031 /* Prepare arguments. */
1032 char *pszArgs = NULL;
1033 size_t cArgs = mData.mProcess.mArguments.size();
1034 if (cArgs)
1035 {
1036 char **papszArgv = (char**)RTMemAlloc(sizeof(char*) * (cArgs + 1));
1037 AssertReturn(papszArgv, VERR_NO_MEMORY);
1038 for (size_t i = 0; RT_SUCCESS(rc) && i < cArgs; i++)
1039 rc = RTStrDupEx(&papszArgv[i], mData.mProcess.mArguments[i].c_str());
1040 papszArgv[cArgs] = NULL;
1041
1042 if (RT_SUCCESS(rc))
1043 rc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1044 }
1045 size_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1046
1047 /* Prepare environment. */
1048 void *pvEnv = NULL;
1049 size_t cbEnv = 0;
1050 rc = mData.mProcess.mEnvironment.BuildEnvironmentBlock(&pvEnv, &cbEnv, NULL /* cEnv */);
1051
1052 if (RT_SUCCESS(rc))
1053 {
1054 /* Prepare HGCM call. */
1055 VBOXHGCMSVCPARM paParms[15];
1056 int i = 0;
1057 paParms[i++].setUInt32(uContextID);
1058 paParms[i++].setPointer((void*)mData.mProcess.mCommand.c_str(),
1059 (ULONG)mData.mProcess.mCommand.length() + 1);
1060 paParms[i++].setUInt32(mData.mProcess.mFlags);
1061 paParms[i++].setUInt32(mData.mProcess.mArguments.size());
1062 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1063 paParms[i++].setUInt32(mData.mProcess.mEnvironment.Size());
1064 paParms[i++].setUInt32(cbEnv);
1065 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1066 paParms[i++].setPointer((void*)sessionCreds.mUser.c_str(), (ULONG)sessionCreds.mUser.length() + 1);
1067 paParms[i++].setPointer((void*)sessionCreds.mPassword.c_str(), (ULONG)sessionCreds.mPassword.length() + 1);
1068 /** @todo New command needs the domain as well! */
1069
1070 /*
1071 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1072 * until the process was started - the process itself then gets an infinite timeout for execution.
1073 * This is handy when we want to start a process inside a worker thread within a certain timeout
1074 * but let the started process perform lengthly operations then.
1075 */
1076 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1077 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1078 else
1079 paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
1080
1081 /* Note: Don't hold the write lock in here, because setErrorInternal */
1082 rc = sendCommand(HOST_EXEC_CMD, i, paParms);
1083 }
1084
1085 GuestEnvironment::FreeEnvironmentBlock(pvEnv);
1086 if (pszArgs)
1087 RTStrFree(pszArgs);
1088
1089 uint32_t uTimeoutMS = mData.mProcess.mTimeoutMS;
1090
1091 /* Drop the write lock again before waiting. */
1092 alock.release();
1093
1094 if (RT_SUCCESS(rc))
1095 {
1096 /*
1097 * Let's wait for the process being started.
1098 * Note: Be sure not keeping a AutoRead/WriteLock here.
1099 */
1100 LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
1101 rc = pCallbackStart->Wait(uTimeoutMS);
1102 if (RT_SUCCESS(rc)) /* Wait was successful, check for supplied information. */
1103 {
1104 rc = pCallbackStart->GetResultCode();
1105 LogFlowThisFunc(("Callback returned rc=%Rrc\n", rc));
1106 }
1107 else
1108 rc = VERR_TIMEOUT;
1109 }
1110
1111 AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 AssertPtr(pCallbackStart);
1114 int rc2 = callbackRemove(uContextID);
1115 if (RT_SUCCESS(rc))
1116 rc = rc2;
1117 }
1118
1119 LogFlowFuncLeaveRC(rc);
1120 return rc;
1121}
1122
1123int GuestProcess::startProcessAsync(void)
1124{
1125 LogFlowThisFuncEnter();
1126
1127 /* Asynchronously start the process on the guest by kicking off a
1128 * worker thread. */
1129 std::auto_ptr<GuestProcessStartTask> pTask(new GuestProcessStartTask(this));
1130 AssertReturn(pTask->isOk(), pTask->rc());
1131
1132 int rc = RTThreadCreate(NULL, GuestProcess::startProcessThread,
1133 (void *)pTask.get(), 0,
1134 RTTHREADTYPE_MAIN_WORKER, 0,
1135 "gctlPrcStart");
1136 if (RT_SUCCESS(rc))
1137 {
1138 /* pTask is now owned by startProcessThread(), so release it. */
1139 pTask.release();
1140 }
1141
1142 LogFlowFuncLeaveRC(rc);
1143 return rc;
1144}
1145
1146DECLCALLBACK(int) GuestProcess::startProcessThread(RTTHREAD Thread, void *pvUser)
1147{
1148 LogFlowFunc(("pvUser=%p\n", pvUser));
1149
1150 std::auto_ptr<GuestProcessStartTask> pTask(static_cast<GuestProcessStartTask*>(pvUser));
1151 AssertPtr(pTask.get());
1152
1153 const ComObjPtr<GuestProcess> pProcess(pTask->mProcess);
1154 Assert(!pProcess.isNull());
1155
1156 AutoCaller autoCaller(pProcess);
1157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1158
1159 int rc = pProcess->startProcess();
1160 if (RT_FAILURE(rc))
1161 {
1162 /** @todo What now? */
1163 }
1164
1165 LogFlowFuncLeaveRC(rc);
1166 return rc;
1167}
1168
1169int GuestProcess::terminateProcess(void)
1170{
1171 LogFlowThisFuncEnter();
1172
1173 if (mData.mParent->getProtocolVersion() < 2)
1174 return VERR_NOT_SUPPORTED;
1175
1176 LogFlowThisFuncLeave();
1177 return VERR_NOT_IMPLEMENTED;
1178}
1179
1180int GuestProcess::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestProcessWaitResult &waitRes)
1181{
1182 LogFlowThisFuncEnter();
1183
1184 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1185
1186 LogFlowThisFunc(("fWaitFlags=%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p\n",
1187 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent));
1188
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 ProcessStatus_T curStatus = mData.mStatus;
1192
1193 /* Did some error occur before? Then skip waiting and return. */
1194 if (curStatus == ProcessStatus_Error)
1195 {
1196 waitRes.mResult = ProcessWaitResult_Error;
1197 return VINF_SUCCESS;
1198 }
1199
1200 waitRes.mResult = ProcessWaitResult_None;
1201 waitRes.mRC = VINF_SUCCESS;
1202
1203 if ( (fWaitFlags & ProcessWaitForFlag_Terminate)
1204 || (fWaitFlags & ProcessWaitForFlag_StdIn)
1205 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1206 || (fWaitFlags & ProcessWaitForFlag_StdErr))
1207 {
1208 /* Filter out waits which are *not* supported using
1209 * older guest control Guest Additions. */
1210 AssertPtr(mData.mParent);
1211 if (mData.mParent->getProtocolVersion() < 2)
1212 {
1213 /* We don't support waiting for stdin, out + err,
1214 * just skip waiting then. */
1215 if ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1216 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1217 || (fWaitFlags & ProcessWaitForFlag_StdErr))
1218 {
1219 /* Use _Any because we don't know what to tell the caller. */
1220 waitRes.mResult = ProcessWaitResult_Any;
1221 }
1222 }
1223
1224 switch (mData.mStatus)
1225 {
1226 case ProcessStatus_TerminatedNormally:
1227 case ProcessStatus_TerminatedSignal:
1228 case ProcessStatus_TerminatedAbnormally:
1229 case ProcessStatus_Down:
1230 waitRes.mResult = ProcessWaitResult_Terminate;
1231 break;
1232
1233 case ProcessStatus_TimedOutKilled:
1234 case ProcessStatus_TimedOutAbnormally:
1235 waitRes.mResult = ProcessWaitResult_Timeout;
1236 break;
1237
1238 case ProcessStatus_Error:
1239 waitRes.mResult = ProcessWaitResult_Error;
1240 waitRes.mRC = mData.mRC;
1241 break;
1242
1243 case ProcessStatus_Undefined:
1244 case ProcessStatus_Starting:
1245 case ProcessStatus_Started:
1246 /* Do the waiting below. */
1247 break;
1248
1249 default:
1250 AssertMsgFailed(("Unhandled process status %ld\n", mData.mStatus));
1251 return VERR_NOT_IMPLEMENTED;
1252 }
1253 }
1254 else if (fWaitFlags & ProcessWaitForFlag_Start)
1255 {
1256 switch (mData.mStatus)
1257 {
1258 case ProcessStatus_Started:
1259 case ProcessStatus_Paused:
1260 case ProcessStatus_Terminating:
1261 case ProcessStatus_TerminatedNormally:
1262 case ProcessStatus_TerminatedSignal:
1263 case ProcessStatus_TerminatedAbnormally:
1264 case ProcessStatus_Down:
1265 waitRes.mResult = ProcessWaitResult_Start;
1266 break;
1267
1268 case ProcessStatus_Error:
1269 waitRes.mResult = ProcessWaitResult_Error;
1270 waitRes.mRC = mData.mRC;
1271 break;
1272
1273 case ProcessStatus_Undefined:
1274 case ProcessStatus_Starting:
1275 /* Do the waiting below. */
1276 break;
1277
1278 default:
1279 AssertMsgFailed(("Unhandled process status %ld\n", mData.mStatus));
1280 return VERR_NOT_IMPLEMENTED;
1281 }
1282 }
1283
1284 LogFlowThisFunc(("waitResult=%ld, waitRC=%Rrc\n", waitRes.mResult, waitRes.mRC));
1285
1286 /* No waiting needed? Return immediately. */
1287 if (waitRes.mResult != ProcessWaitResult_None)
1288 return VINF_SUCCESS;
1289
1290 if (mData.mWaitCount > 0)
1291 return VERR_ALREADY_EXISTS;
1292 mData.mWaitCount++;
1293
1294 Assert(mData.mWaitEvent == NULL);
1295 mData.mWaitEvent = new GuestProcessEvent(fWaitFlags);
1296 AssertPtrReturn(mData.mWaitEvent, VERR_NO_MEMORY);
1297
1298 alock.release(); /* Release lock before waiting. */
1299
1300 int rc = mData.mWaitEvent->Wait(uTimeoutMS);
1301 if (RT_SUCCESS(rc))
1302 waitRes = mData.mWaitEvent->GetResult();
1303
1304 alock.acquire(); /* Get the lock again. */
1305
1306 /* Note: The caller always is responsible of deleting the
1307 * stuff it created before. See close() for more information. */
1308 delete mData.mWaitEvent;
1309 mData.mWaitEvent = NULL;
1310
1311 mData.mWaitCount--;
1312
1313 LogFlowFuncLeaveRC(rc);
1314 return rc;
1315}
1316
1317HRESULT GuestProcess::waitResultToErrorEx(const GuestProcessWaitResult &waitResult, bool fLog)
1318{
1319 int rc = waitResult.mRC;
1320
1321 Utf8Str strMsg;
1322 ProcessStatus_T procStatus = mData.mStatus;
1323
1324 switch (procStatus)
1325 {
1326 case ProcessStatus_Started:
1327 strMsg = Utf8StrFmt(tr("Guest process \"%s\" was started (PID %RU32)"),
1328 mData.mProcess.mCommand.c_str(), mData.mPID);
1329 break;
1330
1331 case ProcessStatus_TerminatedNormally:
1332 strMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %RU32) terminated normally (exit code: %ld)"),
1333 mData.mProcess.mCommand.c_str(), mData.mPID, mData.mExitCode);
1334 break;
1335
1336 case ProcessStatus_TerminatedSignal:
1337 {
1338 strMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %RU32) terminated through signal (signal: %ld)"),
1339 mData.mProcess.mCommand.c_str(), mData.mPID, mData.mExitCode);
1340 break;
1341 }
1342
1343 case ProcessStatus_TerminatedAbnormally:
1344 {
1345 strMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %RU32) terminated abnormally (exit code: %ld)"),
1346 mData.mProcess.mCommand.c_str(), mData.mPID, mData.mExitCode);
1347 break;
1348 }
1349
1350 case ProcessStatus_TimedOutKilled:
1351 {
1352 strMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %RU32) timed out and was killed"),
1353 mData.mProcess.mCommand.c_str(), mData.mPID);
1354 break;
1355 }
1356
1357 case ProcessStatus_TimedOutAbnormally:
1358 {
1359 strMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %RU32) timed out and could not be killed\n"),
1360 mData.mProcess.mCommand.c_str(), mData.mPID);
1361 break;
1362 }
1363
1364 case ProcessStatus_Down:
1365 {
1366 strMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %RU32) was killed because guest OS is shutting down\n"),
1367 mData.mProcess.mCommand.c_str(), mData.mPID);
1368 break;
1369 }
1370
1371 case ProcessStatus_Error:
1372 {
1373 strMsg = Utf8StrFmt(tr("Guest process \"%s\" could not be started: ", mData.mProcess.mCommand.c_str()));
1374
1375 /* Note: It's not required that the process has been started before. */
1376 if (mData.mPID)
1377 {
1378 strMsg += Utf8StrFmt(tr("Error rc=%Rrc occured (PID %RU32)"), rc, mData.mPID);
1379 }
1380 else
1381 {
1382 switch (rc) /* rc contains the IPRT error code from guest side. */
1383 {
1384 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
1385 strMsg += Utf8StrFmt(tr("The specified file was not found on guest"));
1386 break;
1387
1388 case VERR_PATH_NOT_FOUND:
1389 strMsg += Utf8StrFmt(tr("Could not resolve path to specified file was not found on guest"));
1390 break;
1391
1392 case VERR_BAD_EXE_FORMAT:
1393 strMsg += Utf8StrFmt(tr("The specified file is not an executable format on guest"));
1394 break;
1395
1396 case VERR_AUTHENTICATION_FAILURE:
1397 strMsg += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1398 break;
1399
1400 case VERR_INVALID_NAME:
1401 strMsg += Utf8StrFmt(tr("The specified file is an invalid name"));
1402 break;
1403
1404 case VERR_TIMEOUT:
1405 strMsg += Utf8StrFmt(tr("The guest did not respond within time"));
1406 break;
1407
1408 case VERR_CANCELLED:
1409 strMsg += Utf8StrFmt(tr("The execution operation was canceled"));
1410 break;
1411
1412 case VERR_PERMISSION_DENIED:
1413 strMsg += Utf8StrFmt(tr("Invalid user/password credentials"));
1414 break;
1415
1416 case VERR_MAX_PROCS_REACHED:
1417 strMsg += Utf8StrFmt(tr("Maximum number of parallel guest processes has been reached"));
1418 break;
1419
1420 default:
1421 strMsg += Utf8StrFmt(tr("Reported error %Rrc"), rc);
1422 break;
1423 }
1424 }
1425
1426 break;
1427 }
1428
1429 case ProcessStatus_Undefined:
1430 default:
1431
1432 /* Silently skip this request. */
1433 break;
1434 }
1435
1436 HRESULT hr = S_OK;
1437 if (RT_FAILURE(rc))
1438 {
1439 Assert(!strMsg.isEmpty());
1440 hr = setError(VBOX_E_IPRT_ERROR, "%s", strMsg.c_str());
1441 }
1442
1443 if (fLog)
1444 {
1445 Assert(!strMsg.isEmpty());
1446
1447 strMsg.append("\n");
1448 LogRel(("%s", strMsg.c_str()));
1449 }
1450
1451 return hr;
1452}
1453
1454int GuestProcess::writeData(uint32_t uHandle, uint32_t uFlags,
1455 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten)
1456{
1457 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p\n",
1458 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten));
1459 /* All is optional. There can be 0 byte writes. */
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 if (mData.mStatus != ProcessStatus_Started)
1464 return VERR_NOT_AVAILABLE;
1465
1466 uint32_t uContextID = 0;
1467 GuestCtrlCallback *pCallbackWrite = new GuestCtrlCallback();
1468 if (!pCallbackWrite)
1469 return VERR_NO_MEMORY;
1470
1471 /* Create callback and add it to the map. */
1472 int rc = pCallbackWrite->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS);
1473 if (RT_SUCCESS(rc))
1474 rc = callbackAdd(pCallbackWrite, &uContextID);
1475
1476 alock.release(); /* Drop the write lock again. */
1477
1478 if (RT_SUCCESS(rc))
1479 {
1480 VBOXHGCMSVCPARM paParms[5];
1481
1482 int i = 0;
1483 paParms[i++].setUInt32(uContextID);
1484 paParms[i++].setUInt32(mData.mPID);
1485 paParms[i++].setUInt32(uFlags);
1486 paParms[i++].setPointer(pvData, cbData);
1487 paParms[i++].setUInt32(cbData);
1488
1489 rc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
1490 }
1491
1492 if (RT_SUCCESS(rc))
1493 {
1494 /*
1495 * Let's wait for the process being started.
1496 * Note: Be sure not keeping a AutoRead/WriteLock here.
1497 */
1498 LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
1499 rc = pCallbackWrite->Wait(uTimeoutMS);
1500 if (RT_SUCCESS(rc)) /* Wait was successful, check for supplied information. */
1501 {
1502 rc = pCallbackWrite->GetResultCode();
1503 LogFlowThisFunc(("Callback returned rc=%Rrc, cbData=%RU32\n", rc, pCallbackWrite->GetDataSize()));
1504
1505 if (RT_SUCCESS(rc))
1506 {
1507 Assert(pCallbackWrite->GetDataSize() == sizeof(CALLBACKDATAEXECINSTATUS));
1508 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)pCallbackWrite->GetDataRaw();
1509 AssertPtr(pData);
1510
1511 uint32_t cbWritten = 0;
1512 switch (pData->u32Status)
1513 {
1514 case INPUT_STS_WRITTEN:
1515 cbWritten = pData->cbProcessed;
1516 break;
1517
1518 case INPUT_STS_ERROR:
1519 rc = pData->u32Flags; /** @todo Fix int vs. uint32_t! */
1520 break;
1521
1522 case INPUT_STS_TERMINATED:
1523 rc = VERR_CANCELLED;
1524 break;
1525
1526 case INPUT_STS_OVERFLOW:
1527 rc = VERR_BUFFER_OVERFLOW;
1528 break;
1529
1530 default:
1531 /* Silently skip unknown errors. */
1532 break;
1533 }
1534
1535 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
1536
1537 if (puWritten)
1538 *puWritten = cbWritten;
1539 }
1540 }
1541 else
1542 rc = VERR_TIMEOUT;
1543 }
1544
1545 alock.acquire();
1546
1547 AssertPtr(pCallbackWrite);
1548 int rc2 = callbackRemove(uContextID);
1549 if (RT_SUCCESS(rc))
1550 rc = rc2;
1551
1552 LogFlowFuncLeaveRC(rc);
1553 return rc;
1554}
1555
1556// implementation of public methods
1557/////////////////////////////////////////////////////////////////////////////
1558
1559STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aSize, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
1560{
1561#ifndef VBOX_WITH_GUEST_CONTROL
1562 ReturnComNotImplemented();
1563#else
1564 if (aSize == 0)
1565 return setError(E_INVALIDARG, tr("The size to read is zero"));
1566 CheckComArgOutSafeArrayPointerValid(aData);
1567
1568 AutoCaller autoCaller(this);
1569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1570
1571 com::SafeArray<BYTE> data((size_t)aSize);
1572 Assert(data.size() >= aSize);
1573
1574 size_t cbRead;
1575 int rc = readData(aHandle, aSize, aTimeoutMS, data.raw(), aSize, &cbRead);
1576 if (RT_SUCCESS(rc))
1577 {
1578 if (data.size() != cbRead)
1579 data.resize(cbRead);
1580 data.detachTo(ComSafeArrayOutArg(aData));
1581 }
1582
1583 /** @todo Do setError() here. */
1584 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1585 LogFlowFuncLeaveRC(rc);
1586
1587 return hr;
1588#endif /* VBOX_WITH_GUEST_CONTROL */
1589}
1590
1591STDMETHODIMP GuestProcess::Terminate(void)
1592{
1593#ifndef VBOX_WITH_GUEST_CONTROL
1594 ReturnComNotImplemented();
1595#else
1596 LogFlowThisFuncEnter();
1597
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600
1601 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1602
1603 int rc = terminateProcess();
1604 /** @todo Do setError() here. */
1605 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1606 LogFlowFuncLeaveRC(rc);
1607
1608 return hr;
1609#endif /* VBOX_WITH_GUEST_CONTROL */
1610}
1611
1612STDMETHODIMP GuestProcess::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
1613{
1614#ifndef VBOX_WITH_GUEST_CONTROL
1615 ReturnComNotImplemented();
1616#else
1617 LogFlowThisFuncEnter();
1618
1619 CheckComArgOutPointerValid(aReason);
1620
1621 AutoCaller autoCaller(this);
1622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1623
1624 /*
1625 * Note: Do not hold any locks here while waiting!
1626 */
1627 HRESULT hr;
1628
1629 GuestProcessWaitResult waitRes;
1630 int rc = waitFor(aWaitFlags, aTimeoutMS, waitRes);
1631 if (RT_SUCCESS(rc))
1632 {
1633 *aReason = waitRes.mResult;
1634 hr = setErrorExternal();
1635 }
1636 else
1637 {
1638 if (rc == VERR_TIMEOUT)
1639 hr = setError(VBOX_E_IPRT_ERROR,
1640 tr("Process \"%s\" (PID %RU32) did not respond within time (%RU32ms)"),
1641 mData.mProcess.mCommand.c_str(), mData.mPID, aTimeoutMS);
1642 else
1643 hr = setError(VBOX_E_IPRT_ERROR,
1644 tr("Waiting for process \"%s\" (PID %RU32) failed with rc=%Rrc"),
1645 mData.mProcess.mCommand.c_str(), mData.mPID, rc);
1646 }
1647 LogFlowFuncLeaveRC(rc);
1648 return hr;
1649#endif /* VBOX_WITH_GUEST_CONTROL */
1650}
1651
1652STDMETHODIMP GuestProcess::WaitForArray(ComSafeArrayIn(ProcessWaitForFlag_T, aFlags), ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
1653{
1654#ifndef VBOX_WITH_GUEST_CONTROL
1655 ReturnComNotImplemented();
1656#else
1657 LogFlowThisFuncEnter();
1658
1659 CheckComArgOutPointerValid(aReason);
1660
1661 AutoCaller autoCaller(this);
1662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1663
1664 /*
1665 * Note: Do not hold any locks here while waiting!
1666 */
1667 uint32_t fWaitFor = ProcessWaitForFlag_None;
1668 com::SafeArray<ProcessWaitForFlag_T> flags(ComSafeArrayInArg(aFlags));
1669 for (size_t i = 0; i < flags.size(); i++)
1670 fWaitFor |= flags[i];
1671
1672 return WaitFor(fWaitFor, aTimeoutMS, aReason);
1673#endif /* VBOX_WITH_GUEST_CONTROL */
1674}
1675
1676STDMETHODIMP GuestProcess::Write(ULONG aHandle, ULONG aFlags,
1677 ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1678{
1679#ifndef VBOX_WITH_GUEST_CONTROL
1680 ReturnComNotImplemented();
1681#else
1682 LogFlowThisFuncEnter();
1683
1684 CheckComArgOutPointerValid(aWritten);
1685
1686 AutoCaller autoCaller(this);
1687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1688
1689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1690
1691 com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
1692 int rc = writeData(aHandle, aFlags, data.raw(), data.size(), aTimeoutMS, (uint32_t*)aWritten);
1693 /** @todo Do setError() here. */
1694 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1695 LogFlowFuncLeaveRC(rc);
1696
1697 return hr;
1698#endif /* VBOX_WITH_GUEST_CONTROL */
1699}
1700
1701STDMETHODIMP GuestProcess::WriteArray(ULONG aHandle, ComSafeArrayIn(ProcessInputFlag_T, aFlags),
1702 ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1703{
1704#ifndef VBOX_WITH_GUEST_CONTROL
1705 ReturnComNotImplemented();
1706#else
1707 LogFlowThisFuncEnter();
1708
1709 CheckComArgOutPointerValid(aWritten);
1710
1711 AutoCaller autoCaller(this);
1712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1713
1714 /*
1715 * Note: Do not hold any locks here while writing!
1716 */
1717 ULONG fWrite = ProcessInputFlag_None;
1718 com::SafeArray<ProcessInputFlag_T> flags(ComSafeArrayInArg(aFlags));
1719 for (size_t i = 0; i < flags.size(); i++)
1720 fWrite |= flags[i];
1721
1722 return Write(aHandle, fWrite, ComSafeArrayInArg(aData), aTimeoutMS, aWritten);
1723#endif /* VBOX_WITH_GUEST_CONTROL */
1724}
1725
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette