VirtualBox

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

Last change on this file since 42171 was 42171, checked in by vboxsync, 12 years ago

Guest Control 2.0: Update.

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