VirtualBox

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

Last change on this file was 104178, checked in by vboxsync, 5 weeks ago

Guest Control:

  • Factored out most of the guest process stream handling of GuestToolboxStream (deprecated) into a new generic class GuestProcessOutputStream. That way we can make use of most of that code for other, non-toolbox related functionality.
  • Factoredd out most of the guest process wrapping functionality from GuestProcessToolbox into a new generic class GuestProcessWrapper. Ditto (see above).
  • Make more use of VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT to compile a lot less code if not defined. Toolbox handling is required for supporting older Guest Additions (< 7.1) though (so enabled by default).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.7 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 104178 2024-04-05 12:23:48Z 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
532GuestProcessOutputStream::GuestProcessOutputStream(void)
533 : m_cbMax(_32M)
534 , m_cbAllocated(0)
535 , m_cbUsed(0)
536 , m_offBuf(0)
537 , m_pbBuffer(NULL) { }
538
539GuestProcessOutputStream::~GuestProcessOutputStream(void)
540{
541 Destroy();
542}
543
544/**
545 * Adds data to the internal parser buffer. Useful if there
546 * are multiple rounds of adding data needed.
547 *
548 * @return VBox status code. Will return VERR_TOO_MUCH_DATA if the buffer's maximum (limit) has been reached.
549 * @param pbData Pointer to data to add.
550 * @param cbData Size (in bytes) of data to add.
551 */
552int GuestProcessOutputStream::AddData(const BYTE *pbData, size_t cbData)
553{
554 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
555 AssertReturn(cbData, VERR_INVALID_PARAMETER);
556
557 int vrc = VINF_SUCCESS;
558
559 /* Rewind the buffer if it's empty. */
560 size_t cbInBuf = m_cbUsed - m_offBuf;
561 bool const fAddToSet = cbInBuf == 0;
562 if (fAddToSet)
563 m_cbUsed = m_offBuf = 0;
564
565 /* Try and see if we can simply append the data. */
566 if (cbData + m_cbUsed <= m_cbAllocated)
567 {
568 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
569 m_cbUsed += cbData;
570 }
571 else
572 {
573 /* Move any buffered data to the front. */
574 cbInBuf = m_cbUsed - m_offBuf;
575 if (cbInBuf == 0)
576 m_cbUsed = m_offBuf = 0;
577 else if (m_offBuf) /* Do we have something to move? */
578 {
579 memmove(m_pbBuffer, &m_pbBuffer[m_offBuf], cbInBuf);
580 m_cbUsed = cbInBuf;
581 m_offBuf = 0;
582 }
583
584 /* Do we need to grow the buffer? */
585 if (cbData + m_cbUsed > m_cbAllocated)
586 {
587 size_t cbAlloc = m_cbUsed + cbData;
588 if (cbAlloc <= m_cbMax)
589 {
590 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
591 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
592 if (pvNew)
593 {
594 m_pbBuffer = (uint8_t *)pvNew;
595 m_cbAllocated = cbAlloc;
596 }
597 else
598 vrc = VERR_NO_MEMORY;
599 }
600 else
601 vrc = VERR_TOO_MUCH_DATA;
602 }
603
604 /* Finally, copy the data. */
605 if (RT_SUCCESS(vrc))
606 {
607 if (cbData + m_cbUsed <= m_cbAllocated)
608 {
609 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
610 m_cbUsed += cbData;
611 }
612 else
613 vrc = VERR_BUFFER_OVERFLOW;
614 }
615 }
616
617 return vrc;
618}
619
620/**
621 * Destroys the internal data buffer.
622 */
623void GuestProcessOutputStream::Destroy(void)
624{
625 if (m_pbBuffer)
626 {
627 RTMemFree(m_pbBuffer);
628 m_pbBuffer = NULL;
629 }
630
631 m_cbAllocated = 0;
632 m_cbUsed = 0;
633 m_offBuf = 0;
634}
635
636#ifdef DEBUG
637/**
638 * Dumps the raw guest process output to a file on the host.
639 * If the file on the host already exists, it will be overwritten.
640 *
641 * @param pszFile Absolute path to host file to dump the output to.
642 */
643void GuestProcessOutputStream::Dump(const char *pszFile)
644{
645 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
646 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuf, pszFile));
647
648 RTFILE hFile;
649 int vrc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
650 if (RT_SUCCESS(vrc))
651 {
652 vrc = RTFileWrite(hFile, m_pbBuffer, m_cbUsed, NULL /* pcbWritten */);
653 RTFileClose(hFile);
654 }
655}
656#endif /* DEBUG */
657
658///////////////////////////////////////////////////////////////////////////////
659
660#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
661/** @todo *NOT* thread safe yet! */
662/** @todo Add exception handling for STL stuff! */
663
664GuestToolboxStreamBlock::GuestToolboxStreamBlock(void)
665 : m_fComplete(false) { }
666
667GuestToolboxStreamBlock::~GuestToolboxStreamBlock()
668{
669 Clear();
670}
671
672/**
673 * Clears (destroys) the currently stored stream pairs.
674 */
675void GuestToolboxStreamBlock::Clear(void)
676{
677 m_fComplete = false;
678 m_mapPairs.clear();
679}
680
681#ifdef DEBUG
682/**
683 * Dumps the currently stored stream pairs to the (debug) log.
684 */
685void GuestToolboxStreamBlock::DumpToLog(void) const
686{
687 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items, fComplete=%RTbool):\n",
688 this, m_mapPairs.size(), m_fComplete));
689
690 for (GuestCtrlStreamPairMapIterConst it = m_mapPairs.begin();
691 it != m_mapPairs.end(); ++it)
692 {
693 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
694 }
695}
696#endif
697
698/**
699 * Returns a 64-bit signed integer of a specified key.
700 *
701 * @return VBox status code. VERR_NOT_FOUND if key was not found.
702 * @param pszKey Name of key to get the value for.
703 * @param piVal Pointer to value to return.
704 */
705int GuestToolboxStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
706{
707 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
708 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
709 const char *pszValue = GetString(pszKey);
710 if (pszValue)
711 {
712 *piVal = RTStrToInt64(pszValue);
713 return VINF_SUCCESS;
714 }
715 return VERR_NOT_FOUND;
716}
717
718/**
719 * Returns a 64-bit integer of a specified key.
720 *
721 * @return int64_t Value to return, 0 if not found / on failure.
722 * @param pszKey Name of key to get the value for.
723 */
724int64_t GuestToolboxStreamBlock::GetInt64(const char *pszKey) const
725{
726 int64_t iVal;
727 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
728 return iVal;
729 return 0;
730}
731
732/**
733 * Returns the current number of stream pairs.
734 *
735 * @return uint32_t Current number of stream pairs.
736 */
737size_t GuestToolboxStreamBlock::GetCount(void) const
738{
739 return m_mapPairs.size();
740}
741
742/**
743 * Gets the return code (name = "rc") of this stream block.
744 *
745 * @return VBox status code.
746 * @retval VERR_NOT_FOUND if the return code string ("rc") was not found.
747 * @param fSucceedIfNotFound When set to @c true, this reports back VINF_SUCCESS when the key ("rc") is not found.
748 * This can happen with some (older) IPRT-provided tools such as RTPathRmCmd(), which only outputs
749 * rc on failure but not on success. Defaults to @c false.
750 */
751int GuestToolboxStreamBlock::GetVrc(bool fSucceedIfNotFound /* = false */) const
752{
753 const char *pszValue = GetString("rc");
754 if (pszValue)
755 return RTStrToInt16(pszValue);
756 if (fSucceedIfNotFound)
757 return VINF_SUCCESS;
758 /** @todo We probably should have a dedicated error for that, VERR_GSTCTL_GUEST_TOOLBOX_whatever. */
759 return VERR_NOT_FOUND;
760}
761
762/**
763 * Returns a string value of a specified key.
764 *
765 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
766 * @param pszKey Name of key to get the value for.
767 */
768const char *GuestToolboxStreamBlock::GetString(const char *pszKey) const
769{
770 AssertPtrReturn(pszKey, NULL);
771
772 try
773 {
774 GuestCtrlStreamPairMapIterConst itPairs = m_mapPairs.find(pszKey);
775 if (itPairs != m_mapPairs.end())
776 return itPairs->second.mValue.c_str();
777 }
778 catch (const std::exception &ex)
779 {
780 RT_NOREF(ex);
781 }
782 return NULL;
783}
784
785/**
786 * Returns a 32-bit unsigned integer of a specified key.
787 *
788 * @return VBox status code. VERR_NOT_FOUND if key was not found.
789 * @param pszKey Name of key to get the value for.
790 * @param puVal Pointer to value to return.
791 */
792int GuestToolboxStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
793{
794 const char *pszValue = GetString(pszKey);
795 if (pszValue)
796 {
797 *puVal = RTStrToUInt32(pszValue);
798 return VINF_SUCCESS;
799 }
800 return VERR_NOT_FOUND;
801}
802
803/**
804 * Returns a 32-bit signed integer of a specified key.
805 *
806 * @returns 32-bit signed value
807 * @param pszKey Name of key to get the value for.
808 * @param iDefault The default to return on error if not found.
809 */
810int32_t GuestToolboxStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const
811{
812 const char *pszValue = GetString(pszKey);
813 if (pszValue)
814 {
815 int32_t iRet;
816 int vrc = RTStrToInt32Full(pszValue, 0, &iRet);
817 if (RT_SUCCESS(vrc))
818 return iRet;
819 ASSERT_GUEST_MSG_FAILED(("%s=%s\n", pszKey, pszValue));
820 }
821 return iDefault;
822}
823
824/**
825 * Returns a 32-bit unsigned integer of a specified key.
826 *
827 * @return uint32_t Value to return, 0 if not found / on failure.
828 * @param pszKey Name of key to get the value for.
829 * @param uDefault The default value to return.
830 */
831uint32_t GuestToolboxStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const
832{
833 uint32_t uVal;
834 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
835 return uVal;
836 return uDefault;
837}
838
839/**
840 * Sets a value to a key or deletes a key by setting a NULL value. Extended version.
841 *
842 * @return VBox status code.
843 * @param pszKey Key name to process.
844 * @param cwcKey Maximum characters of \a pszKey to process.
845 * @param pszValue Value to set. Set NULL for deleting the key.
846 * @param cwcValue Maximum characters of \a pszValue to process.
847 * @param fOverwrite Whether a key can be overwritten with a new value if it already exists. Will assert otherwise.
848 */
849int GuestToolboxStreamBlock::SetValueEx(const char *pszKey, size_t cwcKey, const char *pszValue, size_t cwcValue,
850 bool fOverwrite /* = false */)
851{
852 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
853 AssertReturn(cwcKey, VERR_INVALID_PARAMETER);
854
855 int vrc = VINF_SUCCESS;
856 try
857 {
858 Utf8Str const strKey(pszKey, cwcKey);
859
860 /* Take a shortcut and prevent crashes on some funny versions
861 * of STL if map is empty initially. */
862 if (!m_mapPairs.empty())
863 {
864 GuestCtrlStreamPairMapIter it = m_mapPairs.find(strKey);
865 if (it != m_mapPairs.end())
866 {
867 if (pszValue == NULL)
868 m_mapPairs.erase(it);
869 else if (!fOverwrite)
870 AssertMsgFailedReturn(("Key '%*s' already exists! Value is '%s'\n", cwcKey, pszKey, m_mapPairs[strKey].mValue.c_str()),
871 VERR_ALREADY_EXISTS);
872 }
873 }
874
875 if (pszValue)
876 {
877 GuestToolboxStreamValue val(pszValue, cwcValue);
878 Log3Func(("strKey='%s', strValue='%s'\n", strKey.c_str(), val.mValue.c_str()));
879 m_mapPairs[strKey] = val;
880 }
881 }
882 catch (const std::exception &)
883 {
884 /** @todo set vrc? */
885 }
886 return vrc;
887}
888
889/**
890 * Sets a value to a key or deletes a key by setting a NULL value.
891 *
892 * @return VBox status code.
893 * @param pszKey Key name to process.
894 * @param pszValue Value to set. Set NULL for deleting the key.
895 */
896int GuestToolboxStreamBlock::SetValue(const char *pszKey, const char *pszValue)
897{
898 return SetValueEx(pszKey, RTSTR_MAX, pszValue, RTSTR_MAX);
899}
900
901///////////////////////////////////////////////////////////////////////////////
902
903GuestToolboxStream::GuestToolboxStream(void)
904 : m_cBlocks(0)
905{
906}
907
908GuestToolboxStream::~GuestToolboxStream(void)
909{
910}
911
912/**
913 * Tries to parse the next upcoming pair block within the internal buffer.
914 *
915 * Parsing behavior:
916 * - A stream can contain one or multiple blocks and is terminated by four (4) "\0".
917 * - A block (or "object") contains one or multiple key=value pairs and is terminated with two (2) "\0".
918 * - Each key=value pair is terminated by a single (1) "\0".
919 *
920 * As new data can arrive at a later time eventually completing a pair / block / stream,
921 * the algorithm needs to be careful not intepreting its current data too early. So only skip termination
922 * sequences if we really know that the termination sequence is complete. See comments down below.
923 *
924 * No locking done.
925 *
926 * @return VBox status code.
927 * @retval VINF_EOF if the stream reached its end.
928 * @param streamBlock Reference to guest stream block to fill
929 */
930int GuestToolboxStream::ParseBlock(GuestToolboxStreamBlock &streamBlock)
931{
932 AssertMsgReturn(streamBlock.m_fComplete == false, ("Block object already marked as being completed\n"), VERR_WRONG_ORDER);
933
934 if ( !m_pbBuffer
935 || !m_cbUsed)
936 return VINF_EOF;
937
938 AssertReturn(m_offBuf <= m_cbUsed, VERR_INVALID_PARAMETER);
939 if (m_offBuf == m_cbUsed)
940 return VINF_EOF;
941
942 char * const pszStart = (char *)&m_pbBuffer[m_offBuf];
943
944 size_t cbLeftParsed = m_offBuf < m_cbUsed ? m_cbUsed - m_offBuf : 0;
945 size_t cbLeftLookAhead = cbLeftParsed;
946
947 char *pszLookAhead = pszStart; /* Look ahead pointer to count terminators. */
948 char *pszParsed = pszStart; /* Points to data considered as being parsed already. */
949
950 Log4Func(("Current @ %zu/%zu:\n%.*Rhxd\n", m_offBuf, m_cbUsed, RT_MIN(cbLeftParsed, _1K), pszStart));
951
952 size_t cTerm = 0;
953
954 /*
955 * We have to be careful when handling single terminators ('\0') here, as we might not know yet
956 * if it's part of a multi-terminator seqeuence.
957 *
958 * So handle and skip those *only* when we hit a non-terminator char again.
959 */
960 int vrc = VINF_SUCCESS;
961 while (cbLeftLookAhead)
962 {
963 /* Count consequtive terminators. */
964 if (*pszLookAhead == GUESTTOOLBOX_STRM_TERM)
965 {
966 cTerm++;
967 pszLookAhead++;
968 cbLeftLookAhead--;
969 continue;
970 }
971
972 pszParsed = pszLookAhead;
973 cbLeftParsed = cbLeftLookAhead;
974
975 /* We hit a non-terminator (again); now interpret where we are, and
976 * bail out if we need to. */
977 if (cTerm >= 2)
978 {
979 Log2Func(("Hit end of termination sequence (%zu)\n", cTerm));
980 break;
981 }
982
983 cTerm = 0; /* Reset consequtive counter. */
984
985 char * const pszPairEnd = RTStrEnd(pszParsed, cbLeftParsed);
986 if (!pszPairEnd) /* No zero terminator found (yet), try next time. */
987 break;
988
989 Log3Func(("Pair '%s' (%u)\n", pszParsed, strlen(pszParsed)));
990
991 Assert(pszPairEnd != pszParsed);
992 size_t const cbPair = (size_t)(pszPairEnd - pszParsed);
993 Assert(cbPair);
994 const char *pszSep = (const char *)memchr(pszParsed, '=', cbPair);
995 if (!pszSep) /* No separator found (yet), try next time. */
996 break;
997
998 /* Skip the separator so that pszSep points to the actual value. */
999 pszSep++;
1000
1001 char const * const pszKey = pszParsed;
1002 char const * const pszVal = pszSep;
1003
1004 vrc = streamBlock.SetValueEx(pszKey, pszSep - pszKey - 1, pszVal, pszPairEnd - pszVal);
1005 if (RT_FAILURE(vrc))
1006 return vrc;
1007
1008 if (cbPair >= cbLeftParsed)
1009 break;
1010
1011 /* Accounting for next iteration. */
1012 pszParsed = pszPairEnd;
1013 Assert(cbLeftParsed >= cbPair);
1014 cbLeftParsed -= cbPair;
1015
1016 pszLookAhead = pszPairEnd;
1017 cbLeftLookAhead = cbLeftParsed;
1018
1019 if (cbLeftParsed)
1020 Log4Func(("Next iteration @ %zu:\n%.*Rhxd\n", pszParsed - pszStart, cbLeftParsed, pszParsed));
1021 }
1022
1023 if (cbLeftParsed)
1024 Log4Func(("Done @ %zu:\n%.*Rhxd\n", pszParsed - pszStart, cbLeftParsed, pszParsed));
1025
1026 m_offBuf += pszParsed - pszStart; /* Only account really parsed content. */
1027 Assert(m_offBuf <= m_cbUsed);
1028
1029 /* Did we hit a block or stream termination sequence? */
1030 if (cTerm >= GUESTTOOLBOX_STRM_BLK_TERM_CNT)
1031 {
1032 if (!streamBlock.IsEmpty()) /* Only account and complete blocks which have values in it. */
1033 {
1034 m_cBlocks++;
1035 streamBlock.m_fComplete = true;
1036#ifdef DEBUG
1037 streamBlock.DumpToLog();
1038#endif
1039 }
1040
1041 if (cTerm >= GUESTTOOLBOX_STRM_TERM_CNT)
1042 {
1043 m_offBuf = m_cbUsed;
1044 vrc = VINF_EOF;
1045 }
1046 }
1047
1048 LogFlowThisFunc(("cbLeft=%zu, offBuffer=%zu / cbUsed=%zu, cBlocks=%zu, cTerm=%zu -> current block has %RU64 pairs (complete = %RTbool), rc=%Rrc\n",
1049 cbLeftParsed, m_offBuf, m_cbUsed, m_cBlocks, cTerm, streamBlock.GetCount(), streamBlock.IsComplete(), vrc));
1050
1051 return vrc;
1052}
1053#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1054
1055GuestBase::GuestBase(void)
1056 : mConsole(NULL)
1057 , mNextContextID(RTRandU32() % VBOX_GUESTCTRL_MAX_CONTEXTS)
1058{
1059}
1060
1061GuestBase::~GuestBase(void)
1062{
1063}
1064
1065/**
1066 * Separate initialization function for the base class.
1067 *
1068 * @returns VBox status code.
1069 */
1070int GuestBase::baseInit(void)
1071{
1072 int const vrc = RTCritSectInit(&mWaitEventCritSect);
1073 LogFlowFuncLeaveRC(vrc);
1074 return vrc;
1075}
1076
1077/**
1078 * Separate uninitialization function for the base class.
1079 */
1080void GuestBase::baseUninit(void)
1081{
1082 LogFlowThisFuncEnter();
1083
1084 /* Make sure to cancel any outstanding wait events. */
1085 int vrc2 = cancelWaitEvents();
1086 AssertRC(vrc2);
1087
1088 vrc2 = RTCritSectDelete(&mWaitEventCritSect);
1089 AssertRC(vrc2);
1090
1091 LogFlowFuncLeaveRC(vrc2);
1092 /* No return value. */
1093}
1094
1095/**
1096 * Cancels all outstanding wait events.
1097 *
1098 * @returns VBox status code.
1099 */
1100int GuestBase::cancelWaitEvents(void)
1101{
1102 LogFlowThisFuncEnter();
1103
1104 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1105 if (RT_SUCCESS(vrc))
1106 {
1107 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
1108 while (itEventGroups != mWaitEventGroups.end())
1109 {
1110 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
1111 while (itEvents != itEventGroups->second.end())
1112 {
1113 GuestWaitEvent *pEvent = itEvents->second;
1114 AssertPtr(pEvent);
1115
1116 /*
1117 * Just cancel the event, but don't remove it from the
1118 * wait events map. Don't delete it though, this (hopefully)
1119 * is done by the caller using unregisterWaitEvent().
1120 */
1121 int vrc2 = pEvent->Cancel();
1122 AssertRC(vrc2);
1123
1124 ++itEvents;
1125 }
1126
1127 ++itEventGroups;
1128 }
1129
1130 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1131 if (RT_SUCCESS(vrc))
1132 vrc = vrc2;
1133 }
1134
1135 LogFlowFuncLeaveRC(vrc);
1136 return vrc;
1137}
1138
1139/**
1140 * Handles generic messages not bound to a specific object type.
1141 *
1142 * @return VBox status code. VERR_NOT_FOUND if no handler has been found or VERR_NOT_SUPPORTED
1143 * if this class does not support the specified callback.
1144 * @param pCtxCb Host callback context.
1145 * @param pSvcCb Service callback data.
1146 */
1147int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1148{
1149 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1150
1151 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1152 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1153
1154 int vrc;
1155
1156 try
1157 {
1158 Log2Func(("uFunc=%RU32, cParms=%RU32\n", pCtxCb->uMessage, pSvcCb->mParms));
1159
1160 switch (pCtxCb->uMessage)
1161 {
1162 case GUEST_MSG_PROGRESS_UPDATE:
1163 vrc = VINF_SUCCESS;
1164 break;
1165
1166 case GUEST_MSG_REPLY:
1167 {
1168 if (pSvcCb->mParms >= 4)
1169 {
1170 int idx = 1; /* Current parameter index. */
1171 CALLBACKDATA_MSG_REPLY dataCb;
1172 /* pSvcCb->mpaParms[0] always contains the context ID. */
1173 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
1174 AssertRCReturn(vrc, vrc);
1175 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
1176 AssertRCReturn(vrc, vrc);
1177 vrc = HGCMSvcGetPv(&pSvcCb->mpaParms[idx++], &dataCb.pvPayload, &dataCb.cbPayload);
1178 AssertRCReturn(vrc, vrc);
1179
1180 try
1181 {
1182 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
1183 vrc = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
1184 }
1185 catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */
1186 {
1187 vrc = vrcEx;
1188 }
1189 }
1190 else
1191 vrc = VERR_INVALID_PARAMETER;
1192 break;
1193 }
1194
1195 default:
1196 vrc = VERR_NOT_SUPPORTED;
1197 break;
1198 }
1199 }
1200 catch (std::bad_alloc &)
1201 {
1202 vrc = VERR_NO_MEMORY;
1203 }
1204 catch (int vrcCatch)
1205 {
1206 vrc = vrcCatch;
1207 }
1208
1209 LogFlowFuncLeaveRC(vrc);
1210 return vrc;
1211}
1212
1213/**
1214 * Generates a context ID (CID) by incrementing the object's count.
1215 * A CID consists of a session ID, an object ID and a count.
1216 *
1217 * Note: This function does not guarantee that the returned CID is unique;
1218 * the caller has to take care of that and eventually retry.
1219 *
1220 * @returns VBox status code.
1221 * @param uSessionID Session ID to use for CID generation.
1222 * @param uObjectID Object ID to use for CID generation.
1223 * @param puContextID Where to store the generated CID on success.
1224 */
1225int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
1226{
1227 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
1228
1229 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
1230 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
1231 return VERR_INVALID_PARAMETER;
1232
1233 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
1234 uCount %= VBOX_GUESTCTRL_MAX_CONTEXTS;
1235
1236 uint32_t uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
1237
1238 *puContextID = uNewContextID;
1239
1240#if 0
1241 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
1242 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
1243#endif
1244 return VINF_SUCCESS;
1245}
1246
1247/**
1248 * Registers (creates) a new wait event based on a given session and object ID.
1249 *
1250 * From those IDs an unique context ID (CID) will be built, which only can be
1251 * around once at a time.
1252 *
1253 * @returns VBox status code.
1254 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
1255 * @param uSessionID Session ID to register wait event for.
1256 * @param uObjectID Object ID to register wait event for.
1257 * @param ppEvent Pointer to registered (created) wait event on success.
1258 * Must be destroyed with unregisterWaitEvent().
1259 */
1260int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID, GuestWaitEvent **ppEvent)
1261{
1262 GuestEventTypes eventTypesEmpty;
1263 return registerWaitEventEx(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
1264}
1265
1266/**
1267 * Creates and registers a new wait event object that waits on a set of events
1268 * related to a given object within the session.
1269 *
1270 * From the session ID and object ID a one-time unique context ID (CID) is built
1271 * for this wait object. Normally the CID is then passed to the guest along
1272 * with a request, and the guest passed the CID back with the reply. The
1273 * handler for the reply then emits a signal on the event type associated with
1274 * the reply, which includes signalling the object returned by this method and
1275 * the waking up the thread waiting on it.
1276 *
1277 * @returns VBox status code.
1278 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
1279 * @param uSessionID Session ID to register wait event for.
1280 * @param uObjectID Object ID to register wait event for.
1281 * @param lstEvents List of events to register the wait event for.
1282 * @param ppEvent Pointer to registered (created) wait event on success.
1283 * Must be destroyed with unregisterWaitEvent().
1284 */
1285int GuestBase::registerWaitEventEx(uint32_t uSessionID, uint32_t uObjectID, const GuestEventTypes &lstEvents,
1286 GuestWaitEvent **ppEvent)
1287{
1288 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1289
1290 uint32_t idContext;
1291 int vrc = generateContextID(uSessionID, uObjectID, &idContext);
1292 AssertRCReturn(vrc, vrc);
1293
1294 GuestWaitEvent *pEvent = new GuestWaitEvent();
1295 AssertPtrReturn(pEvent, VERR_NO_MEMORY);
1296
1297 vrc = pEvent->Init(idContext, lstEvents);
1298 AssertRCReturn(vrc, vrc);
1299
1300 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, idContext));
1301
1302 vrc = RTCritSectEnter(&mWaitEventCritSect);
1303 if (RT_SUCCESS(vrc))
1304 {
1305 /*
1306 * Check that we don't have any context ID collisions (should be very unlikely).
1307 *
1308 * The ASSUMPTION here is that mWaitEvents has all the same events as
1309 * mWaitEventGroups, so it suffices to check one of the two.
1310 */
1311 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1312 {
1313 uint32_t cTries = 0;
1314 do
1315 {
1316 vrc = generateContextID(uSessionID, uObjectID, &idContext);
1317 AssertRCBreak(vrc);
1318 LogFunc(("Found context ID duplicate; trying a different context ID: %#x\n", idContext));
1319 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1320 vrc = VERR_GSTCTL_MAX_CID_COUNT_REACHED;
1321 } while (RT_FAILURE_NP(vrc) && cTries++ < 10);
1322 }
1323 if (RT_SUCCESS(vrc))
1324 {
1325 /*
1326 * Insert event into matching event group. This is for faster per-group lookup of all events later.
1327 */
1328 uint32_t cInserts = 0;
1329 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1330 {
1331 GuestWaitEvents &eventGroup = mWaitEventGroups[*ItType];
1332 if (eventGroup.find(idContext) == eventGroup.end())
1333 {
1334 try
1335 {
1336 eventGroup.insert(std::pair<uint32_t, GuestWaitEvent *>(idContext, pEvent));
1337 cInserts++;
1338 }
1339 catch (std::bad_alloc &)
1340 {
1341 while (ItType != lstEvents.begin())
1342 {
1343 --ItType;
1344 mWaitEventGroups[*ItType].erase(idContext);
1345 }
1346 vrc = VERR_NO_MEMORY;
1347 break;
1348 }
1349 }
1350 else
1351 Assert(cInserts > 0); /* else: lstEvents has duplicate entries. */
1352 }
1353 if (RT_SUCCESS(vrc))
1354 {
1355 Assert(cInserts > 0 || lstEvents.size() == 0);
1356 RT_NOREF(cInserts);
1357
1358 /*
1359 * Register event in the regular event list.
1360 */
1361 try
1362 {
1363 mWaitEvents[idContext] = pEvent;
1364 }
1365 catch (std::bad_alloc &)
1366 {
1367 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1368 mWaitEventGroups[*ItType].erase(idContext);
1369 vrc = VERR_NO_MEMORY;
1370 }
1371 }
1372 }
1373
1374 RTCritSectLeave(&mWaitEventCritSect);
1375 }
1376 if (RT_SUCCESS(vrc))
1377 {
1378 *ppEvent = pEvent;
1379 return vrc;
1380 }
1381
1382 if (pEvent)
1383 delete pEvent;
1384
1385 return vrc;
1386}
1387
1388/**
1389 * Signals all wait events of a specific type (if found)
1390 * and notifies external events accordingly.
1391 *
1392 * @returns VBox status code.
1393 * @param aType Event type to signal.
1394 * @param aEvent Which external event to notify.
1395 */
1396int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
1397{
1398 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1399#ifdef DEBUG
1400 uint32_t cEvents = 0;
1401#endif
1402 if (RT_SUCCESS(vrc))
1403 {
1404 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
1405 if (itGroup != mWaitEventGroups.end())
1406 {
1407 /* Signal all events in the group, leaving the group empty afterwards. */
1408 GuestWaitEvents::iterator ItWaitEvt;
1409 while ((ItWaitEvt = itGroup->second.begin()) != itGroup->second.end())
1410 {
1411 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %#x: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1412 ItWaitEvt->second, aType, ItWaitEvt->first, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(ItWaitEvt->first),
1413 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(ItWaitEvt->first), VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(ItWaitEvt->first)));
1414
1415 int vrc2 = ItWaitEvt->second->SignalExternal(aEvent);
1416 AssertRC(vrc2);
1417
1418 /* Take down the wait event object details before we erase it from this list and invalid ItGrpEvt. */
1419 const GuestEventTypes &EvtTypes = ItWaitEvt->second->Types();
1420 uint32_t idContext = ItWaitEvt->first;
1421 itGroup->second.erase(ItWaitEvt);
1422
1423 for (GuestEventTypes::const_iterator ItType = EvtTypes.begin(); ItType != EvtTypes.end(); ++ItType)
1424 {
1425 GuestEventGroup::iterator EvtTypeGrp = mWaitEventGroups.find(*ItType);
1426 if (EvtTypeGrp != mWaitEventGroups.end())
1427 {
1428 ItWaitEvt = EvtTypeGrp->second.find(idContext);
1429 if (ItWaitEvt != EvtTypeGrp->second.end())
1430 {
1431 LogFlowThisFunc(("Removing event %p (CID %#x) from type %d group\n", ItWaitEvt->second, idContext, *ItType));
1432 EvtTypeGrp->second.erase(ItWaitEvt);
1433 LogFlowThisFunc(("%zu events left for type %d\n", EvtTypeGrp->second.size(), *ItType));
1434 Assert(EvtTypeGrp->second.find(idContext) == EvtTypeGrp->second.end()); /* no duplicates */
1435 }
1436 }
1437 }
1438 }
1439 }
1440
1441 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1442 if (RT_SUCCESS(vrc))
1443 vrc = vrc2;
1444 }
1445
1446#ifdef DEBUG
1447 LogFlowThisFunc(("Signalled %RU32 events, vrc=%Rrc\n", cEvents, vrc));
1448#endif
1449 return vrc;
1450}
1451
1452/**
1453 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1454 *
1455 * @returns VBox status code.
1456 * @param pCbCtx Pointer to host service callback context.
1457 * @param vrcGuest Guest return VBox status code to set.
1458 * @param pPayload Additional wait event payload data set set on return. Optional.
1459 */
1460int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, int vrcGuest, const GuestWaitEventPayload *pPayload)
1461{
1462 if (RT_SUCCESS(vrcGuest))
1463 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS, VINF_SUCCESS /* vrcGuest */, pPayload);
1464
1465 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR, vrcGuest, pPayload);
1466}
1467
1468/**
1469 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1470 * Extended version.
1471 *
1472 * @returns VBox status code.
1473 * @param pCbCtx Pointer to host service callback context.
1474 * @param vrc Return VBox status code to set as wait result.
1475 * @param vrcGuest Guest return VBox status code to set additionally, if
1476 * vrc is set to VERR_GSTCTL_GUEST_ERROR.
1477 * @param pPayload Additional wait event payload data set set on return. Optional.
1478 */
1479int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, int vrc, int vrcGuest,
1480 const GuestWaitEventPayload *pPayload)
1481{
1482 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1483 /* pPayload is optional. */
1484
1485 int vrc2 = RTCritSectEnter(&mWaitEventCritSect);
1486 if (RT_SUCCESS(vrc2))
1487 {
1488 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1489 if (itEvent != mWaitEvents.end())
1490 {
1491 LogFlowThisFunc(("Signalling event=%p (CID %RU32, vrc=%Rrc, vrcGuest=%Rrc, pPayload=%p) ...\n",
1492 itEvent->second, itEvent->first, vrc, vrcGuest, pPayload));
1493 GuestWaitEvent *pEvent = itEvent->second;
1494 AssertPtr(pEvent);
1495 vrc2 = pEvent->SignalInternal(vrc, vrcGuest, pPayload);
1496 }
1497 else
1498 vrc2 = VERR_NOT_FOUND;
1499
1500 int vrc3 = RTCritSectLeave(&mWaitEventCritSect);
1501 if (RT_SUCCESS(vrc2))
1502 vrc2 = vrc3;
1503 }
1504
1505 return vrc2;
1506}
1507
1508/**
1509 * Unregisters (deletes) a wait event.
1510 *
1511 * After successful unregistration the event will not be valid anymore.
1512 *
1513 * @returns VBox status code.
1514 * @param pWaitEvt Wait event to unregister (delete).
1515 */
1516int GuestBase::unregisterWaitEvent(GuestWaitEvent *pWaitEvt)
1517{
1518 if (!pWaitEvt) /* Nothing to unregister. */
1519 return VINF_SUCCESS;
1520
1521 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1522 if (RT_SUCCESS(vrc))
1523 {
1524 LogFlowThisFunc(("pWaitEvt=%p\n", pWaitEvt));
1525
1526/** @todo r=bird: One way of optimizing this would be to use the pointer
1527 * instead of the context ID as index into the groups, i.e. revert the value
1528 * pair for the GuestWaitEvents type.
1529 *
1530 * An even more efficent way, would be to not use sexy std::xxx containers for
1531 * the types, but iprt/list.h, as that would just be a RTListNodeRemove call for
1532 * each type w/o needing to iterate much at all. I.e. add a struct {
1533 * RTLISTNODE, GuestWaitEvent *pSelf} array to GuestWaitEvent, and change
1534 * GuestEventGroup to std::map<VBoxEventType_T, RTListAnchorClass>
1535 * (RTListAnchorClass == RTLISTANCHOR wrapper with a constructor)).
1536 *
1537 * P.S. the try/catch is now longer needed after I changed pWaitEvt->Types() to
1538 * return a const reference rather than a copy of the type list (and it think it
1539 * is safe to assume iterators are not hitting the heap). Copy vs reference is
1540 * an easy mistake to make in C++.
1541 *
1542 * P.P.S. The mWaitEventGroups optimization is probably just a lot of extra work
1543 * with little payoff.
1544 */
1545 try
1546 {
1547 /* Remove the event from all event type groups. */
1548 const GuestEventTypes &lstTypes = pWaitEvt->Types();
1549 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1550 itType != lstTypes.end(); ++itType)
1551 {
1552 /** @todo Slow O(n) lookup. Optimize this. */
1553 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1554 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1555 {
1556 if (itCurEvent->second == pWaitEvt)
1557 {
1558 mWaitEventGroups[(*itType)].erase(itCurEvent);
1559 break;
1560 }
1561 ++itCurEvent;
1562 }
1563 }
1564
1565 /* Remove the event from the general event list as well. */
1566 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pWaitEvt->ContextID());
1567
1568 Assert(itEvent != mWaitEvents.end());
1569 Assert(itEvent->second == pWaitEvt);
1570
1571 mWaitEvents.erase(itEvent);
1572
1573 delete pWaitEvt;
1574 pWaitEvt = NULL;
1575 }
1576 catch (const std::exception &ex)
1577 {
1578 RT_NOREF(ex);
1579 AssertFailedStmt(vrc = VERR_NOT_FOUND);
1580 }
1581
1582 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1583 if (RT_SUCCESS(vrc))
1584 vrc = vrc2;
1585 }
1586
1587 return vrc;
1588}
1589
1590/**
1591 * Waits for an already registered guest wait event.
1592 *
1593 * @return VBox status code.
1594 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1595 * the actual result.
1596 *
1597 * @param pWaitEvt Pointer to event to wait for.
1598 * @param msTimeout Timeout (in ms) for waiting.
1599 * @param pType Event type of following IEvent. Optional.
1600 * @param ppEvent Pointer to IEvent which got triggered for this event. Optional.
1601 */
1602int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEventType_T *pType, IEvent **ppEvent)
1603{
1604 AssertPtrReturn(pWaitEvt, VERR_INVALID_POINTER);
1605 /* pType is optional. */
1606 /* ppEvent is optional. */
1607
1608 int vrc = pWaitEvt->Wait(msTimeout);
1609 if (RT_SUCCESS(vrc))
1610 {
1611 const ComPtr<IEvent> pThisEvent = pWaitEvt->Event();
1612 if (pThisEvent.isNotNull()) /* Make sure that we actually have an event associated. */
1613 {
1614 if (pType)
1615 {
1616 HRESULT hrc = pThisEvent->COMGETTER(Type)(pType);
1617 if (FAILED(hrc))
1618 vrc = VERR_COM_UNEXPECTED;
1619 }
1620 if ( RT_SUCCESS(vrc)
1621 && ppEvent)
1622 pThisEvent.queryInterfaceTo(ppEvent);
1623
1624 unconst(pThisEvent).setNull();
1625 }
1626 }
1627
1628 return vrc;
1629}
1630
1631#ifndef VBOX_GUESTCTRL_TEST_CASE
1632/**
1633 * Convenience function to return a pre-formatted string using an action description and a guest error information.
1634 *
1635 * @returns Pre-formatted string with a user-friendly error string.
1636 * @param strAction Action of when the error occurred.
1637 * @param guestErrorInfo Related guest error information to use.
1638 */
1639/* static */ Utf8Str GuestBase::getErrorAsString(const Utf8Str& strAction, const GuestErrorInfo& guestErrorInfo)
1640{
1641 Assert(strAction.isNotEmpty());
1642 return Utf8StrFmt("%s: %s", strAction.c_str(), getErrorAsString(guestErrorInfo).c_str());
1643}
1644
1645/**
1646 * Returns a user-friendly error message from a given GuestErrorInfo object.
1647 *
1648 * @returns Error message string.
1649 * @param guestErrorInfo Guest error info to return error message for.
1650 */
1651/* static */ Utf8Str GuestBase::getErrorAsString(const GuestErrorInfo& guestErrorInfo)
1652{
1653 AssertMsg(RT_FAILURE(guestErrorInfo.getVrc()), ("Guest vrc does not indicate a failure\n"));
1654
1655 Utf8Str strErr;
1656
1657#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1658# define CASE_TOOL_ERROR(a_eType, a_strTool) \
1659 case a_eType: \
1660 { \
1661 strErr = GuestProcessToolbox::guestErrorToString(a_strTool, guestErrorInfo); \
1662 break; \
1663 }
1664#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1665
1666 switch (guestErrorInfo.getType())
1667 {
1668 case GuestErrorInfo::Type_Session:
1669 strErr = GuestSession::i_guestErrorToString(guestErrorInfo.getVrc());
1670 break;
1671
1672 case GuestErrorInfo::Type_Process:
1673 strErr = GuestProcess::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1674 break;
1675
1676 case GuestErrorInfo::Type_File:
1677 strErr = GuestFile::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1678 break;
1679
1680 case GuestErrorInfo::Type_Directory:
1681 strErr = GuestDirectory::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1682 break;
1683
1684 case GuestErrorInfo::Type_Fs:
1685 strErr = GuestFs::guestErrorToString(guestErrorInfo);
1686 break;
1687
1688#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1689 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolLs, VBOXSERVICE_TOOL_LS);
1690 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkDir, VBOXSERVICE_TOOL_MKDIR);
1691 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkTemp, VBOXSERVICE_TOOL_MKTEMP);
1692 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolRm, VBOXSERVICE_TOOL_RM);
1693 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolStat, VBOXSERVICE_TOOL_STAT);
1694#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1695 default:
1696 AssertMsgFailed(("Type not implemented (type=%RU32, vrc=%Rrc)\n", guestErrorInfo.getType(), guestErrorInfo.getVrc()));
1697 strErr = Utf8StrFmt("Unknown / Not implemented -- Please file a bug report (type=%RU32, vrc=%Rrc)\n",
1698 guestErrorInfo.getType(), guestErrorInfo.getVrc());
1699 break;
1700 }
1701
1702 return strErr;
1703}
1704
1705#endif /* VBOX_GUESTCTRL_TEST_CASE */
1706
1707/**
1708 * Converts RTFMODE to FsObjType_T.
1709 *
1710 * @return Converted FsObjType_T type.
1711 * @param fMode RTFMODE to convert.
1712 */
1713/* static */
1714FsObjType_T GuestBase::fileModeToFsObjType(RTFMODE fMode)
1715{
1716 if (RTFS_IS_FIFO(fMode)) return FsObjType_Fifo;
1717 else if (RTFS_IS_DEV_CHAR(fMode)) return FsObjType_DevChar;
1718 else if (RTFS_IS_DIRECTORY(fMode)) return FsObjType_Directory;
1719 else if (RTFS_IS_DEV_BLOCK(fMode)) return FsObjType_DevBlock;
1720 else if (RTFS_IS_FILE(fMode)) return FsObjType_File;
1721 else if (RTFS_IS_SYMLINK(fMode)) return FsObjType_Symlink;
1722 else if (RTFS_IS_SOCKET(fMode)) return FsObjType_Socket;
1723 else if (RTFS_IS_WHITEOUT(fMode)) return FsObjType_WhiteOut;
1724
1725 return FsObjType_Unknown;
1726}
1727
1728/**
1729 * Converts a FsObjType_T to a human-readable string.
1730 *
1731 * @returns Human-readable string of FsObjType_T.
1732 * @param enmType FsObjType_T to convert.
1733 */
1734/* static */
1735const char *GuestBase::fsObjTypeToStr(FsObjType_T enmType)
1736{
1737 switch (enmType)
1738 {
1739 case FsObjType_Directory: return "directory";
1740 case FsObjType_Symlink: return "symbolic link";
1741 case FsObjType_File: return "file";
1742 default: break;
1743 }
1744
1745 return "unknown";
1746}
1747
1748/**
1749 * Converts a PathStyle_T to a human-readable string.
1750 *
1751 * @returns Human-readable string of PathStyle_T.
1752 * @param enmPathStyle PathStyle_T to convert.
1753 */
1754/* static */
1755const char *GuestBase::pathStyleToStr(PathStyle_T enmPathStyle)
1756{
1757 switch (enmPathStyle)
1758 {
1759 case PathStyle_DOS: return "DOS";
1760 case PathStyle_UNIX: return "UNIX";
1761 case PathStyle_Unknown: return "Unknown";
1762 default: break;
1763 }
1764
1765 return "<invalid>";
1766}
1767
1768GuestObject::GuestObject(void)
1769 : mSession(NULL),
1770 mObjectID(0)
1771{
1772}
1773
1774GuestObject::~GuestObject(void)
1775{
1776}
1777
1778/**
1779 * Binds this guest (control) object to a specific guest (control) session.
1780 *
1781 * @returns VBox status code.
1782 * @param pConsole Pointer to console object to use.
1783 * @param pSession Pointer to session to bind this object to.
1784 * @param uObjectID Object ID for this object to use within that specific session.
1785 * Each object ID must be unique per session.
1786 */
1787int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1788{
1789 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1790 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1791
1792 mConsole = pConsole;
1793 mSession = pSession;
1794 mObjectID = uObjectID;
1795
1796 return VINF_SUCCESS;
1797}
1798
1799/**
1800 * Registers (creates) a new wait event.
1801 *
1802 * @returns VBox status code.
1803 * @param lstEvents List of events which the new wait event gets triggered at.
1804 * @param ppEvent Returns the new wait event on success.
1805 */
1806int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1807 GuestWaitEvent **ppEvent)
1808{
1809 AssertPtr(mSession);
1810 return GuestBase::registerWaitEventEx(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1811}
1812
1813/**
1814 * Sends a HGCM message to the guest (via the guest control host service).
1815 *
1816 * @returns VBox status code.
1817 * @param uMessage Message ID of message to send.
1818 * @param cParms Number of HGCM message parameters to send.
1819 * @param paParms Array of HGCM message parameters to send.
1820 */
1821int GuestObject::sendMessage(uint32_t uMessage, uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1822{
1823#ifndef VBOX_GUESTCTRL_TEST_CASE
1824 ComObjPtr<Console> pConsole = mConsole;
1825 Assert(!pConsole.isNull());
1826
1827 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1828
1829 /* Forward the information to the VMM device. */
1830 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1831 if (pVMMDev)
1832 {
1833 /* HACK ALERT! We extend the first parameter to 64-bit and use the
1834 two topmost bits for call destination information. */
1835 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
1836 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
1837 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_SESSION;
1838
1839 /* Make the call. */
1840 LogFlowThisFunc(("uMessage=%RU32, cParms=%RU32\n", uMessage, cParms));
1841 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, cParms, paParms);
1842 if (RT_FAILURE(vrc))
1843 {
1844 /** @todo What to do here? */
1845 }
1846 }
1847#else
1848 LogFlowThisFuncEnter();
1849
1850 /* Not needed within testcases. */
1851 RT_NOREF(uMessage, cParms, paParms);
1852 int vrc = VINF_SUCCESS;
1853#endif
1854 return vrc;
1855}
1856
1857GuestWaitEventBase::GuestWaitEventBase(void)
1858 : mfAborted(false),
1859 mCID(0),
1860 mEventSem(NIL_RTSEMEVENT),
1861 mVrc(VINF_SUCCESS),
1862 mGuestRc(VINF_SUCCESS)
1863{
1864}
1865
1866GuestWaitEventBase::~GuestWaitEventBase(void)
1867{
1868 if (mEventSem != NIL_RTSEMEVENT)
1869 {
1870 RTSemEventDestroy(mEventSem);
1871 mEventSem = NIL_RTSEMEVENT;
1872 }
1873}
1874
1875/**
1876 * Initializes a wait event with a specific context ID (CID).
1877 *
1878 * @returns VBox status code.
1879 * @param uCID Context ID (CID) to initialize wait event with.
1880 */
1881int GuestWaitEventBase::Init(uint32_t uCID)
1882{
1883 mCID = uCID;
1884
1885 return RTSemEventCreate(&mEventSem);
1886}
1887
1888/**
1889 * Signals a wait event.
1890 *
1891 * @returns VBox status code.
1892 * @param vrc Return VBox status code to set as wait result.
1893 * @param vrcGuest Guest return VBox status code to set additionally, if
1894 * @a vrc is set to VERR_GSTCTL_GUEST_ERROR.
1895 * @param pPayload Additional wait event payload data set set on return. Optional.
1896 */
1897int GuestWaitEventBase::SignalInternal(int vrc, int vrcGuest, const GuestWaitEventPayload *pPayload)
1898{
1899 if (mfAborted)
1900 return VERR_CANCELLED;
1901
1902#ifdef VBOX_STRICT
1903 if (vrc == VERR_GSTCTL_GUEST_ERROR)
1904 AssertMsg(RT_FAILURE(vrcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", vrcGuest));
1905 else
1906 AssertMsg(RT_SUCCESS(vrcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", vrcGuest));
1907#endif
1908
1909 int vrc2;
1910 if (pPayload)
1911 vrc2 = mPayload.CopyFromDeep(*pPayload);
1912 else
1913 vrc2 = VINF_SUCCESS;
1914 if (RT_SUCCESS(vrc2))
1915 {
1916 mVrc = vrc;
1917 mGuestRc = vrcGuest;
1918
1919 vrc2 = RTSemEventSignal(mEventSem);
1920 }
1921
1922 return vrc2;
1923}
1924
1925/**
1926 * Waits for the event to get triggered. Will return success if the
1927 * wait was successufl (e.g. was being triggered), otherwise an error will be returned.
1928 *
1929 * @returns VBox status code.
1930 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1931 * the actual result from the guest side.
1932 *
1933 * @param msTimeout Timeout (in ms) to wait.
1934 * Specifiy 0 to wait indefinitely.
1935 */
1936int GuestWaitEventBase::Wait(RTMSINTERVAL msTimeout)
1937{
1938 int vrc;
1939 if (!mfAborted)
1940 {
1941 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1942
1943 vrc = RTSemEventWait(mEventSem, msTimeout ? msTimeout : RT_INDEFINITE_WAIT);
1944 if ( RT_SUCCESS(vrc)
1945 && mfAborted)
1946 vrc = VERR_CANCELLED;
1947
1948 if (RT_SUCCESS(vrc))
1949 {
1950 /* If waiting succeeded, return the overall
1951 * result code. */
1952 vrc = mVrc;
1953 }
1954 }
1955 else
1956 vrc = VERR_CANCELLED;
1957 return vrc;
1958}
1959
1960GuestWaitEvent::GuestWaitEvent(void)
1961{
1962}
1963
1964GuestWaitEvent::~GuestWaitEvent(void)
1965{
1966
1967}
1968
1969/**
1970 * Cancels the event.
1971 */
1972int GuestWaitEvent::Cancel(void)
1973{
1974 if (mfAborted) /* Already aborted? */
1975 return VINF_SUCCESS;
1976
1977 mfAborted = true;
1978
1979#ifdef DEBUG_andy
1980 LogFlowThisFunc(("Cancelling %p ...\n"));
1981#endif
1982 return RTSemEventSignal(mEventSem);
1983}
1984
1985/**
1986 * Initializes a wait event with a given context ID (CID).
1987 *
1988 * @returns VBox status code.
1989 * @param uCID Context ID to initialize wait event with.
1990 */
1991int GuestWaitEvent::Init(uint32_t uCID)
1992{
1993 return GuestWaitEventBase::Init(uCID);
1994}
1995
1996/**
1997 * Initializes a wait event with a given context ID (CID) and a list of event types to wait for.
1998 *
1999 * @returns VBox status code.
2000 * @param uCID Context ID to initialize wait event with.
2001 * @param lstEvents List of event types to wait for this wait event to get signalled.
2002 */
2003int GuestWaitEvent::Init(uint32_t uCID, const GuestEventTypes &lstEvents)
2004{
2005 int vrc = GuestWaitEventBase::Init(uCID);
2006 if (RT_SUCCESS(vrc))
2007 mEventTypes = lstEvents;
2008
2009 return vrc;
2010}
2011
2012/**
2013 * Signals the event.
2014 *
2015 * @return VBox status code.
2016 * @param pEvent Public IEvent to associate.
2017 * Optional.
2018 */
2019int GuestWaitEvent::SignalExternal(IEvent *pEvent)
2020{
2021 if (pEvent)
2022 mEvent = pEvent;
2023
2024 return RTSemEventSignal(mEventSem);
2025}
2026
2027
2028//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2029// GuestPath
2030//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2031
2032/**
2033 * Builds a (final) destination path from a given source + destination path.
2034 *
2035 * This does not utilize any file system access whatsoever. Used for guest and host paths.
2036 *
2037 * @returns VBox status code.
2038 * @param strSrcPath Source path to build destination path for.
2039 * @param enmSrcPathStyle Path style the source path is in.
2040 * @param strDstPath Destination path to use for building the (final) destination path.
2041 * @param enmDstPathStyle Path style the destination path is in.
2042 *
2043 * @note See rules within the function.
2044 */
2045/* static */
2046int GuestPath::BuildDestinationPath(const Utf8Str &strSrcPath, PathStyle_T enmSrcPathStyle,
2047 Utf8Str &strDstPath, PathStyle_T enmDstPathStyle)
2048{
2049 /*
2050 * Rules:
2051 *
2052 * # source dest final dest remarks
2053 *
2054 * 1 /src/path1/ /dst/path2/ /dst/path2/<contents of path1> Just copies contents of <contents of path1>, not the path1 itself.
2055 * 2 /src/path1 /dst/path2/ /dst/path2/path1 Copies path1 into path2.
2056 * 3 /src/path1 /dst/path2 /dst/path2 Overwrites stuff from path2 with stuff from path1.
2057 * 4 Dotdot ("..") directories are forbidden for security reasons.
2058 */
2059 const char *pszSrcName = RTPathFilenameEx(strSrcPath.c_str(),
2060 enmSrcPathStyle == PathStyle_DOS
2061 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
2062
2063 const char *pszDstName = RTPathFilenameEx(strDstPath.c_str(),
2064 enmDstPathStyle == PathStyle_DOS
2065 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
2066
2067 if ( (!pszSrcName && !pszDstName) /* #1 */
2068 || ( pszSrcName && pszDstName)) /* #3 */
2069 {
2070 /* Note: Must have DirectoryFlag_CopyIntoExisting + FileFlag_NoReplace *not* set. */
2071 }
2072 else if (pszSrcName && !pszDstName) /* #2 */
2073 {
2074 if (!strDstPath.endsWith(PATH_STYLE_SEP_STR(enmDstPathStyle)))
2075 strDstPath += PATH_STYLE_SEP_STR(enmDstPathStyle);
2076 strDstPath += pszSrcName;
2077 }
2078
2079 /* Translate the built destination path to a path compatible with the destination. */
2080 int vrc = GuestPath::Translate(strDstPath, enmSrcPathStyle, enmDstPathStyle);
2081 if (RT_SUCCESS(vrc))
2082 {
2083 union
2084 {
2085 RTPATHPARSED Parsed;
2086 RTPATHSPLIT Split;
2087 uint8_t ab[4096];
2088 } u;
2089 vrc = RTPathParse(strDstPath.c_str(), &u.Parsed, sizeof(u),
2090 enmDstPathStyle == PathStyle_DOS ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
2091 if (RT_SUCCESS(vrc))
2092 {
2093 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS) /* #4 */
2094 vrc = VERR_INVALID_PARAMETER;
2095 }
2096 }
2097
2098 LogRel2(("Guest Control: Building destination path for '%s' (%s) -> '%s' (%s): %Rrc\n",
2099 strSrcPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2100 strDstPath.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2101
2102 return vrc;
2103}
2104
2105/**
2106 * Translates a path from a specific path style into another.
2107 *
2108 * @returns VBox status code.
2109 * @retval VERR_NOT_SUPPORTED if a conversion is not supported.
2110 * @retval VERR_NOT_IMPLEMENTED if path style conversion is not implemented yet.
2111 * @param strPath Path to translate. Will contain the translated path on success. UTF-8 only.
2112 * @param enmSrcPathStyle Source path style \a strPath is expected in.
2113 * @param enmDstPathStyle Destination path style to convert to.
2114 * @param fForce Whether to force the translation to the destination path style or not.
2115 *
2116 * @note This does NOT remove any trailing slashes and/or perform file system lookups!
2117 */
2118/* static */
2119int GuestPath::Translate(Utf8Str &strPath, PathStyle_T enmSrcPathStyle, PathStyle_T enmDstPathStyle, bool fForce /* = false */)
2120{
2121 if (strPath.isEmpty())
2122 return VINF_SUCCESS;
2123
2124 AssertReturn(RTStrIsValidEncoding(strPath.c_str()), VERR_INVALID_PARAMETER);
2125
2126 int vrc = VINF_SUCCESS;
2127
2128 Utf8Str strTranslated;
2129
2130 if ( ( enmSrcPathStyle == PathStyle_DOS
2131 && enmDstPathStyle == PathStyle_UNIX)
2132 || (fForce && enmDstPathStyle == PathStyle_UNIX))
2133 {
2134 strTranslated = strPath;
2135 RTPathChangeToUnixSlashes(strTranslated.mutableRaw(), true /* fForce */);
2136 }
2137 else if ( ( enmSrcPathStyle == PathStyle_UNIX
2138 && enmDstPathStyle == PathStyle_DOS)
2139 || (fForce && enmDstPathStyle == PathStyle_DOS))
2140
2141 {
2142 strTranslated = strPath;
2143 RTPathChangeToDosSlashes(strTranslated.mutableRaw(), true /* fForce */);
2144 }
2145
2146 if ( strTranslated.isEmpty() /* Not forced. */
2147 && enmSrcPathStyle == enmDstPathStyle)
2148 {
2149 strTranslated = strPath;
2150 }
2151
2152 if (RT_FAILURE(vrc))
2153 {
2154 LogRel(("Guest Control: Translating path '%s' (%s) -> '%s' (%s) failed, vrc=%Rrc\n",
2155 strPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2156 strTranslated.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2157 return vrc;
2158 }
2159
2160 /* Cleanup. */
2161 const char *psz = strTranslated.mutableRaw();
2162 size_t const cch = strTranslated.length();
2163 size_t off = 0;
2164 while (off < cch)
2165 {
2166 if (off + 1 > cch)
2167 break;
2168 /* Remove double back slashes (DOS only). */
2169 if ( enmDstPathStyle == PathStyle_DOS
2170 && psz[off] == '\\'
2171 && psz[off + 1] == '\\')
2172 {
2173 strTranslated.erase(off + 1, 1);
2174 off++;
2175 }
2176 /* Remove double forward slashes (UNIX only). */
2177 if ( enmDstPathStyle == PathStyle_UNIX
2178 && psz[off] == '/'
2179 && psz[off + 1] == '/')
2180 {
2181 strTranslated.erase(off + 1, 1);
2182 off++;
2183 }
2184 off++;
2185 }
2186
2187 /* Note: Do not trim() paths here, as technically it's possible to create paths with trailing spaces. */
2188
2189 strTranslated.jolt();
2190
2191 LogRel2(("Guest Control: Translating '%s' (%s) -> '%s' (%s): %Rrc\n",
2192 strPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2193 strTranslated.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2194
2195 if (RT_SUCCESS(vrc))
2196 strPath = strTranslated;
2197
2198 return vrc;
2199}
2200
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use