VirtualBox

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

Last change on this file since 42319 was 42274, checked in by vboxsync, 13 years ago

build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.5 KB
Line 
1
2/* $Id: GuestProcessImpl.cpp 42274 2012-07-20 14:53:51Z vboxsync $ */
3/** @file
4 * VirtualBox Main - XXX.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestProcessImpl.h"
24#include "GuestSessionImpl.h"
25#include "ConsoleImpl.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "Logging.h"
30#include "VMMDev.h"
31
32#include <memory> /* For auto_ptr. */
33
34#include <iprt/asm.h>
35#include <iprt/getopt.h>
36#include <VBox/VMMDev.h>
37#include <VBox/com/array.h>
38
39
40struct GuestProcessTask
41{
42 GuestProcessTask(GuestProcess *pProcess)
43 : mProcess(pProcess) { }
44
45 ~GuestProcessTask(void) { }
46
47 int rc() const { return mRC; }
48 bool isOk() const { return RT_SUCCESS(rc()); }
49
50 const ComObjPtr<GuestProcess> mProcess;
51
52private:
53 int mRC;
54};
55
56struct GuestProcessStartTask : public GuestProcessTask
57{
58 GuestProcessStartTask(GuestProcess *pProcess)
59 : GuestProcessTask(pProcess) { }
60};
61
62
63// constructor / destructor
64/////////////////////////////////////////////////////////////////////////////
65
66DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
67
68HRESULT GuestProcess::FinalConstruct(void)
69{
70 LogFlowFuncEnter();
71
72 mData.mExitCode = 0;
73 mData.mNextContextID = 0;
74 mData.mPID = 0;
75 mData.mProcessID = 0;
76 mData.mStatus = ProcessStatus_Undefined;
77 mData.mStarted = false;
78 mData.mWaiting = false;
79 mData.mWaitFlags = 0;
80 mData.mWaitMutex = NIL_RTSEMMUTEX;
81 mData.mWaitEvent = NIL_RTSEMEVENT;
82
83 HRESULT hr = BaseFinalConstruct();
84 LogFlowFuncLeaveRC(hr);
85 return hr;
86}
87
88void GuestProcess::FinalRelease(void)
89{
90 LogFlowThisFuncEnter();
91 uninit();
92 BaseFinalRelease();
93 LogFlowThisFuncLeave();
94}
95
96// public initializer/uninitializer for internal purposes only
97/////////////////////////////////////////////////////////////////////////////
98
99int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aProcessID, const GuestProcessInfo &aProcInfo)
100{
101 AssertPtrReturn(aSession, VERR_INVALID_POINTER);
102
103 /* Enclose the state transition NotReady->InInit->Ready. */
104 AutoInitSpan autoInitSpan(this);
105 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
106
107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
108
109 mData.mConsole = aConsole;
110 mData.mParent = aSession;
111 mData.mProcessID = aProcessID;
112 mData.mProcess = aProcInfo;
113
114 mData.mStatus = ProcessStatus_Starting;
115 /* Everything else will be set by the actual starting routine. */
116
117 int rc = RTSemEventCreate(&mData.mWaitEvent);
118 if (RT_FAILURE(rc))
119 return rc;
120
121 rc = RTSemMutexCreate(&mData.mWaitMutex);
122 if (RT_FAILURE(rc))
123 return rc;
124
125 /* Asynchronously start the process on the guest by kicking off a
126 * worker thread. */
127 std::auto_ptr<GuestProcessStartTask> pTask(new GuestProcessStartTask(this));
128 AssertReturn(pTask->isOk(), pTask->rc());
129
130 rc = RTThreadCreate(NULL, GuestProcess::startProcessThread,
131 (void *)pTask.get(), 0,
132 RTTHREADTYPE_MAIN_WORKER, 0,
133 "gctlPrcStart");
134 if (RT_SUCCESS(rc))
135 {
136 /* task is now owned by startProcessThread(), so release it. */
137 pTask.release();
138
139 /* Confirm a successful initialization when it's the case. */
140 autoInitSpan.setSucceeded();
141 }
142
143 return rc;
144}
145
146/**
147 * Uninitializes the instance.
148 * Called from FinalRelease().
149 */
150void GuestProcess::uninit(void)
151{
152 LogFlowThisFunc(("\n"));
153
154 /* Enclose the state transition Ready->InUninit->NotReady. */
155 AutoUninitSpan autoUninitSpan(this);
156 if (autoUninitSpan.uninitDone())
157 return;
158
159 int rc = RTSemEventDestroy(mData.mWaitEvent);
160 AssertRC(rc);
161 rc = RTSemMutexDestroy(mData.mWaitMutex);
162 AssertRC(rc);
163
164 LogFlowFuncLeaveRC(rc);
165}
166
167// implementation of public getters/setters for attributes
168/////////////////////////////////////////////////////////////////////////////
169
170STDMETHODIMP GuestProcess::COMGETTER(Arguments)(ComSafeArrayOut(BSTR, aArguments))
171{
172#ifndef VBOX_WITH_GUEST_CONTROL
173 ReturnComNotImplemented();
174#else
175 LogFlowThisFuncEnter();
176
177 AutoCaller autoCaller(this);
178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
179
180 CheckComArgOutSafeArrayPointerValid(aArguments);
181
182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
183
184 com::SafeArray<BSTR> collection(mData.mProcess.mArguments.size());
185 size_t s = 0;
186 for (ProcessArguments::const_iterator it = mData.mProcess.mArguments.begin();
187 it != mData.mProcess.mArguments.end();
188 it++, s++)
189 {
190 Bstr tmp = *it;
191 tmp.cloneTo(&collection[s]);
192 }
193
194 collection.detachTo(ComSafeArrayOutArg(aArguments));
195
196 LogFlowFuncLeaveRC(S_OK);
197 return S_OK;
198#endif /* VBOX_WITH_GUEST_CONTROL */
199}
200
201STDMETHODIMP GuestProcess::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnvironment))
202{
203#ifndef VBOX_WITH_GUEST_CONTROL
204 ReturnComNotImplemented();
205#else
206 LogFlowThisFuncEnter();
207
208 AutoCaller autoCaller(this);
209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
210
211 CheckComArgOutSafeArrayPointerValid(aEnvironment);
212
213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
214
215 com::SafeArray<BSTR> arguments(mData.mProcess.mEnvironment.Size());
216 for (size_t i = 0; i < arguments.size(); i++)
217 {
218 Bstr tmp = mData.mProcess.mEnvironment.Get(i);
219 tmp.cloneTo(&arguments[i]);
220 }
221 arguments.detachTo(ComSafeArrayOutArg(aEnvironment));
222
223 LogFlowFuncLeaveRC(S_OK);
224 return S_OK;
225#endif /* VBOX_WITH_GUEST_CONTROL */
226}
227
228STDMETHODIMP GuestProcess::COMGETTER(ExecutablePath)(BSTR *aExecutablePath)
229{
230#ifndef VBOX_WITH_GUEST_CONTROL
231 ReturnComNotImplemented();
232#else
233 LogFlowThisFuncEnter();
234
235 AutoCaller autoCaller(this);
236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
237
238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
239
240 mData.mProcess.mCommand.cloneTo(aExecutablePath);
241
242 LogFlowFuncLeaveRC(S_OK);
243 return S_OK;
244#endif /* VBOX_WITH_GUEST_CONTROL */
245}
246
247STDMETHODIMP GuestProcess::COMGETTER(ExitCode)(LONG *aExitCode)
248{
249#ifndef VBOX_WITH_GUEST_CONTROL
250 ReturnComNotImplemented();
251#else
252 LogFlowThisFuncEnter();
253
254 AutoCaller autoCaller(this);
255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
256
257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
258
259 *aExitCode = mData.mExitCode;
260
261 LogFlowFuncLeaveRC(S_OK);
262 return S_OK;
263#endif /* VBOX_WITH_GUEST_CONTROL */
264}
265
266STDMETHODIMP GuestProcess::COMGETTER(Pid)(ULONG *aPID)
267{
268#ifndef VBOX_WITH_GUEST_CONTROL
269 ReturnComNotImplemented();
270#else
271 LogFlowThisFuncEnter();
272
273 AutoCaller autoCaller(this);
274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
275
276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
277
278 *aPID = mData.mPID;
279
280 LogFlowFuncLeaveRC(S_OK);
281 return S_OK;
282#endif /* VBOX_WITH_GUEST_CONTROL */
283}
284
285STDMETHODIMP GuestProcess::COMGETTER(Status)(ProcessStatus_T *aStatus)
286{
287#ifndef VBOX_WITH_GUEST_CONTROL
288 ReturnComNotImplemented();
289#else
290 LogFlowThisFuncEnter();
291
292 AutoCaller autoCaller(this);
293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
294
295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
296
297 *aStatus = mData.mStatus;
298
299 LogFlowFuncLeaveRC(S_OK);
300 return S_OK;
301#endif /* VBOX_WITH_GUEST_CONTROL */
302}
303
304// private methods
305/////////////////////////////////////////////////////////////////////////////
306
307/*
308
309 SYNC TO ASK:
310 Everything which involves HGCM communication (start, read/write/status(?)/...)
311 either can be called synchronously or asynchronously by running in a Main worker
312 thread.
313
314 Rules:
315 - Only one async operation per process a time can be around.
316
317*/
318
319int GuestProcess::callbackAdd(GuestCtrlCallback *pCallback, ULONG *puContextID)
320{
321 const ComObjPtr<GuestSession> pSession(mData.mParent);
322 Assert(!pSession.isNull());
323 ULONG uSessionID = 0;
324 HRESULT hr = pSession->COMGETTER(Id)(&uSessionID);
325 ComAssertComRC(hr);
326
327 /* Create a new context ID and assign it. */
328 int rc = VERR_NOT_FOUND;
329 ULONG uNewContextID = 0;
330 ULONG uTries = 0;
331 for (;;)
332 {
333 /* Create a new context ID ... */
334 uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID,
335 mData.mProcessID, mData.mNextContextID++);
336 if (uNewContextID == UINT32_MAX)
337 mData.mNextContextID = 0;
338 /* Is the context ID already used? Try next ID ... */
339 if (!callbackExists(uNewContextID))
340 {
341 /* Callback with context ID was not found. This means
342 * we can use this context ID for our new callback we want
343 * to add below. */
344 rc = VINF_SUCCESS;
345 break;
346 }
347
348 if (++uTries == UINT32_MAX)
349 break; /* Don't try too hard. */
350 }
351
352 if (RT_SUCCESS(rc))
353 {
354 /* Add callback with new context ID to our callback map. */
355 mData.mCallbacks[uNewContextID] = pCallback;
356 Assert(mData.mCallbacks.size());
357
358 /* Report back new context ID. */
359 if (puContextID)
360 *puContextID = uNewContextID;
361 }
362
363 return rc;
364}
365
366int GuestProcess::callbackDispatcher(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
367{
368/* LogFlowFunc(("uPID=%RU32, uContextID=%RU32, uFunction=%RU32, pvData=%p, cbData=%z\n",
369 mData.mPID, uContextID, uFunction, pvData, cbData));*/
370
371 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
372 AssertReturn(cbData, VERR_INVALID_PARAMETER);
373
374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
375
376 int rc;
377 GuestCtrlCallbacks::const_iterator it
378 = mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
379 if (it != mData.mCallbacks.end())
380 {
381 GuestCtrlCallback *pCallback = it->second;
382 AssertPtr(pCallback);
383
384 switch (uFunction)
385 {
386 case GUEST_DISCONNECTED:
387 {
388 PCALLBACKDATACLIENTDISCONNECTED pCallbackData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvData);
389 AssertPtr(pCallbackData);
390 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbData, VERR_INVALID_PARAMETER);
391 AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
392
393 rc = onGuestDisconnected(pCallback, pCallbackData); /* Affects all callbacks. */
394 break;
395 }
396
397 case GUEST_EXEC_SEND_STATUS:
398 {
399 PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvData);
400 AssertPtr(pCallbackData);
401 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbData, VERR_INVALID_PARAMETER);
402 AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
403
404 rc = onProcessStatusChange(pCallback, pCallbackData);
405 break;
406 }
407
408 case GUEST_EXEC_SEND_OUTPUT:
409 {
410 PCALLBACKDATAEXECOUT pCallbackData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvData);
411 AssertPtr(pCallbackData);
412 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbData, VERR_INVALID_PARAMETER);
413 AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
414
415 Assert(mData.mPID == pCallbackData->u32PID);
416 rc = onProcessOutput(pCallback, pCallbackData);
417 break;
418 }
419
420 case GUEST_EXEC_SEND_INPUT_STATUS:
421 {
422 PCALLBACKDATAEXECINSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvData);
423 AssertPtr(pCallbackData);
424 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbData, VERR_INVALID_PARAMETER);
425 AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
426
427 Assert(mData.mPID == pCallbackData->u32PID);
428 rc = onProcessInputStatus(pCallback, pCallbackData);
429 break;
430 }
431
432 default:
433 /* Silently ignore not implemented functions. */
434 rc = VERR_NOT_IMPLEMENTED;
435 break;
436 }
437 }
438 else
439 rc = VERR_NOT_FOUND;
440
441 //LogFlowFuncLeaveRC(rc);
442 return rc;
443}
444
445inline bool GuestProcess::callbackExists(ULONG uContextID)
446{
447 GuestCtrlCallbacks::const_iterator it = mData.mCallbacks.find(uContextID);
448 return (it == mData.mCallbacks.end()) ? false : true;
449}
450
451bool GuestProcess::isReady(void)
452{
453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
454
455 if (mData.mStatus == ProcessStatus_Started)
456 {
457 Assert(mData.mPID); /* PID must not be 0. */
458 return true;
459 }
460
461 return false;
462}
463
464int GuestProcess::onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData)
465{
466 LogFlowFunc(("uPID=%RU32, pCallback=%p, pData=%p\n", mData.mPID, pCallback, pData));
467
468 /* First, signal callback in every case. */
469 pCallback->Signal();
470
471 /* Signal in any case. */
472 int rc = signalWaiters(VERR_CANCELLED, tr("The guest got disconnected"));
473 AssertRC(rc);
474
475 LogFlowFuncLeaveRC(rc);
476 return rc;
477}
478
479int GuestProcess::onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData)
480{
481 LogFlowFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, cbProcessed=%RU32, pCallback=%p, pData=%p\n",
482 mData.mPID, pData->u32Status, pData->u32Flags, pData->cbProcessed, pCallback, pData));
483
484 int rc = VINF_SUCCESS;
485
486 /** @todo Fill data into callback. */
487
488 /* First, signal callback in every case. */
489 pCallback->Signal();
490
491 /* Then do the WaitFor signalling stuff. */
492 if (mData.mWaitFlags & ProcessWaitForFlag_StdIn)
493 rc = signalWaiters(VINF_SUCCESS);
494
495 LogFlowFuncLeaveRC(rc);
496 return rc;
497}
498
499int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
500{
501 LogFlowFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, pCallback=%p, pData=%p\n",
502 pData->u32PID, pData->u32Status, pData->u32Flags, pCallback, pData));
503
504 int rc = VINF_SUCCESS;
505
506 /* Get data from the callback payload. */
507 if (mData.mPID)
508 Assert(mData.mPID == pData->u32PID);
509
510 Utf8Str callbackMsg;
511 int callbackRC = VINF_SUCCESS;
512
513 bool fSignal = false;
514 switch (pData->u32Status)
515 {
516 case PROC_STS_STARTED:
517 fSignal = ( (mData.mWaitFlags & ProcessWaitForFlag_Start)
518 || (mData.mWaitFlags & ProcessWaitForFlag_Status));
519
520 mData.mStatus = ProcessStatus_Started;
521 mData.mPID = pData->u32PID;
522 mData.mStarted = true;
523
524 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" was started (PID %RU32)"),
525 mData.mProcess.mCommand.c_str(), mData.mPID);
526 LogRel((callbackMsg.c_str()));
527 break;
528
529 case PROC_STS_TEN:
530 {
531 mData.mStatus = ProcessStatus_TerminatedNormally;
532
533 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %u) terminated normally (exit code: %d)"),
534 mData.mProcess.mCommand.c_str(), mData.mPID, pData->u32Flags);
535 LogRel((callbackMsg.c_str()));
536 break;
537 }
538
539 case PROC_STS_TES:
540 {
541 mData.mStatus = ProcessStatus_TerminatedSignal;
542
543 callbackRC = VERR_INTERRUPTED;
544 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %u) terminated through signal (exit code: %d)"),
545 mData.mProcess.mCommand.c_str(), mData.mPID, pData->u32Flags);
546 LogRel((callbackMsg.c_str()));
547 break;
548 }
549
550 case PROC_STS_TEA:
551 {
552 mData.mStatus = ProcessStatus_TerminatedAbnormally;
553
554 callbackRC = VERR_BROKEN_PIPE;
555 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %u) terminated abnormally (exit code: %d)"),
556 mData.mProcess.mCommand.c_str(), mData.mPID, pData->u32Flags);
557 LogRel((callbackMsg.c_str()));
558 break;
559 }
560
561 case PROC_STS_TOK:
562 {
563 mData.mStatus = ProcessStatus_TimedOutKilled;
564
565 callbackRC = VERR_TIMEOUT;
566 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %u) timed out and was killed"),
567 mData.mProcess.mCommand.c_str(), mData.mPID);
568 LogRel((callbackMsg.c_str()));
569 break;
570 }
571
572 case PROC_STS_TOA:
573 {
574 mData.mStatus = ProcessStatus_TimedOutAbnormally;
575
576 callbackRC = VERR_TIMEOUT;
577 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %u) timed out and could not be killed\n"),
578 mData.mProcess.mCommand.c_str(), mData.mPID);
579 LogRel((callbackMsg.c_str()));
580 break;
581 }
582
583 case PROC_STS_DWN:
584 {
585 mData.mStatus = ProcessStatus_Down;
586
587 /*
588 * If mFlags has CreateProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
589 * our progress object. This is helpful for waiters which rely on the success of our progress object
590 * even if the executed process was killed because the system/VBoxService is shutting down.
591 *
592 * In this case mFlags contains the actual execution flags reached in via Guest::ExecuteProcess().
593 */
594 callbackRC = mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses
595 ? VINF_SUCCESS : VERR_OBJECT_DESTROYED;
596 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" (PID %u) was killed because system is shutting down\n"),
597 mData.mProcess.mCommand.c_str(), mData.mPID);
598 LogRel((callbackMsg.c_str()));
599 break;
600 }
601
602 case PROC_STS_ERROR:
603 {
604 mData.mStatus = ProcessStatus_Error;
605
606 callbackRC = pData->u32Flags;
607 callbackMsg = Utf8StrFmt(tr("Guest process \"%s\" could not be started: ", mData.mProcess.mCommand.c_str()));
608
609 /* Note: It's not required that the process
610 * has been started before. */
611 if (pData->u32PID)
612 {
613 callbackMsg += Utf8StrFmt(tr("Error rc=%Rrc occured"), pData->u32Flags);
614 }
615 else
616 {
617 switch (callbackRC) /* callbackRC contains the IPRT error code from guest side. */
618 {
619 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
620 callbackMsg += Utf8StrFmt(tr("The specified file was not found on guest"));
621 break;
622
623 case VERR_PATH_NOT_FOUND:
624 callbackMsg += Utf8StrFmt(tr("Could not resolve path to specified file was not found on guest"));
625 break;
626
627 case VERR_BAD_EXE_FORMAT:
628 callbackMsg += Utf8StrFmt(tr("The specified file is not an executable format on guest"));
629 break;
630
631 case VERR_AUTHENTICATION_FAILURE:
632 callbackMsg += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
633 break;
634
635 case VERR_TIMEOUT:
636 callbackMsg += Utf8StrFmt(tr("The guest did not respond within time"));
637 break;
638
639 case VERR_CANCELLED:
640 callbackMsg += Utf8StrFmt(tr("The execution operation was canceled"));
641 break;
642
643 case VERR_PERMISSION_DENIED:
644 callbackMsg += Utf8StrFmt(tr("Invalid user/password credentials"));
645 break;
646
647 case VERR_MAX_PROCS_REACHED:
648 callbackMsg += Utf8StrFmt(tr("Maximum number of parallel guest processes has been reached"));
649 break;
650
651 default:
652 callbackMsg += Utf8StrFmt(tr("Reported error %Rrc"), callbackRC);
653 break;
654 }
655 }
656
657 LogRel((callbackMsg.c_str()));
658 break;
659 }
660
661 case PROC_STS_UNDEFINED:
662 default:
663
664 /* Silently skip this request. */
665 fSignal = true; /* Signal in any case. */
666 break;
667 }
668
669 /*
670 * Now do the signalling stuff.
671 */
672 rc = pCallback->Signal(callbackRC, callbackMsg);
673
674 if (!fSignal)
675 fSignal = mData.mWaitFlags & ProcessWaitForFlag_Status;
676 if (fSignal)
677 {
678 int rc2 = signalWaiters(callbackRC, callbackMsg);
679 if (RT_SUCCESS(rc))
680 rc = rc2;
681 }
682
683 LogFlowFuncLeaveRC(rc);
684 return rc;
685}
686
687int GuestProcess::onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECOUT pData)
688{
689 LogFlowFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, pCallback=%p, pData=%p\n",
690 mData.mPID, pData->u32HandleId, pData->u32Flags, pData->pvData, pData->cbData, pCallback, pData));
691
692 int rc = VINF_SUCCESS;
693
694 /** @todo Fill data into callback. */
695
696 /* First, signal callback in every case. */
697 pCallback->Signal();
698
699 /* Then do the WaitFor signalling stuff. */
700 bool fSignal = false;
701 if ( (mData.mWaitFlags & ProcessWaitForFlag_StdOut)
702 || (mData.mWaitFlags & ProcessWaitForFlag_StdErr))
703 {
704 fSignal = true;
705 }
706 else if ( (mData.mWaitFlags & ProcessWaitForFlag_StdOut)
707 && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT))
708 {
709 fSignal = true;
710 }
711 else if ( (mData.mWaitFlags & ProcessWaitForFlag_StdErr)
712 && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDERR))
713 {
714 fSignal = true;
715 }
716
717 if (fSignal)
718 {
719 Assert(mData.mWaitEvent != NIL_RTSEMEVENT);
720 rc = RTSemEventSignal(mData.mWaitEvent);
721 AssertRC(rc);
722 }
723
724 LogFlowFuncLeaveRC(rc);
725 return rc;
726}
727
728int GuestProcess::readData(ULONG uHandle, ULONG uSize, ULONG uTimeoutMS, BYTE *pbData, size_t cbData)
729{
730 LogFlowFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pbData=%p, cbData=%z\n",
731 mData.mPID, uHandle, uSize, uTimeoutMS, pbData, cbData));
732 AssertReturn(uSize, VERR_INVALID_PARAMETER);
733 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
734 AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
735
736 LogFlowFuncLeave();
737 return 0;
738}
739
740int GuestProcess::sendCommand(uint32_t uFunction,
741 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
742{
743 LogFlowFuncEnter();
744
745 const ComObjPtr<Console> pConsole(mData.mConsole);
746 Assert(!pConsole.isNull());
747
748 VMMDev *pVMMDev = NULL;
749 {
750 /* Make sure mParent is valid, so set the read lock while using.
751 * Do not keep this lock while doing the actual call, because in the meanwhile
752 * another thread could request a write lock which would be a bad idea ... */
753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
754
755 /* Forward the information to the VMM device. */
756 pVMMDev = pConsole->getVMMDev();
757 }
758
759 LogFlowFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
760 int rc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", uFunction, uParms, paParms);
761 if (RT_FAILURE(rc))
762 {
763 }
764
765 LogFlowFuncLeaveRC(rc);
766 return rc;
767}
768
769int GuestProcess::signalWaiters(int rc, const Utf8Str strMessage)
770{
771 LogFlowFunc(("rc=%Rrc, strMessage=%s, mWaiting=%RTbool, mWaitEvent=%p\n",
772 rc, strMessage.c_str(), mData.mWaiting, mData.mWaitEvent));
773
774 /* Note: No locking here -- already done in the callback dispatcher. */
775
776 /* We have to set the error information here because the waiters are public
777 * Main clients which rely on it. */
778 if (RT_FAILURE(rc))
779 {
780 HRESULT hr = setError(VBOX_E_IPRT_ERROR, strMessage.c_str());
781 ComAssertComRC(hr);
782 }
783
784 int rc2 = VINF_SUCCESS;
785 if ( mData.mWaiting
786 && mData.mWaitEvent != NIL_RTSEMEVENT)
787 {
788 rc2 = RTSemEventSignal(mData.mWaitEvent);
789 AssertRC(rc2);
790 }
791
792 LogFlowFuncLeaveRC(rc2);
793 return rc2;
794}
795
796int GuestProcess::startProcess(int *pRC, Utf8Str *pstrMessage)
797{
798 /* Parameters are optional. */
799
800 LogFlowFunc(("aCmd=%s, aTimeoutMS=%RU32, fFlags=%x\n",
801 mData.mProcess.mCommand.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags));
802
803 AssertReturn(!mData.mStarted, VERR_ALREADY_EXISTS);
804
805 int rc;
806 ULONG uContextID;
807 GuestCtrlCallback *pCallbackStart = new GuestCtrlCallback();
808 if (!pCallbackStart)
809 return VERR_NO_MEMORY;
810
811 {
812 /* Wait until the caller function (if kicked off by a thread)
813 * has returned and continue operation. */
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 /* Create callback and add it to the map. */
817 rc = pCallbackStart->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START);
818 if (RT_SUCCESS(rc))
819 rc = callbackAdd(pCallbackStart, &uContextID);
820 }
821
822 if (RT_SUCCESS(rc))
823 {
824 ComObjPtr<GuestSession> pSession(mData.mParent);
825 Assert(!pSession.isNull());
826
827 const GuestCredentials &sessionCreds = pSession->getCredentials();
828
829 /* Prepare arguments. */
830 char *pszArgs = NULL;
831 size_t cArgs = mData.mProcess.mArguments.size();
832 if (cArgs)
833 {
834 char **papszArgv = (char**)RTMemAlloc(sizeof(char*) * (cArgs + 1));
835 AssertReturn(papszArgv, VERR_NO_MEMORY);
836 for (size_t i = 0; RT_SUCCESS(rc) && i < cArgs; i++)
837 rc = RTStrDupEx(&papszArgv[i], mData.mProcess.mArguments[i].c_str());
838 papszArgv[cArgs] = NULL;
839
840 if (RT_SUCCESS(rc))
841 rc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
842 }
843 size_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
844
845 /* Prepare environment. */
846 void *pvEnv = NULL;
847 size_t cbEnv = 0;
848 rc = mData.mProcess.mEnvironment.BuildEnvironmentBlock(&pvEnv, &cbEnv, NULL /* cEnv */);
849
850 if (RT_SUCCESS(rc))
851 {
852 /* Prepare HGCM call. */
853 VBOXHGCMSVCPARM paParms[15];
854 int i = 0;
855 paParms[i++].setUInt32(uContextID);
856 paParms[i++].setPointer((void*)mData.mProcess.mCommand.c_str(),
857 (ULONG)mData.mProcess.mCommand.length() + 1);
858 paParms[i++].setUInt32(mData.mProcess.mFlags);
859 paParms[i++].setUInt32(mData.mProcess.mArguments.size());
860 paParms[i++].setPointer((void*)pszArgs, cbArgs);
861 paParms[i++].setUInt32(mData.mProcess.mEnvironment.Size());
862 paParms[i++].setUInt32(cbEnv);
863 paParms[i++].setPointer((void*)pvEnv, cbEnv);
864 paParms[i++].setPointer((void*)sessionCreds.mUser.c_str(), (ULONG)sessionCreds.mUser.length() + 1);
865 paParms[i++].setPointer((void*)sessionCreds.mPassword.c_str(), (ULONG)sessionCreds.mPassword.length() + 1);
866
867 /*
868 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
869 * until the process was started - the process itself then gets an infinite timeout for execution.
870 * This is handy when we want to start a process inside a worker thread within a certain timeout
871 * but let the started process perform lengthly operations then.
872 */
873 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
874 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
875 else
876 paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
877
878 rc = sendCommand(HOST_EXEC_CMD, i, paParms);
879 }
880
881 GuestEnvironment::FreeEnvironmentBlock(pvEnv);
882 if (pszArgs)
883 RTStrFree(pszArgs);
884
885 if (RT_SUCCESS(rc))
886 {
887 /*
888 * Let's wait for the process being started.
889 * Note: Be sure not keeping a AutoRead/WriteLock here.
890 */
891 rc = pCallbackStart->Wait(mData.mProcess.mTimeoutMS);
892 if (RT_SUCCESS(rc)) /* Wait was successful, check for supplied information. */
893 {
894 if (pRC)
895 *pRC = pCallbackStart->GetResultCode();
896 if (pstrMessage)
897 *pstrMessage = pCallbackStart->GetMessage();
898 }
899 }
900 }
901
902 if (pCallbackStart)
903 delete pCallbackStart;
904
905 LogFlowFuncLeaveRC(rc);
906 return rc;
907}
908
909DECLCALLBACK(int) GuestProcess::startProcessThread(RTTHREAD Thread, void *pvUser)
910{
911 LogFlowFuncEnter();
912
913 std::auto_ptr<GuestProcessStartTask> pTask(static_cast<GuestProcessStartTask*>(pvUser));
914 AssertPtr(pTask.get());
915
916 const ComObjPtr<GuestProcess> pProcess(pTask->mProcess);
917 Assert(!pProcess.isNull());
918
919 int rc = pProcess->startProcess();
920 AssertRC(rc);
921
922 LogFlowFuncLeaveRC(rc);
923 return rc;
924}
925
926int GuestProcess::terminateProcess(void)
927{
928 LogFlowFuncEnter();
929
930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
931
932 LogFlowFuncLeave();
933 return 0;
934}
935
936int GuestProcess::waitFor(uint32_t fFlags, ULONG uTimeoutMS, ProcessWaitReason_T *penmReason)
937{
938 Assert(mData.mWaitEvent != NIL_RTSEMEVENT);
939
940 if (ASMAtomicReadBool(&mData.mWaiting))
941 return VERR_ALREADY_EXISTS;
942
943 /* At the moment we only support one waiter at a time. */
944 int rc = RTSemMutexRequest(mData.mWaitMutex, uTimeoutMS);
945 if (RT_SUCCESS(rc))
946 {
947 ASMAtomicWriteBool(&mData.mWaiting, true);
948 rc = RTSemEventWait(mData.mWaitEvent, uTimeoutMS);
949 if (RT_SUCCESS(rc))
950 {
951 /** @todo Error handling after waiting. */
952 }
953
954 int rc2 = RTSemMutexRelease(mData.mWaitMutex);
955 if (RT_SUCCESS(rc))
956 rc = rc2;
957 }
958
959 return rc;
960}
961
962int GuestProcess::writeData(ULONG uHandle, const BYTE *pbData, size_t cbData, ULONG uTimeoutMS, ULONG *puWritten)
963{
964 LogFlowFunc(("uPID=%RU32, uHandle=%RU32, pbData=%p, cbData=%z, uTimeoutMS=%RU32, puWritten=%p\n",
965 mData.mPID, uHandle, pbData, cbData, uTimeoutMS, puWritten));
966 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
967 AssertReturn(pbData, VERR_INVALID_PARAMETER);
968 /* Rest is optional. */
969
970 LogFlowFuncLeave();
971 return 0;
972}
973
974// implementation of public methods
975/////////////////////////////////////////////////////////////////////////////
976
977STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aSize, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
978{
979#ifndef VBOX_WITH_GUEST_CONTROL
980 ReturnComNotImplemented();
981#else
982 LogFlowThisFuncEnter();
983
984 if (aSize == 0)
985 return setError(E_INVALIDARG, tr("Invalid size to read specified"));
986 CheckComArgOutSafeArrayPointerValid(aData);
987
988 AutoCaller autoCaller(this);
989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
990
991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
992
993 com::SafeArray<BYTE> data(aSize);
994 int rc = readData(aHandle, aSize, aTimeoutMS, data.raw(), aSize);
995 if (RT_SUCCESS(rc))
996 data.detachTo(ComSafeArrayOutArg(aData));
997
998 /** @todo Do setError() here. */
999 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1000 LogFlowFuncLeaveRC(hr);
1001
1002 return hr;
1003#endif /* VBOX_WITH_GUEST_CONTROL */
1004}
1005
1006STDMETHODIMP GuestProcess::Terminate(void)
1007{
1008#ifndef VBOX_WITH_GUEST_CONTROL
1009 ReturnComNotImplemented();
1010#else
1011 LogFlowThisFuncEnter();
1012
1013 AutoCaller autoCaller(this);
1014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1015
1016 int rc = terminateProcess();
1017 /** @todo Do setError() here. */
1018 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1019 LogFlowFuncLeaveRC(hr);
1020
1021 return hr;
1022#endif /* VBOX_WITH_GUEST_CONTROL */
1023}
1024
1025STDMETHODIMP GuestProcess::WaitFor(ComSafeArrayIn(ProcessWaitForFlag_T, aFlags), ULONG aTimeoutMS, ProcessWaitReason_T *aReason)
1026{
1027#ifndef VBOX_WITH_GUEST_CONTROL
1028 ReturnComNotImplemented();
1029#else
1030 LogFlowThisFuncEnter();
1031
1032 CheckComArgOutPointerValid(aReason);
1033
1034 AutoCaller autoCaller(this);
1035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1036
1037 /*
1038 * Note: Do not hold any locks here while waiting!
1039 */
1040 uint32_t fWaitFor = ProcessWaitForFlag_None;
1041 com::SafeArray<ProcessWaitForFlag_T> flags(ComSafeArrayInArg(aFlags));
1042 for (size_t i = 0; i < flags.size(); i++)
1043 fWaitFor |= flags[i];
1044
1045 int rc = waitFor(fWaitFor, aTimeoutMS, aReason);
1046 /** @todo Do setError() here. */
1047 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1048 LogFlowFuncLeaveRC(hr);
1049
1050 return hr;
1051#endif /* VBOX_WITH_GUEST_CONTROL */
1052}
1053
1054STDMETHODIMP GuestProcess::Write(ULONG aHandle, ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1055{
1056#ifndef VBOX_WITH_GUEST_CONTROL
1057 ReturnComNotImplemented();
1058#else
1059 LogFlowThisFuncEnter();
1060
1061 CheckComArgOutPointerValid(aWritten);
1062
1063 AutoCaller autoCaller(this);
1064 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
1069 int rc = writeData(aHandle, data.raw(), data.size(), aTimeoutMS, aWritten);
1070 /** @todo Do setError() here. */
1071 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1072 LogFlowFuncLeaveRC(hr);
1073
1074 return hr;
1075#endif /* VBOX_WITH_GUEST_CONTROL */
1076}
1077
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette