VirtualBox

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

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