VirtualBox

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

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

Guest Control 2.0: Update.

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