VirtualBox

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

Last change on this file since 92707 was 92707, checked in by vboxsync, 3 years ago

Guest Control: Resolved a @todo: A bit more scoping for some defines, even if they're part of the guestControl namespace.

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

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