VirtualBox

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

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

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

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

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