VirtualBox

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

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

Guest Control/Main: Added GuestFsObjData::GetFileMode().

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