VirtualBox

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

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

Main,Config.kmk: GCC 8.2.0 fixes

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

© 2023 Oracle
ContactPrivacy policyTerms of Use