VirtualBox

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

Last change on this file since 98284 was 98278, checked in by vboxsync, 23 months ago

Main/src-client: Some more rc -> hrc/vrc stuff found by grep. A build fix. bugref:10223

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