VirtualBox

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

Last change on this file since 97749 was 97563, checked in by vboxsync, 2 years ago

Guest Control/Main: Comment nits. bugref:10286

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

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