VirtualBox

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

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

GuestCtrl/Main: Added some assertions for GetProcessOutput().

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