VirtualBox

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

Last change on this file since 85091 was 84865, checked in by vboxsync, 5 years ago

Guest Control/Main: Try to fix crashes. bugref:9320

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