VirtualBox

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

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

GuestProcessImpl: fixed warnings

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