VirtualBox

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

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

Main/GuestCtrlPrivate.cpp: Don't use doxygen-style comments inside function for non-doxygen commenting (doxgyen got confused by non-html tags). Added @todo about confusing code in GuestPath::Translate wrt 'escape sequnces' in paths. bugref:10286

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