VirtualBox

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

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

Guest Control: Dropped the "Internal" suffix from function, as the "i_" prefix now states that it's an internal function.

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