VirtualBox

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

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