VirtualBox

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

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