VirtualBox

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

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

Guest Control: Renaming (guestRc -> rcGuest).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 71299 2018-03-12 11:30:08Z 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 >= 3)
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 int rc2 = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
718 AssertRC(rc2);
719 }
720 else
721 vrc = VERR_INVALID_PARAMETER;
722 break;
723 }
724
725 default:
726 vrc = VERR_NOT_SUPPORTED;
727 break;
728 }
729 }
730 catch (std::bad_alloc)
731 {
732 vrc = VERR_NO_MEMORY;
733 }
734 catch (int rc)
735 {
736 vrc = rc;
737 }
738
739 LogFlowFuncLeaveRC(vrc);
740 return vrc;
741}
742
743int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
744{
745 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
746
747 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
748 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
749 return VERR_INVALID_PARAMETER;
750
751 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
752 if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
753 uCount = 0;
754
755 uint32_t uNewContextID =
756 VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
757
758 *puContextID = uNewContextID;
759
760#if 0
761 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
762 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
763#endif
764 return VINF_SUCCESS;
765}
766
767int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
768 GuestWaitEvent **ppEvent)
769{
770 GuestEventTypes eventTypesEmpty;
771 return registerWaitEvent(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
772}
773
774/**
775 * Registers (creates) a new wait event based on a given session and object ID.
776 *
777 * From those IDs an unique context ID (CID) will be built, which only can be
778 * around once at a time.
779 *
780 * @returns IPRT status code. VERR_ALREADY_EXISTS if an event with the given session
781 * and object ID already has been registered.
782 *
783 * @param uSessionID Session ID to register wait event for.
784 * @param uObjectID Object ID to register wait event for.
785 * @param lstEvents List of events to register the wait event for.
786 * @param ppEvent Pointer to registered (created) wait event on success.
787 * Must be destroyed with unregisterWaitEvent().
788 */
789int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
790 const GuestEventTypes &lstEvents,
791 GuestWaitEvent **ppEvent)
792{
793 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
794
795 uint32_t uContextID;
796 int rc = generateContextID(uSessionID, uObjectID, &uContextID);
797 if (RT_FAILURE(rc))
798 return rc;
799
800 rc = RTCritSectEnter(&mWaitEventCritSect);
801 if (RT_SUCCESS(rc))
802 {
803 try
804 {
805 GuestWaitEvent *pEvent = new GuestWaitEvent(uContextID, lstEvents);
806 AssertPtr(pEvent);
807
808 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, uContextID));
809
810 /* Insert event into matching event group. This is for faster per-group
811 * lookup of all events later. */
812 for (GuestEventTypes::const_iterator itEvents = lstEvents.begin();
813 itEvents != lstEvents.end(); ++itEvents)
814 {
815 /* Check if the event group already has an event with the same
816 * context ID in it (collision). */
817 GuestWaitEvents eventGroup = mWaitEventGroups[(*itEvents)];
818 if (eventGroup.find(uContextID) == eventGroup.end())
819 {
820 /* No, insert. */
821 mWaitEventGroups[(*itEvents)].insert(std::pair<uint32_t, GuestWaitEvent *>(uContextID, pEvent));
822 }
823 else
824 {
825 rc = VERR_ALREADY_EXISTS;
826 break;
827 }
828 }
829
830 if (RT_SUCCESS(rc))
831 {
832 /* Register event in regular event list. */
833 if (mWaitEvents.find(uContextID) == mWaitEvents.end())
834 {
835 mWaitEvents[uContextID] = pEvent;
836 }
837 else
838 rc = VERR_ALREADY_EXISTS;
839 }
840
841 if (RT_SUCCESS(rc))
842 *ppEvent = pEvent;
843 }
844 catch(std::bad_alloc &)
845 {
846 rc = VERR_NO_MEMORY;
847 }
848
849 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
850 if (RT_SUCCESS(rc))
851 rc = rc2;
852 }
853
854 return rc;
855}
856
857int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
858{
859 int rc = RTCritSectEnter(&mWaitEventCritSect);
860#ifdef DEBUG
861 uint32_t cEvents = 0;
862#endif
863 if (RT_SUCCESS(rc))
864 {
865 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
866 if (itGroup != mWaitEventGroups.end())
867 {
868 GuestWaitEvents::iterator itEvents = itGroup->second.begin();
869 while (itEvents != itGroup->second.end())
870 {
871#ifdef DEBUG
872 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
873 itEvents->second, aType, itEvents->first,
874 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(itEvents->first),
875 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(itEvents->first),
876 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(itEvents->first)));
877#endif
878 ComPtr<IEvent> pThisEvent = aEvent;
879 Assert(!pThisEvent.isNull());
880 int rc2 = itEvents->second->SignalExternal(aEvent);
881 if (RT_SUCCESS(rc))
882 rc = rc2;
883
884 if (RT_SUCCESS(rc2))
885 {
886 /* Remove the event from all other event groups (except the
887 * original one!) because it was signalled. */
888 AssertPtr(itEvents->second);
889 const GuestEventTypes evTypes = itEvents->second->Types();
890 for (GuestEventTypes::const_iterator itType = evTypes.begin();
891 itType != evTypes.end(); ++itType)
892 {
893 if ((*itType) != aType) /* Only remove all other groups. */
894 {
895 /* Get current event group. */
896 GuestEventGroup::iterator evGroup = mWaitEventGroups.find((*itType));
897 Assert(evGroup != mWaitEventGroups.end());
898
899 /* Lookup event in event group. */
900 GuestWaitEvents::iterator evEvent = evGroup->second.find(itEvents->first /* Context ID */);
901 Assert(evEvent != evGroup->second.end());
902
903 LogFlowThisFunc(("Removing event=%p (type %ld)\n", evEvent->second, (*itType)));
904 evGroup->second.erase(evEvent);
905
906 LogFlowThisFunc(("%zu events for type=%ld left\n",
907 evGroup->second.size(), aType));
908 }
909 }
910
911 /* Remove the event from the passed-in event group. */
912 GuestWaitEvents::iterator itEventsNext = itEvents;
913 ++itEventsNext;
914 itGroup->second.erase(itEvents);
915 itEvents = itEventsNext;
916 }
917 else
918 ++itEvents;
919#ifdef DEBUG
920 cEvents++;
921#endif
922 }
923 }
924
925 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
926 if (RT_SUCCESS(rc))
927 rc = rc2;
928 }
929
930#ifdef DEBUG
931 LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
932#endif
933 return rc;
934}
935
936int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
937 int rcGuest, const GuestWaitEventPayload *pPayload)
938{
939 if (RT_SUCCESS(rcGuest))
940 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
941 0 /* Guest rc */, pPayload);
942
943 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
944 rcGuest, pPayload);
945}
946
947int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
948 int rc, int rcGuest,
949 const GuestWaitEventPayload *pPayload)
950{
951 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
952 /* pPayload is optional. */
953
954 int rc2 = RTCritSectEnter(&mWaitEventCritSect);
955 if (RT_SUCCESS(rc2))
956 {
957 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
958 if (itEvent != mWaitEvents.end())
959 {
960 LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, rcGuest=%Rrc, pPayload=%p) ...\n",
961 itEvent->second, itEvent->first, rc, rcGuest, pPayload));
962 GuestWaitEvent *pEvent = itEvent->second;
963 AssertPtr(pEvent);
964 rc2 = pEvent->SignalInternal(rc, rcGuest, pPayload);
965 }
966 else
967 rc2 = VERR_NOT_FOUND;
968
969 int rc3 = RTCritSectLeave(&mWaitEventCritSect);
970 if (RT_SUCCESS(rc2))
971 rc2 = rc3;
972 }
973
974 return rc2;
975}
976
977/**
978 * Unregisters (deletes) a wait event.
979 *
980 * After successful unregistration the event will not be valid anymore.
981 *
982 * @returns IPRT status code.
983 * @param pEvent Event to unregister (delete).
984 */
985int GuestBase::unregisterWaitEvent(GuestWaitEvent *pEvent)
986{
987 if (!pEvent) /* Nothing to unregister. */
988 return VINF_SUCCESS;
989
990 int rc = RTCritSectEnter(&mWaitEventCritSect);
991 if (RT_SUCCESS(rc))
992 {
993 LogFlowThisFunc(("pEvent=%p\n", pEvent));
994
995 try
996 {
997 /* Remove the event from all event type groups. */
998 const GuestEventTypes lstTypes = pEvent->Types();
999 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1000 itType != lstTypes.end(); ++itType)
1001 {
1002 /** @todo Slow O(n) lookup. Optimize this. */
1003 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1004 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1005 {
1006 if (itCurEvent->second == pEvent)
1007 {
1008 mWaitEventGroups[(*itType)].erase(itCurEvent);
1009 break;
1010 }
1011 else
1012 ++itCurEvent;
1013 }
1014 }
1015
1016 /* Remove the event from the general event list as well. */
1017 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pEvent->ContextID());
1018
1019 Assert(itEvent != mWaitEvents.end());
1020 Assert(itEvent->second == pEvent);
1021
1022 mWaitEvents.erase(itEvent);
1023
1024 delete pEvent;
1025 pEvent = NULL;
1026 }
1027 catch (const std::exception &ex)
1028 {
1029 NOREF(ex);
1030 AssertFailedStmt(rc = VERR_NOT_FOUND);
1031 }
1032
1033 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1034 if (RT_SUCCESS(rc))
1035 rc = rc2;
1036 }
1037
1038 return rc;
1039}
1040
1041/**
1042 * Waits for a formerly registered guest event.
1043 *
1044 * @return IPRT status code.
1045 * @param pEvent Pointer to event to wait for.
1046 * @param uTimeoutMS Timeout (in ms) for waiting.
1047 * @param pType Event type of following IEvent.
1048 * Optional.
1049 * @param ppEvent Pointer to IEvent which got triggered
1050 * for this event. Optional.
1051 */
1052int GuestBase::waitForEvent(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1053 VBoxEventType_T *pType, IEvent **ppEvent)
1054{
1055 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1056 /* pType is optional. */
1057 /* ppEvent is optional. */
1058
1059 int vrc = pEvent->Wait(uTimeoutMS);
1060 if (RT_SUCCESS(vrc))
1061 {
1062 const ComPtr<IEvent> pThisEvent = pEvent->Event();
1063 if (!pThisEvent.isNull()) /* Having a VBoxEventType_ event is optional. */
1064 {
1065 if (pType)
1066 {
1067 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
1068 if (FAILED(hr))
1069 vrc = VERR_COM_UNEXPECTED;
1070 }
1071 if ( RT_SUCCESS(vrc)
1072 && ppEvent)
1073 pThisEvent.queryInterfaceTo(ppEvent);
1074
1075 unconst(pThisEvent).setNull();
1076 }
1077 }
1078
1079 return vrc;
1080}
1081
1082GuestObject::GuestObject(void)
1083 : mSession(NULL),
1084 mObjectID(0)
1085{
1086}
1087
1088GuestObject::~GuestObject(void)
1089{
1090}
1091
1092int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1093{
1094 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1095 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1096
1097 mConsole = pConsole;
1098 mSession = pSession;
1099 mObjectID = uObjectID;
1100
1101 return VINF_SUCCESS;
1102}
1103
1104int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1105 GuestWaitEvent **ppEvent)
1106{
1107 AssertPtr(mSession);
1108 return GuestBase::registerWaitEvent(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1109}
1110
1111int GuestObject::sendCommand(uint32_t uFunction,
1112 uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1113{
1114#ifndef VBOX_GUESTCTRL_TEST_CASE
1115 ComObjPtr<Console> pConsole = mConsole;
1116 Assert(!pConsole.isNull());
1117
1118 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1119
1120 /* Forward the information to the VMM device. */
1121 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1122 if (pVMMDev)
1123 {
1124 LogFlowThisFunc(("uFunction=%RU32, cParms=%RU32\n", uFunction, cParms));
1125 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, cParms, paParms);
1126 if (RT_FAILURE(vrc))
1127 {
1128 /** @todo What to do here? */
1129 }
1130 }
1131#else
1132 LogFlowThisFuncEnter();
1133
1134 /* Not needed within testcases. */
1135 RT_NOREF(uFunction, cParms, paParms);
1136 int vrc = VINF_SUCCESS;
1137#endif
1138 return vrc;
1139}
1140
1141GuestWaitEventBase::GuestWaitEventBase(void)
1142 : mfAborted(false),
1143 mCID(0),
1144 mEventSem(NIL_RTSEMEVENT),
1145 mRc(VINF_SUCCESS),
1146 mGuestRc(VINF_SUCCESS)
1147{
1148}
1149
1150GuestWaitEventBase::~GuestWaitEventBase(void)
1151{
1152 if (mEventSem != NIL_RTSEMEVENT)
1153 {
1154 RTSemEventDestroy(mEventSem);
1155 mEventSem = NIL_RTSEMEVENT;
1156 }
1157}
1158
1159int GuestWaitEventBase::Init(uint32_t uCID)
1160{
1161 mCID = uCID;
1162
1163 return RTSemEventCreate(&mEventSem);
1164}
1165
1166int GuestWaitEventBase::SignalInternal(int rc, int rcGuest,
1167 const GuestWaitEventPayload *pPayload)
1168{
1169 if (ASMAtomicReadBool(&mfAborted))
1170 return VERR_CANCELLED;
1171
1172#ifdef VBOX_STRICT
1173 if (rc == VERR_GSTCTL_GUEST_ERROR)
1174 AssertMsg(RT_FAILURE(rcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", rcGuest));
1175 else
1176 AssertMsg(RT_SUCCESS(rcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", rcGuest));
1177#endif
1178
1179 int rc2;
1180 if (pPayload)
1181 rc2 = mPayload.CopyFromDeep(*pPayload);
1182 else
1183 rc2 = VINF_SUCCESS;
1184 if (RT_SUCCESS(rc2))
1185 {
1186 mRc = rc;
1187 mGuestRc = rcGuest;
1188
1189 rc2 = RTSemEventSignal(mEventSem);
1190 }
1191
1192 return rc2;
1193}
1194
1195int GuestWaitEventBase::Wait(RTMSINTERVAL uTimeoutMS)
1196{
1197 int rc = VINF_SUCCESS;
1198
1199 if (ASMAtomicReadBool(&mfAborted))
1200 rc = VERR_CANCELLED;
1201
1202 if (RT_SUCCESS(rc))
1203 {
1204 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1205
1206 RTMSINTERVAL msInterval = uTimeoutMS;
1207 if (!uTimeoutMS)
1208 msInterval = RT_INDEFINITE_WAIT;
1209 rc = RTSemEventWait(mEventSem, msInterval);
1210 if (ASMAtomicReadBool(&mfAborted))
1211 rc = VERR_CANCELLED;
1212 if (RT_SUCCESS(rc))
1213 {
1214 /* If waiting succeeded, return the overall
1215 * result code. */
1216 rc = mRc;
1217 }
1218 }
1219
1220 return rc;
1221}
1222
1223GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
1224 const GuestEventTypes &lstEvents)
1225{
1226 int rc2 = Init(uCID);
1227 AssertRC(rc2); /** @todo Throw exception here. */
1228
1229 mEventTypes = lstEvents;
1230}
1231
1232GuestWaitEvent::GuestWaitEvent(uint32_t uCID)
1233{
1234 int rc2 = Init(uCID);
1235 AssertRC(rc2); /** @todo Throw exception here. */
1236}
1237
1238GuestWaitEvent::~GuestWaitEvent(void)
1239{
1240
1241}
1242
1243/**
1244 * Cancels the event.
1245 */
1246int GuestWaitEvent::Cancel(void)
1247{
1248 AssertReturn(!mfAborted, VERR_CANCELLED);
1249 ASMAtomicWriteBool(&mfAborted, true);
1250
1251#ifdef DEBUG_andy
1252 LogFlowThisFunc(("Cancelling %p ...\n"));
1253#endif
1254 return RTSemEventSignal(mEventSem);
1255}
1256
1257int GuestWaitEvent::Init(uint32_t uCID)
1258{
1259 return GuestWaitEventBase::Init(uCID);
1260}
1261
1262/**
1263 * Signals the event.
1264 *
1265 * @return IPRT status code.
1266 * @param pEvent Public IEvent to associate.
1267 * Optional.
1268 */
1269int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1270{
1271 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1272
1273 if (pEvent)
1274 mEvent = pEvent;
1275
1276 return RTSemEventSignal(mEventSem);
1277}
1278
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