VirtualBox

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

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

Guest Control/Main + VBoxService: Resolved another @todo: Added ability for guest processes to use argv[0] independently of the actual execution command (follow-up to r136622). bugref:9320

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

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