VirtualBox

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

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

Main/Guest: Added a few codereview comments, mainly in the guest base class area. Replaced the object ID management in GuestSession with a classic allocation bitmap approach as that's much less memory intensive than 16 byte structs in a insert-only std::map and an additional map tracking free 32-bit IDs after we run out. bugref:9313

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