VirtualBox

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

Last change on this file since 63186 was 63186, checked in by vboxsync, 9 years ago

ThreadTask: Cleaning up handler() methods.

  • 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 63186 2016-08-08 17:39:16Z 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 GuestProcess::i_startProcessThreadTask(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 */
1191void GuestProcess::i_startProcessThreadTask(GuestProcessStartTask *pTask)
1192{
1193 LogFlowFunc(("pTask=%p\n", pTask));
1194
1195 const ComObjPtr<GuestProcess> pProcess(pTask->i_process());
1196 Assert(!pProcess.isNull());
1197
1198 AutoCaller autoCaller(pProcess);
1199 if (FAILED(autoCaller.rc()))
1200 return;
1201
1202 int vrc = pProcess->i_startProcess(30 * 1000 /* 30s timeout */,
1203 NULL /* Guest rc, ignored */);
1204/** @todo
1205 *
1206 * r=bird: what's up with vrc here? Safe to ignore it?
1207 *
1208 */
1209
1210 /* Nothing to do here anymore. */
1211
1212 LogFlowFunc(("pProcess=%p vrc=%Rrc (ignored)\n", (GuestProcess *)pProcess, vrc));
1213 NOREF(vrc);
1214}
1215
1216int GuestProcess::i_terminateProcess(uint32_t uTimeoutMS, int *pGuestRc)
1217{
1218 /* pGuestRc is optional. */
1219 LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
1220
1221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 int vrc = VINF_SUCCESS;
1224
1225 if (mData.mStatus != ProcessStatus_Started)
1226 {
1227 LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
1228 mData.mStatus));
1229 }
1230 else
1231 {
1232 AssertPtr(mSession);
1233 /* Note: VBox < 4.3 (aka protocol version 1) does not
1234 * support this, so just skip. */
1235 if (mSession->i_getProtocolVersion() < 2)
1236 vrc = VERR_NOT_SUPPORTED;
1237
1238 if (RT_SUCCESS(vrc))
1239 {
1240 GuestWaitEvent *pEvent = NULL;
1241 GuestEventTypes eventTypes;
1242 try
1243 {
1244 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1245
1246 vrc = registerWaitEvent(eventTypes, &pEvent);
1247 }
1248 catch (std::bad_alloc)
1249 {
1250 vrc = VERR_NO_MEMORY;
1251 }
1252
1253 if (RT_FAILURE(vrc))
1254 return vrc;
1255
1256 VBOXHGCMSVCPARM paParms[4];
1257 int i = 0;
1258 paParms[i++].setUInt32(pEvent->ContextID());
1259 paParms[i++].setUInt32(mData.mPID);
1260
1261 alock.release(); /* Drop the write lock before sending. */
1262
1263 vrc = sendCommand(HOST_EXEC_TERMINATE, i, paParms);
1264 if (RT_SUCCESS(vrc))
1265 vrc = i_waitForStatusChange(pEvent, uTimeoutMS,
1266 NULL /* ProcessStatus */, pGuestRc);
1267 unregisterWaitEvent(pEvent);
1268 }
1269 }
1270
1271 LogFlowFuncLeaveRC(vrc);
1272 return vrc;
1273}
1274
1275/* static */
1276ProcessWaitResult_T GuestProcess::i_waitFlagsToResultEx(uint32_t fWaitFlags,
1277 ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
1278 uint32_t uProcFlags, uint32_t uProtocol)
1279{
1280 ProcessWaitResult_T waitResult = ProcessWaitResult_None;
1281
1282 switch (newStatus)
1283 {
1284 case ProcessStatus_TerminatedNormally:
1285 case ProcessStatus_TerminatedSignal:
1286 case ProcessStatus_TerminatedAbnormally:
1287 case ProcessStatus_Down:
1288 /* Nothing to wait for anymore. */
1289 waitResult = ProcessWaitResult_Terminate;
1290 break;
1291
1292 case ProcessStatus_TimedOutKilled:
1293 case ProcessStatus_TimedOutAbnormally:
1294 /* Dito. */
1295 waitResult = ProcessWaitResult_Timeout;
1296 break;
1297
1298 case ProcessStatus_Started:
1299 switch (oldStatus)
1300 {
1301 case ProcessStatus_Undefined:
1302 case ProcessStatus_Starting:
1303 /* Also wait for process start. */
1304 if (fWaitFlags & ProcessWaitForFlag_Start)
1305 waitResult = ProcessWaitResult_Start;
1306 else
1307 {
1308 /*
1309 * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
1310 * caller is not interested in getting further process statuses -- so just don't notify
1311 * anything here anymore and return.
1312 */
1313 if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1314 waitResult = ProcessWaitResult_Start;
1315 }
1316 break;
1317
1318 case ProcessStatus_Started:
1319 /* Only wait for process start. */
1320 if (fWaitFlags == ProcessWaitForFlag_Start)
1321 waitResult = ProcessWaitResult_Start;
1322 break;
1323
1324 default:
1325 AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
1326 oldStatus));
1327 waitResult = ProcessWaitResult_Start;
1328 break;
1329 }
1330 break;
1331
1332 case ProcessStatus_Error:
1333 /* Nothing to wait for anymore. */
1334 waitResult = ProcessWaitResult_Error;
1335 break;
1336
1337 case ProcessStatus_Undefined:
1338 case ProcessStatus_Starting:
1339 case ProcessStatus_Terminating:
1340 case ProcessStatus_Paused:
1341 /* No result available yet, leave wait
1342 * flags untouched. */
1343 break;
1344 }
1345
1346 if (newStatus == ProcessStatus_Started)
1347 {
1348 /**
1349 * Filter out waits which are *not* supported using
1350 * older guest control Guest Additions.
1351 *
1352 ** @todo ProcessWaitForFlag_Std* flags are not implemented yet.
1353 */
1354 if (uProtocol < 99) /* See @todo above. */
1355 {
1356 if ( waitResult == ProcessWaitResult_None
1357 /* We don't support waiting for stdin, out + err,
1358 * just skip waiting then. */
1359 && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
1360 || (fWaitFlags & ProcessWaitForFlag_StdOut)
1361 || (fWaitFlags & ProcessWaitForFlag_StdErr)
1362 )
1363 )
1364 {
1365 /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
1366 waitResult = ProcessWaitResult_WaitFlagNotSupported;
1367 }
1368 }
1369 }
1370
1371#ifdef DEBUG
1372 LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
1373 oldStatus, newStatus, fWaitFlags, waitResult));
1374#endif
1375 return waitResult;
1376}
1377
1378ProcessWaitResult_T GuestProcess::i_waitFlagsToResult(uint32_t fWaitFlags)
1379{
1380 AssertPtr(mSession);
1381 return GuestProcess::i_waitFlagsToResultEx(fWaitFlags,
1382 mData.mStatus /* curStatus */, mData.mStatus /* newStatus */,
1383 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1384}
1385
1386int GuestProcess::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS,
1387 ProcessWaitResult_T &waitResult, int *pGuestRc)
1388{
1389 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1390
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, procRc=%Rrc, pGuestRc=%p\n",
1394 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, pGuestRc));
1395
1396 /* Did some error occur before? Then skip waiting and return. */
1397 ProcessStatus_T curStatus = mData.mStatus;
1398 if (curStatus == ProcessStatus_Error)
1399 {
1400 waitResult = ProcessWaitResult_Error;
1401 AssertMsg(RT_FAILURE(mData.mLastError),
1402 ("No error rc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
1403 if (pGuestRc)
1404 *pGuestRc = mData.mLastError; /* Return last set error. */
1405 LogFlowThisFunc(("Process is in error state (guestRc=%Rrc)\n", mData.mLastError));
1406 return VERR_GSTCTL_GUEST_ERROR;
1407 }
1408
1409 waitResult = i_waitFlagsToResult(fWaitFlags);
1410
1411 /* No waiting needed? Return immediately using the last set error. */
1412 if (waitResult != ProcessWaitResult_None)
1413 {
1414 if (pGuestRc)
1415 *pGuestRc = mData.mLastError; /* Return last set error (if any). */
1416 LogFlowThisFunc(("Nothing to wait for (guestRc=%Rrc)\n", mData.mLastError));
1417 return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
1418 }
1419
1420 /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
1421 if (!uTimeoutMS)
1422 uTimeoutMS = RT_INDEFINITE_WAIT;
1423
1424 int vrc;
1425
1426 GuestWaitEvent *pEvent = NULL;
1427 GuestEventTypes eventTypes;
1428 try
1429 {
1430 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1431
1432 vrc = registerWaitEvent(eventTypes, &pEvent);
1433 }
1434 catch (std::bad_alloc)
1435 {
1436 vrc = VERR_NO_MEMORY;
1437 }
1438
1439 if (RT_FAILURE(vrc))
1440 return vrc;
1441
1442 alock.release(); /* Release lock before waiting. */
1443
1444 /*
1445 * Do the actual waiting.
1446 */
1447 ProcessStatus_T newStatus = ProcessStatus_Undefined;
1448 uint64_t u64StartMS = RTTimeMilliTS();
1449 for (;;)
1450 {
1451 uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
1452 if ( uTimeoutMS != RT_INDEFINITE_WAIT
1453 && u64ElapsedMS >= uTimeoutMS)
1454 {
1455 vrc = VERR_TIMEOUT;
1456 break;
1457 }
1458
1459 vrc = i_waitForStatusChange(pEvent,
1460 uTimeoutMS == RT_INDEFINITE_WAIT
1461 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
1462 &newStatus, pGuestRc);
1463 if (RT_SUCCESS(vrc))
1464 {
1465 alock.acquire();
1466
1467 waitResult = i_waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
1468 mData.mProcess.mFlags, mSession->i_getProtocolVersion());
1469#ifdef DEBUG
1470 LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
1471 fWaitFlags, newStatus, waitResult));
1472#endif
1473 if (ProcessWaitResult_None != waitResult) /* We got a waiting result. */
1474 break;
1475 }
1476 else /* Waiting failed, bail out. */
1477 break;
1478
1479 alock.release(); /* Don't hold lock in next waiting round. */
1480 }
1481
1482 unregisterWaitEvent(pEvent);
1483
1484 LogFlowThisFunc(("Returned waitResult=%RU32, newStatus=%RU32, rc=%Rrc\n",
1485 waitResult, newStatus, vrc));
1486 return vrc;
1487}
1488
1489int GuestProcess::i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1490 ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
1491{
1492 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1493
1494 VBoxEventType_T evtType;
1495 ComPtr<IEvent> pIEvent;
1496 int vrc = waitForEvent(pEvent, uTimeoutMS,
1497 &evtType, pIEvent.asOutParam());
1498 if (RT_SUCCESS(vrc))
1499 {
1500 if (evtType == VBoxEventType_OnGuestProcessInputNotify)
1501 {
1502 ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
1503 Assert(!pProcessEvent.isNull());
1504
1505 if (pInputStatus)
1506 {
1507 HRESULT hr2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
1508 ComAssertComRC(hr2);
1509 }
1510 if (pcbProcessed)
1511 {
1512 HRESULT hr2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
1513 ComAssertComRC(hr2);
1514 }
1515 }
1516 else
1517 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1518 }
1519
1520 LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, rc=%Rrc\n",
1521 pEvent, uHandle, vrc));
1522 return vrc;
1523}
1524
1525int GuestProcess::i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
1526 void *pvData, size_t cbData, uint32_t *pcbRead)
1527{
1528 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1529 /* pvData is optional. */
1530 /* cbData is optional. */
1531 /* pcbRead is optional. */
1532
1533 LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
1534 pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
1535
1536 int vrc;
1537
1538 VBoxEventType_T evtType;
1539 ComPtr<IEvent> pIEvent;
1540 do
1541 {
1542 vrc = waitForEvent(pEvent, uTimeoutMS,
1543 &evtType, pIEvent.asOutParam());
1544 if (RT_SUCCESS(vrc))
1545 {
1546 if (evtType == VBoxEventType_OnGuestProcessOutput)
1547 {
1548 ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
1549 Assert(!pProcessEvent.isNull());
1550
1551 ULONG uHandleEvent;
1552 HRESULT hr = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
1553 if ( SUCCEEDED(hr)
1554 && uHandleEvent == uHandle)
1555 {
1556 if (pvData)
1557 {
1558 com::SafeArray <BYTE> data;
1559 hr = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1560 ComAssertComRC(hr);
1561 size_t cbRead = data.size();
1562 if (cbRead)
1563 {
1564 if (cbRead <= cbData)
1565 {
1566 /* Copy data from event into our buffer. */
1567 memcpy(pvData, data.raw(), data.size());
1568 }
1569 else
1570 vrc = VERR_BUFFER_OVERFLOW;
1571
1572 LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), rc=%Rrc\n",
1573 cbRead, uHandleEvent, vrc));
1574 }
1575 }
1576
1577 if ( RT_SUCCESS(vrc)
1578 && pcbRead)
1579 {
1580 ULONG cbRead;
1581 hr = pProcessEvent->COMGETTER(Processed)(&cbRead);
1582 ComAssertComRC(hr);
1583 *pcbRead = (uint32_t)cbRead;
1584 }
1585
1586 break;
1587 }
1588 else if (FAILED(hr))
1589 vrc = VERR_COM_UNEXPECTED;
1590 }
1591 else
1592 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1593 }
1594
1595 } while (vrc == VINF_SUCCESS);
1596
1597 if ( vrc != VINF_SUCCESS
1598 && pcbRead)
1599 {
1600 *pcbRead = 0;
1601 }
1602
1603 LogFlowFuncLeaveRC(vrc);
1604 return vrc;
1605}
1606
1607int GuestProcess::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1608 ProcessStatus_T *pProcessStatus, int *pGuestRc)
1609{
1610 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1611 /* pProcessStatus is optional. */
1612 /* pGuestRc is optional. */
1613
1614 VBoxEventType_T evtType;
1615 ComPtr<IEvent> pIEvent;
1616 int vrc = waitForEvent(pEvent, uTimeoutMS,
1617 &evtType, pIEvent.asOutParam());
1618 if (RT_SUCCESS(vrc))
1619 {
1620 Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
1621 ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
1622 Assert(!pProcessEvent.isNull());
1623
1624 ProcessStatus_T procStatus;
1625 HRESULT hr = pProcessEvent->COMGETTER(Status)(&procStatus);
1626 ComAssertComRC(hr);
1627 if (pProcessStatus)
1628 *pProcessStatus = procStatus;
1629
1630 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1631 hr = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
1632 ComAssertComRC(hr);
1633
1634 LONG lGuestRc;
1635 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1636 ComAssertComRC(hr);
1637
1638 LogFlowThisFunc(("Got procStatus=%RU32, guestRc=%RI32 (%Rrc)\n",
1639 procStatus, lGuestRc, lGuestRc));
1640
1641 if (RT_FAILURE((int)lGuestRc))
1642 vrc = VERR_GSTCTL_GUEST_ERROR;
1643
1644 if (pGuestRc)
1645 *pGuestRc = (int)lGuestRc;
1646 }
1647
1648 LogFlowFuncLeaveRC(vrc);
1649 return vrc;
1650}
1651
1652/* static */
1653bool GuestProcess::i_waitResultImpliesEx(ProcessWaitResult_T waitResult,
1654 ProcessStatus_T procStatus, uint32_t uProcFlags,
1655 uint32_t uProtocol)
1656{
1657 bool fImplies;
1658
1659 switch (waitResult)
1660 {
1661 case ProcessWaitResult_Start:
1662 fImplies = procStatus == ProcessStatus_Started;
1663 break;
1664
1665 case ProcessWaitResult_Terminate:
1666 fImplies = ( procStatus == ProcessStatus_TerminatedNormally
1667 || procStatus == ProcessStatus_TerminatedSignal
1668 || procStatus == ProcessStatus_TerminatedAbnormally
1669 || procStatus == ProcessStatus_TimedOutKilled
1670 || procStatus == ProcessStatus_TimedOutAbnormally
1671 || procStatus == ProcessStatus_Down
1672 || procStatus == ProcessStatus_Error);
1673 break;
1674
1675 default:
1676 fImplies = false;
1677 break;
1678 }
1679
1680 return fImplies;
1681}
1682
1683int GuestProcess::i_writeData(uint32_t uHandle, uint32_t uFlags,
1684 void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pGuestRc)
1685{
1686 LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p, pGuestRc=%p\n",
1687 mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten, pGuestRc));
1688 /* All is optional. There can be 0 byte writes. */
1689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1690
1691 if (mData.mStatus != ProcessStatus_Started)
1692 {
1693 if (puWritten)
1694 *puWritten = 0;
1695 if (pGuestRc)
1696 *pGuestRc = VINF_SUCCESS;
1697 return VINF_SUCCESS; /* Not available for writing (anymore). */
1698 }
1699
1700 int vrc;
1701
1702 GuestWaitEvent *pEvent = NULL;
1703 GuestEventTypes eventTypes;
1704 try
1705 {
1706 /*
1707 * On Guest Additions < 4.3 there is no guarantee that the process status
1708 * change arrives *after* the input event, e.g. if this was the last input
1709 * block being written and the process will report status "terminate".
1710 * So just skip checking for process status change and only wait for the
1711 * input event.
1712 */
1713 if (mSession->i_getProtocolVersion() >= 2)
1714 eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
1715 eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
1716
1717 vrc = registerWaitEvent(eventTypes, &pEvent);
1718 }
1719 catch (std::bad_alloc)
1720 {
1721 vrc = VERR_NO_MEMORY;
1722 }
1723
1724 if (RT_FAILURE(vrc))
1725 return vrc;
1726
1727 VBOXHGCMSVCPARM paParms[5];
1728 int i = 0;
1729 paParms[i++].setUInt32(pEvent->ContextID());
1730 paParms[i++].setUInt32(mData.mPID);
1731 paParms[i++].setUInt32(uFlags);
1732 paParms[i++].setPointer(pvData, (uint32_t)cbData);
1733 paParms[i++].setUInt32((uint32_t)cbData);
1734
1735 alock.release(); /* Drop the write lock before sending. */
1736
1737 uint32_t cbProcessed = 0;
1738 vrc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
1739 if (RT_SUCCESS(vrc))
1740 {
1741 ProcessInputStatus_T inputStatus;
1742 vrc = i_waitForInputNotify(pEvent, uHandle, uTimeoutMS,
1743 &inputStatus, &cbProcessed);
1744 if (RT_SUCCESS(vrc))
1745 {
1746 /** @todo Set guestRc. */
1747
1748 if (puWritten)
1749 *puWritten = cbProcessed;
1750 }
1751 /** @todo Error handling. */
1752 }
1753
1754 unregisterWaitEvent(pEvent);
1755
1756 LogFlowThisFunc(("Returning cbProcessed=%RU32, rc=%Rrc\n",
1757 cbProcessed, vrc));
1758 return vrc;
1759}
1760
1761// implementation of public methods
1762/////////////////////////////////////////////////////////////////////////////
1763
1764HRESULT GuestProcess::read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
1765{
1766 LogFlowThisFuncEnter();
1767
1768 if (aToRead == 0)
1769 return setError(E_INVALIDARG, tr("The size to read is zero"));
1770
1771 aData.resize(aToRead);
1772
1773 HRESULT hr = S_OK;
1774
1775 uint32_t cbRead; int guestRc;
1776 int vrc = i_readData(aHandle, aToRead, aTimeoutMS, &aData.front(), aToRead, &cbRead, &guestRc);
1777 if (RT_SUCCESS(vrc))
1778 {
1779 if (aData.size() != cbRead)
1780 aData.resize(cbRead);
1781 }
1782 else
1783 {
1784 aData.resize(0);
1785
1786 switch (vrc)
1787 {
1788 case VERR_GSTCTL_GUEST_ERROR:
1789 hr = GuestProcess::i_setErrorExternal(this, guestRc);
1790 break;
1791
1792 default:
1793 hr = setError(VBOX_E_IPRT_ERROR,
1794 tr("Reading from process \"%s\" (PID %RU32) failed: %Rrc"),
1795 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
1796 break;
1797 }
1798 }
1799
1800 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
1801
1802 LogFlowFuncLeaveRC(vrc);
1803 return hr;
1804}
1805
1806HRESULT GuestProcess::terminate()
1807{
1808 HRESULT hr = S_OK;
1809
1810 int guestRc;
1811 int vrc = i_terminateProcess(30 * 1000 /* Timeout in ms */,
1812 &guestRc);
1813 if (RT_FAILURE(vrc))
1814 {
1815 switch (vrc)
1816 {
1817 case VERR_GSTCTL_GUEST_ERROR:
1818 hr = GuestProcess::i_setErrorExternal(this, guestRc);
1819 break;
1820
1821 case VERR_NOT_SUPPORTED:
1822 hr = setError(VBOX_E_IPRT_ERROR,
1823 tr("Terminating process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
1824 mData.mProcess.mExecutable.c_str(), mData.mPID);
1825 break;
1826
1827 default:
1828 hr = setError(VBOX_E_IPRT_ERROR,
1829 tr("Terminating process \"%s\" (PID %RU32) failed: %Rrc"),
1830 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
1831 break;
1832 }
1833 }
1834
1835 /* Remove process from guest session list. Now only API clients
1836 * still can hold references to it. */
1837 AssertPtr(mSession);
1838 int rc2 = mSession->i_processRemoveFromList(this);
1839 if (RT_SUCCESS(vrc))
1840 vrc = rc2;
1841
1842 LogFlowFuncLeaveRC(vrc);
1843 return hr;
1844}
1845
1846HRESULT GuestProcess::waitFor(ULONG aWaitFor,
1847 ULONG aTimeoutMS,
1848 ProcessWaitResult_T *aReason)
1849{
1850 /*
1851 * Note: Do not hold any locks here while waiting!
1852 */
1853 HRESULT hr = S_OK;
1854
1855 int guestRc;
1856 ProcessWaitResult_T waitResult;
1857 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &guestRc);
1858 if (RT_SUCCESS(vrc))
1859 {
1860 *aReason = waitResult;
1861 }
1862 else
1863 {
1864 switch (vrc)
1865 {
1866 case VERR_GSTCTL_GUEST_ERROR:
1867 hr = GuestProcess::i_setErrorExternal(this, guestRc);
1868 break;
1869
1870 case VERR_TIMEOUT:
1871 *aReason = ProcessWaitResult_Timeout;
1872 break;
1873
1874 default:
1875 hr = setError(VBOX_E_IPRT_ERROR,
1876 tr("Waiting for process \"%s\" (PID %RU32) failed: %Rrc"),
1877 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
1878 break;
1879 }
1880 }
1881
1882 LogFlowFuncLeaveRC(vrc);
1883 return hr;
1884}
1885
1886HRESULT GuestProcess::waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
1887 ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
1888{
1889 /*
1890 * Note: Do not hold any locks here while waiting!
1891 */
1892 uint32_t fWaitFor = ProcessWaitForFlag_None;
1893 for (size_t i = 0; i < aWaitFor.size(); i++)
1894 fWaitFor |= aWaitFor[i];
1895
1896 return WaitFor(fWaitFor, aTimeoutMS, aReason);
1897}
1898
1899HRESULT GuestProcess::write(ULONG aHandle, ULONG aFlags, const std::vector<BYTE> &aData,
1900 ULONG aTimeoutMS, ULONG *aWritten)
1901{
1902 LogFlowThisFuncEnter();
1903
1904 HRESULT hr = S_OK;
1905
1906 uint32_t cbWritten; int guestRc;
1907 uint32_t cbData = (uint32_t)aData.size();
1908 void *pvData = cbData > 0? (void *)&aData.front(): NULL;
1909 int vrc = i_writeData(aHandle, aFlags, pvData, cbData, aTimeoutMS, &cbWritten, &guestRc);
1910 if (RT_FAILURE(vrc))
1911 {
1912 switch (vrc)
1913 {
1914 case VERR_GSTCTL_GUEST_ERROR:
1915 hr = GuestProcess::i_setErrorExternal(this, guestRc);
1916 break;
1917
1918 default:
1919 hr = setError(VBOX_E_IPRT_ERROR,
1920 tr("Writing to process \"%s\" (PID %RU32) failed: %Rrc"),
1921 mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
1922 break;
1923 }
1924 }
1925
1926 LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
1927
1928 *aWritten = (ULONG)cbWritten;
1929
1930 LogFlowFuncLeaveRC(vrc);
1931 return hr;
1932}
1933
1934HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFlag_T> &aFlags,
1935 const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
1936{
1937 LogFlowThisFuncEnter();
1938
1939 /*
1940 * Note: Do not hold any locks here while writing!
1941 */
1942 ULONG fWrite = ProcessInputFlag_None;
1943 for (size_t i = 0; i < aFlags.size(); i++)
1944 fWrite |= aFlags[i];
1945
1946 return write(aHandle, fWrite, aData, aTimeoutMS, aWritten);
1947}
1948
1949///////////////////////////////////////////////////////////////////////////////
1950
1951GuestProcessTool::GuestProcessTool(void)
1952 : pSession(NULL),
1953 pProcess(NULL)
1954{
1955}
1956
1957GuestProcessTool::~GuestProcessTool(void)
1958{
1959 i_terminate(30 * 1000, NULL /* pGuestRc */);
1960}
1961
1962int GuestProcessTool::Init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
1963 bool fAsync, int *pGuestRc)
1964{
1965 LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n",
1966 pGuestSession, startupInfo.mExecutable.c_str(), fAsync));
1967
1968 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
1969 Assert(startupInfo.mArguments[0] == startupInfo.mExecutable);
1970
1971 pSession = pGuestSession;
1972 mStartupInfo = startupInfo;
1973
1974 /* Make sure the process is hidden. */
1975 mStartupInfo.mFlags |= ProcessCreateFlag_Hidden;
1976
1977 int vrc = pSession->i_processCreateExInternal(mStartupInfo, pProcess);
1978 if (RT_SUCCESS(vrc))
1979 {
1980 int vrcGuest = VINF_SUCCESS;
1981 vrc = fAsync
1982 ? pProcess->i_startProcessAsync()
1983 : pProcess->i_startProcess(30 * 1000 /* 30s timeout */, &vrcGuest);
1984
1985 if ( RT_SUCCESS(vrc)
1986 && !fAsync
1987 && RT_FAILURE(vrcGuest)
1988 )
1989 {
1990 if (pGuestRc)
1991 *pGuestRc = vrcGuest;
1992 vrc = VERR_GSTCTL_GUEST_ERROR;
1993 }
1994 }
1995
1996 LogFlowFuncLeaveRC(vrc);
1997 return vrc;
1998}
1999
2000int GuestProcessTool::i_getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock)
2001{
2002 const GuestProcessStream *pStream = NULL;
2003 if (uHandle == OUTPUT_HANDLE_ID_STDOUT)
2004 pStream = &mStdOut;
2005 else if (uHandle == OUTPUT_HANDLE_ID_STDERR)
2006 pStream = &mStdErr;
2007
2008 if (!pStream)
2009 return VERR_INVALID_PARAMETER;
2010
2011 int vrc;
2012 do
2013 {
2014 /* Try parsing the data to see if the current block is complete. */
2015 vrc = mStdOut.ParseBlock(strmBlock);
2016 if (strmBlock.GetCount())
2017 break;
2018 } while (RT_SUCCESS(vrc));
2019
2020 LogFlowThisFunc(("rc=%Rrc, %RU64 pairs\n",
2021 vrc, strmBlock.GetCount()));
2022 return vrc;
2023}
2024
2025int GuestProcessTool::i_getRc(void) const
2026{
2027 LONG exitCode;
2028 HRESULT hr = pProcess->COMGETTER(ExitCode(&exitCode));
2029 Assert(SUCCEEDED(hr));
2030
2031 return GuestProcessTool::i_exitCodeToRc(mStartupInfo, exitCode);
2032}
2033
2034bool GuestProcessTool::i_isRunning(void)
2035{
2036 AssertReturn(!pProcess.isNull(), false);
2037
2038 ProcessStatus_T procStatus = ProcessStatus_Undefined;
2039 HRESULT hr = pProcess->COMGETTER(Status(&procStatus));
2040 Assert(SUCCEEDED(hr));
2041
2042 if ( procStatus == ProcessStatus_Started
2043 || procStatus == ProcessStatus_Paused
2044 || procStatus == ProcessStatus_Terminating)
2045 {
2046 return true;
2047 }
2048
2049 return false;
2050}
2051
2052/* static */
2053int GuestProcessTool::i_run( GuestSession *pGuestSession,
2054 const GuestProcessStartupInfo &startupInfo,
2055 int *pGuestRc /* = NULL */)
2056{
2057 int guestRc;
2058
2059 GuestProcessToolErrorInfo errorInfo;
2060 int vrc = i_runErrorInfo(pGuestSession, startupInfo, errorInfo);
2061 if (RT_SUCCESS(vrc))
2062 {
2063 if (errorInfo.guestRc == VWRN_GSTCTL_PROCESS_EXIT_CODE)
2064 {
2065 guestRc = GuestProcessTool::i_exitCodeToRc(startupInfo, errorInfo.lExitCode);
2066 }
2067 else
2068 guestRc = errorInfo.guestRc;
2069
2070 if (pGuestRc)
2071 *pGuestRc = guestRc;
2072 }
2073
2074 return vrc;
2075}
2076
2077/**
2078 * Static helper function to start and wait for a certain toolbox tool.
2079 *
2080 * @return IPRT status code.
2081 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2082 * @param startupInfo Startup information about the toolbox tool.
2083 * @param errorInfo Error information returned for error handling.
2084 */
2085/* static */
2086int GuestProcessTool::i_runErrorInfo( GuestSession *pGuestSession,
2087 const GuestProcessStartupInfo &startupInfo,
2088 GuestProcessToolErrorInfo &errorInfo)
2089{
2090 return i_runExErrorInfo(pGuestSession, startupInfo,
2091 NULL /* paStrmOutObjects */, 0 /* cStrmOutObjects */,
2092 errorInfo);
2093}
2094
2095/**
2096 * Static helper function to start and wait for output of a certain toolbox tool.
2097 *
2098 * @return IPRT status code.
2099 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2100 * @param startupInfo Startup information about the toolbox tool.
2101 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2102 * Optional.
2103 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2104 * @param pGuestRc Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned. Optional.
2105 */
2106/* static */
2107int GuestProcessTool::i_runEx( GuestSession *pGuestSession,
2108 const GuestProcessStartupInfo &startupInfo,
2109 GuestCtrlStreamObjects *paStrmOutObjects,
2110 uint32_t cStrmOutObjects,
2111 int *pGuestRc /* = NULL */)
2112{
2113 int guestRc;
2114
2115 GuestProcessToolErrorInfo errorInfo;
2116 int vrc = GuestProcessTool::i_runExErrorInfo(pGuestSession, startupInfo, paStrmOutObjects, cStrmOutObjects, errorInfo);
2117 if (RT_SUCCESS(vrc))
2118 {
2119 if (errorInfo.guestRc == VWRN_GSTCTL_PROCESS_EXIT_CODE)
2120 {
2121 guestRc = GuestProcessTool::i_exitCodeToRc(startupInfo, errorInfo.lExitCode);
2122 }
2123 else
2124 guestRc = errorInfo.guestRc;
2125
2126 /* Return VERR_GSTCTL_GUEST_ERROR if we retrieved a guest return code. */
2127 if (RT_FAILURE(guestRc))
2128 vrc = VERR_GSTCTL_GUEST_ERROR;
2129
2130 if (pGuestRc)
2131 *pGuestRc = guestRc;
2132 }
2133
2134 return vrc;
2135}
2136
2137/**
2138 * Static helper function to start and wait for output of a certain toolbox tool.
2139 *
2140 * This is the extended version, which addds the possibility of retrieving parsable so-called guest stream
2141 * objects. Those objects are issued on the guest side as part of VBoxService's toolbox tools (think of a BusyBox-like approach)
2142 * on stdout and can be used on the host side to retrieve more information about the actual command issued on the guest side.
2143 *
2144 * @return IPRT status code.
2145 * @param pGuestSession Guest control session to use for starting the toolbox tool in.
2146 * @param startupInfo Startup information about the toolbox tool.
2147 * @param paStrmOutObjects Pointer to stream objects array to use for retrieving the output of the toolbox tool.
2148 * Optional.
2149 * @param cStrmOutObjects Number of stream objects passed in. Optional.
2150 * @param errorInfo Error information returned for error handling.
2151 */
2152/* static */
2153int GuestProcessTool::i_runExErrorInfo( GuestSession *pGuestSession,
2154 const GuestProcessStartupInfo &startupInfo,
2155 GuestCtrlStreamObjects *paStrmOutObjects,
2156 uint32_t cStrmOutObjects,
2157 GuestProcessToolErrorInfo &errorInfo)
2158{
2159 AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
2160 /* paStrmOutObjects is optional. */
2161
2162 /** @todo Check if this is a valid toolbox. */
2163
2164 GuestProcessTool procTool;
2165 int vrc = procTool.Init(pGuestSession, startupInfo, false /* Async */, &errorInfo.guestRc);
2166 if (RT_SUCCESS(vrc))
2167 {
2168 while (cStrmOutObjects--)
2169 {
2170 try
2171 {
2172 GuestProcessStreamBlock strmBlk;
2173 vrc = procTool.i_waitEx( paStrmOutObjects
2174 ? GUESTPROCESSTOOL_FLAG_STDOUT_BLOCK
2175 : GUESTPROCESSTOOL_FLAG_NONE, &strmBlk, &errorInfo.guestRc);
2176 if (paStrmOutObjects)
2177 paStrmOutObjects->push_back(strmBlk);
2178 }
2179 catch (std::bad_alloc)
2180 {
2181 vrc = VERR_NO_MEMORY;
2182 }
2183 }
2184 }
2185
2186 if (RT_SUCCESS(vrc))
2187 {
2188 /* Make sure the process runs until completion. */
2189 vrc = procTool.i_wait(GUESTPROCESSTOOL_FLAG_NONE, &errorInfo.guestRc);
2190 if (RT_SUCCESS(vrc))
2191 errorInfo.guestRc = procTool.i_terminatedOk(&errorInfo.lExitCode);
2192 }
2193
2194 LogFlowFunc(("Returned rc=%Rrc, guestRc=%Rrc, exitCode=%ld\n", vrc, errorInfo.guestRc, errorInfo.lExitCode));
2195 return vrc;
2196}
2197
2198/**
2199 * Reports if the tool has been run correctly.
2200 *
2201 * @return Will return VWRN_GSTCTL_PROCESS_EXIT_CODE if the tool process returned an exit code <> 0,
2202 * VERR_GSTCTL_PROCESS_WRONG_STATE if the tool process is in a wrong state (e.g. still running),
2203 * or VINF_SUCCESS otherwise.
2204 *
2205 * @param plExitCode Exit code of the tool. Optional.
2206 */
2207int GuestProcessTool::i_terminatedOk(LONG *plExitCode /* = NULL */)
2208{
2209 Assert(!pProcess.isNull());
2210 /* pExitCode is optional. */
2211
2212 int vrc;
2213 if (!i_isRunning())
2214 {
2215 LONG lExitCode;
2216 HRESULT hr = pProcess->COMGETTER(ExitCode(&lExitCode));
2217 Assert(SUCCEEDED(hr));
2218
2219 if (plExitCode)
2220 *plExitCode = lExitCode;
2221
2222 vrc = (lExitCode != 0)
2223 ? VWRN_GSTCTL_PROCESS_EXIT_CODE : VINF_SUCCESS;
2224 }
2225 else
2226 vrc = VERR_GSTCTL_PROCESS_WRONG_STATE;
2227
2228 LogFlowFuncLeaveRC(vrc);
2229 return vrc;
2230}
2231
2232int GuestProcessTool::i_wait(uint32_t fFlags, int *pGuestRc)
2233{
2234 return i_waitEx(fFlags, NULL /* pStrmBlkOut */, pGuestRc);
2235}
2236
2237int GuestProcessTool::i_waitEx(uint32_t fFlags, GuestProcessStreamBlock *pStrmBlkOut, int *pGuestRc)
2238{
2239 LogFlowThisFunc(("fFlags=0x%x, pStreamBlock=%p, pGuestRc=%p\n",
2240 fFlags, pStrmBlkOut, pGuestRc));
2241
2242 /* Can we parse the next block without waiting? */
2243 int vrc;
2244 if (fFlags & GUESTPROCESSTOOL_FLAG_STDOUT_BLOCK)
2245 {
2246 AssertPtr(pStrmBlkOut);
2247 vrc = i_getCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStrmBlkOut);
2248 if (RT_SUCCESS(vrc))
2249 return vrc;
2250 /* else do the the waiting below. */
2251 }
2252
2253 /* Do the waiting. */
2254 uint32_t fWaitFlags = ProcessWaitForFlag_Terminate;
2255 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2256 fWaitFlags |= ProcessWaitForFlag_StdOut;
2257 if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2258 fWaitFlags |= ProcessWaitForFlag_StdErr;
2259
2260 /** @todo Decrease timeout while running. */
2261 uint64_t u64StartMS = RTTimeMilliTS();
2262 uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
2263
2264 int vrcGuest = VINF_SUCCESS;
2265 bool fDone = false;
2266
2267 BYTE byBuf[_64K];
2268 uint32_t cbRead;
2269
2270 bool fHandleStdOut = false;
2271 bool fHandleStdErr = false;
2272
2273 /**
2274 * Updates the elapsed time and checks if a
2275 * timeout happened, then breaking out of the loop.
2276 */
2277#define UPDATE_AND_CHECK_ELAPSED_TIME() \
2278 u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
2279 if ( uTimeoutMS != RT_INDEFINITE_WAIT \
2280 && u64ElapsedMS >= uTimeoutMS) \
2281 { \
2282 vrc = VERR_TIMEOUT; \
2283 break; \
2284 }
2285
2286 /**
2287 * Returns the remaining time (in ms).
2288 */
2289#define GET_REMAINING_TIME \
2290 uTimeoutMS == RT_INDEFINITE_WAIT \
2291 ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS \
2292
2293 ProcessWaitResult_T waitRes = ProcessWaitResult_None;
2294 do
2295 {
2296 uint64_t u64ElapsedMS;
2297 UPDATE_AND_CHECK_ELAPSED_TIME();
2298
2299 vrc = pProcess->i_waitFor(fWaitFlags, GET_REMAINING_TIME,
2300 waitRes, &vrcGuest);
2301 if (RT_FAILURE(vrc))
2302 break;
2303
2304 switch (waitRes)
2305 {
2306 case ProcessWaitResult_StdIn:
2307 vrc = VERR_NOT_IMPLEMENTED;
2308 break;
2309
2310 case ProcessWaitResult_StdOut:
2311 fHandleStdOut = true;
2312 break;
2313
2314 case ProcessWaitResult_StdErr:
2315 fHandleStdErr = true;
2316 break;
2317
2318 case ProcessWaitResult_WaitFlagNotSupported:
2319 if (fWaitFlags & ProcessWaitForFlag_StdOut)
2320 fHandleStdOut = true;
2321 if (fWaitFlags & ProcessWaitForFlag_StdErr)
2322 fHandleStdErr = true;
2323 /* Since waiting for stdout / stderr is not supported by the guest,
2324 * wait a bit to not hog the CPU too much when polling for data. */
2325 RTThreadSleep(1); /* Optional, don't check rc. */
2326 break;
2327
2328 case ProcessWaitResult_Error:
2329 vrc = VERR_GSTCTL_GUEST_ERROR;
2330 break;
2331
2332 case ProcessWaitResult_Terminate:
2333 fDone = true;
2334 break;
2335
2336 case ProcessWaitResult_Timeout:
2337 vrc = VERR_TIMEOUT;
2338 break;
2339
2340 case ProcessWaitResult_Start:
2341 case ProcessWaitResult_Status:
2342 /* Not used here, just skip. */
2343 break;
2344
2345 default:
2346 AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
2347 break;
2348 }
2349
2350 if (RT_FAILURE(vrc))
2351 break;
2352
2353 if (fHandleStdOut)
2354 {
2355 UPDATE_AND_CHECK_ELAPSED_TIME();
2356
2357 cbRead = 0;
2358 vrc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
2359 GET_REMAINING_TIME,
2360 byBuf, sizeof(byBuf),
2361 &cbRead, &vrcGuest);
2362 if ( RT_FAILURE(vrc)
2363 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2364 break;
2365
2366 if (cbRead)
2367 {
2368 LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
2369 vrc = mStdOut.AddData(byBuf, cbRead);
2370
2371 if ( RT_SUCCESS(vrc)
2372 && (fFlags & GUESTPROCESSTOOL_FLAG_STDOUT_BLOCK))
2373 {
2374 AssertPtr(pStrmBlkOut);
2375 vrc = i_getCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStrmBlkOut);
2376
2377 /* When successful, break out of the loop because we're done
2378 * with reading the first stream block. */
2379 if (RT_SUCCESS(vrc))
2380 fDone = true;
2381 }
2382 }
2383
2384 fHandleStdOut = false;
2385 }
2386
2387 if (fHandleStdErr)
2388 {
2389 UPDATE_AND_CHECK_ELAPSED_TIME();
2390
2391 cbRead = 0;
2392 vrc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDERR, sizeof(byBuf),
2393 GET_REMAINING_TIME,
2394 byBuf, sizeof(byBuf),
2395 &cbRead, &vrcGuest);
2396 if ( RT_FAILURE(vrc)
2397 || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
2398 break;
2399
2400 if (cbRead)
2401 {
2402 LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
2403 vrc = mStdErr.AddData(byBuf, cbRead);
2404 }
2405
2406 fHandleStdErr = false;
2407 }
2408
2409 } while (!fDone && RT_SUCCESS(vrc));
2410
2411#undef UPDATE_AND_CHECK_ELAPSED_TIME
2412#undef GET_REMAINING_TIME
2413
2414 if (RT_FAILURE(vrcGuest))
2415 vrc = VERR_GSTCTL_GUEST_ERROR;
2416
2417 LogFlowThisFunc(("Loop ended with rc=%Rrc, vrcGuest=%Rrc, waitRes=%RU32\n",
2418 vrc, vrcGuest, waitRes));
2419 if (pGuestRc)
2420 *pGuestRc = vrcGuest;
2421
2422 LogFlowFuncLeaveRC(vrc);
2423 return vrc;
2424}
2425
2426int GuestProcessTool::i_terminate(uint32_t uTimeoutMS, int *pGuestRc)
2427{
2428 LogFlowThisFuncEnter();
2429
2430 int rc = VINF_SUCCESS;
2431 if (!pProcess.isNull())
2432 {
2433 rc = pProcess->i_terminateProcess(uTimeoutMS, pGuestRc);
2434 pProcess.setNull();
2435 }
2436 else
2437 rc = VERR_NOT_FOUND;
2438
2439 LogFlowFuncLeaveRC(rc);
2440 return rc;
2441}
2442
2443/**
2444 * Converts a toolbox tool's exit code to an IPRT error code.
2445 *
2446 * @return int Returned IPRT error for the particular tool.
2447 * @param startupInfo Startup info of the toolbox tool to lookup error code for.
2448 * @param lExitCode The toolbox tool's exit code to lookup IPRT error for.
2449 */
2450/* static */
2451int GuestProcessTool::i_exitCodeToRc(const GuestProcessStartupInfo &startupInfo, LONG lExitCode)
2452{
2453 if (startupInfo.mArguments.size() == 0)
2454 {
2455 AssertFailed();
2456 return VERR_GENERAL_FAILURE; /* Should not happen. */
2457 }
2458
2459 return i_exitCodeToRc(startupInfo.mArguments[0].c_str(), lExitCode);
2460}
2461
2462/**
2463 * Converts a toolbox tool's exit code to an IPRT error code.
2464 *
2465 * @return int Returned IPRT error for the particular tool.
2466 * @param pszTool Name of toolbox tool to lookup error code for.
2467 * @param rcExit The toolbox tool's exit code to lookup IPRT error for.
2468 */
2469/* static */
2470int GuestProcessTool::i_exitCodeToRc(const char *pszTool, LONG lExitCode)
2471{
2472 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
2473
2474 LogFlowFunc(("%s: %ld\n", pszTool, lExitCode));
2475
2476 if (lExitCode == 0) /* No error? Bail out early. */
2477 return VINF_SUCCESS;
2478
2479 if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_CAT))
2480 {
2481 switch (lExitCode)
2482 {
2483 case VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2484 case VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2485 case VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2486 case VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION: return VERR_SHARING_VIOLATION;
2487 default:
2488 break;
2489 }
2490 }
2491 else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_STAT))
2492 {
2493 switch (lExitCode)
2494 {
2495 case VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
2496 case VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
2497 case VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
2498 default:
2499 break;
2500 }
2501 }
2502
2503 AssertMsgFailed(("Error code %ld for tool '%s' not handled\n", lExitCode, pszTool));
2504 return VERR_GENERAL_FAILURE;
2505}
2506
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