VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.6 KB
Line 
1/* $Id: GuestProcessImpl.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest process handling.
4 */
5
6/*
7 * Copyright (C) 2012-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/**
19 * Locking rules:
20 * - When the main dispatcher (callbackDispatcher) is called it takes the
21 * WriteLock while dispatching to the various on* methods.
22 * - All other outer functions (accessible by Main) must not own a lock
23 * while waiting for a callback or for an event.
24 * - Only keep Read/WriteLocks as short as possible and only when necessary.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_MAIN_GUESTPROCESS
32#include "LoggingNew.h"
33
34#ifndef VBOX_WITH_GUEST_CONTROL
35# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
36#endif
37#include "GuestImpl.h"
38#include "GuestProcessImpl.h"
39#include "GuestSessionImpl.h"
40#include "GuestCtrlImplPrivate.h"
41#include "ConsoleImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43
44#include "Global.h"
45#include "AutoCaller.h"
46#include "VBoxEvents.h"
47#include "ThreadTask.h"
48
49#include <memory> /* For auto_ptr. */
50
51#include <iprt/asm.h>
52#include <iprt/cpp/utils.h> /* For unconst(). */
53#include <iprt/getopt.h>
54
55#include <VBox/com/listeners.h>
56
57#include <VBox/com/array.h>
58
59
60/**
61 * Base class for all guest process tasks.
62 */
63class GuestProcessTask : public ThreadTask
64{
65public:
66
67 GuestProcessTask(GuestProcess *pProcess)
68 : ThreadTask("GenericGuestProcessTask")
69 , mProcess(pProcess)
70 , mRC(VINF_SUCCESS) { }
71
72 virtual ~GuestProcessTask(void) { }
73
74 /** Returns the last set result code. */
75 int i_rc(void) const { return mRC; }
76 /** Returns whether the last set result is okay (successful) or not. */
77 bool i_isOk(void) const { return RT_SUCCESS(mRC); }
78 /** Returns the reference of the belonging progress object. */
79 const ComObjPtr<GuestProcess> &i_process(void) const { return mProcess; }
80
81protected:
82
83 /** Progress object this process belongs to. */
84 const ComObjPtr<GuestProcess> mProcess;
85 /** Last set result code. */
86 int mRC;
87};
88
89/**
90 * Task to start a process on the guest.
91 */
92class GuestProcessStartTask : public GuestProcessTask
93{
94public:
95
96 GuestProcessStartTask(GuestProcess *pProcess)
97 : GuestProcessTask(pProcess)
98 {
99 m_strTaskName = "gctlPrcStart";
100 }
101
102 void handler()
103 {
104 GuestProcess::i_startProcessThreadTask(this);
105 }
106};
107
108/**
109 * Internal listener class to serve events in an
110 * active manner, e.g. without polling delays.
111 */
112class GuestProcessListener
113{
114public:
115
116 GuestProcessListener(void)
117 {
118 }
119
120 virtual ~GuestProcessListener(void)
121 {
122 }
123
124 HRESULT init(GuestProcess *pProcess)
125 {
126 AssertPtrReturn(pProcess, E_POINTER);
127 mProcess = pProcess;
128 return S_OK;
129 }
130
131 void uninit(void)
132 {
133 mProcess = NULL;
134 }
135
136 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
137 {
138 switch (aType)
139 {
140 case VBoxEventType_OnGuestProcessStateChanged:
141 case VBoxEventType_OnGuestProcessInputNotify:
142 case VBoxEventType_OnGuestProcessOutput:
143 {
144 AssertPtrReturn(mProcess, E_POINTER);
145 int rc2 = mProcess->signalWaitEvent(aType, aEvent);
146 RT_NOREF(rc2);
147#ifdef LOG_ENABLED
148 LogFlowThisFunc(("Signalling events of type=%RU32, pProcess=%p resulted in rc=%Rrc\n",
149 aType, &mProcess, rc2));
150#endif
151 break;
152 }
153
154 default:
155 AssertMsgFailed(("Unhandled event %RU32\n", aType));
156 break;
157 }
158
159 return S_OK;
160 }
161
162private:
163
164 GuestProcess *mProcess;
165};
166typedef ListenerImpl<GuestProcessListener, GuestProcess*> GuestProcessListenerImpl;
167
168VBOX_LISTENER_DECLARE(GuestProcessListenerImpl)
169
170// constructor / destructor
171/////////////////////////////////////////////////////////////////////////////
172
173DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
174
175HRESULT GuestProcess::FinalConstruct(void)
176{
177 LogFlowThisFuncEnter();
178 return BaseFinalConstruct();
179}
180
181void GuestProcess::FinalRelease(void)
182{
183 LogFlowThisFuncEnter();
184 uninit();
185 BaseFinalRelease();
186 LogFlowThisFuncLeave();
187}
188
189// public initializer/uninitializer for internal purposes only
190/////////////////////////////////////////////////////////////////////////////
191
192/**
193 * Initialies a guest process object.
194 *
195 * @returns VBox status code.
196 * @param aConsole Console this process is bound to.
197 * @param aSession Guest session this process is bound to.
198 * @param aObjectID Object ID to use for this process object.
199 * @param aProcInfo Process startup information to use.
200 * @param pBaseEnv Guest environment to apply when starting the process on the guest.
201 */
202int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aObjectID,
203 const GuestProcessStartupInfo &aProcInfo, const GuestEnvironment *pBaseEnv)
204{
205 LogFlowThisFunc(("aConsole=%p, aSession=%p, aObjectID=%RU32, pBaseEnv=%p\n",
206 aConsole, aSession, aObjectID, pBaseEnv));
207
208 AssertPtrReturn(aConsole, VERR_INVALID_POINTER);
209 AssertPtrReturn(aSession, VERR_INVALID_POINTER);
210
211 /* Enclose the state transition NotReady->InInit->Ready. */
212 AutoInitSpan autoInitSpan(this);
213 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
214
215 HRESULT hr;
216
217 int vrc = bindToSession(aConsole, aSession, aObjectID);
218 if (RT_SUCCESS(vrc))
219 {
220 hr = unconst(mEventSource).createObject();
221 if (FAILED(hr))
222 vrc = VERR_NO_MEMORY;
223 else
224 {
225 hr = mEventSource->init();
226 if (FAILED(hr))
227 vrc = VERR_COM_UNEXPECTED;
228 }
229 }
230
231 if (RT_SUCCESS(vrc))
232 {
233 try
234 {
235 GuestProcessListener *pListener = new GuestProcessListener();
236 ComObjPtr<GuestProcessListenerImpl> thisListener;
237 hr = thisListener.createObject();
238 if (SUCCEEDED(hr))
239 hr = thisListener->init(pListener, this);
240
241 if (SUCCEEDED(hr))
242 {
243 com::SafeArray <VBoxEventType_T> eventTypes;
244 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
245 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
246 eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
247 hr = mEventSource->RegisterListener(thisListener,
248 ComSafeArrayAsInParam(eventTypes),
249 TRUE /* Active listener */);
250 if (SUCCEEDED(hr))
251 {
252 vrc = baseInit();
253 if (RT_SUCCESS(vrc))
254 {
255 mLocalListener = thisListener;
256 }
257 }
258 else
259 vrc = VERR_COM_UNEXPECTED;
260 }
261 else
262 vrc = VERR_COM_UNEXPECTED;
263 }
264 catch(std::bad_alloc &)
265 {
266 vrc = VERR_NO_MEMORY;
267 }
268 }
269
270 if (RT_SUCCESS(vrc))
271 {
272 mData.mProcess = aProcInfo;
273 mData.mpSessionBaseEnv = pBaseEnv;
274 if (pBaseEnv)
275 pBaseEnv->retainConst();
276 mData.mExitCode = 0;
277 mData.mPID = 0;
278 mData.mLastError = VINF_SUCCESS;
279 mData.mStatus = ProcessStatus_Undefined;
280 /* Everything else will be set by the actual starting routine. */
281
282 /* Confirm a successful initialization when it's the case. */
283 autoInitSpan.setSucceeded();
284
285 return vrc;
286 }
287
288 autoInitSpan.setFailed();
289 return vrc;
290}
291
292/**
293 * Uninitializes the instance.
294 * Called from FinalRelease() or IGuestSession::uninit().
295 */
296void GuestProcess::uninit(void)
297{
298 /* Enclose the state transition Ready->InUninit->NotReady. */
299 AutoUninitSpan autoUninitSpan(this);
300 if (autoUninitSpan.uninitDone())
301 return;
302
303 LogFlowThisFunc(("mExe=%s, PID=%RU32\n", mData.mProcess.mExecutable.c_str(), mData.mPID));
304
305 if (mData.mpSessionBaseEnv)
306 {
307 mData.mpSessionBaseEnv->releaseConst();
308 mData.mpSessionBaseEnv = NULL;
309 }
310
311 baseUninit();
312
313 LogFlowFuncLeave();
314}
315
316// implementation of public getters/setters for attributes
317/////////////////////////////////////////////////////////////////////////////
318HRESULT GuestProcess::getArguments(std::vector<com::Utf8Str> &aArguments)
319{
320 LogFlowThisFuncEnter();
321
322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
323 aArguments = mData.mProcess.mArguments;
324 return S_OK;
325}
326
327HRESULT GuestProcess::getEnvironment(std::vector<com::Utf8Str> &aEnvironment)
328{
329#ifndef VBOX_WITH_GUEST_CONTROL
330 ReturnComNotImplemented();
331#else
332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /* (Paranoia since both environment objects are immutable.) */
333 HRESULT hrc;
334 if (mData.mpSessionBaseEnv)
335 {
336 int vrc;
337 if (mData.mProcess.mEnvironmentChanges.count() == 0)
338 vrc = mData.mpSessionBaseEnv->queryPutEnvArray(&aEnvironment);
339 else
340 {
341 GuestEnvironment TmpEnv;
342 vrc = TmpEnv.copy(*mData.mpSessionBaseEnv);
343 if (RT_SUCCESS(vrc))
344 {
345 vrc = TmpEnv.applyChanges(mData.mProcess.mEnvironmentChanges);
346 if (RT_SUCCESS(vrc))
347 vrc = TmpEnv.queryPutEnvArray(&aEnvironment);
348 }
349 }
350 hrc = Global::vboxStatusCodeToCOM(vrc);
351 }
352 else
353 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by installed Guest Additions"));
354 LogFlowThisFuncLeave();
355 return hrc;
356#endif
357}
358
359HRESULT GuestProcess::getEventSource(ComPtr<IEventSource> &aEventSource)
360{
361 LogFlowThisFuncEnter();
362
363 // no need to lock - lifetime constant
364 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
365
366 LogFlowThisFuncLeave();
367 return S_OK;
368}
369
370HRESULT GuestProcess::getExecutablePath(com::Utf8Str &aExecutablePath)
371{
372 LogFlowThisFuncEnter();
373
374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
375
376 aExecutablePath = mData.mProcess.mExecutable;
377
378 return S_OK;
379}
380
381HRESULT GuestProcess::getExitCode(LONG *aExitCode)
382{
383 LogFlowThisFuncEnter();
384
385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
386
387 *aExitCode = mData.mExitCode;
388
389 return S_OK;
390}
391
392HRESULT GuestProcess::getName(com::Utf8Str &aName)
393{
394 LogFlowThisFuncEnter();
395
396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
397
398 aName = mData.mProcess.mName;
399
400 return S_OK;
401}
402
403HRESULT GuestProcess::getPID(ULONG *aPID)
404{
405 LogFlowThisFuncEnter();
406
407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
408
409 *aPID = mData.mPID;
410
411 return S_OK;
412}
413
414HRESULT GuestProcess::getStatus(ProcessStatus_T *aStatus)
415{
416 LogFlowThisFuncEnter();
417
418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
419
420 *aStatus = mData.mStatus;
421
422 return S_OK;
423}
424
425// private methods
426/////////////////////////////////////////////////////////////////////////////
427
428/**
429 * Entry point for guest side process callbacks.
430 *
431 * @returns VBox status code.
432 * @param pCbCtx Host callback context.
433 * @param pSvcCb Host callback data.
434 */
435int GuestProcess::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
436{
437 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
438 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
439#ifdef DEBUG
440 LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uMessage=%RU32, pSvcCb=%p\n",
441 mData.mPID, pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
442#endif
443
444 int vrc;
445 switch (pCbCtx->uMessage)
446 {
447 case GUEST_MSG_DISCONNECTED:
448 {
449 vrc = i_onGuestDisconnected(pCbCtx, pSvcCb);
450 break;
451 }
452
453 case GUEST_MSG_EXEC_STATUS:
454 {
455 vrc = i_onProcessStatusChange(pCbCtx, pSvcCb);
456 break;
457 }
458
459 case GUEST_MSG_EXEC_OUTPUT:
460 {
461 vrc = i_onProcessOutput(pCbCtx, pSvcCb);
462 break;
463 }
464
465 case GUEST_MSG_EXEC_INPUT_STATUS:
466 {
467 vrc = i_onProcessInputStatus(pCbCtx, pSvcCb);
468 break;
469 }
470
471 default:
472 /* Silently ignore not implemented functions. */
473 vrc = VERR_NOT_SUPPORTED;
474 break;
475 }
476
477#ifdef DEBUG
478 LogFlowFuncLeaveRC(vrc);
479#endif
480 return vrc;
481}
482
483/**
484 * Checks if the current assigned PID matches another PID (from a callback).
485 *
486 * In protocol v1 we don't have the possibility to terminate/kill
487 * processes so it can happen that a formerly started process A
488 * (which has the context ID 0 (session=0, process=0, count=0) will
489 * send a delayed message to the host if this process has already
490 * been discarded there and the same context ID was reused by
491 * a process B. Process B in turn then has a different guest PID.
492 *
493 * Note: This also can happen when restoring from a saved state which
494 * had a guest process running.
495 *
496 * @return IPRT status code.
497 * @param uPID PID to check.
498 */
499inline int GuestProcess::i_checkPID(uint32_t uPID)
500{
501 int rc = VINF_SUCCESS;
502
503 /* Was there a PID assigned yet? */
504 if (mData.mPID)
505 {
506 if (RT_UNLIKELY(mData.mPID != uPID))
507 {
508 LogFlowFunc(("Stale guest process (PID=%RU32) sent data to a newly started process (pProcesS=%p, PID=%RU32, status=%RU32)\n",
509 uPID, this, mData.mPID, mData.mStatus));
510 rc = VERR_NOT_FOUND;
511 }
512 }
513
514 return rc;
515}
516
517/**
518 * Converts a given guest process error to a string.
519 *
520 * @returns Error as a string.
521 * @param rcGuest Guest process error to return string for.
522 * @param pcszWhat Hint of what was involved when the error occurred.
523 */
524/* static */
525Utf8Str GuestProcess::i_guestErrorToString(int rcGuest, const char *pcszWhat)
526{
527 AssertPtrReturn(pcszWhat, "");
528
529 Utf8Str strErr;
530 switch (rcGuest)
531 {
532#define CASE_MSG(a_iRc, ...) \
533 case a_iRc: strErr.printf(__VA_ARGS__); break;
534
535 CASE_MSG(VERR_FILE_NOT_FOUND, tr("No such file or directory \"%s\" on guest"), pcszWhat); /* This is the most likely error. */
536 CASE_MSG(VERR_PATH_NOT_FOUND, tr("No such file or directory \"%s\" on guest"), pcszWhat);
537 CASE_MSG(VERR_INVALID_VM_HANDLE, tr("VMM device is not available (is the VM running?)"));
538 CASE_MSG(VERR_HGCM_SERVICE_NOT_FOUND, tr("The guest execution service is not available"));
539 CASE_MSG(VERR_BAD_EXE_FORMAT, tr("The file \"%s\" is not an executable format on guest"), pcszWhat);
540 CASE_MSG(VERR_AUTHENTICATION_FAILURE, tr("The user \"%s\" was not able to logon on guest"), pcszWhat);
541 CASE_MSG(VERR_INVALID_NAME, tr("The file \"%s\" is an invalid name"), pcszWhat);
542 CASE_MSG(VERR_TIMEOUT, tr("The guest did not respond within time"));
543 CASE_MSG(VERR_CANCELLED, tr("The execution operation for \"%s\" was canceled"), pcszWhat);
544 CASE_MSG(VERR_GSTCTL_MAX_CID_OBJECTS_REACHED, tr("Maximum number of concurrent guest processes has been reached"));
545 CASE_MSG(VERR_NOT_FOUND, tr("The guest execution service is not ready (yet)"));
546 default:
547 strErr.printf(tr("Error %Rrc for guest process \"%s\" occurred\n"), rcGuest, pcszWhat);
548 break;
549#undef CASE_MSG
550 }
551
552 return strErr;
553}
554
555/**
556 * Returns @c true if the passed in error code indicates an error which came
557 * from the guest side, or @c false if not.
558 *
559 * @return bool @c true if the passed in error code indicates an error which came
560 * from the guest side, or @c false if not.
561 * @param rc Error code to check.
562 */
563/* static */
564bool GuestProcess::i_isGuestError(int rc)
565{
566 return ( rc == VERR_GSTCTL_GUEST_ERROR
567 || rc == VERR_GSTCTL_PROCESS_EXIT_CODE);
568}
569
570/**
571 * Returns whether the guest process is alive (i.e. running) or not.
572 *
573 * @returns \c true if alive and running, or \c false if not.
574 */
575inline bool GuestProcess::i_isAlive(void)
576{
577 return ( mData.mStatus == ProcessStatus_Started
578 || mData.mStatus == ProcessStatus_Paused
579 || mData.mStatus == ProcessStatus_Terminating);
580}
581
582/**
583 * Returns whether the guest process has ended (i.e. terminated) or not.
584 *
585 * @returns \c true if ended, or \c false if not.
586 */
587inline bool GuestProcess::i_hasEnded(void)
588{
589 return ( mData.mStatus == ProcessStatus_TerminatedNormally
590 || mData.mStatus == ProcessStatus_TerminatedSignal
591 || mData.mStatus == ProcessStatus_TerminatedAbnormally
592 || mData.mStatus == ProcessStatus_TimedOutKilled
593 || mData.mStatus == ProcessStatus_TimedOutAbnormally
594 || mData.mStatus == ProcessStatus_Down
595 || mData.mStatus == ProcessStatus_Error);
596}
597
598/**
599 * Called when the guest side of the process has been disconnected (closed, terminated, +++).
600 *
601 * @returns VBox status code.
602 * @param pCbCtx Host callback context.
603 * @param pSvcCbData Host callback data.
604 */
605int GuestProcess::i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
606{
607 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
608 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
609
610 int vrc = i_setProcessStatus(ProcessStatus_Down, VINF_SUCCESS);
611
612 LogFlowFuncLeaveRC(vrc);
613 return vrc;
614}
615
616/**
617 * Sets (reports) the current input status of the guest process.
618 *
619 * @returns VBox status code.
620 * @param pCbCtx Host callback context.
621 * @param pSvcCbData Host callback data.
622 *
623 * @note Takes the write lock.
624 */
625int GuestProcess::i_onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
626{
627 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
628 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
629 /* pCallback is optional. */
630
631 if (pSvcCbData->mParms < 5)
632 return VERR_INVALID_PARAMETER;
633
634 CALLBACKDATA_PROC_INPUT dataCb;
635 /* pSvcCb->mpaParms[0] always contains the context ID. */
636 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
637 AssertRCReturn(vrc, vrc);
638 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uStatus);
639 AssertRCReturn(vrc, vrc);
640 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
641 AssertRCReturn(vrc, vrc);
642 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[4], &dataCb.uProcessed);
643 AssertRCReturn(vrc, vrc);
644
645 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32\n",
646 dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed));
647
648 vrc = i_checkPID(dataCb.uPID);
649 if (RT_SUCCESS(vrc))
650 {
651 ProcessInputStatus_T inputStatus = ProcessInputStatus_Undefined;
652 switch (dataCb.uStatus)
653 {
654 case INPUT_STS_WRITTEN:
655 inputStatus = ProcessInputStatus_Written;
656 break;
657 case INPUT_STS_ERROR:
658 inputStatus = ProcessInputStatus_Broken;
659 break;
660 case INPUT_STS_TERMINATED:
661 inputStatus = ProcessInputStatus_Broken;
662 break;
663 case INPUT_STS_OVERFLOW:
664 inputStatus = ProcessInputStatus_Overflow;
665 break;
666 case INPUT_STS_UNDEFINED:
667 /* Fall through is intentional. */
668 default:
669 AssertMsg(!dataCb.uProcessed, ("Processed data is not 0 in undefined input state\n"));
670 break;
671 }
672
673 if (inputStatus != ProcessInputStatus_Undefined)
674 {
675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
676
677 /* Copy over necessary data before releasing lock again. */
678 uint32_t uPID = mData.mPID;
679 /** @todo Also handle mSession? */
680
681 alock.release(); /* Release lock before firing off event. */
682
683 ::FireGuestProcessInputNotifyEvent(mEventSource, mSession, this, uPID, 0 /* StdIn */, dataCb.uProcessed, inputStatus);
684 }
685 }
686
687 LogFlowFuncLeaveRC(vrc);
688 return vrc;
689}
690
691/**
692 * Notifies of an I/O operation of the guest process.
693 *
694 * @returns VERR_NOT_IMPLEMENTED -- not implemented yet.
695 * @param pCbCtx Host callback context.
696 * @param pSvcCbData Host callback data.
697 */
698int GuestProcess::i_onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
699{
700 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
701 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
702
703 return VERR_NOT_IMPLEMENTED;
704}
705
706/**
707 * Sets (reports) the current running status of the guest process.
708 *
709 * @returns VBox status code.
710 * @param pCbCtx Host callback context.
711 * @param pSvcCbData Host callback data.
712 *
713 * @note Takes the write lock.
714 */
715int GuestProcess::i_onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
716{
717 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
718 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
719
720 if (pSvcCbData->mParms < 5)
721 return VERR_INVALID_PARAMETER;
722
723 CALLBACKDATA_PROC_STATUS dataCb;
724 /* pSvcCb->mpaParms[0] always contains the context ID. */
725 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
726 AssertRCReturn(vrc, vrc);
727 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uStatus);
728 AssertRCReturn(vrc, vrc);
729 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
730 AssertRCReturn(vrc, vrc);
731 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[4], &dataCb.pvData, &dataCb.cbData);
732 AssertRCReturn(vrc, vrc);
733
734 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32\n",
735 dataCb.uPID, dataCb.uStatus, dataCb.uFlags));
736
737 vrc = i_checkPID(dataCb.uPID);
738 if (RT_SUCCESS(vrc))
739 {
740 ProcessStatus_T procStatus = ProcessStatus_Undefined;
741 int procRc = VINF_SUCCESS;
742
743 switch (dataCb.uStatus)
744 {
745 case PROC_STS_STARTED:
746 {
747 procStatus = ProcessStatus_Started;
748
749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
750 mData.mPID = dataCb.uPID; /* Set the process PID. */
751 break;
752 }
753
754 case PROC_STS_TEN:
755 {
756 procStatus = ProcessStatus_TerminatedNormally;
757
758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
759 mData.mExitCode = dataCb.uFlags; /* Contains the exit code. */
760 break;
761 }
762
763 case PROC_STS_TES:
764 {
765 procStatus = ProcessStatus_TerminatedSignal;
766
767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
768 mData.mExitCode = dataCb.uFlags; /* Contains the signal. */
769 break;
770 }
771
772 case PROC_STS_TEA:
773 {
774 procStatus = ProcessStatus_TerminatedAbnormally;
775 break;
776 }
777
778 case PROC_STS_TOK:
779 {
780 procStatus = ProcessStatus_TimedOutKilled;
781 break;
782 }
783
784 case PROC_STS_TOA:
785 {
786 procStatus = ProcessStatus_TimedOutAbnormally;
787 break;
788 }
789
790 case PROC_STS_DWN:
791 {
792 procStatus = ProcessStatus_Down;
793 break;
794 }
795
796 case PROC_STS_ERROR:
797 {
798 procRc = dataCb.uFlags; /* mFlags contains the IPRT error sent from the guest. */
799 procStatus = ProcessStatus_Error;
800 break;
801 }
802
803 case PROC_STS_UNDEFINED:
804 default:
805 {
806 /* Silently skip this request. */
807 procStatus = ProcessStatus_Undefined;
808 break;
809 }
810 }
811
812 LogFlowThisFunc(("Got rc=%Rrc, procSts=%RU32, procRc=%Rrc\n",
813 vrc, procStatus, procRc));
814
815 /* Set the process status. */
816 int rc2 = i_setProcessStatus(procStatus, procRc);
817 if (RT_SUCCESS(vrc))
818 vrc = rc2;
819 }
820
821 LogFlowFuncLeaveRC(vrc);
822 return vrc;
823}
824
825/**
826 * Sets (reports) the current output status of the guest process.
827 *
828 * @returns VBox status code.
829 * @param pCbCtx Host callback context.
830 * @param pSvcCbData Host callback data.
831 */
832int GuestProcess::i_onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
833{
834 RT_NOREF(pCbCtx);
835 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
836
837 if (pSvcCbData->mParms < 5)
838 return VERR_INVALID_PARAMETER;
839
840 CALLBACKDATA_PROC_OUTPUT dataCb;
841 /* pSvcCb->mpaParms[0] always contains the context ID. */
842 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
843 AssertRCReturn(vrc, vrc);
844 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uHandle);
845 AssertRCReturn(vrc, vrc);
846 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
847 AssertRCReturn(vrc, vrc);
848 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[4], &dataCb.pvData, &dataCb.cbData);
849 AssertRCReturn(vrc, vrc);
850
851 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32\n",
852 dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData));
853
854 vrc = i_checkPID(dataCb.uPID);
855 if (RT_SUCCESS(vrc))
856 {
857 com::SafeArray<BYTE> data((size_t)dataCb.cbData);
858 if (dataCb.cbData)
859 data.initFrom((BYTE*)dataCb.pvData, dataCb.cbData);
860
861 ::FireGuestProcessOutputEvent(mEventSource, mSession, this,
862 mData.mPID, dataCb.uHandle, dataCb.cbData, ComSafeArrayAsInParam(data));
863 }
864
865 LogFlowFuncLeaveRC(vrc);
866 return vrc;
867}
868
869/**
870 * @copydoc GuestObject::i_onUnregister
871 */
872int GuestProcess::i_onUnregister(void)
873{
874 LogFlowThisFuncEnter();
875
876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
877
878 int vrc = VINF_SUCCESS;
879
880 /*
881 * Note: The event source stuff holds references to this object,
882 * so make sure that this is cleaned up *before* calling uninit().
883 */
884 if (!mEventSource.isNull())
885 {
886 mEventSource->UnregisterListener(mLocalListener);
887
888 mLocalListener.setNull();
889 unconst(mEventSource).setNull();
890 }
891
892 LogFlowFuncLeaveRC(vrc);
893 return vrc;
894}
895
896/**
897 * @copydoc GuestObject::i_onSessionStatusChange
898 */
899int GuestProcess::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
900{
901 LogFlowThisFuncEnter();
902
903 int vrc = VINF_SUCCESS;
904
905 /* If the session now is in a terminated state, set the process status
906 * to "down", as there is not much else we can do now. */
907 if (GuestSession::i_isTerminated(enmSessionStatus))
908 {
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 vrc = i_setProcessStatus(ProcessStatus_Down, 0 /* rc, ignored */);
912 }
913
914 LogFlowFuncLeaveRC(vrc);
915 return vrc;
916}
917
918/**
919 * Reads data from a guest file.
920 *
921 * @returns VBox status code.
922 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
923 * @param uHandle Internal file handle to use for reading.
924 * @param uSize Size (in bytes) to read.
925 * @param uTimeoutMS Timeout (in ms) to wait.
926 * @param pvData Where to store the read data on success.
927 * @param cbData Size (in bytes) of \a pvData on input.
928 * @param pcbRead Where to return to size (in bytes) read on success.
929 * Optional.
930 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
931 * was returned. Optional.
932 *
933 * @note Takes the write lock.
934 */
935int GuestProcess::i_readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
936 void *pvData, size_t cbData, uint32_t *pcbRead, int *prcGuest)
937{
938 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32, prcGuest=%p\n",
939 mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData, prcGuest));
940 AssertReturn(uSize, VERR_INVALID_PARAMETER);
941 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
942 AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
943 /* pcbRead is optional. */
944
945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
946
947 if ( mData.mStatus != ProcessStatus_Started
948 /* Skip reading if the process wasn't started with the appropriate
949 * flags. */
950 || ( ( uHandle == GUEST_PROC_OUT_H_STDOUT
951 || uHandle == GUEST_PROC_OUT_H_STDOUT_DEPRECATED)
952 && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdOut))
953 || ( uHandle == GUEST_PROC_OUT_H_STDERR
954 && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdErr))
955 )
956 {
957 if (pcbRead)
958 *pcbRead = 0;
959 if (prcGuest)
960 *prcGuest = VINF_SUCCESS;
961 return VINF_SUCCESS; /* Nothing to read anymore. */
962 }
963
964 int vrc;
965
966 GuestWaitEvent *pEvent = NULL;
967 GuestEventTypes eventTypes;
968 try
969 {
970 /*
971 * On Guest Additions < 4.3 there is no guarantee that the process status
972 * change arrives *after* the output event, e.g. if this was the last output
973 * block being read and the process will report status "terminate".
974 * So just skip checking for process status change and only wait for the
975 * output event.
976 */
977 if (mSession->i_getProtocolVersion() >= 2)
978 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
979 eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
980
981 vrc = registerWaitEvent(eventTypes, &pEvent);
982 }
983 catch (std::bad_alloc &)
984 {
985 vrc = VERR_NO_MEMORY;
986 }
987
988 if (RT_FAILURE(vrc))
989 return vrc;
990
991 if (RT_SUCCESS(vrc))
992 {
993 VBOXHGCMSVCPARM paParms[8];
994 int i = 0;
995 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
996 HGCMSvcSetU32(&paParms[i++], mData.mPID);
997 HGCMSvcSetU32(&paParms[i++], uHandle);
998 HGCMSvcSetU32(&paParms[i++], 0 /* Flags, none set yet. */);
999
1000 alock.release(); /* Drop the write lock before sending. */
1001
1002 vrc = sendMessage(HOST_MSG_EXEC_GET_OUTPUT, i, paParms);
1003 }
1004
1005 if (RT_SUCCESS(vrc))
1006 vrc = i_waitForOutput(pEvent, uHandle, uTimeoutMS,
1007 pvData, cbData, pcbRead);
1008
1009 unregisterWaitEvent(pEvent);
1010
1011 LogFlowFuncLeaveRC(vrc);
1012 return vrc;
1013}
1014
1015/**
1016 * Sets (reports) the current (overall) status of the guest process.
1017 *
1018 * @returns VBox status code.
1019 * @param procStatus Guest process status to set.
1020 * @param procRc Guest process result code to set.
1021 *
1022 * @note Takes the write lock.
1023 */
1024int GuestProcess::i_setProcessStatus(ProcessStatus_T procStatus, int procRc)
1025{
1026 LogFlowThisFuncEnter();
1027
1028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, procRc=%Rrc\n",
1031 mData.mStatus, procStatus, procRc));
1032
1033 if (procStatus == ProcessStatus_Error)
1034 {
1035 AssertMsg(RT_FAILURE(procRc), ("Guest rc must be an error (%Rrc)\n", procRc));
1036 /* Do not allow overwriting an already set error. If this happens
1037 * this means we forgot some error checking/locking somewhere. */
1038 AssertMsg(RT_SUCCESS(mData.mLastError), ("Guest rc already set (to %Rrc)\n", mData.mLastError));
1039 }
1040 else
1041 AssertMsg(RT_SUCCESS(procRc), ("Guest rc must not be an error (%Rrc)\n", procRc));
1042
1043 int rc = VINF_SUCCESS;
1044
1045 if (mData.mStatus != procStatus) /* Was there a process status change? */
1046 {
1047 mData.mStatus = procStatus;
1048 mData.mLastError = procRc;
1049
1050 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1051 HRESULT hr = errorInfo.createObject();
1052 ComAssertComRC(hr);
1053 if (RT_FAILURE(mData.mLastError))
1054 {
1055 hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, mData.mLastError,
1056 COM_IIDOF(IGuestProcess), getComponentName(),
1057 i_guestErrorToString(mData.mLastError, mData.mProcess.mExecutable.c_str()));
1058 ComAssertComRC(hr);
1059 }
1060
1061 /* Copy over necessary data before releasing lock again. */
1062 uint32_t uPID = mData.mPID;
1063 /** @todo Also handle mSession? */
1064
1065 alock.release(); /* Release lock before firing off event. */
1066
1067 ::FireGuestProcessStateChangedEvent(mEventSource, mSession, this, uPID, procStatus, errorInfo);
1068#if 0
1069 /*
1070 * On Guest Additions < 4.3 there is no guarantee that outstanding
1071 * requests will be delivered to the host after the process has ended,
1072 * so just cancel all waiting events here to not let clients run
1073 * into timeouts.
1074 */
1075 if ( mSession->getProtocolVersion() < 2
1076 && hasEnded())
1077 {
1078 LogFlowThisFunc(("Process ended, canceling outstanding wait events ...\n"));
1079 rc = cancelWaitEvents();
1080 }
1081#endif
1082 }
1083
1084 return rc;
1085}
1086
1087/**
1088 * Starts the process on the guest.
1089 *
1090 * @returns VBox status code.
1091 * @param cMsTimeout Timeout (in ms) to wait for starting the process.
1092 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1093 * was returned. Optional.
1094 *
1095 * @note Takes the write lock.
1096 */
1097int GuestProcess::i_startProcess(uint32_t cMsTimeout, int *prcGuest)
1098{
1099 LogFlowThisFunc(("cMsTimeout=%RU32, procExe=%s, procTimeoutMS=%RU32, procFlags=%x, sessionID=%RU32\n",
1100 cMsTimeout, mData.mProcess.mExecutable.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags,
1101 mSession->i_getId()));
1102
1103 /* Wait until the caller function (if kicked off by a thread)
1104 * has returned and continue operation. */
1105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 mData.mStatus = ProcessStatus_Starting;
1108
1109 int vrc;
1110
1111 GuestWaitEvent *pEvent = NULL;
1112 GuestEventTypes eventTypes;
1113 try
1114 {
1115 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1116 vrc = registerWaitEvent(eventTypes, &pEvent);
1117 }
1118 catch (std::bad_alloc &)
1119 {
1120 vrc = VERR_NO_MEMORY;
1121 }
1122 if (RT_FAILURE(vrc))
1123 return vrc;
1124
1125 vrc = i_startProcessInner(cMsTimeout, alock, pEvent, prcGuest);
1126
1127 unregisterWaitEvent(pEvent);
1128
1129 LogFlowFuncLeaveRC(vrc);
1130 return vrc;
1131}
1132
1133/**
1134 * Helper function to start a process on the guest. Do not call directly!
1135 *
1136 * @returns VBox status code.
1137 * @param cMsTimeout Timeout (in ms) to wait for starting the process.
1138 * @param rLock Write lock to use for serialization.
1139 * @param pEvent Event to use for notifying waiters.
1140 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1141 * was returned. Optional.
1142 */
1143int GuestProcess::i_startProcessInner(uint32_t cMsTimeout, AutoWriteLock &rLock, GuestWaitEvent *pEvent, int *prcGuest)
1144{
1145 GuestSession *pSession = mSession;
1146 AssertPtr(pSession);
1147 uint32_t const uProtocol = pSession->i_getProtocolVersion();
1148
1149 const GuestCredentials &sessionCreds = pSession->i_getCredentials();
1150
1151 /* Prepare arguments. */
1152 size_t cArgs = mData.mProcess.mArguments.size();
1153 if (cArgs >= 128*1024)
1154 return VERR_BUFFER_OVERFLOW;
1155
1156 size_t cbArgs = 0;
1157 char *pszArgs = NULL;
1158 int vrc = VINF_SUCCESS;
1159 if (cArgs)
1160 {
1161 char const **papszArgv = (char const **)RTMemAlloc((cArgs + 1) * sizeof(papszArgv[0]));
1162 AssertReturn(papszArgv, VERR_NO_MEMORY);
1163
1164 for (size_t i = 0; i < cArgs; i++)
1165 {
1166 papszArgv[i] = mData.mProcess.mArguments[i].c_str();
1167 AssertPtr(papszArgv[i]);
1168 }
1169 papszArgv[cArgs] = NULL;
1170
1171 Guest *pGuest = mSession->i_getParent();
1172 AssertPtr(pGuest);
1173
1174 const uint64_t fGuestControlFeatures0 = pGuest->i_getGuestControlFeatures0();
1175
1176 /* If the Guest Additions don't support using argv[0] correctly (< 6.1.x), don't supply it. */
1177 if (!(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0))
1178 vrc = RTGetOptArgvToString(&pszArgs, papszArgv + 1, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1179 else /* ... else send the whole argv, including argv[0]. */
1180 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1181
1182 RTMemFree(papszArgv);
1183 if (RT_FAILURE(vrc))
1184 return vrc;
1185
1186 /* Note! No direct returns after this. */
1187 }
1188
1189 /* Calculate arguments size (in bytes). */
1190 AssertPtr(pszArgs);
1191 cbArgs = strlen(pszArgs) + 1; /* Include terminating zero. */
1192
1193 /* Prepare environment. The guest service dislikes the empty string at the end, so drop it. */
1194 size_t cbEnvBlock = 0; /* Shut up MSVC. */
1195 char *pszzEnvBlock = NULL; /* Ditto. */
1196 vrc = mData.mProcess.mEnvironmentChanges.queryUtf8Block(&pszzEnvBlock, &cbEnvBlock);
1197 if (RT_SUCCESS(vrc))
1198 {
1199 Assert(cbEnvBlock > 0);
1200 cbEnvBlock--;
1201 AssertPtr(pszzEnvBlock);
1202
1203 /* Prepare HGCM call. */
1204 VBOXHGCMSVCPARM paParms[16];
1205 int i = 0;
1206 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1207 HGCMSvcSetRTCStr(&paParms[i++], mData.mProcess.mExecutable);
1208 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mFlags);
1209 HGCMSvcSetU32(&paParms[i++], (uint32_t)mData.mProcess.mArguments.size());
1210 HGCMSvcSetPv(&paParms[i++], pszArgs, (uint32_t)cbArgs);
1211 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mEnvironmentChanges.count());
1212 HGCMSvcSetU32(&paParms[i++], (uint32_t)cbEnvBlock);
1213 HGCMSvcSetPv(&paParms[i++], pszzEnvBlock, (uint32_t)cbEnvBlock);
1214 if (uProtocol < 2)
1215 {
1216 /* In protocol v1 (VBox < 4.3) the credentials were part of the execution
1217 * call. In newer protocols these credentials are part of the opened guest
1218 * session, so not needed anymore here. */
1219 HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mUser);
1220 HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mPassword);
1221 }
1222 /*
1223 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1224 * until the process was started - the process itself then gets an infinite timeout for execution.
1225 * This is handy when we want to start a process inside a worker thread within a certain timeout
1226 * but let the started process perform lengthly operations then.
1227 */
1228 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1229 HGCMSvcSetU32(&paParms[i++], UINT32_MAX /* Infinite timeout */);
1230 else
1231 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mTimeoutMS);
1232 if (uProtocol >= 2)
1233 {
1234 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mPriority);
1235 /* CPU affinity: We only support one CPU affinity block at the moment,
1236 * so that makes up to 64 CPUs total. This can be more in the future. */
1237 HGCMSvcSetU32(&paParms[i++], 1);
1238 /* The actual CPU affinity blocks. */
1239 HGCMSvcSetPv(&paParms[i++], (void *)&mData.mProcess.mAffinity, sizeof(mData.mProcess.mAffinity));
1240 }
1241
1242 rLock.release(); /* Drop the write lock before sending. */
1243
1244 vrc = sendMessage(HOST_MSG_EXEC_CMD, i, paParms);
1245 if (RT_FAILURE(vrc))
1246 {
1247 int rc2 = i_setProcessStatus(ProcessStatus_Error, vrc);
1248 AssertRC(rc2);
1249 }
1250
1251 mData.mProcess.mEnvironmentChanges.freeUtf8Block(pszzEnvBlock);
1252 }
1253
1254 RTStrFree(pszArgs);
1255
1256 if (RT_SUCCESS(vrc))
1257 vrc = i_waitForStatusChange(pEvent, cMsTimeout,
1258 NULL /* Process status */, prcGuest);
1259 return vrc;
1260}
1261
1262/**
1263 * Starts the process asynchronously (via worker thread) on the guest.
1264 *
1265 * @returns VBox status code.
1266 */
1267int GuestProcess::i_startProcessAsync(void)
1268{
1269 LogFlowThisFuncEnter();
1270
1271 /* Create the task: */
1272 GuestProcessStartTask *pTask = NULL;
1273 try
1274 {
1275 pTask = new GuestProcessStartTask(this);
1276 }
1277 catch (std::bad_alloc &)
1278 {
1279 LogFlowThisFunc(("out of memory\n"));
1280 return VERR_NO_MEMORY;
1281 }
1282 AssertReturnStmt(pTask->i_isOk(), delete pTask, E_FAIL); /* cannot fail for GuestProcessStartTask. */
1283 LogFlowThisFunc(("Successfully created GuestProcessStartTask object\n"));
1284
1285 /* Start the thread (always consumes the task): */
1286 HRESULT hrc = pTask->createThread();
1287 pTask = NULL;
1288 if (SUCCEEDED(hrc))
1289 return VINF_SUCCESS;
1290 LogFlowThisFunc(("Failed to create thread for GuestProcessStartTask\n"));
1291 return VERR_GENERAL_FAILURE;
1292}
1293
1294/**
1295 * Thread task which does the asynchronous starting of a guest process.
1296 *
1297 * @returns VBox status code.
1298 * @param pTask Process start task (context) to process.
1299 */
1300/* static */
1301int GuestProcess::i_startProcessThreadTask(GuestProcessStartTask *pTask)
1302{
1303 LogFlowFunc(("pTask=%p\n", pTask));
1304
1305 const ComObjPtr<GuestProcess> pProcess(pTask->i_process());
1306 Assert(!pProcess.isNull());
1307
1308 AutoCaller autoCaller(pProcess);
1309 if (FAILED(autoCaller.rc()))
1310 return VERR_COM_UNEXPECTED;
1311
1312 int vrc = pProcess->i_startProcess(30 * 1000 /* 30s timeout */, NULL /* Guest rc, ignored */);
1313 /* Nothing to do here anymore. */
1314
1315 LogFlowFunc(("pProcess=%p, vrc=%Rrc\n", (GuestProcess *)pProcess, vrc));
1316 return vrc;
1317}
1318
1319/**
1320 * Terminates a guest process.
1321 *
1322 * @returns VBox status code.
1323 * @retval VWRN_INVALID_STATE if process not in running state (anymore).
1324 * @retval VERR_NOT_SUPPORTED if process termination is not supported on the guest.
1325 * @param uTimeoutMS Timeout (in ms) to wait for process termination.
1326 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1327 * was returned. Optional.
1328 *
1329 * @note Takes the write lock.
1330 */
1331int GuestProcess::i_terminateProcess(uint32_t uTimeoutMS, int *prcGuest)
1332{
1333 /* prcGuest is optional. */
1334 LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
1335
1336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 int vrc = VINF_SUCCESS;
1339
1340 if (mData.mStatus != ProcessStatus_Started)
1341 {
1342 LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
1343 mData.mStatus));
1344 vrc = VWRN_INVALID_STATE;
1345 }
1346 else
1347 {
1348 AssertPtr(mSession);
1349 /* Note: VBox < 4.3 (aka protocol version 1) does not
1350 * support this, so just skip. */
1351 if (mSession->i_getProtocolVersion() < 2)
1352 vrc = VERR_NOT_SUPPORTED;
1353
1354 if (RT_SUCCESS(vrc))
1355 {
1356 GuestWaitEvent *pEvent = NULL;
1357 GuestEventTypes eventTypes;
1358 try
1359 {
1360 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1361
1362 vrc = registerWaitEvent(eventTypes, &pEvent);
1363 }
1364 catch (std::bad_alloc &)
1365 {
1366 vrc = VERR_NO_MEMORY;
1367 }
1368
1369 if (RT_FAILURE(vrc))
1370 return vrc;
1371
1372 VBOXHGCMSVCPARM paParms[4];
1373 int i = 0;
1374 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1375 HGCMSvcSetU32(&paParms[i++], mData.mPID);
1376
1377 alock.release(); /* Drop the write lock before sending. */
1378
1379 vrc = sendMessage(HOST_MSG_EXEC_TERMINATE, i, paParms);
1380 if (RT_SUCCESS(vrc))
1381 vrc = i_waitForStatusChange(pEvent, uTimeoutMS,
1382 NULL /* ProcessStatus */, prcGuest);
1383 unregisterWaitEvent(pEvent);
1384 }
1385 }
1386
1387 LogFlowFuncLeaveRC(vrc);
1388 return vrc;
1389}
1390
1391/**
1392 * Converts given process status / flags and wait flag combination
1393 * to an overall process wait result.
1394 *
1395 * @returns Overall process wait result.
1396 * @param fWaitFlags Process wait flags to use for conversion.
1397 * @param oldStatus Old process status to use for conversion.
1398 * @param newStatus New process status to use for conversion.
1399 * @param uProcFlags Process flags to use for conversion.
1400 * @param uProtocol Guest Control protocol version to use for conversion.
1401 */
1402/* static */
1403ProcessWaitResult_T GuestProcess::i_waitFlagsToResultEx(uint32_t fWaitFlags,
1404 ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
1405 uint32_t uProcFlags, uint32_t uProtocol)
1406{
1407 ProcessWaitResult_T waitResult = ProcessWaitResult_None;
1408
1409 switch (newStatus)
1410 {
1411 case ProcessStatus_TerminatedNormally:
1412 case ProcessStatus_TerminatedSignal:
1413 case ProcessStatus_TerminatedAbnormally:
1414 case ProcessStatus_Down:
1415 /* Nothing to wait for anymore. */
1416 waitResult = ProcessWaitResult_Terminate;
1417 break;
1418
1419 case ProcessStatus_TimedOutKilled:
1420 case ProcessStatus_TimedOutAbnormally:
1421 /* Dito. */
1422 waitResult = ProcessWaitResult_Timeout;
1423 break;
1424
1425 case ProcessStatus_Started:
1426 switch (oldStatus)
1427 {
1428 case ProcessStatus_Undefined:
1429 case ProcessStatus_Starting:
1430 /* Also wait for process start. */
1431 if (fWaitFlags & ProcessWaitForFlag_Start)
1432 waitResult = ProcessWaitResult_Start;
1433 else
1434 {
1435 /*
1436 * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
1437 * caller is not interested in getting further process statuses -- so just don't notify
1438 * anything here anymore and return.
1439 */
1440 if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1441 waitResult = ProcessWaitResult_Start;
1442 }
1443 break;
1444
1445 case ProcessStatus_Started:
1446 /* Only wait for process start. */
1447 if (fWaitFlags & ProcessWaitForFlag_Start)
1448 waitResult = ProcessWaitResult_Start;
1449 break;
1450
1451 default:
1452 AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
1453 oldStatus));
1454 if (fWaitFlags & ProcessWaitForFlag_Start)
1455 waitResult = ProcessWaitResult_Start;
1456 break;
1457 }
1458 break;
1459
1460 case ProcessStatus_Error:
1461 /* Nothing to wait for anymore. */
1462 waitResult = ProcessWaitResult_Error;
1463 break;
1464
1465 case ProcessStatus_Undefined:
1466 case ProcessStatus_Starting:
1467 case ProcessStatus_Terminating:
1468 case ProcessStatus_Paused:
1469 /* No result available yet, leave wait
1470 * flags untouched. */
1471 break;
1472#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1473 case ProcessStatus_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
1474#endif
1475 }
1476
1477 if (newStatus == ProcessStatus_Started)
1478 {
1479 /*
1480 * Filter out waits which are *not* supported using
1481 * older guest control Guest Additions.
1482 *
1483 */
1484 /** @todo ProcessWaitForFlag_Std* flags are not implemented yet. */
1485 if (uProtocol < 99) /* See @todo above. */
1486 {
1487 if ( waitResult == ProcessWaitResult_None
1488 /* We don't support waiting for stdin, out + err,
1489 * just skip waiting then. */
1490 && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1491 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1492 || (fWaitFlags & ProcessWaitForFlag_StdErr)
1493 )
1494 )
1495 {
1496 /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
1497 waitResult = ProcessWaitResult_WaitFlagNotSupported;
1498 }
1499 }
1500 }
1501
1502#ifdef DEBUG
1503 LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
1504 oldStatus, newStatus, fWaitFlags, waitResult));
1505#endif
1506 return waitResult;
1507}
1508
1509/**
1510 * Converts given wait flags to an overall process wait result.
1511 *
1512 * @returns Overall process wait result.
1513 * @param fWaitFlags Process wait flags to use for conversion.
1514 */
1515ProcessWaitResult_T GuestProcess::i_waitFlagsToResult(uint32_t fWaitFlags)
1516{
1517 AssertPtr(mSession);
1518 return GuestProcess::i_waitFlagsToResultEx(fWaitFlags,
1519 mData.mStatus /* oldStatus */, mData.mStatus /* newStatus */,
1520 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1521}
1522
1523/**
1524 * Waits for certain events of the guest process.
1525 *
1526 * @returns VBox status code.
1527 * @param fWaitFlags Process wait flags to wait for.
1528 * @param uTimeoutMS Timeout (in ms) to wait.
1529 * @param waitResult Where to return the process wait result on success.
1530 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1531 * was returned. Optional.
1532 * @note Takes the read lock.
1533 */
1534int GuestProcess::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS,
1535 ProcessWaitResult_T &waitResult, int *prcGuest)
1536{
1537 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1538
1539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1540
1541 LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, procRc=%Rrc, prcGuest=%p\n",
1542 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, prcGuest));
1543
1544 /* Did some error occur before? Then skip waiting and return. */
1545 ProcessStatus_T curStatus = mData.mStatus;
1546 if (curStatus == ProcessStatus_Error)
1547 {
1548 waitResult = ProcessWaitResult_Error;
1549 AssertMsg(RT_FAILURE(mData.mLastError),
1550 ("No error rc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
1551 if (prcGuest)
1552 *prcGuest = mData.mLastError; /* Return last set error. */
1553 LogFlowThisFunc(("Process is in error state (rcGuest=%Rrc)\n", mData.mLastError));
1554 return VERR_GSTCTL_GUEST_ERROR;
1555 }
1556
1557 waitResult = i_waitFlagsToResult(fWaitFlags);
1558
1559 /* No waiting needed? Return immediately using the last set error. */
1560 if (waitResult != ProcessWaitResult_None)
1561 {
1562 if (prcGuest)
1563 *prcGuest = mData.mLastError; /* Return last set error (if any). */
1564 LogFlowThisFunc(("Nothing to wait for (rcGuest=%Rrc)\n", mData.mLastError));
1565 return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
1566 }
1567
1568 /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
1569 if (!uTimeoutMS)
1570 uTimeoutMS = RT_INDEFINITE_WAIT;
1571
1572 int vrc;
1573
1574 GuestWaitEvent *pEvent = NULL;
1575 GuestEventTypes eventTypes;
1576 try
1577 {
1578 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1579
1580 vrc = registerWaitEvent(eventTypes, &pEvent);
1581 }
1582 catch (std::bad_alloc &)
1583 {
1584 vrc = VERR_NO_MEMORY;
1585 }
1586
1587 if (RT_FAILURE(vrc))
1588 return vrc;
1589
1590 alock.release(); /* Release lock before waiting. */
1591
1592 /*
1593 * Do the actual waiting.
1594 */
1595 ProcessStatus_T newStatus = ProcessStatus_Undefined;
1596 uint64_t u64StartMS = RTTimeMilliTS();
1597 for (;;)
1598 {
1599 uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
1600 if ( uTimeoutMS != RT_INDEFINITE_WAIT
1601 && u64ElapsedMS >= uTimeoutMS)
1602 {
1603 vrc = VERR_TIMEOUT;
1604 break;
1605 }
1606
1607 vrc = i_waitForStatusChange(pEvent,
1608 uTimeoutMS == RT_INDEFINITE_WAIT
1609 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
1610 &newStatus, prcGuest);
1611 if (RT_SUCCESS(vrc))
1612 {
1613 alock.acquire();
1614
1615 waitResult = i_waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
1616 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1617#ifdef DEBUG
1618 LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
1619 fWaitFlags, newStatus, waitResult));
1620#endif
1621 if (ProcessWaitResult_None != waitResult) /* We got a waiting result. */
1622 break;
1623 }
1624 else /* Waiting failed, bail out. */
1625 break;
1626
1627 alock.release(); /* Don't hold lock in next waiting round. */
1628 }
1629
1630 unregisterWaitEvent(pEvent);
1631
1632 LogFlowThisFunc(("Returned waitResult=%RU32, newStatus=%RU32, rc=%Rrc\n",
1633 waitResult, newStatus, vrc));
1634 return vrc;
1635}
1636
1637/**
1638 * Waits for a guest process input notification.
1639 *
1640 * @param pEvent Wait event to use for waiting.
1641 * @param uHandle Guest process file handle to wait for.
1642 * @param uTimeoutMS Timeout (in ms) to wait.
1643 * @param pInputStatus Where to return the process input status on success.
1644 * @param pcbProcessed Where to return the processed input (in bytes) on success.
1645 */
1646int GuestProcess::i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1647 ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
1648{
1649 RT_NOREF(uHandle);
1650 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1651
1652 VBoxEventType_T evtType;
1653 ComPtr<IEvent> pIEvent;
1654 int vrc = waitForEvent(pEvent, uTimeoutMS,
1655 &evtType, pIEvent.asOutParam());
1656 if (RT_SUCCESS(vrc))
1657 {
1658 if (evtType == VBoxEventType_OnGuestProcessInputNotify)
1659 {
1660 ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
1661 Assert(!pProcessEvent.isNull());
1662
1663 if (pInputStatus)
1664 {
1665 HRESULT hr2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
1666 ComAssertComRC(hr2);
1667 }
1668 if (pcbProcessed)
1669 {
1670 HRESULT hr2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
1671 ComAssertComRC(hr2);
1672 }
1673 }
1674 else
1675 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1676 }
1677
1678 LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, rc=%Rrc\n",
1679 pEvent, uHandle, vrc));
1680 return vrc;
1681}
1682
1683/**
1684 * Waits for a guest process input notification.
1685 *
1686 * @returns VBox status code.
1687 * @param pEvent Wait event to use for waiting.
1688 * @param uHandle Guest process file handle to wait for.
1689 * @param uTimeoutMS Timeout (in ms) to wait.
1690 * @param pvData Where to store the guest process output on success.
1691 * @param cbData Size (in bytes) of \a pvData.
1692 * @param pcbRead Where to return the size (in bytes) read.
1693 */
1694int GuestProcess::i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1695 void *pvData, size_t cbData, uint32_t *pcbRead)
1696{
1697 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1698 /* pvData is optional. */
1699 /* cbData is optional. */
1700 /* pcbRead is optional. */
1701
1702 LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
1703 pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
1704
1705 int vrc;
1706
1707 VBoxEventType_T evtType;
1708 ComPtr<IEvent> pIEvent;
1709 do
1710 {
1711 vrc = waitForEvent(pEvent, uTimeoutMS,
1712 &evtType, pIEvent.asOutParam());
1713 if (RT_SUCCESS(vrc))
1714 {
1715 if (evtType == VBoxEventType_OnGuestProcessOutput)
1716 {
1717 ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
1718 Assert(!pProcessEvent.isNull());
1719
1720 ULONG uHandleEvent;
1721 HRESULT hr = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
1722 if ( SUCCEEDED(hr)
1723 && uHandleEvent == uHandle)
1724 {
1725 if (pvData)
1726 {
1727 com::SafeArray <BYTE> data;
1728 hr = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1729 ComAssertComRC(hr);
1730 size_t cbRead = data.size();
1731 if (cbRead)
1732 {
1733 if (cbRead <= cbData)
1734 {
1735 /* Copy data from event into our buffer. */
1736 memcpy(pvData, data.raw(), data.size());
1737 }
1738 else
1739 vrc = VERR_BUFFER_OVERFLOW;
1740
1741 LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), rc=%Rrc\n",
1742 cbRead, uHandleEvent, vrc));
1743 }
1744 }
1745
1746 if ( RT_SUCCESS(vrc)
1747 && pcbRead)
1748 {
1749 ULONG cbRead;
1750 hr = pProcessEvent->COMGETTER(Processed)(&cbRead);
1751 ComAssertComRC(hr);
1752 *pcbRead = (uint32_t)cbRead;
1753 }
1754
1755 break;
1756 }
1757 else if (FAILED(hr))
1758 vrc = VERR_COM_UNEXPECTED;
1759 }
1760 else
1761 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1762 }
1763
1764 } while (vrc == VINF_SUCCESS);
1765
1766 if ( vrc != VINF_SUCCESS
1767 && pcbRead)
1768 {
1769 *pcbRead = 0;
1770 }
1771
1772 LogFlowFuncLeaveRC(vrc);
1773 return vrc;
1774}
1775
1776/**
1777 * Waits for a guest process status change.
1778 *
1779 * @returns VBox status code.
1780 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1781 * @param pEvent Guest wait event to wait for.
1782 * @param uTimeoutMS Timeout (in ms) to wait.
1783 * @param pProcessStatus Where to return the process status on success.
1784 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1785 * was returned.
1786 */
1787int GuestProcess::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1788 ProcessStatus_T *pProcessStatus, int *prcGuest)
1789{
1790 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1791 /* pProcessStatus is optional. */
1792 /* prcGuest is optional. */
1793
1794 VBoxEventType_T evtType;
1795 ComPtr<IEvent> pIEvent;
1796 int vrc = waitForEvent(pEvent, uTimeoutMS,
1797 &evtType, pIEvent.asOutParam());
1798 if (RT_SUCCESS(vrc))
1799 {
1800 Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
1801 ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
1802 Assert(!pProcessEvent.isNull());
1803
1804 ProcessStatus_T procStatus;
1805 HRESULT hr = pProcessEvent->COMGETTER(Status)(&procStatus);
1806 ComAssertComRC(hr);
1807 if (pProcessStatus)
1808 *pProcessStatus = procStatus;
1809
1810 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1811 hr = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
1812 ComAssertComRC(hr);
1813
1814 LONG lGuestRc;
1815 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1816 ComAssertComRC(hr);
1817
1818 LogFlowThisFunc(("Got procStatus=%RU32, rcGuest=%RI32 (%Rrc)\n",
1819 procStatus, lGuestRc, lGuestRc));
1820
1821 if (RT_FAILURE((int)lGuestRc))
1822 vrc = VERR_GSTCTL_GUEST_ERROR;
1823
1824 if (prcGuest)
1825 *prcGuest = (int)lGuestRc;
1826 }
1827 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
1828 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
1829 *prcGuest = pEvent->GuestResult();
1830 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
1831
1832 LogFlowFuncLeaveRC(vrc);
1833 return vrc;
1834}
1835
1836#if 0 /* Unused */
1837/* static */
1838bool GuestProcess::i_waitResultImpliesEx(ProcessWaitResult_T waitResult, ProcessStatus_T procStatus, uint32_t uProtocol)
1839{
1840 RT_NOREF(uProtocol);
1841
1842 bool fImplies;
1843
1844 switch (waitResult)
1845 {
1846 case ProcessWaitResult_Start:
1847 fImplies = procStatus == ProcessStatus_Started;
1848 break;
1849
1850 case ProcessWaitResult_Terminate:
1851 fImplies = ( procStatus == ProcessStatus_TerminatedNormally
1852 || procStatus == ProcessStatus_TerminatedSignal
1853 || procStatus == ProcessStatus_TerminatedAbnormally
1854 || procStatus == ProcessStatus_TimedOutKilled
1855 || procStatus == ProcessStatus_TimedOutAbnormally
1856 || procStatus == ProcessStatus_Down
1857 || procStatus == ProcessStatus_Error);
1858 break;
1859
1860 default:
1861 fImplies = false;
1862 break;
1863 }
1864
1865 return fImplies;
1866}
1867#endif /* unused */
1868
1869/**
1870 * Writes input data to a guest process.
1871 *
1872 * @returns VBox status code.
1873 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1874 * @param uHandle Guest process file handle to write to.
1875 * @param uFlags Input flags of type PRocessInputFlag_XXX.
1876 * @param pvData Data to write to the guest process.
1877 * @param cbData Size (in bytes) of \a pvData to write.
1878 * @param uTimeoutMS Timeout (in ms) to wait.
1879 * @param puWritten Where to return the size (in bytes) written. Optional.
1880 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1881 * was returned. Optional.
1882 *
1883 * @note Takes the write lock.
1884 */
1885int GuestProcess::i_writeData(uint32_t uHandle, uint32_t uFlags,
1886 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *prcGuest)
1887{
1888 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p, prcGuest=%p\n",
1889 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten, prcGuest));
1890 /* All is optional. There can be 0 byte writes. */
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 if (mData.mStatus != ProcessStatus_Started)
1894 {
1895 if (puWritten)
1896 *puWritten = 0;
1897 if (prcGuest)
1898 *prcGuest = VINF_SUCCESS;
1899 return VINF_SUCCESS; /* Not available for writing (anymore). */
1900 }
1901
1902 int vrc;
1903
1904 GuestWaitEvent *pEvent = NULL;
1905 GuestEventTypes eventTypes;
1906 try
1907 {
1908 /*
1909 * On Guest Additions < 4.3 there is no guarantee that the process status
1910 * change arrives *after* the input event, e.g. if this was the last input
1911 * block being written and the process will report status "terminate".
1912 * So just skip checking for process status change and only wait for the
1913 * input event.
1914 */
1915 if (mSession->i_getProtocolVersion() >= 2)
1916 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1917 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
1918
1919 vrc = registerWaitEvent(eventTypes, &pEvent);
1920 }
1921 catch (std::bad_alloc &)
1922 {
1923 vrc = VERR_NO_MEMORY;
1924 }
1925
1926 if (RT_FAILURE(vrc))
1927 return vrc;
1928
1929 VBOXHGCMSVCPARM paParms[5];
1930 int i = 0;
1931 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1932 HGCMSvcSetU32(&paParms[i++], mData.mPID);
1933 HGCMSvcSetU32(&paParms[i++], uFlags);
1934 HGCMSvcSetPv(&paParms[i++], pvData, (uint32_t)cbData);
1935 HGCMSvcSetU32(&paParms[i++], (uint32_t)cbData);
1936
1937 alock.release(); /* Drop the write lock before sending. */
1938
1939 uint32_t cbProcessed = 0;
1940 vrc = sendMessage(HOST_MSG_EXEC_SET_INPUT, i, paParms);
1941 if (RT_SUCCESS(vrc))
1942 {
1943 ProcessInputStatus_T inputStatus;
1944 vrc = i_waitForInputNotify(pEvent, uHandle, uTimeoutMS,
1945 &inputStatus, &cbProcessed);
1946 if (RT_SUCCESS(vrc))
1947 {
1948 /** @todo Set rcGuest. */
1949
1950 if (puWritten)
1951 *puWritten = cbProcessed;
1952 }
1953 /** @todo Error handling. */
1954 }
1955
1956 unregisterWaitEvent(pEvent);
1957
1958 LogFlowThisFunc(("Returning cbProcessed=%RU32, rc=%Rrc\n",
1959 cbProcessed, vrc));
1960 return vrc;
1961}
1962
1963// implementation of public methods
1964/////////////////////////////////////////////////////////////////////////////
1965
1966HRESULT GuestProcess::read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
1967{
1968 AutoCaller autoCaller(this);
1969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1970
1971 if (aToRead == 0)
1972 return setError(E_INVALIDARG, tr("The size to read is zero"));
1973
1974 LogFlowThisFuncEnter();
1975
1976 aData.resize(aToRead);
1977
1978 HRESULT hr = S_OK;
1979
1980 uint32_t cbRead;
1981 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1982 int vrc = i_readData(aHandle, aToRead, aTimeoutMS, &aData.front(), aToRead, &cbRead, &rcGuest);
1983 if (RT_SUCCESS(vrc))
1984 {
1985 if (aData.size() != cbRead)
1986 aData.resize(cbRead);
1987 }
1988 else
1989 {
1990 aData.resize(0);
1991
1992 switch (vrc)
1993 {
1994 case VERR_GSTCTL_GUEST_ERROR:
1995 {
1996 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, mData.mProcess.mExecutable.c_str());
1997 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
1998 tr("Reading %RU32 bytes from guest process handle %RU32 failed: %s", "", aToRead),
1999 aToRead, aHandle, GuestBase::getErrorAsString(ge).c_str());
2000 break;
2001 }
2002 default:
2003 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from guest process \"%s\" (PID %RU32) failed: %Rrc"),
2004 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2005 break;
2006 }
2007 }
2008
2009 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
2010
2011 LogFlowFuncLeaveRC(vrc);
2012 return hr;
2013}
2014
2015HRESULT GuestProcess::terminate()
2016{
2017 AutoCaller autoCaller(this);
2018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2019
2020 LogFlowThisFuncEnter();
2021
2022 HRESULT hr = S_OK;
2023
2024 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2025 int vrc = i_terminateProcess(30 * 1000 /* Timeout in ms */, &rcGuest);
2026 if (RT_FAILURE(vrc))
2027 {
2028 switch (vrc)
2029 {
2030 case VERR_GSTCTL_GUEST_ERROR:
2031 {
2032 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, mData.mProcess.mExecutable.c_str());
2033 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Terminating guest process failed: %s"),
2034 GuestBase::getErrorAsString(ge).c_str());
2035 break;
2036 }
2037 case VERR_NOT_SUPPORTED:
2038 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
2039 tr("Terminating guest process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
2040 mData.mProcess.mExecutable.c_str(), mData.mPID);
2041 break;
2042
2043 default:
2044 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Terminating guest process \"%s\" (PID %RU32) failed: %Rrc"),
2045 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2046 break;
2047 }
2048 }
2049
2050 if (vrc == VINF_SUCCESS) /* Note: Also could be VWRN_INVALID_STATE from i_terminateProcess(). */
2051 {
2052 /* Remove process from guest session list. Now only API clients
2053 * still can hold references to it. */
2054 AssertPtr(mSession);
2055 int rc2 = mSession->i_processUnregister(this);
2056 if (RT_SUCCESS(vrc))
2057 vrc = rc2;
2058 }
2059
2060 LogFlowFuncLeaveRC(vrc);
2061 return hr;
2062}
2063
2064HRESULT GuestProcess::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
2065{
2066 AutoCaller autoCaller(this);
2067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2068
2069 LogFlowThisFuncEnter();
2070
2071 /* Validate flags: */
2072 static ULONG const s_fValidFlags = ProcessWaitForFlag_None | ProcessWaitForFlag_Start | ProcessWaitForFlag_Terminate
2073 | ProcessWaitForFlag_StdIn | ProcessWaitForFlag_StdOut | ProcessWaitForFlag_StdErr;
2074 if (aWaitFor & ~s_fValidFlags)
2075 return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
2076 aWaitFor, aWaitFor & ~s_fValidFlags);
2077
2078 /*
2079 * Note: Do not hold any locks here while waiting!
2080 */
2081 HRESULT hr = S_OK;
2082
2083 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2084 ProcessWaitResult_T waitResult;
2085 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
2086 if (RT_SUCCESS(vrc))
2087 {
2088 *aReason = waitResult;
2089 }
2090 else
2091 {
2092 switch (vrc)
2093 {
2094 case VERR_GSTCTL_GUEST_ERROR:
2095 {
2096 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, mData.mProcess.mExecutable.c_str());
2097 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Waiting for guest process (flags %#x) failed: %s"),
2098 aWaitFor, GuestBase::getErrorAsString(ge).c_str());
2099 break;
2100 }
2101 case VERR_TIMEOUT:
2102 *aReason = ProcessWaitResult_Timeout;
2103 break;
2104
2105 default:
2106 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Waiting for guest process \"%s\" (PID %RU32) failed: %Rrc"),
2107 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2108 break;
2109 }
2110 }
2111
2112 LogFlowFuncLeaveRC(vrc);
2113 return hr;
2114}
2115
2116HRESULT GuestProcess::waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
2117 ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
2118{
2119 uint32_t fWaitFor = ProcessWaitForFlag_None;
2120 for (size_t i = 0; i < aWaitFor.size(); i++)
2121 fWaitFor |= aWaitFor[i];
2122
2123 return WaitFor(fWaitFor, aTimeoutMS, aReason);
2124}
2125
2126HRESULT GuestProcess::write(ULONG aHandle, ULONG aFlags, const std::vector<BYTE> &aData,
2127 ULONG aTimeoutMS, ULONG *aWritten)
2128{
2129 static ULONG const s_fValidFlags = ProcessInputFlag_None | ProcessInputFlag_EndOfFile;
2130 if (aFlags & ~s_fValidFlags)
2131 return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
2132 aFlags, aFlags & ~s_fValidFlags);
2133
2134 AutoCaller autoCaller(this);
2135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2136
2137 LogFlowThisFuncEnter();
2138
2139 HRESULT hr = S_OK;
2140
2141 uint32_t cbWritten;
2142 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2143 uint32_t cbData = (uint32_t)aData.size();
2144 void *pvData = cbData > 0 ? (void *)&aData.front() : NULL;
2145 int vrc = i_writeData(aHandle, aFlags, pvData, cbData, aTimeoutMS, &cbWritten, &rcGuest);
2146 if (RT_FAILURE(vrc))
2147 {
2148 switch (vrc)
2149 {
2150 case VERR_GSTCTL_GUEST_ERROR:
2151 {
2152 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, mData.mProcess.mExecutable.c_str());
2153 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
2154 tr("Writing %RU32 bytes (flags %#x) to guest process failed: %s", "", cbData),
2155 cbData, aFlags, GuestBase::getErrorAsString(ge).c_str());
2156 break;
2157 }
2158 default:
2159 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing to guest process \"%s\" (PID %RU32) failed: %Rrc"),
2160 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2161 break;
2162 }
2163 }
2164
2165 LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
2166
2167 *aWritten = (ULONG)cbWritten;
2168
2169 LogFlowFuncLeaveRC(vrc);
2170 return hr;
2171}
2172
2173HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFlag_T> &aFlags,
2174 const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
2175{
2176 LogFlowThisFuncEnter();
2177
2178 ULONG fWrite = ProcessInputFlag_None;
2179 for (size_t i = 0; i < aFlags.size(); i++)
2180 fWrite |= aFlags[i];
2181
2182 return write(aHandle, fWrite, aData, aTimeoutMS, aWritten);
2183}
2184
2185///////////////////////////////////////////////////////////////////////////////
2186
2187GuestProcessTool::GuestProcessTool(void)
2188 : pSession(NULL),
2189 pProcess(NULL)
2190{
2191}
2192
2193GuestProcessTool::~GuestProcessTool(void)
2194{
2195 uninit();
2196}
2197
2198/**
2199 * Initializes and starts a process tool on the guest.
2200 *
2201 * @returns VBox status code.
2202 * @param pGuestSession Guest session the process tools should be started in.
2203 * @param startupInfo Guest process startup info to use for starting.
2204 * @param fAsync Whether to start asynchronously or not.
2205 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2206 * was returned. Optional.
2207 */
2208int GuestProcessTool::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
2209 bool fAsync, int *prcGuest)
2210{
2211 LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n",
2212 pGuestSession, startupInfo.mExecutable.c_str(), fAsync));
2213
2214 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2215 Assert(startupInfo.mArguments[0] == startupInfo.mExecutable);
2216
2217 pSession = pGuestSession;
2218 mStartupInfo = startupInfo;
2219
2220 /* Make sure the process is hidden. */
2221 mStartupInfo.mFlags |= ProcessCreateFlag_Hidden;
2222
2223 int vrc = pSession->i_processCreateEx(mStartupInfo, pProcess);
2224 if (RT_SUCCESS(vrc))
2225 {
2226 int vrcGuest = VINF_SUCCESS;
2227 vrc = fAsync
2228 ? pProcess->i_startProcessAsync()
2229 : pProcess->i_startProcess(30 * 1000 /* 30s timeout */, &vrcGuest);
2230
2231 if ( RT_SUCCESS(vrc)
2232 && !fAsync
2233 && RT_FAILURE(vrcGuest)
2234 )
2235 {
2236 vrc = VERR_GSTCTL_GUEST_ERROR;
2237 }
2238
2239 if (prcGuest)
2240 *prcGuest = vrcGuest;
2241 }
2242
2243 LogFlowFuncLeaveRC(vrc);
2244 return vrc;
2245}
2246
2247/**
2248 * Unitializes a guest process tool by terminating it on the guest.
2249 */
2250void GuestProcessTool::uninit(void)
2251{
2252 /* Make sure the process is terminated and unregistered from the guest session. */
2253 int rcGuestIgnored;
2254 terminate(30 * 1000 /* 30s timeout */, &rcGuestIgnored);
2255
2256 /* Unregister the process from the process (and the session's object) list. */
2257 if ( pSession
2258 && pProcess)
2259 pSession->i_processUnregister(pProcess);
2260
2261 /* Release references. */
2262 pProcess.setNull();
2263 pSession.setNull();
2264}
2265
2266/**
2267 * Gets the current guest process stream block.
2268 *
2269 * @returns VBox status code.
2270 * @param uHandle Guest process file handle to get current block for.
2271 * @param strmBlock Where to return the stream block on success.
2272 */
2273int GuestProcessTool::getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock)
2274{
2275 const GuestProcessStream *pStream = NULL;
2276 if (uHandle == GUEST_PROC_OUT_H_STDOUT)
2277 pStream = &mStdOut;
2278 else if (uHandle == GUEST_PROC_OUT_H_STDERR)
2279 pStream = &mStdErr;
2280
2281 if (!pStream)
2282 return VERR_INVALID_PARAMETER;
2283
2284 /** @todo Why not using pStream down below and hardcode to mStdOut? */
2285
2286 int vrc;
2287 do
2288 {
2289 /* Try parsing the data to see if the current block is complete. */
2290 vrc = mStdOut.ParseBlock(strmBlock);
2291 if (strmBlock.GetCount())
2292 break;
2293 } while (RT_SUCCESS(vrc));
2294
2295 LogFlowThisFunc(("rc=%Rrc, %RU64 pairs\n",
2296 vrc, strmBlock.GetCount()));
2297 return vrc;
2298}
2299
2300/**
2301 * Returns the result code from an ended guest process tool.
2302 *
2303 * @returns Result code from guest process tool.
2304 */
2305int GuestProcessTool::getRc(void) const
2306{
2307 LONG exitCode = -1;
2308 HRESULT hr = pProcess->COMGETTER(ExitCode(&exitCode));
2309 AssertComRC(hr);
2310
2311 return GuestProcessTool::exitCodeToRc(mStartupInfo, exitCode);
2312}
2313
2314/**
2315 * Returns whether a guest process tool is still running or not.
2316 *
2317 * @returns \c true if running, or \c false if not.
2318 */
2319bool GuestProcessTool::isRunning(void)
2320{
2321 AssertReturn(!pProcess.isNull(), false);
2322
2323 ProcessStatus_T procStatus = ProcessStatus_Undefined;
2324 HRESULT hr = pProcess->COMGETTER(Status(&procStatus));
2325 AssertComRC(hr);
2326
2327 if ( procStatus == ProcessStatus_Started
2328 || procStatus == ProcessStatus_Paused
2329 || procStatus == ProcessStatus_Terminating)
2330 {
2331 return true;
2332 }
2333
2334 return false;
2335}
2336
2337/**
2338 * Returns whether the tool has been run correctly or not, based on it's internal process
2339 * status and reported exit status.
2340 *
2341 * @return @c true if the tool has been run correctly (exit status 0), or @c false if some error
2342 * occurred (exit status <> 0 or wrong process state).
2343 */
2344bool GuestProcessTool::isTerminatedOk(void)
2345{
2346 return getTerminationStatus() == VINF_SUCCESS ? true : false;
2347}
2348
2349/**
2350 * Static helper function to start and wait for a certain toolbox tool.
2351 *
2352 * This function most likely is the one you want to use in the first place if you
2353 * want to just use a toolbox tool and wait for its result. See runEx() if you also
2354 * needs its output.
2355 *
2356 * @return VBox status code.
2357 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2358 * @param startupInfo Startup information about the toolbox tool.
2359 * @param prcGuest Where to store the toolbox tool's specific error code in case
2360 * VERR_GSTCTL_GUEST_ERROR is returned.
2361 */
2362/* static */
2363int GuestProcessTool::run( GuestSession *pGuestSession,
2364 const GuestProcessStartupInfo &startupInfo,
2365 int *prcGuest /* = NULL */)
2366{
2367 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2368
2369 GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX };
2370 int vrc = runErrorInfo(pGuestSession, startupInfo, errorInfo);
2371 if (RT_SUCCESS(vrc))
2372 {
2373 /* Make sure to check the error information we got from the guest tool. */
2374 if (GuestProcess::i_isGuestError(errorInfo.rcGuest))
2375 {
2376 if (errorInfo.rcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */
2377 rcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2378 else /* At least return something. */
2379 rcGuest = errorInfo.rcGuest;
2380
2381 if (prcGuest)
2382 *prcGuest = rcGuest;
2383
2384 vrc = VERR_GSTCTL_GUEST_ERROR;
2385 }
2386 }
2387
2388 LogFlowFunc(("Returned rc=%Rrc, rcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.rcGuest, errorInfo.iExitCode));
2389 return vrc;
2390}
2391
2392/**
2393 * Static helper function to start and wait for a certain toolbox tool, returning
2394 * extended error information from the guest.
2395 *
2396 * @return VBox status code.
2397 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2398 * @param startupInfo Startup information about the toolbox tool.
2399 * @param errorInfo Error information returned for error handling.
2400 */
2401/* static */
2402int GuestProcessTool::runErrorInfo( GuestSession *pGuestSession,
2403 const GuestProcessStartupInfo &startupInfo,
2404 GuestProcessToolErrorInfo &errorInfo)
2405{
2406 return runExErrorInfo(pGuestSession, startupInfo,
2407 NULL /* paStrmOutObjects */, 0 /* cStrmOutObjects */, errorInfo);
2408}
2409
2410/**
2411 * Static helper function to start and wait for output of a certain toolbox tool.
2412 *
2413 * @return IPRT status code.
2414 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2415 * @param startupInfo Startup information about the toolbox tool.
2416 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2417 * Optional.
2418 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2419 * @param prcGuest Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned. Optional.
2420 */
2421/* static */
2422int GuestProcessTool::runEx( GuestSession *pGuestSession,
2423 const GuestProcessStartupInfo &startupInfo,
2424 GuestCtrlStreamObjects *paStrmOutObjects,
2425 uint32_t cStrmOutObjects,
2426 int *prcGuest /* = NULL */)
2427{
2428 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2429
2430 GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX };
2431 int vrc = GuestProcessTool::runExErrorInfo(pGuestSession, startupInfo, paStrmOutObjects, cStrmOutObjects, errorInfo);
2432 if (RT_SUCCESS(vrc))
2433 {
2434 /* Make sure to check the error information we got from the guest tool. */
2435 if (GuestProcess::i_isGuestError(errorInfo.rcGuest))
2436 {
2437 if (errorInfo.rcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */
2438 rcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2439 else /* At least return something. */
2440 rcGuest = errorInfo.rcGuest;
2441
2442 if (prcGuest)
2443 *prcGuest = rcGuest;
2444
2445 vrc = VERR_GSTCTL_GUEST_ERROR;
2446 }
2447 }
2448
2449 LogFlowFunc(("Returned rc=%Rrc, rcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.rcGuest, errorInfo.iExitCode));
2450 return vrc;
2451}
2452
2453/**
2454 * Static helper function to start and wait for output of a certain toolbox tool.
2455 *
2456 * This is the extended version, which addds the possibility of retrieving parsable so-called guest stream
2457 * objects. Those objects are issued on the guest side as part of VBoxService's toolbox tools (think of a BusyBox-like approach)
2458 * on stdout and can be used on the host side to retrieve more information about the actual command issued on the guest side.
2459 *
2460 * @return VBox status code.
2461 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2462 * @param startupInfo Startup information about the toolbox tool.
2463 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2464 * Optional.
2465 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2466 * @param errorInfo Error information returned for error handling.
2467 */
2468/* static */
2469int GuestProcessTool::runExErrorInfo( GuestSession *pGuestSession,
2470 const GuestProcessStartupInfo &startupInfo,
2471 GuestCtrlStreamObjects *paStrmOutObjects,
2472 uint32_t cStrmOutObjects,
2473 GuestProcessToolErrorInfo &errorInfo)
2474{
2475 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2476 /* paStrmOutObjects is optional. */
2477
2478 /** @todo Check if this is a valid toolbox. */
2479
2480 GuestProcessTool procTool;
2481 int vrc = procTool.init(pGuestSession, startupInfo, false /* Async */, &errorInfo.rcGuest);
2482 if (RT_SUCCESS(vrc))
2483 {
2484 while (cStrmOutObjects--)
2485 {
2486 try
2487 {
2488 GuestProcessStreamBlock strmBlk;
2489 vrc = procTool.waitEx( paStrmOutObjects
2490 ? GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK
2491 : GUESTPROCESSTOOL_WAIT_FLAG_NONE, &strmBlk, &errorInfo.rcGuest);
2492 if (paStrmOutObjects)
2493 paStrmOutObjects->push_back(strmBlk);
2494 }
2495 catch (std::bad_alloc &)
2496 {
2497 vrc = VERR_NO_MEMORY;
2498 }
2499
2500 if (RT_FAILURE(vrc))
2501 break;
2502 }
2503 }
2504
2505 if (RT_SUCCESS(vrc))
2506 {
2507 /* Make sure the process runs until completion. */
2508 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &errorInfo.rcGuest);
2509 if (RT_SUCCESS(vrc))
2510 errorInfo.rcGuest = procTool.getTerminationStatus(&errorInfo.iExitCode);
2511 }
2512
2513 LogFlowFunc(("Returned rc=%Rrc, rcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.rcGuest, errorInfo.iExitCode));
2514 return vrc;
2515}
2516
2517/**
2518 * Reports if the tool has been run correctly.
2519 *
2520 * @return Will return VERR_GSTCTL_PROCESS_EXIT_CODE if the tool process returned an exit code <> 0,
2521 * VERR_GSTCTL_PROCESS_WRONG_STATE if the tool process is in a wrong state (e.g. still running),
2522 * or VINF_SUCCESS otherwise.
2523 *
2524 * @param piExitCode Exit code of the tool. Optional.
2525 */
2526int GuestProcessTool::getTerminationStatus(int32_t *piExitCode /* = NULL */)
2527{
2528 Assert(!pProcess.isNull());
2529 /* pExitCode is optional. */
2530
2531 int vrc;
2532 if (!isRunning())
2533 {
2534 LONG iExitCode = -1;
2535 HRESULT hr = pProcess->COMGETTER(ExitCode(&iExitCode));
2536 AssertComRC(hr);
2537
2538 if (piExitCode)
2539 *piExitCode = iExitCode;
2540
2541 vrc = iExitCode != 0 ? VERR_GSTCTL_PROCESS_EXIT_CODE : VINF_SUCCESS;
2542 }
2543 else
2544 vrc = VERR_GSTCTL_PROCESS_WRONG_STATE;
2545
2546 LogFlowFuncLeaveRC(vrc);
2547 return vrc;
2548}
2549
2550/**
2551 * Waits for a guest process tool.
2552 *
2553 * @returns VBox status code.
2554 * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
2555 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2556 * was returned. Optional.
2557 */
2558int GuestProcessTool::wait(uint32_t fToolWaitFlags, int *prcGuest)
2559{
2560 return waitEx(fToolWaitFlags, NULL /* pStrmBlkOut */, prcGuest);
2561}
2562
2563/**
2564 * Waits for a guest process tool, also returning process output.
2565 *
2566 * @returns VBox status code.
2567 * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
2568 * @param pStrmBlkOut Where to store the guest process output.
2569 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2570 * was returned. Optional.
2571 */
2572int GuestProcessTool::waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *pStrmBlkOut, int *prcGuest)
2573{
2574 LogFlowThisFunc(("fToolWaitFlags=0x%x, pStreamBlock=%p, prcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, prcGuest));
2575
2576 /* Can we parse the next block without waiting? */
2577 int vrc;
2578 if (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK)
2579 {
2580 AssertPtr(pStrmBlkOut);
2581 vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
2582 if (RT_SUCCESS(vrc))
2583 return vrc;
2584 /* else do the waiting below. */
2585 }
2586
2587 /* Do the waiting. */
2588 uint32_t fProcWaitForFlags = ProcessWaitForFlag_Terminate;
2589 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2590 fProcWaitForFlags |= ProcessWaitForFlag_StdOut;
2591 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2592 fProcWaitForFlags |= ProcessWaitForFlag_StdErr;
2593
2594 /** @todo Decrease timeout while running. */
2595 uint64_t u64StartMS = RTTimeMilliTS();
2596 uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
2597
2598 int vrcGuest = VINF_SUCCESS;
2599 bool fDone = false;
2600
2601 BYTE byBuf[_64K];
2602 uint32_t cbRead;
2603
2604 bool fHandleStdOut = false;
2605 bool fHandleStdErr = false;
2606
2607 /**
2608 * Updates the elapsed time and checks if a
2609 * timeout happened, then breaking out of the loop.
2610 */
2611#define UPDATE_AND_CHECK_ELAPSED_TIME() \
2612 u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
2613 if ( uTimeoutMS != RT_INDEFINITE_WAIT \
2614 && u64ElapsedMS >= uTimeoutMS) \
2615 { \
2616 vrc = VERR_TIMEOUT; \
2617 break; \
2618 }
2619
2620 /**
2621 * Returns the remaining time (in ms).
2622 */
2623#define GET_REMAINING_TIME \
2624 uTimeoutMS == RT_INDEFINITE_WAIT \
2625 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS \
2626
2627 ProcessWaitResult_T waitRes = ProcessWaitResult_None;
2628 do
2629 {
2630 uint64_t u64ElapsedMS;
2631 UPDATE_AND_CHECK_ELAPSED_TIME();
2632
2633 vrc = pProcess->i_waitFor(fProcWaitForFlags, GET_REMAINING_TIME, waitRes, &vrcGuest);
2634 if (RT_FAILURE(vrc))
2635 break;
2636
2637 switch (waitRes)
2638 {
2639 case ProcessWaitResult_StdIn:
2640 vrc = VERR_NOT_IMPLEMENTED;
2641 break;
2642
2643 case ProcessWaitResult_StdOut:
2644 fHandleStdOut = true;
2645 break;
2646
2647 case ProcessWaitResult_StdErr:
2648 fHandleStdErr = true;
2649 break;
2650
2651 case ProcessWaitResult_WaitFlagNotSupported:
2652 if (fProcWaitForFlags & ProcessWaitForFlag_StdOut)
2653 fHandleStdOut = true;
2654 if (fProcWaitForFlags & ProcessWaitForFlag_StdErr)
2655 fHandleStdErr = true;
2656 /* Since waiting for stdout / stderr is not supported by the guest,
2657 * wait a bit to not hog the CPU too much when polling for data. */
2658 RTThreadSleep(1); /* Optional, don't check rc. */
2659 break;
2660
2661 case ProcessWaitResult_Error:
2662 vrc = VERR_GSTCTL_GUEST_ERROR;
2663 break;
2664
2665 case ProcessWaitResult_Terminate:
2666 fDone = true;
2667 break;
2668
2669 case ProcessWaitResult_Timeout:
2670 vrc = VERR_TIMEOUT;
2671 break;
2672
2673 case ProcessWaitResult_Start:
2674 case ProcessWaitResult_Status:
2675 /* Not used here, just skip. */
2676 break;
2677
2678 default:
2679 AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
2680 break;
2681 }
2682
2683 if (RT_FAILURE(vrc))
2684 break;
2685
2686 if (fHandleStdOut)
2687 {
2688 UPDATE_AND_CHECK_ELAPSED_TIME();
2689
2690 cbRead = 0;
2691 vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDOUT, sizeof(byBuf),
2692 GET_REMAINING_TIME,
2693 byBuf, sizeof(byBuf),
2694 &cbRead, &vrcGuest);
2695 if ( RT_FAILURE(vrc)
2696 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2697 break;
2698
2699 if (cbRead)
2700 {
2701 LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
2702 vrc = mStdOut.AddData(byBuf, cbRead);
2703
2704 if ( RT_SUCCESS(vrc)
2705 && (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK))
2706 {
2707 AssertPtr(pStrmBlkOut);
2708 vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
2709
2710 /* When successful, break out of the loop because we're done
2711 * with reading the first stream block. */
2712 if (RT_SUCCESS(vrc))
2713 fDone = true;
2714 }
2715 }
2716
2717 fHandleStdOut = false;
2718 }
2719
2720 if (fHandleStdErr)
2721 {
2722 UPDATE_AND_CHECK_ELAPSED_TIME();
2723
2724 cbRead = 0;
2725 vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDERR, sizeof(byBuf),
2726 GET_REMAINING_TIME,
2727 byBuf, sizeof(byBuf),
2728 &cbRead, &vrcGuest);
2729 if ( RT_FAILURE(vrc)
2730 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2731 break;
2732
2733 if (cbRead)
2734 {
2735 LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
2736 vrc = mStdErr.AddData(byBuf, cbRead);
2737 }
2738
2739 fHandleStdErr = false;
2740 }
2741
2742 } while (!fDone && RT_SUCCESS(vrc));
2743
2744#undef UPDATE_AND_CHECK_ELAPSED_TIME
2745#undef GET_REMAINING_TIME
2746
2747 if (RT_FAILURE(vrcGuest))
2748 vrc = VERR_GSTCTL_GUEST_ERROR;
2749
2750 LogFlowThisFunc(("Loop ended with rc=%Rrc, vrcGuest=%Rrc, waitRes=%RU32\n",
2751 vrc, vrcGuest, waitRes));
2752 if (prcGuest)
2753 *prcGuest = vrcGuest;
2754
2755 LogFlowFuncLeaveRC(vrc);
2756 return vrc;
2757}
2758
2759/**
2760 * Terminates a guest process tool.
2761 *
2762 * @returns VBox status code.
2763 * @param uTimeoutMS Timeout (in ms) to wait.
2764 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2765 * was returned. Optional.
2766 */
2767int GuestProcessTool::terminate(uint32_t uTimeoutMS, int *prcGuest)
2768{
2769 LogFlowThisFuncEnter();
2770
2771 int rc;
2772 if (!pProcess.isNull())
2773 rc = pProcess->i_terminateProcess(uTimeoutMS, prcGuest);
2774 else
2775 rc = VERR_NOT_FOUND;
2776
2777 LogFlowFuncLeaveRC(rc);
2778 return rc;
2779}
2780
2781/**
2782 * Converts a toolbox tool's exit code to an IPRT error code.
2783 *
2784 * @returns VBox status code.
2785 * @param startupInfo Startup info of the toolbox tool to lookup error code for.
2786 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
2787 */
2788/* static */
2789int GuestProcessTool::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode)
2790{
2791 if (startupInfo.mArguments.size() == 0)
2792 {
2793 AssertFailed();
2794 return VERR_GENERAL_FAILURE; /* Should not happen. */
2795 }
2796
2797 return exitCodeToRc(startupInfo.mArguments[0].c_str(), iExitCode);
2798}
2799
2800/**
2801 * Converts a toolbox tool's exit code to an IPRT error code.
2802 *
2803 * @returns VBox status code.
2804 * @param pszTool Name of toolbox tool to lookup error code for.
2805 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
2806 */
2807/* static */
2808int GuestProcessTool::exitCodeToRc(const char *pszTool, int32_t iExitCode)
2809{
2810 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
2811
2812 LogFlowFunc(("%s: %d\n", pszTool, iExitCode));
2813
2814 if (iExitCode == 0) /* No error? Bail out early. */
2815 return VINF_SUCCESS;
2816
2817 if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_CAT))
2818 {
2819 switch (iExitCode)
2820 {
2821 case VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2822 case VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2823 case VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2824 case VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION: return VERR_SHARING_VIOLATION;
2825 case VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY: return VERR_IS_A_DIRECTORY;
2826 default: break;
2827 }
2828 }
2829 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_LS))
2830 {
2831 switch (iExitCode)
2832 {
2833 /** @todo Handle access denied? */
2834 case RTEXITCODE_FAILURE: return VERR_PATH_NOT_FOUND;
2835 default: break;
2836 }
2837 }
2838 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_STAT))
2839 {
2840 switch (iExitCode)
2841 {
2842 case VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2843 case VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2844 case VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2845 case VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND: return VERR_NET_PATH_NOT_FOUND;
2846 default: break;
2847 }
2848 }
2849 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKDIR))
2850 {
2851 switch (iExitCode)
2852 {
2853 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2854 default: break;
2855 }
2856 }
2857 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKTEMP))
2858 {
2859 switch (iExitCode)
2860 {
2861 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2862 default: break;
2863 }
2864 }
2865 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_RM))
2866 {
2867 switch (iExitCode)
2868 {
2869 case RTEXITCODE_FAILURE: return VERR_FILE_NOT_FOUND;
2870 /** @todo RTPathRmCmd does not yet distinguish between not found and access denied yet. */
2871 default: break;
2872 }
2873 }
2874
2875 LogFunc(("Warning: Exit code %d not handled for tool '%s', returning VERR_GENERAL_FAILURE\n", iExitCode, pszTool));
2876
2877 if (iExitCode == RTEXITCODE_SYNTAX)
2878 return VERR_INTERNAL_ERROR_5;
2879 return VERR_GENERAL_FAILURE;
2880}
2881
2882/**
2883 * Returns a stringyfied error of a guest process tool error.
2884 *
2885 * @returns Stringyfied error.
2886 * @param pszTool Toolbox tool name to get stringyfied error for.
2887 * @param guestErrorInfo Guest error info to get stringyfied error for.
2888 */
2889/* static */
2890Utf8Str GuestProcessTool::guestErrorToString(const char *pszTool, const GuestErrorInfo &guestErrorInfo)
2891{
2892 Utf8Str strErr;
2893
2894 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
2895 switch (guestErrorInfo.getRc())
2896 {
2897 case VERR_ACCESS_DENIED:
2898 strErr.printf(tr("Access to \"%s\" denied"), guestErrorInfo.getWhat().c_str());
2899 break;
2900
2901 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
2902 RT_FALL_THROUGH();
2903 case VERR_PATH_NOT_FOUND:
2904 strErr.printf(tr("No such file or directory \"%s\""), guestErrorInfo.getWhat().c_str());
2905 break;
2906
2907 case VERR_INVALID_VM_HANDLE:
2908 strErr.printf(tr("VMM device is not available (is the VM running?)"));
2909 break;
2910
2911 case VERR_HGCM_SERVICE_NOT_FOUND:
2912 strErr.printf(tr("The guest execution service is not available"));
2913 break;
2914
2915 case VERR_BAD_EXE_FORMAT:
2916 strErr.printf(tr("The file \"%s\" is not an executable format"), guestErrorInfo.getWhat().c_str());
2917 break;
2918
2919 case VERR_AUTHENTICATION_FAILURE:
2920 strErr.printf(tr("The user \"%s\" was not able to logon"), guestErrorInfo.getWhat().c_str());
2921 break;
2922
2923 case VERR_INVALID_NAME:
2924 strErr.printf(tr("The file \"%s\" is an invalid name"), guestErrorInfo.getWhat().c_str());
2925 break;
2926
2927 case VERR_TIMEOUT:
2928 strErr.printf(tr("The guest did not respond within time"));
2929 break;
2930
2931 case VERR_CANCELLED:
2932 strErr.printf(tr("The execution operation was canceled"));
2933 break;
2934
2935 case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
2936 strErr.printf(tr("Maximum number of concurrent guest processes has been reached"));
2937 break;
2938
2939 case VERR_NOT_FOUND:
2940 strErr.printf(tr("The guest execution service is not ready (yet)"));
2941 break;
2942
2943 default:
2944 strErr.printf(tr("Unhandled error %Rrc for \"%s\" occurred for tool \"%s\" on guest -- please file a bug report"),
2945 guestErrorInfo.getRc(), guestErrorInfo.getWhat().c_str(), pszTool);
2946 break;
2947 }
2948
2949 return strErr;
2950}
2951
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use