VirtualBox

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

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

Guest Control 2.0: Bugfixes.

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