VirtualBox

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

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

GuestCtrl: More bugfixing, use ComPtr/ComObjPtr.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.7 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 39475 2011-11-30 13:20:06Z 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 /* Interprete u32Flags as the guest process' exit code. */
734 case PROC_STS_TES:
735 case PROC_STS_TOK:
736 vrc = processSetStatus(pData->u32PID,
737 (ExecuteProcessStatus_T)pData->u32Status,
738 pData->u32Flags /* Exit code. */, 0 /* Flags. */);
739 break;
740 /* Just reach through flags. */
741 default:
742 vrc = processSetStatus(pData->u32PID,
743 (ExecuteProcessStatus_T)pData->u32Status,
744 0 /* Exit code. */, pData->u32Flags);
745 break;
746 }
747 }
748
749 /* Do progress handling. */
750 switch (pData->u32Status)
751 {
752 case PROC_STS_STARTED:
753 vrc = callbackMoveForward(uContextID, Guest::tr("Waiting for process to exit ..."));
754 LogRel(("Guest process (PID %u) started\n", pData->u32PID)); /** @todo Add process name */
755 break;
756
757 case PROC_STS_TEN: /* Terminated normally. */
758 vrc = callbackNotifyComplete(uContextID);
759 LogRel(("Guest process (PID %u) exited normally\n", pData->u32PID)); /** @todo Add process name */
760 break;
761
762 case PROC_STS_TEA: /* Terminated abnormally. */
763 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
764 pData->u32PID, pData->u32Flags)); /** @todo Add process name */
765 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
766 pData->u32Flags);
767 rcCallback = VERR_GENERAL_FAILURE; /** @todo */
768 break;
769
770 case PROC_STS_TES: /* Terminated through signal. */
771 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
772 pData->u32PID, pData->u32Flags)); /** @todo Add process name */
773 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
774 pData->u32Flags);
775 rcCallback = VERR_GENERAL_FAILURE; /** @todo */
776 break;
777
778 case PROC_STS_TOK:
779 LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */
780 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
781 rcCallback = VERR_TIMEOUT;
782 break;
783
784 case PROC_STS_TOA:
785 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */
786 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
787 rcCallback = VERR_TIMEOUT;
788 break;
789
790 case PROC_STS_DWN:
791 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pData->u32PID)); /** @todo Add process name */
792 /*
793 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
794 * our progress object. This is helpful for waiters which rely on the success of our progress object
795 * even if the executed process was killed because the system/VBoxService is shutting down.
796 *
797 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
798 */
799 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
800 {
801 vrc = callbackNotifyComplete(uContextID);
802 }
803 else
804 {
805 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
806 rcCallback = VERR_CANCELLED;
807 }
808 break;
809
810 case PROC_STS_ERROR:
811 if (pData->u32PID)
812 {
813 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
814 pData->u32PID, pData->u32Flags)); /** @todo Add process name */
815 }
816 else
817 {
818 switch (pData->u32Flags)
819 {
820 case VERR_MAX_PROCS_REACHED:
821 LogRel(("Guest process could not be started because maximum number of parallel guest processes has been reached\n"));
822 break;
823
824 default:
825 LogRel(("Guest process could not be started because of rc=%Rrc\n",
826 pData->u32Flags));
827 }
828
829 }
830 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pData->u32Flags);
831 rcCallback = pData->u32Flags; /* Report back rc. */
832 break;
833
834 default:
835 vrc = VERR_INVALID_PARAMETER;
836 break;
837 }
838 }
839 else
840 {
841 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
842 rcCallback = VERR_CANCELLED;
843 }
844
845 /* Do we need to handle the callback error? */
846 if (RT_FAILURE(rcCallback))
847 {
848 AssertMsg(!errMsg.isEmpty(), ("Error message must not be empty!\n"));
849
850 /* Notify all callbacks which are still waiting on something
851 * which is related to the current PID. */
852 if (pData->u32PID)
853 {
854 int rc2 = callbackNotifyAllForPID(pData->u32PID, rcCallback, errMsg.c_str());
855 if (RT_FAILURE(rc2))
856 {
857 LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
858 pData->u32PID));
859 if (RT_SUCCESS(vrc))
860 vrc = rc2;
861 }
862 }
863
864 /* Let the caller know what went wrong ... */
865 int rc2 = callbackNotifyEx(uContextID, rcCallback, errMsg.c_str());
866 if (RT_FAILURE(rc2))
867 {
868 LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
869 uContextID, pData->u32PID));
870 if (RT_SUCCESS(vrc))
871 vrc = rc2;
872 }
873 LogFlowFunc(("Process (CID=%u, status=%u) reported: %s\n",
874 uContextID, pData->u32Status, errMsg.c_str()));
875 }
876 LogFlowFunc(("Returned with rc=%Rrc, rcCallback=%Rrc\n",
877 vrc, rcCallback));
878 return vrc;
879}
880
881/* Function for handling the execution output notification. */
882int Guest::notifyCtrlExecOut(uint32_t u32Function,
883 PCALLBACKDATAEXECOUT pData)
884{
885 AssertReturn(u32Function, VERR_INVALID_PARAMETER);
886 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
887
888 uint32_t uContextID = pData->hdr.u32ContextID;
889 Assert(uContextID);
890
891 /* Scope write locks as much as possible. */
892 {
893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
894
895 LogFlowFunc(("Output status (CID=%u, pData=0x%p)\n",
896 uContextID, pData));
897
898 PCALLBACKDATAEXECOUT pCallbackData =
899 (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
900 if (pCallbackData)
901 {
902 pCallbackData->u32PID = pData->u32PID;
903 pCallbackData->u32HandleId = pData->u32HandleId;
904 pCallbackData->u32Flags = pData->u32Flags;
905
906 /* Make sure we really got something! */
907 if ( pData->cbData
908 && pData->pvData)
909 {
910 callbackFreeUserData(pCallbackData->pvData);
911
912 /* Allocate data buffer and copy it */
913 pCallbackData->pvData = RTMemAlloc(pData->cbData);
914 pCallbackData->cbData = pData->cbData;
915
916 AssertReturn(pCallbackData->pvData, VERR_NO_MEMORY);
917 memcpy(pCallbackData->pvData, pData->pvData, pData->cbData);
918 }
919 else /* Nothing received ... */
920 {
921 pCallbackData->pvData = NULL;
922 pCallbackData->cbData = 0;
923 }
924 }
925 /* If pCallbackData is NULL this might be an old request for which no user data
926 * might exist anymore. */
927 }
928
929 int vrc;
930 if (callbackIsCanceled(pData->u32PID))
931 {
932 vrc = callbackNotifyEx(uContextID, VERR_CANCELLED,
933 Guest::tr("The output operation was canceled"));
934 }
935 else
936 vrc = callbackNotifyComplete(uContextID);
937
938 return vrc;
939}
940
941/* Function for handling the execution input status notification. */
942int Guest::notifyCtrlExecInStatus(uint32_t u32Function,
943 PCALLBACKDATAEXECINSTATUS pData)
944{
945 AssertReturn(u32Function, VERR_INVALID_PARAMETER);
946 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
947
948 uint32_t uContextID = pData->hdr.u32ContextID;
949 Assert(uContextID);
950
951 /* Scope write locks as much as possible. */
952 {
953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
954
955 LogFlowFunc(("Input status (CID=%u, pData=0x%p)\n",
956 uContextID, pData));
957
958 PCALLBACKDATAEXECINSTATUS pCallbackData =
959 (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
960 if (pCallbackData)
961 {
962 /* Save bytes processed. */
963 pCallbackData->cbProcessed = pData->cbProcessed;
964 pCallbackData->u32Status = pData->u32Status;
965 pCallbackData->u32Flags = pData->u32Flags;
966 pCallbackData->u32PID = pData->u32PID;
967 }
968 /* If pCallbackData is NULL this might be an old request for which no user data
969 * might exist anymore. */
970 }
971
972 return callbackNotifyComplete(uContextID);
973}
974
975int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
976 PCALLBACKDATACLIENTDISCONNECTED pData)
977{
978 /* u32Function is 0. */
979 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
980
981 uint32_t uContextID = pData->hdr.u32ContextID;
982 Assert(uContextID);
983
984 LogFlowFunc(("Client disconnected (CID=%u)\n,", uContextID));
985
986 return callbackNotifyEx(uContextID, VERR_CANCELLED,
987 Guest::tr("Client disconnected"));
988}
989
990/**
991 * Gets guest process information. Removes the process from the map
992 * after the process was marked as exited/terminated.
993 *
994 * @return IPRT status code.
995 * @param u32PID
996 * @param pProcess
997 */
998int Guest::processGetStatus(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
999{
1000 AssertReturn(u32PID, VERR_INVALID_PARAMETER);
1001
1002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1003
1004 GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
1005 if (it != mGuestProcessMap.end())
1006 {
1007 if (pProcess)
1008 {
1009 pProcess->mStatus = it->second.mStatus;
1010 pProcess->mExitCode = it->second.mExitCode;
1011 pProcess->mFlags = it->second.mFlags;
1012 }
1013
1014 /* If the is marked as stopped/terminated
1015 * remove it from the map. */
1016 if (it->second.mStatus != ExecuteProcessStatus_Started)
1017 mGuestProcessMap.erase(it);
1018
1019 return VINF_SUCCESS;
1020 }
1021
1022 return VERR_NOT_FOUND;
1023}
1024
1025int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
1026{
1027 AssertReturn(u32PID, VERR_INVALID_PARAMETER);
1028
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
1032 if (it != mGuestProcessMap.end())
1033 {
1034 it->second.mStatus = enmStatus;
1035 it->second.mExitCode = uExitCode;
1036 it->second.mFlags = uFlags;
1037 }
1038 else
1039 {
1040 VBOXGUESTCTRL_PROCESS process;
1041
1042 process.mStatus = enmStatus;
1043 process.mExitCode = uExitCode;
1044 process.mFlags = uFlags;
1045
1046 mGuestProcessMap[u32PID] = process;
1047 }
1048
1049 return VINF_SUCCESS;
1050}
1051
1052HRESULT Guest::handleErrorCompletion(int rc)
1053{
1054 HRESULT hRC;
1055 if (rc == VERR_NOT_FOUND)
1056 hRC = setErrorNoLog(VBOX_E_VM_ERROR,
1057 tr("VMM device is not available (is the VM running?)"));
1058 else if (rc == VERR_CANCELLED)
1059 hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,
1060 tr("Process execution has been canceled"));
1061 else if (rc == VERR_TIMEOUT)
1062 hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,
1063 tr("The guest did not respond within time"));
1064 else
1065 hRC = setErrorNoLog(E_UNEXPECTED,
1066 tr("Waiting for completion failed with error %Rrc"), rc);
1067 return hRC;
1068}
1069
1070HRESULT Guest::handleErrorHGCM(int rc)
1071{
1072 HRESULT hRC;
1073 if (rc == VERR_INVALID_VM_HANDLE)
1074 hRC = setErrorNoLog(VBOX_E_VM_ERROR,
1075 tr("VMM device is not available (is the VM running?)"));
1076 else if (rc == VERR_NOT_FOUND)
1077 hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,
1078 tr("The guest execution service is not ready (yet)"));
1079 else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
1080 hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,
1081 tr("The guest execution service is not available"));
1082 else /* HGCM call went wrong. */
1083 hRC = setErrorNoLog(E_UNEXPECTED,
1084 tr("The HGCM call failed with error %Rrc"), rc);
1085 return hRC;
1086}
1087#endif /* VBOX_WITH_GUEST_CONTROL */
1088
1089STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
1090 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1091 IN_BSTR aUsername, IN_BSTR aPassword,
1092 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
1093{
1094/** @todo r=bird: Eventually we should clean up all the timeout parameters
1095 * in the API and have the same way of specifying infinite waits! */
1096#ifndef VBOX_WITH_GUEST_CONTROL
1097 ReturnComNotImplemented();
1098#else /* VBOX_WITH_GUEST_CONTROL */
1099 using namespace guestControl;
1100
1101 CheckComArgStrNotEmptyOrNull(aCommand);
1102 CheckComArgOutPointerValid(aPID);
1103 CheckComArgOutPointerValid(aProgress);
1104
1105 /* Do not allow anonymous executions (with system rights). */
1106 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))
1107 return setError(E_INVALIDARG, tr("No user name specified"));
1108
1109 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1110 Utf8Str(aCommand).c_str(), Utf8Str(aUsername).c_str()));
1111
1112 return executeProcessInternal(aCommand, aFlags, ComSafeArrayInArg(aArguments),
1113 ComSafeArrayInArg(aEnvironment),
1114 aUsername, aPassword, aTimeoutMS, aPID, aProgress, NULL /* rc */);
1115#endif
1116}
1117
1118#ifdef VBOX_WITH_GUEST_CONTROL
1119/**
1120 * Executes and waits for an internal tool (that is, a tool which is integrated into
1121 * VBoxService, beginning with "vbox_" (e.g. "vbox_ls")) to finish its operation.
1122 *
1123 * @return HRESULT
1124 * @param aTool Name of tool to execute.
1125 * @param aDescription Friendly description of the operation.
1126 * @param aFlags Execution flags.
1127 * @param aUsername Username to execute tool under.
1128 * @param aPassword The user's password.
1129 * @param uFlagsToAdd ExecuteProcessFlag flags to add to the execution operation.
1130 * @param aProgress Pointer which receives the tool's progress object. Optional.
1131 * @param aPID Pointer which receives the tool's PID. Optional.
1132 */
1133HRESULT Guest::executeAndWaitForTool(IN_BSTR aTool, IN_BSTR aDescription,
1134 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1135 IN_BSTR aUsername, IN_BSTR aPassword,
1136 ULONG uFlagsToAdd,
1137 GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr,
1138 IProgress **aProgress, ULONG *aPID)
1139{
1140 ComPtr<IProgress> progressTool;
1141 ULONG uPID;
1142
1143 ULONG uFlags = ExecuteProcessFlag_Hidden
1144 | ExecuteProcessFlag_WaitForProcessStartOnly;
1145 if (uFlagsToAdd)
1146 uFlags |= uFlagsToAdd;
1147
1148 bool fWaitForOutput = false;
1149 if ( ( (uFlags & ExecuteProcessFlag_WaitForStdOut)
1150 && pObjStdOut)
1151 || ( (uFlags & ExecuteProcessFlag_WaitForStdErr)
1152 && pObjStdErr))
1153 {
1154 fWaitForOutput = true;
1155 }
1156
1157 HRESULT rc = ExecuteProcess(aTool,
1158 uFlags,
1159 ComSafeArrayInArg(aArguments),
1160 ComSafeArrayInArg(aEnvironment),
1161 aUsername, aPassword,
1162 0 /* No timeout. */,
1163 &uPID, progressTool.asOutParam());
1164 if ( SUCCEEDED(rc)
1165 && fWaitForOutput)
1166 {
1167 BOOL fCompleted;
1168 while ( SUCCEEDED(progressTool->COMGETTER(Completed)(&fCompleted))
1169 && !fCompleted)
1170 {
1171 BOOL fCanceled;
1172 rc = progressTool->COMGETTER(Canceled)(&fCanceled);
1173 AssertComRC(rc);
1174 if (fCanceled)
1175 {
1176 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1177 tr("%s was cancelled"), Utf8Str(aDescription).c_str());
1178 break;
1179 }
1180
1181 if ( (uFlags & ExecuteProcessFlag_WaitForStdOut)
1182 && pObjStdOut)
1183 {
1184 rc = executeStreamParse(uPID, ProcessOutputFlag_None /* StdOut */, *pObjStdOut);
1185 }
1186
1187 if ( (uFlags & ExecuteProcessFlag_WaitForStdErr)
1188 && pObjStdErr)
1189 {
1190 rc = executeStreamParse(uPID, ProcessOutputFlag_StdErr, *pObjStdErr);
1191 }
1192
1193 if (FAILED(rc))
1194 break;
1195 }
1196 }
1197
1198 if (SUCCEEDED(rc))
1199 {
1200 if (aProgress)
1201 {
1202 /* Return the progress to the caller. */
1203 progressTool.queryInterfaceTo(aProgress);
1204 }
1205
1206 if (aPID)
1207 *aPID = uPID;
1208 }
1209
1210 return rc;
1211}
1212
1213HRESULT Guest::executeProcessResult(const char *pszCommand, const char *pszUser, ULONG ulTimeout,
1214 PCALLBACKDATAEXECSTATUS pExecStatus, ULONG *puPID)
1215{
1216 AssertPtrReturn(pExecStatus, E_INVALIDARG);
1217 AssertPtrReturn(puPID, E_INVALIDARG);
1218
1219 HRESULT rc = S_OK;
1220
1221 /* Did we get some status? */
1222 switch (pExecStatus->u32Status)
1223 {
1224 case PROC_STS_STARTED:
1225 /* Process is (still) running; get PID. */
1226 *puPID = pExecStatus->u32PID;
1227 break;
1228
1229 /* In any other case the process either already
1230 * terminated or something else went wrong, so no PID ... */
1231 case PROC_STS_TEN: /* Terminated normally. */
1232 case PROC_STS_TEA: /* Terminated abnormally. */
1233 case PROC_STS_TES: /* Terminated through signal. */
1234 case PROC_STS_TOK:
1235 case PROC_STS_TOA:
1236 case PROC_STS_DWN:
1237 /*
1238 * Process (already) ended, but we want to get the
1239 * PID anyway to retrieve the output in a later call.
1240 */
1241 *puPID = pExecStatus->u32PID;
1242 break;
1243
1244 case PROC_STS_ERROR:
1245 {
1246 int vrc = pExecStatus->u32Flags; /* u32Flags member contains IPRT error code. */
1247 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1248 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1249 tr("The file '%s' was not found on guest"), pszCommand);
1250 else if (vrc == VERR_PATH_NOT_FOUND)
1251 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1252 tr("The path to file '%s' was not found on guest"), pszCommand);
1253 else if (vrc == VERR_BAD_EXE_FORMAT)
1254 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1255 tr("The file '%s' is not an executable format on guest"), pszCommand);
1256 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1257 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1258 tr("The specified user '%s' was not able to logon on guest"), pszUser);
1259 else if (vrc == VERR_TIMEOUT)
1260 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1261 tr("The guest did not respond within time (%ums)"), ulTimeout);
1262 else if (vrc == VERR_CANCELLED)
1263 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1264 tr("The execution operation was canceled"));
1265 else if (vrc == VERR_PERMISSION_DENIED)
1266 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1267 tr("Invalid user/password credentials"));
1268 else if (vrc == VERR_MAX_PROCS_REACHED)
1269 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1270 tr("Concurrent guest process limit is reached"));
1271 else
1272 {
1273 if (pExecStatus && pExecStatus->u32Status == PROC_STS_ERROR)
1274 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1275 tr("Process could not be started: %Rrc"), pExecStatus->u32Flags);
1276 else
1277 rc = setErrorNoLog(E_UNEXPECTED,
1278 tr("The service call failed with error %Rrc"), vrc);
1279 }
1280 }
1281 break;
1282
1283 case PROC_STS_UNDEFINED: /* . */
1284 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1285 tr("The operation did not complete within time"));
1286 break;
1287
1288 default:
1289 AssertReleaseMsgFailed(("Process (PID %u) reported back an undefined state!\n",
1290 pExecStatus->u32PID));
1291 rc = E_UNEXPECTED;
1292 break;
1293 }
1294
1295 return rc;
1296}
1297
1298/**
1299 * TODO
1300 *
1301 * @return HRESULT
1302 * @param aObjName
1303 * @param pStreamBlock
1304 * @param pObjInfo
1305 * @param enmAddAttribs
1306 */
1307HRESULT Guest::executeStreamQueryFsObjInfo(IN_BSTR aObjName,
1308 GuestProcessStreamBlock &streamBlock,
1309 PRTFSOBJINFO pObjInfo,
1310 RTFSOBJATTRADD enmAddAttribs)
1311{
1312 HRESULT rc = S_OK;
1313 Utf8Str Utf8ObjName(aObjName);
1314
1315 int64_t iVal;
1316 int vrc = streamBlock.GetInt64Ex("st_size", &iVal);
1317 if (RT_SUCCESS(vrc))
1318 pObjInfo->cbObject = iVal;
1319 else
1320 rc = setError(VBOX_E_IPRT_ERROR,
1321 tr("Unable to retrieve size for \"%s\" (%Rrc)"),
1322 Utf8ObjName.c_str(), vrc);
1323 /** @todo Add more stuff! */
1324 return rc;
1325}
1326
1327/**
1328 * Tries to drain the guest's output and fill it into
1329 * a guest process stream object for later usage.
1330 *
1331 * @todo What's about specifying stderr?
1332 * @return IPRT status code.
1333 * @param aPID PID of process to get the output from.
1334 * @param aFlags Which stream to drain (stdout or stderr).
1335 * @param stream Reference to guest process stream to fill.
1336 */
1337int Guest::executeStreamDrain(ULONG aPID, ULONG aFlags, GuestProcessStream &stream)
1338{
1339 AssertReturn(aPID, VERR_INVALID_PARAMETER);
1340
1341 int rc = VINF_SUCCESS;
1342 for (;;)
1343 {
1344 SafeArray<BYTE> aData;
1345 HRESULT hr = getProcessOutputInternal(aPID, aFlags,
1346 0 /* Infinite timeout */,
1347 _64K, ComSafeArrayAsOutParam(aData), &rc);
1348 if (SUCCEEDED(hr))
1349 {
1350 if (aData.size())
1351 {
1352 rc = stream.AddData(aData.raw(), aData.size());
1353 if (RT_UNLIKELY(RT_FAILURE(rc)))
1354 break;
1355 }
1356
1357 continue; /* Try one more time. */
1358 }
1359 else /* No more output and/or error! */
1360 {
1361 if (rc == VERR_NOT_FOUND)
1362 rc = VINF_SUCCESS;
1363 break;
1364 }
1365 }
1366
1367 return rc;
1368}
1369
1370/**
1371 * Tries to retrieve the next stream block of a given stream and
1372 * drains the process output only as much as needed to get this next
1373 * stream block.
1374 *
1375 * @return IPRT status code.
1376 * @param ulPID
1377 * @param ulFlags
1378 * @param stream
1379 * @param streamBlock
1380 */
1381int Guest::executeStreamGetNextBlock(ULONG ulPID,
1382 ULONG ulFlags,
1383 GuestProcessStream &stream,
1384 GuestProcessStreamBlock &streamBlock)
1385{
1386 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
1387
1388 bool fDrainStream = true;
1389 int rc;
1390 do
1391 {
1392 rc = stream.ParseBlock(streamBlock);
1393 if (RT_FAILURE(rc)) /* More data needed or empty buffer? */
1394 {
1395 if (fDrainStream)
1396 {
1397 SafeArray<BYTE> aData;
1398 HRESULT hr = getProcessOutputInternal(ulPID, ulFlags,
1399 0 /* Infinite timeout */,
1400 _64K, ComSafeArrayAsOutParam(aData), &rc);
1401 if (SUCCEEDED(hr))
1402 {
1403 if (aData.size())
1404 {
1405 rc = stream.AddData(aData.raw(), aData.size());
1406 if (RT_UNLIKELY(RT_FAILURE(rc)))
1407 break;
1408 }
1409
1410 continue; /* Try one more time. */
1411 }
1412 else
1413 {
1414 /* No more output to drain from stream. */
1415 if (rc == VERR_NOT_FOUND)
1416 {
1417 fDrainStream = false;
1418 continue;
1419 }
1420
1421 break;
1422 }
1423 }
1424 else
1425 {
1426 /* We haved drained the stream as much as we can and reached the
1427 * end of our stream buffer -- that means that there simply is no
1428 * stream block anymore, which is ok. */
1429 if (rc == VERR_NO_DATA)
1430 rc = VINF_SUCCESS;
1431 break;
1432 }
1433 }
1434 }
1435 while (!streamBlock.GetCount());
1436
1437 return rc;
1438}
1439
1440/**
1441 * Tries to get the next upcoming value block from a started guest process
1442 * by first draining its output and then processing the received guest stream.
1443 *
1444 * @return IPRT status code.
1445 * @param ulPID PID of process to get/parse the output from.
1446 * @param ulFlags ?
1447 * @param stream Reference to process stream object to use.
1448 * @param streamBlock Reference that receives the next stream block data.
1449 *
1450 */
1451int Guest::executeStreamParseNextBlock(ULONG ulPID,
1452 ULONG ulFlags,
1453 GuestProcessStream &stream,
1454 GuestProcessStreamBlock &streamBlock)
1455{
1456 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
1457
1458 int rc;
1459 do
1460 {
1461 rc = stream.ParseBlock(streamBlock);
1462 if (RT_FAILURE(rc))
1463 break;
1464 }
1465 while (!streamBlock.GetCount());
1466
1467 return rc;
1468}
1469
1470/**
1471 * Gets output from a formerly started guest process, tries to parse all of its guest
1472 * stream (as long as data is available) and returns a stream object which can contain
1473 * multiple stream blocks (which in turn then can contain key=value pairs).
1474 *
1475 * @return HRESULT
1476 * @param ulPID PID of process to get/parse the output from.
1477 * @param ulFlags ?
1478 * @param streamObjects Reference to a guest stream object structure for
1479 * storing the parsed data.
1480 */
1481HRESULT Guest::executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects)
1482{
1483 GuestProcessStream stream;
1484 int rc = executeStreamDrain(ulPID, ulFlags, stream);
1485 if (RT_SUCCESS(rc))
1486 {
1487 do
1488 {
1489 /* Try to parse the stream output we gathered until now. If we still need more
1490 * data the parsing routine will tell us and we just do another poll round. */
1491 GuestProcessStreamBlock curBlock;
1492 rc = executeStreamParseNextBlock(ulPID, ulFlags, stream, curBlock);
1493 if (RT_SUCCESS(rc))
1494 streamObjects.push_back(curBlock);
1495 } while (RT_SUCCESS(rc));
1496
1497 if (rc == VERR_NO_DATA) /* End of data reached. */
1498 rc = VINF_SUCCESS;
1499 }
1500
1501 if (RT_FAILURE(rc))
1502 return setError(VBOX_E_IPRT_ERROR,
1503 tr("Error while parsing guest output (%Rrc)"), rc);
1504 return rc;
1505}
1506
1507/**
1508 * Does busy waiting on a formerly started guest process.
1509 *
1510 * @return HRESULT
1511 * @param uPID PID of guest process to wait for.
1512 * @param uTimeoutMS Waiting timeout (in ms). Specify 0 for an infinite timeout.
1513 * @param pRetStatus Pointer which receives current process status after the change.
1514 * Optional.
1515 * @param puRetExitCode Pointer which receives the final exit code in case of guest process
1516 * termination. Optional.
1517 */
1518HRESULT Guest::executeWaitForStatusChange(ULONG uPID, ULONG uTimeoutMS,
1519 ExecuteProcessStatus_T *pRetStatus, ULONG *puRetExitCode)
1520{
1521 ULONG uExitCode, uRetFlags;
1522 ExecuteProcessStatus_T curStatus;
1523 HRESULT hRC = GetProcessStatus(uPID, &uExitCode, &uRetFlags, &curStatus);
1524 if (FAILED(hRC))
1525 return hRC;
1526
1527 /* We only want to wait for started processes. All other statuses
1528 * won't be found anyway anymore (see processGetStatus() to know why). */
1529 if (curStatus == ExecuteProcessStatus_Started)
1530 {
1531 uint64_t u64StartMS = RTTimeMilliTS();
1532 if (uTimeoutMS == 0)
1533 uTimeoutMS = UINT32_MAX;
1534
1535 do
1536 {
1537 if ( uTimeoutMS != UINT32_MAX
1538 && RTTimeMilliTS() - u64StartMS > uTimeoutMS)
1539 {
1540 hRC = setError(VBOX_E_IPRT_ERROR,
1541 tr("The process (PID %u) did not change its status within time (%ums)"),
1542 uPID, uTimeoutMS);
1543 break;
1544 }
1545 hRC = GetProcessStatus(uPID, &uExitCode, &uRetFlags, &curStatus);
1546 if (FAILED(hRC))
1547 break;
1548 RTThreadSleep(100);
1549 } while(*pRetStatus == curStatus);
1550 }
1551
1552 if (SUCCEEDED(hRC))
1553 {
1554 if (pRetStatus)
1555 *pRetStatus = curStatus;
1556 if (puRetExitCode)
1557 *puRetExitCode = uExitCode;
1558 }
1559 return hRC;
1560}
1561
1562/**
1563 * Does the actual guest process execution, internal function.
1564 *
1565 * @return HRESULT
1566 * @param aCommand Command line to execute.
1567 * @param aFlags Execution flags.
1568 * @param Username Username to execute the process with.
1569 * @param aPassword The user's password.
1570 * @param aTimeoutMS Timeout (in ms) to wait for the execution operation.
1571 * @param aPID Pointer that receives the guest process' PID.
1572 * @param aProgress Pointer that receives the guest process' progress object.
1573 * @param pRC Pointer that receives the internal IPRT return code. Optional.
1574 */
1575HRESULT Guest::executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
1576 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1577 IN_BSTR aUsername, IN_BSTR aPassword,
1578 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC)
1579{
1580/** @todo r=bird: Eventually we should clean up all the timeout parameters
1581 * in the API and have the same way of specifying infinite waits! */
1582 using namespace guestControl;
1583
1584 AutoCaller autoCaller(this);
1585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1586
1587 /* Validate flags. */
1588 if (aFlags != ExecuteProcessFlag_None)
1589 {
1590 if ( !(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
1591 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1592 && !(aFlags & ExecuteProcessFlag_Hidden)
1593 && !(aFlags & ExecuteProcessFlag_NoProfile)
1594 && !(aFlags & ExecuteProcessFlag_WaitForStdOut)
1595 && !(aFlags & ExecuteProcessFlag_WaitForStdErr))
1596 {
1597 if (pRC)
1598 *pRC = VERR_INVALID_PARAMETER;
1599 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1600 }
1601 }
1602
1603 HRESULT rc = S_OK;
1604
1605 try
1606 {
1607 /*
1608 * Create progress object. Note that this is a multi operation
1609 * object to perform the following steps:
1610 * - Operation 1 (0): Create/start process.
1611 * - Operation 2 (1): Wait for process to exit.
1612 * If this progress completed successfully (S_OK), the process
1613 * started and exited normally. In any other case an error/exception
1614 * occurred.
1615 */
1616 ComObjPtr <Progress> pProgress;
1617 rc = pProgress.createObject();
1618 if (SUCCEEDED(rc))
1619 {
1620 rc = pProgress->init(static_cast<IGuest*>(this),
1621 Bstr(tr("Executing process")).raw(),
1622 TRUE,
1623 2, /* Number of operations. */
1624 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1625 }
1626 ComAssertComRC(rc);
1627
1628 /*
1629 * Prepare process execution.
1630 */
1631 int vrc = VINF_SUCCESS;
1632 Utf8Str Utf8Command(aCommand);
1633
1634 /* Adjust timeout. If set to 0, we define
1635 * an infinite timeout. */
1636 if (aTimeoutMS == 0)
1637 aTimeoutMS = UINT32_MAX;
1638
1639 /* Prepare arguments. */
1640 char **papszArgv = NULL;
1641 uint32_t uNumArgs = 0;
1642 if (aArguments)
1643 {
1644 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1645 uNumArgs = args.size();
1646 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1647 AssertReturn(papszArgv, E_OUTOFMEMORY);
1648 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1649 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1650 papszArgv[uNumArgs] = NULL;
1651 }
1652
1653 Utf8Str Utf8UserName(aUsername);
1654 Utf8Str Utf8Password(aPassword);
1655 if (RT_SUCCESS(vrc))
1656 {
1657 uint32_t uContextID = 0;
1658
1659 char *pszArgs = NULL;
1660 if (uNumArgs > 0)
1661 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1662 if (RT_SUCCESS(vrc))
1663 {
1664 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1665
1666 /* Prepare environment. */
1667 void *pvEnv = NULL;
1668 uint32_t uNumEnv = 0;
1669 uint32_t cbEnv = 0;
1670 if (aEnvironment)
1671 {
1672 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1673
1674 for (unsigned i = 0; i < env.size(); i++)
1675 {
1676 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1677 if (RT_FAILURE(vrc))
1678 break;
1679 }
1680 }
1681
1682 if (RT_SUCCESS(vrc))
1683 {
1684 VBOXGUESTCTRL_CALLBACK callback;
1685 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_START, pProgress);
1686 if (RT_SUCCESS(vrc))
1687 {
1688 /* Allocate and assign payload. */
1689 callback.cbData = sizeof(CALLBACKDATAEXECSTATUS);
1690 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(callback.cbData);
1691 AssertReturn(pData, E_OUTOFMEMORY);
1692 RT_BZERO(pData, callback.cbData);
1693 callback.pvData = pData;
1694 }
1695
1696 if (RT_SUCCESS(vrc))
1697 vrc = callbackAdd(&callback, &uContextID);
1698
1699 if (RT_SUCCESS(vrc))
1700 {
1701 VBOXHGCMSVCPARM paParms[15];
1702 int i = 0;
1703 paParms[i++].setUInt32(uContextID);
1704 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1705 paParms[i++].setUInt32(aFlags);
1706 paParms[i++].setUInt32(uNumArgs);
1707 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1708 paParms[i++].setUInt32(uNumEnv);
1709 paParms[i++].setUInt32(cbEnv);
1710 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1711 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1712 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1713
1714 /*
1715 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1716 * until the process was started - the process itself then gets an infinite timeout for execution.
1717 * This is handy when we want to start a process inside a worker thread within a certain timeout
1718 * but let the started process perform lengthly operations then.
1719 */
1720 if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1721 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1722 else
1723 paParms[i++].setUInt32(aTimeoutMS);
1724
1725 VMMDev *pVMMDev = NULL;
1726 {
1727 /* Make sure mParent is valid, so set the read lock while using.
1728 * Do not keep this lock while doing the actual call, because in the meanwhile
1729 * another thread could request a write lock which would be a bad idea ... */
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 /* Forward the information to the VMM device. */
1733 AssertPtr(mParent);
1734 pVMMDev = mParent->getVMMDev();
1735 }
1736
1737 if (pVMMDev)
1738 {
1739 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1740 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1741 i, paParms);
1742 }
1743 else
1744 vrc = VERR_INVALID_VM_HANDLE;
1745 }
1746 RTMemFree(pvEnv);
1747 }
1748 RTStrFree(pszArgs);
1749 }
1750
1751 if (RT_SUCCESS(vrc))
1752 {
1753 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
1754
1755 /*
1756 * Wait for the HGCM low level callback until the process
1757 * has been started (or something went wrong). This is necessary to
1758 * get the PID.
1759 */
1760
1761 PCALLBACKDATAEXECSTATUS pExecStatus = NULL;
1762
1763 /*
1764 * Wait for the first stage (=0) to complete (that is starting the process).
1765 */
1766 vrc = callbackWaitForCompletion(uContextID, 0 /* Stage */, aTimeoutMS);
1767 if (RT_SUCCESS(vrc))
1768 {
1769 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
1770 (void**)&pExecStatus, NULL /* Don't need the size. */);
1771 if (RT_SUCCESS(vrc))
1772 {
1773 rc = executeProcessResult(Utf8Command.c_str(), Utf8UserName.c_str(), aTimeoutMS,
1774 pExecStatus, aPID);
1775 callbackFreeUserData(pExecStatus);
1776 }
1777 else
1778 {
1779 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1780 tr("Unable to retrieve process execution status data"));
1781 }
1782 }
1783 else
1784 rc = handleErrorCompletion(vrc);
1785
1786 /*
1787 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1788 * else (like end of process) ...
1789 */
1790 }
1791 else
1792 rc = handleErrorHGCM(vrc);
1793
1794 for (unsigned i = 0; i < uNumArgs; i++)
1795 RTMemFree(papszArgv[i]);
1796 RTMemFree(papszArgv);
1797 }
1798
1799 if (SUCCEEDED(rc))
1800 {
1801 /* Return the progress to the caller. */
1802 pProgress.queryInterfaceTo(aProgress);
1803 }
1804 else
1805 {
1806 if (!pRC) /* Skip logging internal calls. */
1807 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1808 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1809 }
1810
1811 if (pRC)
1812 *pRC = vrc;
1813 }
1814 catch (std::bad_alloc &)
1815 {
1816 rc = E_OUTOFMEMORY;
1817 }
1818 return rc;
1819}
1820#endif /* VBOX_WITH_GUEST_CONTROL */
1821
1822STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)
1823{
1824#ifndef VBOX_WITH_GUEST_CONTROL
1825 ReturnComNotImplemented();
1826#else /* VBOX_WITH_GUEST_CONTROL */
1827 using namespace guestControl;
1828
1829 CheckComArgExpr(aPID, aPID > 0);
1830 CheckComArgOutPointerValid(aBytesWritten);
1831
1832 /* Validate flags. */
1833 if (aFlags)
1834 {
1835 if (!(aFlags & ProcessInputFlag_EndOfFile))
1836 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1837 }
1838
1839 AutoCaller autoCaller(this);
1840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1841
1842 HRESULT rc = S_OK;
1843
1844 try
1845 {
1846 VBOXGUESTCTRL_PROCESS process;
1847 int vrc = processGetStatus(aPID, &process);
1848 if (RT_SUCCESS(vrc))
1849 {
1850 /* PID exists; check if process is still running. */
1851 if (process.mStatus != ExecuteProcessStatus_Started)
1852 rc = setError(VBOX_E_IPRT_ERROR,
1853 Guest::tr("Cannot inject input to not running process (PID %u)"), aPID);
1854 }
1855 else
1856 rc = setError(VBOX_E_IPRT_ERROR,
1857 Guest::tr("Cannot inject input to non-existent process (PID %u)"), aPID);
1858
1859 if (RT_SUCCESS(vrc))
1860 {
1861 uint32_t uContextID = 0;
1862
1863 /*
1864 * Create progress object.
1865 * This progress object, compared to the one in executeProgress() above,
1866 * is only single-stage local and is used to determine whether the operation
1867 * finished or got canceled.
1868 */
1869 ComObjPtr <Progress> pProgress;
1870 rc = pProgress.createObject();
1871 if (SUCCEEDED(rc))
1872 {
1873 rc = pProgress->init(static_cast<IGuest*>(this),
1874 Bstr(tr("Setting input for process")).raw(),
1875 TRUE /* Cancelable */);
1876 }
1877 if (FAILED(rc)) throw rc;
1878 ComAssert(!pProgress.isNull());
1879
1880 /* Adjust timeout. */
1881 if (aTimeoutMS == 0)
1882 aTimeoutMS = UINT32_MAX;
1883
1884 VBOXGUESTCTRL_CALLBACK callback;
1885 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS, pProgress);
1886 if (RT_SUCCESS(vrc))
1887 {
1888 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)callback.pvData;
1889
1890 /* Save PID + output flags for later use. */
1891 pData->u32PID = aPID;
1892 pData->u32Flags = aFlags;
1893 }
1894
1895 if (RT_SUCCESS(vrc))
1896 vrc = callbackAdd(&callback, &uContextID);
1897
1898 if (RT_SUCCESS(vrc))
1899 {
1900 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
1901 uint32_t cbSize = sfaData.size();
1902
1903 VBOXHGCMSVCPARM paParms[6];
1904 int i = 0;
1905 paParms[i++].setUInt32(uContextID);
1906 paParms[i++].setUInt32(aPID);
1907 paParms[i++].setUInt32(aFlags);
1908 paParms[i++].setPointer(sfaData.raw(), cbSize);
1909 paParms[i++].setUInt32(cbSize);
1910
1911 {
1912 VMMDev *pVMMDev = NULL;
1913 {
1914 /* Make sure mParent is valid, so set the read lock while using.
1915 * Do not keep this lock while doing the actual call, because in the meanwhile
1916 * another thread could request a write lock which would be a bad idea ... */
1917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1918
1919 /* Forward the information to the VMM device. */
1920 AssertPtr(mParent);
1921 pVMMDev = mParent->getVMMDev();
1922 }
1923
1924 if (pVMMDev)
1925 {
1926 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1927 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
1928 i, paParms);
1929 if (RT_FAILURE(vrc))
1930 rc = handleErrorHGCM(vrc);
1931 }
1932 }
1933 }
1934
1935 if (RT_SUCCESS(vrc))
1936 {
1937 LogFlowFunc(("Waiting for HGCM callback ...\n"));
1938
1939 /*
1940 * Wait for getting back the input response from the guest.
1941 */
1942 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);
1943 if (RT_SUCCESS(vrc))
1944 {
1945 PCALLBACKDATAEXECINSTATUS pExecStatusIn;
1946 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
1947 (void**)&pExecStatusIn, NULL /* Don't need the size. */);
1948 if (RT_SUCCESS(vrc))
1949 {
1950 AssertPtr(pExecStatusIn);
1951 switch (pExecStatusIn->u32Status)
1952 {
1953 case INPUT_STS_WRITTEN:
1954 *aBytesWritten = pExecStatusIn->cbProcessed;
1955 break;
1956
1957 case INPUT_STS_ERROR:
1958 rc = setError(VBOX_E_IPRT_ERROR,
1959 tr("Client reported error %Rrc while processing input data"),
1960 pExecStatusIn->u32Flags);
1961 break;
1962
1963 case INPUT_STS_TERMINATED:
1964 rc = setError(VBOX_E_IPRT_ERROR,
1965 tr("Client terminated while processing input data"));
1966 break;
1967
1968 case INPUT_STS_OVERFLOW:
1969 rc = setError(VBOX_E_IPRT_ERROR,
1970 tr("Client reported buffer overflow while processing input data"));
1971 break;
1972
1973 default:
1974 /*AssertReleaseMsgFailed(("Client reported unknown input error, status=%u, flags=%u\n",
1975 pExecStatusIn->u32Status, pExecStatusIn->u32Flags));*/
1976 break;
1977 }
1978
1979 callbackFreeUserData(pExecStatusIn);
1980 }
1981 else
1982 {
1983 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1984 tr("Unable to retrieve process input status data"));
1985 }
1986 }
1987 else
1988 rc = handleErrorCompletion(vrc);
1989 }
1990
1991 /* The callback isn't needed anymore -- just was kept locally. */
1992 callbackDestroy(uContextID);
1993
1994 /* Cleanup. */
1995 if (!pProgress.isNull())
1996 pProgress->uninit();
1997 pProgress.setNull();
1998 }
1999 }
2000 catch (std::bad_alloc &)
2001 {
2002 rc = E_OUTOFMEMORY;
2003 }
2004 return rc;
2005#endif
2006}
2007
2008STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
2009{
2010#ifndef VBOX_WITH_GUEST_CONTROL
2011 ReturnComNotImplemented();
2012#else /* VBOX_WITH_GUEST_CONTROL */
2013 using namespace guestControl;
2014
2015 return getProcessOutputInternal(aPID, aFlags, aTimeoutMS,
2016 aSize, ComSafeArrayOutArg(aData), NULL /* rc */);
2017#endif
2018}
2019
2020HRESULT Guest::getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS,
2021 LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC)
2022{
2023/** @todo r=bird: Eventually we should clean up all the timeout parameters
2024 * in the API and have the same way of specifying infinite waits! */
2025#ifndef VBOX_WITH_GUEST_CONTROL
2026 ReturnComNotImplemented();
2027#else /* VBOX_WITH_GUEST_CONTROL */
2028 using namespace guestControl;
2029
2030 CheckComArgExpr(aPID, aPID > 0);
2031 if (aSize < 0)
2032 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
2033 if (aSize == 0)
2034 return setError(E_INVALIDARG, tr("The size (%lld) is zero"), aSize);
2035 if (aFlags)
2036 {
2037 if (!(aFlags & ProcessOutputFlag_StdErr))
2038 {
2039 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2040 }
2041 }
2042
2043 AutoCaller autoCaller(this);
2044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2045
2046 HRESULT rc = S_OK;
2047
2048 try
2049 {
2050 VBOXGUESTCTRL_PROCESS proc;
2051 int vrc = processGetStatus(aPID, &proc);
2052 if (RT_FAILURE(vrc))
2053 {
2054 rc = setError(VBOX_E_IPRT_ERROR,
2055 Guest::tr("Guest process (PID %u) does not exist"), aPID);
2056 }
2057 else
2058 {
2059 uint32_t uContextID = 0;
2060
2061 /*
2062 * Create progress object.
2063 * This progress object, compared to the one in executeProgress() above,
2064 * is only single-stage local and is used to determine whether the operation
2065 * finished or got canceled.
2066 */
2067 ComObjPtr <Progress> pProgress;
2068 rc = pProgress.createObject();
2069 if (SUCCEEDED(rc))
2070 {
2071 rc = pProgress->init(static_cast<IGuest*>(this),
2072 Bstr(tr("Getting output for guest process")).raw(),
2073 TRUE /* Cancelable */);
2074 }
2075 if (FAILED(rc)) throw rc;
2076 ComAssert(!pProgress.isNull());
2077
2078 /* Adjust timeout. */
2079 if (aTimeoutMS == 0)
2080 aTimeoutMS = UINT32_MAX;
2081
2082 /* Set handle ID. */
2083 uint32_t uHandleID = OUTPUT_HANDLE_ID_STDOUT; /* Default */
2084 if (aFlags & ProcessOutputFlag_StdErr)
2085 uHandleID = OUTPUT_HANDLE_ID_STDERR;
2086
2087 VBOXGUESTCTRL_CALLBACK callback;
2088 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT, pProgress);
2089 if (RT_SUCCESS(vrc))
2090 {
2091 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)callback.pvData;
2092
2093 /* Save PID + output flags for later use. */
2094 pData->u32PID = aPID;
2095 pData->u32Flags = aFlags;
2096 }
2097
2098 if (RT_SUCCESS(vrc))
2099 vrc = callbackAdd(&callback, &uContextID);
2100
2101 if (RT_SUCCESS(vrc))
2102 {
2103 VBOXHGCMSVCPARM paParms[5];
2104 int i = 0;
2105 paParms[i++].setUInt32(uContextID);
2106 paParms[i++].setUInt32(aPID);
2107 paParms[i++].setUInt32(uHandleID);
2108 paParms[i++].setUInt32(0 /* Flags, none set yet */);
2109
2110 VMMDev *pVMMDev = NULL;
2111 {
2112 /* Make sure mParent is valid, so set the read lock while using.
2113 * Do not keep this lock while doing the actual call, because in the meanwhile
2114 * another thread could request a write lock which would be a bad idea ... */
2115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2116
2117 /* Forward the information to the VMM device. */
2118 AssertPtr(mParent);
2119 pVMMDev = mParent->getVMMDev();
2120 }
2121
2122 if (pVMMDev)
2123 {
2124 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
2125 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
2126 i, paParms);
2127 }
2128 }
2129
2130 if (RT_SUCCESS(vrc))
2131 {
2132 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
2133
2134 /*
2135 * Wait for the HGCM low level callback until the process
2136 * has been started (or something went wrong). This is necessary to
2137 * get the PID.
2138 */
2139
2140 /*
2141 * Wait for the first output callback notification to arrive.
2142 */
2143 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);
2144 if (RT_SUCCESS(vrc))
2145 {
2146 PCALLBACKDATAEXECOUT pExecOut = NULL;
2147 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
2148 (void**)&pExecOut, NULL /* Don't need the size. */);
2149 if (RT_SUCCESS(vrc))
2150 {
2151 com::SafeArray<BYTE> outputData((size_t)aSize);
2152
2153 if (pExecOut->cbData)
2154 {
2155 /* Do we need to resize the array? */
2156 if (pExecOut->cbData > aSize)
2157 outputData.resize(pExecOut->cbData);
2158
2159 /* Fill output in supplied out buffer. */
2160 memcpy(outputData.raw(), pExecOut->pvData, pExecOut->cbData);
2161 outputData.resize(pExecOut->cbData); /* Shrink to fit actual buffer size. */
2162 }
2163 else
2164 {
2165 /* No data within specified timeout available. */
2166 outputData.resize(0);
2167 }
2168
2169 /* Detach output buffer to output argument. */
2170 outputData.detachTo(ComSafeArrayOutArg(aData));
2171
2172 callbackFreeUserData(pExecOut);
2173 }
2174 else
2175 {
2176 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
2177 tr("Unable to retrieve process output data"));
2178 }
2179 }
2180 else
2181 rc = handleErrorCompletion(vrc);
2182 }
2183 else
2184 rc = handleErrorHGCM(vrc);
2185
2186 /* The callback isn't needed anymore -- just was kept locally. */
2187 callbackDestroy(uContextID);
2188
2189 /* Cleanup. */
2190 if (!pProgress.isNull())
2191 pProgress->uninit();
2192 pProgress.setNull();
2193 }
2194
2195 if (pRC)
2196 *pRC = vrc;
2197 }
2198 catch (std::bad_alloc &)
2199 {
2200 rc = E_OUTOFMEMORY;
2201 }
2202 return rc;
2203#endif
2204}
2205
2206STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ExecuteProcessStatus_T *aStatus)
2207{
2208#ifndef VBOX_WITH_GUEST_CONTROL
2209 ReturnComNotImplemented();
2210#else /* VBOX_WITH_GUEST_CONTROL */
2211 CheckComArgNotNull(aExitCode);
2212 CheckComArgNotNull(aFlags);
2213 CheckComArgNotNull(aStatus);
2214
2215 AutoCaller autoCaller(this);
2216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2217
2218 HRESULT rc = S_OK;
2219
2220 try
2221 {
2222 VBOXGUESTCTRL_PROCESS process;
2223 int vrc = processGetStatus(aPID, &process);
2224 if (RT_SUCCESS(vrc))
2225 {
2226 *aExitCode = process.mExitCode;
2227 *aFlags = process.mFlags;
2228 *aStatus = process.mStatus;
2229 }
2230 else
2231 rc = setError(VBOX_E_IPRT_ERROR,
2232 tr("Process (PID %u) not found!"), aPID);
2233 }
2234 catch (std::bad_alloc &)
2235 {
2236 rc = E_OUTOFMEMORY;
2237 }
2238 return rc;
2239#endif
2240}
2241
2242STDMETHODIMP Guest::CopyFromGuest(IN_BSTR aSource, IN_BSTR aDest,
2243 IN_BSTR aUsername, IN_BSTR aPassword,
2244 ULONG aFlags, IProgress **aProgress)
2245{
2246#ifndef VBOX_WITH_GUEST_CONTROL
2247 ReturnComNotImplemented();
2248#else /* VBOX_WITH_GUEST_CONTROL */
2249 CheckComArgStrNotEmptyOrNull(aSource);
2250 CheckComArgStrNotEmptyOrNull(aDest);
2251 CheckComArgStrNotEmptyOrNull(aUsername);
2252 CheckComArgStrNotEmptyOrNull(aPassword);
2253 CheckComArgOutPointerValid(aProgress);
2254
2255 AutoCaller autoCaller(this);
2256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2257
2258 /* Validate flags. */
2259 if (aFlags != CopyFileFlag_None)
2260 {
2261 if ( !(aFlags & CopyFileFlag_Recursive)
2262 && !(aFlags & CopyFileFlag_Update)
2263 && !(aFlags & CopyFileFlag_FollowLinks))
2264 {
2265 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2266 }
2267 }
2268
2269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2270
2271 HRESULT rc = S_OK;
2272
2273 ComObjPtr<Progress> progress;
2274 try
2275 {
2276 /* Create the progress object. */
2277 progress.createObject();
2278
2279 rc = progress->init(static_cast<IGuest*>(this),
2280 Bstr(tr("Copying file from guest to host")).raw(),
2281 TRUE /* aCancelable */);
2282 if (FAILED(rc)) throw rc;
2283
2284 /* Initialize our worker task. */
2285 GuestTask *pTask = new GuestTask(GuestTask::TaskType_CopyFileFromGuest, this, progress);
2286 AssertPtr(pTask);
2287 std::auto_ptr<GuestTask> task(pTask);
2288
2289 /* Assign data - aSource is the source file on the host,
2290 * aDest reflects the full path on the guest. */
2291 task->strSource = (Utf8Str(aSource));
2292 task->strDest = (Utf8Str(aDest));
2293 task->strUserName = (Utf8Str(aUsername));
2294 task->strPassword = (Utf8Str(aPassword));
2295 task->uFlags = aFlags;
2296
2297 rc = task->startThread();
2298 if (FAILED(rc)) throw rc;
2299
2300 /* Don't destruct on success. */
2301 task.release();
2302 }
2303 catch (HRESULT aRC)
2304 {
2305 rc = aRC;
2306 }
2307
2308 if (SUCCEEDED(rc))
2309 {
2310 /* Return progress to the caller. */
2311 progress.queryInterfaceTo(aProgress);
2312 }
2313 return rc;
2314#endif /* VBOX_WITH_GUEST_CONTROL */
2315}
2316
2317STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
2318 IN_BSTR aUsername, IN_BSTR aPassword,
2319 ULONG aFlags, IProgress **aProgress)
2320{
2321#ifndef VBOX_WITH_GUEST_CONTROL
2322 ReturnComNotImplemented();
2323#else /* VBOX_WITH_GUEST_CONTROL */
2324 CheckComArgStrNotEmptyOrNull(aSource);
2325 CheckComArgStrNotEmptyOrNull(aDest);
2326 CheckComArgStrNotEmptyOrNull(aUsername);
2327 CheckComArgStrNotEmptyOrNull(aPassword);
2328 CheckComArgOutPointerValid(aProgress);
2329
2330 AutoCaller autoCaller(this);
2331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2332
2333 /* Validate flags. */
2334 if (aFlags != CopyFileFlag_None)
2335 {
2336 if ( !(aFlags & CopyFileFlag_Recursive)
2337 && !(aFlags & CopyFileFlag_Update)
2338 && !(aFlags & CopyFileFlag_FollowLinks))
2339 {
2340 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2341 }
2342 }
2343
2344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2345
2346 HRESULT rc = S_OK;
2347
2348 ComObjPtr<Progress> progress;
2349 try
2350 {
2351 /* Create the progress object. */
2352 progress.createObject();
2353
2354 rc = progress->init(static_cast<IGuest*>(this),
2355 Bstr(tr("Copying file from host to guest")).raw(),
2356 TRUE /* aCancelable */);
2357 if (FAILED(rc)) throw rc;
2358
2359 /* Initialize our worker task. */
2360 GuestTask *pTask = new GuestTask(GuestTask::TaskType_CopyFileToGuest, this, progress);
2361 AssertPtr(pTask);
2362 std::auto_ptr<GuestTask> task(pTask);
2363
2364 /* Assign data - aSource is the source file on the host,
2365 * aDest reflects the full path on the guest. */
2366 task->strSource = (Utf8Str(aSource));
2367 task->strDest = (Utf8Str(aDest));
2368 task->strUserName = (Utf8Str(aUsername));
2369 task->strPassword = (Utf8Str(aPassword));
2370 task->uFlags = aFlags;
2371
2372 rc = task->startThread();
2373 if (FAILED(rc)) throw rc;
2374
2375 /* Don't destruct on success. */
2376 task.release();
2377 }
2378 catch (HRESULT aRC)
2379 {
2380 rc = aRC;
2381 }
2382
2383 if (SUCCEEDED(rc))
2384 {
2385 /* Return progress to the caller. */
2386 progress.queryInterfaceTo(aProgress);
2387 }
2388 return rc;
2389#endif /* VBOX_WITH_GUEST_CONTROL */
2390}
2391
2392STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ULONG aFlags, IProgress **aProgress)
2393{
2394#ifndef VBOX_WITH_GUEST_CONTROL
2395 ReturnComNotImplemented();
2396#else /* VBOX_WITH_GUEST_CONTROL */
2397 CheckComArgStrNotEmptyOrNull(aSource);
2398 CheckComArgOutPointerValid(aProgress);
2399
2400 AutoCaller autoCaller(this);
2401 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2402
2403 /* Validate flags. */
2404 if (aFlags)
2405 {
2406 if (!(aFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2407 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2408 }
2409
2410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2411
2412 HRESULT rc = S_OK;
2413
2414 ComObjPtr<Progress> progress;
2415 try
2416 {
2417 /* Create the progress object. */
2418 progress.createObject();
2419
2420 rc = progress->init(static_cast<IGuest*>(this),
2421 Bstr(tr("Updating Guest Additions")).raw(),
2422 TRUE /* aCancelable */);
2423 if (FAILED(rc)) throw rc;
2424
2425 /* Initialize our worker task. */
2426 GuestTask *pTask = new GuestTask(GuestTask::TaskType_UpdateGuestAdditions, this, progress);
2427 AssertPtr(pTask);
2428 std::auto_ptr<GuestTask> task(pTask);
2429
2430 /* Assign data - in that case aSource is the full path
2431 * to the Guest Additions .ISO we want to mount. */
2432 task->strSource = (Utf8Str(aSource));
2433 task->uFlags = aFlags;
2434
2435 rc = task->startThread();
2436 if (FAILED(rc)) throw rc;
2437
2438 /* Don't destruct on success. */
2439 task.release();
2440 }
2441 catch (HRESULT aRC)
2442 {
2443 rc = aRC;
2444 }
2445
2446 if (SUCCEEDED(rc))
2447 {
2448 /* Return progress to the caller. */
2449 progress.queryInterfaceTo(aProgress);
2450 }
2451 return rc;
2452#endif /* VBOX_WITH_GUEST_CONTROL */
2453}
2454
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