VirtualBox

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

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

GuestSessionImpl.cpp: Forgot to update the GuestProcessTool users with the argument list changes.

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