VirtualBox

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

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

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