VirtualBox

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

Last change on this file since 71326 was 71326, checked in by vboxsync, 7 years ago

Guest Control/Main: Simply ignore callbacks which there are no waiters for (anymore).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 71326 2018-03-14 14:28:28Z 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/time.h>
39
40
41/**
42 * Extracts the timespec from a given stream block key.
43 *
44 * @return Pointer to handed-in timespec, or NULL if invalid / not found.
45 * @param strmBlk Stream block to extract timespec from.
46 * @param strKey Key to get timespec for.
47 * @param pTimeSpec Where to store the extracted timespec.
48 */
49/* static */
50PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec)
51{
52 AssertPtrReturn(pTimeSpec, NULL);
53
54 Utf8Str strTime = strmBlk.GetString(strKey.c_str());
55 if (strTime.isEmpty())
56 return NULL;
57
58 if (!RTTimeSpecFromString(pTimeSpec, strTime.c_str()))
59 return NULL;
60
61 return pTimeSpec;
62}
63
64/**
65 * Extracts the nanoseconds relative from Unix epoch for a given stream block key.
66 *
67 * @return Nanoseconds relative from Unix epoch, or 0 if invalid / not found.
68 * @param strmBlk Stream block to extract nanoseconds from.
69 * @param strKey Key to get nanoseconds for.
70 */
71/* static */
72int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey)
73{
74 RTTIMESPEC TimeSpec;
75 if (!GuestFsObjData::TimeSpecFromKey(strmBlk, strKey, &TimeSpec))
76 return 0;
77
78 return TimeSpec.i64NanosecondsRelativeToUnixEpoch;
79}
80
81/**
82 * Initializes this object data with a stream block from VBOXSERVICE_TOOL_LS.
83 *
84 * @return VBox status code.
85 * @param strmBlk Stream block to use for initialization.
86 * @param fLong Whether the stream block contains long (detailed) information or not.
87 */
88int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk, bool fLong)
89{
90 LogFlowFunc(("\n"));
91
92 int rc = VINF_SUCCESS;
93
94 try
95 {
96#ifdef DEBUG
97 strmBlk.DumpToLog();
98#endif
99 /* Object name. */
100 mName = strmBlk.GetString("name");
101 if (mName.isEmpty()) throw VERR_NOT_FOUND;
102 /* Type. */
103 Utf8Str strType(strmBlk.GetString("ftype"));
104 if (strType.equalsIgnoreCase("-"))
105 mType = FsObjType_File;
106 else if (strType.equalsIgnoreCase("d"))
107 mType = FsObjType_Directory;
108 /** @todo Add more types! */
109 else
110 mType = FsObjType_Unknown;
111 if (fLong)
112 {
113 /* Dates. */
114 mAccessTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_atime");
115 mBirthTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_birthtime");
116 mChangeTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_ctime");
117 mModificationTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_mtime");
118 }
119 /* Object size. */
120 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
121 if (RT_FAILURE(rc)) throw rc;
122 /** @todo Add complete ls info! */
123 }
124 catch (int rc2)
125 {
126 rc = rc2;
127 }
128
129 LogFlowFuncLeaveRC(rc);
130 return rc;
131}
132
133int GuestFsObjData::FromMkTemp(const GuestProcessStreamBlock &strmBlk)
134{
135 LogFlowFunc(("\n"));
136
137 int rc;
138
139 try
140 {
141#ifdef DEBUG
142 strmBlk.DumpToLog();
143#endif
144 /* Object name. */
145 mName = strmBlk.GetString("name");
146 if (mName.isEmpty()) throw VERR_NOT_FOUND;
147 /* Assign the stream block's rc. */
148 rc = strmBlk.GetRc();
149 }
150 catch (int rc2)
151 {
152 rc = rc2;
153 }
154
155 LogFlowFuncLeaveRC(rc);
156 return rc;
157}
158
159int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
160{
161 LogFlowFunc(("\n"));
162
163 int rc = VINF_SUCCESS;
164
165 try
166 {
167#ifdef DEBUG
168 strmBlk.DumpToLog();
169#endif
170 /* Node ID, optional because we don't include this
171 * in older VBoxService (< 4.2) versions. */
172 mNodeID = strmBlk.GetInt64("node_id");
173 /* Object name. */
174 mName = strmBlk.GetString("name");
175 if (mName.isEmpty()) throw VERR_NOT_FOUND;
176 /* Type. */
177 Utf8Str strType(strmBlk.GetString("ftype"));
178 if (strType.equalsIgnoreCase("-"))
179 mType = FsObjType_File;
180 else if (strType.equalsIgnoreCase("d"))
181 mType = FsObjType_Directory;
182 else /** @todo Add more types! */
183 mType = FsObjType_Unknown;
184 /* Dates. */
185 mAccessTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_atime");
186 mBirthTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_birthtime");
187 mChangeTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_ctime");
188 mModificationTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_mtime");
189 /* Object size. */
190 rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
191 if (RT_FAILURE(rc)) throw rc;
192 /** @todo Add complete stat info! */
193 }
194 catch (int rc2)
195 {
196 rc = rc2;
197 }
198
199 LogFlowFuncLeaveRC(rc);
200 return rc;
201}
202
203///////////////////////////////////////////////////////////////////////////////
204
205/** @todo *NOT* thread safe yet! */
206/** @todo Add exception handling for STL stuff! */
207
208GuestProcessStreamBlock::GuestProcessStreamBlock(void)
209{
210
211}
212
213/*
214GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
215{
216 for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
217 it != otherBlock.end(); ++it)
218 {
219 mPairs[it->first] = new
220 if (it->second.pszValue)
221 {
222 RTMemFree(it->second.pszValue);
223 it->second.pszValue = NULL;
224 }
225 }
226}*/
227
228GuestProcessStreamBlock::~GuestProcessStreamBlock()
229{
230 Clear();
231}
232
233/**
234 * Destroys the currently stored stream pairs.
235 *
236 * @return IPRT status code.
237 */
238void GuestProcessStreamBlock::Clear(void)
239{
240 mPairs.clear();
241}
242
243#ifdef DEBUG
244void GuestProcessStreamBlock::DumpToLog(void) const
245{
246 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
247 this, mPairs.size()));
248
249 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
250 it != mPairs.end(); ++it)
251 {
252 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
253 }
254}
255#endif
256
257/**
258 * Returns a 64-bit signed integer of a specified key.
259 *
260 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
261 * @param pszKey Name of key to get the value for.
262 * @param piVal Pointer to value to return.
263 */
264int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
265{
266 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
267 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
268 const char *pszValue = GetString(pszKey);
269 if (pszValue)
270 {
271 *piVal = RTStrToInt64(pszValue);
272 return VINF_SUCCESS;
273 }
274 return VERR_NOT_FOUND;
275}
276
277/**
278 * Returns a 64-bit integer of a specified key.
279 *
280 * @return int64_t Value to return, 0 if not found / on failure.
281 * @param pszKey Name of key to get the value for.
282 */
283int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
284{
285 int64_t iVal;
286 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
287 return iVal;
288 return 0;
289}
290
291/**
292 * Returns the current number of stream pairs.
293 *
294 * @return uint32_t Current number of stream pairs.
295 */
296size_t GuestProcessStreamBlock::GetCount(void) const
297{
298 return mPairs.size();
299}
300
301/**
302 * Gets the return code (name = "rc") of this stream block.
303 *
304 * @return IPRT status code.
305 */
306int GuestProcessStreamBlock::GetRc(void) const
307{
308 const char *pszValue = GetString("rc");
309 if (pszValue)
310 {
311 return RTStrToInt16(pszValue);
312 }
313 return VERR_NOT_FOUND;
314}
315
316/**
317 * Returns a string value of a specified key.
318 *
319 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
320 * @param pszKey Name of key to get the value for.
321 */
322const char* GuestProcessStreamBlock::GetString(const char *pszKey) const
323{
324 AssertPtrReturn(pszKey, NULL);
325
326 try
327 {
328 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey));
329 if (itPairs != mPairs.end())
330 return itPairs->second.mValue.c_str();
331 }
332 catch (const std::exception &ex)
333 {
334 NOREF(ex);
335 }
336 return NULL;
337}
338
339/**
340 * Returns a 32-bit unsigned integer of a specified key.
341 *
342 * @return IPRT status code. VERR_NOT_FOUND if key was not found.
343 * @param pszKey Name of key to get the value for.
344 * @param puVal Pointer to value to return.
345 */
346int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
347{
348 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
349 AssertPtrReturn(puVal, VERR_INVALID_POINTER);
350 const char *pszValue = GetString(pszKey);
351 if (pszValue)
352 {
353 *puVal = RTStrToUInt32(pszValue);
354 return VINF_SUCCESS;
355 }
356 return VERR_NOT_FOUND;
357}
358
359/**
360 * Returns a 32-bit unsigned integer of a specified key.
361 *
362 * @return uint32_t Value to return, 0 if not found / on failure.
363 * @param pszKey Name of key to get the value for.
364 */
365uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey) const
366{
367 uint32_t uVal;
368 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
369 return uVal;
370 return 0;
371}
372
373/**
374 * Sets a value to a key or deletes a key by setting a NULL value.
375 *
376 * @return IPRT status code.
377 * @param pszKey Key name to process.
378 * @param pszValue Value to set. Set NULL for deleting the key.
379 */
380int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
381{
382 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
383
384 int rc = VINF_SUCCESS;
385 try
386 {
387 Utf8Str Utf8Key(pszKey);
388
389 /* Take a shortcut and prevent crashes on some funny versions
390 * of STL if map is empty initially. */
391 if (!mPairs.empty())
392 {
393 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
394 if (it != mPairs.end())
395 mPairs.erase(it);
396 }
397
398 if (pszValue)
399 {
400 GuestProcessStreamValue val(pszValue);
401 mPairs[Utf8Key] = val;
402 }
403 }
404 catch (const std::exception &ex)
405 {
406 NOREF(ex);
407 }
408 return rc;
409}
410
411///////////////////////////////////////////////////////////////////////////////
412
413GuestProcessStream::GuestProcessStream(void)
414 : m_cbAllocated(0),
415 m_cbUsed(0),
416 m_offBuffer(0),
417 m_pbBuffer(NULL)
418{
419
420}
421
422GuestProcessStream::~GuestProcessStream(void)
423{
424 Destroy();
425}
426
427/**
428 * Adds data to the internal parser buffer. Useful if there
429 * are multiple rounds of adding data needed.
430 *
431 * @return IPRT status code.
432 * @param pbData Pointer to data to add.
433 * @param cbData Size (in bytes) of data to add.
434 */
435int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
436{
437 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
438 AssertReturn(cbData, VERR_INVALID_PARAMETER);
439
440 int rc = VINF_SUCCESS;
441
442 /* Rewind the buffer if it's empty. */
443 size_t cbInBuf = m_cbUsed - m_offBuffer;
444 bool const fAddToSet = cbInBuf == 0;
445 if (fAddToSet)
446 m_cbUsed = m_offBuffer = 0;
447
448 /* Try and see if we can simply append the data. */
449 if (cbData + m_cbUsed <= m_cbAllocated)
450 {
451 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
452 m_cbUsed += cbData;
453 }
454 else
455 {
456 /* Move any buffered data to the front. */
457 cbInBuf = m_cbUsed - m_offBuffer;
458 if (cbInBuf == 0)
459 m_cbUsed = m_offBuffer = 0;
460 else if (m_offBuffer) /* Do we have something to move? */
461 {
462 memmove(m_pbBuffer, &m_pbBuffer[m_offBuffer], cbInBuf);
463 m_cbUsed = cbInBuf;
464 m_offBuffer = 0;
465 }
466
467 /* Do we need to grow the buffer? */
468 if (cbData + m_cbUsed > m_cbAllocated)
469 {
470/** @todo Put an upper limit on the allocation? */
471 size_t cbAlloc = m_cbUsed + cbData;
472 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
473 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
474 if (pvNew)
475 {
476 m_pbBuffer = (uint8_t *)pvNew;
477 m_cbAllocated = cbAlloc;
478 }
479 else
480 rc = VERR_NO_MEMORY;
481 }
482
483 /* Finally, copy the data. */
484 if (RT_SUCCESS(rc))
485 {
486 if (cbData + m_cbUsed <= m_cbAllocated)
487 {
488 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
489 m_cbUsed += cbData;
490 }
491 else
492 rc = VERR_BUFFER_OVERFLOW;
493 }
494 }
495
496 return rc;
497}
498
499/**
500 * Destroys the internal data buffer.
501 */
502void GuestProcessStream::Destroy(void)
503{
504 if (m_pbBuffer)
505 {
506 RTMemFree(m_pbBuffer);
507 m_pbBuffer = NULL;
508 }
509
510 m_cbAllocated = 0;
511 m_cbUsed = 0;
512 m_offBuffer = 0;
513}
514
515#ifdef DEBUG
516void GuestProcessStream::Dump(const char *pszFile)
517{
518 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
519 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile));
520
521 RTFILE hFile;
522 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
523 if (RT_SUCCESS(rc))
524 {
525 rc = RTFileWrite(hFile, m_pbBuffer, m_cbUsed, NULL /* pcbWritten */);
526 RTFileClose(hFile);
527 }
528}
529#endif
530
531/**
532 * Tries to parse the next upcoming pair block within the internal
533 * buffer.
534 *
535 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
536 * completely parsed already.
537 *
538 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
539 * stored in stream block) but still contains incomplete (unterminated)
540 * data.
541 *
542 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
543 * block (with zero or more pairs stored in stream block).
544 *
545 * @return IPRT status code.
546 * @param streamBlock Reference to guest stream block to fill.
547 *
548 */
549int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
550{
551 if ( !m_pbBuffer
552 || !m_cbUsed)
553 {
554 return VERR_NO_DATA;
555 }
556
557 AssertReturn(m_offBuffer <= m_cbUsed, VERR_INVALID_PARAMETER);
558 if (m_offBuffer == m_cbUsed)
559 return VERR_NO_DATA;
560
561 int rc = VINF_SUCCESS;
562
563 char *pszOff = (char*)&m_pbBuffer[m_offBuffer];
564 char *pszStart = pszOff;
565 uint32_t uDistance;
566 while (*pszStart)
567 {
568 size_t pairLen = strlen(pszStart);
569 uDistance = (pszStart - pszOff);
570 if (m_offBuffer + uDistance + pairLen + 1 >= m_cbUsed)
571 {
572 rc = VERR_MORE_DATA;
573 break;
574 }
575 else
576 {
577 char *pszSep = strchr(pszStart, '=');
578 char *pszVal = NULL;
579 if (pszSep)
580 pszVal = pszSep + 1;
581 if (!pszSep || !pszVal)
582 {
583 rc = VERR_MORE_DATA;
584 break;
585 }
586
587 /* Terminate the separator so that we can
588 * use pszStart as our key from now on. */
589 *pszSep = '\0';
590
591 rc = streamBlock.SetValue(pszStart, pszVal);
592 if (RT_FAILURE(rc))
593 return rc;
594 }
595
596 /* Next pair. */
597 pszStart += pairLen + 1;
598 }
599
600 /* If we did not do any movement but we have stuff left
601 * in our buffer just skip the current termination so that
602 * we can try next time. */
603 uDistance = (pszStart - pszOff);
604 if ( !uDistance
605 && *pszStart == '\0'
606 && m_offBuffer < m_cbUsed)
607 {
608 uDistance++;
609 }
610 m_offBuffer += uDistance;
611
612 return rc;
613}
614
615GuestBase::GuestBase(void)
616 : mConsole(NULL),
617 mNextContextID(0)
618{
619}
620
621GuestBase::~GuestBase(void)
622{
623}
624
625int GuestBase::baseInit(void)
626{
627 int rc = RTCritSectInit(&mWaitEventCritSect);
628
629 LogFlowFuncLeaveRC(rc);
630 return rc;
631}
632
633void GuestBase::baseUninit(void)
634{
635 LogFlowThisFuncEnter();
636
637 int rc = RTCritSectDelete(&mWaitEventCritSect);
638 NOREF(rc);
639
640 LogFlowFuncLeaveRC(rc);
641 /* No return value. */
642}
643
644int GuestBase::cancelWaitEvents(void)
645{
646 LogFlowThisFuncEnter();
647
648 int rc = RTCritSectEnter(&mWaitEventCritSect);
649 if (RT_SUCCESS(rc))
650 {
651 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
652 while (itEventGroups != mWaitEventGroups.end())
653 {
654 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
655 while (itEvents != itEventGroups->second.end())
656 {
657 GuestWaitEvent *pEvent = itEvents->second;
658 AssertPtr(pEvent);
659
660 /*
661 * Just cancel the event, but don't remove it from the
662 * wait events map. Don't delete it though, this (hopefully)
663 * is done by the caller using unregisterWaitEvent().
664 */
665 int rc2 = pEvent->Cancel();
666 AssertRC(rc2);
667
668 ++itEvents;
669 }
670
671 ++itEventGroups;
672 }
673
674 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
675 if (RT_SUCCESS(rc))
676 rc = rc2;
677 }
678
679 LogFlowFuncLeaveRC(rc);
680 return rc;
681}
682
683int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
684{
685 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
686
687 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
688 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
689
690 int vrc = VINF_SUCCESS;
691
692 try
693 {
694 LogFlowFunc(("uFunc=%RU32, cParms=%RU32\n",
695 pCtxCb->uFunction, pSvcCb->mParms));
696
697 switch (pCtxCb->uFunction)
698 {
699 case GUEST_MSG_PROGRESS_UPDATE:
700 break;
701
702 case GUEST_MSG_REPLY:
703 {
704 if (pSvcCb->mParms >= 4)
705 {
706 int idx = 1; /* Current parameter index. */
707 CALLBACKDATA_MSG_REPLY dataCb;
708 /* pSvcCb->mpaParms[0] always contains the context ID. */
709 vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.uType);
710 AssertRCReturn(vrc, vrc);
711 vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.rc);
712 AssertRCReturn(vrc, vrc);
713 vrc = pSvcCb->mpaParms[idx++].getPointer(&dataCb.pvPayload, &dataCb.cbPayload);
714 AssertRCReturn(vrc, vrc);
715
716 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
717 vrc = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
718 if (vrc == VERR_NOT_FOUND)
719 vrc = VINF_SUCCESS;
720 }
721 else
722 vrc = VERR_INVALID_PARAMETER;
723 break;
724 }
725
726 default:
727 vrc = VERR_NOT_SUPPORTED;
728 break;
729 }
730 }
731 catch (std::bad_alloc)
732 {
733 vrc = VERR_NO_MEMORY;
734 }
735 catch (int rc)
736 {
737 vrc = rc;
738 }
739
740 LogFlowFuncLeaveRC(vrc);
741 return vrc;
742}
743
744int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
745{
746 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
747
748 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
749 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
750 return VERR_INVALID_PARAMETER;
751
752 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
753 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
754 uCount = 0;
755
756 uint32_t uNewContextID =
757 VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
758
759 *puContextID = uNewContextID;
760
761#if 0
762 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
763 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
764#endif
765 return VINF_SUCCESS;
766}
767
768int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
769 GuestWaitEvent **ppEvent)
770{
771 GuestEventTypes eventTypesEmpty;
772 return registerWaitEvent(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
773}
774
775/**
776 * Registers (creates) a new wait event based on a given session and object ID.
777 *
778 * From those IDs an unique context ID (CID) will be built, which only can be
779 * around once at a time.
780 *
781 * @returns IPRT status code. VERR_ALREADY_EXISTS if an event with the given session
782 * and object ID already has been registered.
783 *
784 * @param uSessionID Session ID to register wait event for.
785 * @param uObjectID Object ID to register wait event for.
786 * @param lstEvents List of events to register the wait event for.
787 * @param ppEvent Pointer to registered (created) wait event on success.
788 * Must be destroyed with unregisterWaitEvent().
789 */
790int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
791 const GuestEventTypes &lstEvents,
792 GuestWaitEvent **ppEvent)
793{
794 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
795
796 uint32_t uContextID;
797 int rc = generateContextID(uSessionID, uObjectID, &uContextID);
798 if (RT_FAILURE(rc))
799 return rc;
800
801 rc = RTCritSectEnter(&mWaitEventCritSect);
802 if (RT_SUCCESS(rc))
803 {
804 try
805 {
806 GuestWaitEvent *pEvent = new GuestWaitEvent(uContextID, lstEvents);
807 AssertPtr(pEvent);
808
809 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, uContextID));
810
811 /* Insert event into matching event group. This is for faster per-group
812 * lookup of all events later. */
813 for (GuestEventTypes::const_iterator itEvents = lstEvents.begin();
814 itEvents != lstEvents.end(); ++itEvents)
815 {
816 /* Check if the event group already has an event with the same
817 * context ID in it (collision). */
818 GuestWaitEvents eventGroup = mWaitEventGroups[(*itEvents)];
819 if (eventGroup.find(uContextID) == eventGroup.end())
820 {
821 /* No, insert. */
822 mWaitEventGroups[(*itEvents)].insert(std::pair<uint32_t, GuestWaitEvent *>(uContextID, pEvent));
823 }
824 else
825 {
826 rc = VERR_ALREADY_EXISTS;
827 break;
828 }
829 }
830
831 if (RT_SUCCESS(rc))
832 {
833 /* Register event in regular event list. */
834 if (mWaitEvents.find(uContextID) == mWaitEvents.end())
835 {
836 mWaitEvents[uContextID] = pEvent;
837 }
838 else
839 rc = VERR_ALREADY_EXISTS;
840 }
841
842 if (RT_SUCCESS(rc))
843 *ppEvent = pEvent;
844 }
845 catch(std::bad_alloc &)
846 {
847 rc = VERR_NO_MEMORY;
848 }
849
850 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
851 if (RT_SUCCESS(rc))
852 rc = rc2;
853 }
854
855 return rc;
856}
857
858int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
859{
860 int rc = RTCritSectEnter(&mWaitEventCritSect);
861#ifdef DEBUG
862 uint32_t cEvents = 0;
863#endif
864 if (RT_SUCCESS(rc))
865 {
866 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
867 if (itGroup != mWaitEventGroups.end())
868 {
869 GuestWaitEvents::iterator itEvents = itGroup->second.begin();
870 while (itEvents != itGroup->second.end())
871 {
872#ifdef DEBUG
873 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
874 itEvents->second, aType, itEvents->first,
875 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(itEvents->first),
876 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(itEvents->first),
877 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(itEvents->first)));
878#endif
879 ComPtr<IEvent> pThisEvent = aEvent;
880 Assert(!pThisEvent.isNull());
881 int rc2 = itEvents->second->SignalExternal(aEvent);
882 if (RT_SUCCESS(rc))
883 rc = rc2;
884
885 if (RT_SUCCESS(rc2))
886 {
887 /* Remove the event from all other event groups (except the
888 * original one!) because it was signalled. */
889 AssertPtr(itEvents->second);
890 const GuestEventTypes evTypes = itEvents->second->Types();
891 for (GuestEventTypes::const_iterator itType = evTypes.begin();
892 itType != evTypes.end(); ++itType)
893 {
894 if ((*itType) != aType) /* Only remove all other groups. */
895 {
896 /* Get current event group. */
897 GuestEventGroup::iterator evGroup = mWaitEventGroups.find((*itType));
898 Assert(evGroup != mWaitEventGroups.end());
899
900 /* Lookup event in event group. */
901 GuestWaitEvents::iterator evEvent = evGroup->second.find(itEvents->first /* Context ID */);
902 Assert(evEvent != evGroup->second.end());
903
904 LogFlowThisFunc(("Removing event=%p (type %ld)\n", evEvent->second, (*itType)));
905 evGroup->second.erase(evEvent);
906
907 LogFlowThisFunc(("%zu events for type=%ld left\n",
908 evGroup->second.size(), aType));
909 }
910 }
911
912 /* Remove the event from the passed-in event group. */
913 GuestWaitEvents::iterator itEventsNext = itEvents;
914 ++itEventsNext;
915 itGroup->second.erase(itEvents);
916 itEvents = itEventsNext;
917 }
918 else
919 ++itEvents;
920#ifdef DEBUG
921 cEvents++;
922#endif
923 }
924 }
925
926 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
927 if (RT_SUCCESS(rc))
928 rc = rc2;
929 }
930
931#ifdef DEBUG
932 LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
933#endif
934 return rc;
935}
936
937int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
938 int rcGuest, const GuestWaitEventPayload *pPayload)
939{
940 if (RT_SUCCESS(rcGuest))
941 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
942 0 /* Guest rc */, pPayload);
943
944 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
945 rcGuest, pPayload);
946}
947
948int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
949 int rc, int rcGuest,
950 const GuestWaitEventPayload *pPayload)
951{
952 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
953 /* pPayload is optional. */
954
955 int rc2 = RTCritSectEnter(&mWaitEventCritSect);
956 if (RT_SUCCESS(rc2))
957 {
958 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
959 if (itEvent != mWaitEvents.end())
960 {
961 LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, rcGuest=%Rrc, pPayload=%p) ...\n",
962 itEvent->second, itEvent->first, rc, rcGuest, pPayload));
963 GuestWaitEvent *pEvent = itEvent->second;
964 AssertPtr(pEvent);
965 rc2 = pEvent->SignalInternal(rc, rcGuest, pPayload);
966 }
967 else
968 rc2 = VERR_NOT_FOUND;
969
970 int rc3 = RTCritSectLeave(&mWaitEventCritSect);
971 if (RT_SUCCESS(rc2))
972 rc2 = rc3;
973 }
974
975 return rc2;
976}
977
978/**
979 * Unregisters (deletes) a wait event.
980 *
981 * After successful unregistration the event will not be valid anymore.
982 *
983 * @returns IPRT status code.
984 * @param pEvent Event to unregister (delete).
985 */
986int GuestBase::unregisterWaitEvent(GuestWaitEvent *pEvent)
987{
988 if (!pEvent) /* Nothing to unregister. */
989 return VINF_SUCCESS;
990
991 int rc = RTCritSectEnter(&mWaitEventCritSect);
992 if (RT_SUCCESS(rc))
993 {
994 LogFlowThisFunc(("pEvent=%p\n", pEvent));
995
996 try
997 {
998 /* Remove the event from all event type groups. */
999 const GuestEventTypes lstTypes = pEvent->Types();
1000 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1001 itType != lstTypes.end(); ++itType)
1002 {
1003 /** @todo Slow O(n) lookup. Optimize this. */
1004 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1005 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1006 {
1007 if (itCurEvent->second == pEvent)
1008 {
1009 mWaitEventGroups[(*itType)].erase(itCurEvent);
1010 break;
1011 }
1012 else
1013 ++itCurEvent;
1014 }
1015 }
1016
1017 /* Remove the event from the general event list as well. */
1018 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pEvent->ContextID());
1019
1020 Assert(itEvent != mWaitEvents.end());
1021 Assert(itEvent->second == pEvent);
1022
1023 mWaitEvents.erase(itEvent);
1024
1025 delete pEvent;
1026 pEvent = NULL;
1027 }
1028 catch (const std::exception &ex)
1029 {
1030 NOREF(ex);
1031 AssertFailedStmt(rc = VERR_NOT_FOUND);
1032 }
1033
1034 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1035 if (RT_SUCCESS(rc))
1036 rc = rc2;
1037 }
1038
1039 return rc;
1040}
1041
1042/**
1043 * Waits for a formerly registered guest event.
1044 *
1045 * @return IPRT status code.
1046 * @param pEvent Pointer to event to wait for.
1047 * @param uTimeoutMS Timeout (in ms) for waiting.
1048 * @param pType Event type of following IEvent.
1049 * Optional.
1050 * @param ppEvent Pointer to IEvent which got triggered
1051 * for this event. Optional.
1052 */
1053int GuestBase::waitForEvent(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1054 VBoxEventType_T *pType, IEvent **ppEvent)
1055{
1056 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1057 /* pType is optional. */
1058 /* ppEvent is optional. */
1059
1060 int vrc = pEvent->Wait(uTimeoutMS);
1061 if (RT_SUCCESS(vrc))
1062 {
1063 const ComPtr<IEvent> pThisEvent = pEvent->Event();
1064 if (!pThisEvent.isNull()) /* Having a VBoxEventType_ event is optional. */
1065 {
1066 if (pType)
1067 {
1068 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
1069 if (FAILED(hr))
1070 vrc = VERR_COM_UNEXPECTED;
1071 }
1072 if ( RT_SUCCESS(vrc)
1073 && ppEvent)
1074 pThisEvent.queryInterfaceTo(ppEvent);
1075
1076 unconst(pThisEvent).setNull();
1077 }
1078 }
1079
1080 return vrc;
1081}
1082
1083GuestObject::GuestObject(void)
1084 : mSession(NULL),
1085 mObjectID(0)
1086{
1087}
1088
1089GuestObject::~GuestObject(void)
1090{
1091}
1092
1093int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1094{
1095 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1096 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1097
1098 mConsole = pConsole;
1099 mSession = pSession;
1100 mObjectID = uObjectID;
1101
1102 return VINF_SUCCESS;
1103}
1104
1105int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1106 GuestWaitEvent **ppEvent)
1107{
1108 AssertPtr(mSession);
1109 return GuestBase::registerWaitEvent(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1110}
1111
1112int GuestObject::sendCommand(uint32_t uFunction,
1113 uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1114{
1115#ifndef VBOX_GUESTCTRL_TEST_CASE
1116 ComObjPtr<Console> pConsole = mConsole;
1117 Assert(!pConsole.isNull());
1118
1119 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1120
1121 /* Forward the information to the VMM device. */
1122 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1123 if (pVMMDev)
1124 {
1125 LogFlowThisFunc(("uFunction=%RU32, cParms=%RU32\n", uFunction, cParms));
1126 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, cParms, paParms);
1127 if (RT_FAILURE(vrc))
1128 {
1129 /** @todo What to do here? */
1130 }
1131 }
1132#else
1133 LogFlowThisFuncEnter();
1134
1135 /* Not needed within testcases. */
1136 RT_NOREF(uFunction, cParms, paParms);
1137 int vrc = VINF_SUCCESS;
1138#endif
1139 return vrc;
1140}
1141
1142GuestWaitEventBase::GuestWaitEventBase(void)
1143 : mfAborted(false),
1144 mCID(0),
1145 mEventSem(NIL_RTSEMEVENT),
1146 mRc(VINF_SUCCESS),
1147 mGuestRc(VINF_SUCCESS)
1148{
1149}
1150
1151GuestWaitEventBase::~GuestWaitEventBase(void)
1152{
1153 if (mEventSem != NIL_RTSEMEVENT)
1154 {
1155 RTSemEventDestroy(mEventSem);
1156 mEventSem = NIL_RTSEMEVENT;
1157 }
1158}
1159
1160int GuestWaitEventBase::Init(uint32_t uCID)
1161{
1162 mCID = uCID;
1163
1164 return RTSemEventCreate(&mEventSem);
1165}
1166
1167int GuestWaitEventBase::SignalInternal(int rc, int rcGuest,
1168 const GuestWaitEventPayload *pPayload)
1169{
1170 if (ASMAtomicReadBool(&mfAborted))
1171 return VERR_CANCELLED;
1172
1173#ifdef VBOX_STRICT
1174 if (rc == VERR_GSTCTL_GUEST_ERROR)
1175 AssertMsg(RT_FAILURE(rcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", rcGuest));
1176 else
1177 AssertMsg(RT_SUCCESS(rcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", rcGuest));
1178#endif
1179
1180 int rc2;
1181 if (pPayload)
1182 rc2 = mPayload.CopyFromDeep(*pPayload);
1183 else
1184 rc2 = VINF_SUCCESS;
1185 if (RT_SUCCESS(rc2))
1186 {
1187 mRc = rc;
1188 mGuestRc = rcGuest;
1189
1190 rc2 = RTSemEventSignal(mEventSem);
1191 }
1192
1193 return rc2;
1194}
1195
1196int GuestWaitEventBase::Wait(RTMSINTERVAL uTimeoutMS)
1197{
1198 int rc = VINF_SUCCESS;
1199
1200 if (ASMAtomicReadBool(&mfAborted))
1201 rc = VERR_CANCELLED;
1202
1203 if (RT_SUCCESS(rc))
1204 {
1205 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1206
1207 RTMSINTERVAL msInterval = uTimeoutMS;
1208 if (!uTimeoutMS)
1209 msInterval = RT_INDEFINITE_WAIT;
1210 rc = RTSemEventWait(mEventSem, msInterval);
1211 if (ASMAtomicReadBool(&mfAborted))
1212 rc = VERR_CANCELLED;
1213 if (RT_SUCCESS(rc))
1214 {
1215 /* If waiting succeeded, return the overall
1216 * result code. */
1217 rc = mRc;
1218 }
1219 }
1220
1221 return rc;
1222}
1223
1224GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
1225 const GuestEventTypes &lstEvents)
1226{
1227 int rc2 = Init(uCID);
1228 AssertRC(rc2); /** @todo Throw exception here. */
1229
1230 mEventTypes = lstEvents;
1231}
1232
1233GuestWaitEvent::GuestWaitEvent(uint32_t uCID)
1234{
1235 int rc2 = Init(uCID);
1236 AssertRC(rc2); /** @todo Throw exception here. */
1237}
1238
1239GuestWaitEvent::~GuestWaitEvent(void)
1240{
1241
1242}
1243
1244/**
1245 * Cancels the event.
1246 */
1247int GuestWaitEvent::Cancel(void)
1248{
1249 AssertReturn(!mfAborted, VERR_CANCELLED);
1250 ASMAtomicWriteBool(&mfAborted, true);
1251
1252#ifdef DEBUG_andy
1253 LogFlowThisFunc(("Cancelling %p ...\n"));
1254#endif
1255 return RTSemEventSignal(mEventSem);
1256}
1257
1258int GuestWaitEvent::Init(uint32_t uCID)
1259{
1260 return GuestWaitEventBase::Init(uCID);
1261}
1262
1263/**
1264 * Signals the event.
1265 *
1266 * @return IPRT status code.
1267 * @param pEvent Public IEvent to associate.
1268 * Optional.
1269 */
1270int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1271{
1272 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1273
1274 if (pEvent)
1275 mEvent = pEvent;
1276
1277 return RTSemEventSignal(mEventSem);
1278}
1279
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