VirtualBox

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

Last change on this file since 99392 was 99392, checked in by vboxsync, 14 months ago

Guest Control: Completely revamped / overhauled the (now legacy) stream output parsing code and added lots of documentation to it. This way it should be a lot clearer what it's supposed to be doing.

Also, this now should fix some nasty bugs in that area we had in the past especially with some Linux guests (i.e. OL6), which sometimes send output data in a very unsteady manner.

Also overhauled the testcases while at it.

Luckily, this is all host-based code, so older Guest Additions also will benefit from this.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use