VirtualBox

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

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

Guest Control: Implemented support for long(er) command lines [build fix]. ​bugref:9320

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