VirtualBox

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

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

Main/GuestCtrl: Bugfixes:

  • Hold lock while registering wait event in GuestProcess::waitFor()
  • Fixes for GuestProcess::waitFlagsToResultEx()
  • Use public wait events for internal GuestFile methods
  • Remove wait event from all other event groups when signaled
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.0 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 49440 2013-11-11 15:44:53Z 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 * Tries to parse the next upcoming pair block within the internal
683 * buffer.
684 *
685 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
686 * completely parsed already.
687 *
688 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
689 * stored in stream block) but still contains incomplete (unterminated)
690 * data.
691 *
692 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
693 * block (with zero or more pairs stored in stream block).
694 *
695 * @return IPRT status code.
696 * @param streamBlock Reference to guest stream block to fill.
697 *
698 */
699int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
700{
701 if ( !m_pbBuffer
702 || !m_cbSize)
703 {
704 return VERR_NO_DATA;
705 }
706
707 AssertReturn(m_cbOffset <= m_cbSize, VERR_INVALID_PARAMETER);
708 if (m_cbOffset == m_cbSize)
709 return VERR_NO_DATA;
710
711 int rc = VINF_SUCCESS;
712
713 char *pszOff = (char*)&m_pbBuffer[m_cbOffset];
714 char *pszStart = pszOff;
715 uint32_t uDistance;
716 while (*pszStart)
717 {
718 size_t pairLen = strlen(pszStart);
719 uDistance = (pszStart - pszOff);
720 if (m_cbOffset + uDistance + pairLen + 1 >= m_cbSize)
721 {
722 rc = VERR_MORE_DATA;
723 break;
724 }
725 else
726 {
727 char *pszSep = strchr(pszStart, '=');
728 char *pszVal = NULL;
729 if (pszSep)
730 pszVal = pszSep + 1;
731 if (!pszSep || !pszVal)
732 {
733 rc = VERR_MORE_DATA;
734 break;
735 }
736
737 /* Terminate the separator so that we can
738 * use pszStart as our key from now on. */
739 *pszSep = '\0';
740
741 rc = streamBlock.SetValue(pszStart, pszVal);
742 if (RT_FAILURE(rc))
743 return rc;
744 }
745
746 /* Next pair. */
747 pszStart += pairLen + 1;
748 }
749
750 /* If we did not do any movement but we have stuff left
751 * in our buffer just skip the current termination so that
752 * we can try next time. */
753 uDistance = (pszStart - pszOff);
754 if ( !uDistance
755 && *pszStart == '\0'
756 && m_cbOffset < m_cbSize)
757 {
758 uDistance++;
759 }
760 m_cbOffset += uDistance;
761
762 return rc;
763}
764
765GuestBase::GuestBase(void)
766 : mConsole(NULL),
767 mNextContextID(0)
768{
769}
770
771GuestBase::~GuestBase(void)
772{
773}
774
775int GuestBase::baseInit(void)
776{
777 int rc = RTCritSectInit(&mWaitEventCritSect);
778
779 LogFlowFuncLeaveRC(rc);
780 return rc;
781}
782
783void GuestBase::baseUninit(void)
784{
785 LogFlowThisFuncEnter();
786
787 int rc = RTCritSectDelete(&mWaitEventCritSect);
788
789 LogFlowFuncLeaveRC(rc);
790 /* No return value. */
791}
792
793int GuestBase::cancelWaitEvents(void)
794{
795 LogFlowThisFuncEnter();
796
797 int rc = RTCritSectEnter(&mWaitEventCritSect);
798 if (RT_SUCCESS(rc))
799 {
800 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
801 while (itEventGroups != mWaitEventGroups.end())
802 {
803 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
804 while (itEvents != itEventGroups->second.end())
805 {
806 GuestWaitEvent *pEvent = itEvents->second;
807 AssertPtr(pEvent);
808
809 /*
810 * Just cancel the event, but don't remove it from the
811 * wait events map. Don't delete it though, this (hopefully)
812 * is done by the caller using unregisterWaitEvent().
813 */
814 int rc2 = pEvent->Cancel();
815 AssertRC(rc2);
816
817 itEvents++;
818 }
819
820 itEventGroups++;
821 }
822
823 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
824 if (RT_SUCCESS(rc))
825 rc = rc2;
826 }
827
828 LogFlowFuncLeaveRC(rc);
829 return rc;
830}
831
832int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
833{
834 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
835
836 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
837 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
838
839 int vrc = VINF_SUCCESS;
840
841 try
842 {
843 LogFlowFunc(("uFunc=%RU32, cParms=%RU32\n",
844 pCtxCb->uFunction, pSvcCb->mParms));
845
846 switch (pCtxCb->uFunction)
847 {
848 case GUEST_MSG_PROGRESS_UPDATE:
849 break;
850
851 case GUEST_MSG_REPLY:
852 {
853 if (pSvcCb->mParms >= 3)
854 {
855 int idx = 1; /* Current parameter index. */
856 CALLBACKDATA_MSG_REPLY dataCb;
857 /* pSvcCb->mpaParms[0] always contains the context ID. */
858 vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.uType);
859 AssertRCReturn(vrc, vrc);
860 vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.rc);
861 AssertRCReturn(vrc, vrc);
862 vrc = pSvcCb->mpaParms[idx++].getPointer(&dataCb.pvPayload, &dataCb.cbPayload);
863 AssertRCReturn(vrc, vrc);
864
865 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
866 int rc2 = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
867 AssertRC(rc2);
868 }
869 else
870 vrc = VERR_INVALID_PARAMETER;
871 break;
872 }
873
874 default:
875 vrc = VERR_NOT_SUPPORTED;
876 break;
877 }
878 }
879 catch (std::bad_alloc)
880 {
881 vrc = VERR_NO_MEMORY;
882 }
883 catch (int rc)
884 {
885 vrc = rc;
886 }
887
888 LogFlowFuncLeaveRC(vrc);
889 return vrc;
890}
891
892int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
893{
894 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
895
896 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
897 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
898 return VERR_INVALID_PARAMETER;
899
900 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
901 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
902 uCount = 0;
903
904 uint32_t uNewContextID =
905 VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
906
907 *puContextID = uNewContextID;
908
909#if 0
910 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
911 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
912#endif
913 return VINF_SUCCESS;
914}
915
916int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
917 GuestWaitEvent **ppEvent)
918{
919 GuestEventTypes eventTypesEmpty;
920 return registerWaitEvent(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
921}
922
923int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
924 const GuestEventTypes &lstEvents,
925 GuestWaitEvent **ppEvent)
926{
927 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
928
929 uint32_t uContextID;
930 int rc = generateContextID(uSessionID, uObjectID, &uContextID);
931 if (RT_FAILURE(rc))
932 return rc;
933
934 rc = RTCritSectEnter(&mWaitEventCritSect);
935 if (RT_SUCCESS(rc))
936 {
937 try
938 {
939 GuestWaitEvent *pEvent = new GuestWaitEvent(uContextID, lstEvents);
940 AssertPtr(pEvent);
941
942 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, uContextID));
943
944 /* Insert event into matching event group. This is for faster per-group
945 * lookup of all events later. */
946 for (GuestEventTypes::const_iterator itEvents = lstEvents.begin();
947 itEvents != lstEvents.end(); itEvents++)
948 {
949 mWaitEventGroups[(*itEvents)].insert(
950 std::pair<uint32_t, GuestWaitEvent*>(uContextID, pEvent));
951 /** @todo Check for key collision. */
952 }
953
954 /* Register event in regular event list. */
955 /** @todo Check for key collisions. */
956 mWaitEvents[uContextID] = pEvent;
957
958 *ppEvent = pEvent;
959 }
960 catch(std::bad_alloc &)
961 {
962 rc = VERR_NO_MEMORY;
963 }
964
965 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
966 if (RT_SUCCESS(rc))
967 rc = rc2;
968 }
969
970 return rc;
971}
972
973int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
974{
975 int rc = RTCritSectEnter(&mWaitEventCritSect);
976#ifdef DEBUG
977 uint32_t cEvents = 0;
978#endif
979 if (RT_SUCCESS(rc))
980 {
981 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
982 if (itGroup != mWaitEventGroups.end())
983 {
984 GuestWaitEvents::iterator itEvents = itGroup->second.begin();
985 while (itEvents != itGroup->second.end())
986 {
987#ifdef DEBUG
988 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
989 itEvents->second, aType, itEvents->first,
990 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(itEvents->first),
991 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(itEvents->first),
992 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(itEvents->first)));
993#endif
994 ComPtr<IEvent> pThisEvent = aEvent;
995 Assert(!pThisEvent.isNull());
996 int rc2 = itEvents->second->SignalExternal(aEvent);
997 if (RT_SUCCESS(rc))
998 rc = rc2;
999
1000 if (RT_SUCCESS(rc2))
1001 {
1002 /* Remove the event from all other event groups (except the
1003 * original one!) because it was signalled. */
1004 AssertPtr(itEvents->second);
1005 const GuestEventTypes evTypes = itEvents->second->Types();
1006 for (GuestEventTypes::const_iterator itType = evTypes.begin();
1007 itType != evTypes.end(); itType++)
1008 {
1009 if ((*itType) != aType) /* Only remove all other groups. */
1010 {
1011 /* Get current event group. */
1012 GuestEventGroup::iterator evGroup = mWaitEventGroups.find((*itType));
1013 Assert(evGroup != mWaitEventGroups.end());
1014
1015 /* Lookup event in event group. */
1016 GuestWaitEvents::iterator evEvent = evGroup->second.find(itEvents->first /* Context ID */);
1017 Assert(evEvent != evGroup->second.end());
1018
1019 LogFlowThisFunc(("Removing event=%p (type %ld)\n", evEvent->second, (*itType)));
1020 evGroup->second.erase(evEvent);
1021
1022 LogFlowThisFunc(("%zu events for type=%ld left\n",
1023 evGroup->second.size(), aType));
1024 }
1025 }
1026
1027 /* Remove the event from the passed-in event group. */
1028 itGroup->second.erase(itEvents++);
1029 }
1030 else
1031 itEvents++;
1032#ifdef DEBUG
1033 cEvents++;
1034#endif
1035 }
1036 }
1037
1038 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1039 if (RT_SUCCESS(rc))
1040 rc = rc2;
1041 }
1042
1043#ifdef DEBUG
1044 LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
1045#endif
1046 return rc;
1047}
1048
1049int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1050 int guestRc, const GuestWaitEventPayload *pPayload)
1051{
1052 if (RT_SUCCESS(guestRc))
1053 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
1054 0 /* Guest rc */, pPayload);
1055
1056 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
1057 guestRc, pPayload);
1058}
1059
1060int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1061 int rc, int guestRc,
1062 const GuestWaitEventPayload *pPayload)
1063{
1064 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1065 /* pPayload is optional. */
1066
1067 int rc2;
1068 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1069 if (itEvent != mWaitEvents.end())
1070 {
1071 LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, guestRc=%Rrc, pPayload=%p) ...\n",
1072 itEvent->second, itEvent->first, rc, guestRc, pPayload));
1073 GuestWaitEvent *pEvent = itEvent->second;
1074 AssertPtr(pEvent);
1075 rc2 = pEvent->SignalInternal(rc, guestRc, pPayload);
1076 }
1077 else
1078 rc2 = VERR_NOT_FOUND;
1079
1080 return rc2;
1081}
1082
1083void GuestBase::unregisterWaitEvent(GuestWaitEvent *pEvent)
1084{
1085 if (!pEvent) /* Nothing to unregister. */
1086 return;
1087
1088 int rc = RTCritSectEnter(&mWaitEventCritSect);
1089 if (RT_SUCCESS(rc))
1090 {
1091 LogFlowThisFunc(("pEvent=%p\n", pEvent));
1092
1093 const GuestEventTypes lstTypes = pEvent->Types();
1094 for (GuestEventTypes::const_iterator itEvents = lstTypes.begin();
1095 itEvents != lstTypes.end(); itEvents++)
1096 {
1097 /** @todo Slow O(n) lookup. Optimize this. */
1098 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itEvents)].begin();
1099 while (itCurEvent != mWaitEventGroups[(*itEvents)].end())
1100 {
1101 if (itCurEvent->second == pEvent)
1102 {
1103 mWaitEventGroups[(*itEvents)].erase(itCurEvent++);
1104 break;
1105 }
1106 else
1107 itCurEvent++;
1108 }
1109 }
1110
1111 delete pEvent;
1112 pEvent = NULL;
1113
1114 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1115 if (RT_SUCCESS(rc))
1116 rc = rc2;
1117 }
1118}
1119
1120/**
1121 * Waits for a formerly registered guest event.
1122 *
1123 * @return IPRT status code.
1124 * @param pEvent Pointer to event to wait for.
1125 * @param uTimeoutMS Timeout (in ms) for waiting.
1126 * @param pType Event type of following IEvent.
1127 * Optional.
1128 * @param ppEvent Pointer to IEvent which got triggered
1129 * for this event. Optional.
1130 */
1131int GuestBase::waitForEvent(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1132 VBoxEventType_T *pType, IEvent **ppEvent)
1133{
1134 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1135 /* pType is optional. */
1136 /* ppEvent is optional. */
1137
1138 int vrc = pEvent->Wait(uTimeoutMS);
1139 if (RT_SUCCESS(vrc))
1140 {
1141 const ComPtr<IEvent> pThisEvent = pEvent->Event();
1142 if (!pThisEvent.isNull()) /* Having a VBoxEventType_ event is optional. */
1143 {
1144 if (pType)
1145 {
1146 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
1147 if (FAILED(hr))
1148 vrc = VERR_COM_UNEXPECTED;
1149 }
1150 if ( RT_SUCCESS(vrc)
1151 && ppEvent)
1152 pThisEvent.queryInterfaceTo(ppEvent);
1153
1154 unconst(pThisEvent).setNull();
1155 }
1156 }
1157
1158 return vrc;
1159}
1160
1161GuestObject::GuestObject(void)
1162 : mSession(NULL),
1163 mObjectID(0)
1164{
1165}
1166
1167GuestObject::~GuestObject(void)
1168{
1169}
1170
1171int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1172{
1173 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1174 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1175
1176 mConsole = pConsole;
1177 mSession = pSession;
1178 mObjectID = uObjectID;
1179
1180 return VINF_SUCCESS;
1181}
1182
1183int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1184 GuestWaitEvent **ppEvent)
1185{
1186 AssertPtr(mSession);
1187 return GuestBase::registerWaitEvent(mSession->getId(), mObjectID, lstEvents, ppEvent);
1188}
1189
1190int GuestObject::sendCommand(uint32_t uFunction,
1191 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
1192{
1193#ifndef VBOX_GUESTCTRL_TEST_CASE
1194 ComObjPtr<Console> pConsole = mConsole;
1195 Assert(!pConsole.isNull());
1196
1197 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1198
1199 /* Forward the information to the VMM device. */
1200 VMMDev *pVMMDev = pConsole->getVMMDev();
1201 if (pVMMDev)
1202 {
1203 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
1204 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
1205 if (RT_FAILURE(vrc))
1206 {
1207 /** @todo What to do here? */
1208 }
1209 }
1210#else
1211 LogFlowThisFuncEnter();
1212
1213 /* Not needed within testcases. */
1214 int vrc = VINF_SUCCESS;
1215#endif
1216 return vrc;
1217}
1218
1219GuestWaitEventBase::GuestWaitEventBase(void)
1220 : mfAborted(false),
1221 mCID(0),
1222 mEventSem(NIL_RTSEMEVENT),
1223 mRc(VINF_SUCCESS),
1224 mGuestRc(VINF_SUCCESS)
1225{
1226}
1227
1228GuestWaitEventBase::~GuestWaitEventBase(void)
1229{
1230}
1231
1232int GuestWaitEventBase::Init(uint32_t uCID)
1233{
1234 mCID = uCID;
1235
1236 return RTSemEventCreate(&mEventSem);
1237}
1238
1239int GuestWaitEventBase::SignalInternal(int rc, int guestRc,
1240 const GuestWaitEventPayload *pPayload)
1241{
1242 if (ASMAtomicReadBool(&mfAborted))
1243 return VERR_CANCELLED;
1244
1245#ifdef VBOX_STRICT
1246 if (rc == VERR_GSTCTL_GUEST_ERROR)
1247 AssertMsg(RT_FAILURE(guestRc), ("Guest error indicated but no actual guest error set (%Rrc)\n", guestRc));
1248 else
1249 AssertMsg(RT_SUCCESS(guestRc), ("No guest error indicated but actual guest error set (%Rrc)\n", guestRc));
1250#endif
1251
1252 int rc2;
1253 if (pPayload)
1254 rc2 = mPayload.CopyFromDeep(*pPayload);
1255 else
1256 rc2 = VINF_SUCCESS;
1257 if (RT_SUCCESS(rc2))
1258 {
1259 mRc = rc;
1260 mGuestRc = guestRc;
1261
1262 rc2 = RTSemEventSignal(mEventSem);
1263 }
1264
1265 return rc2;
1266}
1267
1268int GuestWaitEventBase::Wait(RTMSINTERVAL uTimeoutMS)
1269{
1270 int rc = VINF_SUCCESS;
1271
1272 if (ASMAtomicReadBool(&mfAborted))
1273 rc = VERR_CANCELLED;
1274
1275 if (RT_SUCCESS(rc))
1276 {
1277 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1278
1279 RTMSINTERVAL msInterval = uTimeoutMS;
1280 if (!uTimeoutMS)
1281 msInterval = RT_INDEFINITE_WAIT;
1282 rc = RTSemEventWait(mEventSem, msInterval);
1283 if (ASMAtomicReadBool(&mfAborted))
1284 rc = VERR_CANCELLED;
1285 if (RT_SUCCESS(rc))
1286 {
1287 /* If waiting succeeded, return the overall
1288 * result code. */
1289 rc = mRc;
1290 }
1291 }
1292
1293 return rc;
1294}
1295
1296GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
1297 const GuestEventTypes &lstEvents)
1298{
1299 int rc2 = Init(uCID);
1300 AssertRC(rc2); /** @todo Throw exception here. */
1301
1302 mEventTypes = lstEvents;
1303}
1304
1305GuestWaitEvent::GuestWaitEvent(uint32_t uCID)
1306{
1307 int rc2 = Init(uCID);
1308 AssertRC(rc2); /** @todo Throw exception here. */
1309}
1310
1311GuestWaitEvent::~GuestWaitEvent(void)
1312{
1313
1314}
1315
1316/**
1317 * Cancels the event.
1318 */
1319int GuestWaitEvent::Cancel(void)
1320{
1321 AssertReturn(!mfAborted, VERR_CANCELLED);
1322 ASMAtomicWriteBool(&mfAborted, true);
1323
1324#ifdef DEBUG_andy
1325 LogFlowThisFunc(("Cancelling %p ...\n"));
1326#endif
1327 return RTSemEventSignal(mEventSem);
1328}
1329
1330int GuestWaitEvent::Init(uint32_t uCID)
1331{
1332 return GuestWaitEventBase::Init(uCID);
1333}
1334
1335/**
1336 * Signals the event.
1337 *
1338 * @return IPRT status code.
1339 * @param pEvent Public IEvent to associate.
1340 * Optional.
1341 */
1342int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1343{
1344 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1345
1346 if (pEvent)
1347 mEvent = pEvent;
1348
1349 return RTSemEventSignal(mEventSem);
1350}
1351
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