VirtualBox

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

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette