VirtualBox

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

Last change on this file since 63165 was 63155, checked in by vboxsync, 8 years ago

Main: More warnings about uninitialized variables.

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