VirtualBox

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

Last change on this file since 71564 was 71564, checked in by vboxsync, 7 years ago

Guest Control/Main: Added GuestProcessTool::uninit() and also uninitialize the session object there.

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

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