VirtualBox

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

Last change on this file since 94521 was 94359, checked in by vboxsync, 2 years ago

Main/GuestProcessStream::ParseBlock: Be more careful. Added a TODO.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use