VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use