VirtualBox

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

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

6813 src-client/GuestSessionImpl.cpp

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.8 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 50727 2014-03-07 18:21:44Z 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::FromMkTemp(const GuestProcessStreamBlock &strmBlk)
330{
331 LogFlowFunc(("\n"));
332
333 int rc;
334
335 try
336 {
337#ifdef DEBUG
338 strmBlk.DumpToLog();
339#endif
340 /* Object name. */
341 mName = strmBlk.GetString("name");
342 if (mName.isEmpty()) throw VERR_NOT_FOUND;
343 /* Assign the stream block's rc. */
344 rc = strmBlk.GetRc();
345 }
346 catch (int rc2)
347 {
348 rc = rc2;
349 }
350
351 LogFlowFuncLeaveRC(rc);
352 return rc;
353}
354
355int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
356{
357 LogFlowFunc(("\n"));
358
359 int rc = VINF_SUCCESS;
360
361 try
362 {
363#ifdef DEBUG
364 strmBlk.DumpToLog();
365#endif
366 /* Node ID, optional because we don't include this
367 * in older VBoxService (< 4.2) versions. */
368 mNodeID = strmBlk.GetInt64("node_id");
369 /* Object name. */
370 mName = strmBlk.GetString("name");
371 if (mName.isEmpty()) throw VERR_NOT_FOUND;
372 /* Type. */
373 Utf8Str strType(strmBlk.GetString("ftype"));
374 if (strType.equalsIgnoreCase("-"))
375 mType = FsObjType_File;
376 else if (strType.equalsIgnoreCase("d"))
377 mType = FsObjType_Directory;
378 /** @todo Add more types! */
379 else
380 mType = FsObjType_Undefined;
381 /* Object size. */
382 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
383 if (RT_FAILURE(rc)) throw rc;
384 /** @todo Add complete stat info! */
385 }
386 catch (int rc2)
387 {
388 rc = rc2;
389 }
390
391 LogFlowFuncLeaveRC(rc);
392 return rc;
393}
394
395///////////////////////////////////////////////////////////////////////////////
396
397/** @todo *NOT* thread safe yet! */
398/** @todo Add exception handling for STL stuff! */
399
400GuestProcessStreamBlock::GuestProcessStreamBlock(void)
401{
402
403}
404
405/*
406GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
407{
408 for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
409 it != otherBlock.end(); it++)
410 {
411 mPairs[it->first] = new
412 if (it->second.pszValue)
413 {
414 RTMemFree(it->second.pszValue);
415 it->second.pszValue = NULL;
416 }
417 }
418}*/
419
420GuestProcessStreamBlock::~GuestProcessStreamBlock()
421{
422 Clear();
423}
424
425/**
426 * Destroys the currently stored stream pairs.
427 *
428 * @return IPRT status code.
429 */
430void GuestProcessStreamBlock::Clear(void)
431{
432 mPairs.clear();
433}
434
435#ifdef DEBUG
436void GuestProcessStreamBlock::DumpToLog(void) const
437{
438 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
439 this, mPairs.size()));
440
441 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
442 it != mPairs.end(); it++)
443 {
444 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
445 }
446}
447#endif
448
449/**
450 * Returns a 64-bit signed integer of a specified key.
451 *
452 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
453 * @param pszKey Name of key to get the value for.
454 * @param piVal Pointer to value to return.
455 */
456int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
457{
458 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
459 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
460 const char *pszValue = GetString(pszKey);
461 if (pszValue)
462 {
463 *piVal = RTStrToInt64(pszValue);
464 return VINF_SUCCESS;
465 }
466 return VERR_NOT_FOUND;
467}
468
469/**
470 * Returns a 64-bit integer of a specified key.
471 *
472 * @return int64_t Value to return, 0 if not found / on failure.
473 * @param pszKey Name of key to get the value for.
474 */
475int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
476{
477 int64_t iVal;
478 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
479 return iVal;
480 return 0;
481}
482
483/**
484 * Returns the current number of stream pairs.
485 *
486 * @return uint32_t Current number of stream pairs.
487 */
488size_t GuestProcessStreamBlock::GetCount(void) const
489{
490 return mPairs.size();
491}
492
493/**
494 * Gets the return code (name = "rc") of this stream block.
495 *
496 * @return IPRT status code.
497 */
498int GuestProcessStreamBlock::GetRc(void) const
499{
500 const char *pszValue = GetString("rc");
501 if (pszValue)
502 {
503 return RTStrToInt16(pszValue);
504 }
505 return VERR_NOT_FOUND;
506}
507
508/**
509 * Returns a string value of a specified key.
510 *
511 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
512 * @param pszKey Name of key to get the value for.
513 */
514const char* GuestProcessStreamBlock::GetString(const char *pszKey) const
515{
516 AssertPtrReturn(pszKey, NULL);
517
518 try
519 {
520 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey));
521 if (itPairs != mPairs.end())
522 return itPairs->second.mValue.c_str();
523 }
524 catch (const std::exception &ex)
525 {
526 NOREF(ex);
527 }
528 return NULL;
529}
530
531/**
532 * Returns a 32-bit unsigned integer of a specified key.
533 *
534 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
535 * @param pszKey Name of key to get the value for.
536 * @param puVal Pointer to value to return.
537 */
538int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
539{
540 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
541 AssertPtrReturn(puVal, VERR_INVALID_POINTER);
542 const char *pszValue = GetString(pszKey);
543 if (pszValue)
544 {
545 *puVal = RTStrToUInt32(pszValue);
546 return VINF_SUCCESS;
547 }
548 return VERR_NOT_FOUND;
549}
550
551/**
552 * Returns a 32-bit unsigned integer of a specified key.
553 *
554 * @return uint32_t Value to return, 0 if not found / on failure.
555 * @param pszKey Name of key to get the value for.
556 */
557uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey) const
558{
559 uint32_t uVal;
560 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
561 return uVal;
562 return 0;
563}
564
565/**
566 * Sets a value to a key or deletes a key by setting a NULL value.
567 *
568 * @return IPRT status code.
569 * @param pszKey Key name to process.
570 * @param pszValue Value to set. Set NULL for deleting the key.
571 */
572int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
573{
574 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
575
576 int rc = VINF_SUCCESS;
577 try
578 {
579 Utf8Str Utf8Key(pszKey);
580
581 /* Take a shortcut and prevent crashes on some funny versions
582 * of STL if map is empty initially. */
583 if (!mPairs.empty())
584 {
585 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
586 if (it != mPairs.end())
587 mPairs.erase(it);
588 }
589
590 if (pszValue)
591 {
592 GuestProcessStreamValue val(pszValue);
593 mPairs[Utf8Key] = val;
594 }
595 }
596 catch (const std::exception &ex)
597 {
598 NOREF(ex);
599 }
600 return rc;
601}
602
603///////////////////////////////////////////////////////////////////////////////
604
605GuestProcessStream::GuestProcessStream(void)
606 : m_cbAllocated(0),
607 m_cbSize(0),
608 m_cbOffset(0),
609 m_pbBuffer(NULL)
610{
611
612}
613
614GuestProcessStream::~GuestProcessStream(void)
615{
616 Destroy();
617}
618
619/**
620 * Adds data to the internal parser buffer. Useful if there
621 * are multiple rounds of adding data needed.
622 *
623 * @return IPRT status code.
624 * @param pbData Pointer to data to add.
625 * @param cbData Size (in bytes) of data to add.
626 */
627int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
628{
629 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
630 AssertReturn(cbData, VERR_INVALID_PARAMETER);
631
632 int rc = VINF_SUCCESS;
633
634 /* Rewind the buffer if it's empty. */
635 size_t cbInBuf = m_cbSize - m_cbOffset;
636 bool const fAddToSet = cbInBuf == 0;
637 if (fAddToSet)
638 m_cbSize = m_cbOffset = 0;
639
640 /* Try and see if we can simply append the data. */
641 if (cbData + m_cbSize <= m_cbAllocated)
642 {
643 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
644 m_cbSize += cbData;
645 }
646 else
647 {
648 /* Move any buffered data to the front. */
649 cbInBuf = m_cbSize - m_cbOffset;
650 if (cbInBuf == 0)
651 m_cbSize = m_cbOffset = 0;
652 else if (m_cbOffset) /* Do we have something to move? */
653 {
654 memmove(m_pbBuffer, &m_pbBuffer[m_cbOffset], cbInBuf);
655 m_cbSize = cbInBuf;
656 m_cbOffset = 0;
657 }
658
659 /* Do we need to grow the buffer? */
660 if (cbData + m_cbSize > m_cbAllocated)
661 {
662 size_t cbAlloc = m_cbSize + cbData;
663 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
664 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
665 if (pvNew)
666 {
667 m_pbBuffer = (uint8_t *)pvNew;
668 m_cbAllocated = cbAlloc;
669 }
670 else
671 rc = VERR_NO_MEMORY;
672 }
673
674 /* Finally, copy the data. */
675 if (RT_SUCCESS(rc))
676 {
677 if (cbData + m_cbSize <= m_cbAllocated)
678 {
679 memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
680 m_cbSize += cbData;
681 }
682 else
683 rc = VERR_BUFFER_OVERFLOW;
684 }
685 }
686
687 return rc;
688}
689
690/**
691 * Destroys the internal data buffer.
692 */
693void GuestProcessStream::Destroy(void)
694{
695 if (m_pbBuffer)
696 {
697 RTMemFree(m_pbBuffer);
698 m_pbBuffer = NULL;
699 }
700
701 m_cbAllocated = 0;
702 m_cbSize = 0;
703 m_cbOffset = 0;
704}
705
706#ifdef DEBUG
707void GuestProcessStream::Dump(const char *pszFile)
708{
709 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
710 m_pbBuffer, m_cbAllocated, m_cbSize, m_cbOffset, pszFile));
711
712 RTFILE hFile;
713 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
714 if (RT_SUCCESS(rc))
715 {
716 rc = RTFileWrite(hFile, m_pbBuffer, m_cbSize, NULL /* pcbWritten */);
717 RTFileClose(hFile);
718 }
719}
720#endif
721
722/**
723 * Tries to parse the next upcoming pair block within the internal
724 * buffer.
725 *
726 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
727 * completely parsed already.
728 *
729 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
730 * stored in stream block) but still contains incomplete (unterminated)
731 * data.
732 *
733 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
734 * block (with zero or more pairs stored in stream block).
735 *
736 * @return IPRT status code.
737 * @param streamBlock Reference to guest stream block to fill.
738 *
739 */
740int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
741{
742 if ( !m_pbBuffer
743 || !m_cbSize)
744 {
745 return VERR_NO_DATA;
746 }
747
748 AssertReturn(m_cbOffset <= m_cbSize, VERR_INVALID_PARAMETER);
749 if (m_cbOffset == m_cbSize)
750 return VERR_NO_DATA;
751
752 int rc = VINF_SUCCESS;
753
754 char *pszOff = (char*)&m_pbBuffer[m_cbOffset];
755 char *pszStart = pszOff;
756 uint32_t uDistance;
757 while (*pszStart)
758 {
759 size_t pairLen = strlen(pszStart);
760 uDistance = (pszStart - pszOff);
761 if (m_cbOffset + uDistance + pairLen + 1 >= m_cbSize)
762 {
763 rc = VERR_MORE_DATA;
764 break;
765 }
766 else
767 {
768 char *pszSep = strchr(pszStart, '=');
769 char *pszVal = NULL;
770 if (pszSep)
771 pszVal = pszSep + 1;
772 if (!pszSep || !pszVal)
773 {
774 rc = VERR_MORE_DATA;
775 break;
776 }
777
778 /* Terminate the separator so that we can
779 * use pszStart as our key from now on. */
780 *pszSep = '\0';
781
782 rc = streamBlock.SetValue(pszStart, pszVal);
783 if (RT_FAILURE(rc))
784 return rc;
785 }
786
787 /* Next pair. */
788 pszStart += pairLen + 1;
789 }
790
791 /* If we did not do any movement but we have stuff left
792 * in our buffer just skip the current termination so that
793 * we can try next time. */
794 uDistance = (pszStart - pszOff);
795 if ( !uDistance
796 && *pszStart == '\0'
797 && m_cbOffset < m_cbSize)
798 {
799 uDistance++;
800 }
801 m_cbOffset += uDistance;
802
803 return rc;
804}
805
806GuestBase::GuestBase(void)
807 : mConsole(NULL),
808 mNextContextID(0)
809{
810}
811
812GuestBase::~GuestBase(void)
813{
814}
815
816int GuestBase::baseInit(void)
817{
818 int rc = RTCritSectInit(&mWaitEventCritSect);
819
820 LogFlowFuncLeaveRC(rc);
821 return rc;
822}
823
824void GuestBase::baseUninit(void)
825{
826 LogFlowThisFuncEnter();
827
828 int rc = RTCritSectDelete(&mWaitEventCritSect);
829
830 LogFlowFuncLeaveRC(rc);
831 /* No return value. */
832}
833
834int GuestBase::cancelWaitEvents(void)
835{
836 LogFlowThisFuncEnter();
837
838 int rc = RTCritSectEnter(&mWaitEventCritSect);
839 if (RT_SUCCESS(rc))
840 {
841 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
842 while (itEventGroups != mWaitEventGroups.end())
843 {
844 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
845 while (itEvents != itEventGroups->second.end())
846 {
847 GuestWaitEvent *pEvent = itEvents->second;
848 AssertPtr(pEvent);
849
850 /*
851 * Just cancel the event, but don't remove it from the
852 * wait events map. Don't delete it though, this (hopefully)
853 * is done by the caller using unregisterWaitEvent().
854 */
855 int rc2 = pEvent->Cancel();
856 AssertRC(rc2);
857
858 itEvents++;
859 }
860
861 itEventGroups++;
862 }
863
864 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
865 if (RT_SUCCESS(rc))
866 rc = rc2;
867 }
868
869 LogFlowFuncLeaveRC(rc);
870 return rc;
871}
872
873int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
874{
875 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
876
877 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
878 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
879
880 int vrc = VINF_SUCCESS;
881
882 try
883 {
884 LogFlowFunc(("uFunc=%RU32, cParms=%RU32\n",
885 pCtxCb->uFunction, pSvcCb->mParms));
886
887 switch (pCtxCb->uFunction)
888 {
889 case GUEST_MSG_PROGRESS_UPDATE:
890 break;
891
892 case GUEST_MSG_REPLY:
893 {
894 if (pSvcCb->mParms >= 3)
895 {
896 int idx = 1; /* Current parameter index. */
897 CALLBACKDATA_MSG_REPLY dataCb;
898 /* pSvcCb->mpaParms[0] always contains the context ID. */
899 vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.uType);
900 AssertRCReturn(vrc, vrc);
901 vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.rc);
902 AssertRCReturn(vrc, vrc);
903 vrc = pSvcCb->mpaParms[idx++].getPointer(&dataCb.pvPayload, &dataCb.cbPayload);
904 AssertRCReturn(vrc, vrc);
905
906 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
907 int rc2 = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
908 AssertRC(rc2);
909 }
910 else
911 vrc = VERR_INVALID_PARAMETER;
912 break;
913 }
914
915 default:
916 vrc = VERR_NOT_SUPPORTED;
917 break;
918 }
919 }
920 catch (std::bad_alloc)
921 {
922 vrc = VERR_NO_MEMORY;
923 }
924 catch (int rc)
925 {
926 vrc = rc;
927 }
928
929 LogFlowFuncLeaveRC(vrc);
930 return vrc;
931}
932
933int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
934{
935 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
936
937 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
938 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
939 return VERR_INVALID_PARAMETER;
940
941 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
942 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
943 uCount = 0;
944
945 uint32_t uNewContextID =
946 VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
947
948 *puContextID = uNewContextID;
949
950#if 0
951 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
952 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
953#endif
954 return VINF_SUCCESS;
955}
956
957int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
958 GuestWaitEvent **ppEvent)
959{
960 GuestEventTypes eventTypesEmpty;
961 return registerWaitEvent(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
962}
963
964int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
965 const GuestEventTypes &lstEvents,
966 GuestWaitEvent **ppEvent)
967{
968 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
969
970 uint32_t uContextID;
971 int rc = generateContextID(uSessionID, uObjectID, &uContextID);
972 if (RT_FAILURE(rc))
973 return rc;
974
975 rc = RTCritSectEnter(&mWaitEventCritSect);
976 if (RT_SUCCESS(rc))
977 {
978 try
979 {
980 GuestWaitEvent *pEvent = new GuestWaitEvent(uContextID, lstEvents);
981 AssertPtr(pEvent);
982
983 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, uContextID));
984
985 /* Insert event into matching event group. This is for faster per-group
986 * lookup of all events later. */
987 for (GuestEventTypes::const_iterator itEvents = lstEvents.begin();
988 itEvents != lstEvents.end(); itEvents++)
989 {
990 mWaitEventGroups[(*itEvents)].insert(
991 std::pair<uint32_t, GuestWaitEvent*>(uContextID, pEvent));
992 /** @todo Check for key collision. */
993 }
994
995 /* Register event in regular event list. */
996 /** @todo Check for key collisions. */
997 mWaitEvents[uContextID] = pEvent;
998
999 *ppEvent = pEvent;
1000 }
1001 catch(std::bad_alloc &)
1002 {
1003 rc = VERR_NO_MEMORY;
1004 }
1005
1006 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1007 if (RT_SUCCESS(rc))
1008 rc = rc2;
1009 }
1010
1011 return rc;
1012}
1013
1014int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
1015{
1016 int rc = RTCritSectEnter(&mWaitEventCritSect);
1017#ifdef DEBUG
1018 uint32_t cEvents = 0;
1019#endif
1020 if (RT_SUCCESS(rc))
1021 {
1022 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
1023 if (itGroup != mWaitEventGroups.end())
1024 {
1025 GuestWaitEvents::iterator itEvents = itGroup->second.begin();
1026 while (itEvents != itGroup->second.end())
1027 {
1028#ifdef DEBUG
1029 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1030 itEvents->second, aType, itEvents->first,
1031 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(itEvents->first),
1032 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(itEvents->first),
1033 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(itEvents->first)));
1034#endif
1035 ComPtr<IEvent> pThisEvent = aEvent;
1036 Assert(!pThisEvent.isNull());
1037 int rc2 = itEvents->second->SignalExternal(aEvent);
1038 if (RT_SUCCESS(rc))
1039 rc = rc2;
1040
1041 if (RT_SUCCESS(rc2))
1042 {
1043 /* Remove the event from all other event groups (except the
1044 * original one!) because it was signalled. */
1045 AssertPtr(itEvents->second);
1046 const GuestEventTypes evTypes = itEvents->second->Types();
1047 for (GuestEventTypes::const_iterator itType = evTypes.begin();
1048 itType != evTypes.end(); itType++)
1049 {
1050 if ((*itType) != aType) /* Only remove all other groups. */
1051 {
1052 /* Get current event group. */
1053 GuestEventGroup::iterator evGroup = mWaitEventGroups.find((*itType));
1054 Assert(evGroup != mWaitEventGroups.end());
1055
1056 /* Lookup event in event group. */
1057 GuestWaitEvents::iterator evEvent = evGroup->second.find(itEvents->first /* Context ID */);
1058 Assert(evEvent != evGroup->second.end());
1059
1060 LogFlowThisFunc(("Removing event=%p (type %ld)\n", evEvent->second, (*itType)));
1061 evGroup->second.erase(evEvent);
1062
1063 LogFlowThisFunc(("%zu events for type=%ld left\n",
1064 evGroup->second.size(), aType));
1065 }
1066 }
1067
1068 /* Remove the event from the passed-in event group. */
1069 itGroup->second.erase(itEvents++);
1070 }
1071 else
1072 itEvents++;
1073#ifdef DEBUG
1074 cEvents++;
1075#endif
1076 }
1077 }
1078
1079 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1080 if (RT_SUCCESS(rc))
1081 rc = rc2;
1082 }
1083
1084#ifdef DEBUG
1085 LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
1086#endif
1087 return rc;
1088}
1089
1090int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1091 int guestRc, const GuestWaitEventPayload *pPayload)
1092{
1093 if (RT_SUCCESS(guestRc))
1094 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
1095 0 /* Guest rc */, pPayload);
1096
1097 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
1098 guestRc, pPayload);
1099}
1100
1101int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1102 int rc, int guestRc,
1103 const GuestWaitEventPayload *pPayload)
1104{
1105 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1106 /* pPayload is optional. */
1107
1108 int rc2;
1109 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1110 if (itEvent != mWaitEvents.end())
1111 {
1112 LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, guestRc=%Rrc, pPayload=%p) ...\n",
1113 itEvent->second, itEvent->first, rc, guestRc, pPayload));
1114 GuestWaitEvent *pEvent = itEvent->second;
1115 AssertPtr(pEvent);
1116 rc2 = pEvent->SignalInternal(rc, guestRc, pPayload);
1117 }
1118 else
1119 rc2 = VERR_NOT_FOUND;
1120
1121 return rc2;
1122}
1123
1124void GuestBase::unregisterWaitEvent(GuestWaitEvent *pEvent)
1125{
1126 if (!pEvent) /* Nothing to unregister. */
1127 return;
1128
1129 int rc = RTCritSectEnter(&mWaitEventCritSect);
1130 if (RT_SUCCESS(rc))
1131 {
1132 LogFlowThisFunc(("pEvent=%p\n", pEvent));
1133
1134 const GuestEventTypes lstTypes = pEvent->Types();
1135 for (GuestEventTypes::const_iterator itEvents = lstTypes.begin();
1136 itEvents != lstTypes.end(); itEvents++)
1137 {
1138 /** @todo Slow O(n) lookup. Optimize this. */
1139 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itEvents)].begin();
1140 while (itCurEvent != mWaitEventGroups[(*itEvents)].end())
1141 {
1142 if (itCurEvent->second == pEvent)
1143 {
1144 mWaitEventGroups[(*itEvents)].erase(itCurEvent++);
1145 break;
1146 }
1147 else
1148 itCurEvent++;
1149 }
1150 }
1151
1152 delete pEvent;
1153 pEvent = NULL;
1154
1155 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1156 if (RT_SUCCESS(rc))
1157 rc = rc2;
1158 }
1159}
1160
1161/**
1162 * Waits for a formerly registered guest event.
1163 *
1164 * @return IPRT status code.
1165 * @param pEvent Pointer to event to wait for.
1166 * @param uTimeoutMS Timeout (in ms) for waiting.
1167 * @param pType Event type of following IEvent.
1168 * Optional.
1169 * @param ppEvent Pointer to IEvent which got triggered
1170 * for this event. Optional.
1171 */
1172int GuestBase::waitForEvent(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1173 VBoxEventType_T *pType, IEvent **ppEvent)
1174{
1175 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1176 /* pType is optional. */
1177 /* ppEvent is optional. */
1178
1179 int vrc = pEvent->Wait(uTimeoutMS);
1180 if (RT_SUCCESS(vrc))
1181 {
1182 const ComPtr<IEvent> pThisEvent = pEvent->Event();
1183 if (!pThisEvent.isNull()) /* Having a VBoxEventType_ event is optional. */
1184 {
1185 if (pType)
1186 {
1187 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
1188 if (FAILED(hr))
1189 vrc = VERR_COM_UNEXPECTED;
1190 }
1191 if ( RT_SUCCESS(vrc)
1192 && ppEvent)
1193 pThisEvent.queryInterfaceTo(ppEvent);
1194
1195 unconst(pThisEvent).setNull();
1196 }
1197 }
1198
1199 return vrc;
1200}
1201
1202GuestObject::GuestObject(void)
1203 : mSession(NULL),
1204 mObjectID(0)
1205{
1206}
1207
1208GuestObject::~GuestObject(void)
1209{
1210}
1211
1212int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1213{
1214 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1215 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1216
1217 mConsole = pConsole;
1218 mSession = pSession;
1219 mObjectID = uObjectID;
1220
1221 return VINF_SUCCESS;
1222}
1223
1224int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1225 GuestWaitEvent **ppEvent)
1226{
1227 AssertPtr(mSession);
1228 return GuestBase::registerWaitEvent(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1229}
1230
1231int GuestObject::sendCommand(uint32_t uFunction,
1232 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
1233{
1234#ifndef VBOX_GUESTCTRL_TEST_CASE
1235 ComObjPtr<Console> pConsole = mConsole;
1236 Assert(!pConsole.isNull());
1237
1238 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1239
1240 /* Forward the information to the VMM device. */
1241 VMMDev *pVMMDev = pConsole->getVMMDev();
1242 if (pVMMDev)
1243 {
1244 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
1245 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
1246 if (RT_FAILURE(vrc))
1247 {
1248 /** @todo What to do here? */
1249 }
1250 }
1251#else
1252 LogFlowThisFuncEnter();
1253
1254 /* Not needed within testcases. */
1255 int vrc = VINF_SUCCESS;
1256#endif
1257 return vrc;
1258}
1259
1260GuestWaitEventBase::GuestWaitEventBase(void)
1261 : mfAborted(false),
1262 mCID(0),
1263 mEventSem(NIL_RTSEMEVENT),
1264 mRc(VINF_SUCCESS),
1265 mGuestRc(VINF_SUCCESS)
1266{
1267}
1268
1269GuestWaitEventBase::~GuestWaitEventBase(void)
1270{
1271}
1272
1273int GuestWaitEventBase::Init(uint32_t uCID)
1274{
1275 mCID = uCID;
1276
1277 return RTSemEventCreate(&mEventSem);
1278}
1279
1280int GuestWaitEventBase::SignalInternal(int rc, int guestRc,
1281 const GuestWaitEventPayload *pPayload)
1282{
1283 if (ASMAtomicReadBool(&mfAborted))
1284 return VERR_CANCELLED;
1285
1286#ifdef VBOX_STRICT
1287 if (rc == VERR_GSTCTL_GUEST_ERROR)
1288 AssertMsg(RT_FAILURE(guestRc), ("Guest error indicated but no actual guest error set (%Rrc)\n", guestRc));
1289 else
1290 AssertMsg(RT_SUCCESS(guestRc), ("No guest error indicated but actual guest error set (%Rrc)\n", guestRc));
1291#endif
1292
1293 int rc2;
1294 if (pPayload)
1295 rc2 = mPayload.CopyFromDeep(*pPayload);
1296 else
1297 rc2 = VINF_SUCCESS;
1298 if (RT_SUCCESS(rc2))
1299 {
1300 mRc = rc;
1301 mGuestRc = guestRc;
1302
1303 rc2 = RTSemEventSignal(mEventSem);
1304 }
1305
1306 return rc2;
1307}
1308
1309int GuestWaitEventBase::Wait(RTMSINTERVAL uTimeoutMS)
1310{
1311 int rc = VINF_SUCCESS;
1312
1313 if (ASMAtomicReadBool(&mfAborted))
1314 rc = VERR_CANCELLED;
1315
1316 if (RT_SUCCESS(rc))
1317 {
1318 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1319
1320 RTMSINTERVAL msInterval = uTimeoutMS;
1321 if (!uTimeoutMS)
1322 msInterval = RT_INDEFINITE_WAIT;
1323 rc = RTSemEventWait(mEventSem, msInterval);
1324 if (ASMAtomicReadBool(&mfAborted))
1325 rc = VERR_CANCELLED;
1326 if (RT_SUCCESS(rc))
1327 {
1328 /* If waiting succeeded, return the overall
1329 * result code. */
1330 rc = mRc;
1331 }
1332 }
1333
1334 return rc;
1335}
1336
1337GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
1338 const GuestEventTypes &lstEvents)
1339{
1340 int rc2 = Init(uCID);
1341 AssertRC(rc2); /** @todo Throw exception here. */
1342
1343 mEventTypes = lstEvents;
1344}
1345
1346GuestWaitEvent::GuestWaitEvent(uint32_t uCID)
1347{
1348 int rc2 = Init(uCID);
1349 AssertRC(rc2); /** @todo Throw exception here. */
1350}
1351
1352GuestWaitEvent::~GuestWaitEvent(void)
1353{
1354
1355}
1356
1357/**
1358 * Cancels the event.
1359 */
1360int GuestWaitEvent::Cancel(void)
1361{
1362 AssertReturn(!mfAborted, VERR_CANCELLED);
1363 ASMAtomicWriteBool(&mfAborted, true);
1364
1365#ifdef DEBUG_andy
1366 LogFlowThisFunc(("Cancelling %p ...\n"));
1367#endif
1368 return RTSemEventSignal(mEventSem);
1369}
1370
1371int GuestWaitEvent::Init(uint32_t uCID)
1372{
1373 return GuestWaitEventBase::Init(uCID);
1374}
1375
1376/**
1377 * Signals the event.
1378 *
1379 * @return IPRT status code.
1380 * @param pEvent Public IEvent to associate.
1381 * Optional.
1382 */
1383int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1384{
1385 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1386
1387 if (pEvent)
1388 mEvent = pEvent;
1389
1390 return RTSemEventSignal(mEventSem);
1391}
1392
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