VirtualBox

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

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

GuestCtrl/Main: Fixed exit code hickup.

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