VirtualBox

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

Last change on this file since 47469 was 47469, checked in by vboxsync, 11 years ago

Guest Control: Adjustments for supporting < 4.3 Guest Additions in conjunction with the new guest session, extended testcase (now passing using latest 4.2 Guest Additions with latest trunk).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.9 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 47469 2013-07-30 09:43:14Z 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/cpp/utils.h> /* For unconst(). */
28#include <iprt/ctype.h>
29#ifdef DEBUG
30# include <iprt/file.h>
31#endif /* DEBUG */
32
33#ifdef LOG_GROUP
34 #undef LOG_GROUP
35#endif
36#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
37#include <VBox/log.h>
38
39/******************************************************************************
40 * Structures and Typedefs *
41 ******************************************************************************/
42
43int GuestEnvironment::BuildEnvironmentBlock(void **ppvEnv, size_t *pcbEnv, uint32_t *pcEnvVars)
44{
45 AssertPtrReturn(ppvEnv, VERR_INVALID_POINTER);
46 /* Rest is optional. */
47
48 size_t cbEnv = 0;
49 uint32_t cEnvVars = 0;
50
51 int rc = VINF_SUCCESS;
52
53 size_t cEnv = mEnvironment.size();
54 if (cEnv)
55 {
56 std::map<Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.begin();
57 for (; itEnv != mEnvironment.end() && RT_SUCCESS(rc); itEnv++)
58 {
59 char *pszEnv;
60 if (!RTStrAPrintf(&pszEnv, "%s=%s", itEnv->first.c_str(), itEnv->second.c_str()))
61 {
62 rc = VERR_NO_MEMORY;
63 break;
64 }
65 AssertPtr(pszEnv);
66 rc = appendToEnvBlock(pszEnv, ppvEnv, &cbEnv, &cEnvVars);
67 RTStrFree(pszEnv);
68 }
69 Assert(cEnv == cEnvVars);
70 }
71
72 if (pcbEnv)
73 *pcbEnv = cbEnv;
74 if (pcEnvVars)
75 *pcEnvVars = cEnvVars;
76
77 return rc;
78}
79
80void GuestEnvironment::Clear(void)
81{
82 mEnvironment.clear();
83}
84
85int GuestEnvironment::CopyFrom(const GuestEnvironmentArray &environment)
86{
87 int rc = VINF_SUCCESS;
88
89 for (GuestEnvironmentArray::const_iterator it = environment.begin();
90 it != environment.end() && RT_SUCCESS(rc);
91 ++it)
92 {
93 rc = Set((*it));
94 }
95
96 return rc;
97}
98
99int GuestEnvironment::CopyTo(GuestEnvironmentArray &environment)
100{
101 size_t s = 0;
102 for (std::map<Utf8Str, Utf8Str>::const_iterator it = mEnvironment.begin();
103 it != mEnvironment.end();
104 ++it, ++s)
105 {
106 environment[s] = Bstr(it->first + "=" + it->second).raw();
107 }
108
109 return VINF_SUCCESS;
110}
111
112/* static */
113void GuestEnvironment::FreeEnvironmentBlock(void *pvEnv)
114{
115 if (pvEnv)
116 RTMemFree(pvEnv);
117}
118
119Utf8Str GuestEnvironment::Get(size_t nPos)
120{
121 size_t curPos = 0;
122 std::map<Utf8Str, Utf8Str>::const_iterator it = mEnvironment.begin();
123 for (; it != mEnvironment.end() && curPos < nPos;
124 ++it, ++curPos) { }
125
126 if (it != mEnvironment.end())
127 return Utf8Str(it->first + "=" + it->second);
128
129 return Utf8Str("");
130}
131
132Utf8Str GuestEnvironment::Get(const Utf8Str &strKey)
133{
134 std::map <Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.find(strKey);
135 Utf8Str strRet;
136 if (itEnv != mEnvironment.end())
137 strRet = itEnv->second;
138 return strRet;
139}
140
141bool GuestEnvironment::Has(const Utf8Str &strKey)
142{
143 std::map <Utf8Str, Utf8Str>::const_iterator itEnv = mEnvironment.find(strKey);
144 return (itEnv != mEnvironment.end());
145}
146
147int GuestEnvironment::Set(const Utf8Str &strKey, const Utf8Str &strValue)
148{
149 /** @todo Do some validation using regex. */
150 if (strKey.isEmpty())
151 return VERR_INVALID_PARAMETER;
152
153 int rc = VINF_SUCCESS;
154 const char *pszString = strKey.c_str();
155 while (*pszString != '\0' && RT_SUCCESS(rc))
156 {
157 if ( !RT_C_IS_ALNUM(*pszString)
158 && !RT_C_IS_GRAPH(*pszString))
159 rc = VERR_INVALID_PARAMETER;
160 *pszString++;
161 }
162
163 if (RT_SUCCESS(rc))
164 mEnvironment[strKey] = strValue;
165
166 return rc;
167}
168
169int GuestEnvironment::Set(const Utf8Str &strPair)
170{
171 RTCList<RTCString> listPair = strPair.split("=", RTCString::KeepEmptyParts);
172 /* Skip completely empty pairs. Note that we still need pairs with a valid
173 * (set) key and an empty value. */
174 if (listPair.size() <= 1)
175 return VINF_SUCCESS;
176
177 int rc = VINF_SUCCESS;
178 size_t p = 0;
179 while (p < listPair.size() && RT_SUCCESS(rc))
180 {
181 Utf8Str strKey = listPair.at(p++);
182 if ( strKey.isEmpty()
183 || strKey.equals("=")) /* Skip pairs with empty keys (e.g. "=FOO"). */
184 {
185 break;
186 }
187 Utf8Str strValue;
188 if (p < listPair.size()) /* Does the list also contain a value? */
189 strValue = listPair.at(p++);
190
191#ifdef DEBUG
192 LogFlowFunc(("strKey=%s, strValue=%s\n",
193 strKey.c_str(), strValue.c_str()));
194#endif
195 rc = Set(strKey, strValue);
196 }
197
198 return rc;
199}
200
201size_t GuestEnvironment::Size(void)
202{
203 return mEnvironment.size();
204}
205
206int GuestEnvironment::Unset(const Utf8Str &strKey)
207{
208 std::map <Utf8Str, Utf8Str>::iterator itEnv = mEnvironment.find(strKey);
209 if (itEnv != mEnvironment.end())
210 {
211 mEnvironment.erase(itEnv);
212 return VINF_SUCCESS;
213 }
214
215 return VERR_NOT_FOUND;
216}
217
218GuestEnvironment& GuestEnvironment::operator=(const GuestEnvironmentArray &that)
219{
220 CopyFrom(that);
221 return *this;
222}
223
224GuestEnvironment& GuestEnvironment::operator=(const GuestEnvironment &that)
225{
226 for (std::map<Utf8Str, Utf8Str>::const_iterator it = that.mEnvironment.begin();
227 it != that.mEnvironment.end();
228 ++it)
229 {
230 mEnvironment[it->first] = it->second;
231 }
232
233 return *this;
234}
235
236/**
237 * Appends environment variables to the environment block.
238 *
239 * Each var=value pair is separated by the null character ('\\0'). The whole
240 * block will be stored in one blob and disassembled on the guest side later to
241 * fit into the HGCM param structure.
242 *
243 * @returns VBox status code.
244 *
245 * @param pszEnvVar The environment variable=value to append to the
246 * environment block.
247 * @param ppvList This is actually a pointer to a char pointer
248 * variable which keeps track of the environment block
249 * that we're constructing.
250 * @param pcbList Pointer to the variable holding the current size of
251 * the environment block. (List is a misnomer, go
252 * ahead a be confused.)
253 * @param pcEnvVars Pointer to the variable holding count of variables
254 * stored in the environment block.
255 */
256int GuestEnvironment::appendToEnvBlock(const char *pszEnv, void **ppvList, size_t *pcbList, uint32_t *pcEnvVars)
257{
258 int rc = VINF_SUCCESS;
259 size_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
260 if (*ppvList)
261 {
262 size_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
263 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
264 if (pvTmp == NULL)
265 rc = VERR_NO_MEMORY;
266 else
267 {
268 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
269 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
270 *ppvList = (void **)pvTmp;
271 }
272 }
273 else
274 {
275 char *pszTmp;
276 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
277 {
278 *ppvList = (void **)pszTmp;
279 /* Reset counters. */
280 *pcEnvVars = 0;
281 *pcbList = 0;
282 }
283 }
284 if (RT_SUCCESS(rc))
285 {
286 *pcbList += cchEnv + 1; /* Include zero termination. */
287 *pcEnvVars += 1; /* Increase env variable count. */
288 }
289 return rc;
290}
291
292int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk)
293{
294 LogFlowFunc(("\n"));
295
296 int rc = VINF_SUCCESS;
297
298 try
299 {
300#ifdef DEBUG
301 strmBlk.DumpToLog();
302#endif
303 /* Object name. */
304 mName = strmBlk.GetString("name");
305 if (mName.isEmpty()) throw VERR_NOT_FOUND;
306 /* Type. */
307 Utf8Str strType(strmBlk.GetString("ftype"));
308 if (strType.equalsIgnoreCase("-"))
309 mType = FsObjType_File;
310 else if (strType.equalsIgnoreCase("d"))
311 mType = FsObjType_Directory;
312 /** @todo Add more types! */
313 else
314 mType = FsObjType_Undefined;
315 /* Object size. */
316 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
317 if (RT_FAILURE(rc)) throw rc;
318 /** @todo Add complete ls info! */
319 }
320 catch (int rc2)
321 {
322 rc = rc2;
323 }
324
325 LogFlowFuncLeaveRC(rc);
326 return rc;
327}
328
329int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
330{
331 LogFlowFunc(("\n"));
332
333 int rc = VINF_SUCCESS;
334
335 try
336 {
337#ifdef DEBUG
338 strmBlk.DumpToLog();
339#endif
340 /* Node ID, optional because we don't include this
341 * in older VBoxService (< 4.2) versions. */
342 mNodeID = strmBlk.GetInt64("node_id");
343 /* Object name. */
344 mName = strmBlk.GetString("name");
345 if (mName.isEmpty()) throw VERR_NOT_FOUND;
346 /* Type. */
347 Utf8Str strType(strmBlk.GetString("ftype"));
348 if (strType.equalsIgnoreCase("-"))
349 mType = FsObjType_File;
350 else if (strType.equalsIgnoreCase("d"))
351 mType = FsObjType_Directory;
352 /** @todo Add more types! */
353 else
354 mType = FsObjType_Undefined;
355 /* Object size. */
356 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
357 if (RT_FAILURE(rc)) throw rc;
358 /** @todo Add complete stat info! */
359 }
360 catch (int rc2)
361 {
362 rc = rc2;
363 }
364
365 LogFlowFuncLeaveRC(rc);
366 return rc;
367}
368
369///////////////////////////////////////////////////////////////////////////////
370
371/** @todo *NOT* thread safe yet! */
372/** @todo Add exception handling for STL stuff! */
373
374GuestProcessStreamBlock::GuestProcessStreamBlock(void)
375{
376
377}
378
379/*
380GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
381{
382 for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
383 it != otherBlock.end(); it++)
384 {
385 mPairs[it->first] = new
386 if (it->second.pszValue)
387 {
388 RTMemFree(it->second.pszValue);
389 it->second.pszValue = NULL;
390 }
391 }
392}*/
393
394GuestProcessStreamBlock::~GuestProcessStreamBlock()
395{
396 Clear();
397}
398
399/**
400 * Destroys the currently stored stream pairs.
401 *
402 * @return IPRT status code.
403 */
404void GuestProcessStreamBlock::Clear(void)
405{
406 mPairs.clear();
407}
408
409#ifdef DEBUG
410void GuestProcessStreamBlock::DumpToLog(void) const
411{
412 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
413 this, mPairs.size()));
414
415 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
416 it != mPairs.end(); it++)
417 {
418 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
419 }
420}
421#endif
422
423/**
424 * Returns a 64-bit signed integer of a specified key.
425 *
426 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
427 * @param pszKey Name of key to get the value for.
428 * @param piVal Pointer to value to return.
429 */
430int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
431{
432 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
433 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
434 const char *pszValue = GetString(pszKey);
435 if (pszValue)
436 {
437 *piVal = RTStrToInt64(pszValue);
438 return VINF_SUCCESS;
439 }
440 return VERR_NOT_FOUND;
441}
442
443/**
444 * Returns a 64-bit integer of a specified key.
445 *
446 * @return int64_t Value to return, 0 if not found / on failure.
447 * @param pszKey Name of key to get the value for.
448 */
449int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
450{
451 int64_t iVal;
452 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
453 return iVal;
454 return 0;
455}
456
457/**
458 * Returns the current number of stream pairs.
459 *
460 * @return uint32_t Current number of stream pairs.
461 */
462size_t GuestProcessStreamBlock::GetCount(void) const
463{
464 return mPairs.size();
465}
466
467/**
468 * Returns a string value of a specified key.
469 *
470 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
471 * @param pszKey Name of key to get the value for.
472 */
473const char* GuestProcessStreamBlock::GetString(const char *pszKey) const
474{
475 AssertPtrReturn(pszKey, NULL);
476
477 try
478 {
479 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey));
480 if (itPairs != mPairs.end())
481 return itPairs->second.mValue.c_str();
482 }
483 catch (const std::exception &ex)
484 {
485 NOREF(ex);
486 }
487 return NULL;
488}
489
490/**
491 * Returns a 32-bit unsigned integer of a specified key.
492 *
493 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
494 * @param pszKey Name of key to get the value for.
495 * @param puVal Pointer to value to return.
496 */
497int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
498{
499 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
500 AssertPtrReturn(puVal, VERR_INVALID_POINTER);
501 const char *pszValue = GetString(pszKey);
502 if (pszValue)
503 {
504 *puVal = RTStrToUInt32(pszValue);
505 return VINF_SUCCESS;
506 }
507 return VERR_NOT_FOUND;
508}
509
510/**
511 * Returns a 32-bit unsigned integer of a specified key.
512 *
513 * @return uint32_t Value to return, 0 if not found / on failure.
514 * @param pszKey Name of key to get the value for.
515 */
516uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey) const
517{
518 uint32_t uVal;
519 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
520 return uVal;
521 return 0;
522}
523
524/**
525 * Sets a value to a key or deletes a key by setting a NULL value.
526 *
527 * @return IPRT status code.
528 * @param pszKey Key name to process.
529 * @param pszValue Value to set. Set NULL for deleting the key.
530 */
531int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
532{
533 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
534
535 int rc = VINF_SUCCESS;
536 try
537 {
538 Utf8Str Utf8Key(pszKey);
539
540 /* Take a shortcut and prevent crashes on some funny versions
541 * of STL if map is empty initially. */
542 if (!mPairs.empty())
543 {
544 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
545 if (it != mPairs.end())
546 mPairs.erase(it);
547 }
548
549 if (pszValue)
550 {
551 GuestProcessStreamValue val(pszValue);
552 mPairs[Utf8Key] = val;
553 }
554 }
555 catch (const std::exception &ex)
556 {
557 NOREF(ex);
558 }
559 return rc;
560}
561
562///////////////////////////////////////////////////////////////////////////////
563
564GuestProcessStream::GuestProcessStream(void)
565 : m_cbAllocated(0),
566 m_cbSize(0),
567 m_cbOffset(0),
568 m_pbBuffer(NULL)
569{
570
571}
572
573GuestProcessStream::~GuestProcessStream(void)
574{
575 Destroy();
576}
577
578/**
579 * Adds data to the internal parser buffer. Useful if there
580 * are multiple rounds of adding data needed.
581 *
582 * @return IPRT status code.
583 * @param pbData Pointer to data to add.
584 * @param cbData Size (in bytes) of data to add.
585 */
586int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
587{
588 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
589 AssertReturn(cbData, VERR_INVALID_PARAMETER);
590
591 int rc = VINF_SUCCESS;
592
593 /* Rewind the buffer if it's empty. */
594 size_t cbInBuf = m_cbSize - m_cbOffset;
595 bool const fAddToSet = cbInBuf == 0;
596 if (fAddToSet)
597 m_cbSize = m_cbOffset = 0;
598
599 /* Try and see if we can simply append the data. */
600 if (cbData + m_cbSize <= m_cbAllocated)
601 {
602 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
603 m_cbSize += cbData;
604 }
605 else
606 {
607 /* Move any buffered data to the front. */
608 cbInBuf = m_cbSize - m_cbOffset;
609 if (cbInBuf == 0)
610 m_cbSize = m_cbOffset = 0;
611 else if (m_cbOffset) /* Do we have something to move? */
612 {
613 memmove(m_pbBuffer, &m_pbBuffer[m_cbOffset], cbInBuf);
614 m_cbSize = cbInBuf;
615 m_cbOffset = 0;
616 }
617
618 /* Do we need to grow the buffer? */
619 if (cbData + m_cbSize > m_cbAllocated)
620 {
621 size_t cbAlloc = m_cbSize + cbData;
622 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
623 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
624 if (pvNew)
625 {
626 m_pbBuffer = (uint8_t *)pvNew;
627 m_cbAllocated = cbAlloc;
628 }
629 else
630 rc = VERR_NO_MEMORY;
631 }
632
633 /* Finally, copy the data. */
634 if (RT_SUCCESS(rc))
635 {
636 if (cbData + m_cbSize <= m_cbAllocated)
637 {
638 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
639 m_cbSize += cbData;
640 }
641 else
642 rc = VERR_BUFFER_OVERFLOW;
643 }
644 }
645
646 return rc;
647}
648
649/**
650 * Destroys the internal data buffer.
651 */
652void GuestProcessStream::Destroy(void)
653{
654 if (m_pbBuffer)
655 {
656 RTMemFree(m_pbBuffer);
657 m_pbBuffer = NULL;
658 }
659
660 m_cbAllocated = 0;
661 m_cbSize = 0;
662 m_cbOffset = 0;
663}
664
665#ifdef DEBUG
666void GuestProcessStream::Dump(const char *pszFile)
667{
668 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
669 m_pbBuffer, m_cbAllocated, m_cbSize, m_cbOffset, pszFile));
670
671 RTFILE hFile;
672 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
673 if (RT_SUCCESS(rc))
674 {
675 rc = RTFileWrite(hFile, m_pbBuffer, m_cbSize, NULL /* pcbWritten */);
676 RTFileClose(hFile);
677 }
678}
679#endif
680
681/**
682 * Returns the current offset of the parser within
683 * the internal data buffer.
684 *
685 * @return uint32_t Parser offset.
686 */
687uint32_t GuestProcessStream::GetOffset()
688{
689 return m_cbOffset;
690}
691
692uint32_t GuestProcessStream::GetSize()
693{
694 return m_cbSize;
695}
696
697/**
698 * Tries to parse the next upcoming pair block within the internal
699 * buffer.
700 *
701 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
702 * completely parsed already.
703 *
704 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
705 * stored in stream block) but still contains incomplete (unterminated)
706 * data.
707 *
708 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
709 * block (with zero or more pairs stored in stream block).
710 *
711 * @return IPRT status code.
712 * @param streamBlock Reference to guest stream block to fill.
713 *
714 */
715int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
716{
717 if ( !m_pbBuffer
718 || !m_cbSize)
719 {
720 return VERR_NO_DATA;
721 }
722
723 AssertReturn(m_cbOffset <= m_cbSize, VERR_INVALID_PARAMETER);
724 if (m_cbOffset == m_cbSize)
725 return VERR_NO_DATA;
726
727 int rc = VINF_SUCCESS;
728
729 char *pszOff = (char*)&m_pbBuffer[m_cbOffset];
730 char *pszStart = pszOff;
731 uint32_t uDistance;
732 while (*pszStart)
733 {
734 size_t pairLen = strlen(pszStart);
735 uDistance = (pszStart - pszOff);
736 if (m_cbOffset + uDistance + pairLen + 1 >= m_cbSize)
737 {
738 rc = VERR_MORE_DATA;
739 break;
740 }
741 else
742 {
743 char *pszSep = strchr(pszStart, '=');
744 char *pszVal = NULL;
745 if (pszSep)
746 pszVal = pszSep + 1;
747 if (!pszSep || !pszVal)
748 {
749 rc = VERR_MORE_DATA;
750 break;
751 }
752
753 /* Terminate the separator so that we can
754 * use pszStart as our key from now on. */
755 *pszSep = '\0';
756
757 rc = streamBlock.SetValue(pszStart, pszVal);
758 if (RT_FAILURE(rc))
759 return rc;
760 }
761
762 /* Next pair. */
763 pszStart += pairLen + 1;
764 }
765
766 /* If we did not do any movement but we have stuff left
767 * in our buffer just skip the current termination so that
768 * we can try next time. */
769 uDistance = (pszStart - pszOff);
770 if ( !uDistance
771 && *pszStart == '\0'
772 && m_cbOffset < m_cbSize)
773 {
774 uDistance++;
775 }
776 m_cbOffset += uDistance;
777
778 return rc;
779}
780
781GuestBase::GuestBase(void)
782 : mConsole(NULL),
783 mNextContextID(0)
784{
785}
786
787GuestBase::~GuestBase(void)
788{
789}
790
791int GuestBase::baseInit(void)
792{
793 int rc = RTCritSectInit(&mWaitEventCritSect);
794
795 LogFlowFuncLeaveRC(rc);
796 return rc;
797}
798
799void GuestBase::baseUninit(void)
800{
801 LogFlowThisFuncEnter();
802
803 int rc = RTCritSectDelete(&mWaitEventCritSect);
804
805 LogFlowFuncLeaveRC(rc);
806 /* No return value. */
807}
808
809int GuestBase::cancelWaitEvents(void)
810{
811 LogFlowThisFuncEnter();
812
813 int rc = RTCritSectEnter(&mWaitEventCritSect);
814 if (RT_SUCCESS(rc))
815 {
816 GuestWaitEventTypes::iterator itEventTypes = mWaitEvents.begin();
817 while (itEventTypes != mWaitEvents.end())
818 {
819 GuestWaitEvents::iterator itEvents = itEventTypes->second.begin();
820 while (itEvents != itEventTypes->second.end())
821 {
822 GuestWaitEvent *pEvent = (*itEvents);
823 AssertPtr(pEvent);
824
825 /*
826 * Just cancel the event and remove it from the wait events
827 * map. Don't delete it though, this (hopefully) is done by
828 * the caller using unregisterWaitEvent().
829 */
830 int rc2 = pEvent->Signal(NULL /* Cancel */);
831 AssertRC(rc2);
832 itEvents = itEventTypes->second.erase(itEvents);
833 }
834
835 itEventTypes = mWaitEvents.erase(itEventTypes);
836 }
837
838 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
839 if (RT_SUCCESS(rc))
840 rc = rc2;
841 }
842
843 LogFlowFuncLeaveRC(rc);
844 return rc;
845}
846
847int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
848{
849 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
850
851 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
852 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
853 uCount = 0;
854
855 uint32_t uNewContextID =
856 VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
857
858 *puContextID = uNewContextID;
859
860 return VINF_SUCCESS;
861}
862
863int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
864 const std::list<VBoxEventType_T> &lstEvents,
865 GuestWaitEvent **ppEvent)
866{
867 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
868
869 uint32_t uContextID;
870 int rc = generateContextID(uSessionID, uObjectID, &uContextID);
871 if (RT_FAILURE(rc))
872 return rc;
873
874 rc = RTCritSectEnter(&mWaitEventCritSect);
875 if (RT_SUCCESS(rc))
876 {
877 try
878 {
879 GuestWaitEvent *pEvent = new GuestWaitEvent(uContextID, lstEvents);
880 AssertPtr(pEvent);
881
882 for (std::list<VBoxEventType_T>::const_iterator itEvents = lstEvents.begin();
883 itEvents != lstEvents.end(); itEvents++)
884 {
885 mWaitEvents[(*itEvents)].push_back(pEvent);
886 }
887
888 *ppEvent = pEvent;
889 }
890 catch(std::bad_alloc &)
891 {
892 rc = VERR_NO_MEMORY;
893 }
894
895 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
896 if (RT_SUCCESS(rc))
897 rc = rc2;
898 }
899
900 return rc;
901}
902
903int GuestBase::signalWaitEvents(VBoxEventType_T aType, IEvent *aEvent)
904{
905 int rc = RTCritSectEnter(&mWaitEventCritSect);
906 if (RT_SUCCESS(rc))
907 {
908 GuestWaitEventTypes::iterator itTypes = mWaitEvents.find(aType);
909 if (itTypes != mWaitEvents.end())
910 {
911 for (GuestWaitEvents::iterator itEvents = itTypes->second.begin();
912 itEvents != itTypes->second.end(); itEvents++)
913 {
914 ComPtr<IEvent> pThisEvent = aEvent;
915 Assert(!pThisEvent.isNull());
916 int rc2 = (*itEvents)->Signal(aEvent);
917 if (RT_SUCCESS(rc))
918 rc = rc2;
919 }
920 }
921
922 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
923 if (RT_SUCCESS(rc))
924 rc = rc2;
925 }
926
927 return rc;
928}
929
930void GuestBase::unregisterWaitEvent(GuestWaitEvent *pEvent)
931{
932 if (!pEvent) /* Nothing to unregister. */
933 return;
934
935 int rc = RTCritSectEnter(&mWaitEventCritSect);
936 if (RT_SUCCESS(rc))
937 {
938 const std::list<VBoxEventType_T> lstTypes = pEvent->Types();
939 for (std::list<VBoxEventType_T>::const_iterator itEvents = lstTypes.begin();
940 itEvents != lstTypes.end(); itEvents++)
941 {
942 /** @todo Slow O(n) lookup. Optimize this. */
943 GuestWaitEvents::iterator itCurEvent = mWaitEvents[(*itEvents)].begin();
944 while (itCurEvent != mWaitEvents[(*itEvents)].end())
945 {
946 if ((*itCurEvent) == pEvent)
947 {
948 itCurEvent = mWaitEvents[(*itEvents)].erase(itCurEvent);
949 break;
950 }
951 else
952 itCurEvent++;
953 }
954 }
955
956 delete pEvent;
957 pEvent = NULL;
958
959 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
960 if (RT_SUCCESS(rc))
961 rc = rc2;
962 }
963}
964
965int GuestBase::waitForEvent(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
966 VBoxEventType_T *pType, IEvent **ppEvent)
967{
968 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
969
970 LogFlowFunc(("pEvent=%p, uTimeoutMS=%RU32\n",
971 pEvent, uTimeoutMS));
972
973 int vrc = pEvent->Wait(uTimeoutMS);
974 if (RT_SUCCESS(vrc))
975 {
976 const ComPtr<IEvent> pThisEvent = pEvent->Event();
977 Assert(!pThisEvent.isNull());
978
979 if (pType)
980 {
981 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
982 if (FAILED(hr))
983 vrc = VERR_COM_UNEXPECTED;
984 }
985 if ( RT_SUCCESS(vrc)
986 && ppEvent)
987 pThisEvent.queryInterfaceTo(ppEvent);
988
989 unconst(pThisEvent).setNull();
990 }
991
992 LogFlowFuncLeaveRC(vrc);
993 return vrc;
994}
995
996GuestObject::GuestObject(void)
997 : mSession(NULL),
998 mObjectID(0)
999{
1000}
1001
1002GuestObject::~GuestObject(void)
1003{
1004}
1005
1006int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1007{
1008 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1009 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1010
1011 mConsole = pConsole;
1012 mSession = pSession;
1013 mObjectID = uObjectID;
1014
1015 return VINF_SUCCESS;
1016}
1017
1018int GuestObject::registerWaitEvent(const std::list<VBoxEventType_T> &lstEvents,
1019 GuestWaitEvent **ppEvent)
1020{
1021 AssertPtr(mSession);
1022 return GuestBase::registerWaitEvent(mSession->getId(), mObjectID, lstEvents, ppEvent);
1023}
1024
1025int GuestObject::sendCommand(uint32_t uFunction,
1026 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
1027{
1028 LogFlowThisFuncEnter();
1029
1030#ifndef VBOX_GUESTCTRL_TEST_CASE
1031 ComObjPtr<Console> pConsole = mConsole;
1032 Assert(!pConsole.isNull());
1033
1034 /* Forward the information to the VMM device. */
1035 VMMDev *pVMMDev = pConsole->getVMMDev();
1036 AssertPtr(pVMMDev);
1037
1038 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
1039 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
1040 if (RT_FAILURE(vrc))
1041 {
1042 /** @todo What to do here? */
1043 }
1044#else
1045 /* Not needed within testcases. */
1046 int vrc = VINF_SUCCESS;
1047#endif
1048 LogFlowFuncLeaveRC(vrc);
1049 return vrc;
1050}
1051
1052GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
1053 const std::list<VBoxEventType_T> &lstEvents)
1054 : fAborted(false),
1055 mCID(uCID),
1056 mEventTypes(lstEvents),
1057 mEventSem(NIL_RTSEMEVENT)
1058{
1059 int rc = RTSemEventCreate(&mEventSem);
1060 AssertRC(rc);
1061 /** @todo Throw an exception on failure! */
1062}
1063
1064GuestWaitEvent::~GuestWaitEvent(void)
1065{
1066
1067}
1068
1069/**
1070 * Signals the event. Passing NULL will abort (cancel)
1071 * the event.
1072 *
1073 * @return IPRT status code.
1074 * @param pEvent IEvent to associate.
1075 */
1076int GuestWaitEvent::Signal(IEvent *pEvent)
1077{
1078 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1079
1080 if (pEvent)
1081 mEvent = pEvent;
1082 else
1083 ASMAtomicWriteBool(&fAborted, true);
1084
1085 return RTSemEventSignal(mEventSem);
1086}
1087
1088int GuestWaitEvent::Wait(RTMSINTERVAL uTimeoutMS)
1089{
1090 LogFlowThisFunc(("uTimeoutMS=%RU32ms\n", uTimeoutMS));
1091
1092 int rc = VINF_SUCCESS;
1093
1094 if (ASMAtomicReadBool(&fAborted))
1095 rc = VERR_CANCELLED;
1096
1097 if (RT_SUCCESS(rc))
1098 {
1099 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1100
1101 RTMSINTERVAL msInterval = uTimeoutMS;
1102 if (!uTimeoutMS)
1103 msInterval = RT_INDEFINITE_WAIT;
1104 rc = RTSemEventWait(mEventSem, msInterval);
1105 if (ASMAtomicReadBool(&fAborted))
1106 rc = VERR_CANCELLED;
1107 }
1108
1109 LogFlowFuncLeaveRC(rc);
1110 return rc;
1111}
1112
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use