VirtualBox

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

Last change on this file was 104178, checked in by vboxsync, 5 weeks ago

Guest Control:

  • Factored out most of the guest process stream handling of GuestToolboxStream (deprecated) into a new generic class GuestProcessOutputStream. That way we can make use of most of that code for other, non-toolbox related functionality.
  • Factoredd out most of the guest process wrapping functionality from GuestProcessToolbox into a new generic class GuestProcessWrapper. Ditto (see above).
  • Make more use of VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT to compile a lot less code if not defined. Toolbox handling is required for supporting older Guest Additions (< 7.1) though (so enabled by default).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.7 KB
Line 
1/* $Id: GuestProcessImpl.cpp 104178 2024-04-05 12:23:48Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest process handling.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/**
29 * Locking rules:
30 * - When the main dispatcher (callbackDispatcher) is called it takes the
31 * WriteLock while dispatching to the various on* methods.
32 * - All other outer functions (accessible by Main) must not own a lock
33 * while waiting for a callback or for an event.
34 * - Only keep Read/WriteLocks as short as possible and only when necessary.
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_MAIN_GUESTPROCESS
42#include "LoggingNew.h"
43
44#ifndef VBOX_WITH_GUEST_CONTROL
45# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
46#endif
47#include "GuestImpl.h"
48#include "GuestProcessImpl.h"
49#include "GuestSessionImpl.h"
50#include "GuestCtrlImplPrivate.h"
51#include "ConsoleImpl.h"
52#include "VirtualBoxErrorInfoImpl.h"
53
54#include "Global.h"
55#include "AutoCaller.h"
56#include "VBoxEvents.h"
57#include "ThreadTask.h"
58
59#include <memory> /* For auto_ptr. */
60
61#include <iprt/asm.h>
62#include <iprt/cpp/utils.h> /* For unconst(). */
63#include <iprt/getopt.h>
64
65#include <VBox/com/listeners.h>
66
67#include <VBox/com/array.h>
68
69
70/**
71 * Base class for all guest process tasks.
72 */
73class GuestProcessTask : public ThreadTask
74{
75public:
76
77 GuestProcessTask(GuestProcess *pProcess)
78 : ThreadTask("GenericGuestProcessTask")
79 , mProcess(pProcess)
80 , mVrc(VINF_SUCCESS) { }
81
82 virtual ~GuestProcessTask(void) { }
83
84 /** Returns the last set result code. */
85 int i_vrc(void) const { return mVrc; }
86 /** Returns whether the last set result is okay (successful) or not. */
87 bool i_isOk(void) const { return RT_SUCCESS(mVrc); }
88 /** Returns the reference of the belonging progress object. */
89 const ComObjPtr<GuestProcess> &i_process(void) const { return mProcess; }
90
91protected:
92
93 /** Progress object this process belongs to. */
94 const ComObjPtr<GuestProcess> mProcess;
95 /** Last set result code. */
96 int mVrc;
97};
98
99/**
100 * Task to start a process on the guest.
101 */
102class GuestProcessStartTask : public GuestProcessTask
103{
104public:
105
106 GuestProcessStartTask(GuestProcess *pProcess)
107 : GuestProcessTask(pProcess)
108 {
109 m_strTaskName = "gctlPrcStart";
110 }
111
112 void handler()
113 {
114 GuestProcess::i_startProcessThreadTask(this);
115 }
116};
117
118/**
119 * Internal listener class to serve events in an
120 * active manner, e.g. without polling delays.
121 */
122class GuestProcessListener
123{
124public:
125
126 GuestProcessListener(void)
127 {
128 }
129
130 virtual ~GuestProcessListener(void)
131 {
132 }
133
134 HRESULT init(GuestProcess *pProcess)
135 {
136 AssertPtrReturn(pProcess, E_POINTER);
137 mProcess = pProcess;
138 return S_OK;
139 }
140
141 void uninit(void)
142 {
143 mProcess = NULL;
144 }
145
146 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
147 {
148 switch (aType)
149 {
150 case VBoxEventType_OnGuestProcessStateChanged:
151 case VBoxEventType_OnGuestProcessInputNotify:
152 case VBoxEventType_OnGuestProcessOutput:
153 {
154 AssertPtrReturn(mProcess, E_POINTER);
155 int vrc2 = mProcess->signalWaitEvent(aType, aEvent);
156 RT_NOREF(vrc2);
157#ifdef LOG_ENABLED
158 LogFlowThisFunc(("Signalling events of type=%RU32, pProcess=%p resulted in vrc=%Rrc\n",
159 aType, &mProcess, vrc2));
160#endif
161 break;
162 }
163
164 default:
165 AssertMsgFailed(("Unhandled event %RU32\n", aType));
166 break;
167 }
168
169 return S_OK;
170 }
171
172private:
173
174 GuestProcess *mProcess;
175};
176typedef ListenerImpl<GuestProcessListener, GuestProcess*> GuestProcessListenerImpl;
177
178VBOX_LISTENER_DECLARE(GuestProcessListenerImpl)
179
180// constructor / destructor
181/////////////////////////////////////////////////////////////////////////////
182
183DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
184
185HRESULT GuestProcess::FinalConstruct(void)
186{
187 LogFlowThisFuncEnter();
188 return BaseFinalConstruct();
189}
190
191void GuestProcess::FinalRelease(void)
192{
193 LogFlowThisFuncEnter();
194 uninit();
195 BaseFinalRelease();
196 LogFlowThisFuncLeave();
197}
198
199// public initializer/uninitializer for internal purposes only
200/////////////////////////////////////////////////////////////////////////////
201
202/**
203 * Initialies a guest process object.
204 *
205 * @returns VBox status code.
206 * @param aConsole Console this process is bound to.
207 * @param aSession Guest session this process is bound to.
208 * @param aObjectID Object ID to use for this process object.
209 * @param aProcInfo Process startup information to use.
210 * @param pBaseEnv Guest environment to apply when starting the process on the guest.
211 */
212int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aObjectID,
213 const GuestProcessStartupInfo &aProcInfo, const GuestEnvironment *pBaseEnv)
214{
215 LogFlowThisFunc(("aConsole=%p, aSession=%p, aObjectID=%RU32, pBaseEnv=%p\n",
216 aConsole, aSession, aObjectID, pBaseEnv));
217
218 AssertPtrReturn(aConsole, VERR_INVALID_POINTER);
219 AssertPtrReturn(aSession, VERR_INVALID_POINTER);
220
221 /* Enclose the state transition NotReady->InInit->Ready. */
222 AutoInitSpan autoInitSpan(this);
223 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
224
225 HRESULT hrc;
226
227 int vrc = bindToSession(aConsole, aSession, aObjectID);
228 if (RT_SUCCESS(vrc))
229 {
230 hrc = unconst(mEventSource).createObject();
231 if (FAILED(hrc))
232 vrc = VERR_NO_MEMORY;
233 else
234 {
235 hrc = mEventSource->init();
236 if (FAILED(hrc))
237 vrc = VERR_COM_UNEXPECTED;
238 }
239 }
240
241 if (RT_SUCCESS(vrc))
242 {
243 try
244 {
245 GuestProcessListener *pListener = new GuestProcessListener();
246 ComObjPtr<GuestProcessListenerImpl> thisListener;
247 hrc = thisListener.createObject();
248 if (SUCCEEDED(hrc))
249 hrc = thisListener->init(pListener, this);
250
251 if (SUCCEEDED(hrc))
252 {
253 com::SafeArray <VBoxEventType_T> eventTypes;
254 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
255 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
256 eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
257 hrc = mEventSource->RegisterListener(thisListener,
258 ComSafeArrayAsInParam(eventTypes),
259 TRUE /* Active listener */);
260 if (SUCCEEDED(hrc))
261 {
262 vrc = baseInit();
263 if (RT_SUCCESS(vrc))
264 {
265 mLocalListener = thisListener;
266 }
267 }
268 else
269 vrc = VERR_COM_UNEXPECTED;
270 }
271 else
272 vrc = VERR_COM_UNEXPECTED;
273 }
274 catch(std::bad_alloc &)
275 {
276 vrc = VERR_NO_MEMORY;
277 }
278 }
279
280 if (RT_SUCCESS(vrc))
281 {
282 mData.mProcess = aProcInfo;
283 mData.mpSessionBaseEnv = pBaseEnv;
284 if (pBaseEnv)
285 pBaseEnv->retainConst();
286 mData.mExitCode = 0;
287 mData.mPID = 0;
288 mData.mLastError = VINF_SUCCESS;
289 mData.mStatus = ProcessStatus_Undefined;
290 /* Everything else will be set by the actual starting routine. */
291
292 /* Confirm a successful initialization when it's the case. */
293 autoInitSpan.setSucceeded();
294
295 return vrc;
296 }
297
298 autoInitSpan.setFailed();
299 return vrc;
300}
301
302/**
303 * Uninitializes the instance.
304 * Called from FinalRelease() or IGuestSession::uninit().
305 */
306void GuestProcess::uninit(void)
307{
308 /* Enclose the state transition Ready->InUninit->NotReady. */
309 AutoUninitSpan autoUninitSpan(this);
310 if (autoUninitSpan.uninitDone())
311 return;
312
313 LogFlowThisFunc(("mExe=%s, PID=%RU32\n", mData.mProcess.mExecutable.c_str(), mData.mPID));
314
315 if (mData.mpSessionBaseEnv)
316 {
317 mData.mpSessionBaseEnv->releaseConst();
318 mData.mpSessionBaseEnv = NULL;
319 }
320
321 baseUninit();
322
323 LogFlowFuncLeave();
324}
325
326// implementation of public getters/setters for attributes
327/////////////////////////////////////////////////////////////////////////////
328HRESULT GuestProcess::getArguments(std::vector<com::Utf8Str> &aArguments)
329{
330 LogFlowThisFuncEnter();
331
332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
333 aArguments = mData.mProcess.mArguments;
334 return S_OK;
335}
336
337HRESULT GuestProcess::getEnvironment(std::vector<com::Utf8Str> &aEnvironment)
338{
339#ifndef VBOX_WITH_GUEST_CONTROL
340 ReturnComNotImplemented();
341#else
342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /* (Paranoia since both environment objects are immutable.) */
343 HRESULT hrc;
344 if (mData.mpSessionBaseEnv)
345 {
346 int vrc;
347 if (mData.mProcess.mEnvironmentChanges.count() == 0)
348 vrc = mData.mpSessionBaseEnv->queryPutEnvArray(&aEnvironment);
349 else
350 {
351 GuestEnvironment TmpEnv;
352 vrc = TmpEnv.copy(*mData.mpSessionBaseEnv);
353 if (RT_SUCCESS(vrc))
354 {
355 vrc = TmpEnv.applyChanges(mData.mProcess.mEnvironmentChanges);
356 if (RT_SUCCESS(vrc))
357 vrc = TmpEnv.queryPutEnvArray(&aEnvironment);
358 }
359 }
360 hrc = Global::vboxStatusCodeToCOM(vrc);
361 }
362 else
363 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by installed Guest Additions"));
364 LogFlowThisFuncLeave();
365 return hrc;
366#endif
367}
368
369HRESULT GuestProcess::getEventSource(ComPtr<IEventSource> &aEventSource)
370{
371 LogFlowThisFuncEnter();
372
373 // no need to lock - lifetime constant
374 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
375
376 LogFlowThisFuncLeave();
377 return S_OK;
378}
379
380HRESULT GuestProcess::getExecutablePath(com::Utf8Str &aExecutablePath)
381{
382 LogFlowThisFuncEnter();
383
384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
385
386 aExecutablePath = mData.mProcess.mExecutable;
387
388 return S_OK;
389}
390
391HRESULT GuestProcess::getExitCode(LONG *aExitCode)
392{
393 LogFlowThisFuncEnter();
394
395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
396
397 *aExitCode = mData.mExitCode;
398
399 return S_OK;
400}
401
402HRESULT GuestProcess::getName(com::Utf8Str &aName)
403{
404 LogFlowThisFuncEnter();
405
406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
407
408 aName = mData.mProcess.mName;
409
410 return S_OK;
411}
412
413HRESULT GuestProcess::getPID(ULONG *aPID)
414{
415 LogFlowThisFuncEnter();
416
417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
418
419 *aPID = mData.mPID;
420
421 return S_OK;
422}
423
424HRESULT GuestProcess::getStatus(ProcessStatus_T *aStatus)
425{
426 LogFlowThisFuncEnter();
427
428 *aStatus = i_getStatus();
429
430 return S_OK;
431}
432
433// private methods
434/////////////////////////////////////////////////////////////////////////////
435
436/**
437 * Entry point for guest side process callbacks.
438 *
439 * @returns VBox status code.
440 * @param pCbCtx Host callback context.
441 * @param pSvcCb Host callback data.
442 */
443int GuestProcess::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
444{
445 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
446 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
447#ifdef DEBUG
448 LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uMessage=%RU32, pSvcCb=%p\n",
449 mData.mPID, pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
450#endif
451
452 int vrc;
453 switch (pCbCtx->uMessage)
454 {
455 case GUEST_MSG_DISCONNECTED:
456 {
457 vrc = i_onGuestDisconnected(pCbCtx, pSvcCb);
458 break;
459 }
460
461 case GUEST_MSG_EXEC_STATUS:
462 {
463 vrc = i_onProcessStatusChange(pCbCtx, pSvcCb);
464 break;
465 }
466
467 case GUEST_MSG_EXEC_OUTPUT:
468 {
469 vrc = i_onProcessOutput(pCbCtx, pSvcCb);
470 break;
471 }
472
473 case GUEST_MSG_EXEC_INPUT_STATUS:
474 {
475 vrc = i_onProcessInputStatus(pCbCtx, pSvcCb);
476 break;
477 }
478
479 default:
480 /* Silently ignore not implemented functions. */
481 vrc = VERR_NOT_SUPPORTED;
482 break;
483 }
484
485#ifdef DEBUG
486 LogFlowFuncLeaveRC(vrc);
487#endif
488 return vrc;
489}
490
491/**
492 * Checks if the current assigned PID matches another PID (from a callback).
493 *
494 * In protocol v1 we don't have the possibility to terminate/kill
495 * processes so it can happen that a formerly started process A
496 * (which has the context ID 0 (session=0, process=0, count=0) will
497 * send a delayed message to the host if this process has already
498 * been discarded there and the same context ID was reused by
499 * a process B. Process B in turn then has a different guest PID.
500 *
501 * Note: This also can happen when restoring from a saved state which
502 * had a guest process running.
503 *
504 * @return IPRT status code.
505 * @param uPID PID to check.
506 */
507inline int GuestProcess::i_checkPID(uint32_t uPID)
508{
509 int vrc = VINF_SUCCESS;
510
511 /* Was there a PID assigned yet? */
512 if (mData.mPID)
513 {
514 if (RT_UNLIKELY(mData.mPID != uPID))
515 {
516 LogFlowFunc(("Stale guest process (PID=%RU32) sent data to a newly started process (pProcesS=%p, PID=%RU32, status=%RU32)\n",
517 uPID, this, mData.mPID, mData.mStatus));
518 vrc = VERR_NOT_FOUND;
519 }
520 }
521
522 return vrc;
523}
524
525/**
526 * Returns the current process status.
527 *
528 * @returns Current process status.
529 *
530 * @note Takes the read lock.
531 */
532ProcessStatus_T GuestProcess::i_getStatus(void)
533{
534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
535
536 return mData.mStatus;
537}
538
539/**
540 * Converts a given guest process error to a string.
541 *
542 * @returns Error as a string.
543 * @param vrcGuest Guest process error to return string for.
544 * @param pcszWhat Hint of what was involved when the error occurred.
545 */
546/* static */
547Utf8Str GuestProcess::i_guestErrorToString(int vrcGuest, const char *pcszWhat)
548{
549 AssertPtrReturn(pcszWhat, "");
550
551 Utf8Str strErr;
552 switch (vrcGuest)
553 {
554#define CASE_MSG(a_iRc, ...) \
555 case a_iRc: strErr.printf(__VA_ARGS__); break;
556
557 CASE_MSG(VERR_FILE_NOT_FOUND, tr("No such file or directory \"%s\" on guest"), pcszWhat); /* This is the most likely error. */
558 CASE_MSG(VERR_PATH_NOT_FOUND, tr("No such file or directory \"%s\" on guest"), pcszWhat);
559 CASE_MSG(VERR_INVALID_VM_HANDLE, tr("VMM device is not available (is the VM running?)"));
560 CASE_MSG(VERR_HGCM_SERVICE_NOT_FOUND, tr("The guest execution service is not available"));
561 CASE_MSG(VERR_BAD_EXE_FORMAT, tr("The file \"%s\" is not an executable format on guest"), pcszWhat);
562 CASE_MSG(VERR_AUTHENTICATION_FAILURE, tr("The user \"%s\" was not able to logon on guest"), pcszWhat);
563 CASE_MSG(VERR_INVALID_NAME, tr("The file \"%s\" is an invalid name"), pcszWhat);
564 CASE_MSG(VERR_TIMEOUT, tr("The guest did not respond within time"));
565 CASE_MSG(VERR_CANCELLED, tr("The execution operation for \"%s\" was canceled"), pcszWhat);
566 CASE_MSG(VERR_GSTCTL_MAX_CID_OBJECTS_REACHED, tr("Maximum number of concurrent guest processes has been reached"));
567 CASE_MSG(VERR_NOT_FOUND, tr("The guest execution service is not ready (yet)"));
568 default:
569 strErr.printf(tr("Error %Rrc for guest process \"%s\" occurred\n"), vrcGuest, pcszWhat);
570 break;
571#undef CASE_MSG
572 }
573
574 return strErr;
575}
576
577/**
578 * Translates a process status to a human readable string.
579 *
580 * @returns Process status as a string.
581 * @param enmStatus Guest process status to return string for.
582 */
583/* static */
584Utf8Str GuestProcess::i_statusToString(ProcessStatus_T enmStatus)
585{
586 switch (enmStatus)
587 {
588 case ProcessStatus_Starting:
589 return "starting";
590 case ProcessStatus_Started:
591 return "started";
592 case ProcessStatus_Paused:
593 return "paused";
594 case ProcessStatus_Terminating:
595 return "terminating";
596 case ProcessStatus_TerminatedNormally:
597 return "successfully terminated";
598 case ProcessStatus_TerminatedSignal:
599 return "terminated by signal";
600 case ProcessStatus_TerminatedAbnormally:
601 return "abnormally aborted";
602 case ProcessStatus_TimedOutKilled:
603 return "timed out";
604 case ProcessStatus_TimedOutAbnormally:
605 return "timed out, hanging";
606 case ProcessStatus_Down:
607 return "killed";
608 case ProcessStatus_Error:
609 return "error";
610 default:
611 break;
612 }
613
614 AssertFailed(); /* Should never happen! */
615 return "unknown";
616}
617
618/**
619 * Returns @c true if the passed in error code indicates an error which came
620 * from the guest side, or @c false if not.
621 *
622 * @return bool @c true if the passed in error code indicates an error which came
623 * from the guest side, or @c false if not.
624 * @param vrc Error code to check.
625 */
626/* static */
627bool GuestProcess::i_isGuestError(int vrc)
628{
629 return vrc == VERR_GSTCTL_GUEST_ERROR
630 || vrc == VERR_GSTCTL_PROCESS_EXIT_CODE;
631}
632
633/**
634 * Returns whether the guest process is alive (i.e. running) or not.
635 *
636 * @returns \c true if alive and running, or \c false if not.
637 */
638inline bool GuestProcess::i_isAlive(void)
639{
640 return mData.mStatus == ProcessStatus_Started
641 || mData.mStatus == ProcessStatus_Paused
642 || mData.mStatus == ProcessStatus_Terminating;
643}
644
645/**
646 * Returns whether the guest process has ended (i.e. terminated) or not.
647 *
648 * @returns \c true if ended, or \c false if not.
649 */
650inline bool GuestProcess::i_hasEnded(void)
651{
652 return mData.mStatus == ProcessStatus_TerminatedNormally
653 || mData.mStatus == ProcessStatus_TerminatedSignal
654 || mData.mStatus == ProcessStatus_TerminatedAbnormally
655 || mData.mStatus == ProcessStatus_TimedOutKilled
656 || mData.mStatus == ProcessStatus_TimedOutAbnormally
657 || mData.mStatus == ProcessStatus_Down
658 || mData.mStatus == ProcessStatus_Error;
659}
660
661/**
662 * Called when the guest side of the process has been disconnected (closed, terminated, +++).
663 *
664 * @returns VBox status code.
665 * @param pCbCtx Host callback context.
666 * @param pSvcCbData Host callback data.
667 */
668int GuestProcess::i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
669{
670 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
671 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
672
673 int vrc = i_setProcessStatus(ProcessStatus_Down, VINF_SUCCESS);
674
675 LogFlowFuncLeaveRC(vrc);
676 return vrc;
677}
678
679/**
680 * Sets (reports) the current input status of the guest process.
681 *
682 * @returns VBox status code.
683 * @param pCbCtx Host callback context.
684 * @param pSvcCbData Host callback data.
685 *
686 * @note Takes the write lock.
687 */
688int GuestProcess::i_onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
689{
690 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
691 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
692 /* pCallback is optional. */
693
694 if (pSvcCbData->mParms < 5)
695 return VERR_INVALID_PARAMETER;
696
697 CALLBACKDATA_PROC_INPUT dataCb;
698 /* pSvcCb->mpaParms[0] always contains the context ID. */
699 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
700 AssertRCReturn(vrc, vrc);
701 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uStatus);
702 AssertRCReturn(vrc, vrc);
703 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
704 AssertRCReturn(vrc, vrc);
705 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[4], &dataCb.uProcessed);
706 AssertRCReturn(vrc, vrc);
707
708 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32\n",
709 dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed));
710
711 vrc = i_checkPID(dataCb.uPID);
712 if (RT_SUCCESS(vrc))
713 {
714 ProcessInputStatus_T inputStatus = ProcessInputStatus_Undefined;
715 switch (dataCb.uStatus)
716 {
717 case INPUT_STS_WRITTEN:
718 inputStatus = ProcessInputStatus_Written;
719 break;
720 case INPUT_STS_ERROR:
721 inputStatus = ProcessInputStatus_Broken;
722 break;
723 case INPUT_STS_TERMINATED:
724 inputStatus = ProcessInputStatus_Broken;
725 break;
726 case INPUT_STS_OVERFLOW:
727 inputStatus = ProcessInputStatus_Overflow;
728 break;
729 case INPUT_STS_UNDEFINED:
730 /* Fall through is intentional. */
731 default:
732 AssertMsg(!dataCb.uProcessed, ("Processed data is not 0 in undefined input state\n"));
733 break;
734 }
735
736 if (inputStatus != ProcessInputStatus_Undefined)
737 {
738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
739
740 /* Copy over necessary data before releasing lock again. */
741 uint32_t uPID = mData.mPID;
742 /** @todo Also handle mSession? */
743
744 alock.release(); /* Release lock before firing off event. */
745
746 ::FireGuestProcessInputNotifyEvent(mEventSource, mSession, this, uPID, 0 /* StdIn */, dataCb.uProcessed, inputStatus);
747 }
748 }
749
750 LogFlowFuncLeaveRC(vrc);
751 return vrc;
752}
753
754/**
755 * Notifies of an I/O operation of the guest process.
756 *
757 * @returns VERR_NOT_IMPLEMENTED -- not implemented yet.
758 * @param pCbCtx Host callback context.
759 * @param pSvcCbData Host callback data.
760 */
761int GuestProcess::i_onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
762{
763 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
764 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
765
766 return VERR_NOT_IMPLEMENTED;
767}
768
769/**
770 * Sets (reports) the current running status of the guest process.
771 *
772 * @returns VBox status code.
773 * @param pCbCtx Host callback context.
774 * @param pSvcCbData Host callback data.
775 *
776 * @note Takes the write lock.
777 */
778int GuestProcess::i_onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
779{
780 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
781 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
782
783 if (pSvcCbData->mParms < 5)
784 return VERR_INVALID_PARAMETER;
785
786 CALLBACKDATA_PROC_STATUS dataCb;
787 /* pSvcCb->mpaParms[0] always contains the context ID. */
788 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
789 AssertRCReturn(vrc, vrc);
790 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uStatus);
791 AssertRCReturn(vrc, vrc);
792 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
793 AssertRCReturn(vrc, vrc);
794 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[4], &dataCb.pvData, &dataCb.cbData);
795 AssertRCReturn(vrc, vrc);
796
797 LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32\n",
798 dataCb.uPID, dataCb.uStatus, dataCb.uFlags));
799
800 vrc = i_checkPID(dataCb.uPID);
801 if (RT_SUCCESS(vrc))
802 {
803 ProcessStatus_T procStatus = ProcessStatus_Undefined;
804 int vrcProc = VINF_SUCCESS;
805
806 switch (dataCb.uStatus)
807 {
808 case PROC_STS_STARTED:
809 {
810 procStatus = ProcessStatus_Started;
811
812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
813 mData.mPID = dataCb.uPID; /* Set the process PID. */
814 break;
815 }
816
817 case PROC_STS_TEN:
818 {
819 procStatus = ProcessStatus_TerminatedNormally;
820
821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
822 mData.mExitCode = dataCb.uFlags; /* Contains the exit code. */
823 break;
824 }
825
826 case PROC_STS_TES:
827 {
828 procStatus = ProcessStatus_TerminatedSignal;
829
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831 mData.mExitCode = dataCb.uFlags; /* Contains the signal. */
832 break;
833 }
834
835 case PROC_STS_TEA:
836 {
837 procStatus = ProcessStatus_TerminatedAbnormally;
838 break;
839 }
840
841 case PROC_STS_TOK:
842 {
843 procStatus = ProcessStatus_TimedOutKilled;
844 break;
845 }
846
847 case PROC_STS_TOA:
848 {
849 procStatus = ProcessStatus_TimedOutAbnormally;
850 break;
851 }
852
853 case PROC_STS_DWN:
854 {
855 procStatus = ProcessStatus_Down;
856 break;
857 }
858
859 case PROC_STS_ERROR:
860 {
861 vrcProc = dataCb.uFlags; /* mFlags contains the IPRT error sent from the guest. */
862 procStatus = ProcessStatus_Error;
863 break;
864 }
865
866 case PROC_STS_UNDEFINED:
867 default:
868 {
869 /* Silently skip this request. */
870 procStatus = ProcessStatus_Undefined;
871 break;
872 }
873 }
874
875 LogFlowThisFunc(("Got vrc=%Rrc, procSts=%RU32, vrcProc=%Rrc\n", vrc, procStatus, vrcProc));
876
877 /* Set the process status. */
878 int vrc2 = i_setProcessStatus(procStatus, vrcProc);
879 if (RT_SUCCESS(vrc))
880 vrc = vrc2;
881 }
882
883 LogFlowFuncLeaveRC(vrc);
884 return vrc;
885}
886
887/**
888 * Sets (reports) the current output status of the guest process.
889 *
890 * @returns VBox status code.
891 * @param pCbCtx Host callback context.
892 * @param pSvcCbData Host callback data.
893 */
894int GuestProcess::i_onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
895{
896 RT_NOREF(pCbCtx);
897 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
898
899 if (pSvcCbData->mParms < 5)
900 return VERR_INVALID_PARAMETER;
901
902 CALLBACKDATA_PROC_OUTPUT dataCb;
903 /* pSvcCb->mpaParms[0] always contains the context ID. */
904 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
905 AssertRCReturn(vrc, vrc);
906 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uHandle);
907 AssertRCReturn(vrc, vrc);
908 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
909 AssertRCReturn(vrc, vrc);
910 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[4], &dataCb.pvData, &dataCb.cbData);
911 AssertRCReturn(vrc, vrc);
912
913 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32\n",
914 dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData));
915
916 vrc = i_checkPID(dataCb.uPID);
917 if (RT_SUCCESS(vrc))
918 {
919 com::SafeArray<BYTE> data((size_t)dataCb.cbData);
920 if (dataCb.cbData)
921 data.initFrom((BYTE*)dataCb.pvData, dataCb.cbData);
922
923 ::FireGuestProcessOutputEvent(mEventSource, mSession, this,
924 mData.mPID, dataCb.uHandle, dataCb.cbData, ComSafeArrayAsInParam(data));
925 }
926
927 LogFlowFuncLeaveRC(vrc);
928 return vrc;
929}
930
931/**
932 * @copydoc GuestObject::i_onUnregister
933 */
934int GuestProcess::i_onUnregister(void)
935{
936 LogFlowThisFuncEnter();
937
938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
939
940 int vrc = VINF_SUCCESS;
941
942 /*
943 * Note: The event source stuff holds references to this object,
944 * so make sure that this is cleaned up *before* calling uninit().
945 */
946 if (!mEventSource.isNull())
947 {
948 mEventSource->UnregisterListener(mLocalListener);
949
950 mLocalListener.setNull();
951 unconst(mEventSource).setNull();
952 }
953
954 LogFlowFuncLeaveRC(vrc);
955 return vrc;
956}
957
958/**
959 * @copydoc GuestObject::i_onSessionStatusChange
960 */
961int GuestProcess::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
962{
963 LogFlowThisFuncEnter();
964
965 int vrc = VINF_SUCCESS;
966
967 /* If the session now is in a terminated state, set the process status
968 * to "down", as there is not much else we can do now. */
969 if (GuestSession::i_isTerminated(enmSessionStatus))
970 {
971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
972
973 vrc = i_setProcessStatus(ProcessStatus_Down, VINF_SUCCESS /* vrcProc, ignored */);
974 }
975
976 LogFlowFuncLeaveRC(vrc);
977 return vrc;
978}
979
980/**
981 * Reads data from a guest file.
982 *
983 * @returns VBox status code.
984 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
985 * @param uHandle Internal file handle to use for reading.
986 * @param uSize Size (in bytes) to read.
987 * @param uTimeoutMS Timeout (in ms) to wait.
988 * @param pvData Where to store the read data on success.
989 * @param cbData Size (in bytes) of \a pvData on input.
990 * @param pcbRead Where to return to size (in bytes) read on success.
991 * Optional.
992 * @param pvrcGuest Where to return the guest error when
993 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
994 *
995 * @note Takes the write lock.
996 */
997int GuestProcess::i_readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
998 void *pvData, size_t cbData, uint32_t *pcbRead, int *pvrcGuest)
999{
1000 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32, pvrcGuest=%p\n",
1001 mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData, pvrcGuest));
1002 AssertReturn(uSize, VERR_INVALID_PARAMETER);
1003 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1004 AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
1005 /* pcbRead is optional. */
1006
1007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1008
1009 if ( mData.mStatus != ProcessStatus_Started
1010 /* Skip reading if the process wasn't started with the appropriate
1011 * flags. */
1012 || ( ( uHandle == GUEST_PROC_OUT_H_STDOUT
1013 || uHandle == GUEST_PROC_OUT_H_STDOUT_DEPRECATED)
1014 && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdOut))
1015 || ( uHandle == GUEST_PROC_OUT_H_STDERR
1016 && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdErr))
1017 )
1018 {
1019 if (pcbRead)
1020 *pcbRead = 0;
1021 if (pvrcGuest)
1022 *pvrcGuest = VINF_SUCCESS;
1023 return VINF_SUCCESS; /* Nothing to read anymore. */
1024 }
1025
1026 int vrc;
1027
1028 GuestWaitEvent *pEvent = NULL;
1029 GuestEventTypes eventTypes;
1030 try
1031 {
1032 /*
1033 * On Guest Additions < 4.3 there is no guarantee that the process status
1034 * change arrives *after* the output event, e.g. if this was the last output
1035 * block being read and the process will report status "terminate".
1036 * So just skip checking for process status change and only wait for the
1037 * output event.
1038 */
1039 if (mSession->i_getProtocolVersion() >= 2)
1040 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1041 eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
1042
1043 vrc = registerWaitEvent(eventTypes, &pEvent);
1044 }
1045 catch (std::bad_alloc &)
1046 {
1047 vrc = VERR_NO_MEMORY;
1048 }
1049
1050 if (RT_FAILURE(vrc))
1051 return vrc;
1052
1053 if (RT_SUCCESS(vrc))
1054 {
1055 VBOXHGCMSVCPARM paParms[8];
1056 int i = 0;
1057 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1058 HGCMSvcSetU32(&paParms[i++], mData.mPID);
1059 HGCMSvcSetU32(&paParms[i++], uHandle);
1060 HGCMSvcSetU32(&paParms[i++], 0 /* Flags, none set yet. */);
1061
1062 alock.release(); /* Drop the write lock before sending. */
1063
1064 vrc = sendMessage(HOST_MSG_EXEC_GET_OUTPUT, i, paParms);
1065 }
1066
1067 if (RT_SUCCESS(vrc))
1068 vrc = i_waitForOutput(pEvent, uHandle, uTimeoutMS,
1069 pvData, cbData, pcbRead);
1070
1071 unregisterWaitEvent(pEvent);
1072
1073 LogFlowFuncLeaveRC(vrc);
1074 return vrc;
1075}
1076
1077/**
1078 * Sets (reports) the current (overall) status of the guest process.
1079 *
1080 * @returns VBox status code.
1081 * @param procStatus Guest process status to set.
1082 * @param vrcProc Guest process result code to set.
1083 *
1084 * @note Takes the write lock.
1085 */
1086int GuestProcess::i_setProcessStatus(ProcessStatus_T procStatus, int vrcProc)
1087{
1088 LogFlowThisFuncEnter();
1089
1090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcProc=%Rrc\n", mData.mStatus, procStatus, vrcProc));
1093
1094 if (procStatus == ProcessStatus_Error)
1095 {
1096 AssertMsg(RT_FAILURE(vrcProc), ("Guest vrcProc must be an error (%Rrc)\n", vrcProc));
1097 /* Do not allow overwriting an already set error. If this happens
1098 * this means we forgot some error checking/locking somewhere. */
1099 AssertMsg(RT_SUCCESS(mData.mLastError), ("Guest vrcProc already set (to %Rrc)\n", mData.mLastError));
1100 }
1101 else
1102 AssertMsg(RT_SUCCESS(vrcProc), ("Guest vrcProc must not be an error (%Rrc)\n", vrcProc));
1103
1104 int vrc = VINF_SUCCESS;
1105
1106 if (mData.mStatus != procStatus) /* Was there a process status change? */
1107 {
1108 mData.mStatus = procStatus;
1109 mData.mLastError = vrcProc;
1110
1111 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1112 HRESULT hrc = errorInfo.createObject();
1113 ComAssertComRC(hrc);
1114 if (RT_FAILURE(mData.mLastError))
1115 {
1116 hrc = errorInfo->initEx(VBOX_E_IPRT_ERROR, mData.mLastError,
1117 COM_IIDOF(IGuestProcess), getComponentName(),
1118 i_guestErrorToString(mData.mLastError, mData.mProcess.mExecutable.c_str()));
1119 ComAssertComRC(hrc);
1120 }
1121
1122 /* Copy over necessary data before releasing lock again. */
1123 uint32_t uPID = mData.mPID;
1124 /** @todo Also handle mSession? */
1125
1126 alock.release(); /* Release lock before firing off event. */
1127
1128 ::FireGuestProcessStateChangedEvent(mEventSource, mSession, this, uPID, procStatus, errorInfo);
1129#if 0
1130 /*
1131 * On Guest Additions < 4.3 there is no guarantee that outstanding
1132 * requests will be delivered to the host after the process has ended,
1133 * so just cancel all waiting events here to not let clients run
1134 * into timeouts.
1135 */
1136 if ( mSession->getProtocolVersion() < 2
1137 && hasEnded())
1138 {
1139 LogFlowThisFunc(("Process ended, canceling outstanding wait events ...\n"));
1140 vrc = cancelWaitEvents();
1141 }
1142#endif
1143 }
1144
1145 return vrc;
1146}
1147
1148/**
1149 * Starts the process on the guest.
1150 *
1151 * @returns VBox status code.
1152 * @param cMsTimeout Timeout (in ms) to wait for starting the process.
1153 * @param pvrcGuest Where to return the guest error when
1154 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1155 *
1156 * @note Takes the write lock.
1157 */
1158int GuestProcess::i_startProcess(uint32_t cMsTimeout, int *pvrcGuest)
1159{
1160 LogFlowThisFunc(("cMsTimeout=%RU32, procExe=%s, cwd=%s, procTimeoutMS=%RU32, procFlags=%x, sessionID=%RU32\n",
1161 cMsTimeout, mData.mProcess.mExecutable.c_str(), mData.mProcess.mCwd.c_str(),
1162 mData.mProcess.mTimeoutMS, mData.mProcess.mFlags, mSession->i_getId()));
1163
1164 /* Wait until the caller function (if kicked off by a thread)
1165 * has returned and continue operation. */
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 mData.mStatus = ProcessStatus_Starting;
1169
1170 int vrc;
1171
1172 GuestWaitEvent *pEvent = NULL;
1173 GuestEventTypes eventTypes;
1174 try
1175 {
1176 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1177 vrc = registerWaitEvent(eventTypes, &pEvent);
1178 }
1179 catch (std::bad_alloc &)
1180 {
1181 vrc = VERR_NO_MEMORY;
1182 }
1183 if (RT_FAILURE(vrc))
1184 return vrc;
1185
1186 vrc = i_startProcessInner(cMsTimeout, alock, pEvent, pvrcGuest);
1187
1188 unregisterWaitEvent(pEvent);
1189
1190 LogFlowFuncLeaveRC(vrc);
1191 return vrc;
1192}
1193
1194/**
1195 * Helper function to start a process on the guest. Do not call directly!
1196 *
1197 * @returns VBox status code.
1198 * @param cMsTimeout Timeout (in ms) to wait for starting the process.
1199 * @param rLock Write lock to use for serialization.
1200 * @param pEvent Event to use for notifying waiters.
1201 * @param pvrcGuest Where to return the guest error when
1202 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1203 */
1204int GuestProcess::i_startProcessInner(uint32_t cMsTimeout, AutoWriteLock &rLock, GuestWaitEvent *pEvent, int *pvrcGuest)
1205{
1206 GuestSession *pSession = mSession;
1207 AssertPtr(pSession);
1208 uint32_t const uProtocol = pSession->i_getProtocolVersion();
1209
1210 const GuestCredentials &sessionCreds = pSession->i_getCredentials();
1211
1212 /* Prepare arguments. */
1213 size_t cArgs = mData.mProcess.mArguments.size();
1214 if (cArgs >= 128*1024)
1215 return VERR_BUFFER_OVERFLOW;
1216
1217 Guest *pGuest = mSession->i_getParent();
1218 AssertPtr(pGuest);
1219 const uint64_t fGuestControlFeatures0 = pGuest->i_getGuestControlFeatures0();
1220
1221 /* Check if the Guest Additions support setting the current working directory for the new process
1222 * if the caller wants to set one, and bail out early if it doesn't. */
1223 if ( !mData.mProcess.mCwd.isEmpty()
1224 && ( uProtocol < 2
1225 || !(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_CWD)))
1226 {
1227 LogRel2(("Guest Control: Installed Guest Addtions don't support setting the current working directory to '%s'!\n",
1228 mData.mProcess.mCwd.c_str()));
1229 return VERR_NOT_SUPPORTED;
1230 }
1231
1232 size_t cbArgs = 0;
1233 char *pszArgs = NULL;
1234 int vrc = VINF_SUCCESS;
1235
1236 if (cArgs)
1237 {
1238 char const **papszArgv = (char const **)RTMemAlloc((cArgs + 1) * sizeof(papszArgv[0]));
1239 AssertReturn(papszArgv, VERR_NO_MEMORY);
1240
1241 for (size_t i = 0; i < cArgs; i++)
1242 {
1243 papszArgv[i] = mData.mProcess.mArguments[i].c_str();
1244 AssertPtr(papszArgv[i]);
1245 }
1246 papszArgv[cArgs] = NULL;
1247
1248 /* If the Guest Additions don't support using argv[0] correctly (< 6.1.x), don't supply it. */
1249 if (!(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0))
1250 vrc = RTGetOptArgvToString(&pszArgs, papszArgv + 1, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1251 else /* ... else send the whole argv, including argv[0]. */
1252 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1253
1254 RTMemFree(papszArgv);
1255 if (RT_FAILURE(vrc))
1256 return vrc;
1257
1258 /* Note! No direct returns after this. */
1259 }
1260
1261 /* Calculate arguments size (in bytes). */
1262 AssertPtr(pszArgs);
1263 cbArgs = strlen(pszArgs) + 1; /* Include terminating zero. */
1264
1265 /* Prepare environment. The guest service dislikes the empty string at the end, so drop it. */
1266 size_t cbEnvBlock = 0; /* Shut up MSVC. */
1267 char *pszzEnvBlock = NULL; /* Ditto. */
1268 vrc = mData.mProcess.mEnvironmentChanges.queryUtf8Block(&pszzEnvBlock, &cbEnvBlock);
1269 if (RT_SUCCESS(vrc))
1270 {
1271 Assert(cbEnvBlock > 0);
1272 cbEnvBlock--;
1273 AssertPtr(pszzEnvBlock);
1274
1275 /* Prepare HGCM call. */
1276 VBOXHGCMSVCPARM paParms[16];
1277 int i = 0;
1278 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1279 HGCMSvcSetRTCStr(&paParms[i++], mData.mProcess.mExecutable);
1280 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mFlags);
1281 HGCMSvcSetU32(&paParms[i++], (uint32_t)mData.mProcess.mArguments.size());
1282 HGCMSvcSetPv(&paParms[i++], pszArgs, (uint32_t)cbArgs);
1283 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mEnvironmentChanges.count());
1284 HGCMSvcSetU32(&paParms[i++], (uint32_t)cbEnvBlock);
1285 HGCMSvcSetPv(&paParms[i++], pszzEnvBlock, (uint32_t)cbEnvBlock);
1286 if (uProtocol < 2)
1287 {
1288 /* In protocol v1 (VBox < 4.3) the credentials were part of the execution
1289 * call. In newer protocols these credentials are part of the opened guest
1290 * session, so not needed anymore here. */
1291 HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mUser);
1292 HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mPassword);
1293 }
1294 /*
1295 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1296 * until the process was started - the process itself then gets an infinite timeout for execution.
1297 * This is handy when we want to start a process inside a worker thread within a certain timeout
1298 * but let the started process perform lengthly operations then.
1299 */
1300 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1301 HGCMSvcSetU32(&paParms[i++], UINT32_MAX /* Infinite timeout */);
1302 else
1303 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mTimeoutMS);
1304 if (uProtocol >= 2)
1305 {
1306 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mPriority);
1307 /* CPU affinity: We only support one CPU affinity block at the moment,
1308 * so that makes up to 64 CPUs total. This can be more in the future. */
1309 HGCMSvcSetU32(&paParms[i++], 1);
1310 /* The actual CPU affinity blocks. */
1311 HGCMSvcSetPv(&paParms[i++], (void *)&mData.mProcess.mAffinity, sizeof(mData.mProcess.mAffinity));
1312 /* Supply working directory, if guest supports it. */
1313 if (fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_CWD)
1314 HGCMSvcSetRTCStr(&paParms[i++], mData.mProcess.mCwd);
1315 }
1316
1317 rLock.release(); /* Drop the write lock before sending. */
1318
1319 vrc = sendMessage(HOST_MSG_EXEC_CMD, i, paParms);
1320 if (RT_FAILURE(vrc))
1321 {
1322 int vrc2 = i_setProcessStatus(ProcessStatus_Error, vrc);
1323 AssertRC(vrc2);
1324 }
1325
1326 mData.mProcess.mEnvironmentChanges.freeUtf8Block(pszzEnvBlock);
1327 }
1328
1329 RTStrFree(pszArgs);
1330
1331 if (RT_SUCCESS(vrc))
1332 vrc = i_waitForStatusChange(pEvent, cMsTimeout, NULL /* Process status */, pvrcGuest);
1333 return vrc;
1334}
1335
1336/**
1337 * Starts the process asynchronously (via worker thread) on the guest.
1338 *
1339 * @returns VBox status code.
1340 */
1341int GuestProcess::i_startProcessAsync(void)
1342{
1343 LogFlowThisFuncEnter();
1344
1345 /* Create the task: */
1346 GuestProcessStartTask *pTask = NULL;
1347 try
1348 {
1349 pTask = new GuestProcessStartTask(this);
1350 }
1351 catch (std::bad_alloc &)
1352 {
1353 LogFlowThisFunc(("out of memory\n"));
1354 return VERR_NO_MEMORY;
1355 }
1356 AssertReturnStmt(pTask->i_isOk(), delete pTask, E_FAIL); /* cannot fail for GuestProcessStartTask. */
1357 LogFlowThisFunc(("Successfully created GuestProcessStartTask object\n"));
1358
1359 /* Start the thread (always consumes the task): */
1360 HRESULT hrc = pTask->createThread();
1361 pTask = NULL;
1362 if (SUCCEEDED(hrc))
1363 return VINF_SUCCESS;
1364 LogFlowThisFunc(("Failed to create thread for GuestProcessStartTask\n"));
1365 return VERR_GENERAL_FAILURE;
1366}
1367
1368/**
1369 * Thread task which does the asynchronous starting of a guest process.
1370 *
1371 * @returns VBox status code.
1372 * @param pTask Process start task (context) to process.
1373 */
1374/* static */
1375int GuestProcess::i_startProcessThreadTask(GuestProcessStartTask *pTask)
1376{
1377 LogFlowFunc(("pTask=%p\n", pTask));
1378
1379 const ComObjPtr<GuestProcess> pProcess(pTask->i_process());
1380 Assert(!pProcess.isNull());
1381
1382 AutoCaller autoCaller(pProcess);
1383 if (FAILED(autoCaller.hrc()))
1384 return VERR_COM_UNEXPECTED;
1385
1386 int vrc = pProcess->i_startProcess(GSTCTL_DEFAULT_TIMEOUT_MS, NULL /* pvrcGuest, ignored */);
1387 /* Nothing to do here anymore. */
1388
1389 LogFlowFunc(("pProcess=%p, vrc=%Rrc\n", (GuestProcess *)pProcess, vrc));
1390 return vrc;
1391}
1392
1393/**
1394 * Terminates a guest process.
1395 *
1396 * @returns VBox status code.
1397 * @retval VWRN_INVALID_STATE if process not in running state (anymore).
1398 * @retval VERR_NOT_SUPPORTED if process termination is not supported on the guest.
1399 * @param uTimeoutMS Timeout (in ms) to wait for process termination.
1400 * @param pvrcGuest Where to return the guest error when
1401 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1402 *
1403 * @note Takes the write lock.
1404 */
1405int GuestProcess::i_terminateProcess(uint32_t uTimeoutMS, int *pvrcGuest)
1406{
1407 LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 int vrc = VINF_SUCCESS;
1412
1413 if (mData.mStatus != ProcessStatus_Started)
1414 {
1415 LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
1416 mData.mStatus));
1417 vrc = VWRN_INVALID_STATE;
1418 }
1419 else
1420 {
1421 AssertPtr(mSession);
1422 /* Note: VBox < 4.3 (aka protocol version 1) does not
1423 * support this, so just skip. */
1424 if (mSession->i_getProtocolVersion() < 2)
1425 vrc = VERR_NOT_SUPPORTED;
1426
1427 if (RT_SUCCESS(vrc))
1428 {
1429 GuestWaitEvent *pEvent = NULL;
1430 GuestEventTypes eventTypes;
1431 try
1432 {
1433 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1434
1435 vrc = registerWaitEvent(eventTypes, &pEvent);
1436 }
1437 catch (std::bad_alloc &)
1438 {
1439 vrc = VERR_NO_MEMORY;
1440 }
1441
1442 if (RT_FAILURE(vrc))
1443 return vrc;
1444
1445 VBOXHGCMSVCPARM paParms[4];
1446 int i = 0;
1447 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1448 HGCMSvcSetU32(&paParms[i++], mData.mPID);
1449
1450 alock.release(); /* Drop the write lock before sending. */
1451
1452 vrc = sendMessage(HOST_MSG_EXEC_TERMINATE, i, paParms);
1453 if (RT_SUCCESS(vrc))
1454 vrc = i_waitForStatusChange(pEvent, uTimeoutMS,
1455 NULL /* ProcessStatus */, pvrcGuest);
1456 unregisterWaitEvent(pEvent);
1457 }
1458 }
1459
1460 LogFlowFuncLeaveRC(vrc);
1461 return vrc;
1462}
1463
1464/**
1465 * Converts given process status / flags and wait flag combination
1466 * to an overall process wait result.
1467 *
1468 * @returns Overall process wait result.
1469 * @param fWaitFlags Process wait flags to use for conversion.
1470 * @param oldStatus Old process status to use for conversion.
1471 * @param newStatus New process status to use for conversion.
1472 * @param uProcFlags Process flags to use for conversion.
1473 * @param uProtocol Guest Control protocol version to use for conversion.
1474 */
1475/* static */
1476ProcessWaitResult_T GuestProcess::i_waitFlagsToResultEx(uint32_t fWaitFlags,
1477 ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
1478 uint32_t uProcFlags, uint32_t uProtocol)
1479{
1480 ProcessWaitResult_T waitResult = ProcessWaitResult_None;
1481
1482 switch (newStatus)
1483 {
1484 case ProcessStatus_TerminatedNormally:
1485 case ProcessStatus_TerminatedSignal:
1486 case ProcessStatus_TerminatedAbnormally:
1487 case ProcessStatus_Down:
1488 /* Nothing to wait for anymore. */
1489 waitResult = ProcessWaitResult_Terminate;
1490 break;
1491
1492 case ProcessStatus_TimedOutKilled:
1493 case ProcessStatus_TimedOutAbnormally:
1494 /* Dito. */
1495 waitResult = ProcessWaitResult_Timeout;
1496 break;
1497
1498 case ProcessStatus_Started:
1499 switch (oldStatus)
1500 {
1501 case ProcessStatus_Undefined:
1502 case ProcessStatus_Starting:
1503 /* Also wait for process start. */
1504 if (fWaitFlags & ProcessWaitForFlag_Start)
1505 waitResult = ProcessWaitResult_Start;
1506 else
1507 {
1508 /*
1509 * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
1510 * caller is not interested in getting further process statuses -- so just don't notify
1511 * anything here anymore and return.
1512 */
1513 if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1514 waitResult = ProcessWaitResult_Start;
1515 }
1516 break;
1517
1518 case ProcessStatus_Started:
1519 /* Only wait for process start. */
1520 if (fWaitFlags & ProcessWaitForFlag_Start)
1521 waitResult = ProcessWaitResult_Start;
1522 break;
1523
1524 default:
1525 AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
1526 oldStatus));
1527 if (fWaitFlags & ProcessWaitForFlag_Start)
1528 waitResult = ProcessWaitResult_Start;
1529 break;
1530 }
1531 break;
1532
1533 case ProcessStatus_Error:
1534 /* Nothing to wait for anymore. */
1535 waitResult = ProcessWaitResult_Error;
1536 break;
1537
1538 case ProcessStatus_Undefined:
1539 case ProcessStatus_Starting:
1540 case ProcessStatus_Terminating:
1541 case ProcessStatus_Paused:
1542 /* No result available yet, leave wait
1543 * flags untouched. */
1544 break;
1545#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1546 case ProcessStatus_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
1547#endif
1548 }
1549
1550 if (newStatus == ProcessStatus_Started)
1551 {
1552 /*
1553 * Filter out waits which are *not* supported using
1554 * older guest control Guest Additions.
1555 *
1556 */
1557 /** @todo ProcessWaitForFlag_Std* flags are not implemented yet. */
1558 if (uProtocol < 99) /* See @todo above. */
1559 {
1560 if ( waitResult == ProcessWaitResult_None
1561 /* We don't support waiting for stdin, out + err,
1562 * just skip waiting then. */
1563 && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1564 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1565 || (fWaitFlags & ProcessWaitForFlag_StdErr)
1566 )
1567 )
1568 {
1569 /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
1570 waitResult = ProcessWaitResult_WaitFlagNotSupported;
1571 }
1572 }
1573 }
1574
1575#ifdef DEBUG
1576 LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
1577 oldStatus, newStatus, fWaitFlags, waitResult));
1578#endif
1579 return waitResult;
1580}
1581
1582/**
1583 * Converts given wait flags to an overall process wait result.
1584 *
1585 * @returns Overall process wait result.
1586 * @param fWaitFlags Process wait flags to use for conversion.
1587 */
1588ProcessWaitResult_T GuestProcess::i_waitFlagsToResult(uint32_t fWaitFlags)
1589{
1590 AssertPtr(mSession);
1591 return GuestProcess::i_waitFlagsToResultEx(fWaitFlags,
1592 mData.mStatus /* oldStatus */, mData.mStatus /* newStatus */,
1593 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1594}
1595
1596/**
1597 * Waits for certain events of the guest process.
1598 *
1599 * @returns VBox status code.
1600 * @param fWaitFlags Process wait flags to wait for.
1601 * @param uTimeoutMS Timeout (in ms) to wait.
1602 * @param waitResult Where to return the process wait result on success.
1603 * @param pvrcGuest Where to return the guest error when
1604 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1605 * @note Takes the read lock.
1606 */
1607int GuestProcess::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, ProcessWaitResult_T &waitResult, int *pvrcGuest)
1608{
1609 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1610
1611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1612
1613 LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, vrcProc=%Rrc, pvrcGuest=%p\n",
1614 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, pvrcGuest));
1615
1616 /* Did some error occur before? Then skip waiting and return. */
1617 ProcessStatus_T curStatus = mData.mStatus;
1618 if (curStatus == ProcessStatus_Error)
1619 {
1620 waitResult = ProcessWaitResult_Error;
1621 AssertMsg(RT_FAILURE(mData.mLastError),
1622 ("No error vrc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
1623 if (pvrcGuest)
1624 *pvrcGuest = mData.mLastError; /* Return last set error. */
1625 LogFlowThisFunc(("Process is in error state (vrcGuest=%Rrc)\n", mData.mLastError));
1626 return VERR_GSTCTL_GUEST_ERROR;
1627 }
1628
1629 waitResult = i_waitFlagsToResult(fWaitFlags);
1630
1631 /* No waiting needed? Return immediately using the last set error. */
1632 if (waitResult != ProcessWaitResult_None)
1633 {
1634 if (pvrcGuest)
1635 *pvrcGuest = mData.mLastError; /* Return last set error (if any). */
1636 LogFlowThisFunc(("Nothing to wait for (vrcGuest=%Rrc)\n", mData.mLastError));
1637 return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
1638 }
1639
1640 /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
1641 if (!uTimeoutMS)
1642 uTimeoutMS = RT_INDEFINITE_WAIT;
1643
1644 int vrc;
1645
1646 GuestWaitEvent *pEvent = NULL;
1647 GuestEventTypes eventTypes;
1648 try
1649 {
1650 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1651
1652 vrc = registerWaitEvent(eventTypes, &pEvent);
1653 }
1654 catch (std::bad_alloc &)
1655 {
1656 vrc = VERR_NO_MEMORY;
1657 }
1658
1659 if (RT_FAILURE(vrc))
1660 return vrc;
1661
1662 alock.release(); /* Release lock before waiting. */
1663
1664 /*
1665 * Do the actual waiting.
1666 */
1667 ProcessStatus_T newStatus = ProcessStatus_Undefined;
1668 uint64_t u64StartMS = RTTimeMilliTS();
1669 for (;;)
1670 {
1671 uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
1672 if ( uTimeoutMS != RT_INDEFINITE_WAIT
1673 && u64ElapsedMS >= uTimeoutMS)
1674 {
1675 vrc = VERR_TIMEOUT;
1676 break;
1677 }
1678
1679 vrc = i_waitForStatusChange(pEvent,
1680 uTimeoutMS == RT_INDEFINITE_WAIT ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
1681 &newStatus, pvrcGuest);
1682 if (RT_SUCCESS(vrc))
1683 {
1684 alock.acquire();
1685
1686 waitResult = i_waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
1687 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1688#ifdef DEBUG
1689 LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
1690 fWaitFlags, newStatus, waitResult));
1691#endif
1692 if (waitResult != ProcessWaitResult_None) /* We got a waiting result. */
1693 break;
1694 }
1695 else /* Waiting failed, bail out. */
1696 break;
1697
1698 alock.release(); /* Don't hold lock in next waiting round. */
1699 }
1700
1701 unregisterWaitEvent(pEvent);
1702
1703 LogFlowThisFunc(("returns %Rrc - waitResult=%RU32 newStatus=%RU32 *pvrcGuest=%Rrc\n",
1704 vrc, waitResult, newStatus, pvrcGuest ? *pvrcGuest : -VERR_IPE_UNINITIALIZED_STATUS));
1705 return vrc;
1706}
1707
1708/**
1709 * Waits for a guest process input notification.
1710 *
1711 * @param pEvent Wait event to use for waiting.
1712 * @param uHandle Guest process file handle to wait for.
1713 * @param uTimeoutMS Timeout (in ms) to wait.
1714 * @param pInputStatus Where to return the process input status on success.
1715 * @param pcbProcessed Where to return the processed input (in bytes) on success.
1716 */
1717int GuestProcess::i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1718 ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
1719{
1720 RT_NOREF(uHandle);
1721 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1722
1723 VBoxEventType_T evtType;
1724 ComPtr<IEvent> pIEvent;
1725 int vrc = waitForEvent(pEvent, uTimeoutMS,
1726 &evtType, pIEvent.asOutParam());
1727 if (RT_SUCCESS(vrc))
1728 {
1729 if (evtType == VBoxEventType_OnGuestProcessInputNotify)
1730 {
1731 ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
1732 Assert(!pProcessEvent.isNull());
1733
1734 if (pInputStatus)
1735 {
1736 HRESULT hrc2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
1737 ComAssertComRC(hrc2);
1738 }
1739 if (pcbProcessed)
1740 {
1741 HRESULT hrc2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
1742 ComAssertComRC(hrc2);
1743 }
1744 }
1745 else
1746 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1747 }
1748
1749 LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, vrc=%Rrc\n", pEvent, uHandle, vrc));
1750 return vrc;
1751}
1752
1753/**
1754 * Waits for a guest process input notification.
1755 *
1756 * @returns VBox status code.
1757 * @param pEvent Wait event to use for waiting.
1758 * @param uHandle Guest process file handle to wait for.
1759 * @param uTimeoutMS Timeout (in ms) to wait.
1760 * @param pvData Where to store the guest process output on success.
1761 * @param cbData Size (in bytes) of \a pvData.
1762 * @param pcbRead Where to return the size (in bytes) read.
1763 */
1764int GuestProcess::i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1765 void *pvData, size_t cbData, uint32_t *pcbRead)
1766{
1767 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1768 /* pvData is optional. */
1769 /* cbData is optional. */
1770 /* pcbRead is optional. */
1771
1772 LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
1773 pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
1774
1775 int vrc;
1776
1777 VBoxEventType_T evtType;
1778 ComPtr<IEvent> pIEvent;
1779 do
1780 {
1781 vrc = waitForEvent(pEvent, uTimeoutMS,
1782 &evtType, pIEvent.asOutParam());
1783 if (RT_SUCCESS(vrc))
1784 {
1785 if (evtType == VBoxEventType_OnGuestProcessOutput)
1786 {
1787 ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
1788 Assert(!pProcessEvent.isNull());
1789
1790 ULONG uHandleEvent;
1791 HRESULT hrc = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
1792 if ( SUCCEEDED(hrc)
1793 && uHandleEvent == uHandle)
1794 {
1795 if (pvData)
1796 {
1797 com::SafeArray <BYTE> data;
1798 hrc = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1799 ComAssertComRC(hrc);
1800 size_t cbRead = data.size();
1801 if (cbRead)
1802 {
1803 if (cbRead <= cbData)
1804 {
1805 /* Copy data from event into our buffer. */
1806 memcpy(pvData, data.raw(), data.size());
1807 }
1808 else
1809 vrc = VERR_BUFFER_OVERFLOW;
1810
1811 LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), vrc=%Rrc\n", cbRead, uHandleEvent, vrc));
1812 }
1813 }
1814
1815 if ( RT_SUCCESS(vrc)
1816 && pcbRead)
1817 {
1818 ULONG cbRead;
1819 hrc = pProcessEvent->COMGETTER(Processed)(&cbRead);
1820 ComAssertComRC(hrc);
1821 *pcbRead = (uint32_t)cbRead;
1822 }
1823
1824 break;
1825 }
1826 else if (FAILED(hrc))
1827 vrc = VERR_COM_UNEXPECTED;
1828 }
1829 else
1830 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1831 }
1832
1833 } while (vrc == VINF_SUCCESS);
1834
1835 if ( vrc != VINF_SUCCESS
1836 && pcbRead)
1837 {
1838 *pcbRead = 0;
1839 }
1840
1841 LogFlowFuncLeaveRC(vrc);
1842 return vrc;
1843}
1844
1845/**
1846 * Waits for a guest process status change.
1847 *
1848 * @returns VBox status code.
1849 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1850 * @param pEvent Guest wait event to wait for.
1851 * @param uTimeoutMS Timeout (in ms) to wait.
1852 * @param pProcessStatus Where to return the process status on success.
1853 * @param pvrcGuest Where to return the guest error when
1854 * VERR_GSTCTL_GUEST_ERROR was returned.
1855 */
1856int GuestProcess::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1857 ProcessStatus_T *pProcessStatus, int *pvrcGuest)
1858{
1859 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1860 /* pProcessStatus is optional. */
1861 /* pvrcGuest is optional. */
1862
1863 VBoxEventType_T evtType;
1864 ComPtr<IEvent> pIEvent;
1865 int vrc = waitForEvent(pEvent, uTimeoutMS,
1866 &evtType, pIEvent.asOutParam());
1867 if (RT_SUCCESS(vrc))
1868 {
1869 Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
1870 ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
1871 Assert(!pProcessEvent.isNull());
1872
1873 ProcessStatus_T procStatus;
1874 HRESULT hrc = pProcessEvent->COMGETTER(Status)(&procStatus);
1875 ComAssertComRC(hrc);
1876 if (pProcessStatus)
1877 *pProcessStatus = procStatus;
1878
1879 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1880 hrc = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
1881 ComAssertComRC(hrc);
1882
1883 LONG lGuestRc;
1884 hrc = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1885 ComAssertComRC(hrc);
1886
1887 LogFlowThisFunc(("Got procStatus=%RU32, vrcGuest=%RI32 (%Rrc)\n", procStatus, lGuestRc, lGuestRc));
1888
1889 if (RT_FAILURE((int)lGuestRc))
1890 vrc = VERR_GSTCTL_GUEST_ERROR;
1891
1892 if (pvrcGuest)
1893 *pvrcGuest = (int)lGuestRc;
1894 }
1895 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make pvrcGuest is set. */
1896 else if (pEvent->HasGuestError() && pvrcGuest)
1897 *pvrcGuest = pEvent->GuestResult();
1898 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !pvrcGuest || *pvrcGuest != (int)0xcccccccc);
1899
1900 LogFlowFunc(("LEAVE: %Rrc *pvrcGuest=%Rrc *pProcessStatus=%d\n",
1901 vrc, pvrcGuest ? *pvrcGuest : -VERR_IPE_UNINITIALIZED_STATUS, pProcessStatus ? *pProcessStatus : -1));
1902 return vrc;
1903}
1904
1905#if 0 /* Unused */
1906/* static */
1907bool GuestProcess::i_waitResultImpliesEx(ProcessWaitResult_T waitResult, ProcessStatus_T procStatus, uint32_t uProtocol)
1908{
1909 RT_NOREF(uProtocol);
1910
1911 bool fImplies;
1912
1913 switch (waitResult)
1914 {
1915 case ProcessWaitResult_Start:
1916 fImplies = procStatus == ProcessStatus_Started;
1917 break;
1918
1919 case ProcessWaitResult_Terminate:
1920 fImplies = ( procStatus == ProcessStatus_TerminatedNormally
1921 || procStatus == ProcessStatus_TerminatedSignal
1922 || procStatus == ProcessStatus_TerminatedAbnormally
1923 || procStatus == ProcessStatus_TimedOutKilled
1924 || procStatus == ProcessStatus_TimedOutAbnormally
1925 || procStatus == ProcessStatus_Down
1926 || procStatus == ProcessStatus_Error);
1927 break;
1928
1929 default:
1930 fImplies = false;
1931 break;
1932 }
1933
1934 return fImplies;
1935}
1936#endif /* unused */
1937
1938/**
1939 * Writes input data to a guest process.
1940 *
1941 * @returns VBox status code.
1942 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1943 * @param uHandle Guest process file handle to write to.
1944 * @param uFlags Input flags of type PRocessInputFlag_XXX.
1945 * @param pvData Data to write to the guest process.
1946 * @param cbData Size (in bytes) of \a pvData to write.
1947 * @param uTimeoutMS Timeout (in ms) to wait.
1948 * @param puWritten Where to return the size (in bytes) written. Optional.
1949 * @param pvrcGuest Where to return the guest error when
1950 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1951 *
1952 * @note Takes the write lock.
1953 */
1954int GuestProcess::i_writeData(uint32_t uHandle, uint32_t uFlags,
1955 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pvrcGuest)
1956{
1957 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p, pvrcGuest=%p\n",
1958 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten, pvrcGuest));
1959 /* All is optional. There can be 0 byte writes. */
1960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 if (mData.mStatus != ProcessStatus_Started)
1963 {
1964 if (puWritten)
1965 *puWritten = 0;
1966 if (pvrcGuest)
1967 *pvrcGuest = VINF_SUCCESS;
1968 return VINF_SUCCESS; /* Not available for writing (anymore). */
1969 }
1970
1971 int vrc;
1972
1973 GuestWaitEvent *pEvent = NULL;
1974 GuestEventTypes eventTypes;
1975 try
1976 {
1977 /*
1978 * On Guest Additions < 4.3 there is no guarantee that the process status
1979 * change arrives *after* the input event, e.g. if this was the last input
1980 * block being written and the process will report status "terminate".
1981 * So just skip checking for process status change and only wait for the
1982 * input event.
1983 */
1984 if (mSession->i_getProtocolVersion() >= 2)
1985 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1986 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
1987
1988 vrc = registerWaitEvent(eventTypes, &pEvent);
1989 }
1990 catch (std::bad_alloc &)
1991 {
1992 vrc = VERR_NO_MEMORY;
1993 }
1994
1995 if (RT_FAILURE(vrc))
1996 return vrc;
1997
1998 VBOXHGCMSVCPARM paParms[5];
1999 int i = 0;
2000 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2001 HGCMSvcSetU32(&paParms[i++], mData.mPID);
2002 HGCMSvcSetU32(&paParms[i++], uFlags);
2003 HGCMSvcSetPv(&paParms[i++], pvData, (uint32_t)cbData);
2004 HGCMSvcSetU32(&paParms[i++], (uint32_t)cbData);
2005
2006 alock.release(); /* Drop the write lock before sending. */
2007
2008 uint32_t cbProcessed = 0;
2009 vrc = sendMessage(HOST_MSG_EXEC_SET_INPUT, i, paParms);
2010 if (RT_SUCCESS(vrc))
2011 {
2012 ProcessInputStatus_T inputStatus;
2013 vrc = i_waitForInputNotify(pEvent, uHandle, uTimeoutMS,
2014 &inputStatus, &cbProcessed);
2015 if (RT_SUCCESS(vrc))
2016 {
2017 /** @todo Set vrcGuest. */
2018
2019 if (puWritten)
2020 *puWritten = cbProcessed;
2021 }
2022 /** @todo Error handling. */
2023 }
2024
2025 unregisterWaitEvent(pEvent);
2026
2027 LogFlowThisFunc(("Returning cbProcessed=%RU32, vrc=%Rrc\n", cbProcessed, vrc));
2028 return vrc;
2029}
2030
2031// implementation of public methods
2032/////////////////////////////////////////////////////////////////////////////
2033
2034HRESULT GuestProcess::read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
2035{
2036 AutoCaller autoCaller(this);
2037 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2038
2039 if (aToRead == 0)
2040 return setError(E_INVALIDARG, tr("The size to read is zero"));
2041
2042 LogFlowThisFuncEnter();
2043
2044 aData.resize(aToRead);
2045
2046 HRESULT hrc = S_OK;
2047
2048 uint32_t cbRead;
2049 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2050 int vrc = i_readData(aHandle, aToRead, aTimeoutMS, &aData.front(), aToRead, &cbRead, &vrcGuest);
2051 if (RT_SUCCESS(vrc))
2052 {
2053 if (aData.size() != cbRead)
2054 aData.resize(cbRead);
2055 }
2056 else
2057 {
2058 aData.resize(0);
2059
2060 switch (vrc)
2061 {
2062 case VERR_GSTCTL_GUEST_ERROR:
2063 {
2064 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2065 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
2066 tr("Reading %RU32 bytes from guest process handle %RU32 failed: %s", "", aToRead),
2067 aToRead, aHandle, GuestBase::getErrorAsString(ge).c_str());
2068 break;
2069 }
2070 default:
2071 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from guest process \"%s\" (PID %RU32) failed: %Rrc"),
2072 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2073 break;
2074 }
2075 }
2076
2077 LogFlowThisFunc(("vrc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
2078 LogFlowFuncLeaveRC(vrc);
2079 return hrc;
2080}
2081
2082HRESULT GuestProcess::terminate()
2083{
2084 AutoCaller autoCaller(this);
2085 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2086
2087 LogFlowThisFuncEnter();
2088
2089 HRESULT hrc = S_OK;
2090
2091 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2092 int vrc = i_terminateProcess(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuest);
2093
2094 switch (vrc)
2095 {
2096 case VINF_SUCCESS:
2097 /* Nothing to do here, all good. */
2098 break;
2099
2100 case VWRN_INVALID_STATE:
2101 {
2102 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2103 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, VWRN_INVALID_STATE,
2104 tr("Guest process is not in '%s' state anymore (current is in '%s')"),
2105 GuestProcess::i_statusToString(ProcessStatus_Started).c_str(),
2106 GuestProcess::i_statusToString(i_getStatus()).c_str());
2107 break;
2108 }
2109
2110 case VERR_GSTCTL_GUEST_ERROR:
2111 {
2112 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2113 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Terminating guest process failed: %s"),
2114 GuestBase::getErrorAsString(ge).c_str());
2115 break;
2116 }
2117
2118 case VERR_NOT_SUPPORTED:
2119 {
2120 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
2121 tr("Terminating guest process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
2122 mData.mProcess.mExecutable.c_str(), mData.mPID);
2123 break;
2124 }
2125
2126 default:
2127 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Terminating guest process \"%s\" (PID %RU32) failed: %Rrc"),
2128 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2129 break;
2130 }
2131
2132 /* Note: Also could be VWRN_INVALID_STATE from i_terminateProcess().
2133 * In such a case we have to keep the process in our list in order to fullfill any upcoming responses / requests. */
2134 if (vrc == VINF_SUCCESS)
2135 {
2136 /* Remove process from guest session list. Now only API clients
2137 * still can hold references to it. */
2138 AssertPtr(mSession);
2139 int vrc2 = mSession->i_processUnregister(this);
2140 if (RT_SUCCESS(vrc))
2141 vrc = vrc2;
2142 }
2143
2144 LogFlowFuncLeaveRC(vrc);
2145 return hrc;
2146}
2147
2148/* Deprecated; use GuestProcess::waitForArray() instead. */
2149HRESULT GuestProcess::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
2150{
2151 AutoCaller autoCaller(this);
2152 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2153
2154 LogFlowThisFuncEnter();
2155
2156 /* Validate flags: */
2157 static ULONG const s_fValidFlags = ProcessWaitForFlag_None | ProcessWaitForFlag_Start | ProcessWaitForFlag_Terminate
2158 | ProcessWaitForFlag_StdIn | ProcessWaitForFlag_StdOut | ProcessWaitForFlag_StdErr;
2159 if (aWaitFor & ~s_fValidFlags)
2160 return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
2161 aWaitFor, aWaitFor & ~s_fValidFlags);
2162
2163 /*
2164 * Note: Do not hold any locks here while waiting!
2165 */
2166 HRESULT hrc = S_OK;
2167
2168 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2169 ProcessWaitResult_T waitResult;
2170 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &vrcGuest);
2171 if (RT_SUCCESS(vrc))
2172 {
2173 *aReason = waitResult;
2174 }
2175 else
2176 {
2177 switch (vrc)
2178 {
2179 case VERR_GSTCTL_GUEST_ERROR:
2180 {
2181 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2182 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Waiting for guest process (flags %#x) failed: %s"),
2183 aWaitFor, GuestBase::getErrorAsString(ge).c_str());
2184 break;
2185 }
2186 case VERR_TIMEOUT:
2187 *aReason = ProcessWaitResult_Timeout;
2188 break;
2189
2190 default:
2191 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Waiting for guest process \"%s\" (PID %RU32) failed: %Rrc"),
2192 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2193 break;
2194 }
2195 }
2196
2197 LogFlowFuncLeaveRC(vrc);
2198 return hrc;
2199}
2200
2201HRESULT GuestProcess::waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
2202 ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
2203{
2204 uint32_t fWaitFor = ProcessWaitForFlag_None;
2205 for (size_t i = 0; i < aWaitFor.size(); i++)
2206 fWaitFor |= aWaitFor[i];
2207
2208 return WaitFor(fWaitFor, aTimeoutMS, aReason);
2209}
2210
2211HRESULT GuestProcess::write(ULONG aHandle, ULONG aFlags, const std::vector<BYTE> &aData,
2212 ULONG aTimeoutMS, ULONG *aWritten)
2213{
2214 static ULONG const s_fValidFlags = ProcessInputFlag_None | ProcessInputFlag_EndOfFile;
2215 if (aFlags & ~s_fValidFlags)
2216 return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
2217 aFlags, aFlags & ~s_fValidFlags);
2218
2219 AutoCaller autoCaller(this);
2220 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2221
2222 LogFlowThisFuncEnter();
2223
2224 HRESULT hrc = S_OK;
2225
2226 uint32_t cbWritten;
2227 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2228 uint32_t cbData = (uint32_t)aData.size();
2229 void *pvData = cbData > 0 ? (void *)&aData.front() : NULL;
2230 int vrc = i_writeData(aHandle, aFlags, pvData, cbData, aTimeoutMS, &cbWritten, &vrcGuest);
2231 if (RT_FAILURE(vrc))
2232 {
2233 switch (vrc)
2234 {
2235 case VERR_GSTCTL_GUEST_ERROR:
2236 {
2237 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2238 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
2239 tr("Writing %RU32 bytes (flags %#x) to guest process failed: %s", "", cbData),
2240 cbData, aFlags, GuestBase::getErrorAsString(ge).c_str());
2241 break;
2242 }
2243 default:
2244 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing to guest process \"%s\" (PID %RU32) failed: %Rrc"),
2245 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2246 break;
2247 }
2248 }
2249
2250 LogFlowThisFunc(("vrc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
2251
2252 *aWritten = (ULONG)cbWritten;
2253
2254 LogFlowFuncLeaveRC(vrc);
2255 return hrc;
2256}
2257
2258HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFlag_T> &aFlags,
2259 const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
2260{
2261 LogFlowThisFuncEnter();
2262
2263 ULONG fWrite = ProcessInputFlag_None;
2264 for (size_t i = 0; i < aFlags.size(); i++)
2265 fWrite |= aFlags[i];
2266
2267 return write(aHandle, fWrite, aData, aTimeoutMS, aWritten);
2268}
2269
2270///////////////////////////////////////////////////////////////////////////////
2271
2272GuestProcessWrapper::GuestProcessWrapper(void)
2273 : pSession(NULL),
2274 pProcess(NULL)
2275{
2276}
2277
2278GuestProcessWrapper::~GuestProcessWrapper(void)
2279{
2280 uninit();
2281}
2282
2283/**
2284 * Initializes and starts a process on the guest.
2285 *
2286 * @returns VBox status code.
2287 * @param pGuestSession Guest session the process should be started in.
2288 * @param startupInfo Guest process startup info to use for starting.
2289 * @param fAsync Whether to start asynchronously or not.
2290 * @param pvrcGuest Where to return the guest error when
2291 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2292 */
2293int GuestProcessWrapper::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
2294 bool fAsync, int *pvrcGuest)
2295{
2296 LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n",
2297 pGuestSession, startupInfo.mExecutable.c_str(), fAsync));
2298
2299 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2300 Assert(startupInfo.mArguments[0] == startupInfo.mExecutable);
2301
2302 pSession = pGuestSession;
2303 mStartupInfo = startupInfo;
2304
2305 /* Make sure the process is hidden. */
2306 mStartupInfo.mFlags |= ProcessCreateFlag_Hidden;
2307
2308 int vrc = pSession->i_processCreateEx(mStartupInfo, pProcess);
2309 if (RT_SUCCESS(vrc))
2310 {
2311 int vrcGuest = VINF_SUCCESS;
2312 vrc = fAsync
2313 ? pProcess->i_startProcessAsync()
2314 : pProcess->i_startProcess(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuest);
2315
2316 if ( RT_SUCCESS(vrc)
2317 && !fAsync
2318 && RT_FAILURE(vrcGuest)
2319 )
2320 {
2321 vrc = VERR_GSTCTL_GUEST_ERROR;
2322 }
2323
2324 if (pvrcGuest)
2325 *pvrcGuest = vrcGuest;
2326 }
2327
2328 LogFlowFuncLeaveRC(vrc);
2329 return vrc;
2330}
2331
2332/**
2333 * Unitializes a guest process by terminating it on the guest.
2334 */
2335void GuestProcessWrapper::uninit(void)
2336{
2337 /* Make sure the process is terminated and unregistered from the guest session. */
2338 int vrcGuestIgnored;
2339 terminate(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuestIgnored);
2340
2341 /* Unregister the process from the process (and the session's object) list. */
2342 if ( pSession
2343 && pProcess)
2344 pSession->i_processUnregister(pProcess);
2345
2346 /* Release references. */
2347 pProcess.setNull();
2348 pSession.setNull();
2349}
2350
2351/**
2352 * Returns whether a guest process is still running or not.
2353 *
2354 * @returns \c true if running, or \c false if not.
2355 */
2356bool GuestProcessWrapper::isRunning(void)
2357{
2358 AssertReturn(!pProcess.isNull(), false);
2359
2360 ProcessStatus_T procStatus = ProcessStatus_Undefined;
2361 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
2362 AssertComRC(hrc);
2363
2364 if ( procStatus == ProcessStatus_Started
2365 || procStatus == ProcessStatus_Paused
2366 || procStatus == ProcessStatus_Terminating)
2367 {
2368 return true;
2369 }
2370
2371 return false;
2372}
2373
2374/**
2375 * Returns whether the guest process has been run correctly or not, based on it's internal process
2376 * status and reported exit status.
2377 *
2378 * @return @c true if the guest process has been run correctly (exit status 0), or @c false if some error
2379 * occurred (exit status <> 0 or wrong process state).
2380 */
2381bool GuestProcessWrapper::isTerminatedOk(void)
2382{
2383 return getTerminationStatus() == VINF_SUCCESS;
2384}
2385
2386
2387/**
2388 * Reports if the guest process has been run correctly.
2389 *
2390 * @retval VINF_SUCCESS if the process has stopped and the exit code is zero.
2391 * @retval VERR_GSTCTL_PROCESS_EXIT_CODE if the exit code is _not_ zero.
2392 * @retval VERR_GSTCTL_PROCESS_WRONG_STATE if still running.
2393 *
2394 * @param piExitCode Exit code of the tool. Optional.
2395 */
2396int GuestProcessWrapper::getTerminationStatus(int32_t *piExitCode /* = NULL */)
2397{
2398 Assert(!pProcess.isNull());
2399 AssertPtrNull(piExitCode);
2400
2401 int vrc;
2402 if (!isRunning())
2403 {
2404 LONG iExitCode = -1;
2405 HRESULT hrc = pProcess->COMGETTER(ExitCode(&iExitCode));
2406 AssertComRC(hrc);
2407
2408 if (piExitCode)
2409 *piExitCode = iExitCode;
2410
2411 vrc = iExitCode == 0 ? VINF_SUCCESS : VERR_GSTCTL_PROCESS_EXIT_CODE;
2412 }
2413 else
2414 {
2415 if (piExitCode)
2416 *piExitCode = -1;
2417 vrc = VERR_GSTCTL_PROCESS_WRONG_STATE;
2418 }
2419
2420 LogFlowFuncLeaveRC(vrc);
2421 return vrc;
2422}
2423
2424/**
2425 * Terminates a guest process.
2426 *
2427 * @returns VBox status code.
2428 * @param uTimeoutMS Timeout (in ms) to wait.
2429 * @param pvrcGuest Where to return the guest error when
2430 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2431 */
2432int GuestProcessWrapper::terminate(uint32_t uTimeoutMS, int *pvrcGuest)
2433{
2434 LogFlowThisFuncEnter();
2435
2436 int vrc;
2437 if (!pProcess.isNull())
2438 vrc = pProcess->i_terminateProcess(uTimeoutMS, pvrcGuest);
2439 else
2440 vrc = VERR_NOT_FOUND;
2441
2442 LogFlowFuncLeaveRC(vrc);
2443 return vrc;
2444}
2445
2446///////////////////////////////////////////////////////////////////////////////
2447
2448#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
2449GuestProcessToolbox::GuestProcessToolbox(void)
2450{
2451}
2452
2453GuestProcessToolbox::~GuestProcessToolbox(void)
2454{
2455}
2456
2457/**
2458 * Waits for a guest toolbox process.
2459 *
2460 * @returns VBox status code.
2461 * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
2462 * @param pvrcGuest Where to return the guest error when
2463 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2464 */
2465int GuestProcessToolbox::wait(uint32_t fToolWaitFlags, int *pvrcGuest)
2466{
2467 return waitEx(fToolWaitFlags, NULL /* pStrmBlkOut */, pvrcGuest);
2468}
2469
2470/**
2471 * Waits for a guest toolbox process, also returning process output.
2472 *
2473 * @returns VBox status code.
2474 * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
2475 * @param pStrmBlkOut Where to store the guest toolbox output.
2476 * @param pvrcGuest Where to return the guest error when
2477 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2478 */
2479int GuestProcessToolbox::waitEx(uint32_t fToolWaitFlags, GuestToolboxStreamBlock *pStrmBlkOut, int *pvrcGuest)
2480{
2481 LogFlowThisFunc(("fToolWaitFlags=0x%x, pStrmBlkOut=%p, pvrcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, pvrcGuest));
2482
2483 int vrc;
2484
2485 /* Is the next block complete without waiting for new data from the guest? */
2486 if (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK)
2487 {
2488 AssertPtr(pStrmBlkOut);
2489 vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
2490 if ( RT_SUCCESS(vrc)
2491 && pStrmBlkOut->IsComplete())
2492 return vrc;
2493 /* else do the waiting below. */
2494 }
2495
2496 /* Do the waiting. */
2497 uint32_t fProcWaitForFlags = ProcessWaitForFlag_Terminate;
2498 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2499 fProcWaitForFlags |= ProcessWaitForFlag_StdOut;
2500 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2501 fProcWaitForFlags |= ProcessWaitForFlag_StdErr;
2502
2503 /** @todo Decrease timeout while running. */
2504 uint64_t u64StartMS = RTTimeMilliTS();
2505 uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
2506
2507 int vrcGuest = VINF_SUCCESS;
2508 bool fDone = false;
2509
2510 BYTE abBuf[_64K];
2511 uint32_t cbRead;
2512
2513 bool fHandleStdOut = false;
2514 bool fHandleStdErr = false;
2515
2516 /**
2517 * Updates the elapsed time and checks if a
2518 * timeout happened, then breaking out of the loop.
2519 */
2520#define UPDATE_AND_CHECK_ELAPSED_TIME() \
2521 u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
2522 if ( uTimeoutMS != RT_INDEFINITE_WAIT \
2523 && u64ElapsedMS >= uTimeoutMS) \
2524 { \
2525 vrc = VERR_TIMEOUT; \
2526 break; \
2527 }
2528
2529 /**
2530 * Returns the remaining time (in ms).
2531 */
2532#define GET_REMAINING_TIME (uTimeoutMS == RT_INDEFINITE_WAIT ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS)
2533
2534 ProcessWaitResult_T waitRes = ProcessWaitResult_None;
2535 do
2536 {
2537 uint64_t u64ElapsedMS;
2538 UPDATE_AND_CHECK_ELAPSED_TIME();
2539
2540 vrc = pProcess->i_waitFor(fProcWaitForFlags, GET_REMAINING_TIME, waitRes, &vrcGuest);
2541 if (RT_FAILURE(vrc))
2542 break;
2543
2544 switch (waitRes)
2545 {
2546 case ProcessWaitResult_StdIn:
2547 vrc = VERR_NOT_IMPLEMENTED;
2548 break;
2549
2550 case ProcessWaitResult_StdOut:
2551 fHandleStdOut = true;
2552 break;
2553
2554 case ProcessWaitResult_StdErr:
2555 fHandleStdErr = true;
2556 break;
2557
2558 case ProcessWaitResult_WaitFlagNotSupported:
2559 if (fProcWaitForFlags & ProcessWaitForFlag_StdOut)
2560 fHandleStdOut = true;
2561 if (fProcWaitForFlags & ProcessWaitForFlag_StdErr)
2562 fHandleStdErr = true;
2563 /* Since waiting for stdout / stderr is not supported by the guest,
2564 * wait a bit to not hog the CPU too much when polling for data. */
2565 RTThreadSleep(1); /* Optional, don't check vrc. */
2566 break;
2567
2568 case ProcessWaitResult_Error:
2569 vrc = VERR_GSTCTL_GUEST_ERROR;
2570 break;
2571
2572 case ProcessWaitResult_Terminate:
2573 fDone = true;
2574 break;
2575
2576 case ProcessWaitResult_Timeout:
2577 vrc = VERR_TIMEOUT;
2578 break;
2579
2580 case ProcessWaitResult_Start:
2581 case ProcessWaitResult_Status:
2582 /* Not used here, just skip. */
2583 break;
2584
2585 default:
2586 AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
2587 break;
2588 }
2589
2590 if (RT_FAILURE(vrc))
2591 break;
2592
2593 if (fHandleStdOut)
2594 {
2595 UPDATE_AND_CHECK_ELAPSED_TIME();
2596
2597 cbRead = 0;
2598 vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDOUT, sizeof(abBuf),
2599 GET_REMAINING_TIME,
2600 abBuf, sizeof(abBuf),
2601 &cbRead, &vrcGuest);
2602 if ( RT_FAILURE(vrc)
2603 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2604 break;
2605
2606 if (cbRead)
2607 {
2608 LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
2609 vrc = mStdOut.AddData(abBuf, cbRead);
2610
2611 if ( RT_SUCCESS(vrc)
2612 && (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK))
2613 {
2614 AssertPtr(pStrmBlkOut);
2615 vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
2616
2617 /* When successful, break out of the loop because we're done
2618 * with reading the first stream block. */
2619 if (RT_SUCCESS(vrc))
2620 fDone = true;
2621 }
2622 }
2623
2624 fHandleStdOut = false;
2625 }
2626
2627 if (fHandleStdErr)
2628 {
2629 UPDATE_AND_CHECK_ELAPSED_TIME();
2630
2631 cbRead = 0;
2632 vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDERR, sizeof(abBuf),
2633 GET_REMAINING_TIME,
2634 abBuf, sizeof(abBuf),
2635 &cbRead, &vrcGuest);
2636 if ( RT_FAILURE(vrc)
2637 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2638 break;
2639
2640 if (cbRead)
2641 {
2642 LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
2643 vrc = mStdErr.AddData(abBuf, cbRead);
2644 }
2645
2646 fHandleStdErr = false;
2647 }
2648
2649 } while (!fDone && RT_SUCCESS(vrc));
2650
2651#undef UPDATE_AND_CHECK_ELAPSED_TIME
2652#undef GET_REMAINING_TIME
2653
2654 if (RT_FAILURE(vrcGuest))
2655 vrc = VERR_GSTCTL_GUEST_ERROR;
2656
2657 LogFlowThisFunc(("Loop ended with vrc=%Rrc, vrcGuest=%Rrc, waitRes=%RU32\n", vrc, vrcGuest, waitRes));
2658 if (pvrcGuest)
2659 *pvrcGuest = vrcGuest;
2660
2661 LogFlowFuncLeaveRC(vrc);
2662 return vrc;
2663}
2664
2665/**
2666 * Gets the current guest toolbox process stream block.
2667 *
2668 * @returns VBox status code.
2669 * @retval VINF_EOF if the stream reached its end.
2670 * @param uHandle Guest process file handle to get current block for.
2671 * @param strmBlock Where to return the stream block on success.
2672 */
2673int GuestProcessToolbox::getCurrentBlock(uint32_t uHandle, GuestToolboxStreamBlock &strmBlock)
2674{
2675 GuestToolboxStream *pStream = NULL;
2676 if (uHandle == GUEST_PROC_OUT_H_STDOUT)
2677 pStream = &mStdOut;
2678 else if (uHandle == GUEST_PROC_OUT_H_STDERR)
2679 pStream = &mStdErr;
2680
2681 if (!pStream)
2682 return VERR_INVALID_PARAMETER;
2683
2684 int vrc = pStream->ParseBlock(strmBlock);
2685
2686 LogFlowThisFunc(("vrc=%Rrc, currently %RU64 pairs\n", vrc, strmBlock.GetCount()));
2687 return vrc;
2688}
2689
2690/**
2691 * Returns the result code from an ended guest toolbox process.
2692 *
2693 * @returns Result code from guest toolbox process.
2694 */
2695int GuestProcessToolbox::getRc(void) const
2696{
2697 LONG exitCode = -1;
2698 HRESULT hrc = pProcess->COMGETTER(ExitCode(&exitCode));
2699 AssertComRC(hrc);
2700
2701 return GuestProcessToolbox::exitCodeToRc(mStartupInfo, exitCode);
2702}
2703
2704/**
2705 * Static helper function to start and wait for output of a certain toolbox tool.
2706 *
2707 * The optional @a pLstStdOutObjects argument enables the caller to retrieve
2708 * so-called parsable guest stream objects containing additional information on
2709 * a key value pair basis. Those objects are emitted on the guest side by the
2710 * tool to convey retrieve additional information, like names in a directory or
2711 * file statistics, to the host.
2712 *
2713 * @return IPRT status code.
2714 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2715 * @param startupInfo Startup information about the toolbox tool.
2716 * @param pvrcGuest Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned.
2717 * @param pLstStdOutObjects Pointer to vector to receive the stdout
2718 * objects. Optional.
2719 * @param cStdOutObjectsToRead Number of parsable guest stream objects to
2720 * try receive and parse. Ignored when
2721 * pLstStdOutObjects is NULL. Optional.
2722 */
2723/* static */
2724int GuestProcessToolbox::runTool(GuestSession *pGuestSession,
2725 GuestProcessStartupInfo const &startupInfo,
2726 int *pvrcGuest,
2727 GuestCtrlStreamObjects *pLstStdOutObjects /* = NULL */,
2728 uint32_t cStdOutObjectsToRead /* = 1 */)
2729{
2730 GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX };
2731 int vrc = GuestProcessToolbox::runToolWithErrorInfo(pGuestSession, startupInfo,
2732 errorInfo, pLstStdOutObjects, cStdOutObjectsToRead);
2733 if (RT_SUCCESS(vrc))
2734 {
2735 /* Translate exit statuses to VBox statuses according to the guest tool: */
2736 if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE)
2737 {
2738 errorInfo.vrcGuest = GuestProcessToolbox::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2739 vrc = VERR_GSTCTL_GUEST_ERROR;
2740 }
2741 /* In case of something else that may be relevant: */
2742 else if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_WRONG_STATE /* possible getTerminationStatus result, see below */)
2743 vrc = VERR_GSTCTL_GUEST_ERROR;
2744 else
2745 AssertMsg(errorInfo.vrcGuest == VERR_IPE_UNINITIALIZED_STATUS || errorInfo.vrcGuest == VINF_SUCCESS,
2746 ("vrcGuest=%Rrc iExitCode=%d vrc=%Rrc\n", errorInfo.vrcGuest, errorInfo.iExitCode, vrc));
2747 }
2748
2749 *pvrcGuest = errorInfo.vrcGuest;
2750 LogFlowFunc(("returns %Rrc - vrcGuest=%Rrc iExitCode=%d\n", vrc, errorInfo.vrcGuest, errorInfo.iExitCode));
2751 return vrc;
2752}
2753
2754/**
2755 * Bare-bone version of runTool that doesn't translate exit codes to statuses.
2756 *
2757 * It's currently not used by anyone other than runTool().
2758 *
2759 * @return VBox status code.
2760 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2761 * @param startupInfo Startup information about the toolbox tool.
2762 * @param errorInfo Error information returned for error handling.
2763 * @param pLstStdOutObjects Pointer to vector to receive the stdout
2764 * objects. Optional.
2765 * @param cStdOutObjectsToRead Number of parsable guest stream objects to
2766 * try receive and parse. Ignored when
2767 * pLstStdOutObjects is NULL. Optional.
2768 *
2769 * @note This will not do call exitCodeToRc, for that use runTool().
2770 */
2771/* static */
2772int GuestProcessToolbox::runToolWithErrorInfo(GuestSession *pGuestSession,
2773 GuestProcessStartupInfo const &startupInfo,
2774 GuestProcessToolErrorInfo &errorInfo,
2775 GuestCtrlStreamObjects *pLstStdOutObjects /* = NULL */,
2776 uint32_t cStdOutObjectsToRead /* = 1 */)
2777{
2778 GuestProcessToolbox procTool;
2779 int vrc = procTool.init(pGuestSession, startupInfo, false /* fAsync */, &errorInfo.vrcGuest);
2780 if (pLstStdOutObjects)
2781 while (RT_SUCCESS(vrc) && cStdOutObjectsToRead-- > 0)
2782 try
2783 {
2784 GuestToolboxStreamBlock strmBlk;
2785 vrc = procTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &strmBlk, &errorInfo.vrcGuest);
2786 pLstStdOutObjects->push_back(strmBlk);
2787 }
2788 catch (std::bad_alloc &)
2789 {
2790 vrc = VERR_NO_MEMORY;
2791 }
2792 if (RT_SUCCESS(vrc))
2793 {
2794 /* Make sure the process runs until completion. */
2795 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &errorInfo.vrcGuest);
2796 if (RT_SUCCESS(vrc))
2797 errorInfo.vrcGuest = procTool.getTerminationStatus(&errorInfo.iExitCode);
2798 }
2799
2800 LogFlowFunc(("returns %Rrc - vrcGuest=%Rrc iExitCode=%d (%#x)\n",
2801 vrc, errorInfo.vrcGuest, errorInfo.iExitCode, errorInfo.iExitCode));
2802 return vrc;
2803}
2804
2805/**
2806 * Converts a toolbox tool's exit code to an IPRT error code.
2807 *
2808 * @returns VBox status code.
2809 * @param startupInfo Startup info of the toolbox tool to lookup error code for.
2810 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
2811 */
2812/* static */
2813int GuestProcessToolbox::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode)
2814{
2815 if (startupInfo.mArguments.size() == 0)
2816 {
2817 AssertFailed();
2818 return VERR_GENERAL_FAILURE; /* Should not happen. */
2819 }
2820
2821 return exitCodeToRc(startupInfo.mArguments[0].c_str(), iExitCode);
2822}
2823
2824/**
2825 * Converts a toolbox tool's exit code to an IPRT error code.
2826 *
2827 * @returns VBox status code.
2828 * @param pszTool Name of toolbox tool to lookup error code for.
2829 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
2830 */
2831/* static */
2832int GuestProcessToolbox::exitCodeToRc(const char *pszTool, int32_t iExitCode)
2833{
2834 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
2835
2836 LogFlowFunc(("%s: %d\n", pszTool, iExitCode));
2837
2838 if (iExitCode == 0) /* No error? Bail out early. */
2839 return VINF_SUCCESS;
2840
2841 if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_CAT))
2842 {
2843 switch (iExitCode)
2844 {
2845 case VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2846 case VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2847 case VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2848 case VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION: return VERR_SHARING_VIOLATION;
2849 case VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY: return VERR_IS_A_DIRECTORY;
2850 default: break;
2851 }
2852 }
2853 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_LS))
2854 {
2855 switch (iExitCode)
2856 {
2857 /** @todo Handle access denied? */
2858 case RTEXITCODE_FAILURE: return VERR_PATH_NOT_FOUND;
2859 default: break;
2860 }
2861 }
2862 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_STAT))
2863 {
2864 switch (iExitCode)
2865 {
2866 case VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2867 case VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2868 case VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2869 case VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND: return VERR_NET_PATH_NOT_FOUND;
2870 default: break;
2871 }
2872 }
2873 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKDIR))
2874 {
2875 switch (iExitCode)
2876 {
2877 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2878 default: break;
2879 }
2880 }
2881 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKTEMP))
2882 {
2883 switch (iExitCode)
2884 {
2885 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2886 default: break;
2887 }
2888 }
2889 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_RM))
2890 {
2891 switch (iExitCode)
2892 {
2893 case RTEXITCODE_FAILURE: return VERR_FILE_NOT_FOUND;
2894 /** @todo RTPathRmCmd does not yet distinguish between not found and access denied yet. */
2895 default: break;
2896 }
2897 }
2898
2899 LogFunc(("Warning: Exit code %d not handled for tool '%s', returning VERR_GENERAL_FAILURE\n", iExitCode, pszTool));
2900
2901 if (iExitCode == RTEXITCODE_SYNTAX)
2902 return VERR_INTERNAL_ERROR_5;
2903 return VERR_GENERAL_FAILURE;
2904}
2905
2906/**
2907 * Returns a stringyfied error of a guest process tool error.
2908 *
2909 * @returns Stringyfied error.
2910 * @param pszTool Toolbox tool name to get stringyfied error for.
2911 * @param guestErrorInfo Guest error info to get stringyfied error for.
2912 */
2913/* static */
2914Utf8Str GuestProcessToolbox::guestErrorToString(const char *pszTool, const GuestErrorInfo &guestErrorInfo)
2915{
2916 Utf8Str strErr;
2917
2918 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
2919 switch (guestErrorInfo.getVrc())
2920 {
2921 case VERR_ACCESS_DENIED:
2922 strErr.printf(tr("Access to \"%s\" denied"), guestErrorInfo.getWhat().c_str());
2923 break;
2924
2925 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
2926 RT_FALL_THROUGH();
2927 case VERR_PATH_NOT_FOUND:
2928 strErr.printf(tr("No such file or directory \"%s\""), guestErrorInfo.getWhat().c_str());
2929 break;
2930
2931 case VERR_INVALID_VM_HANDLE:
2932 strErr.printf(tr("VMM device is not available (is the VM running?)"));
2933 break;
2934
2935 case VERR_HGCM_SERVICE_NOT_FOUND:
2936 strErr.printf(tr("The guest execution service is not available"));
2937 break;
2938
2939 case VERR_BAD_EXE_FORMAT:
2940 strErr.printf(tr("The file \"%s\" is not an executable format"), guestErrorInfo.getWhat().c_str());
2941 break;
2942
2943 case VERR_AUTHENTICATION_FAILURE:
2944 strErr.printf(tr("The user \"%s\" was not able to logon"), guestErrorInfo.getWhat().c_str());
2945 break;
2946
2947 case VERR_INVALID_NAME:
2948 strErr.printf(tr("The file \"%s\" is an invalid name"), guestErrorInfo.getWhat().c_str());
2949 break;
2950
2951 case VERR_TIMEOUT:
2952 strErr.printf(tr("The guest did not respond within time"));
2953 break;
2954
2955 case VERR_CANCELLED:
2956 strErr.printf(tr("The execution operation was canceled"));
2957 break;
2958
2959 case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
2960 strErr.printf(tr("Maximum number of concurrent guest processes has been reached"));
2961 break;
2962
2963 case VERR_NOT_FOUND:
2964 strErr.printf(tr("The guest execution service is not ready (yet)"));
2965 break;
2966
2967 default:
2968 strErr.printf(tr("Unhandled error %Rrc for \"%s\" occurred for tool \"%s\" on guest -- please file a bug report"),
2969 guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str(), pszTool);
2970 break;
2971 }
2972
2973 return strErr;
2974}
2975#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use