VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.7 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * Internal helpers/structures for guest control functionality.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
33#include "LoggingNew.h"
34
35#ifndef VBOX_WITH_GUEST_CONTROL
36# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
37#endif
38#include "GuestCtrlImplPrivate.h"
39#include "GuestSessionImpl.h"
40#include "VMMDev.h"
41
42#include <iprt/asm.h>
43#include <iprt/cpp/utils.h> /* For unconst(). */
44#include <iprt/ctype.h>
45#ifdef DEBUG
46# include <iprt/file.h>
47#endif
48#include <iprt/fs.h>
49#include <iprt/rand.h>
50#include <iprt/time.h>
51#include <VBox/AssertGuest.h>
52
53
54/**
55 * Extracts the timespec from a given stream block key.
56 *
57 * @return Pointer to handed-in timespec, or NULL if invalid / not found.
58 * @param strmBlk Stream block to extract timespec from.
59 * @param strKey Key to get timespec for.
60 * @param pTimeSpec Where to store the extracted timespec.
61 */
62/* static */
63PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec)
64{
65 AssertPtrReturn(pTimeSpec, NULL);
66
67 Utf8Str strTime = strmBlk.GetString(strKey.c_str());
68 if (strTime.isEmpty())
69 return NULL;
70
71 if (!RTTimeSpecFromString(pTimeSpec, strTime.c_str()))
72 return NULL;
73
74 return pTimeSpec;
75}
76
77/**
78 * Extracts the nanoseconds relative from Unix epoch for a given stream block key.
79 *
80 * @return Nanoseconds relative from Unix epoch, or 0 if invalid / not found.
81 * @param strmBlk Stream block to extract nanoseconds from.
82 * @param strKey Key to get nanoseconds for.
83 */
84/* static */
85int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey)
86{
87 RTTIMESPEC TimeSpec;
88 if (!GuestFsObjData::TimeSpecFromKey(strmBlk, strKey, &TimeSpec))
89 return 0;
90
91 return TimeSpec.i64NanosecondsRelativeToUnixEpoch;
92}
93
94/**
95 * Initializes this object data with a stream block from VBOXSERVICE_TOOL_LS.
96 *
97 * This is also used by FromStat since the output should be identical given that
98 * they use the same output function on the guest side when fLong is true.
99 *
100 * @return VBox status code.
101 * @param strmBlk Stream block to use for initialization.
102 * @param fLong Whether the stream block contains long (detailed) information or not.
103 */
104int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk, bool fLong)
105{
106 LogFlowFunc(("\n"));
107#ifdef DEBUG
108 strmBlk.DumpToLog();
109#endif
110
111 /* Object name. */
112 mName = strmBlk.GetString("name");
113 ASSERT_GUEST_RETURN(mName.isNotEmpty(), VERR_NOT_FOUND);
114
115 /* Type & attributes. */
116 bool fHaveAttribs = false;
117 char szAttribs[32];
118 memset(szAttribs, '?', sizeof(szAttribs) - 1);
119 mType = FsObjType_Unknown;
120 const char *psz = strmBlk.GetString("ftype");
121 if (psz)
122 {
123 fHaveAttribs = true;
124 szAttribs[0] = *psz;
125 switch (*psz)
126 {
127 case '-': mType = FsObjType_File; break;
128 case 'd': mType = FsObjType_Directory; break;
129 case 'l': mType = FsObjType_Symlink; break;
130 case 'c': mType = FsObjType_DevChar; break;
131 case 'b': mType = FsObjType_DevBlock; break;
132 case 'f': mType = FsObjType_Fifo; break;
133 case 's': mType = FsObjType_Socket; break;
134 case 'w': mType = FsObjType_WhiteOut; break;
135 default:
136 AssertMsgFailed(("%s\n", psz));
137 szAttribs[0] = '?';
138 fHaveAttribs = false;
139 break;
140 }
141 }
142 psz = strmBlk.GetString("owner_mask");
143 if ( psz
144 && (psz[0] == '-' || psz[0] == 'r')
145 && (psz[1] == '-' || psz[1] == 'w')
146 && (psz[2] == '-' || psz[2] == 'x'))
147 {
148 szAttribs[1] = psz[0];
149 szAttribs[2] = psz[1];
150 szAttribs[3] = psz[2];
151 fHaveAttribs = true;
152 }
153 psz = strmBlk.GetString("group_mask");
154 if ( psz
155 && (psz[0] == '-' || psz[0] == 'r')
156 && (psz[1] == '-' || psz[1] == 'w')
157 && (psz[2] == '-' || psz[2] == 'x'))
158 {
159 szAttribs[4] = psz[0];
160 szAttribs[5] = psz[1];
161 szAttribs[6] = psz[2];
162 fHaveAttribs = true;
163 }
164 psz = strmBlk.GetString("other_mask");
165 if ( psz
166 && (psz[0] == '-' || psz[0] == 'r')
167 && (psz[1] == '-' || psz[1] == 'w')
168 && (psz[2] == '-' || psz[2] == 'x'))
169 {
170 szAttribs[7] = psz[0];
171 szAttribs[8] = psz[1];
172 szAttribs[9] = psz[2];
173 fHaveAttribs = true;
174 }
175 szAttribs[10] = ' '; /* Reserve three chars for sticky bits. */
176 szAttribs[11] = ' ';
177 szAttribs[12] = ' ';
178 szAttribs[13] = ' '; /* Separator. */
179 psz = strmBlk.GetString("dos_mask");
180 if ( psz
181 && (psz[ 0] == '-' || psz[ 0] == 'R')
182 && (psz[ 1] == '-' || psz[ 1] == 'H')
183 && (psz[ 2] == '-' || psz[ 2] == 'S')
184 && (psz[ 3] == '-' || psz[ 3] == 'D')
185 && (psz[ 4] == '-' || psz[ 4] == 'A')
186 && (psz[ 5] == '-' || psz[ 5] == 'd')
187 && (psz[ 6] == '-' || psz[ 6] == 'N')
188 && (psz[ 7] == '-' || psz[ 7] == 'T')
189 && (psz[ 8] == '-' || psz[ 8] == 'P')
190 && (psz[ 9] == '-' || psz[ 9] == 'J')
191 && (psz[10] == '-' || psz[10] == 'C')
192 && (psz[11] == '-' || psz[11] == 'O')
193 && (psz[12] == '-' || psz[12] == 'I')
194 && (psz[13] == '-' || psz[13] == 'E'))
195 {
196 memcpy(&szAttribs[14], psz, 14);
197 fHaveAttribs = true;
198 }
199 szAttribs[28] = '\0';
200 if (fHaveAttribs)
201 mFileAttrs = szAttribs;
202
203 /* Object size. */
204 int rc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
205 ASSERT_GUEST_RC_RETURN(rc, rc);
206 strmBlk.GetInt64Ex("alloc", &mAllocatedSize);
207
208 /* INode number and device. */
209 psz = strmBlk.GetString("node_id");
210 if (!psz)
211 psz = strmBlk.GetString("cnode_id"); /* copy & past error fixed in 6.0 RC1 */
212 if (psz)
213 mNodeID = RTStrToInt64(psz);
214 mNodeIDDevice = strmBlk.GetUInt32("inode_dev"); /* (Produced by GAs prior to 6.0 RC1.) */
215
216 if (fLong)
217 {
218 /* Dates. */
219 mAccessTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_atime");
220 mBirthTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_birthtime");
221 mChangeTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_ctime");
222 mModificationTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_mtime");
223
224 /* Owner & group. */
225 mUID = strmBlk.GetInt32("uid");
226 psz = strmBlk.GetString("username");
227 if (psz)
228 mUserName = psz;
229 mGID = strmBlk.GetInt32("gid");
230 psz = strmBlk.GetString("groupname");
231 if (psz)
232 mGroupName = psz;
233
234 /* Misc attributes: */
235 mNumHardLinks = strmBlk.GetUInt32("hlinks", 1);
236 mDeviceNumber = strmBlk.GetUInt32("st_rdev");
237 mGenerationID = strmBlk.GetUInt32("st_gen");
238 mUserFlags = strmBlk.GetUInt32("st_flags");
239
240 /** @todo ACL */
241 }
242
243 LogFlowFuncLeave();
244 return VINF_SUCCESS;
245}
246
247/**
248 * Parses stream block output data which came from the 'stat' (vbox_stat)
249 * VBoxService toolbox command. The result will be stored in this object.
250 *
251 * @returns VBox status code.
252 * @param strmBlk Stream block output data to parse.
253 */
254int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
255{
256 /* Should be identical output. */
257 return GuestFsObjData::FromLs(strmBlk, true /*fLong*/);
258}
259
260/**
261 * Parses stream block output data which came from the 'mktemp' (vbox_mktemp)
262 * VBoxService toolbox command. The result will be stored in this object.
263 *
264 * @returns VBox status code.
265 * @param strmBlk Stream block output data to parse.
266 */
267int GuestFsObjData::FromMkTemp(const GuestProcessStreamBlock &strmBlk)
268{
269 LogFlowFunc(("\n"));
270
271#ifdef DEBUG
272 strmBlk.DumpToLog();
273#endif
274 /* Object name. */
275 mName = strmBlk.GetString("name");
276 ASSERT_GUEST_RETURN(mName.isNotEmpty(), VERR_NOT_FOUND);
277
278 /* Assign the stream block's rc. */
279 int rc = strmBlk.GetRc();
280
281 LogFlowFuncLeaveRC(rc);
282 return rc;
283}
284
285/**
286 * Returns the IPRT-compatible file mode.
287 * Note: Only handling RTFS_TYPE_ flags are implemented for now.
288 *
289 * @return IPRT file mode.
290 */
291RTFMODE GuestFsObjData::GetFileMode(void) const
292{
293 RTFMODE fMode = 0;
294
295 switch (mType)
296 {
297 case FsObjType_Directory:
298 fMode |= RTFS_TYPE_DIRECTORY;
299 break;
300
301 case FsObjType_File:
302 fMode |= RTFS_TYPE_FILE;
303 break;
304
305 case FsObjType_Symlink:
306 fMode |= RTFS_TYPE_SYMLINK;
307 break;
308
309 default:
310 break;
311 }
312
313 /** @todo Implement more stuff. */
314
315 return fMode;
316}
317
318///////////////////////////////////////////////////////////////////////////////
319
320/** @todo *NOT* thread safe yet! */
321/** @todo Add exception handling for STL stuff! */
322
323GuestProcessStreamBlock::GuestProcessStreamBlock(void)
324{
325
326}
327
328GuestProcessStreamBlock::~GuestProcessStreamBlock()
329{
330 Clear();
331}
332
333/**
334 * Clears (destroys) the currently stored stream pairs.
335 */
336void GuestProcessStreamBlock::Clear(void)
337{
338 mPairs.clear();
339}
340
341#ifdef DEBUG
342/**
343 * Dumps the currently stored stream pairs to the (debug) log.
344 */
345void GuestProcessStreamBlock::DumpToLog(void) const
346{
347 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
348 this, mPairs.size()));
349
350 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
351 it != mPairs.end(); ++it)
352 {
353 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
354 }
355}
356#endif
357
358/**
359 * Returns a 64-bit signed integer of a specified key.
360 *
361 * @return VBox status code. VERR_NOT_FOUND if key was not found.
362 * @param pszKey Name of key to get the value for.
363 * @param piVal Pointer to value to return.
364 */
365int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
366{
367 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
368 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
369 const char *pszValue = GetString(pszKey);
370 if (pszValue)
371 {
372 *piVal = RTStrToInt64(pszValue);
373 return VINF_SUCCESS;
374 }
375 return VERR_NOT_FOUND;
376}
377
378/**
379 * Returns a 64-bit integer of a specified key.
380 *
381 * @return int64_t Value to return, 0 if not found / on failure.
382 * @param pszKey Name of key to get the value for.
383 */
384int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
385{
386 int64_t iVal;
387 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
388 return iVal;
389 return 0;
390}
391
392/**
393 * Returns the current number of stream pairs.
394 *
395 * @return uint32_t Current number of stream pairs.
396 */
397size_t GuestProcessStreamBlock::GetCount(void) const
398{
399 return mPairs.size();
400}
401
402/**
403 * Gets the return code (name = "rc") of this stream block.
404 *
405 * @return VBox status code.
406 */
407int GuestProcessStreamBlock::GetRc(void) const
408{
409 const char *pszValue = GetString("rc");
410 if (pszValue)
411 {
412 return RTStrToInt16(pszValue);
413 }
414 return VERR_NOT_FOUND;
415}
416
417/**
418 * Returns a string value of a specified key.
419 *
420 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
421 * @param pszKey Name of key to get the value for.
422 */
423const char *GuestProcessStreamBlock::GetString(const char *pszKey) const
424{
425 AssertPtrReturn(pszKey, NULL);
426
427 try
428 {
429 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(pszKey);
430 if (itPairs != mPairs.end())
431 return itPairs->second.mValue.c_str();
432 }
433 catch (const std::exception &ex)
434 {
435 RT_NOREF(ex);
436 }
437 return NULL;
438}
439
440/**
441 * Returns a 32-bit unsigned integer of a specified key.
442 *
443 * @return VBox status code. VERR_NOT_FOUND if key was not found.
444 * @param pszKey Name of key to get the value for.
445 * @param puVal Pointer to value to return.
446 */
447int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
448{
449 const char *pszValue = GetString(pszKey);
450 if (pszValue)
451 {
452 *puVal = RTStrToUInt32(pszValue);
453 return VINF_SUCCESS;
454 }
455 return VERR_NOT_FOUND;
456}
457
458/**
459 * Returns a 32-bit signed integer of a specified key.
460 *
461 * @returns 32-bit signed value
462 * @param pszKey Name of key to get the value for.
463 * @param iDefault The default to return on error if not found.
464 */
465int32_t GuestProcessStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const
466{
467 const char *pszValue = GetString(pszKey);
468 if (pszValue)
469 {
470 int32_t iRet;
471 int rc = RTStrToInt32Full(pszValue, 0, &iRet);
472 if (RT_SUCCESS(rc))
473 return iRet;
474 ASSERT_GUEST_MSG_FAILED(("%s=%s\n", pszKey, pszValue));
475 }
476 return iDefault;
477}
478
479/**
480 * Returns a 32-bit unsigned integer of a specified key.
481 *
482 * @return uint32_t Value to return, 0 if not found / on failure.
483 * @param pszKey Name of key to get the value for.
484 * @param uDefault The default value to return.
485 */
486uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const
487{
488 uint32_t uVal;
489 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
490 return uVal;
491 return uDefault;
492}
493
494/**
495 * Sets a value to a key or deletes a key by setting a NULL value.
496 *
497 * @return VBox status code.
498 * @param pszKey Key name to process.
499 * @param pszValue Value to set. Set NULL for deleting the key.
500 */
501int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
502{
503 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
504
505 int rc = VINF_SUCCESS;
506 try
507 {
508 Utf8Str Utf8Key(pszKey);
509
510 /* Take a shortcut and prevent crashes on some funny versions
511 * of STL if map is empty initially. */
512 if (!mPairs.empty())
513 {
514 GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
515 if (it != mPairs.end())
516 mPairs.erase(it);
517 }
518
519 if (pszValue)
520 {
521 GuestProcessStreamValue val(pszValue);
522 mPairs[Utf8Key] = val;
523 }
524 }
525 catch (const std::exception &ex)
526 {
527 RT_NOREF(ex);
528 }
529 return rc;
530}
531
532///////////////////////////////////////////////////////////////////////////////
533
534GuestProcessStream::GuestProcessStream(void)
535 : m_cbMax(_32M)
536 , m_cbAllocated(0)
537 , m_cbUsed(0)
538 , m_offBuffer(0)
539 , m_pbBuffer(NULL) { }
540
541GuestProcessStream::~GuestProcessStream(void)
542{
543 Destroy();
544}
545
546/**
547 * Adds data to the internal parser buffer. Useful if there
548 * are multiple rounds of adding data needed.
549 *
550 * @return VBox status code. Will return VERR_TOO_MUCH_DATA if the buffer's maximum (limit) has been reached.
551 * @param pbData Pointer to data to add.
552 * @param cbData Size (in bytes) of data to add.
553 */
554int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
555{
556 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
557 AssertReturn(cbData, VERR_INVALID_PARAMETER);
558
559 int rc = VINF_SUCCESS;
560
561 /* Rewind the buffer if it's empty. */
562 size_t cbInBuf = m_cbUsed - m_offBuffer;
563 bool const fAddToSet = cbInBuf == 0;
564 if (fAddToSet)
565 m_cbUsed = m_offBuffer = 0;
566
567 /* Try and see if we can simply append the data. */
568 if (cbData + m_cbUsed <= m_cbAllocated)
569 {
570 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
571 m_cbUsed += cbData;
572 }
573 else
574 {
575 /* Move any buffered data to the front. */
576 cbInBuf = m_cbUsed - m_offBuffer;
577 if (cbInBuf == 0)
578 m_cbUsed = m_offBuffer = 0;
579 else if (m_offBuffer) /* Do we have something to move? */
580 {
581 memmove(m_pbBuffer, &m_pbBuffer[m_offBuffer], cbInBuf);
582 m_cbUsed = cbInBuf;
583 m_offBuffer = 0;
584 }
585
586 /* Do we need to grow the buffer? */
587 if (cbData + m_cbUsed > m_cbAllocated)
588 {
589 size_t cbAlloc = m_cbUsed + cbData;
590 if (cbAlloc <= m_cbMax)
591 {
592 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
593 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
594 if (pvNew)
595 {
596 m_pbBuffer = (uint8_t *)pvNew;
597 m_cbAllocated = cbAlloc;
598 }
599 else
600 rc = VERR_NO_MEMORY;
601 }
602 else
603 rc = VERR_TOO_MUCH_DATA;
604 }
605
606 /* Finally, copy the data. */
607 if (RT_SUCCESS(rc))
608 {
609 if (cbData + m_cbUsed <= m_cbAllocated)
610 {
611 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
612 m_cbUsed += cbData;
613 }
614 else
615 rc = VERR_BUFFER_OVERFLOW;
616 }
617 }
618
619 return rc;
620}
621
622/**
623 * Destroys the internal data buffer.
624 */
625void GuestProcessStream::Destroy(void)
626{
627 if (m_pbBuffer)
628 {
629 RTMemFree(m_pbBuffer);
630 m_pbBuffer = NULL;
631 }
632
633 m_cbAllocated = 0;
634 m_cbUsed = 0;
635 m_offBuffer = 0;
636}
637
638#ifdef DEBUG
639/**
640 * Dumps the raw guest process output to a file on the host.
641 * If the file on the host already exists, it will be overwritten.
642 *
643 * @param pszFile Absolute path to host file to dump the output to.
644 */
645void GuestProcessStream::Dump(const char *pszFile)
646{
647 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
648 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile));
649
650 RTFILE hFile;
651 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
652 if (RT_SUCCESS(rc))
653 {
654 rc = RTFileWrite(hFile, m_pbBuffer, m_cbUsed, NULL /* pcbWritten */);
655 RTFileClose(hFile);
656 }
657}
658#endif
659
660/**
661 * Tries to parse the next upcoming pair block within the internal
662 * buffer.
663 *
664 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
665 * completely parsed already.
666 *
667 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
668 * stored in stream block) but still contains incomplete (unterminated)
669 * data.
670 *
671 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
672 * block (with zero or more pairs stored in stream block).
673 *
674 * @return VBox status code.
675 * @param streamBlock Reference to guest stream block to fill.
676 */
677int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
678{
679 if ( !m_pbBuffer
680 || !m_cbUsed)
681 return VERR_NO_DATA;
682
683 AssertReturn(m_offBuffer <= m_cbUsed, VERR_INVALID_PARAMETER);
684 if (m_offBuffer == m_cbUsed)
685 return VERR_NO_DATA;
686
687 int rc = VINF_SUCCESS;
688 char * const pszOff = (char *)&m_pbBuffer[m_offBuffer];
689 size_t cbLeft = m_offBuffer < m_cbUsed ? m_cbUsed - m_offBuffer : 0;
690 char *pszStart = pszOff;
691 while (cbLeft > 0 && *pszStart != '\0')
692 {
693 char * const pszPairEnd = RTStrEnd(pszStart, cbLeft);
694 if (!pszPairEnd)
695 {
696 rc = VERR_MORE_DATA;
697 break;
698 }
699 size_t const cchPair = (size_t)(pszPairEnd - pszStart);
700 char *pszSep = (char *)memchr(pszStart, '=', cchPair);
701 if (pszSep)
702 *pszSep = '\0'; /* Terminate the separator so that we can use pszStart as our key from now on. */
703 else
704 {
705 rc = VERR_MORE_DATA; /** @todo r=bird: This is BOGUS because we'll be stuck here if the guest feeds us bad data! */
706 break;
707 }
708 char const * const pszVal = pszSep + 1;
709
710 rc = streamBlock.SetValue(pszStart, pszVal);
711 if (RT_FAILURE(rc))
712 return rc;
713
714 /* Next pair. */
715 pszStart = pszPairEnd + 1;
716 cbLeft -= cchPair + 1;
717 }
718
719 /* If we did not do any movement but we have stuff left
720 * in our buffer just skip the current termination so that
721 * we can try next time. */
722 size_t cbDistance = (pszStart - pszOff);
723 if ( !cbDistance
724 && cbLeft > 0
725 && *pszStart == '\0'
726 && m_offBuffer < m_cbUsed)
727 cbDistance++;
728 m_offBuffer += cbDistance;
729
730 return rc;
731}
732
733GuestBase::GuestBase(void)
734 : mConsole(NULL)
735 , mNextContextID(RTRandU32() % VBOX_GUESTCTRL_MAX_CONTEXTS)
736{
737}
738
739GuestBase::~GuestBase(void)
740{
741}
742
743/**
744 * Separate initialization function for the base class.
745 *
746 * @returns VBox status code.
747 */
748int GuestBase::baseInit(void)
749{
750 int rc = RTCritSectInit(&mWaitEventCritSect);
751
752 LogFlowFuncLeaveRC(rc);
753 return rc;
754}
755
756/**
757 * Separate uninitialization function for the base class.
758 */
759void GuestBase::baseUninit(void)
760{
761 LogFlowThisFuncEnter();
762
763 /* Make sure to cancel any outstanding wait events. */
764 int rc2 = cancelWaitEvents();
765 AssertRC(rc2);
766
767 rc2 = RTCritSectDelete(&mWaitEventCritSect);
768 AssertRC(rc2);
769
770 LogFlowFuncLeaveRC(rc2);
771 /* No return value. */
772}
773
774/**
775 * Cancels all outstanding wait events.
776 *
777 * @returns VBox status code.
778 */
779int GuestBase::cancelWaitEvents(void)
780{
781 LogFlowThisFuncEnter();
782
783 int rc = RTCritSectEnter(&mWaitEventCritSect);
784 if (RT_SUCCESS(rc))
785 {
786 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
787 while (itEventGroups != mWaitEventGroups.end())
788 {
789 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
790 while (itEvents != itEventGroups->second.end())
791 {
792 GuestWaitEvent *pEvent = itEvents->second;
793 AssertPtr(pEvent);
794
795 /*
796 * Just cancel the event, but don't remove it from the
797 * wait events map. Don't delete it though, this (hopefully)
798 * is done by the caller using unregisterWaitEvent().
799 */
800 int rc2 = pEvent->Cancel();
801 AssertRC(rc2);
802
803 ++itEvents;
804 }
805
806 ++itEventGroups;
807 }
808
809 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
810 if (RT_SUCCESS(rc))
811 rc = rc2;
812 }
813
814 LogFlowFuncLeaveRC(rc);
815 return rc;
816}
817
818/**
819 * Handles generic messages not bound to a specific object type.
820 *
821 * @return VBox status code. VERR_NOT_FOUND if no handler has been found or VERR_NOT_SUPPORTED
822 * if this class does not support the specified callback.
823 * @param pCtxCb Host callback context.
824 * @param pSvcCb Service callback data.
825 */
826int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
827{
828 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
829
830 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
831 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
832
833 int vrc;
834
835 try
836 {
837 Log2Func(("uFunc=%RU32, cParms=%RU32\n", pCtxCb->uMessage, pSvcCb->mParms));
838
839 switch (pCtxCb->uMessage)
840 {
841 case GUEST_MSG_PROGRESS_UPDATE:
842 vrc = VINF_SUCCESS;
843 break;
844
845 case GUEST_MSG_REPLY:
846 {
847 if (pSvcCb->mParms >= 4)
848 {
849 int idx = 1; /* Current parameter index. */
850 CALLBACKDATA_MSG_REPLY dataCb;
851 /* pSvcCb->mpaParms[0] always contains the context ID. */
852 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
853 AssertRCReturn(vrc, vrc);
854 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
855 AssertRCReturn(vrc, vrc);
856 vrc = HGCMSvcGetPv(&pSvcCb->mpaParms[idx++], &dataCb.pvPayload, &dataCb.cbPayload);
857 AssertRCReturn(vrc, vrc);
858
859 try
860 {
861 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
862 vrc = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
863 }
864 catch (int rcEx) /* Thrown by GuestWaitEventPayload constructor. */
865 {
866 vrc = rcEx;
867 }
868 }
869 else
870 vrc = VERR_INVALID_PARAMETER;
871 break;
872 }
873
874 default:
875 vrc = VERR_NOT_SUPPORTED;
876 break;
877 }
878 }
879 catch (std::bad_alloc &)
880 {
881 vrc = VERR_NO_MEMORY;
882 }
883 catch (int rc)
884 {
885 vrc = rc;
886 }
887
888 LogFlowFuncLeaveRC(vrc);
889 return vrc;
890}
891
892/**
893 * Generates a context ID (CID) by incrementing the object's count.
894 * A CID consists of a session ID, an object ID and a count.
895 *
896 * Note: This function does not guarantee that the returned CID is unique;
897 * the caller has to take care of that and eventually retry.
898 *
899 * @returns VBox status code.
900 * @param uSessionID Session ID to use for CID generation.
901 * @param uObjectID Object ID to use for CID generation.
902 * @param puContextID Where to store the generated CID on success.
903 */
904int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
905{
906 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
907
908 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
909 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
910 return VERR_INVALID_PARAMETER;
911
912 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
913 uCount %= VBOX_GUESTCTRL_MAX_CONTEXTS;
914
915 uint32_t uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
916
917 *puContextID = uNewContextID;
918
919#if 0
920 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
921 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
922#endif
923 return VINF_SUCCESS;
924}
925
926/**
927 * Registers (creates) a new wait event based on a given session and object ID.
928 *
929 * From those IDs an unique context ID (CID) will be built, which only can be
930 * around once at a time.
931 *
932 * @returns VBox status code.
933 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
934 * @param uSessionID Session ID to register wait event for.
935 * @param uObjectID Object ID to register wait event for.
936 * @param ppEvent Pointer to registered (created) wait event on success.
937 * Must be destroyed with unregisterWaitEvent().
938 */
939int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID, GuestWaitEvent **ppEvent)
940{
941 GuestEventTypes eventTypesEmpty;
942 return registerWaitEventEx(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
943}
944
945/**
946 * Creates and registers a new wait event object that waits on a set of events
947 * related to a given object within the session.
948 *
949 * From the session ID and object ID a one-time unique context ID (CID) is built
950 * for this wait object. Normally the CID is then passed to the guest along
951 * with a request, and the guest passed the CID back with the reply. The
952 * handler for the reply then emits a signal on the event type associated with
953 * the reply, which includes signalling the object returned by this method and
954 * the waking up the thread waiting on it.
955 *
956 * @returns VBox status code.
957 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
958 * @param uSessionID Session ID to register wait event for.
959 * @param uObjectID Object ID to register wait event for.
960 * @param lstEvents List of events to register the wait event for.
961 * @param ppEvent Pointer to registered (created) wait event on success.
962 * Must be destroyed with unregisterWaitEvent().
963 */
964int GuestBase::registerWaitEventEx(uint32_t uSessionID, uint32_t uObjectID, const GuestEventTypes &lstEvents,
965 GuestWaitEvent **ppEvent)
966{
967 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
968
969 uint32_t idContext;
970 int rc = generateContextID(uSessionID, uObjectID, &idContext);
971 AssertRCReturn(rc, rc);
972
973 GuestWaitEvent *pEvent = new GuestWaitEvent();
974 AssertPtrReturn(pEvent, VERR_NO_MEMORY);
975
976 rc = pEvent->Init(idContext, lstEvents);
977 AssertRCReturn(rc, rc);
978
979 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, idContext));
980
981 rc = RTCritSectEnter(&mWaitEventCritSect);
982 if (RT_SUCCESS(rc))
983 {
984 /*
985 * Check that we don't have any context ID collisions (should be very unlikely).
986 *
987 * The ASSUMPTION here is that mWaitEvents has all the same events as
988 * mWaitEventGroups, so it suffices to check one of the two.
989 */
990 if (mWaitEvents.find(idContext) != mWaitEvents.end())
991 {
992 uint32_t cTries = 0;
993 do
994 {
995 rc = generateContextID(uSessionID, uObjectID, &idContext);
996 AssertRCBreak(rc);
997 LogFunc(("Found context ID duplicate; trying a different context ID: %#x\n", idContext));
998 if (mWaitEvents.find(idContext) != mWaitEvents.end())
999 rc = VERR_GSTCTL_MAX_CID_COUNT_REACHED;
1000 } while (RT_FAILURE_NP(rc) && cTries++ < 10);
1001 }
1002 if (RT_SUCCESS(rc))
1003 {
1004 /*
1005 * Insert event into matching event group. This is for faster per-group lookup of all events later.
1006 */
1007 uint32_t cInserts = 0;
1008 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1009 {
1010 GuestWaitEvents &eventGroup = mWaitEventGroups[*ItType];
1011 if (eventGroup.find(idContext) == eventGroup.end())
1012 {
1013 try
1014 {
1015 eventGroup.insert(std::pair<uint32_t, GuestWaitEvent *>(idContext, pEvent));
1016 cInserts++;
1017 }
1018 catch (std::bad_alloc &)
1019 {
1020 while (ItType != lstEvents.begin())
1021 {
1022 --ItType;
1023 mWaitEventGroups[*ItType].erase(idContext);
1024 }
1025 rc = VERR_NO_MEMORY;
1026 break;
1027 }
1028 }
1029 else
1030 Assert(cInserts > 0); /* else: lstEvents has duplicate entries. */
1031 }
1032 if (RT_SUCCESS(rc))
1033 {
1034 Assert(cInserts > 0 || lstEvents.size() == 0);
1035 RT_NOREF(cInserts);
1036
1037 /*
1038 * Register event in the regular event list.
1039 */
1040 try
1041 {
1042 mWaitEvents[idContext] = pEvent;
1043 }
1044 catch (std::bad_alloc &)
1045 {
1046 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1047 mWaitEventGroups[*ItType].erase(idContext);
1048 rc = VERR_NO_MEMORY;
1049 }
1050 }
1051 }
1052
1053 RTCritSectLeave(&mWaitEventCritSect);
1054 }
1055 if (RT_SUCCESS(rc))
1056 {
1057 *ppEvent = pEvent;
1058 return rc;
1059 }
1060
1061 if (pEvent)
1062 delete pEvent;
1063
1064 return rc;
1065}
1066
1067/**
1068 * Signals all wait events of a specific type (if found)
1069 * and notifies external events accordingly.
1070 *
1071 * @returns VBox status code.
1072 * @param aType Event type to signal.
1073 * @param aEvent Which external event to notify.
1074 */
1075int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
1076{
1077 int rc = RTCritSectEnter(&mWaitEventCritSect);
1078#ifdef DEBUG
1079 uint32_t cEvents = 0;
1080#endif
1081 if (RT_SUCCESS(rc))
1082 {
1083 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
1084 if (itGroup != mWaitEventGroups.end())
1085 {
1086 /* Signal all events in the group, leaving the group empty afterwards. */
1087 GuestWaitEvents::iterator ItWaitEvt;
1088 while ((ItWaitEvt = itGroup->second.begin()) != itGroup->second.end())
1089 {
1090 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %#x: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1091 ItWaitEvt->second, aType, ItWaitEvt->first, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(ItWaitEvt->first),
1092 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(ItWaitEvt->first), VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(ItWaitEvt->first)));
1093
1094 int rc2 = ItWaitEvt->second->SignalExternal(aEvent);
1095 AssertRC(rc2);
1096
1097 /* Take down the wait event object details before we erase it from this list and invalid ItGrpEvt. */
1098 const GuestEventTypes &EvtTypes = ItWaitEvt->second->Types();
1099 uint32_t idContext = ItWaitEvt->first;
1100 itGroup->second.erase(ItWaitEvt);
1101
1102 for (GuestEventTypes::const_iterator ItType = EvtTypes.begin(); ItType != EvtTypes.end(); ++ItType)
1103 {
1104 GuestEventGroup::iterator EvtTypeGrp = mWaitEventGroups.find(*ItType);
1105 if (EvtTypeGrp != mWaitEventGroups.end())
1106 {
1107 ItWaitEvt = EvtTypeGrp->second.find(idContext);
1108 if (ItWaitEvt != EvtTypeGrp->second.end())
1109 {
1110 LogFlowThisFunc(("Removing event %p (CID %#x) from type %d group\n", ItWaitEvt->second, idContext, *ItType));
1111 EvtTypeGrp->second.erase(ItWaitEvt);
1112 LogFlowThisFunc(("%zu events left for type %d\n", EvtTypeGrp->second.size(), *ItType));
1113 Assert(EvtTypeGrp->second.find(idContext) == EvtTypeGrp->second.end()); /* no duplicates */
1114 }
1115 }
1116 }
1117 }
1118 }
1119
1120 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1121 if (RT_SUCCESS(rc))
1122 rc = rc2;
1123 }
1124
1125#ifdef DEBUG
1126 LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
1127#endif
1128 return rc;
1129}
1130
1131/**
1132 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1133 *
1134 * @returns VBox status code.
1135 * @param pCbCtx Pointer to host service callback context.
1136 * @param rcGuest Guest return code (rc) to set additionally, if rc is set to VERR_GSTCTL_GUEST_ERROR.
1137 * @param pPayload Additional wait event payload data set set on return. Optional.
1138 */
1139int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1140 int rcGuest, const GuestWaitEventPayload *pPayload)
1141{
1142 if (RT_SUCCESS(rcGuest))
1143 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
1144 0 /* Guest rc */, pPayload);
1145
1146 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
1147 rcGuest, pPayload);
1148}
1149
1150/**
1151 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1152 * Extended version.
1153 *
1154 * @returns VBox status code.
1155 * @param pCbCtx Pointer to host service callback context.
1156 * @param rc Return code (rc) to set as wait result.
1157 * @param rcGuest Guest return code (rc) to set additionally, if rc is set to VERR_GSTCTL_GUEST_ERROR.
1158 * @param pPayload Additional wait event payload data set set on return. Optional.
1159 */
1160int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
1161 int rc, int rcGuest,
1162 const GuestWaitEventPayload *pPayload)
1163{
1164 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1165 /* pPayload is optional. */
1166
1167 int rc2 = RTCritSectEnter(&mWaitEventCritSect);
1168 if (RT_SUCCESS(rc2))
1169 {
1170 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1171 if (itEvent != mWaitEvents.end())
1172 {
1173 LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, rcGuest=%Rrc, pPayload=%p) ...\n",
1174 itEvent->second, itEvent->first, rc, rcGuest, pPayload));
1175 GuestWaitEvent *pEvent = itEvent->second;
1176 AssertPtr(pEvent);
1177 rc2 = pEvent->SignalInternal(rc, rcGuest, pPayload);
1178 }
1179 else
1180 rc2 = VERR_NOT_FOUND;
1181
1182 int rc3 = RTCritSectLeave(&mWaitEventCritSect);
1183 if (RT_SUCCESS(rc2))
1184 rc2 = rc3;
1185 }
1186
1187 return rc2;
1188}
1189
1190/**
1191 * Unregisters (deletes) a wait event.
1192 *
1193 * After successful unregistration the event will not be valid anymore.
1194 *
1195 * @returns VBox status code.
1196 * @param pWaitEvt Wait event to unregister (delete).
1197 */
1198int GuestBase::unregisterWaitEvent(GuestWaitEvent *pWaitEvt)
1199{
1200 if (!pWaitEvt) /* Nothing to unregister. */
1201 return VINF_SUCCESS;
1202
1203 int rc = RTCritSectEnter(&mWaitEventCritSect);
1204 if (RT_SUCCESS(rc))
1205 {
1206 LogFlowThisFunc(("pWaitEvt=%p\n", pWaitEvt));
1207
1208/** @todo r=bird: One way of optimizing this would be to use the pointer
1209 * instead of the context ID as index into the groups, i.e. revert the value
1210 * pair for the GuestWaitEvents type.
1211 *
1212 * An even more efficent way, would be to not use sexy std::xxx containers for
1213 * the types, but iprt/list.h, as that would just be a RTListNodeRemove call for
1214 * each type w/o needing to iterate much at all. I.e. add a struct {
1215 * RTLISTNODE, GuestWaitEvent *pSelf} array to GuestWaitEvent, and change
1216 * GuestEventGroup to std::map<VBoxEventType_T, RTListAnchorClass>
1217 * (RTListAnchorClass == RTLISTANCHOR wrapper with a constructor)).
1218 *
1219 * P.S. the try/catch is now longer needed after I changed pWaitEvt->Types() to
1220 * return a const reference rather than a copy of the type list (and it think it
1221 * is safe to assume iterators are not hitting the heap). Copy vs reference is
1222 * an easy mistake to make in C++.
1223 *
1224 * P.P.S. The mWaitEventGroups optimization is probably just a lot of extra work
1225 * with little payoff.
1226 */
1227 try
1228 {
1229 /* Remove the event from all event type groups. */
1230 const GuestEventTypes &lstTypes = pWaitEvt->Types();
1231 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1232 itType != lstTypes.end(); ++itType)
1233 {
1234 /** @todo Slow O(n) lookup. Optimize this. */
1235 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1236 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1237 {
1238 if (itCurEvent->second == pWaitEvt)
1239 {
1240 mWaitEventGroups[(*itType)].erase(itCurEvent);
1241 break;
1242 }
1243 ++itCurEvent;
1244 }
1245 }
1246
1247 /* Remove the event from the general event list as well. */
1248 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pWaitEvt->ContextID());
1249
1250 Assert(itEvent != mWaitEvents.end());
1251 Assert(itEvent->second == pWaitEvt);
1252
1253 mWaitEvents.erase(itEvent);
1254
1255 delete pWaitEvt;
1256 pWaitEvt = NULL;
1257 }
1258 catch (const std::exception &ex)
1259 {
1260 RT_NOREF(ex);
1261 AssertFailedStmt(rc = VERR_NOT_FOUND);
1262 }
1263
1264 int rc2 = RTCritSectLeave(&mWaitEventCritSect);
1265 if (RT_SUCCESS(rc))
1266 rc = rc2;
1267 }
1268
1269 return rc;
1270}
1271
1272/**
1273 * Waits for an already registered guest wait event.
1274 *
1275 * @return VBox status code.
1276 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1277 * the actual result.
1278 *
1279 * @param pWaitEvt Pointer to event to wait for.
1280 * @param msTimeout Timeout (in ms) for waiting.
1281 * @param pType Event type of following IEvent. Optional.
1282 * @param ppEvent Pointer to IEvent which got triggered for this event. Optional.
1283 */
1284int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEventType_T *pType, IEvent **ppEvent)
1285{
1286 AssertPtrReturn(pWaitEvt, VERR_INVALID_POINTER);
1287 /* pType is optional. */
1288 /* ppEvent is optional. */
1289
1290 int vrc = pWaitEvt->Wait(msTimeout);
1291 if (RT_SUCCESS(vrc))
1292 {
1293 const ComPtr<IEvent> pThisEvent = pWaitEvt->Event();
1294 if (pThisEvent.isNotNull()) /* Make sure that we actually have an event associated. */
1295 {
1296 if (pType)
1297 {
1298 HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
1299 if (FAILED(hr))
1300 vrc = VERR_COM_UNEXPECTED;
1301 }
1302 if ( RT_SUCCESS(vrc)
1303 && ppEvent)
1304 pThisEvent.queryInterfaceTo(ppEvent);
1305
1306 unconst(pThisEvent).setNull();
1307 }
1308 }
1309
1310 return vrc;
1311}
1312
1313#ifndef VBOX_GUESTCTRL_TEST_CASE
1314/**
1315 * Convenience function to return a pre-formatted string using an action description and a guest error information.
1316 *
1317 * @returns Pre-formatted string with a user-friendly error string.
1318 * @param strAction Action of when the error occurred.
1319 * @param guestErrorInfo Related guest error information to use.
1320 */
1321/* static */ Utf8Str GuestBase::getErrorAsString(const Utf8Str& strAction, const GuestErrorInfo& guestErrorInfo)
1322{
1323 Assert(strAction.isNotEmpty());
1324 return Utf8StrFmt("%s: %s", strAction.c_str(), getErrorAsString(guestErrorInfo).c_str());
1325}
1326
1327/**
1328 * Returns a user-friendly error message from a given GuestErrorInfo object.
1329 *
1330 * @returns Error message string.
1331 * @param guestErrorInfo Guest error info to return error message for.
1332 */
1333/* static */ Utf8Str GuestBase::getErrorAsString(const GuestErrorInfo& guestErrorInfo)
1334{
1335 AssertMsg(RT_FAILURE(guestErrorInfo.getRc()), ("Guest rc does not indicate a failure\n"));
1336
1337 Utf8Str strErr;
1338
1339#define CASE_TOOL_ERROR(a_eType, a_strTool) \
1340 case a_eType: \
1341 { \
1342 strErr = GuestProcessTool::guestErrorToString(a_strTool, guestErrorInfo); \
1343 break; \
1344 }
1345
1346 switch (guestErrorInfo.getType())
1347 {
1348 case GuestErrorInfo::Type_Session:
1349 strErr = GuestSession::i_guestErrorToString(guestErrorInfo.getRc());
1350 break;
1351
1352 case GuestErrorInfo::Type_Process:
1353 strErr = GuestProcess::i_guestErrorToString(guestErrorInfo.getRc(), guestErrorInfo.getWhat().c_str());
1354 break;
1355
1356 case GuestErrorInfo::Type_File:
1357 strErr = GuestFile::i_guestErrorToString(guestErrorInfo.getRc(), guestErrorInfo.getWhat().c_str());
1358 break;
1359
1360 case GuestErrorInfo::Type_Directory:
1361 strErr = GuestDirectory::i_guestErrorToString(guestErrorInfo.getRc(), guestErrorInfo.getWhat().c_str());
1362 break;
1363
1364 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolCat, VBOXSERVICE_TOOL_CAT);
1365 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolLs, VBOXSERVICE_TOOL_LS);
1366 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkDir, VBOXSERVICE_TOOL_MKDIR);
1367 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkTemp, VBOXSERVICE_TOOL_MKTEMP);
1368 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolRm, VBOXSERVICE_TOOL_RM);
1369 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolStat, VBOXSERVICE_TOOL_STAT);
1370
1371 default:
1372 AssertMsgFailed(("Type not implemented (type=%RU32, rc=%Rrc)\n", guestErrorInfo.getType(), guestErrorInfo.getRc()));
1373 strErr = Utf8StrFmt("Unknown / Not implemented -- Please file a bug report (type=%RU32, rc=%Rrc)\n",
1374 guestErrorInfo.getType(), guestErrorInfo.getRc());
1375 break;
1376 }
1377
1378 return strErr;
1379}
1380
1381#endif /* VBOX_GUESTCTRL_TEST_CASE */
1382
1383/**
1384 * Converts RTFMODE to FsObjType_T.
1385 *
1386 * @return Converted FsObjType_T type.
1387 * @param fMode RTFMODE to convert.
1388 */
1389/* static */
1390FsObjType_T GuestBase::fileModeToFsObjType(RTFMODE fMode)
1391{
1392 if (RTFS_IS_FILE(fMode)) return FsObjType_File;
1393 else if (RTFS_IS_DIRECTORY(fMode)) return FsObjType_Directory;
1394 else if (RTFS_IS_SYMLINK(fMode)) return FsObjType_Symlink;
1395
1396 return FsObjType_Unknown;
1397}
1398
1399GuestObject::GuestObject(void)
1400 : mSession(NULL),
1401 mObjectID(0)
1402{
1403}
1404
1405GuestObject::~GuestObject(void)
1406{
1407}
1408
1409/**
1410 * Binds this guest (control) object to a specific guest (control) session.
1411 *
1412 * @returns VBox status code.
1413 * @param pConsole Pointer to console object to use.
1414 * @param pSession Pointer to session to bind this object to.
1415 * @param uObjectID Object ID for this object to use within that specific session.
1416 * Each object ID must be unique per session.
1417 */
1418int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1419{
1420 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1421 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1422
1423 mConsole = pConsole;
1424 mSession = pSession;
1425 mObjectID = uObjectID;
1426
1427 return VINF_SUCCESS;
1428}
1429
1430/**
1431 * Registers (creates) a new wait event.
1432 *
1433 * @returns VBox status code.
1434 * @param lstEvents List of events which the new wait event gets triggered at.
1435 * @param ppEvent Returns the new wait event on success.
1436 */
1437int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1438 GuestWaitEvent **ppEvent)
1439{
1440 AssertPtr(mSession);
1441 return GuestBase::registerWaitEventEx(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1442}
1443
1444/**
1445 * Sends a HGCM message to the guest (via the guest control host service).
1446 *
1447 * @returns VBox status code.
1448 * @param uMessage Message ID of message to send.
1449 * @param cParms Number of HGCM message parameters to send.
1450 * @param paParms Array of HGCM message parameters to send.
1451 */
1452int GuestObject::sendMessage(uint32_t uMessage, uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1453{
1454#ifndef VBOX_GUESTCTRL_TEST_CASE
1455 ComObjPtr<Console> pConsole = mConsole;
1456 Assert(!pConsole.isNull());
1457
1458 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1459
1460 /* Forward the information to the VMM device. */
1461 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1462 if (pVMMDev)
1463 {
1464 /* HACK ALERT! We extend the first parameter to 64-bit and use the
1465 two topmost bits for call destination information. */
1466 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
1467 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
1468 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_SESSION;
1469
1470 /* Make the call. */
1471 LogFlowThisFunc(("uMessage=%RU32, cParms=%RU32\n", uMessage, cParms));
1472 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, cParms, paParms);
1473 if (RT_FAILURE(vrc))
1474 {
1475 /** @todo What to do here? */
1476 }
1477 }
1478#else
1479 LogFlowThisFuncEnter();
1480
1481 /* Not needed within testcases. */
1482 RT_NOREF(uMessage, cParms, paParms);
1483 int vrc = VINF_SUCCESS;
1484#endif
1485 return vrc;
1486}
1487
1488GuestWaitEventBase::GuestWaitEventBase(void)
1489 : mfAborted(false),
1490 mCID(0),
1491 mEventSem(NIL_RTSEMEVENT),
1492 mRc(VINF_SUCCESS),
1493 mGuestRc(VINF_SUCCESS)
1494{
1495}
1496
1497GuestWaitEventBase::~GuestWaitEventBase(void)
1498{
1499 if (mEventSem != NIL_RTSEMEVENT)
1500 {
1501 RTSemEventDestroy(mEventSem);
1502 mEventSem = NIL_RTSEMEVENT;
1503 }
1504}
1505
1506/**
1507 * Initializes a wait event with a specific context ID (CID).
1508 *
1509 * @returns VBox status code.
1510 * @param uCID Context ID (CID) to initialize wait event with.
1511 */
1512int GuestWaitEventBase::Init(uint32_t uCID)
1513{
1514 mCID = uCID;
1515
1516 return RTSemEventCreate(&mEventSem);
1517}
1518
1519/**
1520 * Signals a wait event.
1521 *
1522 * @returns VBox status code.
1523 * @param rc Return code (rc) to set as wait result.
1524 * @param rcGuest Guest return code (rc) to set additionally, if rc is set to VERR_GSTCTL_GUEST_ERROR.
1525 * @param pPayload Additional wait event payload data set set on return. Optional.
1526 */
1527int GuestWaitEventBase::SignalInternal(int rc, int rcGuest,
1528 const GuestWaitEventPayload *pPayload)
1529{
1530 if (mfAborted)
1531 return VERR_CANCELLED;
1532
1533#ifdef VBOX_STRICT
1534 if (rc == VERR_GSTCTL_GUEST_ERROR)
1535 AssertMsg(RT_FAILURE(rcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", rcGuest));
1536 else
1537 AssertMsg(RT_SUCCESS(rcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", rcGuest));
1538#endif
1539
1540 int rc2;
1541 if (pPayload)
1542 rc2 = mPayload.CopyFromDeep(*pPayload);
1543 else
1544 rc2 = VINF_SUCCESS;
1545 if (RT_SUCCESS(rc2))
1546 {
1547 mRc = rc;
1548 mGuestRc = rcGuest;
1549
1550 rc2 = RTSemEventSignal(mEventSem);
1551 }
1552
1553 return rc2;
1554}
1555
1556/**
1557 * Waits for the event to get triggered. Will return success if the
1558 * wait was successufl (e.g. was being triggered), otherwise an error will be returned.
1559 *
1560 * @returns VBox status code.
1561 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1562 * the actual result.
1563 *
1564 * @param msTimeout Timeout (in ms) to wait.
1565 * Specifiy 0 to wait indefinitely.
1566 */
1567int GuestWaitEventBase::Wait(RTMSINTERVAL msTimeout)
1568{
1569 int rc = VINF_SUCCESS;
1570
1571 if (mfAborted)
1572 rc = VERR_CANCELLED;
1573
1574 if (RT_SUCCESS(rc))
1575 {
1576 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1577
1578 rc = RTSemEventWait(mEventSem, msTimeout ? msTimeout : RT_INDEFINITE_WAIT);
1579 if ( RT_SUCCESS(rc)
1580 && mfAborted)
1581 {
1582 rc = VERR_CANCELLED;
1583 }
1584
1585 if (RT_SUCCESS(rc))
1586 {
1587 /* If waiting succeeded, return the overall
1588 * result code. */
1589 rc = mRc;
1590 }
1591 }
1592
1593 return rc;
1594}
1595
1596GuestWaitEvent::GuestWaitEvent(void)
1597{
1598}
1599
1600GuestWaitEvent::~GuestWaitEvent(void)
1601{
1602
1603}
1604
1605/**
1606 * Cancels the event.
1607 */
1608int GuestWaitEvent::Cancel(void)
1609{
1610 if (mfAborted) /* Already aborted? */
1611 return VINF_SUCCESS;
1612
1613 mfAborted = true;
1614
1615#ifdef DEBUG_andy
1616 LogFlowThisFunc(("Cancelling %p ...\n"));
1617#endif
1618 return RTSemEventSignal(mEventSem);
1619}
1620
1621/**
1622 * Initializes a wait event with a given context ID (CID).
1623 *
1624 * @returns VBox status code.
1625 * @param uCID Context ID to initialize wait event with.
1626 */
1627int GuestWaitEvent::Init(uint32_t uCID)
1628{
1629 return GuestWaitEventBase::Init(uCID);
1630}
1631
1632/**
1633 * Initializes a wait event with a given context ID (CID) and a list of event types to wait for.
1634 *
1635 * @returns VBox status code.
1636 * @param uCID Context ID to initialize wait event with.
1637 * @param lstEvents List of event types to wait for this wait event to get signalled.
1638 */
1639int GuestWaitEvent::Init(uint32_t uCID, const GuestEventTypes &lstEvents)
1640{
1641 int rc = GuestWaitEventBase::Init(uCID);
1642 if (RT_SUCCESS(rc))
1643 {
1644 mEventTypes = lstEvents;
1645 }
1646
1647 return rc;
1648}
1649
1650/**
1651 * Signals the event.
1652 *
1653 * @return VBox status code.
1654 * @param pEvent Public IEvent to associate.
1655 * Optional.
1656 */
1657int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1658{
1659 if (pEvent)
1660 mEvent = pEvent;
1661
1662 return RTSemEventSignal(mEventSem);
1663}
1664
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use