VirtualBox

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

Last change on this file since 63147 was 62157, checked in by vboxsync, 8 years ago

*: Instead of suppressing -Wno-delete-non-virtual-dtor globally we try to make gcc happy and introduce virtual destructors for affected classes. XPCOM is not touched here.

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