VirtualBox

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

Last change on this file since 98706 was 98666, checked in by vboxsync, 22 months ago

Guest Control: Streamlined GuestWaitEventBase / GuestWaitEvent interfaces and use getters for retrieving guest side errors where applicable. bugref:9783

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

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