VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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