VirtualBox

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

Last change on this file since 38293 was 38293, checked in by vboxsync, 14 years ago

Build fix.

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