VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.7 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * Internal helpers/structures for guest control functionality.
4 */
5
6/*
7 * Copyright (C) 2011-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
23#include "LoggingNew.h"
24
25#ifndef VBOX_WITH_GUEST_CONTROL
26# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
27#endif
28#include "GuestCtrlImplPrivate.h"
29#include "GuestSessionImpl.h"
30#include "VMMDev.h"
31
32#include <iprt/asm.h>
33#include <iprt/cpp/utils.h> /* For unconst(). */
34#include <iprt/ctype.h>
35#ifdef DEBUG
36# include <iprt/file.h>
37#endif
38#include <iprt/fs.h>
39#include <iprt/rand.h>
40#include <iprt/time.h>
41#include <VBox/AssertGuest.h>
42
43
44/**
45 * Extracts the timespec from a given stream block key.
46 *
47 * @return Pointer to handed-in timespec, or NULL if invalid / not found.
48 * @param strmBlk Stream block to extract timespec from.
49 * @param strKey Key to get timespec for.
50 * @param pTimeSpec Where to store the extracted timespec.
51 */
52/* static */
53PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec)
54{
55 AssertPtrReturn(pTimeSpec, NULL);
56
57 Utf8Str strTime = strmBlk.GetString(strKey.c_str());
58 if (strTime.isEmpty())
59 return NULL;
60
61 if (!RTTimeSpecFromString(pTimeSpec, strTime.c_str()))
62 return NULL;
63
64 return pTimeSpec;
65}
66
67/**
68 * Extracts the nanoseconds relative from Unix epoch for a given stream block key.
69 *
70 * @return Nanoseconds relative from Unix epoch, or 0 if invalid / not found.
71 * @param strmBlk Stream block to extract nanoseconds from.
72 * @param strKey Key to get nanoseconds for.
73 */
74/* static */
75int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey)
76{
77 RTTIMESPEC TimeSpec;
78 if (!GuestFsObjData::TimeSpecFromKey(strmBlk, strKey, &TimeSpec))
79 return 0;
80
81 return TimeSpec.i64NanosecondsRelativeToUnixEpoch;
82}
83
84/**
85 * Initializes this object data with a stream block from VBOXSERVICE_TOOL_LS.
86 *
87 * This is also used by FromStat since the output should be identical given that
88 * they use the same output function on the guest side when fLong is true.
89 *
90 * @return VBox status code.
91 * @param strmBlk Stream block to use for initialization.
92 * @param fLong Whether the stream block contains long (detailed) information or not.
93 */
94int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk, bool fLong)
95{
96 LogFlowFunc(("\n"));
97#ifdef DEBUG
98 strmBlk.DumpToLog();
99#endif
100
101 /* Object name. */
102 mName = strmBlk.GetString("name");
103 ASSERT_GUEST_RETURN(mName.isNotEmpty(), VERR_NOT_FOUND);
104
105 /* Type & attributes. */
106 bool fHaveAttribs = false;
107 char szAttribs[32];
108 memset(szAttribs, '?', sizeof(szAttribs) - 1);
109 mType = FsObjType_Unknown;
110 const char *psz = strmBlk.GetString("ftype");
111 if (psz)
112 {
113 fHaveAttribs = true;
114 szAttribs[0] = *psz;
115 switch (*psz)
116 {
117 case '-': mType = FsObjType_File; break;
118 case 'd': mType = FsObjType_Directory; break;
119 case 'l': mType = FsObjType_Symlink; break;
120 case 'c': mType = FsObjType_DevChar; break;
121 case 'b': mType = FsObjType_DevBlock; break;
122 case 'f': mType = FsObjType_Fifo; break;
123 case 's': mType = FsObjType_Socket; break;
124 case 'w': mType = FsObjType_WhiteOut; break;
125 default:
126 AssertMsgFailed(("%s\n", psz));
127 szAttribs[0] = '?';
128 fHaveAttribs = false;
129 break;
130 }
131 }
132 psz = strmBlk.GetString("owner_mask");
133 if ( psz
134 && (psz[0] == '-' || psz[0] == 'r')
135 && (psz[1] == '-' || psz[1] == 'w')
136 && (psz[2] == '-' || psz[2] == 'x'))
137 {
138 szAttribs[1] = psz[0];
139 szAttribs[2] = psz[1];
140 szAttribs[3] = psz[2];
141 fHaveAttribs = true;
142 }
143 psz = strmBlk.GetString("group_mask");
144 if ( psz
145 && (psz[0] == '-' || psz[0] == 'r')
146 && (psz[1] == '-' || psz[1] == 'w')
147 && (psz[2] == '-' || psz[2] == 'x'))
148 {
149 szAttribs[4] = psz[0];
150 szAttribs[5] = psz[1];
151 szAttribs[6] = psz[2];
152 fHaveAttribs = true;
153 }
154 psz = strmBlk.GetString("other_mask");
155 if ( psz
156 && (psz[0] == '-' || psz[0] == 'r')
157 && (psz[1] == '-' || psz[1] == 'w')
158 && (psz[2] == '-' || psz[2] == 'x'))
159 {
160 szAttribs[7] = psz[0];
161 szAttribs[8] = psz[1];
162 szAttribs[9] = psz[2];
163 fHaveAttribs = true;
164 }
165 szAttribs[10] = ' '; /* Reserve three chars for sticky bits. */
166 szAttribs[11] = ' ';
167 szAttribs[12] = ' ';
168 szAttribs[13] = ' '; /* Separator. */
169 psz = strmBlk.GetString("dos_mask");
170 if ( psz
171 && (psz[ 0] == '-' || psz[ 0] == 'R')
172 && (psz[ 1] == '-' || psz[ 1] == 'H')
173 && (psz[ 2] == '-' || psz[ 2] == 'S')
174 && (psz[ 3] == '-' || psz[ 3] == 'D')
175 && (psz[ 4] == '-' || psz[ 4] == 'A')
176 && (psz[ 5] == '-' || psz[ 5] == 'd')
177 && (psz[ 6] == '-' || psz[ 6] == 'N')
178 && (psz[ 7] == '-' || psz[ 7] == 'T')
179 && (psz[ 8] == '-' || psz[ 8] == 'P')
180 && (psz[ 9] == '-' || psz[ 9] == 'J')
181 && (psz[10] == '-' || psz[10] == 'C')
182 && (psz[11] == '-' || psz[11] == 'O')
183 && (psz[12] == '-' || psz[12] == 'I')
184 && (psz[13] == '-' || psz[13] == 'E'))
185 {
186 memcpy(&szAttribs[14], psz, 14);
187 fHaveAttribs = true;
188 }
189 szAttribs[28] = '\0';
190 if (fHaveAttribs)
191 mFileAttrs = szAttribs;
192
193 /* Object size. */
194 int rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
195 ASSERT_GUEST_RC_RETURN(rc, rc);
196 strmBlk.GetInt64Ex("alloc", &mAllocatedSize);
197
198 /* INode number and device. */
199 psz = strmBlk.GetString("node_id");
200 if (!psz)
201 psz = strmBlk.GetString("cnode_id"); /* copy & past error fixed in 6.0 RC1 */
202 if (psz)
203 mNodeID = RTStrToInt64(psz);
204 mNodeIDDevice = strmBlk.GetUInt32("inode_dev"); /* (Produced by GAs prior to 6.0 RC1.) */
205
206 if (fLong)
207 {
208 /* Dates. */
209 mAccessTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_atime");
210 mBirthTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_birthtime");
211 mChangeTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_ctime");
212 mModificationTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_mtime");
213
214 /* Owner & group. */
215 mUID = strmBlk.GetInt32("uid");
216 psz = strmBlk.GetString("username");
217 if (psz)
218 mUserName = psz;
219 mGID = strmBlk.GetInt32("gid");
220 psz = strmBlk.GetString("groupname");
221 if (psz)
222 mGroupName = psz;
223
224 /* Misc attributes: */
225 mNumHardLinks = strmBlk.GetUInt32("hlinks", 1);
226 mDeviceNumber = strmBlk.GetUInt32("st_rdev");
227 mGenerationID = strmBlk.GetUInt32("st_gen");
228 mUserFlags = strmBlk.GetUInt32("st_flags");
229
230 /** @todo ACL */
231 }
232
233 LogFlowFuncLeave();
234 return VINF_SUCCESS;
235}
236
237int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
238{
239 /* Should be identical output. */
240 return GuestFsObjData::FromLs(strmBlk, true /*fLong*/);
241}
242
243int GuestFsObjData::FromMkTemp(const GuestProcessStreamBlock &strmBlk)
244{
245 LogFlowFunc(("\n"));
246
247#ifdef DEBUG
248 strmBlk.DumpToLog();
249#endif
250 /* Object name. */
251 mName = strmBlk.GetString("name");
252 ASSERT_GUEST_RETURN(mName.isNotEmpty(), VERR_NOT_FOUND);
253
254 /* Assign the stream block's rc. */
255 int rc = strmBlk.GetRc();
256
257 LogFlowFuncLeaveRC(rc);
258 return rc;
259}
260
261
262
263/**
264 * Returns the IPRT-compatible file mode.
265 * Note: Only handling RTFS_TYPE_ flags are implemented for now.
266 *
267 * @return IPRT file mode.
268 */
269RTFMODE GuestFsObjData::GetFileMode(void) const
270{
271 RTFMODE fMode = 0;
272
273 switch (mType)
274 {
275 case FsObjType_Directory:
276 fMode |= RTFS_TYPE_DIRECTORY;
277 break;
278
279 case FsObjType_File:
280 fMode |= RTFS_TYPE_FILE;
281 break;
282
283 case FsObjType_Symlink:
284 fMode |= RTFS_TYPE_SYMLINK;
285 break;
286
287 default:
288 break;
289 }
290
291 /** @todo Implement more stuff. */
292
293 return fMode;
294}
295
296///////////////////////////////////////////////////////////////////////////////
297
298/** @todo *NOT* thread safe yet! */
299/** @todo Add exception handling for STL stuff! */
300
301GuestProcessStreamBlock::GuestProcessStreamBlock(void)
302{
303
304}
305
306/*
307GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
308{
309 for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
310 it != otherBlock.end(); ++it)
311 {
312 mPairs[it->first] = new
313 if (it->second.pszValue)
314 {
315 RTMemFree(it->second.pszValue);
316 it->second.pszValue = NULL;
317 }
318 }
319}*/
320
321GuestProcessStreamBlock::~GuestProcessStreamBlock()
322{
323 Clear();
324}
325
326/**
327 * Destroys the currently stored stream pairs.
328 *
329 * @return IPRT status code.
330 */
331void GuestProcessStreamBlock::Clear(void)
332{
333 mPairs.clear();
334}
335
336#ifdef DEBUG
337void GuestProcessStreamBlock::DumpToLog(void) const
338{
339 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
340 this, mPairs.size()));
341
342 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
343 it != mPairs.end(); ++it)
344 {
345 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
346 }
347}
348#endif
349
350/**
351 * Returns a 64-bit signed integer of a specified key.
352 *
353 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
354 * @param pszKey Name of key to get the value for.
355 * @param piVal Pointer to value to return.
356 */
357int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
358{
359 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
360 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
361 const char *pszValue = GetString(pszKey);
362 if (pszValue)
363 {
364 *piVal = RTStrToInt64(pszValue);
365 return VINF_SUCCESS;
366 }
367 return VERR_NOT_FOUND;
368}
369
370/**
371 * Returns a 64-bit integer of a specified key.
372 *
373 * @return int64_t Value to return, 0 if not found / on failure.
374 * @param pszKey Name of key to get the value for.
375 */
376int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
377{
378 int64_t iVal;
379 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
380 return iVal;
381 return 0;
382}
383
384/**
385 * Returns the current number of stream pairs.
386 *
387 * @return uint32_t Current number of stream pairs.
388 */
389size_t GuestProcessStreamBlock::GetCount(void) const
390{
391 return mPairs.size();
392}
393
394/**
395 * Gets the return code (name = "rc") of this stream block.
396 *
397 * @return IPRT status code.
398 */
399int GuestProcessStreamBlock::GetRc(void) const
400{
401 const char *pszValue = GetString("rc");
402 if (pszValue)
403 {
404 return RTStrToInt16(pszValue);
405 }
406 return VERR_NOT_FOUND;
407}
408
409/**
410 * Returns a string value of a specified key.
411 *
412 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
413 * @param pszKey Name of key to get the value for.
414 */
415const char *GuestProcessStreamBlock::GetString(const char *pszKey) const
416{
417 AssertPtrReturn(pszKey, NULL);
418
419 try
420 {
421 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey)); /** @todo r=bird: this string conversion is excellent performance wise... */
422 if (itPairs != mPairs.end())
423 return itPairs->second.mValue.c_str();
424 }
425 catch (const std::exception &ex)
426 {
427 NOREF(ex);
428 }
429 return NULL;
430}
431
432/**
433 * Returns a 32-bit unsigned integer of a specified key.
434 *
435 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
436 * @param pszKey Name of key to get the value for.
437 * @param puVal Pointer to value to return.
438 */
439int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
440{
441 const char *pszValue = GetString(pszKey);
442 if (pszValue)
443 {
444 *puVal = RTStrToUInt32(pszValue);
445 return VINF_SUCCESS;
446 }
447 return VERR_NOT_FOUND;
448}
449
450/**
451 * Returns a 32-bit signed integer of a specified key.
452 *
453 * @returns 32-bit signed value
454 * @param pszKey Name of key to get the value for.
455 * @param iDefault The default to return on error if not found.
456 */
457int32_t GuestProcessStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const
458{
459 const char *pszValue = GetString(pszKey);
460 if (pszValue)
461 {
462 int32_t iRet;
463 int rc = RTStrToInt32Full(pszValue, 0, &iRet);
464 if (RT_SUCCESS(rc))
465 return iRet;
466 ASSERT_GUEST_MSG_FAILED(("%s=%s\n", pszKey, pszValue));
467 }
468 return iDefault;
469}
470
471/**
472 * Returns a 32-bit unsigned integer of a specified key.
473 *
474 * @return uint32_t Value to return, 0 if not found / on failure.
475 * @param pszKey Name of key to get the value for.
476 * @param uDefault The default value to return.
477 */
478uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const
479{
480 uint32_t uVal;
481 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
482 return uVal;
483 return uDefault;
484}
485
486/**
487 * Sets a value to a key or deletes a key by setting a NULL value.
488 *
489 * @return IPRT status code.
490 * @param pszKey Key name to process.
491 * @param pszValue Value to set. Set NULL for deleting the key.
492 */
493int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
494{
495 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
496
497 int rc = VINF_SUCCESS;
498 try
499 {
500 Utf8Str Utf8Key(pszKey);
501
502 /* Take a shortcut and prevent crashes on some funny versions
503 * of STL if map is empty initially. */
504 if (!mPairs.empty())
505 {
506 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
507 if (it != mPairs.end())
508 mPairs.erase(it);
509 }
510
511 if (pszValue)
512 {
513 GuestProcessStreamValue val(pszValue);
514 mPairs[Utf8Key] = val;
515 }
516 }
517 catch (const std::exception &ex)
518 {
519 NOREF(ex);
520 }
521 return rc;
522}
523
524///////////////////////////////////////////////////////////////////////////////
525
526GuestProcessStream::GuestProcessStream(void)
527 : m_cbAllocated(0),
528 m_cbUsed(0),
529 m_offBuffer(0),
530 m_pbBuffer(NULL)
531{
532
533}
534
535GuestProcessStream::~GuestProcessStream(void)
536{
537 Destroy();
538}
539
540/**
541 * Adds data to the internal parser buffer. Useful if there
542 * are multiple rounds of adding data needed.
543 *
544 * @return IPRT status code.
545 * @param pbData Pointer to data to add.
546 * @param cbData Size (in bytes) of data to add.
547 */
548int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
549{
550 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
551 AssertReturn(cbData, VERR_INVALID_PARAMETER);
552
553 int rc = VINF_SUCCESS;
554
555 /* Rewind the buffer if it's empty. */
556 size_t cbInBuf = m_cbUsed - m_offBuffer;
557 bool const fAddToSet = cbInBuf == 0;
558 if (fAddToSet)
559 m_cbUsed = m_offBuffer = 0;
560
561 /* Try and see if we can simply append the data. */
562 if (cbData + m_cbUsed <= m_cbAllocated)
563 {
564 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
565 m_cbUsed += cbData;
566 }
567 else
568 {
569 /* Move any buffered data to the front. */
570 cbInBuf = m_cbUsed - m_offBuffer;
571 if (cbInBuf == 0)
572 m_cbUsed = m_offBuffer = 0;
573 else if (m_offBuffer) /* Do we have something to move? */
574 {
575 memmove(m_pbBuffer, &m_pbBuffer[m_offBuffer], cbInBuf);
576 m_cbUsed = cbInBuf;
577 m_offBuffer = 0;
578 }
579
580 /* Do we need to grow the buffer? */
581 if (cbData + m_cbUsed > m_cbAllocated)
582 {
583/** @todo Put an upper limit on the allocation? */
584 size_t cbAlloc = m_cbUsed + cbData;
585 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
586 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
587 if (pvNew)
588 {
589 m_pbBuffer = (uint8_t *)pvNew;
590 m_cbAllocated = cbAlloc;
591 }
592 else
593 rc = VERR_NO_MEMORY;
594 }
595
596 /* Finally, copy the data. */
597 if (RT_SUCCESS(rc))
598 {
599 if (cbData + m_cbUsed <= m_cbAllocated)
600 {
601 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
602 m_cbUsed += cbData;
603 }
604 else
605 rc = VERR_BUFFER_OVERFLOW;
606 }
607 }
608
609 return rc;
610}
611
612/**
613 * Destroys the internal data buffer.
614 */
615void GuestProcessStream::Destroy(void)
616{
617 if (m_pbBuffer)
618 {
619 RTMemFree(m_pbBuffer);
620 m_pbBuffer = NULL;
621 }
622
623 m_cbAllocated = 0;
624 m_cbUsed = 0;
625 m_offBuffer = 0;
626}
627
628#ifdef DEBUG
629void GuestProcessStream::Dump(const char *pszFile)
630{
631 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
632 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile));
633
634 RTFILE hFile;
635 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
636 if (RT_SUCCESS(rc))
637 {
638 rc = RTFileWrite(hFile, m_pbBuffer, m_cbUsed, NULL /* pcbWritten */);
639 RTFileClose(hFile);
640 }
641}
642#endif
643
644/**
645 * Tries to parse the next upcoming pair block within the internal
646 * buffer.
647 *
648 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
649 * completely parsed already.
650 *
651 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
652 * stored in stream block) but still contains incomplete (unterminated)
653 * data.
654 *
655 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
656 * block (with zero or more pairs stored in stream block).
657 *
658 * @return IPRT status code.
659 * @param streamBlock Reference to guest stream block to fill.
660 *
661 */
662int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
663{
664 if ( !m_pbBuffer
665 || !m_cbUsed)
666 {
667 return VERR_NO_DATA;
668 }
669
670 AssertReturn(m_offBuffer <= m_cbUsed, VERR_INVALID_PARAMETER);
671 if (m_offBuffer == m_cbUsed)
672 return VERR_NO_DATA;
673
674 int rc = VINF_SUCCESS;
675
676 char *pszOff = (char*)&m_pbBuffer[m_offBuffer];
677 char *pszStart = pszOff;
678 uint32_t uDistance;
679 while (*pszStart)
680 {
681 size_t pairLen = strlen(pszStart);
682 uDistance = (pszStart - pszOff);
683 if (m_offBuffer + uDistance + pairLen + 1 >= m_cbUsed)
684 {
685 rc = VERR_MORE_DATA;
686 break;
687 }
688 else
689 {
690 char *pszSep = strchr(pszStart, '=');
691 char *pszVal = NULL;
692 if (pszSep)
693 pszVal = pszSep + 1;
694 if (!pszSep || !pszVal)
695 {
696 rc = VERR_MORE_DATA;
697 break;
698 }
699
700 /* Terminate the separator so that we can
701 * use pszStart as our key from now on. */
702 *pszSep = '\0';
703
704 rc = streamBlock.SetValue(pszStart, pszVal);
705 if (RT_FAILURE(rc))
706 return rc;
707 }
708
709 /* Next pair. */
710 pszStart += pairLen + 1;
711 }
712
713 /* If we did not do any movement but we have stuff left
714 * in our buffer just skip the current termination so that
715 * we can try next time. */
716 uDistance = (pszStart - pszOff);
717 if ( !uDistance
718 && *pszStart == '\0'
719 && m_offBuffer < m_cbUsed)
720 {
721 uDistance++;
722 }
723 m_offBuffer += uDistance;
724
725 return rc;
726}
727
728GuestBase::GuestBase(void)
729 : mConsole(NULL)
730 , mNextContextID(RTRandU32() % VBOX_GUESTCTRL_MAX_CONTEXTS)
731{
732}
733
734GuestBase::~GuestBase(void)
735{
736}
737
738int GuestBase::baseInit(void)
739{
740 int rc = RTCritSectInit(&mWaitEventCritSect);
741
742 LogFlowFuncLeaveRC(rc);
743 return rc;
744}
745
746void GuestBase::baseUninit(void)
747{
748 LogFlowThisFuncEnter();
749
750 int rc2 = RTCritSectDelete(&mWaitEventCritSect);
751 AssertRC(rc2);
752
753 LogFlowFuncLeaveRC(rc2);
754 /* No return value. */
755}
756
757int GuestBase::cancelWaitEvents(void)
758{
759 LogFlowThisFuncEnter();
760
761 int rc = RTCritSectEnter(&mWaitEventCritSect);
762 if (RT_SUCCESS(rc))
763 {
764 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
765 while (itEventGroups != mWaitEventGroups.end())
766 {
767 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
768 while (itEvents != itEventGroups->second.end())
769 {
770 GuestWaitEvent *pEvent = itEvents->second;
771 AssertPtr(pEvent);
772
773 /*
774 * Just cancel the event, but don't remove it from the
775 * wait events map. Don't delete it though, this (hopefully)
776 * is done by the caller using unregisterWaitEvent().
777 */
778 int rc2 = pEvent->Cancel();
779 AssertRC(rc2);
780
781 ++itEvents;
782 }
783
784 ++itEventGroups;
785 }
786
787 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
788 if (RT_SUCCESS(rc))
789 rc = rc2;
790 }
791
792 LogFlowFuncLeaveRC(rc);
793 return rc;
794}
795
796/**
797 * Handles generic messages not bound to a specific object type.
798 *
799 * @return VBox status code. VERR_NOT_FOUND if no handler has been found or VERR_NOT_SUPPORTED
800 * if this class does not support the specified callback.
801 * @param pCtxCb Host callback context.
802 * @param pSvcCb Service callback data.
803 */
804int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
805{
806 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
807
808 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
809 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
810
811 int vrc;
812
813 try
814 {
815 Log2Func(("uFunc=%RU32, cParms=%RU32\n", pCtxCb->uFunction, pSvcCb->mParms));
816
817 switch (pCtxCb->uFunction)
818 {
819 case GUEST_MSG_PROGRESS_UPDATE:
820 vrc = VINF_SUCCESS;
821 break;
822
823 case GUEST_MSG_REPLY:
824 {
825 if (pSvcCb->mParms >= 4)
826 {
827 int idx = 1; /* Current parameter index. */
828 CALLBACKDATA_MSG_REPLY dataCb;
829 /* pSvcCb->mpaParms[0] always contains the context ID. */
830 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
831 AssertRCReturn(vrc, vrc);
832 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
833 AssertRCReturn(vrc, vrc);
834 vrc = HGCMSvcGetPv(&pSvcCb->mpaParms[idx++], &dataCb.pvPayload, &dataCb.cbPayload);
835 AssertRCReturn(vrc, vrc);
836
837 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload); /* This bugger throws int. */
838 vrc = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
839 }
840 else
841 vrc = VERR_INVALID_PARAMETER;
842 break;
843 }
844
845 default:
846 vrc = VERR_NOT_SUPPORTED;
847 break;
848 }
849 }
850 catch (std::bad_alloc &)
851 {
852 vrc = VERR_NO_MEMORY;
853 }
854 catch (int rc)
855 {
856 vrc = rc;
857 }
858
859 LogFlowFuncLeaveRC(vrc);
860 return vrc;
861}
862
863int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
864{
865 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
866
867 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
868 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
869 return VERR_INVALID_PARAMETER;
870
871 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
872 if (uCount >= VBOX_GUESTCTRL_MAX_CONTEXTS)
873 uCount = 0;
874
875 uint32_t uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
876
877 *puContextID = uNewContextID;
878
879#if 0
880 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
881 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
882#endif
883 return VINF_SUCCESS;
884}
885
886/**
887 * Registers (creates) a new wait event based on a given session and object ID.
888 *
889 * From those IDs an unique context ID (CID) will be built, which only can be
890 * around once at a time.
891 *
892 * @returns IPRT status code.
893 * @retval VERR_ALREADY_EXISTS if an event with the given session and object ID
894 * already has been registered. r=bird: Incorrect, see explanation in
895 * registerWaitEventEx().
896 *
897 * @param uSessionID Session ID to register wait event for.
898 * @param uObjectID Object ID to register wait event for.
899 * @param ppEvent Pointer to registered (created) wait event on success.
900 * Must be destroyed with unregisterWaitEvent().
901 */
902int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID, GuestWaitEvent **ppEvent)
903{
904 GuestEventTypes eventTypesEmpty;
905 return registerWaitEventEx(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
906}
907
908/**
909 * Creates and registers a new wait event object that waits on a set of events
910 * related to a given object within the session.
911 *
912 * From the session ID and object ID a one-time unique context ID (CID) is built
913 * for this wait object. Normally the CID is then passed to the guest along
914 * with a request, and the guest passed the CID back with the reply. The
915 * handler for the reply then emits a signal on the event type associated with
916 * the reply, which includes signalling the object returned by this method and
917 * the waking up the thread waiting on it.
918 *
919 * @returns VBox status code.
920 * @retval VERR_ALREADY_EXISTS if an event with the given session and object ID
921 * already has been registered. r=bird: No, this isn't when this is
922 * returned, it is returned when generateContextID() generates a
923 * duplicate. The duplicate being in the count part (bits 15:0) of the
924 * session ID. So, VERR_DUPLICATE would be more appropraite.
925 *
926 * @param uSessionID Session ID to register wait event for.
927 * @param uObjectID Object ID to register wait event for.
928 * @param lstEvents List of events to register the wait event for.
929 * @param ppEvent Pointer to registered (created) wait event on success.
930 * Must be destroyed with unregisterWaitEvent().
931 */
932int GuestBase::registerWaitEventEx(uint32_t uSessionID, uint32_t uObjectID, const GuestEventTypes &lstEvents,
933 GuestWaitEvent **ppEvent)
934{
935 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
936
937 uint32_t idContext;
938 int rc = generateContextID(uSessionID, uObjectID, &idContext);
939 AssertRCReturn(rc, rc);
940
941#if 1 /** @todo r=bird: Incorrect exception and memory handling, no strategy for dealing with duplicate IDs: */
942 rc = RTCritSectEnter(&mWaitEventCritSect);
943 if (RT_SUCCESS(rc))
944 {
945 try
946 {
947 GuestWaitEvent *pEvent = new GuestWaitEvent(idContext, lstEvents);
948 AssertPtr(pEvent);
949
950 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, idContext));
951
952 /* Insert event into matching event group. This is for faster per-group
953 * lookup of all events later. */
954 for (GuestEventTypes::const_iterator itEvents = lstEvents.begin();
955 itEvents != lstEvents.end(); ++itEvents)
956 {
957 /* Check if the event group already has an event with the same
958 * context ID in it (collision). */
959 GuestWaitEvents eventGroup = mWaitEventGroups[(*itEvents)]; /** @todo r=bird: Why copy it? */
960 if (eventGroup.find(idContext) == eventGroup.end())
961 {
962 /* No, insert. */
963 mWaitEventGroups[(*itEvents)].insert(std::pair<uint32_t, GuestWaitEvent *>(idContext, pEvent));
964 }
965 else
966 {
967 rc = VERR_ALREADY_EXISTS;
968 break;
969 }
970 }
971
972 if (RT_SUCCESS(rc))
973 {
974 /* Register event in regular event list. */
975 if (mWaitEvents.find(idContext) == mWaitEvents.end())
976 {
977 mWaitEvents[idContext] = pEvent;
978 }
979 else
980 rc = VERR_ALREADY_EXISTS;
981 }
982
983 if (RT_SUCCESS(rc))
984 *ppEvent = pEvent;
985 }
986 catch(std::bad_alloc &)
987 {
988 rc = VERR_NO_MEMORY;
989 }
990
991 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
992 if (RT_SUCCESS(rc))
993 rc = rc2;
994 }
995 return rc;
996
997#else /** @todo r=bird: Version with proper exception handling, no leaks and limited duplicate CID handling: */
998
999 GuestWaitEvent *pEvent = new GuestWaitEvent(idContext, lstEvents);
1000 AssertReturn(pEvent, VERR_NO_MEMORY);
1001 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, idContext));
1002
1003 rc = RTCritSectEnter(&mWaitEventCritSect);
1004 if (RT_SUCCESS(rc))
1005 {
1006 /*
1007 * Check that we don't have any context ID collisions (should be very unlikely).
1008 *
1009 * The ASSUMPTION here is that mWaitEvents has all the same events as
1010 * mWaitEventGroups, so it suffices to check one of the two.
1011 */
1012 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1013 {
1014 uint32_t cTries = 0;
1015 do
1016 {
1017 rc = generateContextID(uSessionID, uObjectID, &idContext);
1018 AssertRCBreak(rc);
1019 Log(("Duplicate! Trying a different context ID: %#x\n", idContext));
1020 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1021 rc = VERR_ALREADY_EXISTS;
1022 } while (RT_FAILURE_NP(rc) && cTries++ < 10);
1023 }
1024 if (RT_SUCCESS(rc))
1025 {
1026 /*
1027 * Insert event into matching event group. This is for faster per-group lookup of all events later.
1028 */
1029 uint32_t cInserts = 0;
1030 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1031 {
1032 GuestWaitEvents &eventGroup = mWaitEventGroups[*ItType];
1033 if (eventGroup.find(idContext) == eventGroup.end())
1034 {
1035 try
1036 {
1037 eventGroup.insert(std::pair<uint32_t, GuestWaitEvent *>(idContext, pEvent));
1038 cInserts++;
1039 }
1040 catch (std::bad_alloc &)
1041 {
1042 while (ItType != lstEvents.begin())
1043 {
1044 --ItType;
1045 mWaitEventGroups[*ItType].erase(idContext);
1046 }
1047 rc = VERR_NO_MEMORY;
1048 break;
1049 }
1050 }
1051 else
1052 Assert(cInserts > 0); /* else: lstEvents has duplicate entries. */
1053 }
1054 if (RT_SUCCESS(rc))
1055 {
1056 Assert(cInserts > 0);
1057 NOREF(cInserts);
1058
1059 /*
1060 * Register event in the regular event list.
1061 */
1062 try
1063 {
1064 mWaitEvents[idContext] = pEvent;
1065 }
1066 catch (std::bad_alloc &)
1067 {
1068 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1069 mWaitEventGroups[*ItType].erase(idContext);
1070 rc = VERR_NO_MEMORY;
1071 }
1072 }
1073 }
1074
1075 RTCritSectLeave(&mWaitEventCritSect);
1076 }
1077 if (RT_SUCCESS(rc))
1078 return rc;
1079
1080 delete pEvent;
1081 return rc;
1082#endif
1083}
1084
1085int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
1086{
1087 int rc = RTCritSectEnter(&mWaitEventCritSect);
1088#ifdef DEBUG
1089 uint32_t cEvents = 0;
1090#endif
1091 if (RT_SUCCESS(rc))
1092 {
1093 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
1094 if (itGroup != mWaitEventGroups.end())
1095 {
1096#if 1 /** @todo r=bird: consider the other variant. */
1097 GuestWaitEvents::iterator itEvents = itGroup->second.begin();
1098 while (itEvents != itGroup->second.end())
1099 {
1100#ifdef DEBUG
1101 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1102 itEvents->second, aType, itEvents->first,
1103 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(itEvents->first),
1104 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(itEvents->first),
1105 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(itEvents->first)));
1106#endif
1107 ComPtr<IEvent> pThisEvent = aEvent; /** @todo r=bird: This means addref/release for each iteration. Isn't that silly? */
1108 Assert(!pThisEvent.isNull());
1109 int rc2 = itEvents->second->SignalExternal(aEvent);
1110 if (RT_SUCCESS(rc))
1111 rc = rc2; /** @todo r=bird: This doesn't make much sense since it will only fail if not
1112 * properly initialized or major memory corruption. And if it's broken, why
1113 * don't you just remove it instead of leaving it in the group??? It would
1114 * make life so much easier here as you could just change the while condition
1115 * to while ((itEvents = itGroup->second.begin() != itGroup->second.end())
1116 * and skip all this two step removal below. I'll put this in a #if 0 and show what I mean... */
1117
1118 if (RT_SUCCESS(rc2))
1119 {
1120 /** @todo r=bird: I don't follow the logic here. Why don't you just remove
1121 * it from all groups, including this one? You just have move the */
1122
1123 /* Remove the event from all other event groups (except the
1124 * original one!) because it was signalled. */
1125 AssertPtr(itEvents->second);
1126 const GuestEventTypes evTypes = itEvents->second->Types();
1127 for (GuestEventTypes::const_iterator itType = evTypes.begin();
1128 itType != evTypes.end(); ++itType)
1129 {
1130 if ((*itType) != aType) /* Only remove all other groups. */
1131 {
1132 /* Get current event group. */
1133 GuestEventGroup::iterator evGroup = mWaitEventGroups.find((*itType));
1134 Assert(evGroup != mWaitEventGroups.end());
1135
1136 /* Lookup event in event group. */
1137 GuestWaitEvents::iterator evEvent = evGroup->second.find(itEvents->first /* Context ID */);
1138 Assert(evEvent != evGroup->second.end());
1139
1140 LogFlowThisFunc(("Removing event=%p (type %ld)\n", evEvent->second, (*itType)));
1141 evGroup->second.erase(evEvent);
1142
1143 LogFlowThisFunc(("%zu events for type=%ld left\n",
1144 evGroup->second.size(), aType));
1145 }
1146 }
1147
1148 /* Remove the event from the passed-in event group. */
1149 GuestWaitEvents::iterator itEventsNext = itEvents;
1150 ++itEventsNext;
1151 itGroup->second.erase(itEvents);
1152 itEvents = itEventsNext;
1153 }
1154 else
1155 ++itEvents;
1156#ifdef DEBUG
1157 cEvents++;
1158#endif
1159 }
1160#else
1161 /* Signal all events in the group, leaving the group empty afterwards. */
1162 GuestWaitEvents::iterator ItWaitEvt;
1163 while ((ItWaitEvt = itGroup->second.begin()) != itGroup->second.end())
1164 {
1165 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %#x: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1166 ItWaitEvt->second, aType, ItWaitEvt->first, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(ItWaitEvt->first),
1167 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(ItWaitEvt->first), VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(ItWaitEvt->first)));
1168
1169 int rc2 = ItWaitEvt->second->SignalExternal(aEvent);
1170 AssertRC(rc2);
1171
1172 /* Take down the wait event object details before we erase it from this list and invalid ItGrpEvt. */
1173 const GuestEventTypes &EvtTypes = ItWaitEvt->second->Types();
1174 uint32_t idContext = ItWaitEvt->first;
1175 itGroup->second.erase(ItWaitEvt);
1176
1177 for (GuestEventTypes::const_iterator ItType = EvtTypes.begin(); ItType != EvtTypes.end(); ++ItType)
1178 {
1179 GuestEventGroup::iterator EvtTypeGrp = mWaitEventGroups.find(*ItType);
1180 if (EvtTypeGrp != mWaitEventGroups.end())
1181 {
1182 ItWaitEvt = EvtTypeGrp->second.find(idContext);
1183 if (ItWaitEvt != EvtTypeGrp->second.end())
1184 {
1185 LogFlowThisFunc(("Removing event %p (CID %#x) from type %d group\n", ItWaitEvt->second, idContext, *ItType));
1186 EvtTypeGrp->second.erase(ItWaitEvt);
1187 LogFlowThisFunc(("%zu events left for type %d\n", EvtTypeGrp->second.size(), *ItType));
1188 Assert(EvtTypeGrp->second.find(idContext) == EvtTypeGrp->second.end()); /* no duplicates */
1189 }
1190 }
1191 }
1192 }
1193#endif
1194 }
1195
1196 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1197 if (RT_SUCCESS(rc))
1198 rc = rc2;
1199 }
1200
1201#ifdef DEBUG
1202 LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
1203#endif
1204 return rc;
1205}
1206
1207int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1208 int rcGuest, const GuestWaitEventPayload *pPayload)
1209{
1210 if (RT_SUCCESS(rcGuest))
1211 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
1212 0 /* Guest rc */, pPayload);
1213
1214 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
1215 rcGuest, pPayload);
1216}
1217
1218int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1219 int rc, int rcGuest,
1220 const GuestWaitEventPayload *pPayload)
1221{
1222 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1223 /* pPayload is optional. */
1224
1225 int rc2 = RTCritSectEnter(&mWaitEventCritSect);
1226 if (RT_SUCCESS(rc2))
1227 {
1228 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1229 if (itEvent != mWaitEvents.end())
1230 {
1231 LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, rcGuest=%Rrc, pPayload=%p) ...\n",
1232 itEvent->second, itEvent->first, rc, rcGuest, pPayload));
1233 GuestWaitEvent *pEvent = itEvent->second;
1234 AssertPtr(pEvent);
1235 rc2 = pEvent->SignalInternal(rc, rcGuest, pPayload);
1236 }
1237 else
1238 rc2 = VERR_NOT_FOUND;
1239
1240 int rc3 = RTCritSectLeave(&mWaitEventCritSect);
1241 if (RT_SUCCESS(rc2))
1242 rc2 = rc3;
1243 }
1244
1245 return rc2;
1246}
1247
1248/**
1249 * Unregisters (deletes) a wait event.
1250 *
1251 * After successful unregistration the event will not be valid anymore.
1252 *
1253 * @returns IPRT status code.
1254 * @param pWaitEvt Wait event to unregister (delete).
1255 */
1256int GuestBase::unregisterWaitEvent(GuestWaitEvent *pWaitEvt)
1257{
1258 if (!pWaitEvt) /* Nothing to unregister. */
1259 return VINF_SUCCESS;
1260
1261 int rc = RTCritSectEnter(&mWaitEventCritSect);
1262 if (RT_SUCCESS(rc))
1263 {
1264 LogFlowThisFunc(("pWaitEvt=%p\n", pWaitEvt));
1265
1266/** @todo r=bird: One way of optimizing this would be to use the pointer
1267 * instead of the context ID as index into the groups, i.e. revert the value
1268 * pair for the GuestWaitEvents type.
1269 *
1270 * An even more efficent way, would be to not use sexy std::xxx containers for
1271 * the types, but iprt/list.h, as that would just be a RTListNodeRemove call for
1272 * each type w/o needing to iterate much at all. I.e. add a struct {
1273 * RTLISTNODE, GuestWaitEvent *pSelf} array to GuestWaitEvent, and change
1274 * GuestEventGroup to std::map<VBoxEventType_T, RTListAnchorClass>
1275 * (RTListAnchorClass == RTLISTANCHOR wrapper with a constructor)).
1276 *
1277 * P.S. the try/catch is now longer needed after I changed pWaitEvt->Types() to
1278 * return a const reference rather than a copy of the type list (and it think it
1279 * is safe to assume iterators are not hitting the heap). Copy vs reference is
1280 * an easy mistake to make in C++.
1281 *
1282 * P.P.S. The mWaitEventGroups optimization is probably just a lot of extra work
1283 * with little payoff.
1284 */
1285 try
1286 {
1287 /* Remove the event from all event type groups. */
1288 const GuestEventTypes &lstTypes = pWaitEvt->Types();
1289 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1290 itType != lstTypes.end(); ++itType)
1291 {
1292 /** @todo Slow O(n) lookup. Optimize this. */
1293 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1294 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1295 {
1296 if (itCurEvent->second == pWaitEvt)
1297 {
1298 mWaitEventGroups[(*itType)].erase(itCurEvent);
1299 break;
1300 }
1301 ++itCurEvent;
1302 }
1303 }
1304
1305 /* Remove the event from the general event list as well. */
1306 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pWaitEvt->ContextID());
1307
1308 Assert(itEvent != mWaitEvents.end());
1309 Assert(itEvent->second == pWaitEvt);
1310
1311 mWaitEvents.erase(itEvent);
1312
1313 delete pWaitEvt;
1314 pWaitEvt = NULL;
1315 }
1316 catch (const std::exception &ex)
1317 {
1318 NOREF(ex);
1319 AssertFailedStmt(rc = VERR_NOT_FOUND);
1320 }
1321
1322 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1323 if (RT_SUCCESS(rc))
1324 rc = rc2;
1325 }
1326
1327 return rc;
1328}
1329
1330/**
1331 * Waits for an already registered guest wait event.
1332 *
1333 * @return IPRT status code.
1334 * @param pWaitEvt Pointer to event to wait for.
1335 * @param msTimeout Timeout (in ms) for waiting.
1336 * @param pType Event type of following IEvent.
1337 * Optional.
1338 * @param ppEvent Pointer to IEvent which got triggered
1339 * for this event. Optional.
1340 */
1341int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEventType_T *pType, IEvent **ppEvent)
1342{
1343 AssertPtrReturn(pWaitEvt, VERR_INVALID_POINTER);
1344 /* pType is optional. */
1345 /* ppEvent is optional. */
1346
1347 int vrc = pWaitEvt->Wait(msTimeout);
1348 if (RT_SUCCESS(vrc))
1349 {
1350 const ComPtr<IEvent> pThisEvent = pWaitEvt->Event();
1351 if (pThisEvent.isNotNull()) /* Having a VBoxEventType_ event is optional. */ /** @todo r=bird: misplaced comment? */
1352 {
1353 if (pType)
1354 {
1355 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
1356 if (FAILED(hr))
1357 vrc = VERR_COM_UNEXPECTED;
1358 }
1359 if ( RT_SUCCESS(vrc)
1360 && ppEvent)
1361 pThisEvent.queryInterfaceTo(ppEvent);
1362
1363 unconst(pThisEvent).setNull();
1364 }
1365 }
1366
1367 return vrc;
1368}
1369
1370/**
1371 * Converts RTFMODE to FsObjType_T.
1372 *
1373 * @return Converted FsObjType_T type.
1374 * @param fMode RTFMODE to convert.
1375 */
1376/* static */
1377FsObjType_T GuestBase::fileModeToFsObjType(RTFMODE fMode)
1378{
1379 if (RTFS_IS_FILE(fMode)) return FsObjType_File;
1380 else if (RTFS_IS_DIRECTORY(fMode)) return FsObjType_Directory;
1381 else if (RTFS_IS_SYMLINK(fMode)) return FsObjType_Symlink;
1382
1383 return FsObjType_Unknown;
1384}
1385
1386GuestObject::GuestObject(void)
1387 : mSession(NULL),
1388 mObjectID(0)
1389{
1390}
1391
1392GuestObject::~GuestObject(void)
1393{
1394}
1395
1396int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1397{
1398 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1399 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1400
1401 mConsole = pConsole;
1402 mSession = pSession;
1403 mObjectID = uObjectID;
1404
1405 return VINF_SUCCESS;
1406}
1407
1408int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1409 GuestWaitEvent **ppEvent)
1410{
1411 AssertPtr(mSession);
1412 return GuestBase::registerWaitEventEx(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1413}
1414
1415int GuestObject::sendCommand(uint32_t uFunction, uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1416{
1417#ifndef VBOX_GUESTCTRL_TEST_CASE
1418 ComObjPtr<Console> pConsole = mConsole;
1419 Assert(!pConsole.isNull());
1420
1421 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1422
1423 /* Forward the information to the VMM device. */
1424 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1425 if (pVMMDev)
1426 {
1427 /* HACK ALERT! We extend the first parameter to 64-bit and use the
1428 two topmost bits for call destination information. */
1429 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
1430 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
1431 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_SESSION;
1432
1433 /* Make the call. */
1434 LogFlowThisFunc(("uFunction=%RU32, cParms=%RU32\n", uFunction, cParms));
1435 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, cParms, paParms);
1436 if (RT_FAILURE(vrc))
1437 {
1438 /** @todo What to do here? */
1439 }
1440 }
1441#else
1442 LogFlowThisFuncEnter();
1443
1444 /* Not needed within testcases. */
1445 RT_NOREF(uFunction, cParms, paParms);
1446 int vrc = VINF_SUCCESS;
1447#endif
1448 return vrc;
1449}
1450
1451GuestWaitEventBase::GuestWaitEventBase(void)
1452 : mfAborted(false),
1453 mCID(0),
1454 mEventSem(NIL_RTSEMEVENT),
1455 mRc(VINF_SUCCESS),
1456 mGuestRc(VINF_SUCCESS)
1457{
1458}
1459
1460GuestWaitEventBase::~GuestWaitEventBase(void)
1461{
1462 if (mEventSem != NIL_RTSEMEVENT)
1463 {
1464 RTSemEventDestroy(mEventSem);
1465 mEventSem = NIL_RTSEMEVENT;
1466 }
1467}
1468
1469int GuestWaitEventBase::Init(uint32_t uCID)
1470{
1471 mCID = uCID;
1472
1473 return RTSemEventCreate(&mEventSem);
1474}
1475
1476int GuestWaitEventBase::SignalInternal(int rc, int rcGuest,
1477 const GuestWaitEventPayload *pPayload)
1478{
1479 if (ASMAtomicReadBool(&mfAborted))
1480 return VERR_CANCELLED;
1481
1482#ifdef VBOX_STRICT
1483 if (rc == VERR_GSTCTL_GUEST_ERROR)
1484 AssertMsg(RT_FAILURE(rcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", rcGuest));
1485 else
1486 AssertMsg(RT_SUCCESS(rcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", rcGuest));
1487#endif
1488
1489 int rc2;
1490 if (pPayload)
1491 rc2 = mPayload.CopyFromDeep(*pPayload);
1492 else
1493 rc2 = VINF_SUCCESS;
1494 if (RT_SUCCESS(rc2))
1495 {
1496 mRc = rc;
1497 mGuestRc = rcGuest;
1498
1499 rc2 = RTSemEventSignal(mEventSem);
1500 }
1501
1502 return rc2;
1503}
1504
1505int GuestWaitEventBase::Wait(RTMSINTERVAL msTimeout)
1506{
1507 int rc = VINF_SUCCESS;
1508
1509 if (ASMAtomicReadBool(&mfAborted))
1510 rc = VERR_CANCELLED;
1511
1512 if (RT_SUCCESS(rc))
1513 {
1514 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1515
1516 rc = RTSemEventWait(mEventSem, msTimeout ? msTimeout : RT_INDEFINITE_WAIT);
1517 if (ASMAtomicReadBool(&mfAborted))
1518 rc = VERR_CANCELLED;
1519 if (RT_SUCCESS(rc))
1520 {
1521 /* If waiting succeeded, return the overall
1522 * result code. */
1523 rc = mRc;
1524 }
1525 }
1526
1527 return rc;
1528}
1529
1530GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
1531 const GuestEventTypes &lstEvents)
1532{
1533 int rc2 = Init(uCID);
1534 AssertRC(rc2); /** @todo Throw exception here. */ /** @todo r=bird: add+use Init() instead. Will cause weird VERR_CANCELLED errors in GuestBase::signalWaitEvent. */
1535
1536 mEventTypes = lstEvents;
1537}
1538
1539GuestWaitEvent::GuestWaitEvent(uint32_t uCID)
1540{
1541 int rc2 = Init(uCID);
1542 AssertRC(rc2); /** @todo Throw exception here. */ /** @todo r=bird: add+use Init() instead. Will cause weird VERR_CANCELLED errors in GuestBase::signalWaitEvent. */
1543}
1544
1545GuestWaitEvent::~GuestWaitEvent(void)
1546{
1547
1548}
1549
1550/**
1551 * Cancels the event.
1552 */
1553int GuestWaitEvent::Cancel(void)
1554{
1555 AssertReturn(!mfAborted, VERR_CANCELLED);
1556 ASMAtomicWriteBool(&mfAborted, true);
1557
1558#ifdef DEBUG_andy
1559 LogFlowThisFunc(("Cancelling %p ...\n"));
1560#endif
1561 return RTSemEventSignal(mEventSem);
1562}
1563
1564int GuestWaitEvent::Init(uint32_t uCID)
1565{
1566 return GuestWaitEventBase::Init(uCID);
1567}
1568
1569/**
1570 * Signals the event.
1571 *
1572 * @return IPRT status code.
1573 * @param pEvent Public IEvent to associate.
1574 * Optional.
1575 */
1576int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1577{
1578 /** @todo r=bird: VERR_CANCELLED is misleading. mEventSem can only be NIL if
1579 * not successfully initialized! */
1580 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1581
1582 if (pEvent)
1583 mEvent = pEvent;
1584
1585 return RTSemEventSignal(mEventSem);
1586}
1587
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