VirtualBox

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

Last change on this file since 98273 was 98272, checked in by vboxsync, 23 months ago

Main/Guest*: rc -> hrc/vrc. bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.8 KB
Line 
1/* $Id: GuestProcessImpl.cpp 98272 2023-01-24 10:57:32Z 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, procTimeoutMS=%RU32, procFlags=%x, sessionID=%RU32\n",
1161 cMsTimeout, mData.mProcess.mExecutable.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags,
1162 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 size_t cbArgs = 0;
1218 char *pszArgs = NULL;
1219 int vrc = VINF_SUCCESS;
1220 if (cArgs)
1221 {
1222 char const **papszArgv = (char const **)RTMemAlloc((cArgs + 1) * sizeof(papszArgv[0]));
1223 AssertReturn(papszArgv, VERR_NO_MEMORY);
1224
1225 for (size_t i = 0; i < cArgs; i++)
1226 {
1227 papszArgv[i] = mData.mProcess.mArguments[i].c_str();
1228 AssertPtr(papszArgv[i]);
1229 }
1230 papszArgv[cArgs] = NULL;
1231
1232 Guest *pGuest = mSession->i_getParent();
1233 AssertPtr(pGuest);
1234
1235 const uint64_t fGuestControlFeatures0 = pGuest->i_getGuestControlFeatures0();
1236
1237 /* If the Guest Additions don't support using argv[0] correctly (< 6.1.x), don't supply it. */
1238 if (!(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0))
1239 vrc = RTGetOptArgvToString(&pszArgs, papszArgv + 1, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1240 else /* ... else send the whole argv, including argv[0]. */
1241 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1242
1243 RTMemFree(papszArgv);
1244 if (RT_FAILURE(vrc))
1245 return vrc;
1246
1247 /* Note! No direct returns after this. */
1248 }
1249
1250 /* Calculate arguments size (in bytes). */
1251 AssertPtr(pszArgs);
1252 cbArgs = strlen(pszArgs) + 1; /* Include terminating zero. */
1253
1254 /* Prepare environment. The guest service dislikes the empty string at the end, so drop it. */
1255 size_t cbEnvBlock = 0; /* Shut up MSVC. */
1256 char *pszzEnvBlock = NULL; /* Ditto. */
1257 vrc = mData.mProcess.mEnvironmentChanges.queryUtf8Block(&pszzEnvBlock, &cbEnvBlock);
1258 if (RT_SUCCESS(vrc))
1259 {
1260 Assert(cbEnvBlock > 0);
1261 cbEnvBlock--;
1262 AssertPtr(pszzEnvBlock);
1263
1264 /* Prepare HGCM call. */
1265 VBOXHGCMSVCPARM paParms[16];
1266 int i = 0;
1267 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1268 HGCMSvcSetRTCStr(&paParms[i++], mData.mProcess.mExecutable);
1269 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mFlags);
1270 HGCMSvcSetU32(&paParms[i++], (uint32_t)mData.mProcess.mArguments.size());
1271 HGCMSvcSetPv(&paParms[i++], pszArgs, (uint32_t)cbArgs);
1272 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mEnvironmentChanges.count());
1273 HGCMSvcSetU32(&paParms[i++], (uint32_t)cbEnvBlock);
1274 HGCMSvcSetPv(&paParms[i++], pszzEnvBlock, (uint32_t)cbEnvBlock);
1275 if (uProtocol < 2)
1276 {
1277 /* In protocol v1 (VBox < 4.3) the credentials were part of the execution
1278 * call. In newer protocols these credentials are part of the opened guest
1279 * session, so not needed anymore here. */
1280 HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mUser);
1281 HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mPassword);
1282 }
1283 /*
1284 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1285 * until the process was started - the process itself then gets an infinite timeout for execution.
1286 * This is handy when we want to start a process inside a worker thread within a certain timeout
1287 * but let the started process perform lengthly operations then.
1288 */
1289 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1290 HGCMSvcSetU32(&paParms[i++], UINT32_MAX /* Infinite timeout */);
1291 else
1292 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mTimeoutMS);
1293 if (uProtocol >= 2)
1294 {
1295 HGCMSvcSetU32(&paParms[i++], mData.mProcess.mPriority);
1296 /* CPU affinity: We only support one CPU affinity block at the moment,
1297 * so that makes up to 64 CPUs total. This can be more in the future. */
1298 HGCMSvcSetU32(&paParms[i++], 1);
1299 /* The actual CPU affinity blocks. */
1300 HGCMSvcSetPv(&paParms[i++], (void *)&mData.mProcess.mAffinity, sizeof(mData.mProcess.mAffinity));
1301 }
1302
1303 rLock.release(); /* Drop the write lock before sending. */
1304
1305 vrc = sendMessage(HOST_MSG_EXEC_CMD, i, paParms);
1306 if (RT_FAILURE(vrc))
1307 {
1308 int vrc2 = i_setProcessStatus(ProcessStatus_Error, vrc);
1309 AssertRC(vrc2);
1310 }
1311
1312 mData.mProcess.mEnvironmentChanges.freeUtf8Block(pszzEnvBlock);
1313 }
1314
1315 RTStrFree(pszArgs);
1316
1317 if (RT_SUCCESS(vrc))
1318 vrc = i_waitForStatusChange(pEvent, cMsTimeout, NULL /* Process status */, pvrcGuest);
1319 return vrc;
1320}
1321
1322/**
1323 * Starts the process asynchronously (via worker thread) on the guest.
1324 *
1325 * @returns VBox status code.
1326 */
1327int GuestProcess::i_startProcessAsync(void)
1328{
1329 LogFlowThisFuncEnter();
1330
1331 /* Create the task: */
1332 GuestProcessStartTask *pTask = NULL;
1333 try
1334 {
1335 pTask = new GuestProcessStartTask(this);
1336 }
1337 catch (std::bad_alloc &)
1338 {
1339 LogFlowThisFunc(("out of memory\n"));
1340 return VERR_NO_MEMORY;
1341 }
1342 AssertReturnStmt(pTask->i_isOk(), delete pTask, E_FAIL); /* cannot fail for GuestProcessStartTask. */
1343 LogFlowThisFunc(("Successfully created GuestProcessStartTask object\n"));
1344
1345 /* Start the thread (always consumes the task): */
1346 HRESULT hrc = pTask->createThread();
1347 pTask = NULL;
1348 if (SUCCEEDED(hrc))
1349 return VINF_SUCCESS;
1350 LogFlowThisFunc(("Failed to create thread for GuestProcessStartTask\n"));
1351 return VERR_GENERAL_FAILURE;
1352}
1353
1354/**
1355 * Thread task which does the asynchronous starting of a guest process.
1356 *
1357 * @returns VBox status code.
1358 * @param pTask Process start task (context) to process.
1359 */
1360/* static */
1361int GuestProcess::i_startProcessThreadTask(GuestProcessStartTask *pTask)
1362{
1363 LogFlowFunc(("pTask=%p\n", pTask));
1364
1365 const ComObjPtr<GuestProcess> pProcess(pTask->i_process());
1366 Assert(!pProcess.isNull());
1367
1368 AutoCaller autoCaller(pProcess);
1369 if (FAILED(autoCaller.hrc()))
1370 return VERR_COM_UNEXPECTED;
1371
1372 int vrc = pProcess->i_startProcess(30 * 1000 /* 30s timeout */, NULL /* pvrcGuest, ignored */);
1373 /* Nothing to do here anymore. */
1374
1375 LogFlowFunc(("pProcess=%p, vrc=%Rrc\n", (GuestProcess *)pProcess, vrc));
1376 return vrc;
1377}
1378
1379/**
1380 * Terminates a guest process.
1381 *
1382 * @returns VBox status code.
1383 * @retval VWRN_INVALID_STATE if process not in running state (anymore).
1384 * @retval VERR_NOT_SUPPORTED if process termination is not supported on the guest.
1385 * @param uTimeoutMS Timeout (in ms) to wait for process termination.
1386 * @param pvrcGuest Where to return the guest error when
1387 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1388 *
1389 * @note Takes the write lock.
1390 */
1391int GuestProcess::i_terminateProcess(uint32_t uTimeoutMS, int *pvrcGuest)
1392{
1393 LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
1394
1395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 int vrc = VINF_SUCCESS;
1398
1399 if (mData.mStatus != ProcessStatus_Started)
1400 {
1401 LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
1402 mData.mStatus));
1403 vrc = VWRN_INVALID_STATE;
1404 }
1405 else
1406 {
1407 AssertPtr(mSession);
1408 /* Note: VBox < 4.3 (aka protocol version 1) does not
1409 * support this, so just skip. */
1410 if (mSession->i_getProtocolVersion() < 2)
1411 vrc = VERR_NOT_SUPPORTED;
1412
1413 if (RT_SUCCESS(vrc))
1414 {
1415 GuestWaitEvent *pEvent = NULL;
1416 GuestEventTypes eventTypes;
1417 try
1418 {
1419 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1420
1421 vrc = registerWaitEvent(eventTypes, &pEvent);
1422 }
1423 catch (std::bad_alloc &)
1424 {
1425 vrc = VERR_NO_MEMORY;
1426 }
1427
1428 if (RT_FAILURE(vrc))
1429 return vrc;
1430
1431 VBOXHGCMSVCPARM paParms[4];
1432 int i = 0;
1433 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1434 HGCMSvcSetU32(&paParms[i++], mData.mPID);
1435
1436 alock.release(); /* Drop the write lock before sending. */
1437
1438 vrc = sendMessage(HOST_MSG_EXEC_TERMINATE, i, paParms);
1439 if (RT_SUCCESS(vrc))
1440 vrc = i_waitForStatusChange(pEvent, uTimeoutMS,
1441 NULL /* ProcessStatus */, pvrcGuest);
1442 unregisterWaitEvent(pEvent);
1443 }
1444 }
1445
1446 LogFlowFuncLeaveRC(vrc);
1447 return vrc;
1448}
1449
1450/**
1451 * Converts given process status / flags and wait flag combination
1452 * to an overall process wait result.
1453 *
1454 * @returns Overall process wait result.
1455 * @param fWaitFlags Process wait flags to use for conversion.
1456 * @param oldStatus Old process status to use for conversion.
1457 * @param newStatus New process status to use for conversion.
1458 * @param uProcFlags Process flags to use for conversion.
1459 * @param uProtocol Guest Control protocol version to use for conversion.
1460 */
1461/* static */
1462ProcessWaitResult_T GuestProcess::i_waitFlagsToResultEx(uint32_t fWaitFlags,
1463 ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
1464 uint32_t uProcFlags, uint32_t uProtocol)
1465{
1466 ProcessWaitResult_T waitResult = ProcessWaitResult_None;
1467
1468 switch (newStatus)
1469 {
1470 case ProcessStatus_TerminatedNormally:
1471 case ProcessStatus_TerminatedSignal:
1472 case ProcessStatus_TerminatedAbnormally:
1473 case ProcessStatus_Down:
1474 /* Nothing to wait for anymore. */
1475 waitResult = ProcessWaitResult_Terminate;
1476 break;
1477
1478 case ProcessStatus_TimedOutKilled:
1479 case ProcessStatus_TimedOutAbnormally:
1480 /* Dito. */
1481 waitResult = ProcessWaitResult_Timeout;
1482 break;
1483
1484 case ProcessStatus_Started:
1485 switch (oldStatus)
1486 {
1487 case ProcessStatus_Undefined:
1488 case ProcessStatus_Starting:
1489 /* Also wait for process start. */
1490 if (fWaitFlags & ProcessWaitForFlag_Start)
1491 waitResult = ProcessWaitResult_Start;
1492 else
1493 {
1494 /*
1495 * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
1496 * caller is not interested in getting further process statuses -- so just don't notify
1497 * anything here anymore and return.
1498 */
1499 if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1500 waitResult = ProcessWaitResult_Start;
1501 }
1502 break;
1503
1504 case ProcessStatus_Started:
1505 /* Only wait for process start. */
1506 if (fWaitFlags & ProcessWaitForFlag_Start)
1507 waitResult = ProcessWaitResult_Start;
1508 break;
1509
1510 default:
1511 AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
1512 oldStatus));
1513 if (fWaitFlags & ProcessWaitForFlag_Start)
1514 waitResult = ProcessWaitResult_Start;
1515 break;
1516 }
1517 break;
1518
1519 case ProcessStatus_Error:
1520 /* Nothing to wait for anymore. */
1521 waitResult = ProcessWaitResult_Error;
1522 break;
1523
1524 case ProcessStatus_Undefined:
1525 case ProcessStatus_Starting:
1526 case ProcessStatus_Terminating:
1527 case ProcessStatus_Paused:
1528 /* No result available yet, leave wait
1529 * flags untouched. */
1530 break;
1531#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1532 case ProcessStatus_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
1533#endif
1534 }
1535
1536 if (newStatus == ProcessStatus_Started)
1537 {
1538 /*
1539 * Filter out waits which are *not* supported using
1540 * older guest control Guest Additions.
1541 *
1542 */
1543 /** @todo ProcessWaitForFlag_Std* flags are not implemented yet. */
1544 if (uProtocol < 99) /* See @todo above. */
1545 {
1546 if ( waitResult == ProcessWaitResult_None
1547 /* We don't support waiting for stdin, out + err,
1548 * just skip waiting then. */
1549 && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1550 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1551 || (fWaitFlags & ProcessWaitForFlag_StdErr)
1552 )
1553 )
1554 {
1555 /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
1556 waitResult = ProcessWaitResult_WaitFlagNotSupported;
1557 }
1558 }
1559 }
1560
1561#ifdef DEBUG
1562 LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
1563 oldStatus, newStatus, fWaitFlags, waitResult));
1564#endif
1565 return waitResult;
1566}
1567
1568/**
1569 * Converts given wait flags to an overall process wait result.
1570 *
1571 * @returns Overall process wait result.
1572 * @param fWaitFlags Process wait flags to use for conversion.
1573 */
1574ProcessWaitResult_T GuestProcess::i_waitFlagsToResult(uint32_t fWaitFlags)
1575{
1576 AssertPtr(mSession);
1577 return GuestProcess::i_waitFlagsToResultEx(fWaitFlags,
1578 mData.mStatus /* oldStatus */, mData.mStatus /* newStatus */,
1579 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1580}
1581
1582/**
1583 * Waits for certain events of the guest process.
1584 *
1585 * @returns VBox status code.
1586 * @param fWaitFlags Process wait flags to wait for.
1587 * @param uTimeoutMS Timeout (in ms) to wait.
1588 * @param waitResult Where to return the process wait result on success.
1589 * @param pvrcGuest Where to return the guest error when
1590 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1591 * @note Takes the read lock.
1592 */
1593int GuestProcess::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, ProcessWaitResult_T &waitResult, int *pvrcGuest)
1594{
1595 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1596
1597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, vrcProc=%Rrc, pvrcGuest=%p\n",
1600 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, pvrcGuest));
1601
1602 /* Did some error occur before? Then skip waiting and return. */
1603 ProcessStatus_T curStatus = mData.mStatus;
1604 if (curStatus == ProcessStatus_Error)
1605 {
1606 waitResult = ProcessWaitResult_Error;
1607 AssertMsg(RT_FAILURE(mData.mLastError),
1608 ("No error vrc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
1609 if (pvrcGuest)
1610 *pvrcGuest = mData.mLastError; /* Return last set error. */
1611 LogFlowThisFunc(("Process is in error state (vrcGuest=%Rrc)\n", mData.mLastError));
1612 return VERR_GSTCTL_GUEST_ERROR;
1613 }
1614
1615 waitResult = i_waitFlagsToResult(fWaitFlags);
1616
1617 /* No waiting needed? Return immediately using the last set error. */
1618 if (waitResult != ProcessWaitResult_None)
1619 {
1620 if (pvrcGuest)
1621 *pvrcGuest = mData.mLastError; /* Return last set error (if any). */
1622 LogFlowThisFunc(("Nothing to wait for (vrcGuest=%Rrc)\n", mData.mLastError));
1623 return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
1624 }
1625
1626 /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
1627 if (!uTimeoutMS)
1628 uTimeoutMS = RT_INDEFINITE_WAIT;
1629
1630 int vrc;
1631
1632 GuestWaitEvent *pEvent = NULL;
1633 GuestEventTypes eventTypes;
1634 try
1635 {
1636 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1637
1638 vrc = registerWaitEvent(eventTypes, &pEvent);
1639 }
1640 catch (std::bad_alloc &)
1641 {
1642 vrc = VERR_NO_MEMORY;
1643 }
1644
1645 if (RT_FAILURE(vrc))
1646 return vrc;
1647
1648 alock.release(); /* Release lock before waiting. */
1649
1650 /*
1651 * Do the actual waiting.
1652 */
1653 ProcessStatus_T newStatus = ProcessStatus_Undefined;
1654 uint64_t u64StartMS = RTTimeMilliTS();
1655 for (;;)
1656 {
1657 uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
1658 if ( uTimeoutMS != RT_INDEFINITE_WAIT
1659 && u64ElapsedMS >= uTimeoutMS)
1660 {
1661 vrc = VERR_TIMEOUT;
1662 break;
1663 }
1664
1665 vrc = i_waitForStatusChange(pEvent,
1666 uTimeoutMS == RT_INDEFINITE_WAIT ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
1667 &newStatus, pvrcGuest);
1668 if (RT_SUCCESS(vrc))
1669 {
1670 alock.acquire();
1671
1672 waitResult = i_waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
1673 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1674#ifdef DEBUG
1675 LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
1676 fWaitFlags, newStatus, waitResult));
1677#endif
1678 if (ProcessWaitResult_None != waitResult) /* We got a waiting result. */
1679 break;
1680 }
1681 else /* Waiting failed, bail out. */
1682 break;
1683
1684 alock.release(); /* Don't hold lock in next waiting round. */
1685 }
1686
1687 unregisterWaitEvent(pEvent);
1688
1689 LogFlowThisFunc(("Returned waitResult=%RU32, newStatus=%RU32, vrc=%Rrc\n", waitResult, newStatus, vrc));
1690 return vrc;
1691}
1692
1693/**
1694 * Waits for a guest process input notification.
1695 *
1696 * @param pEvent Wait event to use for waiting.
1697 * @param uHandle Guest process file handle to wait for.
1698 * @param uTimeoutMS Timeout (in ms) to wait.
1699 * @param pInputStatus Where to return the process input status on success.
1700 * @param pcbProcessed Where to return the processed input (in bytes) on success.
1701 */
1702int GuestProcess::i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1703 ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
1704{
1705 RT_NOREF(uHandle);
1706 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1707
1708 VBoxEventType_T evtType;
1709 ComPtr<IEvent> pIEvent;
1710 int vrc = waitForEvent(pEvent, uTimeoutMS,
1711 &evtType, pIEvent.asOutParam());
1712 if (RT_SUCCESS(vrc))
1713 {
1714 if (evtType == VBoxEventType_OnGuestProcessInputNotify)
1715 {
1716 ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
1717 Assert(!pProcessEvent.isNull());
1718
1719 if (pInputStatus)
1720 {
1721 HRESULT hrc2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
1722 ComAssertComRC(hrc2);
1723 }
1724 if (pcbProcessed)
1725 {
1726 HRESULT hrc2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
1727 ComAssertComRC(hrc2);
1728 }
1729 }
1730 else
1731 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1732 }
1733
1734 LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, vrc=%Rrc\n", pEvent, uHandle, vrc));
1735 return vrc;
1736}
1737
1738/**
1739 * Waits for a guest process input notification.
1740 *
1741 * @returns VBox status code.
1742 * @param pEvent Wait event to use for waiting.
1743 * @param uHandle Guest process file handle to wait for.
1744 * @param uTimeoutMS Timeout (in ms) to wait.
1745 * @param pvData Where to store the guest process output on success.
1746 * @param cbData Size (in bytes) of \a pvData.
1747 * @param pcbRead Where to return the size (in bytes) read.
1748 */
1749int GuestProcess::i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1750 void *pvData, size_t cbData, uint32_t *pcbRead)
1751{
1752 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1753 /* pvData is optional. */
1754 /* cbData is optional. */
1755 /* pcbRead is optional. */
1756
1757 LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
1758 pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
1759
1760 int vrc;
1761
1762 VBoxEventType_T evtType;
1763 ComPtr<IEvent> pIEvent;
1764 do
1765 {
1766 vrc = waitForEvent(pEvent, uTimeoutMS,
1767 &evtType, pIEvent.asOutParam());
1768 if (RT_SUCCESS(vrc))
1769 {
1770 if (evtType == VBoxEventType_OnGuestProcessOutput)
1771 {
1772 ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
1773 Assert(!pProcessEvent.isNull());
1774
1775 ULONG uHandleEvent;
1776 HRESULT hrc = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
1777 if ( SUCCEEDED(hrc)
1778 && uHandleEvent == uHandle)
1779 {
1780 if (pvData)
1781 {
1782 com::SafeArray <BYTE> data;
1783 hrc = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1784 ComAssertComRC(hrc);
1785 size_t cbRead = data.size();
1786 if (cbRead)
1787 {
1788 if (cbRead <= cbData)
1789 {
1790 /* Copy data from event into our buffer. */
1791 memcpy(pvData, data.raw(), data.size());
1792 }
1793 else
1794 vrc = VERR_BUFFER_OVERFLOW;
1795
1796 LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), vrc=%Rrc\n", cbRead, uHandleEvent, vrc));
1797 }
1798 }
1799
1800 if ( RT_SUCCESS(vrc)
1801 && pcbRead)
1802 {
1803 ULONG cbRead;
1804 hrc = pProcessEvent->COMGETTER(Processed)(&cbRead);
1805 ComAssertComRC(hrc);
1806 *pcbRead = (uint32_t)cbRead;
1807 }
1808
1809 break;
1810 }
1811 else if (FAILED(hrc))
1812 vrc = VERR_COM_UNEXPECTED;
1813 }
1814 else
1815 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1816 }
1817
1818 } while (vrc == VINF_SUCCESS);
1819
1820 if ( vrc != VINF_SUCCESS
1821 && pcbRead)
1822 {
1823 *pcbRead = 0;
1824 }
1825
1826 LogFlowFuncLeaveRC(vrc);
1827 return vrc;
1828}
1829
1830/**
1831 * Waits for a guest process status change.
1832 *
1833 * @returns VBox status code.
1834 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1835 * @param pEvent Guest wait event to wait for.
1836 * @param uTimeoutMS Timeout (in ms) to wait.
1837 * @param pProcessStatus Where to return the process status on success.
1838 * @param pvrcGuest Where to return the guest error when
1839 * VERR_GSTCTL_GUEST_ERROR was returned.
1840 */
1841int GuestProcess::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1842 ProcessStatus_T *pProcessStatus, int *pvrcGuest)
1843{
1844 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1845 /* pProcessStatus is optional. */
1846 /* pvrcGuest is optional. */
1847
1848 VBoxEventType_T evtType;
1849 ComPtr<IEvent> pIEvent;
1850 int vrc = waitForEvent(pEvent, uTimeoutMS,
1851 &evtType, pIEvent.asOutParam());
1852 if (RT_SUCCESS(vrc))
1853 {
1854 Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
1855 ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
1856 Assert(!pProcessEvent.isNull());
1857
1858 ProcessStatus_T procStatus;
1859 HRESULT hrc = pProcessEvent->COMGETTER(Status)(&procStatus);
1860 ComAssertComRC(hrc);
1861 if (pProcessStatus)
1862 *pProcessStatus = procStatus;
1863
1864 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1865 hrc = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
1866 ComAssertComRC(hrc);
1867
1868 LONG lGuestRc;
1869 hrc = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1870 ComAssertComRC(hrc);
1871
1872 LogFlowThisFunc(("Got procStatus=%RU32, vrcGuest=%RI32 (%Rrc)\n", procStatus, lGuestRc, lGuestRc));
1873
1874 if (RT_FAILURE((int)lGuestRc))
1875 vrc = VERR_GSTCTL_GUEST_ERROR;
1876
1877 if (pvrcGuest)
1878 *pvrcGuest = (int)lGuestRc;
1879 }
1880 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make pvrcGuest is set. */
1881 else if (vrc == VERR_GSTCTL_GUEST_ERROR && pvrcGuest)
1882 *pvrcGuest = pEvent->GuestResult();
1883 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !pvrcGuest || *pvrcGuest != (int)0xcccccccc);
1884
1885 LogFlowFuncLeaveRC(vrc);
1886 return vrc;
1887}
1888
1889#if 0 /* Unused */
1890/* static */
1891bool GuestProcess::i_waitResultImpliesEx(ProcessWaitResult_T waitResult, ProcessStatus_T procStatus, uint32_t uProtocol)
1892{
1893 RT_NOREF(uProtocol);
1894
1895 bool fImplies;
1896
1897 switch (waitResult)
1898 {
1899 case ProcessWaitResult_Start:
1900 fImplies = procStatus == ProcessStatus_Started;
1901 break;
1902
1903 case ProcessWaitResult_Terminate:
1904 fImplies = ( procStatus == ProcessStatus_TerminatedNormally
1905 || procStatus == ProcessStatus_TerminatedSignal
1906 || procStatus == ProcessStatus_TerminatedAbnormally
1907 || procStatus == ProcessStatus_TimedOutKilled
1908 || procStatus == ProcessStatus_TimedOutAbnormally
1909 || procStatus == ProcessStatus_Down
1910 || procStatus == ProcessStatus_Error);
1911 break;
1912
1913 default:
1914 fImplies = false;
1915 break;
1916 }
1917
1918 return fImplies;
1919}
1920#endif /* unused */
1921
1922/**
1923 * Writes input data to a guest process.
1924 *
1925 * @returns VBox status code.
1926 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1927 * @param uHandle Guest process file handle to write to.
1928 * @param uFlags Input flags of type PRocessInputFlag_XXX.
1929 * @param pvData Data to write to the guest process.
1930 * @param cbData Size (in bytes) of \a pvData to write.
1931 * @param uTimeoutMS Timeout (in ms) to wait.
1932 * @param puWritten Where to return the size (in bytes) written. Optional.
1933 * @param pvrcGuest Where to return the guest error when
1934 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
1935 *
1936 * @note Takes the write lock.
1937 */
1938int GuestProcess::i_writeData(uint32_t uHandle, uint32_t uFlags,
1939 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pvrcGuest)
1940{
1941 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p, pvrcGuest=%p\n",
1942 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten, pvrcGuest));
1943 /* All is optional. There can be 0 byte writes. */
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 if (mData.mStatus != ProcessStatus_Started)
1947 {
1948 if (puWritten)
1949 *puWritten = 0;
1950 if (pvrcGuest)
1951 *pvrcGuest = VINF_SUCCESS;
1952 return VINF_SUCCESS; /* Not available for writing (anymore). */
1953 }
1954
1955 int vrc;
1956
1957 GuestWaitEvent *pEvent = NULL;
1958 GuestEventTypes eventTypes;
1959 try
1960 {
1961 /*
1962 * On Guest Additions < 4.3 there is no guarantee that the process status
1963 * change arrives *after* the input event, e.g. if this was the last input
1964 * block being written and the process will report status "terminate".
1965 * So just skip checking for process status change and only wait for the
1966 * input event.
1967 */
1968 if (mSession->i_getProtocolVersion() >= 2)
1969 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1970 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
1971
1972 vrc = registerWaitEvent(eventTypes, &pEvent);
1973 }
1974 catch (std::bad_alloc &)
1975 {
1976 vrc = VERR_NO_MEMORY;
1977 }
1978
1979 if (RT_FAILURE(vrc))
1980 return vrc;
1981
1982 VBOXHGCMSVCPARM paParms[5];
1983 int i = 0;
1984 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1985 HGCMSvcSetU32(&paParms[i++], mData.mPID);
1986 HGCMSvcSetU32(&paParms[i++], uFlags);
1987 HGCMSvcSetPv(&paParms[i++], pvData, (uint32_t)cbData);
1988 HGCMSvcSetU32(&paParms[i++], (uint32_t)cbData);
1989
1990 alock.release(); /* Drop the write lock before sending. */
1991
1992 uint32_t cbProcessed = 0;
1993 vrc = sendMessage(HOST_MSG_EXEC_SET_INPUT, i, paParms);
1994 if (RT_SUCCESS(vrc))
1995 {
1996 ProcessInputStatus_T inputStatus;
1997 vrc = i_waitForInputNotify(pEvent, uHandle, uTimeoutMS,
1998 &inputStatus, &cbProcessed);
1999 if (RT_SUCCESS(vrc))
2000 {
2001 /** @todo Set vrcGuest. */
2002
2003 if (puWritten)
2004 *puWritten = cbProcessed;
2005 }
2006 /** @todo Error handling. */
2007 }
2008
2009 unregisterWaitEvent(pEvent);
2010
2011 LogFlowThisFunc(("Returning cbProcessed=%RU32, vrc=%Rrc\n", cbProcessed, vrc));
2012 return vrc;
2013}
2014
2015// implementation of public methods
2016/////////////////////////////////////////////////////////////////////////////
2017
2018HRESULT GuestProcess::read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
2019{
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2022
2023 if (aToRead == 0)
2024 return setError(E_INVALIDARG, tr("The size to read is zero"));
2025
2026 LogFlowThisFuncEnter();
2027
2028 aData.resize(aToRead);
2029
2030 HRESULT hrc = S_OK;
2031
2032 uint32_t cbRead;
2033 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2034 int vrc = i_readData(aHandle, aToRead, aTimeoutMS, &aData.front(), aToRead, &cbRead, &vrcGuest);
2035 if (RT_SUCCESS(vrc))
2036 {
2037 if (aData.size() != cbRead)
2038 aData.resize(cbRead);
2039 }
2040 else
2041 {
2042 aData.resize(0);
2043
2044 switch (vrc)
2045 {
2046 case VERR_GSTCTL_GUEST_ERROR:
2047 {
2048 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2049 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
2050 tr("Reading %RU32 bytes from guest process handle %RU32 failed: %s", "", aToRead),
2051 aToRead, aHandle, GuestBase::getErrorAsString(ge).c_str());
2052 break;
2053 }
2054 default:
2055 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from guest process \"%s\" (PID %RU32) failed: %Rrc"),
2056 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2057 break;
2058 }
2059 }
2060
2061 LogFlowThisFunc(("vrc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
2062 LogFlowFuncLeaveRC(vrc);
2063 return hrc;
2064}
2065
2066HRESULT GuestProcess::terminate()
2067{
2068 AutoCaller autoCaller(this);
2069 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2070
2071 LogFlowThisFuncEnter();
2072
2073 HRESULT hrc = S_OK;
2074
2075 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2076 int vrc = i_terminateProcess(30 * 1000 /* Timeout in ms */, &vrcGuest);
2077
2078 switch (vrc)
2079 {
2080 case VINF_SUCCESS:
2081 /* Nothing to do here, all good. */
2082 break;
2083
2084 case VWRN_INVALID_STATE:
2085 {
2086 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2087 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, VWRN_INVALID_STATE,
2088 tr("Guest process is not in '%s' state anymore (current is in '%s')"),
2089 GuestProcess::i_statusToString(ProcessStatus_Started).c_str(),
2090 GuestProcess::i_statusToString(i_getStatus()).c_str());
2091 break;
2092 }
2093
2094 case VERR_GSTCTL_GUEST_ERROR:
2095 {
2096 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2097 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Terminating guest process failed: %s"),
2098 GuestBase::getErrorAsString(ge).c_str());
2099 break;
2100 }
2101
2102 case VERR_NOT_SUPPORTED:
2103 {
2104 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
2105 tr("Terminating guest process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
2106 mData.mProcess.mExecutable.c_str(), mData.mPID);
2107 break;
2108 }
2109
2110 default:
2111 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Terminating guest process \"%s\" (PID %RU32) failed: %Rrc"),
2112 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2113 break;
2114 }
2115
2116 /* Note: Also could be VWRN_INVALID_STATE from i_terminateProcess().
2117 * In such a case we have to keep the process in our list in order to fullfill any upcoming responses / requests. */
2118 if (vrc == VINF_SUCCESS)
2119 {
2120 /* Remove process from guest session list. Now only API clients
2121 * still can hold references to it. */
2122 AssertPtr(mSession);
2123 int vrc2 = mSession->i_processUnregister(this);
2124 if (RT_SUCCESS(vrc))
2125 vrc = vrc2;
2126 }
2127
2128 LogFlowFuncLeaveRC(vrc);
2129 return hrc;
2130}
2131
2132HRESULT GuestProcess::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
2133{
2134 AutoCaller autoCaller(this);
2135 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2136
2137 LogFlowThisFuncEnter();
2138
2139 /* Validate flags: */
2140 static ULONG const s_fValidFlags = ProcessWaitForFlag_None | ProcessWaitForFlag_Start | ProcessWaitForFlag_Terminate
2141 | ProcessWaitForFlag_StdIn | ProcessWaitForFlag_StdOut | ProcessWaitForFlag_StdErr;
2142 if (aWaitFor & ~s_fValidFlags)
2143 return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
2144 aWaitFor, aWaitFor & ~s_fValidFlags);
2145
2146 /*
2147 * Note: Do not hold any locks here while waiting!
2148 */
2149 HRESULT hrc = S_OK;
2150
2151 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2152 ProcessWaitResult_T waitResult;
2153 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &vrcGuest);
2154 if (RT_SUCCESS(vrc))
2155 {
2156 *aReason = waitResult;
2157 }
2158 else
2159 {
2160 switch (vrc)
2161 {
2162 case VERR_GSTCTL_GUEST_ERROR:
2163 {
2164 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2165 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Waiting for guest process (flags %#x) failed: %s"),
2166 aWaitFor, GuestBase::getErrorAsString(ge).c_str());
2167 break;
2168 }
2169 case VERR_TIMEOUT:
2170 *aReason = ProcessWaitResult_Timeout;
2171 break;
2172
2173 default:
2174 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Waiting for guest process \"%s\" (PID %RU32) failed: %Rrc"),
2175 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2176 break;
2177 }
2178 }
2179
2180 LogFlowFuncLeaveRC(vrc);
2181 return hrc;
2182}
2183
2184HRESULT GuestProcess::waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
2185 ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
2186{
2187 uint32_t fWaitFor = ProcessWaitForFlag_None;
2188 for (size_t i = 0; i < aWaitFor.size(); i++)
2189 fWaitFor |= aWaitFor[i];
2190
2191 return WaitFor(fWaitFor, aTimeoutMS, aReason);
2192}
2193
2194HRESULT GuestProcess::write(ULONG aHandle, ULONG aFlags, const std::vector<BYTE> &aData,
2195 ULONG aTimeoutMS, ULONG *aWritten)
2196{
2197 static ULONG const s_fValidFlags = ProcessInputFlag_None | ProcessInputFlag_EndOfFile;
2198 if (aFlags & ~s_fValidFlags)
2199 return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
2200 aFlags, aFlags & ~s_fValidFlags);
2201
2202 AutoCaller autoCaller(this);
2203 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
2204
2205 LogFlowThisFuncEnter();
2206
2207 HRESULT hrc = S_OK;
2208
2209 uint32_t cbWritten;
2210 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2211 uint32_t cbData = (uint32_t)aData.size();
2212 void *pvData = cbData > 0 ? (void *)&aData.front() : NULL;
2213 int vrc = i_writeData(aHandle, aFlags, pvData, cbData, aTimeoutMS, &cbWritten, &vrcGuest);
2214 if (RT_FAILURE(vrc))
2215 {
2216 switch (vrc)
2217 {
2218 case VERR_GSTCTL_GUEST_ERROR:
2219 {
2220 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
2221 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
2222 tr("Writing %RU32 bytes (flags %#x) to guest process failed: %s", "", cbData),
2223 cbData, aFlags, GuestBase::getErrorAsString(ge).c_str());
2224 break;
2225 }
2226 default:
2227 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing to guest process \"%s\" (PID %RU32) failed: %Rrc"),
2228 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
2229 break;
2230 }
2231 }
2232
2233 LogFlowThisFunc(("vrc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
2234
2235 *aWritten = (ULONG)cbWritten;
2236
2237 LogFlowFuncLeaveRC(vrc);
2238 return hrc;
2239}
2240
2241HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFlag_T> &aFlags,
2242 const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
2243{
2244 LogFlowThisFuncEnter();
2245
2246 ULONG fWrite = ProcessInputFlag_None;
2247 for (size_t i = 0; i < aFlags.size(); i++)
2248 fWrite |= aFlags[i];
2249
2250 return write(aHandle, fWrite, aData, aTimeoutMS, aWritten);
2251}
2252
2253///////////////////////////////////////////////////////////////////////////////
2254
2255GuestProcessTool::GuestProcessTool(void)
2256 : pSession(NULL),
2257 pProcess(NULL)
2258{
2259}
2260
2261GuestProcessTool::~GuestProcessTool(void)
2262{
2263 uninit();
2264}
2265
2266/**
2267 * Initializes and starts a process tool on the guest.
2268 *
2269 * @returns VBox status code.
2270 * @param pGuestSession Guest session the process tools should be started in.
2271 * @param startupInfo Guest process startup info to use for starting.
2272 * @param fAsync Whether to start asynchronously or not.
2273 * @param pvrcGuest Where to return the guest error when
2274 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2275 */
2276int GuestProcessTool::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
2277 bool fAsync, int *pvrcGuest)
2278{
2279 LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n",
2280 pGuestSession, startupInfo.mExecutable.c_str(), fAsync));
2281
2282 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2283 Assert(startupInfo.mArguments[0] == startupInfo.mExecutable);
2284
2285 pSession = pGuestSession;
2286 mStartupInfo = startupInfo;
2287
2288 /* Make sure the process is hidden. */
2289 mStartupInfo.mFlags |= ProcessCreateFlag_Hidden;
2290
2291 int vrc = pSession->i_processCreateEx(mStartupInfo, pProcess);
2292 if (RT_SUCCESS(vrc))
2293 {
2294 int vrcGuest = VINF_SUCCESS;
2295 vrc = fAsync
2296 ? pProcess->i_startProcessAsync()
2297 : pProcess->i_startProcess(30 * 1000 /* 30s timeout */, &vrcGuest);
2298
2299 if ( RT_SUCCESS(vrc)
2300 && !fAsync
2301 && RT_FAILURE(vrcGuest)
2302 )
2303 {
2304 vrc = VERR_GSTCTL_GUEST_ERROR;
2305 }
2306
2307 if (pvrcGuest)
2308 *pvrcGuest = vrcGuest;
2309 }
2310
2311 LogFlowFuncLeaveRC(vrc);
2312 return vrc;
2313}
2314
2315/**
2316 * Unitializes a guest process tool by terminating it on the guest.
2317 */
2318void GuestProcessTool::uninit(void)
2319{
2320 /* Make sure the process is terminated and unregistered from the guest session. */
2321 int vrcGuestIgnored;
2322 terminate(30 * 1000 /* 30s timeout */, &vrcGuestIgnored);
2323
2324 /* Unregister the process from the process (and the session's object) list. */
2325 if ( pSession
2326 && pProcess)
2327 pSession->i_processUnregister(pProcess);
2328
2329 /* Release references. */
2330 pProcess.setNull();
2331 pSession.setNull();
2332}
2333
2334/**
2335 * Gets the current guest process stream block.
2336 *
2337 * @returns VBox status code.
2338 * @param uHandle Guest process file handle to get current block for.
2339 * @param strmBlock Where to return the stream block on success.
2340 */
2341int GuestProcessTool::getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock)
2342{
2343 const GuestProcessStream *pStream = NULL;
2344 if (uHandle == GUEST_PROC_OUT_H_STDOUT)
2345 pStream = &mStdOut;
2346 else if (uHandle == GUEST_PROC_OUT_H_STDERR)
2347 pStream = &mStdErr;
2348
2349 if (!pStream)
2350 return VERR_INVALID_PARAMETER;
2351
2352 /** @todo Why not using pStream down below and hardcode to mStdOut? */
2353
2354 int vrc;
2355 do
2356 {
2357 /* Try parsing the data to see if the current block is complete. */
2358 vrc = mStdOut.ParseBlock(strmBlock);
2359 if (strmBlock.GetCount())
2360 break;
2361 } while (RT_SUCCESS(vrc));
2362
2363 LogFlowThisFunc(("vrc=%Rrc, %RU64 pairs\n", vrc, strmBlock.GetCount()));
2364 return vrc;
2365}
2366
2367/**
2368 * Returns the result code from an ended guest process tool.
2369 *
2370 * @returns Result code from guest process tool.
2371 */
2372int GuestProcessTool::getRc(void) const
2373{
2374 LONG exitCode = -1;
2375 HRESULT hrc = pProcess->COMGETTER(ExitCode(&exitCode));
2376 AssertComRC(hrc);
2377
2378 return GuestProcessTool::exitCodeToRc(mStartupInfo, exitCode);
2379}
2380
2381/**
2382 * Returns whether a guest process tool is still running or not.
2383 *
2384 * @returns \c true if running, or \c false if not.
2385 */
2386bool GuestProcessTool::isRunning(void)
2387{
2388 AssertReturn(!pProcess.isNull(), false);
2389
2390 ProcessStatus_T procStatus = ProcessStatus_Undefined;
2391 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
2392 AssertComRC(hrc);
2393
2394 if ( procStatus == ProcessStatus_Started
2395 || procStatus == ProcessStatus_Paused
2396 || procStatus == ProcessStatus_Terminating)
2397 {
2398 return true;
2399 }
2400
2401 return false;
2402}
2403
2404/**
2405 * Returns whether the tool has been run correctly or not, based on it's internal process
2406 * status and reported exit status.
2407 *
2408 * @return @c true if the tool has been run correctly (exit status 0), or @c false if some error
2409 * occurred (exit status <> 0 or wrong process state).
2410 */
2411bool GuestProcessTool::isTerminatedOk(void)
2412{
2413 return getTerminationStatus() == VINF_SUCCESS ? true : false;
2414}
2415
2416/**
2417 * Static helper function to start and wait for a certain toolbox tool.
2418 *
2419 * This function most likely is the one you want to use in the first place if you
2420 * want to just use a toolbox tool and wait for its result. See runEx() if you also
2421 * needs its output.
2422 *
2423 * @return VBox status code.
2424 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2425 * @param startupInfo Startup information about the toolbox tool.
2426 * @param pvrcGuest Where to store the toolbox tool's specific error code in case
2427 * VERR_GSTCTL_GUEST_ERROR is returned.
2428 */
2429/* static */
2430int GuestProcessTool::run( GuestSession *pGuestSession,
2431 const GuestProcessStartupInfo &startupInfo,
2432 int *pvrcGuest /* = NULL */)
2433{
2434 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2435
2436 GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX };
2437 int vrc = runErrorInfo(pGuestSession, startupInfo, errorInfo);
2438 if (RT_SUCCESS(vrc))
2439 {
2440 /* Make sure to check the error information we got from the guest tool. */
2441 if (GuestProcess::i_isGuestError(errorInfo.vrcGuest))
2442 {
2443 if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */
2444 vrcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2445 else /* At least return something. */
2446 vrcGuest = errorInfo.vrcGuest;
2447
2448 if (pvrcGuest)
2449 *pvrcGuest = vrcGuest;
2450
2451 vrc = VERR_GSTCTL_GUEST_ERROR;
2452 }
2453 }
2454
2455 LogFlowFunc(("Returned vrc=%Rrc, vrcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.vrcGuest, errorInfo.iExitCode));
2456 return vrc;
2457}
2458
2459/**
2460 * Static helper function to start and wait for a certain toolbox tool, returning
2461 * extended error information from the guest.
2462 *
2463 * @return VBox status code.
2464 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2465 * @param startupInfo Startup information about the toolbox tool.
2466 * @param errorInfo Error information returned for error handling.
2467 */
2468/* static */
2469int GuestProcessTool::runErrorInfo(GuestSession *pGuestSession,
2470 GuestProcessStartupInfo const &startupInfo,
2471 GuestProcessToolErrorInfo &errorInfo)
2472{
2473 return runExErrorInfo(pGuestSession, startupInfo, NULL /* paStrmOutObjects */, 0 /* cStrmOutObjects */, errorInfo);
2474}
2475
2476/**
2477 * Static helper function to start and wait for output of a certain toolbox tool.
2478 *
2479 * @return IPRT status code.
2480 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2481 * @param startupInfo Startup information about the toolbox tool.
2482 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2483 * Optional.
2484 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2485 * @param pvrcGuest Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned. Optional.
2486 */
2487/* static */
2488int GuestProcessTool::runEx(GuestSession *pGuestSession,
2489 GuestProcessStartupInfo const &startupInfo,
2490 GuestCtrlStreamObjects *paStrmOutObjects,
2491 uint32_t cStrmOutObjects,
2492 int *pvrcGuest /* = NULL */)
2493{
2494 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2495
2496 GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX };
2497 int vrc = GuestProcessTool::runExErrorInfo(pGuestSession, startupInfo, paStrmOutObjects, cStrmOutObjects, errorInfo);
2498 if (RT_SUCCESS(vrc))
2499 {
2500 /* Make sure to check the error information we got from the guest tool. */
2501 if (GuestProcess::i_isGuestError(errorInfo.vrcGuest))
2502 {
2503 if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */
2504 vrcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode);
2505 else /* At least return something. */
2506 vrcGuest = errorInfo.vrcGuest;
2507
2508 if (pvrcGuest)
2509 *pvrcGuest = vrcGuest;
2510
2511 vrc = VERR_GSTCTL_GUEST_ERROR;
2512 }
2513 }
2514
2515 LogFlowFunc(("Returned vrc=%Rrc, vrcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.vrcGuest, errorInfo.iExitCode));
2516 return vrc;
2517}
2518
2519/**
2520 * Static helper function to start and wait for output of a certain toolbox tool.
2521 *
2522 * This is the extended version, which addds the possibility of retrieving parsable so-called guest stream
2523 * objects. Those objects are issued on the guest side as part of VBoxService's toolbox tools (think of a BusyBox-like approach)
2524 * on stdout and can be used on the host side to retrieve more information about the actual command issued on the guest side.
2525 *
2526 * @return VBox status code.
2527 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2528 * @param startupInfo Startup information about the toolbox tool.
2529 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2530 * Optional.
2531 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2532 * @param errorInfo Error information returned for error handling.
2533 */
2534/* static */
2535int GuestProcessTool::runExErrorInfo(GuestSession *pGuestSession,
2536 GuestProcessStartupInfo const &startupInfo,
2537 GuestCtrlStreamObjects *paStrmOutObjects,
2538 uint32_t cStrmOutObjects,
2539 GuestProcessToolErrorInfo &errorInfo)
2540{
2541 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2542 /* paStrmOutObjects is optional. */
2543
2544 /** @todo Check if this is a valid toolbox. */
2545
2546 GuestProcessTool procTool;
2547 int vrc = procTool.init(pGuestSession, startupInfo, false /* Async */, &errorInfo.vrcGuest);
2548 if (RT_SUCCESS(vrc))
2549 {
2550 while (cStrmOutObjects--)
2551 {
2552 try
2553 {
2554 GuestProcessStreamBlock strmBlk;
2555 vrc = procTool.waitEx( paStrmOutObjects
2556 ? GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK
2557 : GUESTPROCESSTOOL_WAIT_FLAG_NONE, &strmBlk, &errorInfo.vrcGuest);
2558 if (paStrmOutObjects)
2559 paStrmOutObjects->push_back(strmBlk);
2560 }
2561 catch (std::bad_alloc &)
2562 {
2563 vrc = VERR_NO_MEMORY;
2564 }
2565
2566 if (RT_FAILURE(vrc))
2567 break;
2568 }
2569 }
2570
2571 if (RT_SUCCESS(vrc))
2572 {
2573 /* Make sure the process runs until completion. */
2574 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &errorInfo.vrcGuest);
2575 if (RT_SUCCESS(vrc))
2576 errorInfo.vrcGuest = procTool.getTerminationStatus(&errorInfo.iExitCode);
2577 }
2578
2579 LogFlowFunc(("Returned vrc=%Rrc, vrcGuest=%Rrc, iExitCode=%d\n", vrc, errorInfo.vrcGuest, errorInfo.iExitCode));
2580 return vrc;
2581}
2582
2583/**
2584 * Reports if the tool has been run correctly.
2585 *
2586 * @return Will return VERR_GSTCTL_PROCESS_EXIT_CODE if the tool process returned an exit code <> 0,
2587 * VERR_GSTCTL_PROCESS_WRONG_STATE if the tool process is in a wrong state (e.g. still running),
2588 * or VINF_SUCCESS otherwise.
2589 *
2590 * @param piExitCode Exit code of the tool. Optional.
2591 */
2592int GuestProcessTool::getTerminationStatus(int32_t *piExitCode /* = NULL */)
2593{
2594 Assert(!pProcess.isNull());
2595 /* pExitCode is optional. */
2596
2597 int vrc;
2598 if (!isRunning())
2599 {
2600 LONG iExitCode = -1;
2601 HRESULT hrc = pProcess->COMGETTER(ExitCode(&iExitCode));
2602 AssertComRC(hrc);
2603
2604 if (piExitCode)
2605 *piExitCode = iExitCode;
2606
2607 vrc = iExitCode != 0 ? VERR_GSTCTL_PROCESS_EXIT_CODE : VINF_SUCCESS;
2608 }
2609 else
2610 vrc = VERR_GSTCTL_PROCESS_WRONG_STATE;
2611
2612 LogFlowFuncLeaveRC(vrc);
2613 return vrc;
2614}
2615
2616/**
2617 * Waits for a guest process tool.
2618 *
2619 * @returns VBox status code.
2620 * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
2621 * @param pvrcGuest Where to return the guest error when
2622 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2623 */
2624int GuestProcessTool::wait(uint32_t fToolWaitFlags, int *pvrcGuest)
2625{
2626 return waitEx(fToolWaitFlags, NULL /* pStrmBlkOut */, pvrcGuest);
2627}
2628
2629/**
2630 * Waits for a guest process tool, also returning process output.
2631 *
2632 * @returns VBox status code.
2633 * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
2634 * @param pStrmBlkOut Where to store the guest process output.
2635 * @param pvrcGuest Where to return the guest error when
2636 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2637 */
2638int GuestProcessTool::waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *pStrmBlkOut, int *pvrcGuest)
2639{
2640 LogFlowThisFunc(("fToolWaitFlags=0x%x, pStreamBlock=%p, pvrcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, pvrcGuest));
2641
2642 /* Can we parse the next block without waiting? */
2643 int vrc;
2644 if (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK)
2645 {
2646 AssertPtr(pStrmBlkOut);
2647 vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
2648 if (RT_SUCCESS(vrc))
2649 return vrc;
2650 /* else do the waiting below. */
2651 }
2652
2653 /* Do the waiting. */
2654 uint32_t fProcWaitForFlags = ProcessWaitForFlag_Terminate;
2655 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2656 fProcWaitForFlags |= ProcessWaitForFlag_StdOut;
2657 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2658 fProcWaitForFlags |= ProcessWaitForFlag_StdErr;
2659
2660 /** @todo Decrease timeout while running. */
2661 uint64_t u64StartMS = RTTimeMilliTS();
2662 uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
2663
2664 int vrcGuest = VINF_SUCCESS;
2665 bool fDone = false;
2666
2667 BYTE byBuf[_64K];
2668 uint32_t cbRead;
2669
2670 bool fHandleStdOut = false;
2671 bool fHandleStdErr = false;
2672
2673 /**
2674 * Updates the elapsed time and checks if a
2675 * timeout happened, then breaking out of the loop.
2676 */
2677#define UPDATE_AND_CHECK_ELAPSED_TIME() \
2678 u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
2679 if ( uTimeoutMS != RT_INDEFINITE_WAIT \
2680 && u64ElapsedMS >= uTimeoutMS) \
2681 { \
2682 vrc = VERR_TIMEOUT; \
2683 break; \
2684 }
2685
2686 /**
2687 * Returns the remaining time (in ms).
2688 */
2689#define GET_REMAINING_TIME \
2690 uTimeoutMS == RT_INDEFINITE_WAIT \
2691 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS \
2692
2693 ProcessWaitResult_T waitRes = ProcessWaitResult_None;
2694 do
2695 {
2696 uint64_t u64ElapsedMS;
2697 UPDATE_AND_CHECK_ELAPSED_TIME();
2698
2699 vrc = pProcess->i_waitFor(fProcWaitForFlags, GET_REMAINING_TIME, waitRes, &vrcGuest);
2700 if (RT_FAILURE(vrc))
2701 break;
2702
2703 switch (waitRes)
2704 {
2705 case ProcessWaitResult_StdIn:
2706 vrc = VERR_NOT_IMPLEMENTED;
2707 break;
2708
2709 case ProcessWaitResult_StdOut:
2710 fHandleStdOut = true;
2711 break;
2712
2713 case ProcessWaitResult_StdErr:
2714 fHandleStdErr = true;
2715 break;
2716
2717 case ProcessWaitResult_WaitFlagNotSupported:
2718 if (fProcWaitForFlags & ProcessWaitForFlag_StdOut)
2719 fHandleStdOut = true;
2720 if (fProcWaitForFlags & ProcessWaitForFlag_StdErr)
2721 fHandleStdErr = true;
2722 /* Since waiting for stdout / stderr is not supported by the guest,
2723 * wait a bit to not hog the CPU too much when polling for data. */
2724 RTThreadSleep(1); /* Optional, don't check vrc. */
2725 break;
2726
2727 case ProcessWaitResult_Error:
2728 vrc = VERR_GSTCTL_GUEST_ERROR;
2729 break;
2730
2731 case ProcessWaitResult_Terminate:
2732 fDone = true;
2733 break;
2734
2735 case ProcessWaitResult_Timeout:
2736 vrc = VERR_TIMEOUT;
2737 break;
2738
2739 case ProcessWaitResult_Start:
2740 case ProcessWaitResult_Status:
2741 /* Not used here, just skip. */
2742 break;
2743
2744 default:
2745 AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
2746 break;
2747 }
2748
2749 if (RT_FAILURE(vrc))
2750 break;
2751
2752 if (fHandleStdOut)
2753 {
2754 UPDATE_AND_CHECK_ELAPSED_TIME();
2755
2756 cbRead = 0;
2757 vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDOUT, sizeof(byBuf),
2758 GET_REMAINING_TIME,
2759 byBuf, sizeof(byBuf),
2760 &cbRead, &vrcGuest);
2761 if ( RT_FAILURE(vrc)
2762 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2763 break;
2764
2765 if (cbRead)
2766 {
2767 LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
2768 vrc = mStdOut.AddData(byBuf, cbRead);
2769
2770 if ( RT_SUCCESS(vrc)
2771 && (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK))
2772 {
2773 AssertPtr(pStrmBlkOut);
2774 vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
2775
2776 /* When successful, break out of the loop because we're done
2777 * with reading the first stream block. */
2778 if (RT_SUCCESS(vrc))
2779 fDone = true;
2780 }
2781 }
2782
2783 fHandleStdOut = false;
2784 }
2785
2786 if (fHandleStdErr)
2787 {
2788 UPDATE_AND_CHECK_ELAPSED_TIME();
2789
2790 cbRead = 0;
2791 vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDERR, sizeof(byBuf),
2792 GET_REMAINING_TIME,
2793 byBuf, sizeof(byBuf),
2794 &cbRead, &vrcGuest);
2795 if ( RT_FAILURE(vrc)
2796 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2797 break;
2798
2799 if (cbRead)
2800 {
2801 LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
2802 vrc = mStdErr.AddData(byBuf, cbRead);
2803 }
2804
2805 fHandleStdErr = false;
2806 }
2807
2808 } while (!fDone && RT_SUCCESS(vrc));
2809
2810#undef UPDATE_AND_CHECK_ELAPSED_TIME
2811#undef GET_REMAINING_TIME
2812
2813 if (RT_FAILURE(vrcGuest))
2814 vrc = VERR_GSTCTL_GUEST_ERROR;
2815
2816 LogFlowThisFunc(("Loop ended with vrc=%Rrc, vrcGuest=%Rrc, waitRes=%RU32\n", vrc, vrcGuest, waitRes));
2817 if (pvrcGuest)
2818 *pvrcGuest = vrcGuest;
2819
2820 LogFlowFuncLeaveRC(vrc);
2821 return vrc;
2822}
2823
2824/**
2825 * Terminates a guest process tool.
2826 *
2827 * @returns VBox status code.
2828 * @param uTimeoutMS Timeout (in ms) to wait.
2829 * @param pvrcGuest Where to return the guest error when
2830 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
2831 */
2832int GuestProcessTool::terminate(uint32_t uTimeoutMS, int *pvrcGuest)
2833{
2834 LogFlowThisFuncEnter();
2835
2836 int vrc;
2837 if (!pProcess.isNull())
2838 vrc = pProcess->i_terminateProcess(uTimeoutMS, pvrcGuest);
2839 else
2840 vrc = VERR_NOT_FOUND;
2841
2842 LogFlowFuncLeaveRC(vrc);
2843 return vrc;
2844}
2845
2846/**
2847 * Converts a toolbox tool's exit code to an IPRT error code.
2848 *
2849 * @returns VBox status code.
2850 * @param startupInfo Startup info of the toolbox tool to lookup error code for.
2851 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
2852 */
2853/* static */
2854int GuestProcessTool::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode)
2855{
2856 if (startupInfo.mArguments.size() == 0)
2857 {
2858 AssertFailed();
2859 return VERR_GENERAL_FAILURE; /* Should not happen. */
2860 }
2861
2862 return exitCodeToRc(startupInfo.mArguments[0].c_str(), iExitCode);
2863}
2864
2865/**
2866 * Converts a toolbox tool's exit code to an IPRT error code.
2867 *
2868 * @returns VBox status code.
2869 * @param pszTool Name of toolbox tool to lookup error code for.
2870 * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
2871 */
2872/* static */
2873int GuestProcessTool::exitCodeToRc(const char *pszTool, int32_t iExitCode)
2874{
2875 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
2876
2877 LogFlowFunc(("%s: %d\n", pszTool, iExitCode));
2878
2879 if (iExitCode == 0) /* No error? Bail out early. */
2880 return VINF_SUCCESS;
2881
2882 if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_CAT))
2883 {
2884 switch (iExitCode)
2885 {
2886 case VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2887 case VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2888 case VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2889 case VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION: return VERR_SHARING_VIOLATION;
2890 case VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY: return VERR_IS_A_DIRECTORY;
2891 default: break;
2892 }
2893 }
2894 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_LS))
2895 {
2896 switch (iExitCode)
2897 {
2898 /** @todo Handle access denied? */
2899 case RTEXITCODE_FAILURE: return VERR_PATH_NOT_FOUND;
2900 default: break;
2901 }
2902 }
2903 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_STAT))
2904 {
2905 switch (iExitCode)
2906 {
2907 case VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2908 case VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2909 case VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2910 case VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND: return VERR_NET_PATH_NOT_FOUND;
2911 default: break;
2912 }
2913 }
2914 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKDIR))
2915 {
2916 switch (iExitCode)
2917 {
2918 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2919 default: break;
2920 }
2921 }
2922 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKTEMP))
2923 {
2924 switch (iExitCode)
2925 {
2926 case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
2927 default: break;
2928 }
2929 }
2930 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_RM))
2931 {
2932 switch (iExitCode)
2933 {
2934 case RTEXITCODE_FAILURE: return VERR_FILE_NOT_FOUND;
2935 /** @todo RTPathRmCmd does not yet distinguish between not found and access denied yet. */
2936 default: break;
2937 }
2938 }
2939
2940 LogFunc(("Warning: Exit code %d not handled for tool '%s', returning VERR_GENERAL_FAILURE\n", iExitCode, pszTool));
2941
2942 if (iExitCode == RTEXITCODE_SYNTAX)
2943 return VERR_INTERNAL_ERROR_5;
2944 return VERR_GENERAL_FAILURE;
2945}
2946
2947/**
2948 * Returns a stringyfied error of a guest process tool error.
2949 *
2950 * @returns Stringyfied error.
2951 * @param pszTool Toolbox tool name to get stringyfied error for.
2952 * @param guestErrorInfo Guest error info to get stringyfied error for.
2953 */
2954/* static */
2955Utf8Str GuestProcessTool::guestErrorToString(const char *pszTool, const GuestErrorInfo &guestErrorInfo)
2956{
2957 Utf8Str strErr;
2958
2959 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
2960 switch (guestErrorInfo.getVrc())
2961 {
2962 case VERR_ACCESS_DENIED:
2963 strErr.printf(tr("Access to \"%s\" denied"), guestErrorInfo.getWhat().c_str());
2964 break;
2965
2966 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
2967 RT_FALL_THROUGH();
2968 case VERR_PATH_NOT_FOUND:
2969 strErr.printf(tr("No such file or directory \"%s\""), guestErrorInfo.getWhat().c_str());
2970 break;
2971
2972 case VERR_INVALID_VM_HANDLE:
2973 strErr.printf(tr("VMM device is not available (is the VM running?)"));
2974 break;
2975
2976 case VERR_HGCM_SERVICE_NOT_FOUND:
2977 strErr.printf(tr("The guest execution service is not available"));
2978 break;
2979
2980 case VERR_BAD_EXE_FORMAT:
2981 strErr.printf(tr("The file \"%s\" is not an executable format"), guestErrorInfo.getWhat().c_str());
2982 break;
2983
2984 case VERR_AUTHENTICATION_FAILURE:
2985 strErr.printf(tr("The user \"%s\" was not able to logon"), guestErrorInfo.getWhat().c_str());
2986 break;
2987
2988 case VERR_INVALID_NAME:
2989 strErr.printf(tr("The file \"%s\" is an invalid name"), guestErrorInfo.getWhat().c_str());
2990 break;
2991
2992 case VERR_TIMEOUT:
2993 strErr.printf(tr("The guest did not respond within time"));
2994 break;
2995
2996 case VERR_CANCELLED:
2997 strErr.printf(tr("The execution operation was canceled"));
2998 break;
2999
3000 case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
3001 strErr.printf(tr("Maximum number of concurrent guest processes has been reached"));
3002 break;
3003
3004 case VERR_NOT_FOUND:
3005 strErr.printf(tr("The guest execution service is not ready (yet)"));
3006 break;
3007
3008 default:
3009 strErr.printf(tr("Unhandled error %Rrc for \"%s\" occurred for tool \"%s\" on guest -- please file a bug report"),
3010 guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str(), pszTool);
3011 break;
3012 }
3013
3014 return strErr;
3015}
3016
Note: See TracBrowser for help on using the repository browser.

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