VirtualBox

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

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

Guest Control/Main: Check rc in GuestBase::baseUninit().

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