VirtualBox

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

Last change on this file since 99393 was 99393, checked in by vboxsync, 20 months ago

Guest Control: Completely revamped / overhauled the (now legacy) stream output parsing code and added lots of documentation to it. This way it should be a lot clearer what it's supposed to be doing. Also, this now should fix some nasty bugs in that area we had in the past especially with some Linux guests (i.e. OL6), which sometimes send output data in a very unsteady manner. Also overhauled the testcases while at it [build fix].

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