VirtualBox

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

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

Main: big API naming cleanup, use all caps acronyms everywhere, including SDK docs
Frontends/VBoxManage: implement guestcontrol execute for new API, disabled by default

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