VirtualBox

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

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

Guest Control: Implemented support for IGuestFsObjInfo::[accessTime,birthTime,changeTime,modificationTime].

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette