VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp@ 39435

Last change on this file since 39435 was 39424, 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: 84.8 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 39424 2011-11-25 11:16:26Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2011 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#include "GuestImpl.h"
19#include "GuestCtrlImplPrivate.h"
20
21#include "Global.h"
22#include "ConsoleImpl.h"
23#include "ProgressImpl.h"
24#include "VMMDev.h"
25
26#include "AutoCaller.h"
27#include "Logging.h"
28
29#include <VBox/VMMDev.h>
30#ifdef VBOX_WITH_GUEST_CONTROL
31# include <VBox/com/array.h>
32# include <VBox/com/ErrorInfo.h>
33#endif
34#include <iprt/cpp/utils.h>
35#include <iprt/file.h>
36#include <iprt/getopt.h>
37#include <iprt/isofs.h>
38#include <iprt/list.h>
39#include <iprt/path.h>
40#include <VBox/vmm/pgm.h>
41
42#include <memory>
43
44// public methods only for internal purposes
45/////////////////////////////////////////////////////////////////////////////
46
47#ifdef VBOX_WITH_GUEST_CONTROL
48/**
49 * Appends environment variables to the environment block.
50 *
51 * Each var=value pair is separated by the null character ('\\0'). The whole
52 * block will be stored in one blob and disassembled on the guest side later to
53 * fit into the HGCM param structure.
54 *
55 * @returns VBox status code.
56 *
57 * @param pszEnvVar The environment variable=value to append to the
58 * environment block.
59 * @param ppvList This is actually a pointer to a char pointer
60 * variable which keeps track of the environment block
61 * that we're constructing.
62 * @param pcbList Pointer to the variable holding the current size of
63 * the environment block. (List is a misnomer, go
64 * ahead a be confused.)
65 * @param pcEnvVars Pointer to the variable holding count of variables
66 * stored in the environment block.
67 */
68int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)
69{
70 int rc = VINF_SUCCESS;
71 uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
72 if (*ppvList)
73 {
74 uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
75 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
76 if (pvTmp == NULL)
77 rc = VERR_NO_MEMORY;
78 else
79 {
80 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
81 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
82 *ppvList = (void **)pvTmp;
83 }
84 }
85 else
86 {
87 char *pszTmp;
88 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
89 {
90 *ppvList = (void **)pszTmp;
91 /* Reset counters. */
92 *pcEnvVars = 0;
93 *pcbList = 0;
94 }
95 }
96 if (RT_SUCCESS(rc))
97 {
98 *pcbList += cchEnv + 1; /* Include zero termination. */
99 *pcEnvVars += 1; /* Increase env variable count. */
100 }
101 return rc;
102}
103
104/**
105 * Adds a callback with a user provided data block and an optional progress object
106 * to the callback map. A callback is identified by a unique context ID which is used
107 * to identify a callback from the guest side.
108 *
109 * @return IPRT status code.
110 * @param pCallback
111 * @param puContextID
112 */
113int Guest::callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallback, uint32_t *puContextID)
114{
115 AssertPtrReturn(pCallback, VERR_INVALID_PARAMETER);
116 /* puContextID is optional. */
117
118 int rc;
119
120 /* Create a new context ID and assign it. */
121 uint32_t uNewContextID = 0;
122 for (;;)
123 {
124 /* Create a new context ID ... */
125 uNewContextID = ASMAtomicIncU32(&mNextContextID);
126 if (uNewContextID == UINT32_MAX)
127 ASMAtomicUoWriteU32(&mNextContextID, 1000);
128 /* Is the context ID already used? Try next ID ... */
129 if (!callbackExists(uNewContextID))
130 {
131 /* Callback with context ID was not found. This means
132 * we can use this context ID for our new callback we want
133 * to add below. */
134 rc = VINF_SUCCESS;
135 break;
136 }
137 }
138
139 if (RT_SUCCESS(rc))
140 {
141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
142
143 /* Add callback with new context ID to our callback map. */
144 mCallbackMap[uNewContextID] = *pCallback;
145 Assert(mCallbackMap.size());
146
147 /* Report back new context ID. */
148 if (puContextID)
149 *puContextID = uNewContextID;
150 }
151
152 return rc;
153}
154
155/**
156 * Does not do locking!
157 *
158 * @param uContextID
159 */
160void Guest::callbackDestroy(uint32_t uContextID)
161{
162 AssertReturnVoid(uContextID);
163
164 CallbackMapIter it = mCallbackMap.find(uContextID);
165 if (it != mCallbackMap.end())
166 {
167 LogFlowFunc(("Callback with CID=%u found\n", uContextID));
168 if (it->second.pvData)
169 {
170 LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
171
172 callbackFreeUserData(it->second.pvData);
173 it->second.cbData = 0;
174 }
175
176 /* Remove callback context (not used anymore). */
177 mCallbackMap.erase(it);
178 }
179}
180
181bool Guest::callbackExists(uint32_t uContextID)
182{
183 AssertReturn(uContextID, false);
184
185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
186
187 CallbackMapIter it = mCallbackMap.find(uContextID);
188 return (it == mCallbackMap.end()) ? false : true;
189}
190
191void Guest::callbackFreeUserData(void *pvData)
192{
193 if (pvData)
194 {
195 RTMemFree(pvData);
196 pvData = NULL;
197 }
198}
199
200int Guest::callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType,
201 void **ppvData, size_t *pcbData)
202{
203 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
204 /* pEnmType is optional. */
205 AssertPtrReturn(ppvData, VERR_INVALID_PARAMETER);
206 /* pcbData is optional. */
207
208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
209
210 CallbackMapIterConst it = mCallbackMap.find(uContextID);
211 if (it != mCallbackMap.end())
212 {
213 if (pEnmType)
214 *pEnmType = it->second.mType;
215
216 void *pvData = RTMemAlloc(it->second.cbData);
217 AssertPtrReturn(pvData, VERR_NO_MEMORY);
218 memcpy(pvData, it->second.pvData, it->second.cbData);
219 *ppvData = pvData;
220
221 if (pcbData)
222 *pcbData = it->second.cbData;
223
224 return VINF_SUCCESS;
225 }
226
227 return VERR_NOT_FOUND;
228}
229
230/* Does not do locking! Caller has to take care of it because the caller needs to
231 * modify the data ...*/
232void* Guest::callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData)
233{
234 AssertReturn(uContextID, NULL);
235 /* pcbData is optional. */
236
237 CallbackMapIterConst it = mCallbackMap.find(uContextID);
238 if (it != mCallbackMap.end())
239 {
240 if (pcbData)
241 *pcbData = it->second.cbData;
242 return it->second.pvData;
243 }
244
245 return NULL;
246}
247
248int Guest::callbackInit(PVBOXGUESTCTRL_CALLBACK pCallback, eVBoxGuestCtrlCallbackType enmType,
249 ComPtr<Progress> pProgress)
250{
251 AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
252 /* Everything else is optional. */
253
254 int rc = VINF_SUCCESS;
255 switch (enmType)
256 {
257 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
258 {
259 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
260 AssertPtrReturn(pData, VERR_NO_MEMORY);
261 RT_BZERO(pData, sizeof(CALLBACKDATAEXECSTATUS));
262 pCallback->cbData = sizeof(CALLBACKDATAEXECSTATUS);
263 pCallback->pvData = pData;
264 break;
265 }
266
267 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
268 {
269 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
270 AssertPtrReturn(pData, VERR_NO_MEMORY);
271 RT_BZERO(pData, sizeof(CALLBACKDATAEXECOUT));
272 pCallback->cbData = sizeof(CALLBACKDATAEXECOUT);
273 pCallback->pvData = pData;
274 break;
275 }
276
277 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
278 {
279 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
280 AssertPtrReturn(pData, VERR_NO_MEMORY);
281 RT_BZERO(pData, sizeof(PCALLBACKDATAEXECINSTATUS));
282 pCallback->cbData = sizeof(PCALLBACKDATAEXECINSTATUS);
283 pCallback->pvData = pData;
284 break;
285 }
286
287 default:
288 rc = VERR_INVALID_PARAMETER;
289 break;
290 }
291
292 if (RT_SUCCESS(rc))
293 {
294 /* Init/set common stuff. */
295 pCallback->mType = enmType;
296 pCallback->pProgress = pProgress;
297 }
298
299 return rc;
300}
301
302bool Guest::callbackIsCanceled(uint32_t uContextID)
303{
304 AssertReturn(uContextID, true);
305
306 Progress *pProgress = NULL;
307 {
308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
309
310 CallbackMapIterConst it = mCallbackMap.find(uContextID);
311 if (it != mCallbackMap.end())
312 pProgress = it->second.pProgress;
313 }
314
315 if (pProgress)
316 {
317 BOOL fCanceled = FALSE;
318 HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
319 if ( SUCCEEDED(hRC)
320 && !fCanceled)
321 {
322 return false;
323 }
324 }
325
326 return true; /* No progress / error means canceled. */
327}
328
329bool Guest::callbackIsComplete(uint32_t uContextID)
330{
331 AssertReturn(uContextID, true);
332
333 Progress *pProgress = NULL;
334 {
335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
336
337 CallbackMapIterConst it = mCallbackMap.find(uContextID);
338 if (it != mCallbackMap.end())
339 pProgress = it->second.pProgress;
340 }
341
342 if (pProgress)
343 {
344 BOOL fCompleted = FALSE;
345 HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
346 if ( SUCCEEDED(hRC)
347 && fCompleted)
348 {
349 return true;
350 }
351 }
352
353 return false;
354}
355
356int Guest::callbackMoveForward(uint32_t uContextID, const char *pszMessage)
357{
358 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
359 AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);
360
361 Progress *pProgress = NULL;
362 {
363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
364
365 CallbackMapIterConst it = mCallbackMap.find(uContextID);
366 if (it != mCallbackMap.end())
367 pProgress = it->second.pProgress;
368 }
369
370 if (pProgress)
371 {
372 HRESULT hr = pProgress->SetNextOperation(Bstr(pszMessage).raw(), 1 /* Weight */);
373 if (FAILED(hr))
374 return VERR_CANCELLED;
375
376 return VINF_SUCCESS;
377 }
378
379 return VERR_NOT_FOUND;
380}
381
382/**
383 * Notifies a specified callback about its final status.
384 *
385 * @return IPRT status code.
386 * @param uContextID
387 * @param iRC
388 * @param pszMessage
389 */
390int Guest::callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage)
391{
392 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
393 if (RT_FAILURE(iRC))
394 AssertReturn(pszMessage, VERR_INVALID_PARAMETER);
395
396 LogFlowFunc(("Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n",
397 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
398
399 Progress *pProgress = NULL;
400 {
401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
402
403 CallbackMapIterConst it = mCallbackMap.find(uContextID);
404 if (it != mCallbackMap.end())
405 pProgress = it->second.pProgress;
406 }
407
408#if 0
409 BOOL fCanceled = FALSE;
410 HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
411 if ( SUCCEEDED(hRC)
412 && fCanceled)
413 {
414 /* If progress already canceled do nothing here. */
415 return VINF_SUCCESS;
416 }
417#endif
418
419 if (pProgress)
420 {
421 /*
422 * Assume we didn't complete to make sure we clean up even if the
423 * following call fails.
424 */
425 BOOL fCompleted = FALSE;
426 HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
427 if ( SUCCEEDED(hRC)
428 && !fCompleted)
429 {
430 LogFlowFunc(("Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n",
431 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
432
433 /*
434 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
435 * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
436 * is disconnecting without having the chance to sending a status message before, so we
437 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
438 * progress object to become signalled.
439 */
440 if (RT_SUCCESS(iRC))
441 {
442 hRC = pProgress->notifyComplete(S_OK);
443 }
444 else
445 {
446
447 hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,
448 COM_IIDOF(IGuest),
449 Guest::getStaticComponentName(),
450 pszMessage);
451 }
452
453 LogFlowFunc(("Notified callback with CID=%u returned %Rhrc (0x%x)\n",
454 uContextID, hRC, hRC));
455 }
456 else
457 LogFlowFunc(("Callback with CID=%u already notified\n", uContextID));
458
459 /*
460 * Do *not* NULL pProgress here, because waiting function like executeProcess()
461 * will still rely on this object for checking whether they have to give up!
462 */
463 }
464 /* If pProgress is not found (anymore) that's fine.
465 * Might be destroyed already. */
466 return S_OK;
467}
468
469/**
470 * TODO
471 *
472 * @return IPRT status code.
473 * @param uPID
474 * @param iRC
475 * @param pszMessage
476 */
477int Guest::callbackNotifyAllForPID(uint32_t uPID, int iRC, const char *pszMessage)
478{
479 AssertReturn(uPID, VERR_INVALID_PARAMETER);
480
481 int vrc = VINF_SUCCESS;
482
483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
484
485 CallbackMapIter it;
486 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
487 {
488 switch (it->second.mType)
489 {
490 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
491 break;
492
493 /* When waiting for process output while the process is destroyed,
494 * make sure we also destroy the actual waiting operation (internal progress object)
495 * in order to not block the caller. */
496 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
497 {
498 PCALLBACKDATAEXECOUT pItData = (PCALLBACKDATAEXECOUT)it->second.pvData;
499 AssertPtr(pItData);
500 if (pItData->u32PID == uPID)
501 vrc = callbackNotifyEx(it->first, iRC, pszMessage);
502 break;
503 }
504
505 /* When waiting for injecting process input while the process is destroyed,
506 * make sure we also destroy the actual waiting operation (internal progress object)
507 * in order to not block the caller. */
508 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
509 {
510 PCALLBACKDATAEXECINSTATUS pItData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
511 AssertPtr(pItData);
512 if (pItData->u32PID == uPID)
513 vrc = callbackNotifyEx(it->first, iRC, pszMessage);
514 break;
515 }
516
517 default:
518 vrc = VERR_INVALID_PARAMETER;
519 AssertMsgFailed(("Unknown callback type %d, iRC=%d, message=%s\n",
520 it->second.mType, iRC, pszMessage ? pszMessage : "<No message given>"));
521 break;
522 }
523
524 if (RT_FAILURE(vrc))
525 break;
526 }
527
528 return vrc;
529}
530
531int Guest::callbackNotifyComplete(uint32_t uContextID)
532{
533 return callbackNotifyEx(uContextID, S_OK, NULL /* No message */);
534}
535
536/**
537 * Waits for a callback (using its context ID) to complete.
538 *
539 * @return IPRT status code.
540 * @param uContextID Context ID to wait for.
541 * @param lStage Stage to wait for. Specify -1 if no staging is present/required.
542 * Specifying a stage is only needed if there's a multi operation progress
543 * object to wait for.
544 * @param lTimeout Timeout (in ms) to wait for.
545 */
546int Guest::callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout)
547{
548 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
549
550 /*
551 * Wait for the HGCM low level callback until the process
552 * has been started (or something went wrong). This is necessary to
553 * get the PID.
554 */
555
556 int vrc = VINF_SUCCESS;
557 Progress *pProgress = NULL;
558 {
559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
560
561 CallbackMapIterConst it = mCallbackMap.find(uContextID);
562 if (it != mCallbackMap.end())
563 pProgress = it->second.pProgress;
564 else
565 vrc = VERR_NOT_FOUND;
566 }
567
568 if (RT_SUCCESS(vrc))
569 {
570 LogFlowFunc(("Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n",
571 uContextID, lStage, lTimeout));
572 HRESULT rc;
573 if (lStage < 0)
574 rc = pProgress->WaitForCompletion(lTimeout);
575 else
576 rc = pProgress->WaitForOperationCompletion((ULONG)lStage, lTimeout);
577 if (SUCCEEDED(rc))
578 {
579 if (!callbackIsComplete(uContextID))
580 vrc = callbackIsCanceled(uContextID)
581 ? VERR_CANCELLED : VINF_SUCCESS;
582 }
583 else
584 vrc = VERR_TIMEOUT;
585 }
586
587 LogFlowFunc(("Callback (CID=%u) completed with rc=%Rrc\n",
588 uContextID, vrc));
589 return vrc;
590}
591
592/**
593 * Static callback function for receiving updates on guest control commands
594 * from the guest. Acts as a dispatcher for the actual class instance.
595 *
596 * @returns VBox status code.
597 *
598 * @todo
599 *
600 */
601DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
602 uint32_t u32Function,
603 void *pvParms,
604 uint32_t cbParms)
605{
606 using namespace guestControl;
607
608 /*
609 * No locking, as this is purely a notification which does not make any
610 * changes to the object state.
611 */
612#ifdef DEBUG_andy
613 LogFlowFunc(("pvExtension=%p, u32Function=%d, pvParms=%p, cbParms=%d\n",
614 pvExtension, u32Function, pvParms, cbParms));
615#endif
616 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
617
618 int rc = VINF_SUCCESS;
619 switch (u32Function)
620 {
621 case GUEST_DISCONNECTED:
622 {
623 //LogFlowFunc(("GUEST_DISCONNECTED\n"));
624
625 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
626 AssertPtr(pCBData);
627 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);
628 AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
629
630 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);
631 break;
632 }
633
634 case GUEST_EXEC_SEND_STATUS:
635 {
636 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
637
638 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
639 AssertPtr(pCBData);
640 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
641 AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
642
643 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);
644 break;
645 }
646
647 case GUEST_EXEC_SEND_OUTPUT:
648 {
649 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
650
651 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);
652 AssertPtr(pCBData);
653 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);
654 AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
655
656 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
657 break;
658 }
659
660 case GUEST_EXEC_SEND_INPUT_STATUS:
661 {
662 //LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));
663
664 PCALLBACKDATAEXECINSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvParms);
665 AssertPtr(pCBData);
666 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbParms, VERR_INVALID_PARAMETER);
667 AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
668
669 rc = pGuest->notifyCtrlExecInStatus(u32Function, pCBData);
670 break;
671 }
672
673 default:
674 AssertMsgFailed(("Unknown guest control notification received, u32Function=%u\n", u32Function));
675 rc = VERR_INVALID_PARAMETER;
676 break;
677 }
678 return rc;
679}
680
681/* Function for handling the execution start/termination notification. */
682/* Callback can be called several times. */
683int Guest::notifyCtrlExecStatus(uint32_t u32Function,
684 PCALLBACKDATAEXECSTATUS pData)
685{
686 AssertReturn(u32Function, VERR_INVALID_PARAMETER);
687 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
688
689 uint32_t uContextID = pData->hdr.u32ContextID;
690 Assert(uContextID);
691
692 /* Scope write locks as much as possible. */
693 {
694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
695
696 LogFlowFunc(("Execution status (CID=%u, pData=0x%p)\n",
697 uContextID, pData));
698
699 PCALLBACKDATAEXECSTATUS pCallbackData =
700 (PCALLBACKDATAEXECSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
701 if (pCallbackData)
702 {
703 pCallbackData->u32PID = pData->u32PID;
704 pCallbackData->u32Status = pData->u32Status;
705 pCallbackData->u32Flags = pData->u32Flags;
706 /** @todo Copy void* buffer contents? */
707 }
708 /* If pCallbackData is NULL this might be an old request for which no user data
709 * might exist anymore. */
710 }
711
712 int vrc = VINF_SUCCESS; /* Function result. */
713 int rcCallback = VINF_SUCCESS; /* Callback result. */
714 Utf8Str errMsg;
715
716 /* Was progress canceled before? */
717 bool fCanceled = callbackIsCanceled(uContextID);
718 if (!fCanceled)
719 {
720 /* Handle process map. This needs to be done first in order to have a valid
721 * map in case some callback gets notified a bit below. */
722
723 /* Note: PIDs never get removed here in case the guest process signalled its
724 * end; instead the next call of GetProcessStatus() will remove the PID
725 * from the process map after we got the process' final (exit) status.
726 * See waitpid() for an example. */
727 if (pData->u32PID) /* Only add/change a process if it has a valid PID (>0). */
728 {
729 switch (pData->u32Status)
730 {
731 /* Interprete u32Flags as the guest process' exit code. */
732 case PROC_STS_TES:
733 case PROC_STS_TOK:
734 vrc = processSetStatus(pData->u32PID,
735 (ExecuteProcessStatus_T)pData->u32Status,
736 pData->u32Flags /* Exit code. */, 0 /* Flags. */);
737 break;
738 /* Just reach through flags. */
739 default:
740 vrc = processSetStatus(pData->u32PID,
741 (ExecuteProcessStatus_T)pData->u32Status,
742 0 /* Exit code. */, pData->u32Flags);
743 break;
744 }
745 }
746
747 /* Do progress handling. */
748 switch (pData->u32Status)
749 {
750 case PROC_STS_STARTED:
751 vrc = callbackMoveForward(uContextID, Guest::tr("Waiting for process to exit ..."));
752 LogRel(("Guest process (PID %u) started\n", pData->u32PID)); /** @todo Add process name */
753 break;
754
755 case PROC_STS_TEN: /* Terminated normally. */
756 vrc = callbackNotifyComplete(uContextID);
757 LogRel(("Guest process (PID %u) exited normally\n", pData->u32PID)); /** @todo Add process name */
758 break;
759
760 case PROC_STS_TEA: /* Terminated abnormally. */
761 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
762 pData->u32PID, pData->u32Flags)); /** @todo Add process name */
763 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
764 pData->u32Flags);
765 rcCallback = VERR_GENERAL_FAILURE; /** @todo */
766 break;
767
768 case PROC_STS_TES: /* Terminated through signal. */
769 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
770 pData->u32PID, pData->u32Flags)); /** @todo Add process name */
771 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
772 pData->u32Flags);
773 rcCallback = VERR_GENERAL_FAILURE; /** @todo */
774 break;
775
776 case PROC_STS_TOK:
777 LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */
778 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
779 rcCallback = VERR_TIMEOUT;
780 break;
781
782 case PROC_STS_TOA:
783 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */
784 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
785 rcCallback = VERR_TIMEOUT;
786 break;
787
788 case PROC_STS_DWN:
789 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pData->u32PID)); /** @todo Add process name */
790 /*
791 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
792 * our progress object. This is helpful for waiters which rely on the success of our progress object
793 * even if the executed process was killed because the system/VBoxService is shutting down.
794 *
795 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
796 */
797 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
798 {
799 vrc = callbackNotifyComplete(uContextID);
800 }
801 else
802 {
803 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
804 rcCallback = VERR_CANCELLED;
805 }
806 break;
807
808 case PROC_STS_ERROR:
809 if (pData->u32PID)
810 {
811 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
812 pData->u32PID, pData->u32Flags)); /** @todo Add process name */
813 }
814 else
815 {
816 switch (pData->u32Flags)
817 {
818 case VERR_MAX_PROCS_REACHED:
819 LogRel(("Guest process could not be started because maximum number of parallel guest processes has been reached\n"));
820 break;
821
822 default:
823 LogRel(("Guest process could not be started because of rc=%Rrc\n",
824 pData->u32Flags));
825 }
826
827 }
828 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pData->u32Flags);
829 rcCallback = pData->u32Flags; /* Report back rc. */
830 break;
831
832 default:
833 vrc = VERR_INVALID_PARAMETER;
834 break;
835 }
836 }
837 else
838 {
839 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
840 rcCallback = VERR_CANCELLED;
841 }
842
843 /* Do we need to handle the callback error? */
844 if (RT_FAILURE(rcCallback))
845 {
846 AssertMsg(!errMsg.isEmpty(), ("Error message must not be empty!\n"));
847
848 /* Notify all callbacks which are still waiting on something
849 * which is related to the current PID. */
850 if (pData->u32PID)
851 {
852 int rc2 = callbackNotifyAllForPID(pData->u32PID, rcCallback, errMsg.c_str());
853 if (RT_FAILURE(rc2))
854 {
855 LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
856 pData->u32PID));
857 if (RT_SUCCESS(vrc))
858 vrc = rc2;
859 }
860 }
861
862 /* Let the caller know what went wrong ... */
863 int rc2 = callbackNotifyEx(uContextID, rcCallback, errMsg.c_str());
864 if (RT_FAILURE(rc2))
865 {
866 LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
867 uContextID, pData->u32PID));
868 if (RT_SUCCESS(vrc))
869 vrc = rc2;
870 }
871 LogFlowFunc(("Process (CID=%u, status=%u) reported: %s\n",
872 uContextID, pData->u32Status, errMsg.c_str()));
873 }
874 LogFlowFunc(("Returned with rc=%Rrc, rcCallback=%Rrc\n",
875 vrc, rcCallback));
876 return vrc;
877}
878
879/* Function for handling the execution output notification. */
880int Guest::notifyCtrlExecOut(uint32_t u32Function,
881 PCALLBACKDATAEXECOUT pData)
882{
883 AssertReturn(u32Function, VERR_INVALID_PARAMETER);
884 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
885
886 uint32_t uContextID = pData->hdr.u32ContextID;
887 Assert(uContextID);
888
889 /* Scope write locks as much as possible. */
890 {
891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
892
893 LogFlowFunc(("Output status (CID=%u, pData=0x%p)\n",
894 uContextID, pData));
895
896 PCALLBACKDATAEXECOUT pCallbackData =
897 (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
898 if (pCallbackData)
899 {
900 pCallbackData->u32PID = pData->u32PID;
901 pCallbackData->u32HandleId = pData->u32HandleId;
902 pCallbackData->u32Flags = pData->u32Flags;
903
904 /* Make sure we really got something! */
905 if ( pData->cbData
906 && pData->pvData)
907 {
908 callbackFreeUserData(pCallbackData->pvData);
909
910 /* Allocate data buffer and copy it */
911 pCallbackData->pvData = RTMemAlloc(pData->cbData);
912 pCallbackData->cbData = pData->cbData;
913
914 AssertReturn(pCallbackData->pvData, VERR_NO_MEMORY);
915 memcpy(pCallbackData->pvData, pData->pvData, pData->cbData);
916 }
917 else /* Nothing received ... */
918 {
919 pCallbackData->pvData = NULL;
920 pCallbackData->cbData = 0;
921 }
922 }
923 /* If pCallbackData is NULL this might be an old request for which no user data
924 * might exist anymore. */
925 }
926
927 int vrc;
928 if (callbackIsCanceled(pData->u32PID))
929 {
930 vrc = callbackNotifyEx(uContextID, VERR_CANCELLED,
931 Guest::tr("The output operation was canceled"));
932 }
933 else
934 vrc = callbackNotifyComplete(uContextID);
935
936 return vrc;
937}
938
939/* Function for handling the execution input status notification. */
940int Guest::notifyCtrlExecInStatus(uint32_t u32Function,
941 PCALLBACKDATAEXECINSTATUS pData)
942{
943 AssertReturn(u32Function, VERR_INVALID_PARAMETER);
944 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
945
946 uint32_t uContextID = pData->hdr.u32ContextID;
947 Assert(uContextID);
948
949 /* Scope write locks as much as possible. */
950 {
951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
952
953 LogFlowFunc(("Input status (CID=%u, pData=0x%p)\n",
954 uContextID, pData));
955
956 PCALLBACKDATAEXECINSTATUS pCallbackData =
957 (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
958 if (pCallbackData)
959 {
960 /* Save bytes processed. */
961 pCallbackData->cbProcessed = pData->cbProcessed;
962 pCallbackData->u32Status = pData->u32Status;
963 pCallbackData->u32Flags = pData->u32Flags;
964 pCallbackData->u32PID = pData->u32PID;
965 }
966 /* If pCallbackData is NULL this might be an old request for which no user data
967 * might exist anymore. */
968 }
969
970 return callbackNotifyComplete(uContextID);
971}
972
973int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
974 PCALLBACKDATACLIENTDISCONNECTED pData)
975{
976 /* u32Function is 0. */
977 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
978
979 uint32_t uContextID = pData->hdr.u32ContextID;
980 Assert(uContextID);
981
982 LogFlowFunc(("Client disconnected (CID=%u)\n,", uContextID));
983
984 return callbackNotifyEx(uContextID, VERR_CANCELLED,
985 Guest::tr("Client disconnected"));
986}
987
988/**
989 * Gets guest process information. Removes the process from the map
990 * after the process was marked as exited/terminated.
991 *
992 * @return IPRT status code.
993 * @param u32PID
994 * @param pProcess
995 */
996int Guest::processGetStatus(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
997{
998 AssertReturn(u32PID, VERR_INVALID_PARAMETER);
999
1000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
1003 if (it != mGuestProcessMap.end())
1004 {
1005 if (pProcess)
1006 {
1007 pProcess->mStatus = it->second.mStatus;
1008 pProcess->mExitCode = it->second.mExitCode;
1009 pProcess->mFlags = it->second.mFlags;
1010 }
1011
1012 /* If the is marked as stopped/terminated
1013 * remove it from the map. */
1014 if (it->second.mStatus != ExecuteProcessStatus_Started)
1015 mGuestProcessMap.erase(it);
1016
1017 return VINF_SUCCESS;
1018 }
1019
1020 return VERR_NOT_FOUND;
1021}
1022
1023int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
1024{
1025 AssertReturn(u32PID, VERR_INVALID_PARAMETER);
1026
1027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
1030 if (it != mGuestProcessMap.end())
1031 {
1032 it->second.mStatus = enmStatus;
1033 it->second.mExitCode = uExitCode;
1034 it->second.mFlags = uFlags;
1035 }
1036 else
1037 {
1038 VBOXGUESTCTRL_PROCESS process;
1039
1040 process.mStatus = enmStatus;
1041 process.mExitCode = uExitCode;
1042 process.mFlags = uFlags;
1043
1044 mGuestProcessMap[u32PID] = process;
1045 }
1046
1047 return VINF_SUCCESS;
1048}
1049
1050HRESULT Guest::handleErrorCompletion(int rc)
1051{
1052 HRESULT hRC;
1053 if (rc == VERR_NOT_FOUND)
1054 hRC = setErrorNoLog(VBOX_E_VM_ERROR,
1055 tr("VMM device is not available (is the VM running?)"));
1056 else if (rc == VERR_CANCELLED)
1057 hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,
1058 tr("Process execution has been canceled"));
1059 else if (rc == VERR_TIMEOUT)
1060 hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,
1061 tr("The guest did not respond within time"));
1062 else
1063 hRC = setErrorNoLog(E_UNEXPECTED,
1064 tr("Waiting for completion failed with error %Rrc"), rc);
1065 return hRC;
1066}
1067
1068HRESULT Guest::handleErrorHGCM(int rc)
1069{
1070 HRESULT hRC;
1071 if (rc == VERR_INVALID_VM_HANDLE)
1072 hRC = setErrorNoLog(VBOX_E_VM_ERROR,
1073 tr("VMM device is not available (is the VM running?)"));
1074 else if (rc == VERR_NOT_FOUND)
1075 hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,
1076 tr("The guest execution service is not ready (yet)"));
1077 else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
1078 hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,
1079 tr("The guest execution service is not available"));
1080 else /* HGCM call went wrong. */
1081 hRC = setErrorNoLog(E_UNEXPECTED,
1082 tr("The HGCM call failed with error %Rrc"), rc);
1083 return hRC;
1084}
1085#endif /* VBOX_WITH_GUEST_CONTROL */
1086
1087STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
1088 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1089 IN_BSTR aUsername, IN_BSTR aPassword,
1090 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
1091{
1092/** @todo r=bird: Eventually we should clean up all the timeout parameters
1093 * in the API and have the same way of specifying infinite waits! */
1094#ifndef VBOX_WITH_GUEST_CONTROL
1095 ReturnComNotImplemented();
1096#else /* VBOX_WITH_GUEST_CONTROL */
1097 using namespace guestControl;
1098
1099 CheckComArgStrNotEmptyOrNull(aCommand);
1100 CheckComArgOutPointerValid(aPID);
1101 CheckComArgOutPointerValid(aProgress);
1102
1103 /* Do not allow anonymous executions (with system rights). */
1104 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))
1105 return setError(E_INVALIDARG, tr("No user name specified"));
1106
1107 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1108 Utf8Str(aCommand).c_str(), Utf8Str(aUsername).c_str()));
1109
1110 return executeProcessInternal(aCommand, aFlags, ComSafeArrayInArg(aArguments),
1111 ComSafeArrayInArg(aEnvironment),
1112 aUsername, aPassword, aTimeoutMS, aPID, aProgress, NULL /* rc */);
1113#endif
1114}
1115
1116#ifdef VBOX_WITH_GUEST_CONTROL
1117/**
1118 * Executes and waits for an internal tool (that is, a tool which is integrated into
1119 * VBoxService, beginning with "vbox_" (e.g. "vbox_ls")) to finish its operation.
1120 *
1121 * @return HRESULT
1122 * @param aTool Name of tool to execute.
1123 * @param aDescription Friendly description of the operation.
1124 * @param aFlags Execution flags.
1125 * @param aUsername Username to execute tool under.
1126 * @param aPassword The user's password.
1127 * @param uFlagsToAdd ExecuteProcessFlag flags to add to the execution operation.
1128 * @param aProgress Pointer which receives the tool's progress object. Optional.
1129 * @param aPID Pointer which receives the tool's PID. Optional.
1130 */
1131HRESULT Guest::executeAndWaitForTool(IN_BSTR aTool, IN_BSTR aDescription,
1132 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1133 IN_BSTR aUsername, IN_BSTR aPassword,
1134 ULONG uFlagsToAdd,
1135 GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr,
1136 IProgress **aProgress, ULONG *aPID)
1137{
1138 ComPtr<IProgress> progressTool;
1139 ULONG uPID;
1140
1141 ULONG uFlags = ExecuteProcessFlag_Hidden
1142 | ExecuteProcessFlag_WaitForProcessStartOnly;
1143 if (uFlagsToAdd)
1144 uFlags |= uFlagsToAdd;
1145
1146 bool fWaitForOutput = false;
1147 if ( ( (uFlags & ExecuteProcessFlag_WaitForStdOut)
1148 && pObjStdOut)
1149 || ( (uFlags & ExecuteProcessFlag_WaitForStdErr)
1150 && pObjStdErr))
1151 {
1152 fWaitForOutput = true;
1153 }
1154
1155 HRESULT rc = ExecuteProcess(aTool,
1156 uFlags,
1157 ComSafeArrayInArg(aArguments),
1158 ComSafeArrayInArg(aEnvironment),
1159 aUsername, aPassword,
1160 0 /* No timeout. */,
1161 &uPID, progressTool.asOutParam());
1162 if ( SUCCEEDED(rc)
1163 && fWaitForOutput)
1164 {
1165 BOOL fCompleted;
1166 while ( SUCCEEDED(progressTool->COMGETTER(Completed)(&fCompleted))
1167 && !fCompleted)
1168 {
1169 BOOL fCanceled;
1170 rc = progressTool->COMGETTER(Canceled)(&fCanceled);
1171 AssertComRC(rc);
1172 if (fCanceled)
1173 {
1174 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1175 tr("%s was cancelled"), Utf8Str(aDescription).c_str());
1176 break;
1177 }
1178
1179 if ( (uFlags & ExecuteProcessFlag_WaitForStdOut)
1180 && pObjStdOut)
1181 {
1182 rc = executeStreamParse(uPID, ProcessOutputFlag_None /* StdOut */, *pObjStdOut);
1183 }
1184
1185 if ( (uFlags & ExecuteProcessFlag_WaitForStdErr)
1186 && pObjStdErr)
1187 {
1188 rc = executeStreamParse(uPID, ProcessOutputFlag_StdErr, *pObjStdErr);
1189 }
1190
1191 if (FAILED(rc))
1192 break;
1193 }
1194 }
1195
1196 if (SUCCEEDED(rc))
1197 {
1198 if (aProgress)
1199 {
1200 /* Return the progress to the caller. */
1201 progressTool.queryInterfaceTo(aProgress);
1202 }
1203
1204 if (aPID)
1205 *aPID = uPID;
1206 }
1207
1208 return rc;
1209}
1210
1211HRESULT Guest::executeProcessResult(const char *pszCommand, const char *pszUser, ULONG ulTimeout,
1212 PCALLBACKDATAEXECSTATUS pExecStatus, ULONG *puPID)
1213{
1214 AssertPtrReturn(pExecStatus, E_INVALIDARG);
1215 AssertPtrReturn(puPID, E_INVALIDARG);
1216
1217 HRESULT rc = S_OK;
1218
1219 /* Did we get some status? */
1220 switch (pExecStatus->u32Status)
1221 {
1222 case PROC_STS_STARTED:
1223 /* Process is (still) running; get PID. */
1224 *puPID = pExecStatus->u32PID;
1225 break;
1226
1227 /* In any other case the process either already
1228 * terminated or something else went wrong, so no PID ... */
1229 case PROC_STS_TEN: /* Terminated normally. */
1230 case PROC_STS_TEA: /* Terminated abnormally. */
1231 case PROC_STS_TES: /* Terminated through signal. */
1232 case PROC_STS_TOK:
1233 case PROC_STS_TOA:
1234 case PROC_STS_DWN:
1235 /*
1236 * Process (already) ended, but we want to get the
1237 * PID anyway to retrieve the output in a later call.
1238 */
1239 *puPID = pExecStatus->u32PID;
1240 break;
1241
1242 case PROC_STS_ERROR:
1243 {
1244 int vrc = pExecStatus->u32Flags; /* u32Flags member contains IPRT error code. */
1245 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1246 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1247 tr("The file '%s' was not found on guest"), pszCommand);
1248 else if (vrc == VERR_PATH_NOT_FOUND)
1249 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1250 tr("The path to file '%s' was not found on guest"), pszCommand);
1251 else if (vrc == VERR_BAD_EXE_FORMAT)
1252 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1253 tr("The file '%s' is not an executable format on guest"), pszCommand);
1254 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1255 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1256 tr("The specified user '%s' was not able to logon on guest"), pszUser);
1257 else if (vrc == VERR_TIMEOUT)
1258 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1259 tr("The guest did not respond within time (%ums)"), ulTimeout);
1260 else if (vrc == VERR_CANCELLED)
1261 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1262 tr("The execution operation was canceled"));
1263 else if (vrc == VERR_PERMISSION_DENIED)
1264 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1265 tr("Invalid user/password credentials"));
1266 else if (vrc == VERR_MAX_PROCS_REACHED)
1267 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1268 tr("Concurrent guest process limit is reached"));
1269 else
1270 {
1271 if (pExecStatus && pExecStatus->u32Status == PROC_STS_ERROR)
1272 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1273 tr("Process could not be started: %Rrc"), pExecStatus->u32Flags);
1274 else
1275 rc = setErrorNoLog(E_UNEXPECTED,
1276 tr("The service call failed with error %Rrc"), vrc);
1277 }
1278 }
1279 break;
1280
1281 case PROC_STS_UNDEFINED: /* . */
1282 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1283 tr("The operation did not complete within time"));
1284 break;
1285
1286 default:
1287 AssertReleaseMsgFailed(("Process (PID %u) reported back an undefined state!\n",
1288 pExecStatus->u32PID));
1289 rc = E_UNEXPECTED;
1290 break;
1291 }
1292
1293 return rc;
1294}
1295
1296/**
1297 * TODO
1298 *
1299 * @return HRESULT
1300 * @param aObjName
1301 * @param pStreamBlock
1302 * @param pObjInfo
1303 * @param enmAddAttribs
1304 */
1305HRESULT Guest::executeStreamQueryFsObjInfo(IN_BSTR aObjName,
1306 GuestProcessStreamBlock &streamBlock,
1307 PRTFSOBJINFO pObjInfo,
1308 RTFSOBJATTRADD enmAddAttribs)
1309{
1310 HRESULT rc = S_OK;
1311 Utf8Str Utf8ObjName(aObjName);
1312
1313 int64_t iVal;
1314 int vrc = streamBlock.GetInt64Ex("st_size", &iVal);
1315 if (RT_SUCCESS(vrc))
1316 pObjInfo->cbObject = iVal;
1317 else
1318 rc = setError(VBOX_E_IPRT_ERROR,
1319 tr("Unable to retrieve size for \"%s\" (%Rrc)"),
1320 Utf8ObjName.c_str(), vrc);
1321 /** @todo Add more stuff! */
1322 return rc;
1323}
1324
1325/**
1326 * Tries to drain the guest's output and fill it into
1327 * a guest process stream object for later usage.
1328 *
1329 * @todo What's about specifying stderr?
1330 * @return IPRT status code.
1331 * @param aPID PID of process to get the output from.
1332 * @param aFlags Which stream to drain (stdout or stderr).
1333 * @param stream Reference to guest process stream to fill.
1334 */
1335int Guest::executeStreamDrain(ULONG aPID, ULONG aFlags, GuestProcessStream &stream)
1336{
1337 AssertReturn(aPID, VERR_INVALID_PARAMETER);
1338
1339 int rc = VINF_SUCCESS;
1340 for (;;)
1341 {
1342 SafeArray<BYTE> aData;
1343 HRESULT hr = getProcessOutputInternal(aPID, aFlags,
1344 0 /* Infinite timeout */,
1345 _64K, ComSafeArrayAsOutParam(aData), &rc);
1346 if (SUCCEEDED(hr))
1347 {
1348 if (aData.size())
1349 {
1350 rc = stream.AddData(aData.raw(), aData.size());
1351 if (RT_UNLIKELY(RT_FAILURE(rc)))
1352 break;
1353 }
1354
1355 continue; /* Try one more time. */
1356 }
1357 else /* No more output and/or error! */
1358 {
1359 if (rc == VERR_NOT_FOUND)
1360 rc = VINF_SUCCESS;
1361 break;
1362 }
1363 }
1364
1365 return rc;
1366}
1367
1368/**
1369 * Tries to retrieve the next stream block of a given stream and
1370 * drains the process output only as much as needed to get this next
1371 * stream block.
1372 *
1373 * @return IPRT status code.
1374 * @param ulPID
1375 * @param ulFlags
1376 * @param stream
1377 * @param streamBlock
1378 */
1379int Guest::executeStreamGetNextBlock(ULONG ulPID,
1380 ULONG ulFlags,
1381 GuestProcessStream &stream,
1382 GuestProcessStreamBlock &streamBlock)
1383{
1384 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
1385
1386 bool fDrainStream = true;
1387 int rc;
1388 do
1389 {
1390 rc = stream.ParseBlock(streamBlock);
1391 if (RT_FAILURE(rc)) /* More data needed or empty buffer? */
1392 {
1393 if (fDrainStream)
1394 {
1395 SafeArray<BYTE> aData;
1396 HRESULT hr = getProcessOutputInternal(ulPID, ulFlags,
1397 0 /* Infinite timeout */,
1398 _64K, ComSafeArrayAsOutParam(aData), &rc);
1399 if (SUCCEEDED(hr))
1400 {
1401 if (aData.size())
1402 {
1403 rc = stream.AddData(aData.raw(), aData.size());
1404 if (RT_UNLIKELY(RT_FAILURE(rc)))
1405 break;
1406 }
1407
1408 continue; /* Try one more time. */
1409 }
1410 else
1411 {
1412 /* No more output to drain from stream. */
1413 if (rc == VERR_NOT_FOUND)
1414 {
1415 fDrainStream = false;
1416 continue;
1417 }
1418
1419 break;
1420 }
1421 }
1422 else
1423 {
1424 /* We haved drained the stream as much as we can and reached the
1425 * end of our stream buffer -- that means that there simply is no
1426 * stream block anymore, which is ok. */
1427 if (rc == VERR_NO_DATA)
1428 rc = VINF_SUCCESS;
1429 break;
1430 }
1431 }
1432 }
1433 while (!streamBlock.GetCount());
1434
1435 return rc;
1436}
1437
1438/**
1439 * Tries to get the next upcoming value block from a started guest process
1440 * by first draining its output and then processing the received guest stream.
1441 *
1442 * @return IPRT status code.
1443 * @param ulPID PID of process to get/parse the output from.
1444 * @param ulFlags ?
1445 * @param stream Reference to process stream object to use.
1446 * @param streamBlock Reference that receives the next stream block data.
1447 *
1448 */
1449int Guest::executeStreamParseNextBlock(ULONG ulPID,
1450 ULONG ulFlags,
1451 GuestProcessStream &stream,
1452 GuestProcessStreamBlock &streamBlock)
1453{
1454 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
1455
1456 int rc;
1457 do
1458 {
1459 rc = stream.ParseBlock(streamBlock);
1460 if (RT_FAILURE(rc))
1461 break;
1462 }
1463 while (!streamBlock.GetCount());
1464
1465 return rc;
1466}
1467
1468/**
1469 * Gets output from a formerly started guest process, tries to parse all of its guest
1470 * stream (as long as data is available) and returns a stream object which can contain
1471 * multiple stream blocks (which in turn then can contain key=value pairs).
1472 *
1473 * @return HRESULT
1474 * @param ulPID PID of process to get/parse the output from.
1475 * @param ulFlags ?
1476 * @param streamObjects Reference to a guest stream object structure for
1477 * storing the parsed data.
1478 */
1479HRESULT Guest::executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects)
1480{
1481 GuestProcessStream stream;
1482 int rc = executeStreamDrain(ulPID, ulFlags, stream);
1483 if (RT_SUCCESS(rc))
1484 {
1485 do
1486 {
1487 /* Try to parse the stream output we gathered until now. If we still need more
1488 * data the parsing routine will tell us and we just do another poll round. */
1489 GuestProcessStreamBlock curBlock;
1490 rc = executeStreamParseNextBlock(ulPID, ulFlags, stream, curBlock);
1491 if (RT_SUCCESS(rc))
1492 streamObjects.push_back(curBlock);
1493 } while (RT_SUCCESS(rc));
1494
1495 if (rc == VERR_NO_DATA) /* End of data reached. */
1496 rc = VINF_SUCCESS;
1497 }
1498
1499 if (RT_FAILURE(rc))
1500 return setError(VBOX_E_IPRT_ERROR,
1501 tr("Error while parsing guest output (%Rrc)"), rc);
1502 return rc;
1503}
1504
1505/**
1506 * Does busy waiting on a formerly started guest process.
1507 *
1508 * @return HRESULT
1509 * @param uPID PID of guest process to wait for.
1510 * @param uTimeoutMS Waiting timeout (in ms). Specify 0 for an infinite timeout.
1511 * @param pRetStatus Pointer which receives current process status after the change.
1512 * Optional.
1513 * @param puRetExitCode Pointer which receives the final exit code in case of guest process
1514 * termination. Optional.
1515 */
1516HRESULT Guest::executeWaitForStatusChange(ULONG uPID, ULONG uTimeoutMS,
1517 ExecuteProcessStatus_T *pRetStatus, ULONG *puRetExitCode)
1518{
1519 if (uTimeoutMS == 0)
1520 uTimeoutMS = UINT32_MAX;
1521
1522 uint64_t u64StartMS = RTTimeMilliTS();
1523
1524 HRESULT hRC;
1525 ULONG uExitCode, uRetFlags;
1526 ExecuteProcessStatus_T curStatus;
1527 hRC = GetProcessStatus(uPID, &uExitCode, &uRetFlags, &curStatus);
1528 if (FAILED(hRC))
1529 return hRC;
1530
1531 do
1532 {
1533 if ( uTimeoutMS != UINT32_MAX
1534 && RTTimeMilliTS() - u64StartMS > uTimeoutMS)
1535 {
1536 hRC = setError(VBOX_E_IPRT_ERROR,
1537 tr("The process (PID %u) did not change its status within time (%ums)"),
1538 uPID, uTimeoutMS);
1539 break;
1540 }
1541 hRC = GetProcessStatus(uPID, &uExitCode, &uRetFlags, &curStatus);
1542 if (FAILED(hRC))
1543 break;
1544 RTThreadSleep(100);
1545 } while(*pRetStatus == curStatus);
1546
1547 if (SUCCEEDED(hRC))
1548 {
1549 if (pRetStatus)
1550 *pRetStatus = curStatus;
1551 if (puRetExitCode)
1552 *puRetExitCode = uExitCode;
1553 }
1554 return hRC;
1555}
1556
1557/**
1558 * Does the actual guest process execution, internal function.
1559 *
1560 * @return HRESULT
1561 * @param aCommand Command line to execute.
1562 * @param aFlags Execution flags.
1563 * @param Username Username to execute the process with.
1564 * @param aPassword The user's password.
1565 * @param aTimeoutMS Timeout (in ms) to wait for the execution operation.
1566 * @param aPID Pointer that receives the guest process' PID.
1567 * @param aProgress Pointer that receives the guest process' progress object.
1568 * @param pRC Pointer that receives the internal IPRT return code. Optional.
1569 */
1570HRESULT Guest::executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
1571 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1572 IN_BSTR aUsername, IN_BSTR aPassword,
1573 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC)
1574{
1575/** @todo r=bird: Eventually we should clean up all the timeout parameters
1576 * in the API and have the same way of specifying infinite waits! */
1577 using namespace guestControl;
1578
1579 AutoCaller autoCaller(this);
1580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1581
1582 /* Validate flags. */
1583 if (aFlags != ExecuteProcessFlag_None)
1584 {
1585 if ( !(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
1586 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1587 && !(aFlags & ExecuteProcessFlag_Hidden)
1588 && !(aFlags & ExecuteProcessFlag_NoProfile)
1589 && !(aFlags & ExecuteProcessFlag_WaitForStdOut)
1590 && !(aFlags & ExecuteProcessFlag_WaitForStdErr))
1591 {
1592 if (pRC)
1593 *pRC = VERR_INVALID_PARAMETER;
1594 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1595 }
1596 }
1597
1598 HRESULT rc = S_OK;
1599
1600 try
1601 {
1602 /*
1603 * Create progress object. Note that this is a multi operation
1604 * object to perform the following steps:
1605 * - Operation 1 (0): Create/start process.
1606 * - Operation 2 (1): Wait for process to exit.
1607 * If this progress completed successfully (S_OK), the process
1608 * started and exited normally. In any other case an error/exception
1609 * occurred.
1610 */
1611 ComObjPtr <Progress> pProgress;
1612 rc = pProgress.createObject();
1613 if (SUCCEEDED(rc))
1614 {
1615 rc = pProgress->init(static_cast<IGuest*>(this),
1616 Bstr(tr("Executing process")).raw(),
1617 TRUE,
1618 2, /* Number of operations. */
1619 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1620 }
1621 ComAssertComRC(rc);
1622
1623 /*
1624 * Prepare process execution.
1625 */
1626 int vrc = VINF_SUCCESS;
1627 Utf8Str Utf8Command(aCommand);
1628
1629 /* Adjust timeout. If set to 0, we define
1630 * an infinite timeout. */
1631 if (aTimeoutMS == 0)
1632 aTimeoutMS = UINT32_MAX;
1633
1634 /* Prepare arguments. */
1635 char **papszArgv = NULL;
1636 uint32_t uNumArgs = 0;
1637 if (aArguments)
1638 {
1639 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1640 uNumArgs = args.size();
1641 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1642 AssertReturn(papszArgv, E_OUTOFMEMORY);
1643 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1644 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1645 papszArgv[uNumArgs] = NULL;
1646 }
1647
1648 Utf8Str Utf8UserName(aUsername);
1649 Utf8Str Utf8Password(aPassword);
1650 if (RT_SUCCESS(vrc))
1651 {
1652 uint32_t uContextID = 0;
1653
1654 char *pszArgs = NULL;
1655 if (uNumArgs > 0)
1656 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1657 if (RT_SUCCESS(vrc))
1658 {
1659 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1660
1661 /* Prepare environment. */
1662 void *pvEnv = NULL;
1663 uint32_t uNumEnv = 0;
1664 uint32_t cbEnv = 0;
1665 if (aEnvironment)
1666 {
1667 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1668
1669 for (unsigned i = 0; i < env.size(); i++)
1670 {
1671 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1672 if (RT_FAILURE(vrc))
1673 break;
1674 }
1675 }
1676
1677 if (RT_SUCCESS(vrc))
1678 {
1679 VBOXGUESTCTRL_CALLBACK callback;
1680 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_START, pProgress);
1681 if (RT_SUCCESS(vrc))
1682 {
1683 /* Allocate and assign payload. */
1684 callback.cbData = sizeof(CALLBACKDATAEXECSTATUS);
1685 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(callback.cbData);
1686 AssertReturn(pData, E_OUTOFMEMORY);
1687 RT_BZERO(pData, callback.cbData);
1688 callback.pvData = pData;
1689 }
1690
1691 if (RT_SUCCESS(vrc))
1692 vrc = callbackAdd(&callback, &uContextID);
1693
1694 if (RT_SUCCESS(vrc))
1695 {
1696 VBOXHGCMSVCPARM paParms[15];
1697 int i = 0;
1698 paParms[i++].setUInt32(uContextID);
1699 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1700 paParms[i++].setUInt32(aFlags);
1701 paParms[i++].setUInt32(uNumArgs);
1702 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1703 paParms[i++].setUInt32(uNumEnv);
1704 paParms[i++].setUInt32(cbEnv);
1705 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1706 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1707 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1708
1709 /*
1710 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1711 * until the process was started - the process itself then gets an infinite timeout for execution.
1712 * This is handy when we want to start a process inside a worker thread within a certain timeout
1713 * but let the started process perform lengthly operations then.
1714 */
1715 if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1716 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1717 else
1718 paParms[i++].setUInt32(aTimeoutMS);
1719
1720 VMMDev *pVMMDev = NULL;
1721 {
1722 /* Make sure mParent is valid, so set the read lock while using.
1723 * Do not keep this lock while doing the actual call, because in the meanwhile
1724 * another thread could request a write lock which would be a bad idea ... */
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 /* Forward the information to the VMM device. */
1728 AssertPtr(mParent);
1729 pVMMDev = mParent->getVMMDev();
1730 }
1731
1732 if (pVMMDev)
1733 {
1734 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1735 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1736 i, paParms);
1737 }
1738 else
1739 vrc = VERR_INVALID_VM_HANDLE;
1740 }
1741 RTMemFree(pvEnv);
1742 }
1743 RTStrFree(pszArgs);
1744 }
1745
1746 if (RT_SUCCESS(vrc))
1747 {
1748 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
1749
1750 /*
1751 * Wait for the HGCM low level callback until the process
1752 * has been started (or something went wrong). This is necessary to
1753 * get the PID.
1754 */
1755
1756 PCALLBACKDATAEXECSTATUS pExecStatus = NULL;
1757
1758 /*
1759 * Wait for the first stage (=0) to complete (that is starting the process).
1760 */
1761 vrc = callbackWaitForCompletion(uContextID, 0 /* Stage */, aTimeoutMS);
1762 if (RT_SUCCESS(vrc))
1763 {
1764 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
1765 (void**)&pExecStatus, NULL /* Don't need the size. */);
1766 if (RT_SUCCESS(vrc))
1767 {
1768 rc = executeProcessResult(Utf8Command.c_str(), Utf8UserName.c_str(), aTimeoutMS,
1769 pExecStatus, aPID);
1770 callbackFreeUserData(pExecStatus);
1771 }
1772 else
1773 {
1774 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1775 tr("Unable to retrieve process execution status data"));
1776 }
1777 }
1778 else
1779 rc = handleErrorCompletion(vrc);
1780
1781 /*
1782 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1783 * else (like end of process) ...
1784 */
1785 }
1786 else
1787 rc = handleErrorHGCM(vrc);
1788
1789 for (unsigned i = 0; i < uNumArgs; i++)
1790 RTMemFree(papszArgv[i]);
1791 RTMemFree(papszArgv);
1792 }
1793
1794 if (SUCCEEDED(rc))
1795 {
1796 /* Return the progress to the caller. */
1797 pProgress.queryInterfaceTo(aProgress);
1798 }
1799 else
1800 {
1801 if (!pRC) /* Skip logging internal calls. */
1802 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1803 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1804 }
1805
1806 if (pRC)
1807 *pRC = vrc;
1808 }
1809 catch (std::bad_alloc &)
1810 {
1811 rc = E_OUTOFMEMORY;
1812 }
1813 return rc;
1814}
1815#endif /* VBOX_WITH_GUEST_CONTROL */
1816
1817STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)
1818{
1819#ifndef VBOX_WITH_GUEST_CONTROL
1820 ReturnComNotImplemented();
1821#else /* VBOX_WITH_GUEST_CONTROL */
1822 using namespace guestControl;
1823
1824 CheckComArgExpr(aPID, aPID > 0);
1825 CheckComArgOutPointerValid(aBytesWritten);
1826
1827 /* Validate flags. */
1828 if (aFlags)
1829 {
1830 if (!(aFlags & ProcessInputFlag_EndOfFile))
1831 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1832 }
1833
1834 AutoCaller autoCaller(this);
1835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1836
1837 HRESULT rc = S_OK;
1838
1839 try
1840 {
1841 VBOXGUESTCTRL_PROCESS process;
1842 int vrc = processGetStatus(aPID, &process);
1843 if (RT_SUCCESS(vrc))
1844 {
1845 /* PID exists; check if process is still running. */
1846 if (process.mStatus != ExecuteProcessStatus_Started)
1847 rc = setError(VBOX_E_IPRT_ERROR,
1848 Guest::tr("Cannot inject input to not running process (PID %u)"), aPID);
1849 }
1850 else
1851 rc = setError(VBOX_E_IPRT_ERROR,
1852 Guest::tr("Cannot inject input to non-existent process (PID %u)"), aPID);
1853
1854 if (SUCCEEDED(rc))
1855 {
1856 uint32_t uContextID = 0;
1857
1858 /*
1859 * Create progress object.
1860 * This progress object, compared to the one in executeProgress() above,
1861 * is only single-stage local and is used to determine whether the operation
1862 * finished or got canceled.
1863 */
1864 ComObjPtr <Progress> pProgress;
1865 rc = pProgress.createObject();
1866 if (SUCCEEDED(rc))
1867 {
1868 rc = pProgress->init(static_cast<IGuest*>(this),
1869 Bstr(tr("Setting input for process")).raw(),
1870 TRUE /* Cancelable */);
1871 }
1872 if (FAILED(rc)) throw rc;
1873 ComAssert(!pProgress.isNull());
1874
1875 /* Adjust timeout. */
1876 if (aTimeoutMS == 0)
1877 aTimeoutMS = UINT32_MAX;
1878
1879 VBOXGUESTCTRL_CALLBACK callback;
1880 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS, pProgress);
1881 if (RT_SUCCESS(vrc))
1882 {
1883 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)callback.pvData;
1884
1885 /* Save PID + output flags for later use. */
1886 pData->u32PID = aPID;
1887 pData->u32Flags = aFlags;
1888 }
1889
1890 if (RT_SUCCESS(vrc))
1891 vrc = callbackAdd(&callback, &uContextID);
1892
1893 if (RT_SUCCESS(vrc))
1894 {
1895 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
1896 uint32_t cbSize = sfaData.size();
1897
1898 VBOXHGCMSVCPARM paParms[6];
1899 int i = 0;
1900 paParms[i++].setUInt32(uContextID);
1901 paParms[i++].setUInt32(aPID);
1902 paParms[i++].setUInt32(aFlags);
1903 paParms[i++].setPointer(sfaData.raw(), cbSize);
1904 paParms[i++].setUInt32(cbSize);
1905
1906 {
1907 VMMDev *pVMMDev = NULL;
1908 {
1909 /* Make sure mParent is valid, so set the read lock while using.
1910 * Do not keep this lock while doing the actual call, because in the meanwhile
1911 * another thread could request a write lock which would be a bad idea ... */
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 /* Forward the information to the VMM device. */
1915 AssertPtr(mParent);
1916 pVMMDev = mParent->getVMMDev();
1917 }
1918
1919 if (pVMMDev)
1920 {
1921 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1922 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
1923 i, paParms);
1924 }
1925 }
1926 }
1927
1928 if (RT_SUCCESS(vrc))
1929 {
1930 LogFlowFunc(("Waiting for HGCM callback ...\n"));
1931
1932 /*
1933 * Wait for the HGCM low level callback until the process
1934 * has been started (or something went wrong). This is necessary to
1935 * get the PID.
1936 */
1937
1938 PCALLBACKDATAEXECINSTATUS pExecStatusIn = NULL;
1939
1940 /*
1941 * Wait for the first stage (=0) to complete (that is starting the process).
1942 */
1943 vrc = callbackWaitForCompletion(uContextID, 0 /* Stage */, aTimeoutMS);
1944 if (RT_SUCCESS(vrc))
1945 {
1946 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
1947 (void**)&pExecStatusIn, NULL /* Don't need the size. */);
1948 if (RT_SUCCESS(vrc))
1949 {
1950 switch (pExecStatusIn->u32Status)
1951 {
1952 case INPUT_STS_WRITTEN:
1953 *aBytesWritten = pExecStatusIn->cbProcessed;
1954 break;
1955
1956 default:
1957 rc = setError(VBOX_E_IPRT_ERROR,
1958 tr("Client error %u while processing input data"), pExecStatusIn->u32Status);
1959 break;
1960 }
1961
1962 callbackFreeUserData(pExecStatusIn);
1963 }
1964 else
1965 {
1966 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1967 tr("Unable to retrieve process input status data"));
1968 }
1969 }
1970 else
1971 rc = handleErrorCompletion(vrc);
1972 }
1973 else
1974 rc = handleErrorHGCM(vrc);
1975
1976 if (SUCCEEDED(rc))
1977 {
1978 /* Nothing to do here yet. */
1979 }
1980
1981 /* The callback isn't needed anymore -- just was kept locally. */
1982 callbackDestroy(uContextID);
1983
1984 /* Cleanup. */
1985 if (!pProgress.isNull())
1986 pProgress->uninit();
1987 pProgress.setNull();
1988 }
1989 }
1990 catch (std::bad_alloc &)
1991 {
1992 rc = E_OUTOFMEMORY;
1993 }
1994 return rc;
1995#endif
1996}
1997
1998STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
1999{
2000#ifndef VBOX_WITH_GUEST_CONTROL
2001 ReturnComNotImplemented();
2002#else /* VBOX_WITH_GUEST_CONTROL */
2003 using namespace guestControl;
2004
2005 return getProcessOutputInternal(aPID, aFlags, aTimeoutMS,
2006 aSize, ComSafeArrayOutArg(aData), NULL /* rc */);
2007#endif
2008}
2009
2010HRESULT Guest::getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS,
2011 LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC)
2012{
2013/** @todo r=bird: Eventually we should clean up all the timeout parameters
2014 * in the API and have the same way of specifying infinite waits! */
2015#ifndef VBOX_WITH_GUEST_CONTROL
2016 ReturnComNotImplemented();
2017#else /* VBOX_WITH_GUEST_CONTROL */
2018 using namespace guestControl;
2019
2020 CheckComArgExpr(aPID, aPID > 0);
2021 if (aSize < 0)
2022 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
2023 if (aSize == 0)
2024 return setError(E_INVALIDARG, tr("The size (%lld) is zero"), aSize);
2025 if (aFlags)
2026 {
2027 if (!(aFlags & ProcessOutputFlag_StdErr))
2028 {
2029 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2030 }
2031 }
2032
2033 AutoCaller autoCaller(this);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 HRESULT rc = S_OK;
2037
2038 try
2039 {
2040 VBOXGUESTCTRL_PROCESS proc;
2041 int vrc = processGetStatus(aPID, &proc);
2042 if (RT_FAILURE(vrc))
2043 {
2044 rc = setError(VBOX_E_IPRT_ERROR,
2045 Guest::tr("Guest process (PID %u) does not exist"), aPID);
2046 }
2047 else
2048 {
2049 uint32_t uContextID = 0;
2050
2051 /*
2052 * Create progress object.
2053 * This progress object, compared to the one in executeProgress() above,
2054 * is only single-stage local and is used to determine whether the operation
2055 * finished or got canceled.
2056 */
2057 ComObjPtr <Progress> pProgress;
2058 rc = pProgress.createObject();
2059 if (SUCCEEDED(rc))
2060 {
2061 rc = pProgress->init(static_cast<IGuest*>(this),
2062 Bstr(tr("Getting output for guest process")).raw(),
2063 TRUE /* Cancelable */);
2064 }
2065 if (FAILED(rc)) throw rc;
2066 ComAssert(!pProgress.isNull());
2067
2068 /* Adjust timeout. */
2069 if (aTimeoutMS == 0)
2070 aTimeoutMS = UINT32_MAX;
2071
2072 /* Set handle ID. */
2073 uint32_t uHandleID = OUTPUT_HANDLE_ID_STDOUT; /* Default */
2074 if (aFlags & ProcessOutputFlag_StdErr)
2075 uHandleID = OUTPUT_HANDLE_ID_STDERR;
2076
2077 VBOXGUESTCTRL_CALLBACK callback;
2078 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT, pProgress);
2079 if (RT_SUCCESS(vrc))
2080 {
2081 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)callback.pvData;
2082
2083 /* Save PID + output flags for later use. */
2084 pData->u32PID = aPID;
2085 pData->u32Flags = aFlags;
2086 }
2087
2088 if (RT_SUCCESS(vrc))
2089 vrc = callbackAdd(&callback, &uContextID);
2090
2091 if (RT_SUCCESS(vrc))
2092 {
2093 VBOXHGCMSVCPARM paParms[5];
2094 int i = 0;
2095 paParms[i++].setUInt32(uContextID);
2096 paParms[i++].setUInt32(aPID);
2097 paParms[i++].setUInt32(uHandleID);
2098 paParms[i++].setUInt32(0 /* Flags, none set yet */);
2099
2100 VMMDev *pVMMDev = NULL;
2101 {
2102 /* Make sure mParent is valid, so set the read lock while using.
2103 * Do not keep this lock while doing the actual call, because in the meanwhile
2104 * another thread could request a write lock which would be a bad idea ... */
2105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2106
2107 /* Forward the information to the VMM device. */
2108 AssertPtr(mParent);
2109 pVMMDev = mParent->getVMMDev();
2110 }
2111
2112 if (pVMMDev)
2113 {
2114 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
2115 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
2116 i, paParms);
2117 }
2118 }
2119
2120 if (RT_SUCCESS(vrc))
2121 {
2122 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
2123
2124 /*
2125 * Wait for the HGCM low level callback until the process
2126 * has been started (or something went wrong). This is necessary to
2127 * get the PID.
2128 */
2129
2130 /*
2131 * Wait for the first output callback notification to arrive.
2132 */
2133 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);
2134 if (RT_SUCCESS(vrc))
2135 {
2136 PCALLBACKDATAEXECOUT pExecOut = NULL;
2137 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
2138 (void**)&pExecOut, NULL /* Don't need the size. */);
2139 if (RT_SUCCESS(vrc))
2140 {
2141 com::SafeArray<BYTE> outputData((size_t)aSize);
2142
2143 if (pExecOut->cbData)
2144 {
2145 /* Do we need to resize the array? */
2146 if (pExecOut->cbData > aSize)
2147 outputData.resize(pExecOut->cbData);
2148
2149 /* Fill output in supplied out buffer. */
2150 memcpy(outputData.raw(), pExecOut->pvData, pExecOut->cbData);
2151 outputData.resize(pExecOut->cbData); /* Shrink to fit actual buffer size. */
2152 }
2153 else
2154 {
2155 /* No data within specified timeout available. */
2156 outputData.resize(0);
2157 }
2158
2159 /* Detach output buffer to output argument. */
2160 outputData.detachTo(ComSafeArrayOutArg(aData));
2161
2162 callbackFreeUserData(pExecOut);
2163 }
2164 else
2165 {
2166 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
2167 tr("Unable to retrieve process output data"));
2168 }
2169 }
2170 else
2171 rc = handleErrorCompletion(vrc);
2172 }
2173 else
2174 rc = handleErrorHGCM(vrc);
2175
2176 /* The callback isn't needed anymore -- just was kept locally. */
2177 callbackDestroy(uContextID);
2178
2179 /* Cleanup. */
2180 if (!pProgress.isNull())
2181 pProgress->uninit();
2182 pProgress.setNull();
2183 }
2184
2185 if (pRC)
2186 *pRC = vrc;
2187 }
2188 catch (std::bad_alloc &)
2189 {
2190 rc = E_OUTOFMEMORY;
2191 }
2192 return rc;
2193#endif
2194}
2195
2196STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ExecuteProcessStatus_T *aStatus)
2197{
2198#ifndef VBOX_WITH_GUEST_CONTROL
2199 ReturnComNotImplemented();
2200#else /* VBOX_WITH_GUEST_CONTROL */
2201 CheckComArgNotNull(aExitCode);
2202 CheckComArgNotNull(aFlags);
2203 CheckComArgNotNull(aStatus);
2204
2205 AutoCaller autoCaller(this);
2206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2207
2208 HRESULT rc = S_OK;
2209
2210 try
2211 {
2212 VBOXGUESTCTRL_PROCESS process;
2213 int vrc = processGetStatus(aPID, &process);
2214 if (RT_SUCCESS(vrc))
2215 {
2216 *aExitCode = process.mExitCode;
2217 *aFlags = process.mFlags;
2218 *aStatus = process.mStatus;
2219 }
2220 else
2221 rc = setError(VBOX_E_IPRT_ERROR,
2222 tr("Process (PID %u) not found!"), aPID);
2223 }
2224 catch (std::bad_alloc &)
2225 {
2226 rc = E_OUTOFMEMORY;
2227 }
2228 return rc;
2229#endif
2230}
2231
2232STDMETHODIMP Guest::CopyFromGuest(IN_BSTR aSource, IN_BSTR aDest,
2233 IN_BSTR aUsername, IN_BSTR aPassword,
2234 ULONG aFlags, IProgress **aProgress)
2235{
2236#ifndef VBOX_WITH_GUEST_CONTROL
2237 ReturnComNotImplemented();
2238#else /* VBOX_WITH_GUEST_CONTROL */
2239 CheckComArgStrNotEmptyOrNull(aSource);
2240 CheckComArgStrNotEmptyOrNull(aDest);
2241 CheckComArgStrNotEmptyOrNull(aUsername);
2242 CheckComArgStrNotEmptyOrNull(aPassword);
2243 CheckComArgOutPointerValid(aProgress);
2244
2245 AutoCaller autoCaller(this);
2246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2247
2248 /* Validate flags. */
2249 if (aFlags != CopyFileFlag_None)
2250 {
2251 if ( !(aFlags & CopyFileFlag_Recursive)
2252 && !(aFlags & CopyFileFlag_Update)
2253 && !(aFlags & CopyFileFlag_FollowLinks))
2254 {
2255 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2256 }
2257 }
2258
2259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2260
2261 HRESULT rc = S_OK;
2262
2263 ComObjPtr<Progress> progress;
2264 try
2265 {
2266 /* Create the progress object. */
2267 progress.createObject();
2268
2269 rc = progress->init(static_cast<IGuest*>(this),
2270 Bstr(tr("Copying file from guest to host")).raw(),
2271 TRUE /* aCancelable */);
2272 if (FAILED(rc)) throw rc;
2273
2274 /* Initialize our worker task. */
2275 GuestTask *pTask = new GuestTask(GuestTask::TaskType_CopyFileFromGuest, this, progress);
2276 AssertPtr(pTask);
2277 std::auto_ptr<GuestTask> task(pTask);
2278
2279 /* Assign data - aSource is the source file on the host,
2280 * aDest reflects the full path on the guest. */
2281 task->strSource = (Utf8Str(aSource));
2282 task->strDest = (Utf8Str(aDest));
2283 task->strUserName = (Utf8Str(aUsername));
2284 task->strPassword = (Utf8Str(aPassword));
2285 task->uFlags = aFlags;
2286
2287 rc = task->startThread();
2288 if (FAILED(rc)) throw rc;
2289
2290 /* Don't destruct on success. */
2291 task.release();
2292 }
2293 catch (HRESULT aRC)
2294 {
2295 rc = aRC;
2296 }
2297
2298 if (SUCCEEDED(rc))
2299 {
2300 /* Return progress to the caller. */
2301 progress.queryInterfaceTo(aProgress);
2302 }
2303 return rc;
2304#endif /* VBOX_WITH_GUEST_CONTROL */
2305}
2306
2307STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
2308 IN_BSTR aUsername, IN_BSTR aPassword,
2309 ULONG aFlags, IProgress **aProgress)
2310{
2311#ifndef VBOX_WITH_GUEST_CONTROL
2312 ReturnComNotImplemented();
2313#else /* VBOX_WITH_GUEST_CONTROL */
2314 CheckComArgStrNotEmptyOrNull(aSource);
2315 CheckComArgStrNotEmptyOrNull(aDest);
2316 CheckComArgStrNotEmptyOrNull(aUsername);
2317 CheckComArgStrNotEmptyOrNull(aPassword);
2318 CheckComArgOutPointerValid(aProgress);
2319
2320 AutoCaller autoCaller(this);
2321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2322
2323 /* Validate flags. */
2324 if (aFlags != CopyFileFlag_None)
2325 {
2326 if ( !(aFlags & CopyFileFlag_Recursive)
2327 && !(aFlags & CopyFileFlag_Update)
2328 && !(aFlags & CopyFileFlag_FollowLinks))
2329 {
2330 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2331 }
2332 }
2333
2334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2335
2336 HRESULT rc = S_OK;
2337
2338 ComObjPtr<Progress> progress;
2339 try
2340 {
2341 /* Create the progress object. */
2342 progress.createObject();
2343
2344 rc = progress->init(static_cast<IGuest*>(this),
2345 Bstr(tr("Copying file from host to guest")).raw(),
2346 TRUE /* aCancelable */);
2347 if (FAILED(rc)) throw rc;
2348
2349 /* Initialize our worker task. */
2350 GuestTask *pTask = new GuestTask(GuestTask::TaskType_CopyFileToGuest, this, progress);
2351 AssertPtr(pTask);
2352 std::auto_ptr<GuestTask> task(pTask);
2353
2354 /* Assign data - aSource is the source file on the host,
2355 * aDest reflects the full path on the guest. */
2356 task->strSource = (Utf8Str(aSource));
2357 task->strDest = (Utf8Str(aDest));
2358 task->strUserName = (Utf8Str(aUsername));
2359 task->strPassword = (Utf8Str(aPassword));
2360 task->uFlags = aFlags;
2361
2362 rc = task->startThread();
2363 if (FAILED(rc)) throw rc;
2364
2365 /* Don't destruct on success. */
2366 task.release();
2367 }
2368 catch (HRESULT aRC)
2369 {
2370 rc = aRC;
2371 }
2372
2373 if (SUCCEEDED(rc))
2374 {
2375 /* Return progress to the caller. */
2376 progress.queryInterfaceTo(aProgress);
2377 }
2378 return rc;
2379#endif /* VBOX_WITH_GUEST_CONTROL */
2380}
2381
2382STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ULONG aFlags, IProgress **aProgress)
2383{
2384#ifndef VBOX_WITH_GUEST_CONTROL
2385 ReturnComNotImplemented();
2386#else /* VBOX_WITH_GUEST_CONTROL */
2387 CheckComArgStrNotEmptyOrNull(aSource);
2388 CheckComArgOutPointerValid(aProgress);
2389
2390 AutoCaller autoCaller(this);
2391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2392
2393 /* Validate flags. */
2394 if (aFlags)
2395 {
2396 if (!(aFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2397 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2398 }
2399
2400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2401
2402 HRESULT rc = S_OK;
2403
2404 ComObjPtr<Progress> progress;
2405 try
2406 {
2407 /* Create the progress object. */
2408 progress.createObject();
2409
2410 rc = progress->init(static_cast<IGuest*>(this),
2411 Bstr(tr("Updating Guest Additions")).raw(),
2412 TRUE /* aCancelable */);
2413 if (FAILED(rc)) throw rc;
2414
2415 /* Initialize our worker task. */
2416 GuestTask *pTask = new GuestTask(GuestTask::TaskType_UpdateGuestAdditions, this, progress);
2417 AssertPtr(pTask);
2418 std::auto_ptr<GuestTask> task(pTask);
2419
2420 /* Assign data - in that case aSource is the full path
2421 * to the Guest Additions .ISO we want to mount. */
2422 task->strSource = (Utf8Str(aSource));
2423 task->uFlags = aFlags;
2424
2425 rc = task->startThread();
2426 if (FAILED(rc)) throw rc;
2427
2428 /* Don't destruct on success. */
2429 task.release();
2430 }
2431 catch (HRESULT aRC)
2432 {
2433 rc = aRC;
2434 }
2435
2436 if (SUCCEEDED(rc))
2437 {
2438 /* Return progress to the caller. */
2439 progress.queryInterfaceTo(aProgress);
2440 }
2441 return rc;
2442#endif /* VBOX_WITH_GUEST_CONTROL */
2443}
2444
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