VirtualBox

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

Last change on this file since 42412 was 42411, 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: 98.1 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 42411 2012-07-26 14:07:13Z 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#include "Logging.h"
29
30#include <VBox/VMMDev.h>
31#ifdef VBOX_WITH_GUEST_CONTROL
32# include <VBox/com/array.h>
33# include <VBox/com/ErrorInfo.h>
34#endif
35#include <iprt/cpp/utils.h>
36#include <iprt/file.h>
37#include <iprt/getopt.h>
38#include <iprt/isofs.h>
39#include <iprt/list.h>
40#include <iprt/path.h>
41#include <VBox/vmm/pgm.h>
42
43#include <memory>
44
45// public methods only for internal purposes
46/////////////////////////////////////////////////////////////////////////////
47
48#ifdef VBOX_WITH_GUEST_CONTROL
49/**
50 * Appends environment variables to the environment block.
51 *
52 * Each var=value pair is separated by the null character ('\\0'). The whole
53 * block will be stored in one blob and disassembled on the guest side later to
54 * fit into the HGCM param structure.
55 *
56 * @returns VBox status code.
57 *
58 * @param pszEnvVar The environment variable=value to append to the
59 * environment block.
60 * @param ppvList This is actually a pointer to a char pointer
61 * variable which keeps track of the environment block
62 * that we're constructing.
63 * @param pcbList Pointer to the variable holding the current size of
64 * the environment block. (List is a misnomer, go
65 * ahead a be confused.)
66 * @param pcEnvVars Pointer to the variable holding count of variables
67 * stored in the environment block.
68 */
69int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)
70{
71 int rc = VINF_SUCCESS;
72 uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
73 if (*ppvList)
74 {
75 uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
76 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
77 if (pvTmp == NULL)
78 rc = VERR_NO_MEMORY;
79 else
80 {
81 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
82 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
83 *ppvList = (void **)pvTmp;
84 }
85 }
86 else
87 {
88 char *pszTmp;
89 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
90 {
91 *ppvList = (void **)pszTmp;
92 /* Reset counters. */
93 *pcEnvVars = 0;
94 *pcbList = 0;
95 }
96 }
97 if (RT_SUCCESS(rc))
98 {
99 *pcbList += cchEnv + 1; /* Include zero termination. */
100 *pcEnvVars += 1; /* Increase env variable count. */
101 }
102 return rc;
103}
104
105/**
106 * Adds a callback with a user provided data block and an optional progress object
107 * to the callback map. A callback is identified by a unique context ID which is used
108 * to identify a callback from the guest side.
109 *
110 * @return IPRT status code.
111 * @param pCallback
112 * @param puContextID
113 */
114int Guest::callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallback, uint32_t *puContextID)
115{
116 AssertPtrReturn(pCallback, VERR_INVALID_PARAMETER);
117 /* puContextID is optional. */
118
119 int rc = VERR_NOT_FOUND;
120
121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
122
123 /* Create a new context ID and assign it. */
124 uint32_t uNewContextID = 0;
125 uint32_t uTries = 0;
126 for (;;)
127 {
128 /* Create a new context ID ... */
129 uNewContextID = ASMAtomicIncU32(&mNextContextID);
130 if (uNewContextID == UINT32_MAX)
131 ASMAtomicUoWriteU32(&mNextContextID, 1000);
132 /* Is the context ID already used? Try next ID ... */
133 if (!callbackExists(uNewContextID))
134 {
135 /* Callback with context ID was not found. This means
136 * we can use this context ID for our new callback we want
137 * to add below. */
138 rc = VINF_SUCCESS;
139 break;
140 }
141
142 if (++uTries == UINT32_MAX)
143 break; /* Don't try too hard. */
144 }
145
146 if (RT_SUCCESS(rc))
147 {
148 /* Add callback with new context ID to our callback map. */
149 mCallbackMap[uNewContextID] = *pCallback;
150 Assert(mCallbackMap.size());
151
152 /* Report back new context ID. */
153 if (puContextID)
154 *puContextID = uNewContextID;
155 }
156
157 return rc;
158}
159
160/**
161 * Assigns a host PID to a specified context.
162 * Does not do locking!
163 *
164 * @param uContextID
165 * @param uHostPID
166 */
167int Guest::callbackAssignHostPID(uint32_t uContextID, uint32_t uHostPID)
168{
169 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
170 AssertReturn(uHostPID, VERR_INVALID_PARAMETER);
171
172 int rc = VINF_SUCCESS;
173
174 CallbackMapIter it = mCallbackMap.find(uContextID);
175 if (it == mCallbackMap.end())
176 return VERR_NOT_FOUND;
177
178 it->second.uHostPID = uHostPID;
179
180 return VINF_SUCCESS;
181}
182
183/**
184 * Destroys the formerly allocated callback data. The callback then
185 * needs to get removed from the callback map via callbackRemove().
186 * Does not do locking!
187 *
188 * @param uContextID
189 */
190void Guest::callbackDestroy(uint32_t uContextID)
191{
192 AssertReturnVoid(uContextID);
193
194 CallbackMapIter it = mCallbackMap.find(uContextID);
195 if (it != mCallbackMap.end())
196 {
197 LogFlowFunc(("Callback with CID=%u found\n", uContextID));
198 if (it->second.pvData)
199 {
200 LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
201
202 callbackFreeUserData(it->second.pvData);
203 it->second.cbData = 0;
204 }
205 }
206}
207
208/**
209 * Removes a callback from the callback map.
210 * Does not do locking!
211 *
212 * @param uContextID
213 */
214void Guest::callbackRemove(uint32_t uContextID)
215{
216 callbackDestroy(uContextID);
217
218 mCallbackMap.erase(uContextID);
219}
220
221/**
222 * Checks whether a callback with the given context ID
223 * exists or not.
224 * Does not do locking!
225 *
226 * @return bool True, if callback exists, false if not.
227 * @param uContextID Context ID to check.
228 */
229bool Guest::callbackExists(uint32_t uContextID)
230{
231 AssertReturn(uContextID, false);
232
233 CallbackMapIter it = mCallbackMap.find(uContextID);
234 return (it == mCallbackMap.end()) ? false : true;
235}
236
237/**
238 * Frees the user data (actual context data) of a callback.
239 * Does not do locking!
240 *
241 * @param pvData Data to free.
242 */
243void Guest::callbackFreeUserData(void *pvData)
244{
245 if (pvData)
246 {
247 RTMemFree(pvData);
248 pvData = NULL;
249 }
250}
251
252/**
253 * Retrieves the (generated) host PID of a given callback.
254 *
255 * @return The host PID, if found, 0 otherwise.
256 * @param uContextID Context ID to lookup host PID for.
257 * @param puHostPID Where to store the host PID.
258 */
259uint32_t Guest::callbackGetHostPID(uint32_t uContextID)
260{
261 AssertReturn(uContextID, 0);
262
263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
264
265 CallbackMapIterConst it = mCallbackMap.find(uContextID);
266 if (it == mCallbackMap.end())
267 return 0;
268
269 return it->second.uHostPID;
270}
271
272int Guest::callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType,
273 void **ppvData, size_t *pcbData)
274{
275 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
276 /* pEnmType is optional. */
277 /* ppvData is optional. */
278 /* pcbData is optional. */
279
280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
281
282 CallbackMapIterConst it = mCallbackMap.find(uContextID);
283 if (it == mCallbackMap.end())
284 return VERR_NOT_FOUND;
285
286 if (pEnmType)
287 *pEnmType = it->second.mType;
288
289 if ( ppvData
290 && it->second.cbData)
291 {
292 void *pvData = RTMemAlloc(it->second.cbData);
293 AssertPtrReturn(pvData, VERR_NO_MEMORY);
294 memcpy(pvData, it->second.pvData, it->second.cbData);
295 *ppvData = pvData;
296 }
297
298 if (pcbData)
299 *pcbData = it->second.cbData;
300
301 return VINF_SUCCESS;
302}
303
304/* Does not do locking! Caller has to take care of it because the caller needs to
305 * modify the data ...*/
306void* Guest::callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData)
307{
308 /* uContextID can be 0. */
309 /* pcbData is optional. */
310
311 CallbackMapIterConst it = mCallbackMap.find(uContextID);
312 if (it != mCallbackMap.end())
313 {
314 if (pcbData)
315 *pcbData = it->second.cbData;
316 return it->second.pvData;
317 }
318
319 return NULL;
320}
321
322int Guest::callbackInit(PVBOXGUESTCTRL_CALLBACK pCallback, eVBoxGuestCtrlCallbackType enmType,
323 ComPtr<Progress> pProgress)
324{
325 AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
326 /* Everything else is optional. */
327
328 int vrc = VINF_SUCCESS;
329 switch (enmType)
330 {
331 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
332 {
333 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
334 AssertPtrReturn(pData, VERR_NO_MEMORY);
335 RT_BZERO(pData, sizeof(CALLBACKDATAEXECSTATUS));
336 pCallback->cbData = sizeof(CALLBACKDATAEXECSTATUS);
337 pCallback->pvData = pData;
338 break;
339 }
340
341 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
342 {
343 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
344 AssertPtrReturn(pData, VERR_NO_MEMORY);
345 RT_BZERO(pData, sizeof(CALLBACKDATAEXECOUT));
346 pCallback->cbData = sizeof(CALLBACKDATAEXECOUT);
347 pCallback->pvData = pData;
348 break;
349 }
350
351 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
352 {
353 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
354 AssertPtrReturn(pData, VERR_NO_MEMORY);
355 RT_BZERO(pData, sizeof(CALLBACKDATAEXECINSTATUS));
356 pCallback->cbData = sizeof(CALLBACKDATAEXECINSTATUS);
357 pCallback->pvData = pData;
358 break;
359 }
360
361 default:
362 vrc = VERR_INVALID_PARAMETER;
363 break;
364 }
365
366 if (RT_SUCCESS(vrc))
367 {
368 /* Init/set common stuff. */
369 pCallback->mType = enmType;
370 pCallback->pProgress = pProgress;
371 }
372
373 return vrc;
374}
375
376bool Guest::callbackIsCanceled(uint32_t uContextID)
377{
378 if (!uContextID)
379 return true; /* If no context ID given then take a shortcut. */
380
381 ComPtr<IProgress> pProgress;
382 {
383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
384
385 CallbackMapIterConst it = mCallbackMap.find(uContextID);
386 if (it != mCallbackMap.end())
387 pProgress = it->second.pProgress;
388 }
389
390 if (pProgress)
391 {
392 BOOL fCanceled = FALSE;
393 HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
394 if ( SUCCEEDED(hRC)
395 && !fCanceled)
396 {
397 return false;
398 }
399 }
400
401 return true; /* No progress / error means canceled. */
402}
403
404bool Guest::callbackIsComplete(uint32_t uContextID)
405{
406 AssertReturn(uContextID, true);
407
408 ComPtr<IProgress> pProgress;
409 {
410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
411
412 CallbackMapIterConst it = mCallbackMap.find(uContextID);
413 if (it != mCallbackMap.end())
414 pProgress = it->second.pProgress;
415 }
416
417 if (pProgress)
418 {
419 BOOL fCompleted = FALSE;
420 HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
421 if ( SUCCEEDED(hRC)
422 && fCompleted)
423 {
424 return true;
425 }
426 }
427
428 return false;
429}
430
431int Guest::callbackMoveForward(uint32_t uContextID, const char *pszMessage)
432{
433 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
434 AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);
435
436 ComPtr<IProgress> pProgress;
437 {
438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
439
440 CallbackMapIterConst it = mCallbackMap.find(uContextID);
441 if (it != mCallbackMap.end())
442 pProgress = it->second.pProgress;
443 }
444
445 if (pProgress)
446 {
447 HRESULT hr = pProgress->SetNextOperation(Bstr(pszMessage).raw(), 1 /* Weight */);
448 if (FAILED(hr))
449 return VERR_CANCELLED;
450
451 return VINF_SUCCESS;
452 }
453
454 return VERR_NOT_FOUND;
455}
456
457/**
458 * Notifies a specified callback about its final status.
459 *
460 * @return IPRT status code.
461 * @param uContextID
462 * @param iRC
463 * @param pszMessage
464 */
465int Guest::callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage)
466{
467 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
468 if (RT_FAILURE(iRC))
469 AssertReturn(pszMessage, VERR_INVALID_PARAMETER);
470
471 LogFlowFunc(("Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n",
472 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
473
474 ComObjPtr<Progress> pProgress;
475 {
476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
477
478 CallbackMapIterConst it = mCallbackMap.find(uContextID);
479 if (it != mCallbackMap.end())
480 pProgress = it->second.pProgress;
481 }
482
483#if 0
484 BOOL fCanceled = FALSE;
485 HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
486 if ( SUCCEEDED(hRC)
487 && fCanceled)
488 {
489 /* If progress already canceled do nothing here. */
490 return VINF_SUCCESS;
491 }
492#endif
493
494 if (pProgress)
495 {
496 /*
497 * Assume we didn't complete to make sure we clean up even if the
498 * following call fails.
499 */
500 BOOL fCompleted = FALSE;
501 HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
502 if ( SUCCEEDED(hRC)
503 && !fCompleted)
504 {
505 LogFlowFunc(("Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n",
506 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
507
508 /*
509 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
510 * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
511 * is disconnecting without having the chance to sending a status message before, so we
512 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
513 * progress object to become signalled.
514 */
515 if (RT_SUCCESS(iRC))
516 {
517 hRC = pProgress->notifyComplete(S_OK);
518 }
519 else
520 {
521 hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,
522 COM_IIDOF(IGuest),
523 Guest::getStaticComponentName(),
524 pszMessage);
525 }
526
527 LogFlowFunc(("Notified callback with CID=%u returned %Rhrc (0x%x)\n",
528 uContextID, hRC, hRC));
529 }
530 else
531 LogFlowFunc(("Callback with CID=%u already notified\n", uContextID));
532
533 /*
534 * Do *not* NULL pProgress here, because waiting function like executeProcess()
535 * will still rely on this object for checking whether they have to give up!
536 */
537 }
538 /* If pProgress is not found (anymore) that's fine.
539 * Might be destroyed already. */
540 return S_OK;
541}
542
543/**
544 * Notifies all callbacks which are assigned to a certain guest PID to set a certain
545 * return/error code and an optional (error) message.
546 *
547 * @return IPRT status code.
548 * @param uGuestPID Guest PID to find all callbacks for.
549 * @param iRC Return (error) code to set for the found callbacks.
550 * @param pszMessage Optional (error) message to set.
551 */
552int Guest::callbackNotifyAllForPID(uint32_t uGuestPID, int iRC, const char *pszMessage)
553{
554 AssertReturn(uGuestPID, VERR_INVALID_PARAMETER);
555
556 int vrc = VINF_SUCCESS;
557
558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
559
560 CallbackMapIter it;
561 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
562 {
563 switch (it->second.mType)
564 {
565 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
566 break;
567
568 /* When waiting for process output while the process is destroyed,
569 * make sure we also destroy the actual waiting operation (internal progress object)
570 * in order to not block the caller. */
571 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
572 {
573 PCALLBACKDATAEXECOUT pItData = (PCALLBACKDATAEXECOUT)it->second.pvData;
574 AssertPtr(pItData);
575 if (pItData->u32PID == uGuestPID)
576 vrc = callbackNotifyEx(it->first, iRC, pszMessage);
577 break;
578 }
579
580 /* When waiting for injecting process input while the process is destroyed,
581 * make sure we also destroy the actual waiting operation (internal progress object)
582 * in order to not block the caller. */
583 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
584 {
585 PCALLBACKDATAEXECINSTATUS pItData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
586 AssertPtr(pItData);
587 if (pItData->u32PID == uGuestPID)
588 vrc = callbackNotifyEx(it->first, iRC, pszMessage);
589 break;
590 }
591
592 default:
593 vrc = VERR_INVALID_PARAMETER;
594 AssertMsgFailed(("Unknown callback type %d, iRC=%d, message=%s\n",
595 it->second.mType, iRC, pszMessage ? pszMessage : "<No message given>"));
596 break;
597 }
598
599 if (RT_FAILURE(vrc))
600 break;
601 }
602
603 return vrc;
604}
605
606int Guest::callbackNotifyComplete(uint32_t uContextID)
607{
608 return callbackNotifyEx(uContextID, S_OK, NULL /* No message */);
609}
610
611/**
612 * Waits for a callback (using its context ID) to complete.
613 *
614 * @return IPRT status code.
615 * @param uContextID Context ID to wait for.
616 * @param lStage Stage to wait for. Specify -1 if no staging is present/required.
617 * Specifying a stage is only needed if there's a multi operation progress
618 * object to wait for.
619 * @param lTimeout Timeout (in ms) to wait for.
620 */
621int Guest::callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout)
622{
623 AssertReturn(uContextID, VERR_INVALID_PARAMETER);
624
625 /*
626 * Wait for the HGCM low level callback until the process
627 * has been started (or something went wrong). This is necessary to
628 * get the PID.
629 */
630
631 int vrc = VINF_SUCCESS;
632 ComPtr<IProgress> pProgress;
633 {
634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
635
636 CallbackMapIterConst it = mCallbackMap.find(uContextID);
637 if (it != mCallbackMap.end())
638 pProgress = it->second.pProgress;
639 else
640 vrc = VERR_NOT_FOUND;
641 }
642
643 if (RT_SUCCESS(vrc))
644 {
645 LogFlowFunc(("Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n",
646 uContextID, lStage, lTimeout));
647 HRESULT rc;
648 if (lStage < 0)
649 rc = pProgress->WaitForCompletion(lTimeout);
650 else
651 rc = pProgress->WaitForOperationCompletion((ULONG)lStage, lTimeout);
652 if (SUCCEEDED(rc))
653 {
654 if (!callbackIsComplete(uContextID))
655 vrc = callbackIsCanceled(uContextID)
656 ? VERR_CANCELLED : VINF_SUCCESS;
657 }
658 else
659 vrc = VERR_TIMEOUT;
660 }
661
662 LogFlowFunc(("Callback (CID=%u) completed with rc=%Rrc\n",
663 uContextID, vrc));
664 return vrc;
665}
666
667/**
668 * Static callback function for receiving updates on guest control commands
669 * from the guest. Acts as a dispatcher for the actual class instance.
670 *
671 * @returns VBox status code.
672 *
673 * @todo
674 *
675 */
676DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
677 uint32_t u32Function,
678 void *pvParms,
679 uint32_t cbParms)
680{
681 using namespace guestControl;
682
683 /*
684 * No locking, as this is purely a notification which does not make any
685 * changes to the object state.
686 */
687 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
688 pvExtension, u32Function, pvParms, cbParms));
689 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
690 Assert(!pGuest.isNull());
691
692 /*
693 * For guest control 2.0 using the legacy commands we need to do the following here:
694 * - Get the callback header to access the context ID
695 * - Get the context ID of the callback
696 * - Extract the session ID out of the context ID
697 * - Dispatch the whole stuff to the appropriate session (if still exists)
698 */
699
700 PCALLBACKHEADER pHeader = (PCALLBACKHEADER)pvParms;
701 AssertPtr(pHeader);
702
703#ifdef DEBUG
704 LogFlowFunc(("uSession=%RU32, uProcess=%RU32, uCount=%RU32\n",
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 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2689 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2690
2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 int rc;
2694 GuestSessions::const_iterator itSession
2695 = mData.mGuestSessions.find(VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID));
2696 if (itSession != mData.mGuestSessions.end())
2697 {
2698 ComObjPtr<GuestSession> pSession(itSession->second);
2699 Assert(!pSession.isNull());
2700
2701 alock.release();
2702 rc = pSession->dispatchToProcess(uContextID, uFunction, pvData, cbData);
2703 }
2704 else
2705 rc = VERR_NOT_FOUND;
2706
2707 LogFlowFuncLeaveRC(rc);
2708 return rc;
2709}
2710
2711int Guest::sessionClose(ComObjPtr<GuestSession> pSession)
2712{
2713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
2716 itSessions != mData.mGuestSessions.end(); ++itSessions)
2717 {
2718 if (pSession == itSessions->second)
2719 {
2720 mData.mGuestSessions.erase(itSessions);
2721 return VINF_SUCCESS;
2722 }
2723 }
2724
2725 return VERR_NOT_FOUND;
2726}
2727
2728int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, const Utf8Str &strDomain,
2729 const Utf8Str &strSessionName, ComObjPtr<GuestSession> &pGuestSession)
2730{
2731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 int rc = VERR_MAX_PROCS_REACHED;
2734 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
2735 return rc;
2736
2737 try
2738 {
2739 /* Create a new session ID and assign it. */
2740 uint32_t uNewSessionID = 0;
2741 uint32_t uTries = 0;
2742
2743 for (;;)
2744 {
2745 /* Is the context ID already used? */
2746 if (!sessionExists(uNewSessionID))
2747 {
2748 rc = VINF_SUCCESS;
2749 break;
2750 }
2751 uNewSessionID++;
2752
2753 if (++uTries == UINT32_MAX)
2754 break; /* Don't try too hard. */
2755 }
2756 if (RT_FAILURE(rc)) throw rc;
2757
2758 /* Create the session object. */
2759 HRESULT hr = pGuestSession.createObject();
2760 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
2761
2762 rc = pGuestSession->init(this, uNewSessionID,
2763 strUser, strPassword, strDomain, strSessionName);
2764 if (RT_FAILURE(rc)) throw rc;
2765
2766 mData.mGuestSessions[uNewSessionID] = pGuestSession;
2767 }
2768 catch (int rc2)
2769 {
2770 rc = rc2;
2771 }
2772
2773 return rc;
2774}
2775
2776inline bool Guest::sessionExists(uint32_t uSessionID)
2777{
2778 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
2779 return (itSessions == mData.mGuestSessions.end()) ? false : true;
2780}
2781
2782// implementation of public methods
2783/////////////////////////////////////////////////////////////////////////////
2784
2785STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain, IN_BSTR aSessionName, IGuestSession **aGuestSession)
2786{
2787#ifndef VBOX_WITH_GUEST_CONTROL
2788 ReturnComNotImplemented();
2789#else /* VBOX_WITH_GUEST_CONTROL */
2790
2791 LogFlowFuncEnter();
2792
2793 /* Do not allow anonymous sessions (with system rights) with official API. */
2794 if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
2795 return setError(E_INVALIDARG, tr("No user name specified"));
2796 CheckComArgOutPointerValid(aGuestSession);
2797 /* Rest is optional. */
2798
2799 AutoCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 HRESULT hr = S_OK;
2803
2804 ComObjPtr<GuestSession> pSession;
2805 int rc = sessionCreate(aUser, aPassword, aDomain, aSessionName, pSession);
2806 if (RT_SUCCESS(rc))
2807 {
2808 /* Return guest session to the caller. */
2809 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
2810 if (FAILED(hr2))
2811 rc = VERR_COM_OBJECT_NOT_FOUND;
2812 }
2813
2814 if (RT_FAILURE(rc))
2815 {
2816 switch (rc)
2817 {
2818 case VERR_MAX_PROCS_REACHED:
2819 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
2820 VERR_MAX_PROCS_REACHED);
2821 break;
2822
2823 /** @todo Add more errors here. */
2824
2825 default:
2826 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session, rc=%Rrc"), rc);
2827 break;
2828 }
2829 }
2830
2831 LogFlowFuncLeaveRC(rc);
2832 return hr;
2833#endif /* VBOX_WITH_GUEST_CONTROL */
2834}
2835
2836STDMETHODIMP Guest::FindSession(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions))
2837{
2838 ReturnComNotImplemented();
2839}
2840
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