VirtualBox

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

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

Guest Control 2.0: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.3 KB
Line 
1
2/* $Id: GuestProcessImpl.cpp 42171 2012-07-16 20:28:47Z 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 LogFlowThisFunc(("\n"));
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
79 return BaseFinalConstruct();
80}
81
82void GuestProcess::FinalRelease(void)
83{
84 LogFlowThisFuncEnter();
85 uninit();
86 BaseFinalRelease();
87 LogFlowThisFuncLeave();
88}
89
90// public initializer/uninitializer for internal purposes only
91/////////////////////////////////////////////////////////////////////////////
92
93int GuestProcess::init(Console *aConsole, GuestSession *aSession, uint32_t aProcessID, const GuestProcessInfo &aProcInfo)
94{
95 AssertPtrReturn(aSession, VERR_INVALID_POINTER);
96
97 /* Enclose the state transition NotReady->InInit->Ready. */
98 AutoInitSpan autoInitSpan(this);
99 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
100
101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
102
103 mData.mConsole = aConsole;
104 mData.mParent = aSession;
105 mData.mProcessID = aProcessID;
106 mData.mStatus = ProcessStatus_Starting;
107 /* Everything else will be set by the actual starting routine. */
108
109 /* Asynchronously start the process on the guest by kicking off a
110 * worker thread. */
111 std::auto_ptr<GuestProcessStartTask> pTask(new GuestProcessStartTask(this));
112 AssertReturn(pTask->isOk(), pTask->rc());
113
114 int rc = RTThreadCreate(NULL, GuestProcess::startProcessThread,
115 (void *)pTask.get(), 0,
116 RTTHREADTYPE_MAIN_WORKER, 0,
117 "gctlPrcStart");
118 if (RT_SUCCESS(rc))
119 {
120 /* task is now owned by startProcessThread(), so release it. */
121 pTask.release();
122
123 /* Confirm a successful initialization when it's the case. */
124 autoInitSpan.setSucceeded();
125 }
126
127 return rc;
128}
129
130/**
131 * Uninitializes the instance.
132 * Called from FinalRelease().
133 */
134void GuestProcess::uninit(void)
135{
136 LogFlowThisFunc(("\n"));
137
138 /* Enclose the state transition Ready->InUninit->NotReady. */
139 AutoUninitSpan autoUninitSpan(this);
140 if (autoUninitSpan.uninitDone())
141 return;
142}
143
144// implementation of public getters/setters for attributes
145/////////////////////////////////////////////////////////////////////////////
146
147STDMETHODIMP GuestProcess::COMGETTER(Arguments)(ComSafeArrayOut(BSTR, aArguments))
148{
149#ifndef VBOX_WITH_GUEST_CONTROL
150 ReturnComNotImplemented();
151#else
152 AutoCaller autoCaller(this);
153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
154
155 CheckComArgOutSafeArrayPointerValid(aArguments);
156
157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
158
159 com::SafeArray<BSTR> collection(mData.mProcess.mArguments.size());
160 size_t s = 0;
161 for (ProcessArguments::const_iterator it = mData.mProcess.mArguments.begin();
162 it != mData.mProcess.mArguments.end();
163 it++, s++)
164 {
165 collection[s] = Bstr(*it).raw();
166 }
167
168 collection.detachTo(ComSafeArrayOutArg(aArguments));
169
170 return S_OK;
171#endif /* VBOX_WITH_GUEST_CONTROL */
172}
173
174STDMETHODIMP GuestProcess::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnvironment))
175{
176#ifndef VBOX_WITH_GUEST_CONTROL
177 ReturnComNotImplemented();
178#else
179 AutoCaller autoCaller(this);
180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
181
182 CheckComArgOutSafeArrayPointerValid(aEnvironment);
183
184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
185
186 com::SafeArray<BSTR> arguments(mData.mProcess.mEnvironment.Size());
187 for (size_t i = 0; i < arguments.size(); i++)
188 arguments[i] = Bstr(mData.mProcess.mEnvironment.Get(i)).raw();
189 arguments.detachTo(ComSafeArrayOutArg(aEnvironment));
190
191 return S_OK;
192#endif /* VBOX_WITH_GUEST_CONTROL */
193}
194
195STDMETHODIMP GuestProcess::COMGETTER(ExecutablePath)(BSTR *aExecutablePath)
196{
197#ifndef VBOX_WITH_GUEST_CONTROL
198 ReturnComNotImplemented();
199#else
200 AutoCaller autoCaller(this);
201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
202
203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
204
205 mData.mProcess.mCommand.cloneTo(aExecutablePath);
206
207 return S_OK;
208#endif /* VBOX_WITH_GUEST_CONTROL */
209}
210
211STDMETHODIMP GuestProcess::COMGETTER(ExitCode)(LONG *aExitCode)
212{
213#ifndef VBOX_WITH_GUEST_CONTROL
214 ReturnComNotImplemented();
215#else
216 AutoCaller autoCaller(this);
217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
218
219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
220
221 *aExitCode = mData.mExitCode;
222
223 return S_OK;
224#endif /* VBOX_WITH_GUEST_CONTROL */
225}
226
227STDMETHODIMP GuestProcess::COMGETTER(Pid)(ULONG *aPID)
228{
229#ifndef VBOX_WITH_GUEST_CONTROL
230 ReturnComNotImplemented();
231#else
232 AutoCaller autoCaller(this);
233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
234
235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
236
237 *aPID = mData.mPID;
238
239 return S_OK;
240#endif /* VBOX_WITH_GUEST_CONTROL */
241}
242
243STDMETHODIMP GuestProcess::COMGETTER(Status)(ProcessStatus_T *aStatus)
244{
245#ifndef VBOX_WITH_GUEST_CONTROL
246 ReturnComNotImplemented();
247#else
248 AutoCaller autoCaller(this);
249 if (FAILED(autoCaller.rc())) return autoCaller.rc();
250
251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
252
253 *aStatus = mData.mStatus;
254
255 return S_OK;
256#endif /* VBOX_WITH_GUEST_CONTROL */
257}
258
259// private methods
260/////////////////////////////////////////////////////////////////////////////
261
262/*
263
264 SYNC TO ASK:
265 Everything which involves HGCM communication (start, read/write/status(?)/...)
266 either can be called synchronously or asynchronously by running in a Main worker
267 thread.
268
269 Rules:
270 - Only one async operation per process a time can be around.
271
272*/
273
274int GuestProcess::callbackAdd(const GuestCtrlCallback& theCallback, uint32_t *puContextID)
275{
276 const ComObjPtr<GuestSession> pSession(mData.mParent);
277 Assert(!pSession.isNull());
278 ULONG uSessionID = 0;
279 HRESULT hr = pSession->COMGETTER(Id)(&uSessionID);
280 ComAssertComRC(hr);
281
282 /* Create a new context ID and assign it. */
283 int rc = VERR_NOT_FOUND;
284 uint32_t uNewContextID = 0;
285 uint32_t uTries = 0;
286 for (;;)
287 {
288 /* Create a new context ID ... */
289 uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID,
290 mData.mProcessID, ASMAtomicIncU32(&mData.mNextContextID));
291 if (uNewContextID == UINT32_MAX)
292 ASMAtomicUoWriteU32(&mData.mNextContextID, 0);
293 /* Is the context ID already used? Try next ID ... */
294 if (!callbackExists(uNewContextID))
295 {
296 /* Callback with context ID was not found. This means
297 * we can use this context ID for our new callback we want
298 * to add below. */
299 rc = VINF_SUCCESS;
300 break;
301 }
302
303 if (++uTries == UINT32_MAX)
304 break; /* Don't try too hard. */
305 }
306
307 if (RT_SUCCESS(rc))
308 {
309 /* Add callback with new context ID to our callback map. */
310 mData.mCallbacks[uNewContextID] = theCallback;
311 Assert(mData.mCallbacks.size());
312
313 /* Report back new context ID. */
314 if (puContextID)
315 *puContextID = uNewContextID;
316 }
317
318 return rc;
319}
320
321bool GuestProcess::callbackExists(uint32_t uContextID)
322{
323 AssertReturn(uContextID, false);
324
325 GuestCtrlCallbacks::const_iterator it = mData.mCallbacks.find(uContextID);
326 return (it == mData.mCallbacks.end()) ? false : true;
327}
328
329bool GuestProcess::isReady(void)
330{
331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
332
333 if (mData.mStatus == ProcessStatus_Started)
334 {
335 Assert(mData.mPID); /* PID must not be 0. */
336 return true;
337 }
338
339 return false;
340}
341
342int GuestProcess::readData(ULONG aHandle, ULONG aSize, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
343{
344 LogFlowFuncEnter();
345
346 LogFlowFuncLeave();
347 return 0;
348}
349
350int GuestProcess::startProcess(void)
351{
352 LogFlowFuncEnter();
353
354 AssertReturn(!mData.mStarted, VERR_ALREADY_EXISTS);
355
356 int rc;
357 uint32_t uContextID;
358
359 {
360 /* Wait until the caller function (if kicked off by a thread)
361 * has returned and continue operation. */
362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
363
364 /* Create callback and add it to the map. */
365 GuestCtrlCallback callbackStart;
366 rc = callbackStart.Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START);
367 if (RT_FAILURE(rc))
368 return rc;
369
370 rc = callbackAdd(callbackStart, &uContextID);
371 Assert(uContextID);
372 }
373
374 if (RT_SUCCESS(rc))
375 {
376 ComObjPtr<GuestSession> pSession(mData.mParent);
377 Assert(!pSession.isNull());
378
379 const GuestCredentials &sessionCreds = pSession->getCredentials();
380
381 /* Prepare arguments. */
382 char *pszArgs = NULL;
383 size_t cArgs = mData.mProcess.mArguments.size();
384 if (cArgs)
385 {
386 char **papszArgv = (char**)RTMemAlloc(sizeof(char*) * (cArgs + 1));
387 AssertReturn(papszArgv, VERR_NO_MEMORY);
388 for (size_t i = 0; RT_SUCCESS(rc) && i < cArgs; i++)
389 rc = RTStrDupEx(&papszArgv[i], mData.mProcess.mArguments[i].c_str());
390 papszArgv[cArgs] = NULL;
391
392 if (RT_SUCCESS(rc))
393 rc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
394 }
395 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
396
397 /* Prepare environment. */
398 void *pvEnv = NULL;
399 uint32_t cbEnv = 0;
400 rc = mData.mProcess.mEnvironment.BuildEnvironmentBlock(&pvEnv, &cbEnv, NULL /* cEnv */);
401
402 if (RT_SUCCESS(rc))
403 {
404 /* Prepare HGCM call. */
405 VBOXHGCMSVCPARM paParms[15];
406 int i = 0;
407 paParms[i++].setUInt32(uContextID);
408 paParms[i++].setPointer((void*)mData.mProcess.mCommand.c_str(),
409 (uint32_t)mData.mProcess.mCommand.length() + 1);
410 paParms[i++].setUInt32(mData.mProcess.mFlags);
411 paParms[i++].setUInt32(mData.mProcess.mArguments.size());
412 paParms[i++].setPointer((void*)pszArgs, cbArgs);
413 paParms[i++].setUInt32(mData.mProcess.mEnvironment.Size());
414 paParms[i++].setUInt32(cbEnv);
415 paParms[i++].setPointer((void*)pvEnv, cbEnv);
416 paParms[i++].setPointer((void*)sessionCreds.mUser.c_str(), (uint32_t)sessionCreds.mUser.length() + 1);
417 paParms[i++].setPointer((void*)sessionCreds.mPassword.c_str(), (uint32_t)sessionCreds.mPassword.length() + 1);
418
419 /*
420 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
421 * until the process was started - the process itself then gets an infinite timeout for execution.
422 * This is handy when we want to start a process inside a worker thread within a certain timeout
423 * but let the started process perform lengthly operations then.
424 */
425 if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
426 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
427 else
428 paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
429
430 const ComObjPtr<Console> pConsole(mData.mConsole);
431 Assert(!pConsole.isNull());
432
433 VMMDev *pVMMDev = NULL;
434 {
435 /* Make sure mParent is valid, so set the read lock while using.
436 * Do not keep this lock while doing the actual call, because in the meanwhile
437 * another thread could request a write lock which would be a bad idea ... */
438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
439
440 /* Forward the information to the VMM device. */
441 pVMMDev = pConsole->getVMMDev();
442 }
443
444 LogFlowFunc(("hgcmHostCall numParms=%d, CID=%RU32\n", i, uContextID));
445 rc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
446 i, paParms);
447 }
448
449 GuestEnvironment::FreeEnvironmentBlock(pvEnv);
450 if (pszArgs)
451 RTStrFree(pszArgs);
452 }
453
454 LogFlowFuncLeave();
455 return rc;
456}
457
458DECLCALLBACK(int) GuestProcess::startProcessThread(RTTHREAD Thread, void *pvUser)
459{
460 LogFlowFuncEnter();
461
462 const ComObjPtr<GuestProcess> pProcess = static_cast<GuestProcess*>(pvUser);
463 Assert(!pProcess.isNull());
464
465 int rc = pProcess->startProcess();
466
467 LogFlowFuncLeave();
468 return rc;
469}
470
471int GuestProcess::terminateProcess(void)
472{
473 LogFlowFuncEnter();
474
475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
476
477 LogFlowFuncLeave();
478 return 0;
479}
480
481int GuestProcess::waitFor(ComSafeArrayIn(ProcessWaitForFlag_T, aFlags), ULONG aTimeoutMS, ProcessWaitReason_T *aReason)
482{
483 LogFlowFuncEnter();
484
485 LogFlowFuncLeave();
486 return 0;
487}
488
489int GuestProcess::writeData(ULONG aHandle, ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
490{
491 LogFlowFuncEnter();
492
493 LogFlowFuncLeave();
494 return 0;
495}
496
497// implementation of public methods
498/////////////////////////////////////////////////////////////////////////////
499
500STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aSize, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
501{
502#ifndef VBOX_WITH_GUEST_CONTROL
503 ReturnComNotImplemented();
504#else
505
506 AutoCaller autoCaller(this);
507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
508
509 int rc = readData(aHandle, aSize, aTimeoutMS, ComSafeArrayOutArg(aData));
510 /** @todo Do setError() here. */
511 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
512#endif /* VBOX_WITH_GUEST_CONTROL */
513}
514
515STDMETHODIMP GuestProcess::Terminate(void)
516{
517#ifndef VBOX_WITH_GUEST_CONTROL
518 ReturnComNotImplemented();
519#else
520 AutoCaller autoCaller(this);
521 if (FAILED(autoCaller.rc())) return autoCaller.rc();
522
523 int rc = terminateProcess();
524 /** @todo Do setError() here. */
525 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
526#endif /* VBOX_WITH_GUEST_CONTROL */
527}
528
529STDMETHODIMP GuestProcess::WaitFor(ComSafeArrayIn(ProcessWaitForFlag_T, aFlags), ULONG aTimeoutMS, ProcessWaitReason_T *aReason)
530{
531#ifndef VBOX_WITH_GUEST_CONTROL
532 ReturnComNotImplemented();
533#else
534 AutoCaller autoCaller(this);
535 if (FAILED(autoCaller.rc())) return autoCaller.rc();
536
537 int rc = waitFor(ComSafeArrayInArg(aFlags), aTimeoutMS, aReason);
538 /** @todo Do setError() here. */
539 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
540#endif /* VBOX_WITH_GUEST_CONTROL */
541}
542
543STDMETHODIMP GuestProcess::Write(ULONG aHandle, ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
544{
545#ifndef VBOX_WITH_GUEST_CONTROL
546 ReturnComNotImplemented();
547#else
548 AutoCaller autoCaller(this);
549 if (FAILED(autoCaller.rc())) return autoCaller.rc();
550
551 int rc = writeData(aHandle, ComSafeArrayInArg(aData), aTimeoutMS, aWritten);
552 /** @todo Do setError() here. */
553 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
554#endif /* VBOX_WITH_GUEST_CONTROL */
555}
556
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