VirtualBox

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

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

GuestCtrl: Don't try too hard when searching for a new context ID.

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