VirtualBox

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

Last change on this file since 77496 was 77496, checked in by vboxsync, 6 years ago

Guest Control/Main: Added exit code handling for VBOXSERVICE_TOOL_MKTEMP + VBOXSERVICE_TOOL_RM in GuestProcessTool::exitCodeToRc().

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