VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp@ 45076

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

GuestCtrl: *_NOTFIY_* -> *_NOTIFY_*.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 45076 2013-03-18 20:07:37Z vboxsync $ */
2/** @file
3 *
4 * Internal helpers/structures for guest control functionality.
5 */
6
7/*
8 * Copyright (C) 2011-2013 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/******************************************************************************
20 * Header Files *
21 ******************************************************************************/
22#include "GuestCtrlImplPrivate.h"
23#include "GuestSessionImpl.h"
24#include "VMMDev.h"
25
26#include <iprt/asm.h>
27#include <iprt/ctype.h>
28#ifdef DEBUG
29# include <iprt/file.h>
30#endif /* DEBUG */
31
32#ifdef LOG_GROUP
33 #undef LOG_GROUP
34#endif
35#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
36#include <VBox/log.h>
37
38/******************************************************************************
39 * Structures and Typedefs *
40 ******************************************************************************/
41
42GuestCtrlEvent::GuestCtrlEvent(void)
43 : fCanceled(false),
44 fCompleted(false),
45 hEventSem(NIL_RTSEMEVENT),
46 mRC(VINF_SUCCESS)
47{
48}
49
50GuestCtrlEvent::~GuestCtrlEvent(void)
51{
52 Destroy();
53}
54
55int GuestCtrlEvent::Cancel(void)
56{
57 int rc = VINF_SUCCESS;
58 if (!ASMAtomicReadBool(&fCompleted))
59 {
60 if (!ASMAtomicReadBool(&fCanceled))
61 {
62 ASMAtomicXchgBool(&fCanceled, true);
63
64 LogFlowThisFunc(("Cancelling event ...\n"));
65 rc = hEventSem != NIL_RTSEMEVENT
66 ? RTSemEventSignal(hEventSem) : VINF_SUCCESS;
67 }
68 }
69
70 return rc;
71}
72
73bool GuestCtrlEvent::Canceled(void)
74{
75 return ASMAtomicReadBool(&fCanceled);
76}
77
78void GuestCtrlEvent::Destroy(void)
79{
80 int rc = Cancel();
81 AssertRC(rc);
82
83 if (hEventSem != NIL_RTSEMEVENT)
84 {
85 RTSemEventDestroy(hEventSem);
86 hEventSem = NIL_RTSEMEVENT;
87 }
88}
89
90int GuestCtrlEvent::Init(void)
91{
92 return RTSemEventCreate(&hEventSem);
93}
94
95int GuestCtrlEvent::Signal(int rc /*= VINF_SUCCESS*/)
96{
97 AssertReturn(hEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
98
99 mRC = rc;
100
101 return RTSemEventSignal(hEventSem);
102}
103
104int GuestCtrlEvent::Wait(ULONG uTimeoutMS)
105{
106 LogFlowThisFuncEnter();
107
108 AssertReturn(hEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
109
110 RTMSINTERVAL msInterval = uTimeoutMS;
111 if (!uTimeoutMS)
112 msInterval = RT_INDEFINITE_WAIT;
113 int rc = RTSemEventWait(hEventSem, msInterval);
114 if (RT_SUCCESS(rc))
115 ASMAtomicWriteBool(&fCompleted, true);
116
117 LogFlowFuncLeaveRC(rc);
118 return rc;
119}
120
121///////////////////////////////////////////////////////////////////////////////
122
123GuestCtrlCallback::GuestCtrlCallback(void)
124 : pvData(NULL),
125 cbData(0),
126 mType(CALLBACKTYPE_UNKNOWN),
127 uFlags(0),
128 pvPayload(NULL),
129 cbPayload(0)
130{
131}
132
133GuestCtrlCallback::GuestCtrlCallback(CALLBACKTYPE enmType)
134 : pvData(NULL),
135 cbData(0),
136 mType(CALLBACKTYPE_UNKNOWN),
137 uFlags(0),
138 pvPayload(NULL),
139 cbPayload(0)
140{
141 int rc = Init(enmType);
142 AssertRC(rc);
143}
144
145GuestCtrlCallback::~GuestCtrlCallback(void)
146{
147 Destroy();
148}
149
150int GuestCtrlCallback::Init(CALLBACKTYPE enmType)
151{
152 AssertReturn(enmType > CALLBACKTYPE_UNKNOWN, VERR_INVALID_PARAMETER);
153 Assert((pvData == NULL) && !cbData);
154
155 int rc = VINF_SUCCESS;
156
157 switch (enmType)
158 {
159 case CALLBACKTYPE_SESSION_NOTIFY:
160 {
161 pvData = (PCALLBACKDATA_SESSION_NOTIFY)RTMemAllocZ(sizeof(CALLBACKDATA_SESSION_NOTIFY));
162 AssertPtrReturn(pvData, VERR_NO_MEMORY);
163 cbData = sizeof(CALLBACKDATA_SESSION_NOTIFY);
164 break;
165 }
166
167 case CALLBACKTYPE_PROC_STATUS:
168 {
169 pvData = (PCALLBACKDATA_PROC_STATUS)RTMemAllocZ(sizeof(CALLBACKDATA_PROC_STATUS));
170 AssertPtrReturn(pvData, VERR_NO_MEMORY);
171 cbData = sizeof(CALLBACKDATA_PROC_STATUS);
172 break;
173 }
174
175 case CALLBACKTYPE_PROC_OUTPUT:
176 {
177 pvData = (PCALLBACKDATA_PROC_OUTPUT)RTMemAllocZ(sizeof(CALLBACKDATA_PROC_OUTPUT));
178 AssertPtrReturn(pvData, VERR_NO_MEMORY);
179 cbData = sizeof(CALLBACKDATA_PROC_OUTPUT);
180 break;
181 }
182
183 case CALLBACKTYPE_PROC_INPUT:
184 {
185 pvData = (PCALLBACKDATA_PROC_INPUT)RTMemAllocZ(sizeof(CALLBACKDATA_PROC_INPUT));
186 AssertPtrReturn(pvData, VERR_NO_MEMORY);
187 cbData = sizeof(CALLBACKDATA_PROC_INPUT);
188 break;
189 }
190
191 default:
192 AssertMsgFailed(("Unknown callback type specified (%ld)\n", enmType));
193 rc = VERR_NOT_IMPLEMENTED;
194 break;
195 }
196
197 if (RT_SUCCESS(rc))
198 {
199 rc = GuestCtrlEvent::Init();
200 if (RT_SUCCESS(rc))
201 mType = enmType;
202 }
203
204 return rc;
205}
206
207void GuestCtrlCallback::Destroy(void)
208{
209 GuestCtrlEvent::Destroy();
210
211 switch (mType)
212 {
213 case CALLBACKTYPE_PROC_OUTPUT:
214 {
215 PCALLBACKDATA_PROC_OUTPUT pThis = (PCALLBACKDATA_PROC_OUTPUT)pvData;
216 AssertPtr(pThis);
217 if (pThis->pvData)
218 RTMemFree(pThis->pvData);
219 break;
220 }
221
222 case CALLBACKTYPE_FILE_READ:
223 {
224 PCALLBACKPAYLOAD_FILE_NOTIFY_READ pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_READ)pvData;
225 AssertPtr(pThis);
226 if (pThis->pvData)
227 RTMemFree(pThis->pvData);
228 break;
229 }
230
231 default:
232 break;
233 }
234
235 mType = CALLBACKTYPE_UNKNOWN;
236 if (pvData)
237 {
238 RTMemFree(pvData);
239 pvData = NULL;
240 }
241 cbData = 0;
242
243 if (pvPayload)
244 {
245 RTMemFree(pvPayload);
246 pvPayload = NULL;
247 }
248 cbPayload = 0;
249}
250
251int GuestCtrlCallback::SetData(const void *pvCallback, size_t cbCallback)
252{
253 if (!cbCallback)
254 return VINF_SUCCESS;
255 AssertPtr(pvCallback);
256
257 int rc = VINF_SUCCESS;
258 switch (mType)
259 {
260 case CALLBACKTYPE_SESSION_NOTIFY:
261 {
262 PCALLBACKDATA_SESSION_NOTIFY pThis = (PCALLBACKDATA_SESSION_NOTIFY)pvData;
263 PCALLBACKDATA_SESSION_NOTIFY pCB = (PCALLBACKDATA_SESSION_NOTIFY)pvCallback;
264 Assert(cbCallback == sizeof(CALLBACKDATA_SESSION_NOTIFY));
265
266 pThis->uType = pCB->uType;
267 pThis->uResult = pCB->uResult;
268 break;
269 }
270
271 case CALLBACKTYPE_PROC_STATUS:
272 {
273 PCALLBACKDATA_PROC_STATUS pThis = (PCALLBACKDATA_PROC_STATUS)pvData;
274 PCALLBACKDATA_PROC_STATUS pCB = (PCALLBACKDATA_PROC_STATUS)pvCallback;
275 Assert(cbCallback == sizeof(CALLBACKDATA_PROC_STATUS));
276
277 pThis->uFlags = pCB->uFlags;
278 pThis->uPID = pCB->uPID;
279 pThis->uStatus = pCB->uStatus;
280 break;
281 }
282
283 case CALLBACKTYPE_PROC_OUTPUT:
284 {
285 PCALLBACKDATA_PROC_OUTPUT pThis = (PCALLBACKDATA_PROC_OUTPUT)pvData;
286 PCALLBACKDATA_PROC_OUTPUT pCB = (PCALLBACKDATA_PROC_OUTPUT)pvCallback;
287 Assert(cbCallback == sizeof(CALLBACKDATA_PROC_OUTPUT));
288
289 if (pCB->cbData)
290 {
291 pThis->pvData = RTMemAlloc(pCB->cbData);
292 AssertPtrReturn(pThis->pvData, VERR_NO_MEMORY);
293 memcpy(pThis->pvData, pCB->pvData, pCB->cbData);
294 pThis->cbData = pCB->cbData;
295 }
296 pThis->uFlags = pCB->uFlags;
297 pThis->uPID = pCB->uPID;
298 break;
299 }
300
301 case CALLBACKTYPE_PROC_INPUT:
302 {
303 PCALLBACKDATA_PROC_INPUT pThis = (PCALLBACKDATA_PROC_INPUT)pvData;
304 PCALLBACKDATA_PROC_INPUT pCB = (PCALLBACKDATA_PROC_INPUT)pvCallback;
305 Assert(cbCallback == sizeof(CALLBACKDATA_PROC_INPUT));
306
307 pThis->uProcessed = pCB->uProcessed;
308 pThis->uFlags = pCB->uFlags;
309 pThis->uPID = pCB->uPID;
310 pThis->uStatus = pCB->uStatus;
311 break;
312 }
313
314 case CALLBACKTYPE_FILE_OPEN:
315 {
316 PCALLBACKPAYLOAD_FILE_NOTIFY_OPEN pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_OPEN)pvData;
317 PCALLBACKPAYLOAD_FILE_NOTIFY_OPEN pCB = (PCALLBACKPAYLOAD_FILE_NOTIFY_OPEN)pvCallback;
318 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTIFY_OPEN));
319
320 pThis->rc = pCB->rc;
321 pThis->uHandle = pCB->uHandle;
322 break;
323 }
324
325 case CALLBACKTYPE_FILE_CLOSE:
326 {
327 PCALLBACKPAYLOAD_FILE_NOTIFY_CLOSE pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_CLOSE)pvData;
328 PCALLBACKPAYLOAD_FILE_NOTIFY_CLOSE pCB = (PCALLBACKPAYLOAD_FILE_NOTIFY_CLOSE)pvCallback;
329 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTIFY_CLOSE));
330
331 pThis->rc = pCB->rc;
332 break;
333 }
334
335 case CALLBACKTYPE_FILE_READ:
336 {
337 PCALLBACKPAYLOAD_FILE_NOTIFY_READ pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_READ)pvData;
338 PCALLBACKPAYLOAD_FILE_NOTIFY_READ pCB = (PCALLBACKPAYLOAD_FILE_NOTIFY_READ)pvCallback;
339 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTIFY_READ));
340
341 pThis->rc = pCB->rc;
342 if (pCB->cbData)
343 {
344 pThis->pvData = RTMemAlloc(pCB->cbData);
345 AssertPtrReturn(pThis->pvData, VERR_NO_MEMORY);
346 memcpy(pThis->pvData, pCB->pvData, pCB->cbData);
347 pThis->cbData = pCB->cbData;
348 }
349 break;
350 }
351
352 case CALLBACKTYPE_FILE_WRITE:
353 {
354 PCALLBACKPAYLOAD_FILE_NOTIFY_WRITE pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_WRITE)pvData;
355 PCALLBACKPAYLOAD_FILE_NOTIFY_WRITE pCB = (PCALLBACKPAYLOAD_FILE_NOTIFY_WRITE)pvCallback;
356 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTIFY_WRITE));
357
358 pThis->rc = pCB->rc;
359 pThis->cbWritten = pCB->cbWritten;
360 break;
361 }
362
363 case CALLBACKTYPE_FILE_SEEK:
364 {
365 PCALLBACKPAYLOAD_FILE_NOTIFY_SEEK pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_SEEK)pvData;
366 PCALLBACKPAYLOAD_FILE_NOTIFY_SEEK pCB = (PCALLBACKPAYLOAD_FILE_NOTIFY_SEEK)pvCallback;
367 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTFIY_SEEK));
368
369 pThis->rc = pCB->rc;
370 pThis->uOffActual = pCB->uOffActual;
371 break;
372 }
373
374 case CALLBACKTYPE_FILE_TELL:
375 {
376 PCALLBACKPAYLOAD_FILE_NOTIFY_TELL pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_TELL)pvData;
377 PCALLBACKPAYLOAD_FILE_NOTIFY_TELL pCB = (PCALLBACKPAYLOAD_FILE_NOTIFY_TELL)pvCallback;
378 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTFIY_TELL));
379
380 pThis->rc = pCB->rc;
381 pThis->uOffActual = pCB->uOffActual;
382 break;
383 }
384
385 default:
386 AssertMsgFailed(("Callback type not supported (%ld)\n", mType));
387 rc = VERR_NOT_SUPPORTED;
388 break;
389 }
390
391 return rc;
392}
393
394int GuestCtrlCallback::SetPayload(const void *pvToWrite, size_t cbToWrite)
395{
396 if (!cbToWrite)
397 return VINF_SUCCESS;
398 AssertPtr(pvToWrite);
399
400 Assert(pvPayload == NULL); /* Can't reuse callbacks! */
401 pvPayload = RTMemAlloc(cbToWrite);
402 if (!pvPayload)
403 return VERR_NO_MEMORY;
404
405 memcpy(pvPayload, pvToWrite, cbToWrite);
406 cbPayload = cbToWrite;
407
408 return VINF_SUCCESS;
409}
410
411///////////////////////////////////////////////////////////////////////////////
412
413GuestProcessWaitEvent::GuestProcessWaitEvent(void)
414 : mFlags(0),
415 mResult(ProcessWaitResult_None)
416{
417}
418
419GuestProcessWaitEvent::GuestProcessWaitEvent(uint32_t uWaitFlags)
420 : mFlags(uWaitFlags)
421{
422 int rc = GuestCtrlEvent::Init();
423 AssertRC(rc);
424}
425
426GuestProcessWaitEvent::~GuestProcessWaitEvent(void)
427{
428 Destroy();
429}
430
431void GuestProcessWaitEvent::Destroy(void)
432{
433 GuestCtrlEvent::Destroy();
434
435 mFlags = ProcessWaitForFlag_None;
436}
437
438int GuestProcessWaitEvent::Signal(ProcessWaitResult_T enmResult, int rc /*= VINF_SUCCESS*/)
439{
440 mResult = enmResult;
441
442 return GuestCtrlEvent::Signal(rc);
443}
444
445///////////////////////////////////////////////////////////////////////////////
446
447GuestSessionWaitEvent::GuestSessionWaitEvent(void)
448 : mFlags(0),
449 mResult(GuestSessionWaitResult_None)
450{
451}
452
453GuestSessionWaitEvent::GuestSessionWaitEvent(uint32_t uWaitFlags)
454 : mFlags(uWaitFlags)
455{
456 int rc = GuestCtrlEvent::Init();
457 AssertRC(rc);
458}
459
460GuestSessionWaitEvent::~GuestSessionWaitEvent(void)
461{
462 Destroy();
463}
464
465void GuestSessionWaitEvent::Destroy(void)
466{
467 GuestCtrlEvent::Destroy();
468
469 mFlags = GuestSessionWaitForFlag_None;
470}
471
472int GuestSessionWaitEvent::Signal(GuestSessionWaitResult_T enmResult, int rc /*= VINF_SUCCESS*/)
473{
474 mResult = enmResult;
475
476 return GuestCtrlEvent::Signal(rc);
477}
478
479///////////////////////////////////////////////////////////////////////////////
480
481int GuestEnvironment::BuildEnvironmentBlock(void **ppvEnv, size_t *pcbEnv, uint32_t *pcEnvVars)
482{
483 AssertPtrReturn(ppvEnv, VERR_INVALID_POINTER);
484 /* Rest is optional. */
485
486 size_t cbEnv = 0;
487 uint32_t cEnvVars = 0;
488
489 int rc = VINF_SUCCESS;
490
491 size_t cEnv = mEnvironment.size();
492 if (cEnv)
493 {
494 std::map<Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.begin();
495 for (; itEnv != mEnvironment.end() && RT_SUCCESS(rc); itEnv++)
496 {
497 char *pszEnv;
498 if (!RTStrAPrintf(&pszEnv, "%s=%s", itEnv->first.c_str(), itEnv->second.c_str()))
499 {
500 rc = VERR_NO_MEMORY;
501 break;
502 }
503 AssertPtr(pszEnv);
504 rc = appendToEnvBlock(pszEnv, ppvEnv, &cbEnv, &cEnvVars);
505 RTStrFree(pszEnv);
506 }
507 Assert(cEnv == cEnvVars);
508 }
509
510 if (pcbEnv)
511 *pcbEnv = cbEnv;
512 if (pcEnvVars)
513 *pcEnvVars = cEnvVars;
514
515 return rc;
516}
517
518void GuestEnvironment::Clear(void)
519{
520 mEnvironment.clear();
521}
522
523int GuestEnvironment::CopyFrom(const GuestEnvironmentArray &environment)
524{
525 int rc = VINF_SUCCESS;
526
527 for (GuestEnvironmentArray::const_iterator it = environment.begin();
528 it != environment.end() && RT_SUCCESS(rc);
529 ++it)
530 {
531 rc = Set((*it));
532 }
533
534 return rc;
535}
536
537int GuestEnvironment::CopyTo(GuestEnvironmentArray &environment)
538{
539 size_t s = 0;
540 for (std::map<Utf8Str, Utf8Str>::const_iterator it = mEnvironment.begin();
541 it != mEnvironment.end();
542 ++it, ++s)
543 {
544 environment[s] = Bstr(it->first + "=" + it->second).raw();
545 }
546
547 return VINF_SUCCESS;
548}
549
550/* static */
551void GuestEnvironment::FreeEnvironmentBlock(void *pvEnv)
552{
553 if (pvEnv)
554 RTMemFree(pvEnv);
555}
556
557Utf8Str GuestEnvironment::Get(size_t nPos)
558{
559 size_t curPos = 0;
560 std::map<Utf8Str, Utf8Str>::const_iterator it = mEnvironment.begin();
561 for (; it != mEnvironment.end() && curPos < nPos;
562 ++it, ++curPos) { }
563
564 if (it != mEnvironment.end())
565 return Utf8Str(it->first + "=" + it->second);
566
567 return Utf8Str("");
568}
569
570Utf8Str GuestEnvironment::Get(const Utf8Str &strKey)
571{
572 std::map <Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.find(strKey);
573 Utf8Str strRet;
574 if (itEnv != mEnvironment.end())
575 strRet = itEnv->second;
576 return strRet;
577}
578
579bool GuestEnvironment::Has(const Utf8Str &strKey)
580{
581 std::map <Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.find(strKey);
582 return (itEnv != mEnvironment.end());
583}
584
585int GuestEnvironment::Set(const Utf8Str &strKey, const Utf8Str &strValue)
586{
587 /** @todo Do some validation using regex. */
588 if (strKey.isEmpty())
589 return VERR_INVALID_PARAMETER;
590
591 int rc = VINF_SUCCESS;
592 const char *pszString = strKey.c_str();
593 while (*pszString != '\0' && RT_SUCCESS(rc))
594 {
595 if ( !RT_C_IS_ALNUM(*pszString)
596 && !RT_C_IS_GRAPH(*pszString))
597 rc = VERR_INVALID_PARAMETER;
598 *pszString++;
599 }
600
601 if (RT_SUCCESS(rc))
602 mEnvironment[strKey] = strValue;
603
604 return rc;
605}
606
607int GuestEnvironment::Set(const Utf8Str &strPair)
608{
609 RTCList<RTCString> listPair = strPair.split("=", RTCString::KeepEmptyParts);
610 /* Skip completely empty pairs. Note that we still need pairs with a valid
611 * (set) key and an empty value. */
612 if (listPair.size() <= 1)
613 return VINF_SUCCESS;
614
615 int rc = VINF_SUCCESS;
616 size_t p = 0;
617 while(p < listPair.size() && RT_SUCCESS(rc))
618 {
619 Utf8Str strKey = listPair.at(p++);
620 if ( strKey.isEmpty()
621 || strKey.equals("=")) /* Skip pairs with empty keys (e.g. "=FOO"). */
622 {
623 break;
624 }
625 Utf8Str strValue;
626 if (p < listPair.size()) /* Does the list also contain a value? */
627 strValue = listPair.at(p++);
628
629#ifdef DEBUG
630 LogFlowFunc(("strKey=%s, strValue=%s\n",
631 strKey.c_str(), strValue.c_str()));
632#endif
633 rc = Set(strKey, strValue);
634 }
635
636 return rc;
637}
638
639size_t GuestEnvironment::Size(void)
640{
641 return mEnvironment.size();
642}
643
644int GuestEnvironment::Unset(const Utf8Str &strKey)
645{
646 std::map <Utf8Str, Utf8Str>::iterator itEnv = mEnvironment.find(strKey);
647 if (itEnv != mEnvironment.end())
648 {
649 mEnvironment.erase(itEnv);
650 return VINF_SUCCESS;
651 }
652
653 return VERR_NOT_FOUND;
654}
655
656GuestEnvironment& GuestEnvironment::operator=(const GuestEnvironmentArray &that)
657{
658 CopyFrom(that);
659 return *this;
660}
661
662GuestEnvironment& GuestEnvironment::operator=(const GuestEnvironment &that)
663{
664 for (std::map<Utf8Str, Utf8Str>::const_iterator it = that.mEnvironment.begin();
665 it != that.mEnvironment.end();
666 ++it)
667 {
668 mEnvironment[it->first] = it->second;
669 }
670
671 return *this;
672}
673
674/**
675 * Appends environment variables to the environment block.
676 *
677 * Each var=value pair is separated by the null character ('\\0'). The whole
678 * block will be stored in one blob and disassembled on the guest side later to
679 * fit into the HGCM param structure.
680 *
681 * @returns VBox status code.
682 *
683 * @param pszEnvVar The environment variable=value to append to the
684 * environment block.
685 * @param ppvList This is actually a pointer to a char pointer
686 * variable which keeps track of the environment block
687 * that we're constructing.
688 * @param pcbList Pointer to the variable holding the current size of
689 * the environment block. (List is a misnomer, go
690 * ahead a be confused.)
691 * @param pcEnvVars Pointer to the variable holding count of variables
692 * stored in the environment block.
693 */
694int GuestEnvironment::appendToEnvBlock(const char *pszEnv, void **ppvList, size_t *pcbList, uint32_t *pcEnvVars)
695{
696 int rc = VINF_SUCCESS;
697 size_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
698 if (*ppvList)
699 {
700 size_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
701 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
702 if (pvTmp == NULL)
703 rc = VERR_NO_MEMORY;
704 else
705 {
706 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
707 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
708 *ppvList = (void **)pvTmp;
709 }
710 }
711 else
712 {
713 char *pszTmp;
714 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
715 {
716 *ppvList = (void **)pszTmp;
717 /* Reset counters. */
718 *pcEnvVars = 0;
719 *pcbList = 0;
720 }
721 }
722 if (RT_SUCCESS(rc))
723 {
724 *pcbList += cchEnv + 1; /* Include zero termination. */
725 *pcEnvVars += 1; /* Increase env variable count. */
726 }
727 return rc;
728}
729
730int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk)
731{
732 LogFlowFunc(("\n"));
733
734 int rc = VINF_SUCCESS;
735
736 try
737 {
738#ifdef DEBUG
739 strmBlk.DumpToLog();
740#endif
741 /* Object name. */
742 mName = strmBlk.GetString("name");
743 if (mName.isEmpty()) throw VERR_NOT_FOUND;
744 /* Type. */
745 Utf8Str strType(strmBlk.GetString("ftype"));
746 if (strType.equalsIgnoreCase("-"))
747 mType = FsObjType_File;
748 else if (strType.equalsIgnoreCase("d"))
749 mType = FsObjType_Directory;
750 /** @todo Add more types! */
751 else
752 mType = FsObjType_Undefined;
753 /* Object size. */
754 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
755 if (RT_FAILURE(rc)) throw rc;
756 /** @todo Add complete ls info! */
757 }
758 catch (int rc2)
759 {
760 rc = rc2;
761 }
762
763 LogFlowFuncLeaveRC(rc);
764 return rc;
765}
766
767int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
768{
769 LogFlowFunc(("\n"));
770
771 int rc = VINF_SUCCESS;
772
773 try
774 {
775#ifdef DEBUG
776 strmBlk.DumpToLog();
777#endif
778 /* Node ID, optional because we don't include this
779 * in older VBoxService (< 4.2) versions. */
780 mNodeID = strmBlk.GetInt64("node_id");
781 /* Object name. */
782 mName = strmBlk.GetString("name");
783 if (mName.isEmpty()) throw VERR_NOT_FOUND;
784 /* Type. */
785 Utf8Str strType(strmBlk.GetString("ftype"));
786 if (strType.equalsIgnoreCase("-"))
787 mType = FsObjType_File;
788 else if (strType.equalsIgnoreCase("d"))
789 mType = FsObjType_Directory;
790 /** @todo Add more types! */
791 else
792 mType = FsObjType_Undefined;
793 /* Object size. */
794 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
795 if (RT_FAILURE(rc)) throw rc;
796 /** @todo Add complete stat info! */
797 }
798 catch (int rc2)
799 {
800 rc = rc2;
801 }
802
803 LogFlowFuncLeaveRC(rc);
804 return rc;
805}
806
807///////////////////////////////////////////////////////////////////////////////
808
809/** @todo *NOT* thread safe yet! */
810/** @todo Add exception handling for STL stuff! */
811
812GuestProcessStreamBlock::GuestProcessStreamBlock(void)
813{
814
815}
816
817/*
818GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
819{
820 for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
821 it != otherBlock.end(); it++)
822 {
823 mPairs[it->first] = new
824 if (it->second.pszValue)
825 {
826 RTMemFree(it->second.pszValue);
827 it->second.pszValue = NULL;
828 }
829 }
830}*/
831
832GuestProcessStreamBlock::~GuestProcessStreamBlock()
833{
834 Clear();
835}
836
837/**
838 * Destroys the currently stored stream pairs.
839 *
840 * @return IPRT status code.
841 */
842void GuestProcessStreamBlock::Clear(void)
843{
844 mPairs.clear();
845}
846
847#ifdef DEBUG
848void GuestProcessStreamBlock::DumpToLog(void) const
849{
850 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
851 this, mPairs.size()));
852
853 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
854 it != mPairs.end(); it++)
855 {
856 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
857 }
858}
859#endif
860
861/**
862 * Returns a 64-bit signed integer of a specified key.
863 *
864 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
865 * @param pszKey Name of key to get the value for.
866 * @param piVal Pointer to value to return.
867 */
868int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
869{
870 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
871 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
872 const char *pszValue = GetString(pszKey);
873 if (pszValue)
874 {
875 *piVal = RTStrToInt64(pszValue);
876 return VINF_SUCCESS;
877 }
878 return VERR_NOT_FOUND;
879}
880
881/**
882 * Returns a 64-bit integer of a specified key.
883 *
884 * @return int64_t Value to return, 0 if not found / on failure.
885 * @param pszKey Name of key to get the value for.
886 */
887int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
888{
889 int64_t iVal;
890 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
891 return iVal;
892 return 0;
893}
894
895/**
896 * Returns the current number of stream pairs.
897 *
898 * @return uint32_t Current number of stream pairs.
899 */
900size_t GuestProcessStreamBlock::GetCount(void) const
901{
902 return mPairs.size();
903}
904
905/**
906 * Returns a string value of a specified key.
907 *
908 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
909 * @param pszKey Name of key to get the value for.
910 */
911const char* GuestProcessStreamBlock::GetString(const char *pszKey) const
912{
913 AssertPtrReturn(pszKey, NULL);
914
915 try
916 {
917 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey));
918 if (itPairs != mPairs.end())
919 return itPairs->second.mValue.c_str();
920 }
921 catch (const std::exception &ex)
922 {
923 NOREF(ex);
924 }
925 return NULL;
926}
927
928/**
929 * Returns a 32-bit unsigned integer of a specified key.
930 *
931 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
932 * @param pszKey Name of key to get the value for.
933 * @param puVal Pointer to value to return.
934 */
935int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
936{
937 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
938 AssertPtrReturn(puVal, VERR_INVALID_POINTER);
939 const char *pszValue = GetString(pszKey);
940 if (pszValue)
941 {
942 *puVal = RTStrToUInt32(pszValue);
943 return VINF_SUCCESS;
944 }
945 return VERR_NOT_FOUND;
946}
947
948/**
949 * Returns a 32-bit unsigned integer of a specified key.
950 *
951 * @return uint32_t Value to return, 0 if not found / on failure.
952 * @param pszKey Name of key to get the value for.
953 */
954uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey) const
955{
956 uint32_t uVal;
957 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
958 return uVal;
959 return 0;
960}
961
962/**
963 * Sets a value to a key or deletes a key by setting a NULL value.
964 *
965 * @return IPRT status code.
966 * @param pszKey Key name to process.
967 * @param pszValue Value to set. Set NULL for deleting the key.
968 */
969int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
970{
971 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
972
973 int rc = VINF_SUCCESS;
974 try
975 {
976 Utf8Str Utf8Key(pszKey);
977
978 /* Take a shortcut and prevent crashes on some funny versions
979 * of STL if map is empty initially. */
980 if (!mPairs.empty())
981 {
982 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
983 if (it != mPairs.end())
984 mPairs.erase(it);
985 }
986
987 if (pszValue)
988 {
989 GuestProcessStreamValue val(pszValue);
990 mPairs[Utf8Key] = val;
991 }
992 }
993 catch (const std::exception &ex)
994 {
995 NOREF(ex);
996 }
997 return rc;
998}
999
1000///////////////////////////////////////////////////////////////////////////////
1001
1002GuestProcessStream::GuestProcessStream(void)
1003 : m_cbAllocated(0),
1004 m_cbSize(0),
1005 m_cbOffset(0),
1006 m_pbBuffer(NULL)
1007{
1008
1009}
1010
1011GuestProcessStream::~GuestProcessStream(void)
1012{
1013 Destroy();
1014}
1015
1016/**
1017 * Adds data to the internal parser buffer. Useful if there
1018 * are multiple rounds of adding data needed.
1019 *
1020 * @return IPRT status code.
1021 * @param pbData Pointer to data to add.
1022 * @param cbData Size (in bytes) of data to add.
1023 */
1024int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
1025{
1026 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
1027 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1028
1029 int rc = VINF_SUCCESS;
1030
1031 /* Rewind the buffer if it's empty. */
1032 size_t cbInBuf = m_cbSize - m_cbOffset;
1033 bool const fAddToSet = cbInBuf == 0;
1034 if (fAddToSet)
1035 m_cbSize = m_cbOffset = 0;
1036
1037 /* Try and see if we can simply append the data. */
1038 if (cbData + m_cbSize <= m_cbAllocated)
1039 {
1040 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
1041 m_cbSize += cbData;
1042 }
1043 else
1044 {
1045 /* Move any buffered data to the front. */
1046 cbInBuf = m_cbSize - m_cbOffset;
1047 if (cbInBuf == 0)
1048 m_cbSize = m_cbOffset = 0;
1049 else if (m_cbOffset) /* Do we have something to move? */
1050 {
1051 memmove(m_pbBuffer, &m_pbBuffer[m_cbOffset], cbInBuf);
1052 m_cbSize = cbInBuf;
1053 m_cbOffset = 0;
1054 }
1055
1056 /* Do we need to grow the buffer? */
1057 if (cbData + m_cbSize > m_cbAllocated)
1058 {
1059 size_t cbAlloc = m_cbSize + cbData;
1060 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
1061 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
1062 if (pvNew)
1063 {
1064 m_pbBuffer = (uint8_t *)pvNew;
1065 m_cbAllocated = cbAlloc;
1066 }
1067 else
1068 rc = VERR_NO_MEMORY;
1069 }
1070
1071 /* Finally, copy the data. */
1072 if (RT_SUCCESS(rc))
1073 {
1074 if (cbData + m_cbSize <= m_cbAllocated)
1075 {
1076 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
1077 m_cbSize += cbData;
1078 }
1079 else
1080 rc = VERR_BUFFER_OVERFLOW;
1081 }
1082 }
1083
1084 return rc;
1085}
1086
1087/**
1088 * Destroys the internal data buffer.
1089 */
1090void GuestProcessStream::Destroy(void)
1091{
1092 if (m_pbBuffer)
1093 {
1094 RTMemFree(m_pbBuffer);
1095 m_pbBuffer = NULL;
1096 }
1097
1098 m_cbAllocated = 0;
1099 m_cbSize = 0;
1100 m_cbOffset = 0;
1101}
1102
1103#ifdef DEBUG
1104void GuestProcessStream::Dump(const char *pszFile)
1105{
1106 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
1107 m_pbBuffer, m_cbAllocated, m_cbSize, m_cbOffset, pszFile));
1108
1109 RTFILE hFile;
1110 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
1111 if (RT_SUCCESS(rc))
1112 {
1113 rc = RTFileWrite(hFile, m_pbBuffer, m_cbSize, NULL /* pcbWritten */);
1114 RTFileClose(hFile);
1115 }
1116}
1117#endif
1118
1119/**
1120 * Returns the current offset of the parser within
1121 * the internal data buffer.
1122 *
1123 * @return uint32_t Parser offset.
1124 */
1125uint32_t GuestProcessStream::GetOffset()
1126{
1127 return m_cbOffset;
1128}
1129
1130uint32_t GuestProcessStream::GetSize()
1131{
1132 return m_cbSize;
1133}
1134
1135/**
1136 * Tries to parse the next upcoming pair block within the internal
1137 * buffer.
1138 *
1139 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
1140 * completely parsed already.
1141 *
1142 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
1143 * stored in stream block) but still contains incomplete (unterminated)
1144 * data.
1145 *
1146 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
1147 * block (with zero or more pairs stored in stream block).
1148 *
1149 * @return IPRT status code.
1150 * @param streamBlock Reference to guest stream block to fill.
1151 *
1152 */
1153int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
1154{
1155 if ( !m_pbBuffer
1156 || !m_cbSize)
1157 {
1158 return VERR_NO_DATA;
1159 }
1160
1161 AssertReturn(m_cbOffset <= m_cbSize, VERR_INVALID_PARAMETER);
1162 if (m_cbOffset == m_cbSize)
1163 return VERR_NO_DATA;
1164
1165 int rc = VINF_SUCCESS;
1166
1167 char *pszOff = (char*)&m_pbBuffer[m_cbOffset];
1168 char *pszStart = pszOff;
1169 uint32_t uDistance;
1170 while (*pszStart)
1171 {
1172 size_t pairLen = strlen(pszStart);
1173 uDistance = (pszStart - pszOff);
1174 if (m_cbOffset + uDistance + pairLen + 1 >= m_cbSize)
1175 {
1176 rc = VERR_MORE_DATA;
1177 break;
1178 }
1179 else
1180 {
1181 char *pszSep = strchr(pszStart, '=');
1182 char *pszVal = NULL;
1183 if (pszSep)
1184 pszVal = pszSep + 1;
1185 if (!pszSep || !pszVal)
1186 {
1187 rc = VERR_MORE_DATA;
1188 break;
1189 }
1190
1191 /* Terminate the separator so that we can
1192 * use pszStart as our key from now on. */
1193 *pszSep = '\0';
1194
1195 rc = streamBlock.SetValue(pszStart, pszVal);
1196 if (RT_FAILURE(rc))
1197 return rc;
1198 }
1199
1200 /* Next pair. */
1201 pszStart += pairLen + 1;
1202 }
1203
1204 /* If we did not do any movement but we have stuff left
1205 * in our buffer just skip the current termination so that
1206 * we can try next time. */
1207 uDistance = (pszStart - pszOff);
1208 if ( !uDistance
1209 && *pszStart == '\0'
1210 && m_cbOffset < m_cbSize)
1211 {
1212 uDistance++;
1213 }
1214 m_cbOffset += uDistance;
1215
1216 return rc;
1217}
1218
1219int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1220{
1221 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1222 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1223
1224 mObject.mConsole = pConsole;
1225 mObject.mSession = pSession;
1226
1227 mObject.mNextContextID = 0;
1228 mObject.mObjectID = uObjectID;
1229
1230 return VINF_SUCCESS;
1231}
1232
1233int GuestObject::callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID)
1234{
1235 const ComObjPtr<GuestSession> pSession(mObject.mSession);
1236 Assert(!pSession.isNull());
1237 ULONG uSessionID = 0;
1238 pSession->COMGETTER(Id)(&uSessionID);
1239
1240 /* Create a new context ID and assign it. */
1241 int vrc = VERR_NOT_FOUND;
1242
1243 ULONG uCount = mObject.mNextContextID++;
1244 ULONG uNewContextID = 0;
1245 ULONG uTries = 0;
1246 for (;;)
1247 {
1248 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
1249 uCount = 0;
1250
1251 /* Create a new context ID ... */
1252 uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, mObject.mObjectID, uCount);
1253
1254 /* Is the context ID already used? Try next ID ... */
1255 if (!callbackExists(uCount))
1256 {
1257 /* Callback with context ID was not found. This means
1258 * we can use this context ID for our new callback we want
1259 * to add below. */
1260 vrc = VINF_SUCCESS;
1261 break;
1262 }
1263
1264 uCount++;
1265 if (++uTries == UINT32_MAX)
1266 break; /* Don't try too hard. */
1267 }
1268
1269 if (RT_SUCCESS(vrc))
1270 {
1271 /* Add callback with new context ID to our callback map.
1272 * Note: This is *not* uNewContextID (which also includes
1273 * the session + process ID), just the context count
1274 * will be used here. */
1275 mObject.mCallbacks[uCount] = pCallback;
1276 Assert(mObject.mCallbacks.size());
1277
1278 /* Report back new context ID. */
1279 if (puContextID)
1280 *puContextID = uNewContextID;
1281
1282 LogFlowThisFunc(("Added new callback (Session: %RU32, Object: %RU32, Count: %RU32) CID=%RU32\n",
1283 uSessionID, mObject.mObjectID, uCount, uNewContextID));
1284 }
1285
1286 return vrc;
1287}
1288
1289bool GuestObject::callbackExists(uint32_t uContextID)
1290{
1291 GuestCtrlCallbacks::const_iterator it =
1292 mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
1293 return (it == mObject.mCallbacks.end()) ? false : true;
1294}
1295
1296int GuestObject::callbackRemove(uint32_t uContextID)
1297{
1298 LogFlowThisFunc(("Removing callback (Session=%RU32, Object=%RU32, Count=%RU32) CID=%RU32\n",
1299 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
1300 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
1301 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID),
1302 uContextID));
1303
1304 GuestCtrlCallbacks::iterator it =
1305 mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
1306 if (it != mObject.mCallbacks.end())
1307 {
1308 delete it->second;
1309 mObject.mCallbacks.erase(it);
1310
1311 return VINF_SUCCESS;
1312 }
1313
1314 return VERR_NOT_FOUND;
1315}
1316
1317int GuestObject::callbackRemoveAll(void)
1318{
1319 int vrc = VINF_SUCCESS;
1320
1321 /*
1322 * Cancel all callbacks + waiters.
1323 * Note: Deleting them is the job of the caller!
1324 */
1325 for (GuestCtrlCallbacks::iterator itCallbacks = mObject.mCallbacks.begin();
1326 itCallbacks != mObject.mCallbacks.end(); ++itCallbacks)
1327 {
1328 GuestCtrlCallback *pCallback = itCallbacks->second;
1329 AssertPtr(pCallback);
1330 int rc2 = pCallback->Cancel();
1331 if (RT_SUCCESS(vrc))
1332 vrc = rc2;
1333 }
1334 mObject.mCallbacks.clear();
1335
1336 return vrc;
1337}
1338
1339int GuestObject::sendCommand(uint32_t uFunction,
1340 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
1341{
1342 LogFlowThisFuncEnter();
1343
1344#ifndef VBOX_GUESTCTRL_TEST_CASE
1345 ComObjPtr<Console> pConsole = mObject.mConsole;
1346 Assert(!pConsole.isNull());
1347
1348 /* Forward the information to the VMM device. */
1349 VMMDev *pVMMDev = pConsole->getVMMDev();
1350 AssertPtr(pVMMDev);
1351
1352 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
1353 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
1354 if (RT_FAILURE(vrc))
1355 {
1356 /** @todo What to do here? */
1357 }
1358#else
1359 /* Not needed within testcases. */
1360 int vrc = VINF_SUCCESS;
1361#endif
1362 LogFlowFuncLeaveRC(vrc);
1363 return vrc;
1364}
1365
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