VirtualBox

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

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

Guest Control: Revamped internal object [un]registration and organization to provide faster lookups and avoid code duplication. No protocol changes.

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