VirtualBox

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

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

GuestCtrl: Infrastructure changes for handling and executing dedicated guest sessions and protocol versioning (untested, work in progress).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.6 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 44863 2013-02-28 12:18:17Z 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_NOTFIY_READ pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_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_NOTFIY_OPEN pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_OPEN)pvData;
317 PCALLBACKPAYLOAD_FILE_NOTFIY_OPEN pCB = (PCALLBACKPAYLOAD_FILE_NOTFIY_OPEN)pvCallback;
318 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTFIY_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_NOTFIY_CLOSE pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_CLOSE)pvData;
328 PCALLBACKPAYLOAD_FILE_NOTFIY_CLOSE pCB = (PCALLBACKPAYLOAD_FILE_NOTFIY_CLOSE)pvCallback;
329 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTFIY_CLOSE));
330
331 pThis->rc = pCB->rc;
332 break;
333 }
334
335 case CALLBACKTYPE_FILE_READ:
336 {
337 PCALLBACKPAYLOAD_FILE_NOTFIY_READ pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_READ)pvData;
338 PCALLBACKPAYLOAD_FILE_NOTFIY_READ pCB = (PCALLBACKPAYLOAD_FILE_NOTFIY_READ)pvCallback;
339 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTFIY_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_NOTFIY_WRITE pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_WRITE)pvData;
355 PCALLBACKPAYLOAD_FILE_NOTFIY_WRITE pCB = (PCALLBACKPAYLOAD_FILE_NOTFIY_WRITE)pvCallback;
356 Assert(cbCallback == sizeof(CALLBACKPAYLOAD_FILE_NOTFIY_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_NOTFIY_SEEK pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_SEEK)pvData;
366 PCALLBACKPAYLOAD_FILE_NOTFIY_SEEK pCB = (PCALLBACKPAYLOAD_FILE_NOTFIY_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_NOTFIY_TELL pThis = (PCALLBACKPAYLOAD_FILE_NOTFIY_TELL)pvData;
377 PCALLBACKPAYLOAD_FILE_NOTFIY_TELL pCB = (PCALLBACKPAYLOAD_FILE_NOTFIY_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
447int GuestEnvironment::BuildEnvironmentBlock(void **ppvEnv, size_t *pcbEnv, uint32_t *pcEnvVars)
448{
449 AssertPtrReturn(ppvEnv, VERR_INVALID_POINTER);
450 /* Rest is optional. */
451
452 size_t cbEnv = 0;
453 uint32_t cEnvVars = 0;
454
455 int rc = VINF_SUCCESS;
456
457 size_t cEnv = mEnvironment.size();
458 if (cEnv)
459 {
460 std::map<Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.begin();
461 for (; itEnv != mEnvironment.end() && RT_SUCCESS(rc); itEnv++)
462 {
463 char *pszEnv;
464 if (!RTStrAPrintf(&pszEnv, "%s=%s", itEnv->first.c_str(), itEnv->second.c_str()))
465 {
466 rc = VERR_NO_MEMORY;
467 break;
468 }
469 AssertPtr(pszEnv);
470 rc = appendToEnvBlock(pszEnv, ppvEnv, &cbEnv, &cEnvVars);
471 RTStrFree(pszEnv);
472 }
473 Assert(cEnv == cEnvVars);
474 }
475
476 if (pcbEnv)
477 *pcbEnv = cbEnv;
478 if (pcEnvVars)
479 *pcEnvVars = cEnvVars;
480
481 return rc;
482}
483
484void GuestEnvironment::Clear(void)
485{
486 mEnvironment.clear();
487}
488
489int GuestEnvironment::CopyFrom(const GuestEnvironmentArray &environment)
490{
491 int rc = VINF_SUCCESS;
492
493 for (GuestEnvironmentArray::const_iterator it = environment.begin();
494 it != environment.end() && RT_SUCCESS(rc);
495 ++it)
496 {
497 rc = Set((*it));
498 }
499
500 return rc;
501}
502
503int GuestEnvironment::CopyTo(GuestEnvironmentArray &environment)
504{
505 size_t s = 0;
506 for (std::map<Utf8Str, Utf8Str>::const_iterator it = mEnvironment.begin();
507 it != mEnvironment.end();
508 ++it, ++s)
509 {
510 environment[s] = Bstr(it->first + "=" + it->second).raw();
511 }
512
513 return VINF_SUCCESS;
514}
515
516/* static */
517void GuestEnvironment::FreeEnvironmentBlock(void *pvEnv)
518{
519 if (pvEnv)
520 RTMemFree(pvEnv);
521}
522
523Utf8Str GuestEnvironment::Get(size_t nPos)
524{
525 size_t curPos = 0;
526 std::map<Utf8Str, Utf8Str>::const_iterator it = mEnvironment.begin();
527 for (; it != mEnvironment.end() && curPos < nPos;
528 ++it, ++curPos) { }
529
530 if (it != mEnvironment.end())
531 return Utf8Str(it->first + "=" + it->second);
532
533 return Utf8Str("");
534}
535
536Utf8Str GuestEnvironment::Get(const Utf8Str &strKey)
537{
538 std::map <Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.find(strKey);
539 Utf8Str strRet;
540 if (itEnv != mEnvironment.end())
541 strRet = itEnv->second;
542 return strRet;
543}
544
545bool GuestEnvironment::Has(const Utf8Str &strKey)
546{
547 std::map <Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.find(strKey);
548 return (itEnv != mEnvironment.end());
549}
550
551int GuestEnvironment::Set(const Utf8Str &strKey, const Utf8Str &strValue)
552{
553 /** @todo Do some validation using regex. */
554 if (strKey.isEmpty())
555 return VERR_INVALID_PARAMETER;
556
557 int rc = VINF_SUCCESS;
558 const char *pszString = strKey.c_str();
559 while (*pszString != '\0' && RT_SUCCESS(rc))
560 {
561 if ( !RT_C_IS_ALNUM(*pszString)
562 && !RT_C_IS_GRAPH(*pszString))
563 rc = VERR_INVALID_PARAMETER;
564 *pszString++;
565 }
566
567 if (RT_SUCCESS(rc))
568 mEnvironment[strKey] = strValue;
569
570 return rc;
571}
572
573int GuestEnvironment::Set(const Utf8Str &strPair)
574{
575 RTCList<RTCString> listPair = strPair.split("=", RTCString::KeepEmptyParts);
576 /* Skip completely empty pairs. Note that we still need pairs with a valid
577 * (set) key and an empty value. */
578 if (listPair.size() <= 1)
579 return VINF_SUCCESS;
580
581 int rc = VINF_SUCCESS;
582 size_t p = 0;
583 while(p < listPair.size() && RT_SUCCESS(rc))
584 {
585 Utf8Str strKey = listPair.at(p++);
586 if ( strKey.isEmpty()
587 || strKey.equals("=")) /* Skip pairs with empty keys (e.g. "=FOO"). */
588 {
589 break;
590 }
591 Utf8Str strValue;
592 if (p < listPair.size()) /* Does the list also contain a value? */
593 strValue = listPair.at(p++);
594
595#ifdef DEBUG
596 LogFlowFunc(("strKey=%s, strValue=%s\n",
597 strKey.c_str(), strValue.c_str()));
598#endif
599 rc = Set(strKey, strValue);
600 }
601
602 return rc;
603}
604
605size_t GuestEnvironment::Size(void)
606{
607 return mEnvironment.size();
608}
609
610int GuestEnvironment::Unset(const Utf8Str &strKey)
611{
612 std::map <Utf8Str, Utf8Str>::iterator itEnv = mEnvironment.find(strKey);
613 if (itEnv != mEnvironment.end())
614 {
615 mEnvironment.erase(itEnv);
616 return VINF_SUCCESS;
617 }
618
619 return VERR_NOT_FOUND;
620}
621
622GuestEnvironment& GuestEnvironment::operator=(const GuestEnvironmentArray &that)
623{
624 CopyFrom(that);
625 return *this;
626}
627
628GuestEnvironment& GuestEnvironment::operator=(const GuestEnvironment &that)
629{
630 for (std::map<Utf8Str, Utf8Str>::const_iterator it = that.mEnvironment.begin();
631 it != that.mEnvironment.end();
632 ++it)
633 {
634 mEnvironment[it->first] = it->second;
635 }
636
637 return *this;
638}
639
640/**
641 * Appends environment variables to the environment block.
642 *
643 * Each var=value pair is separated by the null character ('\\0'). The whole
644 * block will be stored in one blob and disassembled on the guest side later to
645 * fit into the HGCM param structure.
646 *
647 * @returns VBox status code.
648 *
649 * @param pszEnvVar The environment variable=value to append to the
650 * environment block.
651 * @param ppvList This is actually a pointer to a char pointer
652 * variable which keeps track of the environment block
653 * that we're constructing.
654 * @param pcbList Pointer to the variable holding the current size of
655 * the environment block. (List is a misnomer, go
656 * ahead a be confused.)
657 * @param pcEnvVars Pointer to the variable holding count of variables
658 * stored in the environment block.
659 */
660int GuestEnvironment::appendToEnvBlock(const char *pszEnv, void **ppvList, size_t *pcbList, uint32_t *pcEnvVars)
661{
662 int rc = VINF_SUCCESS;
663 size_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
664 if (*ppvList)
665 {
666 size_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
667 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
668 if (pvTmp == NULL)
669 rc = VERR_NO_MEMORY;
670 else
671 {
672 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
673 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
674 *ppvList = (void **)pvTmp;
675 }
676 }
677 else
678 {
679 char *pszTmp;
680 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
681 {
682 *ppvList = (void **)pszTmp;
683 /* Reset counters. */
684 *pcEnvVars = 0;
685 *pcbList = 0;
686 }
687 }
688 if (RT_SUCCESS(rc))
689 {
690 *pcbList += cchEnv + 1; /* Include zero termination. */
691 *pcEnvVars += 1; /* Increase env variable count. */
692 }
693 return rc;
694}
695
696int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk)
697{
698 LogFlowFunc(("\n"));
699
700 int rc = VINF_SUCCESS;
701
702 try
703 {
704#ifdef DEBUG
705 strmBlk.DumpToLog();
706#endif
707 /* Object name. */
708 mName = strmBlk.GetString("name");
709 if (mName.isEmpty()) throw VERR_NOT_FOUND;
710 /* Type. */
711 Utf8Str strType(strmBlk.GetString("ftype"));
712 if (strType.equalsIgnoreCase("-"))
713 mType = FsObjType_File;
714 else if (strType.equalsIgnoreCase("d"))
715 mType = FsObjType_Directory;
716 /** @todo Add more types! */
717 else
718 mType = FsObjType_Undefined;
719 /* Object size. */
720 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
721 if (RT_FAILURE(rc)) throw rc;
722 /** @todo Add complete ls info! */
723 }
724 catch (int rc2)
725 {
726 rc = rc2;
727 }
728
729 LogFlowFuncLeaveRC(rc);
730 return rc;
731}
732
733int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
734{
735 LogFlowFunc(("\n"));
736
737 int rc = VINF_SUCCESS;
738
739 try
740 {
741#ifdef DEBUG
742 strmBlk.DumpToLog();
743#endif
744 /* Node ID, optional because we don't include this
745 * in older VBoxService (< 4.2) versions. */
746 mNodeID = strmBlk.GetInt64("node_id");
747 /* Object name. */
748 mName = strmBlk.GetString("name");
749 if (mName.isEmpty()) throw VERR_NOT_FOUND;
750 /* Type. */
751 Utf8Str strType(strmBlk.GetString("ftype"));
752 if (strType.equalsIgnoreCase("-"))
753 mType = FsObjType_File;
754 else if (strType.equalsIgnoreCase("d"))
755 mType = FsObjType_Directory;
756 /** @todo Add more types! */
757 else
758 mType = FsObjType_Undefined;
759 /* Object size. */
760 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
761 if (RT_FAILURE(rc)) throw rc;
762 /** @todo Add complete stat info! */
763 }
764 catch (int rc2)
765 {
766 rc = rc2;
767 }
768
769 LogFlowFuncLeaveRC(rc);
770 return rc;
771}
772
773///////////////////////////////////////////////////////////////////////////////
774
775/** @todo *NOT* thread safe yet! */
776/** @todo Add exception handling for STL stuff! */
777
778GuestProcessStreamBlock::GuestProcessStreamBlock(void)
779{
780
781}
782
783/*
784GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
785{
786 for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
787 it != otherBlock.end(); it++)
788 {
789 mPairs[it->first] = new
790 if (it->second.pszValue)
791 {
792 RTMemFree(it->second.pszValue);
793 it->second.pszValue = NULL;
794 }
795 }
796}*/
797
798GuestProcessStreamBlock::~GuestProcessStreamBlock()
799{
800 Clear();
801}
802
803/**
804 * Destroys the currently stored stream pairs.
805 *
806 * @return IPRT status code.
807 */
808void GuestProcessStreamBlock::Clear(void)
809{
810 mPairs.clear();
811}
812
813#ifdef DEBUG
814void GuestProcessStreamBlock::DumpToLog(void) const
815{
816 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
817 this, mPairs.size()));
818
819 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
820 it != mPairs.end(); it++)
821 {
822 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
823 }
824}
825#endif
826
827/**
828 * Returns a 64-bit signed integer of a specified key.
829 *
830 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
831 * @param pszKey Name of key to get the value for.
832 * @param piVal Pointer to value to return.
833 */
834int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
835{
836 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
837 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
838 const char *pszValue = GetString(pszKey);
839 if (pszValue)
840 {
841 *piVal = RTStrToInt64(pszValue);
842 return VINF_SUCCESS;
843 }
844 return VERR_NOT_FOUND;
845}
846
847/**
848 * Returns a 64-bit integer of a specified key.
849 *
850 * @return int64_t Value to return, 0 if not found / on failure.
851 * @param pszKey Name of key to get the value for.
852 */
853int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
854{
855 int64_t iVal;
856 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
857 return iVal;
858 return 0;
859}
860
861/**
862 * Returns the current number of stream pairs.
863 *
864 * @return uint32_t Current number of stream pairs.
865 */
866size_t GuestProcessStreamBlock::GetCount(void) const
867{
868 return mPairs.size();
869}
870
871/**
872 * Returns a string value of a specified key.
873 *
874 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
875 * @param pszKey Name of key to get the value for.
876 */
877const char* GuestProcessStreamBlock::GetString(const char *pszKey) const
878{
879 AssertPtrReturn(pszKey, NULL);
880
881 try
882 {
883 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey));
884 if (itPairs != mPairs.end())
885 return itPairs->second.mValue.c_str();
886 }
887 catch (const std::exception &ex)
888 {
889 NOREF(ex);
890 }
891 return NULL;
892}
893
894/**
895 * Returns a 32-bit unsigned integer of a specified key.
896 *
897 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
898 * @param pszKey Name of key to get the value for.
899 * @param puVal Pointer to value to return.
900 */
901int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
902{
903 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
904 AssertPtrReturn(puVal, VERR_INVALID_POINTER);
905 const char *pszValue = GetString(pszKey);
906 if (pszValue)
907 {
908 *puVal = RTStrToUInt32(pszValue);
909 return VINF_SUCCESS;
910 }
911 return VERR_NOT_FOUND;
912}
913
914/**
915 * Returns a 32-bit unsigned integer of a specified key.
916 *
917 * @return uint32_t Value to return, 0 if not found / on failure.
918 * @param pszKey Name of key to get the value for.
919 */
920uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey) const
921{
922 uint32_t uVal;
923 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
924 return uVal;
925 return 0;
926}
927
928/**
929 * Sets a value to a key or deletes a key by setting a NULL value.
930 *
931 * @return IPRT status code.
932 * @param pszKey Key name to process.
933 * @param pszValue Value to set. Set NULL for deleting the key.
934 */
935int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
936{
937 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
938
939 int rc = VINF_SUCCESS;
940 try
941 {
942 Utf8Str Utf8Key(pszKey);
943
944 /* Take a shortcut and prevent crashes on some funny versions
945 * of STL if map is empty initially. */
946 if (!mPairs.empty())
947 {
948 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
949 if (it != mPairs.end())
950 mPairs.erase(it);
951 }
952
953 if (pszValue)
954 {
955 GuestProcessStreamValue val(pszValue);
956 mPairs[Utf8Key] = val;
957 }
958 }
959 catch (const std::exception &ex)
960 {
961 NOREF(ex);
962 }
963 return rc;
964}
965
966///////////////////////////////////////////////////////////////////////////////
967
968GuestProcessStream::GuestProcessStream(void)
969 : m_cbAllocated(0),
970 m_cbSize(0),
971 m_cbOffset(0),
972 m_pbBuffer(NULL)
973{
974
975}
976
977GuestProcessStream::~GuestProcessStream(void)
978{
979 Destroy();
980}
981
982/**
983 * Adds data to the internal parser buffer. Useful if there
984 * are multiple rounds of adding data needed.
985 *
986 * @return IPRT status code.
987 * @param pbData Pointer to data to add.
988 * @param cbData Size (in bytes) of data to add.
989 */
990int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
991{
992 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
993 AssertReturn(cbData, VERR_INVALID_PARAMETER);
994
995 int rc = VINF_SUCCESS;
996
997 /* Rewind the buffer if it's empty. */
998 size_t cbInBuf = m_cbSize - m_cbOffset;
999 bool const fAddToSet = cbInBuf == 0;
1000 if (fAddToSet)
1001 m_cbSize = m_cbOffset = 0;
1002
1003 /* Try and see if we can simply append the data. */
1004 if (cbData + m_cbSize <= m_cbAllocated)
1005 {
1006 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
1007 m_cbSize += cbData;
1008 }
1009 else
1010 {
1011 /* Move any buffered data to the front. */
1012 cbInBuf = m_cbSize - m_cbOffset;
1013 if (cbInBuf == 0)
1014 m_cbSize = m_cbOffset = 0;
1015 else if (m_cbOffset) /* Do we have something to move? */
1016 {
1017 memmove(m_pbBuffer, &m_pbBuffer[m_cbOffset], cbInBuf);
1018 m_cbSize = cbInBuf;
1019 m_cbOffset = 0;
1020 }
1021
1022 /* Do we need to grow the buffer? */
1023 if (cbData + m_cbSize > m_cbAllocated)
1024 {
1025 size_t cbAlloc = m_cbSize + cbData;
1026 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
1027 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
1028 if (pvNew)
1029 {
1030 m_pbBuffer = (uint8_t *)pvNew;
1031 m_cbAllocated = cbAlloc;
1032 }
1033 else
1034 rc = VERR_NO_MEMORY;
1035 }
1036
1037 /* Finally, copy the data. */
1038 if (RT_SUCCESS(rc))
1039 {
1040 if (cbData + m_cbSize <= m_cbAllocated)
1041 {
1042 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
1043 m_cbSize += cbData;
1044 }
1045 else
1046 rc = VERR_BUFFER_OVERFLOW;
1047 }
1048 }
1049
1050 return rc;
1051}
1052
1053/**
1054 * Destroys the internal data buffer.
1055 */
1056void GuestProcessStream::Destroy(void)
1057{
1058 if (m_pbBuffer)
1059 {
1060 RTMemFree(m_pbBuffer);
1061 m_pbBuffer = NULL;
1062 }
1063
1064 m_cbAllocated = 0;
1065 m_cbSize = 0;
1066 m_cbOffset = 0;
1067}
1068
1069#ifdef DEBUG
1070void GuestProcessStream::Dump(const char *pszFile)
1071{
1072 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
1073 m_pbBuffer, m_cbAllocated, m_cbSize, m_cbOffset, pszFile));
1074
1075 RTFILE hFile;
1076 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
1077 if (RT_SUCCESS(rc))
1078 {
1079 rc = RTFileWrite(hFile, m_pbBuffer, m_cbSize, NULL /* pcbWritten */);
1080 RTFileClose(hFile);
1081 }
1082}
1083#endif
1084
1085/**
1086 * Returns the current offset of the parser within
1087 * the internal data buffer.
1088 *
1089 * @return uint32_t Parser offset.
1090 */
1091uint32_t GuestProcessStream::GetOffset()
1092{
1093 return m_cbOffset;
1094}
1095
1096uint32_t GuestProcessStream::GetSize()
1097{
1098 return m_cbSize;
1099}
1100
1101/**
1102 * Tries to parse the next upcoming pair block within the internal
1103 * buffer.
1104 *
1105 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
1106 * completely parsed already.
1107 *
1108 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
1109 * stored in stream block) but still contains incomplete (unterminated)
1110 * data.
1111 *
1112 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
1113 * block (with zero or more pairs stored in stream block).
1114 *
1115 * @return IPRT status code.
1116 * @param streamBlock Reference to guest stream block to fill.
1117 *
1118 */
1119int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
1120{
1121 if ( !m_pbBuffer
1122 || !m_cbSize)
1123 {
1124 return VERR_NO_DATA;
1125 }
1126
1127 AssertReturn(m_cbOffset <= m_cbSize, VERR_INVALID_PARAMETER);
1128 if (m_cbOffset == m_cbSize)
1129 return VERR_NO_DATA;
1130
1131 int rc = VINF_SUCCESS;
1132
1133 char *pszOff = (char*)&m_pbBuffer[m_cbOffset];
1134 char *pszStart = pszOff;
1135 uint32_t uDistance;
1136 while (*pszStart)
1137 {
1138 size_t pairLen = strlen(pszStart);
1139 uDistance = (pszStart - pszOff);
1140 if (m_cbOffset + uDistance + pairLen + 1 >= m_cbSize)
1141 {
1142 rc = VERR_MORE_DATA;
1143 break;
1144 }
1145 else
1146 {
1147 char *pszSep = strchr(pszStart, '=');
1148 char *pszVal = NULL;
1149 if (pszSep)
1150 pszVal = pszSep + 1;
1151 if (!pszSep || !pszVal)
1152 {
1153 rc = VERR_MORE_DATA;
1154 break;
1155 }
1156
1157 /* Terminate the separator so that we can
1158 * use pszStart as our key from now on. */
1159 *pszSep = '\0';
1160
1161 rc = streamBlock.SetValue(pszStart, pszVal);
1162 if (RT_FAILURE(rc))
1163 return rc;
1164 }
1165
1166 /* Next pair. */
1167 pszStart += pairLen + 1;
1168 }
1169
1170 /* If we did not do any movement but we have stuff left
1171 * in our buffer just skip the current termination so that
1172 * we can try next time. */
1173 uDistance = (pszStart - pszOff);
1174 if ( !uDistance
1175 && *pszStart == '\0'
1176 && m_cbOffset < m_cbSize)
1177 {
1178 uDistance++;
1179 }
1180 m_cbOffset += uDistance;
1181
1182 return rc;
1183}
1184
1185int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1186{
1187 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1188 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1189
1190 mObject.mConsole = pConsole;
1191 mObject.mSession = pSession;
1192
1193 mObject.mNextContextID = 0;
1194 mObject.mObjectID = uObjectID;
1195
1196 return VINF_SUCCESS;
1197}
1198
1199int GuestObject::callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID)
1200{
1201 const ComObjPtr<GuestSession> pSession(mObject.mSession);
1202 Assert(!pSession.isNull());
1203 ULONG uSessionID = 0;
1204 HRESULT hr = pSession->COMGETTER(Id)(&uSessionID);
1205 ComAssertComRC(hr);
1206
1207 /* Create a new context ID and assign it. */
1208 int vrc = VERR_NOT_FOUND;
1209
1210 ULONG uCount = mObject.mNextContextID++;
1211 ULONG uNewContextID = 0;
1212 ULONG uTries = 0;
1213 for (;;)
1214 {
1215 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
1216 uCount = 0;
1217
1218 /* Create a new context ID ... */
1219 uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, mObject.mObjectID, uCount);
1220
1221 /* Is the context ID already used? Try next ID ... */
1222 if (!callbackExists(uCount))
1223 {
1224 /* Callback with context ID was not found. This means
1225 * we can use this context ID for our new callback we want
1226 * to add below. */
1227 vrc = VINF_SUCCESS;
1228 break;
1229 }
1230
1231 uCount++;
1232 if (++uTries == UINT32_MAX)
1233 break; /* Don't try too hard. */
1234 }
1235
1236 if (RT_SUCCESS(vrc))
1237 {
1238 /* Add callback with new context ID to our callback map.
1239 * Note: This is *not* uNewContextID (which also includes
1240 * the session + process ID), just the context count
1241 * will be used here. */
1242 mObject.mCallbacks[uCount] = pCallback;
1243 Assert(mObject.mCallbacks.size());
1244
1245 /* Report back new context ID. */
1246 if (puContextID)
1247 *puContextID = uNewContextID;
1248
1249 LogFlowThisFunc(("Added new callback (Session: %RU32, Object: %RU32, Count: %RU32) CID=%RU32\n",
1250 uSessionID, mObject.mObjectID, uCount, uNewContextID));
1251 }
1252
1253 return vrc;
1254}
1255
1256bool GuestObject::callbackExists(uint32_t uContextID)
1257{
1258 GuestCtrlCallbacks::const_iterator it =
1259 mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
1260 return (it == mObject.mCallbacks.end()) ? false : true;
1261}
1262
1263int GuestObject::callbackRemove(uint32_t uContextID)
1264{
1265 LogFlowThisFunc(("Removing callback (Session=%RU32, Object=%RU32, Count=%RU32) CID=%RU32\n",
1266 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
1267 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
1268 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID),
1269 uContextID));
1270
1271 GuestCtrlCallbacks::iterator it =
1272 mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
1273 if (it != mObject.mCallbacks.end())
1274 {
1275 delete it->second;
1276 mObject.mCallbacks.erase(it);
1277
1278 return VINF_SUCCESS;
1279 }
1280
1281 return VERR_NOT_FOUND;
1282}
1283
1284int GuestObject::callbackRemoveAll(void)
1285{
1286 int vrc = VINF_SUCCESS;
1287
1288 /*
1289 * Cancel all callbacks + waiters.
1290 * Note: Deleting them is the job of the caller!
1291 */
1292 for (GuestCtrlCallbacks::iterator itCallbacks = mObject.mCallbacks.begin();
1293 itCallbacks != mObject.mCallbacks.end(); ++itCallbacks)
1294 {
1295 GuestCtrlCallback *pCallback = itCallbacks->second;
1296 AssertPtr(pCallback);
1297 int rc2 = pCallback->Cancel();
1298 if (RT_SUCCESS(vrc))
1299 vrc = rc2;
1300 }
1301 mObject.mCallbacks.clear();
1302
1303 return vrc;
1304}
1305
1306int GuestObject::sendCommand(uint32_t uFunction,
1307 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
1308{
1309 LogFlowThisFuncEnter();
1310
1311#ifndef VBOX_GUESTCTRL_TEST_CASE
1312 ComObjPtr<Console> pConsole = mObject.mConsole;
1313 Assert(!pConsole.isNull());
1314
1315 /* Forward the information to the VMM device. */
1316 VMMDev *pVMMDev = pConsole->getVMMDev();
1317 AssertPtr(pVMMDev);
1318
1319 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
1320 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
1321 if (RT_FAILURE(vrc))
1322 {
1323 /** @todo What to do here? */
1324 }
1325#else
1326 /* Not needed within testcases. */
1327 int vrc = VINF_SUCCESS;
1328#endif
1329 LogFlowFuncLeaveRC(vrc);
1330 return vrc;
1331}
1332
Note: See TracBrowser for help on using the repository browser.

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