VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp@ 99134

Last change on this file since 99134 was 99120, checked in by vboxsync, 23 months ago

Guest Control: Added ability of specifying an optional current working directory to started guest processes. This needs Guest Additions which support this. bugref:8053

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 133.9 KB
Line 
1/* $Id: VBoxServiceControlSession.cpp 99120 2023-03-22 17:30:14Z vboxsync $ */
2/** @file
3 * VBoxServiceControlSession - Guest session handling. Also handles the spawned session processes.
4 */
5
6/*
7 * Copyright (C) 2013-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#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/dir.h>
35#include <iprt/env.h>
36#include <iprt/file.h>
37#include <iprt/getopt.h>
38#include <iprt/handle.h>
39#include <iprt/mem.h>
40#include <iprt/message.h>
41#include <iprt/path.h>
42#include <iprt/pipe.h>
43#include <iprt/poll.h>
44#include <iprt/process.h>
45#include <iprt/rand.h>
46#include <iprt/system.h> /* For RTShutdown. */
47
48#include "VBoxServiceInternal.h"
49#include "VBoxServiceUtils.h"
50#include "VBoxServiceControl.h"
51
52using namespace guestControl;
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58/** Generic option indices for session spawn arguments. */
59enum
60{
61 VBOXSERVICESESSIONOPT_FIRST = 1000, /* For initialization. */
62 VBOXSERVICESESSIONOPT_DOMAIN,
63#ifdef DEBUG
64 VBOXSERVICESESSIONOPT_DUMP_STDOUT,
65 VBOXSERVICESESSIONOPT_DUMP_STDERR,
66#endif
67 VBOXSERVICESESSIONOPT_LOG_FILE,
68 VBOXSERVICESESSIONOPT_USERNAME,
69 VBOXSERVICESESSIONOPT_SESSION_ID,
70 VBOXSERVICESESSIONOPT_SESSION_PROTO,
71 VBOXSERVICESESSIONOPT_THREAD_ID
72};
73
74
75static int vgsvcGstCtrlSessionCleanupProcesses(const PVBOXSERVICECTRLSESSION pSession);
76static int vgsvcGstCtrlSessionProcessRemoveInternal(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess);
77
78
79/**
80 * Helper that grows the scratch buffer.
81 * @returns Success indicator.
82 */
83static bool vgsvcGstCtrlSessionGrowScratchBuf(void **ppvScratchBuf, uint32_t *pcbScratchBuf, uint32_t cbMinBuf)
84{
85 uint32_t cbNew = *pcbScratchBuf * 2;
86 if ( cbNew <= VMMDEV_MAX_HGCM_DATA_SIZE
87 && cbMinBuf <= VMMDEV_MAX_HGCM_DATA_SIZE)
88 {
89 while (cbMinBuf > cbNew)
90 cbNew *= 2;
91 void *pvNew = RTMemRealloc(*ppvScratchBuf, cbNew);
92 if (pvNew)
93 {
94 *ppvScratchBuf = pvNew;
95 *pcbScratchBuf = cbNew;
96 return true;
97 }
98 }
99 return false;
100}
101
102
103#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
104/**
105 * Free's a guest directory entry.
106 *
107 * @returns VBox status code.
108 * @param pDir Directory entry to free.
109 * The pointer will be invalid on success.
110 */
111static int vgsvcGstCtrlSessionDirFree(PVBOXSERVICECTRLDIR pDir)
112{
113 if (!pDir)
114 return VINF_SUCCESS;
115
116 RTMemFree(pDir->pDirEntryEx);
117
118 int rc;
119 if (pDir->hDir != NIL_RTDIR)
120 {
121 rc = RTDirClose(pDir->hDir);
122 pDir->hDir = NIL_RTDIR;
123 }
124 else
125 rc = VINF_SUCCESS;
126
127 if (RT_SUCCESS(rc))
128 {
129 RTStrFree(pDir->pszPathAbs);
130 RTListNodeRemove(&pDir->Node);
131 RTMemFree(pDir);
132 }
133
134 return rc;
135}
136
137
138/**
139 * Acquires an internal guest directory.
140 *
141 * Must be released via vgsvcGstCtrlSessionDirRelease().
142 *
143 * @returns VBox status code.
144 * @param pSession Guest control session to acquire guest directory for.
145 * @param uHandle Handle of directory to acquire.
146 *
147 * @note No locking done yet.
148 */
149static PVBOXSERVICECTRLDIR vgsvcGstCtrlSessionDirAcquire(const PVBOXSERVICECTRLSESSION pSession, uint32_t uHandle)
150{
151 AssertPtrReturn(pSession, NULL);
152
153 /** @todo Use a map later! */
154 PVBOXSERVICECTRLDIR pDirCur;
155 RTListForEach(&pSession->lstDirs, pDirCur, VBOXSERVICECTRLDIR, Node)
156 {
157 if (pDirCur->uHandle == uHandle)
158 return pDirCur;
159 }
160
161 return NULL;
162}
163
164
165/**
166 * Releases a formerly acquired guest directory.
167 *
168 * @param pDir Directory to release.
169 */
170static void vgsvcGstCtrlSessionDirRelease(PVBOXSERVICECTRLDIR pDir)
171{
172 RT_NOREF(pDir);
173}
174#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
175
176
177static int vgsvcGstCtrlSessionFileFree(PVBOXSERVICECTRLFILE pFile)
178{
179 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
180
181 int rc = RTFileClose(pFile->hFile);
182 if (RT_SUCCESS(rc))
183 {
184 RTStrFree(pFile->pszName);
185
186 /* Remove file entry in any case. */
187 RTListNodeRemove(&pFile->Node);
188 /* Destroy this object. */
189 RTMemFree(pFile);
190 }
191
192 return rc;
193}
194
195
196/** @todo No locking done yet! */
197static PVBOXSERVICECTRLFILE vgsvcGstCtrlSessionFileGetLocked(const PVBOXSERVICECTRLSESSION pSession, uint32_t uHandle)
198{
199 AssertPtrReturn(pSession, NULL);
200
201 /** @todo Use a map later! */
202 PVBOXSERVICECTRLFILE pFileCur;
203 RTListForEach(&pSession->lstFiles, pFileCur, VBOXSERVICECTRLFILE, Node)
204 {
205 if (pFileCur->uHandle == uHandle)
206 return pFileCur;
207 }
208
209 return NULL;
210}
211
212
213/**
214 * Recursion worker for vgsvcGstCtrlSessionHandleDirRemove.
215 * Only (recursively) removes directory structures which are not empty. Will fail if not empty.
216 *
217 * @returns IPRT status code.
218 * @param pszDir The directory buffer, RTPATH_MAX in length.
219 * Contains the abs path to the directory to
220 * recurse into. Trailing slash.
221 * @param cchDir The length of the directory we're recursing into,
222 * including the trailing slash.
223 * @param pDirEntry The dir entry buffer. (Shared to save stack.)
224 */
225static int vgsvcGstCtrlSessionHandleDirRemoveSub(char *pszDir, size_t cchDir, PRTDIRENTRY pDirEntry)
226{
227 RTDIR hDir;
228 int rc = RTDirOpen(&hDir, pszDir);
229 if (RT_FAILURE(rc))
230 {
231 /* Ignore non-existing directories like RTDirRemoveRecursive does: */
232 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
233 return VINF_SUCCESS;
234 return rc;
235 }
236
237 for (;;)
238 {
239 rc = RTDirRead(hDir, pDirEntry, NULL);
240 if (RT_FAILURE(rc))
241 {
242 if (rc == VERR_NO_MORE_FILES)
243 rc = VINF_SUCCESS;
244 break;
245 }
246
247 if (!RTDirEntryIsStdDotLink(pDirEntry))
248 {
249 /* Construct the full name of the entry. */
250 if (cchDir + pDirEntry->cbName + 1 /* dir slash */ < RTPATH_MAX)
251 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
252 else
253 {
254 rc = VERR_FILENAME_TOO_LONG;
255 break;
256 }
257
258 /* Make sure we've got the entry type. */
259 if (pDirEntry->enmType == RTDIRENTRYTYPE_UNKNOWN)
260 RTDirQueryUnknownType(pszDir, false /*fFollowSymlinks*/, &pDirEntry->enmType);
261
262 /* Recurse into subdirs and remove them: */
263 if (pDirEntry->enmType == RTDIRENTRYTYPE_DIRECTORY)
264 {
265 size_t cchSubDir = cchDir + pDirEntry->cbName;
266 pszDir[cchSubDir++] = RTPATH_SLASH;
267 pszDir[cchSubDir] = '\0';
268 rc = vgsvcGstCtrlSessionHandleDirRemoveSub(pszDir, cchSubDir, pDirEntry);
269 if (RT_SUCCESS(rc))
270 {
271 pszDir[cchSubDir] = '\0';
272 rc = RTDirRemove(pszDir);
273 if (RT_FAILURE(rc))
274 break;
275 }
276 else
277 break;
278 }
279 /* Not a subdirectory - fail: */
280 else
281 {
282 rc = VERR_DIR_NOT_EMPTY;
283 break;
284 }
285 }
286 }
287
288 RTDirClose(hDir);
289 return rc;
290}
291
292
293static int vgsvcGstCtrlSessionHandleDirRemove(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
294{
295 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
296 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
297
298 /*
299 * Retrieve the message.
300 */
301 char szDir[RTPATH_MAX];
302 uint32_t fFlags; /* DIRREMOVE_FLAG_XXX */
303 int rc = VbglR3GuestCtrlDirGetRemove(pHostCtx, szDir, sizeof(szDir), &fFlags);
304 if (RT_SUCCESS(rc))
305 {
306 /*
307 * Do some validating before executing the job.
308 */
309 if (!(fFlags & ~DIRREMOVEREC_FLAG_VALID_MASK))
310 {
311 if (fFlags & DIRREMOVEREC_FLAG_RECURSIVE)
312 {
313 if (fFlags & (DIRREMOVEREC_FLAG_CONTENT_AND_DIR | DIRREMOVEREC_FLAG_CONTENT_ONLY))
314 {
315 uint32_t fFlagsRemRec = fFlags & DIRREMOVEREC_FLAG_CONTENT_AND_DIR
316 ? RTDIRRMREC_F_CONTENT_AND_DIR : RTDIRRMREC_F_CONTENT_ONLY;
317 rc = RTDirRemoveRecursive(szDir, fFlagsRemRec);
318 }
319 else /* Only remove empty directory structures. Will fail if non-empty. */
320 {
321 RTDIRENTRY DirEntry;
322 RTPathEnsureTrailingSeparator(szDir, sizeof(szDir));
323 rc = vgsvcGstCtrlSessionHandleDirRemoveSub(szDir, strlen(szDir), &DirEntry);
324 }
325 VGSvcVerbose(4, "[Dir %s]: rmdir /s (%#x) -> rc=%Rrc\n", szDir, fFlags, rc);
326 }
327 else
328 {
329 /* Only delete directory if not empty. */
330 rc = RTDirRemove(szDir);
331 VGSvcVerbose(4, "[Dir %s]: rmdir (%#x), rc=%Rrc\n", szDir, fFlags, rc);
332 }
333 }
334 else
335 {
336 VGSvcError("[Dir %s]: Unsupported flags: %#x (all %#x)\n", szDir, (fFlags & ~DIRREMOVEREC_FLAG_VALID_MASK), fFlags);
337 rc = VERR_NOT_SUPPORTED;
338 }
339
340 /*
341 * Report result back to host.
342 */
343 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
344 if (RT_FAILURE(rc2))
345 {
346 VGSvcError("[Dir %s]: Failed to report removing status, rc=%Rrc\n", szDir, rc2);
347 if (RT_SUCCESS(rc))
348 rc = rc2;
349 }
350 }
351 else
352 {
353 VGSvcError("Error fetching parameters for rmdir operation: %Rrc\n", rc);
354 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
355 }
356
357 VGSvcVerbose(6, "Removing directory '%s' returned rc=%Rrc\n", szDir, rc);
358 return rc;
359}
360
361
362static int vgsvcGstCtrlSessionHandleFileOpen(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
363{
364 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
365 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
366
367 /*
368 * Retrieve the message.
369 */
370 char szFile[RTPATH_MAX];
371 char szAccess[64];
372 char szDisposition[64];
373 char szSharing[64];
374 uint32_t uCreationMode = 0;
375 uint64_t offOpen = 0;
376 uint32_t uHandle = 0;
377 int rc = VbglR3GuestCtrlFileGetOpen(pHostCtx,
378 /* File to open. */
379 szFile, sizeof(szFile),
380 /* Open mode. */
381 szAccess, sizeof(szAccess),
382 /* Disposition. */
383 szDisposition, sizeof(szDisposition),
384 /* Sharing. */
385 szSharing, sizeof(szSharing),
386 /* Creation mode. */
387 &uCreationMode,
388 /* Offset. */
389 &offOpen);
390 VGSvcVerbose(4, "[File %s]: szAccess=%s, szDisposition=%s, szSharing=%s, offOpen=%RU64, rc=%Rrc\n",
391 szFile, szAccess, szDisposition, szSharing, offOpen, rc);
392 if (RT_SUCCESS(rc))
393 {
394 PVBOXSERVICECTRLFILE pFile = (PVBOXSERVICECTRLFILE)RTMemAllocZ(sizeof(VBOXSERVICECTRLFILE));
395 if (pFile)
396 {
397 pFile->hFile = NIL_RTFILE; /* Not zero or NULL! */
398 if (szFile[0])
399 {
400 pFile->pszName = RTStrDup(szFile);
401 if (!pFile->pszName)
402 rc = VERR_NO_MEMORY;
403/** @todo
404 * Implement szSharing!
405 */
406 uint64_t fFlags;
407 if (RT_SUCCESS(rc))
408 {
409 rc = RTFileModeToFlagsEx(szAccess, szDisposition, NULL /* pszSharing, not used yet */, &fFlags);
410 VGSvcVerbose(4, "[File %s] Opening with fFlags=%#RX64 -> rc=%Rrc\n", pFile->pszName, fFlags, rc);
411 }
412
413 if (RT_SUCCESS(rc))
414 {
415 fFlags |= (uCreationMode << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
416 /* If we're opening a file in read-only mode, strip truncation mode.
417 * rtFileRecalcAndValidateFlags() will validate it anyway, but avoid asserting in debug builds. */
418 if (fFlags & RTFILE_O_READ)
419 fFlags &= ~RTFILE_O_TRUNCATE;
420 rc = RTFileOpen(&pFile->hFile, pFile->pszName, fFlags);
421 if (RT_SUCCESS(rc))
422 {
423 RTFSOBJINFO objInfo;
424 rc = RTFileQueryInfo(pFile->hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
425 if (RT_SUCCESS(rc))
426 {
427 /* Make sure that we only open stuff we really support.
428 * Only POSIX / UNIX we could open stuff like directories and sockets as well. */
429 if ( RT_LIKELY(RTFS_IS_FILE(objInfo.Attr.fMode))
430 || RTFS_IS_SYMLINK(objInfo.Attr.fMode))
431 {
432 /* Seeking is optional. However, the whole operation
433 * will fail if we don't succeed seeking to the wanted position. */
434 if (offOpen)
435 rc = RTFileSeek(pFile->hFile, (int64_t)offOpen, RTFILE_SEEK_BEGIN, NULL /* Current offset */);
436 if (RT_SUCCESS(rc))
437 {
438 /*
439 * Succeeded!
440 */
441 uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID);
442 pFile->uHandle = uHandle;
443 pFile->fOpen = fFlags;
444 RTListAppend(&pSession->lstFiles, &pFile->Node);
445 VGSvcVerbose(2, "[File %s] Opened (ID=%RU32)\n", pFile->pszName, pFile->uHandle);
446 }
447 else
448 VGSvcError("[File %s] Seeking to offset %RU64 failed: rc=%Rrc\n", pFile->pszName, offOpen, rc);
449 }
450 else
451 {
452 VGSvcError("[File %s] Unsupported mode %#x\n", pFile->pszName, objInfo.Attr.fMode);
453 rc = VERR_NOT_SUPPORTED;
454 }
455 }
456 else
457 VGSvcError("[File %s] Getting mode failed with rc=%Rrc\n", pFile->pszName, rc);
458 }
459 else
460 VGSvcError("[File %s] Opening failed with rc=%Rrc\n", pFile->pszName, rc);
461 }
462 }
463 else
464 {
465 VGSvcError("Opening file failed: Empty filename!\n");
466 rc = VERR_INVALID_NAME;
467 }
468
469 /* clean up if we failed. */
470 if (RT_FAILURE(rc))
471 {
472 RTStrFree(pFile->pszName);
473 if (pFile->hFile != NIL_RTFILE)
474 RTFileClose(pFile->hFile);
475 RTMemFree(pFile);
476 }
477 }
478 else
479 rc = VERR_NO_MEMORY;
480
481 /*
482 * Report result back to host.
483 */
484 int rc2 = VbglR3GuestCtrlFileCbOpen(pHostCtx, rc, uHandle);
485 if (RT_FAILURE(rc2))
486 {
487 VGSvcError("[File %s]: Failed to report file open status, rc=%Rrc\n", szFile, rc2);
488 if (RT_SUCCESS(rc))
489 rc = rc2;
490 }
491 }
492 else
493 {
494 VGSvcError("Error fetching parameters for open file operation: %Rrc\n", rc);
495 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
496 }
497
498 VGSvcVerbose(4, "[File %s] Opening (open mode='%s', disposition='%s', creation mode=0x%x) returned rc=%Rrc\n",
499 szFile, szAccess, szDisposition, uCreationMode, rc);
500 return rc;
501}
502
503
504static int vgsvcGstCtrlSessionHandleFileClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
505{
506 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
507 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
508
509 /*
510 * Retrieve the message.
511 */
512 uint32_t uHandle = 0;
513 int rc = VbglR3GuestCtrlFileGetClose(pHostCtx, &uHandle /* File handle to close */);
514 if (RT_SUCCESS(rc))
515 {
516 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
517 if (pFile)
518 {
519 VGSvcVerbose(2, "[File %s] Closing (handle=%RU32)\n", pFile ? pFile->pszName : "<Not found>", uHandle);
520 rc = vgsvcGstCtrlSessionFileFree(pFile);
521 }
522 else
523 {
524 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
525 rc = VERR_NOT_FOUND;
526 }
527
528 /*
529 * Report result back to host.
530 */
531 int rc2 = VbglR3GuestCtrlFileCbClose(pHostCtx, rc);
532 if (RT_FAILURE(rc2))
533 {
534 VGSvcError("Failed to report file close status, rc=%Rrc\n", rc2);
535 if (RT_SUCCESS(rc))
536 rc = rc2;
537 }
538 }
539 else
540 {
541 VGSvcError("Error fetching parameters for close file operation: %Rrc\n", rc);
542 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
543 }
544 return rc;
545}
546
547
548static int vgsvcGstCtrlSessionHandleFileRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
549 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
550{
551 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
552 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
553
554 /*
555 * Retrieve the request.
556 */
557 uint32_t uHandle = 0;
558 uint32_t cbToRead;
559 int rc = VbglR3GuestCtrlFileGetRead(pHostCtx, &uHandle, &cbToRead);
560 if (RT_SUCCESS(rc))
561 {
562 /*
563 * Locate the file and do the reading.
564 *
565 * If the request is larger than our scratch buffer, try grow it - just
566 * ignore failure as the host better respect our buffer limits.
567 */
568 uint32_t offNew = 0;
569 size_t cbRead = 0;
570 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
571 if (pFile)
572 {
573 if (*pcbScratchBuf < cbToRead)
574 vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToRead);
575
576 rc = RTFileRead(pFile->hFile, *ppvScratchBuf, RT_MIN(cbToRead, *pcbScratchBuf), &cbRead);
577 offNew = (int64_t)RTFileTell(pFile->hFile);
578 VGSvcVerbose(5, "[File %s] Read %zu/%RU32 bytes, rc=%Rrc, offNew=%RI64\n", pFile->pszName, cbRead, cbToRead, rc, offNew);
579 }
580 else
581 {
582 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
583 rc = VERR_NOT_FOUND;
584 }
585
586 /*
587 * Report result and data back to the host.
588 */
589 int rc2;
590 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
591 rc2 = VbglR3GuestCtrlFileCbReadOffset(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead, offNew);
592 else
593 rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead);
594 if (RT_FAILURE(rc2))
595 {
596 VGSvcError("Failed to report file read status, rc=%Rrc\n", rc2);
597 if (RT_SUCCESS(rc))
598 rc = rc2;
599 }
600 }
601 else
602 {
603 VGSvcError("Error fetching parameters for file read operation: %Rrc\n", rc);
604 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
605 }
606 return rc;
607}
608
609
610static int vgsvcGstCtrlSessionHandleFileReadAt(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
611 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
612{
613 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
614 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
615
616 /*
617 * Retrieve the request.
618 */
619 uint32_t uHandle = 0;
620 uint32_t cbToRead;
621 uint64_t offReadAt;
622 int rc = VbglR3GuestCtrlFileGetReadAt(pHostCtx, &uHandle, &cbToRead, &offReadAt);
623 if (RT_SUCCESS(rc))
624 {
625 /*
626 * Locate the file and do the reading.
627 *
628 * If the request is larger than our scratch buffer, try grow it - just
629 * ignore failure as the host better respect our buffer limits.
630 */
631 int64_t offNew = 0;
632 size_t cbRead = 0;
633 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
634 if (pFile)
635 {
636 if (*pcbScratchBuf < cbToRead)
637 vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToRead);
638
639 rc = RTFileReadAt(pFile->hFile, (RTFOFF)offReadAt, *ppvScratchBuf, RT_MIN(cbToRead, *pcbScratchBuf), &cbRead);
640 if (RT_SUCCESS(rc))
641 {
642 offNew = offReadAt + cbRead;
643 RTFileSeek(pFile->hFile, offNew, RTFILE_SEEK_BEGIN, NULL); /* RTFileReadAt does not always change position. */
644 }
645 else
646 offNew = (int64_t)RTFileTell(pFile->hFile);
647 VGSvcVerbose(5, "[File %s] Read %zu bytes @ %RU64, rc=%Rrc, offNew=%RI64\n", pFile->pszName, cbRead, offReadAt, rc, offNew);
648 }
649 else
650 {
651 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
652 rc = VERR_NOT_FOUND;
653 }
654
655 /*
656 * Report result and data back to the host.
657 */
658 int rc2;
659 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
660 rc2 = VbglR3GuestCtrlFileCbReadOffset(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead, offNew);
661 else
662 rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead);
663 if (RT_FAILURE(rc2))
664 {
665 VGSvcError("Failed to report file read at status, rc=%Rrc\n", rc2);
666 if (RT_SUCCESS(rc))
667 rc = rc2;
668 }
669 }
670 else
671 {
672 VGSvcError("Error fetching parameters for file read at operation: %Rrc\n", rc);
673 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
674 }
675 return rc;
676}
677
678
679static int vgsvcGstCtrlSessionHandleFileWrite(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
680 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
681{
682 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
683 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
684
685 /*
686 * Retrieve the request and data to write.
687 */
688 uint32_t uHandle = 0;
689 uint32_t cbToWrite;
690 int rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite);
691 if ( rc == VERR_BUFFER_OVERFLOW
692 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToWrite))
693 rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite);
694 if (RT_SUCCESS(rc))
695 {
696 /*
697 * Locate the file and do the writing.
698 */
699 int64_t offNew = 0;
700 size_t cbWritten = 0;
701 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
702 if (pFile)
703 {
704 rc = RTFileWrite(pFile->hFile, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), &cbWritten);
705 offNew = (int64_t)RTFileTell(pFile->hFile);
706 VGSvcVerbose(5, "[File %s] Writing %p LB %RU32 => %Rrc, cbWritten=%zu, offNew=%RI64\n",
707 pFile->pszName, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), rc, cbWritten, offNew);
708 }
709 else
710 {
711 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
712 rc = VERR_NOT_FOUND;
713 }
714
715 /*
716 * Report result back to host.
717 */
718 int rc2;
719 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
720 rc2 = VbglR3GuestCtrlFileCbWriteOffset(pHostCtx, rc, (uint32_t)cbWritten, offNew);
721 else
722 rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten);
723 if (RT_FAILURE(rc2))
724 {
725 VGSvcError("Failed to report file write status, rc=%Rrc\n", rc2);
726 if (RT_SUCCESS(rc))
727 rc = rc2;
728 }
729 }
730 else
731 {
732 VGSvcError("Error fetching parameters for file write operation: %Rrc\n", rc);
733 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
734 }
735 return rc;
736}
737
738
739static int vgsvcGstCtrlSessionHandleFileWriteAt(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
740 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
741{
742 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
743 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
744
745 /*
746 * Retrieve the request and data to write.
747 */
748 uint32_t uHandle = 0;
749 uint32_t cbToWrite;
750 uint64_t offWriteAt;
751 int rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite, &offWriteAt);
752 if ( rc == VERR_BUFFER_OVERFLOW
753 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToWrite))
754 rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite, &offWriteAt);
755 if (RT_SUCCESS(rc))
756 {
757 /*
758 * Locate the file and do the writing.
759 */
760 int64_t offNew = 0;
761 size_t cbWritten = 0;
762 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
763 if (pFile)
764 {
765 rc = RTFileWriteAt(pFile->hFile, (RTFOFF)offWriteAt, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), &cbWritten);
766 if (RT_SUCCESS(rc))
767 {
768 offNew = offWriteAt + cbWritten;
769
770 /* RTFileWriteAt does not always change position: */
771 if (!(pFile->fOpen & RTFILE_O_APPEND))
772 RTFileSeek(pFile->hFile, offNew, RTFILE_SEEK_BEGIN, NULL);
773 else
774 RTFileSeek(pFile->hFile, 0, RTFILE_SEEK_END, (uint64_t *)&offNew);
775 }
776 else
777 offNew = (int64_t)RTFileTell(pFile->hFile);
778 VGSvcVerbose(5, "[File %s] Writing %p LB %RU32 @ %RU64 => %Rrc, cbWritten=%zu, offNew=%RI64\n",
779 pFile->pszName, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), offWriteAt, rc, cbWritten, offNew);
780 }
781 else
782 {
783 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
784 rc = VERR_NOT_FOUND;
785 }
786
787 /*
788 * Report result back to host.
789 */
790 int rc2;
791 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
792 rc2 = VbglR3GuestCtrlFileCbWriteOffset(pHostCtx, rc, (uint32_t)cbWritten, offNew);
793 else
794 rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten);
795 if (RT_FAILURE(rc2))
796 {
797 VGSvcError("Failed to report file write status, rc=%Rrc\n", rc2);
798 if (RT_SUCCESS(rc))
799 rc = rc2;
800 }
801 }
802 else
803 {
804 VGSvcError("Error fetching parameters for file write at operation: %Rrc\n", rc);
805 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
806 }
807 return rc;
808}
809
810
811static int vgsvcGstCtrlSessionHandleFileSeek(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
812{
813 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
814 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
815
816 /*
817 * Retrieve the request.
818 */
819 uint32_t uHandle = 0;
820 uint32_t uSeekMethod;
821 uint64_t offSeek; /* Will be converted to int64_t. */
822 int rc = VbglR3GuestCtrlFileGetSeek(pHostCtx, &uHandle, &uSeekMethod, &offSeek);
823 if (RT_SUCCESS(rc))
824 {
825 uint64_t offActual = 0;
826
827 /*
828 * Validate and convert the seek method to IPRT speak.
829 */
830 static const uint8_t s_abMethods[GUEST_FILE_SEEKTYPE_END + 1] =
831 {
832 UINT8_MAX, RTFILE_SEEK_BEGIN, UINT8_MAX, UINT8_MAX, RTFILE_SEEK_CURRENT,
833 UINT8_MAX, UINT8_MAX, UINT8_MAX, RTFILE_SEEK_END
834 };
835 if ( uSeekMethod < RT_ELEMENTS(s_abMethods)
836 && s_abMethods[uSeekMethod] != UINT8_MAX)
837 {
838 /*
839 * Locate the file and do the seek.
840 */
841 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
842 if (pFile)
843 {
844 rc = RTFileSeek(pFile->hFile, (int64_t)offSeek, s_abMethods[uSeekMethod], &offActual);
845 VGSvcVerbose(5, "[File %s]: Seeking to offSeek=%RI64, uSeekMethodIPRT=%u, rc=%Rrc\n",
846 pFile->pszName, offSeek, s_abMethods[uSeekMethod], rc);
847 }
848 else
849 {
850 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
851 rc = VERR_NOT_FOUND;
852 }
853 }
854 else
855 {
856 VGSvcError("Invalid seek method: %#x\n", uSeekMethod);
857 rc = VERR_NOT_SUPPORTED;
858 }
859
860 /*
861 * Report result back to host.
862 */
863 int rc2 = VbglR3GuestCtrlFileCbSeek(pHostCtx, rc, offActual);
864 if (RT_FAILURE(rc2))
865 {
866 VGSvcError("Failed to report file seek status, rc=%Rrc\n", rc2);
867 if (RT_SUCCESS(rc))
868 rc = rc2;
869 }
870 }
871 else
872 {
873 VGSvcError("Error fetching parameters for file seek operation: %Rrc\n", rc);
874 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
875 }
876 return rc;
877}
878
879
880static int vgsvcGstCtrlSessionHandleFileTell(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
881{
882 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
883 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
884
885 /*
886 * Retrieve the request.
887 */
888 uint32_t uHandle = 0;
889 int rc = VbglR3GuestCtrlFileGetTell(pHostCtx, &uHandle);
890 if (RT_SUCCESS(rc))
891 {
892 /*
893 * Locate the file and ask for the current position.
894 */
895 uint64_t offCurrent = 0;
896 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
897 if (pFile)
898 {
899 offCurrent = RTFileTell(pFile->hFile);
900 VGSvcVerbose(5, "[File %s]: Telling offCurrent=%RU64\n", pFile->pszName, offCurrent);
901 }
902 else
903 {
904 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
905 rc = VERR_NOT_FOUND;
906 }
907
908 /*
909 * Report result back to host.
910 */
911 int rc2 = VbglR3GuestCtrlFileCbTell(pHostCtx, rc, offCurrent);
912 if (RT_FAILURE(rc2))
913 {
914 VGSvcError("Failed to report file tell status, rc=%Rrc\n", rc2);
915 if (RT_SUCCESS(rc))
916 rc = rc2;
917 }
918 }
919 else
920 {
921 VGSvcError("Error fetching parameters for file tell operation: %Rrc\n", rc);
922 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
923 }
924 return rc;
925}
926
927
928static int vgsvcGstCtrlSessionHandleFileSetSize(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
929{
930 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
931 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
932
933 /*
934 * Retrieve the request.
935 */
936 uint32_t uHandle = 0;
937 uint64_t cbNew = 0;
938 int rc = VbglR3GuestCtrlFileGetSetSize(pHostCtx, &uHandle, &cbNew);
939 if (RT_SUCCESS(rc))
940 {
941 /*
942 * Locate the file and ask for the current position.
943 */
944 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
945 if (pFile)
946 {
947 rc = RTFileSetSize(pFile->hFile, cbNew);
948 VGSvcVerbose(5, "[File %s]: Changing size to %RU64 (%#RX64), rc=%Rrc\n", pFile->pszName, cbNew, cbNew, rc);
949 }
950 else
951 {
952 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
953 cbNew = UINT64_MAX;
954 rc = VERR_NOT_FOUND;
955 }
956
957 /*
958 * Report result back to host.
959 */
960 int rc2 = VbglR3GuestCtrlFileCbSetSize(pHostCtx, rc, cbNew);
961 if (RT_FAILURE(rc2))
962 {
963 VGSvcError("Failed to report file tell status, rc=%Rrc\n", rc2);
964 if (RT_SUCCESS(rc))
965 rc = rc2;
966 }
967 }
968 else
969 {
970 VGSvcError("Error fetching parameters for file tell operation: %Rrc\n", rc);
971 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
972 }
973 return rc;
974}
975
976
977#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
978static int vgsvcGstCtrlSessionHandleFileRemove(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
979{
980 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
981 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
982
983 /*
984 * Retrieve the request.
985 */
986 char szPath[RTPATH_MAX];
987 int rc = VbglR3GuestCtrlFileGetRemove(pHostCtx, szPath, sizeof(szPath));
988 if (RT_SUCCESS(rc))
989 {
990 VGSvcVerbose(4, "Deleting file szPath=%s\n", szPath);
991 rc = RTFileDelete(szPath);
992
993 /*
994 * Report result back to host.
995 */
996 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
997 if (RT_FAILURE(rc2))
998 {
999 VGSvcError("Failed to report file deletion status, rc=%Rrc\n", rc2);
1000 if (RT_SUCCESS(rc))
1001 rc = rc2;
1002 }
1003 }
1004 else
1005 {
1006 VGSvcError("Error fetching parameters for file deletion operation: %Rrc\n", rc);
1007 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1008 }
1009 VGSvcVerbose(5, "Deleting file returned rc=%Rrc\n", rc);
1010 return rc;
1011}
1012
1013
1014static int vgsvcGstCtrlSessionHandleDirOpen(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1015{
1016 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1017 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1018
1019 char szPath[RTPATH_MAX];
1020 uint32_t fFlags;
1021 GSTCTLDIRFILTER enmFilter;
1022 uint32_t uHandle = 0;
1023 uint32_t fReadFlags;
1024 GSTCTLFSOBJATTRADD enmAttrAdd;
1025 int rc = VbglR3GuestCtrlDirGetOpen(pHostCtx, szPath, sizeof(szPath), &fFlags, &enmFilter, &enmAttrAdd, &fReadFlags);
1026 VGSvcVerbose(4, "[Dir %s]: fFlags=%#x, enmFilter=%#x, rc=%Rrc\n", szPath, fFlags, enmFilter, rc);
1027 if (RT_SUCCESS(rc))
1028 {
1029 PVBOXSERVICECTRLDIR pDir = (PVBOXSERVICECTRLDIR)RTMemAllocZ(sizeof(VBOXSERVICECTRLDIR));
1030 AssertPtrReturn(pDir, VERR_NO_MEMORY);
1031 pDir->hDir = NIL_RTDIR; /* Not zero or NULL! */
1032 if (szPath[0])
1033 {
1034 pDir->pszPathAbs = RTStrDup(szPath);
1035 if (!pDir->pszPathAbs)
1036 rc = VERR_NO_MEMORY;
1037
1038 /* Save reading parameters for subsequent directory entry read calls later. */
1039 pDir->fRead = fReadFlags;
1040 pDir->enmReadAttrAdd = enmAttrAdd;
1041
1042 pDir->cbDirEntryEx = GSTCTL_DIRENTRY_MAX_SIZE;
1043 pDir->pDirEntryEx = (PRTDIRENTRYEX)RTMemAlloc(pDir->cbDirEntryEx);
1044 AssertPtrReturn(pDir->pDirEntryEx, VERR_NO_MEMORY);
1045
1046 if (RT_SUCCESS(rc))
1047 {
1048 rc = RTDirOpenFiltered(&pDir->hDir, pDir->pszPathAbs, (RTDIRFILTER)enmFilter, fFlags);
1049 if (RT_SUCCESS(rc))
1050 {
1051 uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID);
1052 pDir->uHandle = uHandle;
1053 RTListAppend(&pSession->lstDirs, &pDir->Node);
1054 VGSvcVerbose(2, "[Dir %s] Opened (ID=%RU32)\n", pDir->pszPathAbs, pDir->uHandle);
1055 }
1056 }
1057 }
1058 else
1059 {
1060 VGSvcError("Opening directory failed: Empty path!\n");
1061 rc = VERR_INVALID_NAME;
1062 }
1063
1064 /* Clean up if we failed. */
1065 if (RT_FAILURE(rc))
1066 {
1067 RTStrFree(pDir->pszPathAbs);
1068 if (pDir->hDir != NIL_RTDIR)
1069 RTDirClose(pDir->hDir);
1070 RTMemFree(pDir);
1071 }
1072
1073 /*
1074 * Report result back to host.
1075 */
1076 int rc2 = VbglR3GuestCtrlDirCbOpen(pHostCtx, rc, uHandle);
1077 if (RT_FAILURE(rc2))
1078 {
1079 VGSvcError("[Dir %s]: Failed to report directory open status, rc=%Rrc\n", szPath, rc2);
1080 if (RT_SUCCESS(rc))
1081 rc = rc2;
1082 }
1083 }
1084 else
1085 {
1086 VGSvcError("Error fetching parameters for directory open operation: %Rrc\n", rc);
1087 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1088 }
1089
1090 VGSvcVerbose(4, "[Dir %s] Opening (flags=%#x, filter flags=%#x) returned rc=%Rrc\n",
1091 szPath, fFlags, enmFilter, rc);
1092 return rc;
1093}
1094
1095
1096static int vgsvcGstCtrlSessionHandleDirClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1097{
1098 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1099 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1100
1101 /*
1102 * Retrieve the message.
1103 */
1104 uint32_t uHandle = 0;
1105 int rc = VbglR3GuestCtrlDirGetClose(pHostCtx, &uHandle /* Dir handle to close */);
1106 if (RT_SUCCESS(rc))
1107 {
1108 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1109 if (pDir)
1110 {
1111 VGSvcVerbose(2, "[Dir %s] Closing (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle);
1112 rc = vgsvcGstCtrlSessionDirFree(pDir);
1113
1114 vgsvcGstCtrlSessionDirRelease(pDir);
1115 }
1116 else
1117 {
1118 VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle);
1119 rc = VERR_NOT_FOUND;
1120 }
1121
1122 /*
1123 * Report result back to host.
1124 */
1125 int rc2 = VbglR3GuestCtrlDirCbClose(pHostCtx, rc);
1126 if (RT_FAILURE(rc2))
1127 {
1128 VGSvcError("Failed to report directory close status, rc=%Rrc\n", rc2);
1129 if (RT_SUCCESS(rc))
1130 rc = rc2;
1131 }
1132 }
1133 else
1134 {
1135 VGSvcError("Error fetching parameters for directory close operation: %Rrc\n", rc);
1136 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1137 }
1138 return rc;
1139}
1140
1141
1142/**
1143 * Worker function for reading a next guest directory entry.
1144 *
1145 * @returns VBox status code.
1146 * @param pSession Guest Control session to use.
1147 * @param pDir Guest directory to read the next entry for.
1148 * @param ppDirEntryEx Where to store the pointer to the next guest directory entry on success.
1149 * @param pcbDirEntryEx Where to return the size (in bytes) of \a ppGstCtlDirEntryEx on success.
1150 * @param ppszUser Where to return the resolved user name string.
1151 * @param ppszGroups Where to return the resolved groups string.
1152 */
1153static int vgsvcGstCtrlSessionDirReadNext(const PVBOXSERVICECTRLSESSION pSession,
1154 PVBOXSERVICECTRLDIR pDir,
1155 PGSTCTLDIRENTRYEX *ppDirEntryEx, size_t *pcbDirEntryEx,
1156 const char **ppszUser, const char **ppszGroups)
1157{
1158 PRTDIRENTRYEX pDirEntryEx = pDir->pDirEntryEx;
1159
1160 size_t cbDirEntryEx = pDir->cbDirEntryEx;
1161 int rc = RTDirReadEx(pDir->hDir, pDir->pDirEntryEx, &cbDirEntryEx, (RTFSOBJATTRADD)pDir->enmReadAttrAdd, pDir->fRead);
1162
1163 VGSvcVerbose(2, "[Dir %s] Read next entry '%s' -> %Rrc (%zu bytes)\n",
1164 pDir->pszPathAbs, RT_SUCCESS(rc) ? pDirEntryEx->szName : "<None>", rc, cbDirEntryEx);
1165
1166 if (RT_FAILURE(rc))
1167 return rc;
1168
1169 /* Paranoia. */
1170 AssertReturn(cbDirEntryEx <= pDir->cbDirEntryEx, VERR_BUFFER_OVERFLOW);
1171
1172 *ppszUser = VGSvcIdCacheGetUidName(&pSession->UidCache,
1173 pDirEntryEx->Info.Attr.u.Unix.uid, pDirEntryEx->szName, pDir->pszPathAbs);
1174 *ppszGroups = VGSvcIdCacheGetGidName(&pSession->GidCache,
1175 pDirEntryEx->Info.Attr.u.Unix.gid, pDirEntryEx->szName, pDir->pszPathAbs);
1176
1177 VGSvcVerbose(2, "[Dir %s] Entry '%s': %zu bytes, uid=%s (%d), gid=%s (%d)\n",
1178 pDir->pszPathAbs, pDirEntryEx->szName, pDirEntryEx->Info.cbObject,
1179 *ppszUser, pDirEntryEx->Info.Attr.u.UnixOwner.uid,
1180 *ppszGroups, pDirEntryEx->Info.Attr.u.UnixGroup.gid);
1181
1182 /*
1183 * For now we ASSUME that RTDIRENTRYEX == GSTCTLDIRENTRYEX, which implies that we simply can cast RTDIRENTRYEX
1184 * to GSTCTLDIRENTRYEX. This might change in the future, however, so be extra cautious here.
1185 *
1186 * Ditto for RTFSOBJATTRADD == GSTCTLFSOBJATTRADD.
1187 */
1188 AssertCompile(sizeof(GSTCTLDIRENTRYEX) == sizeof(RTDIRENTRYEX));
1189 AssertCompile(RT_OFFSETOF(GSTCTLDIRENTRYEX, Info) == RT_OFFSETOF(RTDIRENTRYEX, Info));
1190 AssertCompile(RT_OFFSETOF(GSTCTLDIRENTRYEX, cbName) == RT_OFFSETOF(RTDIRENTRYEX, cbName));
1191 AssertCompile(RT_OFFSETOF(GSTCTLDIRENTRYEX, szName) == RT_OFFSETOF(RTDIRENTRYEX, szName));
1192 AssertCompile(RT_OFFSETOF(GSTCTLFSOBJINFO, Attr) == RT_OFFSETOF(RTFSOBJINFO, Attr));
1193
1194 if (RT_SUCCESS(rc))
1195 {
1196 *ppDirEntryEx = (PGSTCTLDIRENTRYEX)pDirEntryEx;
1197 *pcbDirEntryEx = cbDirEntryEx;
1198 }
1199
1200 return rc;
1201}
1202
1203
1204static int vgsvcGstCtrlSessionHandleDirRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1205{
1206 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1207 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1208
1209 /*
1210 * Retrieve the message.
1211 */
1212 uint32_t uHandle;
1213 int rc = VbglR3GuestCtrlDirGetRead(pHostCtx, &uHandle);
1214 if (RT_SUCCESS(rc))
1215 {
1216 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1217 if (pDir)
1218 {
1219 PGSTCTLDIRENTRYEX pDirEntryEx;
1220 size_t cbDirEntryEx;
1221 const char *pszUser = NULL;
1222 const char *pszGroups = NULL;
1223 rc = vgsvcGstCtrlSessionDirReadNext(pSession, pDir,
1224 &pDirEntryEx, &cbDirEntryEx, &pszUser, &pszGroups);
1225
1226 VGSvcVerbose(2, "[Dir %s] %Rrc\n", pDir->pszPathAbs, rc);
1227
1228 int rc2;
1229 if (RT_SUCCESS(rc))
1230 rc2 = VbglR3GuestCtrlDirCbReadEx(pHostCtx, rc, pDirEntryEx, (uint32_t)cbDirEntryEx, pszUser, pszGroups);
1231 else
1232 rc2 = VbglR3GuestCtrlDirCbRead(pHostCtx, rc, NULL /* pEntry */, 0 /* cbSize */);
1233 if (RT_FAILURE(rc2))
1234 VGSvcError("Failed to report directory read status (%Rrc), rc=%Rrc\n", rc, rc2);
1235
1236 VGSvcVerbose(2, "[Dir %s] 2 %Rrc\n", pDir->pszPathAbs, rc);
1237
1238 if (rc == VERR_NO_MORE_FILES) /* Directory reading done. */
1239 rc = VINF_SUCCESS;
1240
1241 vgsvcGstCtrlSessionDirRelease(pDir);
1242 }
1243 else
1244 {
1245 VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle);
1246 rc = VERR_NOT_FOUND;
1247 }
1248 }
1249 else
1250 {
1251 VGSvcError("Error fetching parameters for directory read operation: %Rrc\n", rc);
1252 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1253 }
1254 return rc;
1255}
1256
1257
1258static int vgsvcGstCtrlSessionHandleDirRewind(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1259{
1260 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1261 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1262
1263 /*
1264 * Retrieve the message.
1265 */
1266 uint32_t uHandle = 0;
1267 int rc = VbglR3GuestCtrlDirGetRewind(pHostCtx, &uHandle);
1268 if (RT_SUCCESS(rc))
1269 {
1270 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1271 if (pDir)
1272 {
1273 VGSvcVerbose(2, "[Dir %s] Rewinding (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle);
1274
1275 rc = RTDirRewind(pDir->hDir);
1276
1277 vgsvcGstCtrlSessionDirRelease(pDir);
1278 }
1279 else
1280 {
1281 VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle);
1282 rc = VERR_NOT_FOUND;
1283 }
1284
1285 /*
1286 * Report result back to host.
1287 */
1288 int rc2 = VbglR3GuestCtrlDirCbRewind(pHostCtx, rc);
1289 if (RT_FAILURE(rc2))
1290 {
1291 VGSvcError("Failed to report directory rewind status, rc=%Rrc\n", rc2);
1292 if (RT_SUCCESS(rc))
1293 rc = rc2;
1294 }
1295 }
1296 else
1297 {
1298 VGSvcError("Error fetching parameters for directory rewind operation: %Rrc\n", rc);
1299 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1300 }
1301 return rc;
1302}
1303
1304
1305static int vgsvcGstCtrlSessionHandleDirCreate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1306{
1307 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1308 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1309
1310 /*
1311 * Retrieve the request.
1312 */
1313 char szPath[RTPATH_MAX];
1314 RTFMODE fMode;
1315 uint32_t fCreate;
1316 int rc = VbglR3GuestCtrlDirGetCreate(pHostCtx, szPath, sizeof(szPath), &fMode, &fCreate);
1317 if (RT_SUCCESS(rc))
1318 {
1319 if (!(fCreate & ~GSTCTL_CREATEDIRECTORY_F_VALID_MASK))
1320 {
1321 /* Translate flags. */
1322 int fCreateRuntime = 0; /* RTDIRCREATE_FLAGS_XXX */
1323 if (fCreate & GSTCTL_CREATEDIRECTORY_F_NO_SYMLINKS)
1324 fCreateRuntime |= RTDIRCREATE_FLAGS_NO_SYMLINKS;
1325 if (fCreate & GSTCTL_CREATEDIRECTORY_F_IGNORE_UMASK)
1326 fCreateRuntime |= RTDIRCREATE_FLAGS_IGNORE_UMASK;
1327
1328 if (fCreate & GSTCTL_CREATEDIRECTORY_F_PARENTS)
1329 rc = RTDirCreateFullPath(szPath, fMode);
1330 else
1331 rc = RTDirCreate(szPath, fMode, fCreateRuntime);
1332
1333 VGSvcVerbose(4, "Creating directory (szPath='%s', fMode=%#x, fCreate=%#x) -> rc=%Rrc\n", szPath, fMode, fCreate, rc);
1334 }
1335 else
1336 {
1337 VGSvcError("Invalid directory creation flags: %#x\n", fCreate);
1338 rc = VERR_NOT_SUPPORTED;
1339 }
1340
1341 /*
1342 * Report result back to host.
1343 */
1344 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
1345 if (RT_FAILURE(rc2))
1346 {
1347 VGSvcError("Failed to report directory creation status, rc=%Rrc\n", rc2);
1348 if (RT_SUCCESS(rc))
1349 rc = rc2;
1350 }
1351 }
1352 else
1353 {
1354 VGSvcError("Error fetching parameters for directory creation operation: %Rrc\n", rc);
1355 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1356 }
1357 VGSvcVerbose(5, "Creating directory returned rc=%Rrc\n", rc);
1358 return rc;
1359}
1360
1361static int vgsvcGstCtrlSessionHandleDirList(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1362{
1363 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1364 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1365
1366 /*
1367 * Retrieve the request.
1368 */
1369 uint32_t uHandle;
1370 uint32_t cEntries;
1371 uint32_t cEntriesRead = 0;
1372 uint32_t fFlags;
1373 int rc = VbglR3GuestCtrlDirGetList(pHostCtx, &uHandle, &cEntries, &fFlags);
1374 if (RT_SUCCESS(rc))
1375 {
1376 size_t cbGrowSize = _64K;
1377 void *pvBuf = RTMemAlloc(cbGrowSize);
1378 if (!pvBuf)
1379 rc = VERR_NO_MEMORY;
1380 size_t cbBufUsed = 0;
1381 if (RT_SUCCESS(rc))
1382 {
1383 if (!(fFlags & ~GSTCTL_DIRLIST_F_VALID_MASK))
1384 {
1385 size_t cbBufSize = cbGrowSize;
1386
1387 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1388 if (pDir)
1389 {
1390 PGSTCTLDIRENTRYEX pDirEntryEx;
1391 size_t cbDirEntryEx;
1392 const char *pszUser = NULL;
1393 const char *pszGroups = NULL;
1394
1395 while (cEntries--)
1396 {
1397 rc = vgsvcGstCtrlSessionDirReadNext(pSession, pDir,
1398 &pDirEntryEx, &cbDirEntryEx, &pszUser, &pszGroups);
1399 if (RT_FAILURE(rc)) /* Might be VERR_NO_MORE_FILES. */
1400 break;
1401
1402 size_t const cbHdr = sizeof(GSTCTLDIRENTRYLISTHDR);
1403 size_t const cbUser = strlen(pszUser) + 1; /* Include terminator. */
1404 size_t const cbGroups = strlen(pszGroups) + 1; /* Ditto. */
1405 size_t const cbTotal = cbHdr + cbDirEntryEx + cbUser + cbGroups;
1406
1407 if (cbBufSize - cbBufUsed < cbTotal) /* Grow buffer, if needed. */
1408 {
1409 AssertBreakStmt(cbTotal <= cbGrowSize, VERR_BUFFER_OVERFLOW);
1410 pvBuf = RTMemRealloc(pvBuf, cbBufSize + cbGrowSize);
1411 AssertPtrBreakStmt(pvBuf, VERR_NO_MEMORY);
1412 cbBufSize += cbGrowSize;
1413 }
1414
1415 GSTCTLDIRENTRYLISTHDR Hdr;
1416 Hdr.cbDirEntryEx = (uint32_t)cbDirEntryEx;
1417 Hdr.cbUser = (uint32_t)cbUser;
1418 Hdr.cbGroups = (uint32_t)cbGroups;
1419
1420 memcpy((uint8_t *)pvBuf + cbBufUsed, &Hdr, cbHdr);
1421 cbBufUsed += cbHdr;
1422 memcpy((uint8_t *)pvBuf + cbBufUsed, pDirEntryEx, cbDirEntryEx);
1423 cbBufUsed += cbDirEntryEx;
1424 memcpy((uint8_t *)pvBuf + cbBufUsed, pszUser, cbUser);
1425 cbBufUsed += cbUser;
1426 memcpy((uint8_t *)pvBuf + cbBufUsed, pszGroups, cbGroups);
1427 cbBufUsed += cbGroups;
1428
1429 Assert(cbBufUsed <= cbBufSize);
1430 cEntriesRead++;
1431 }
1432
1433 vgsvcGstCtrlSessionDirRelease(pDir);
1434 }
1435
1436 VGSvcVerbose(4, "Read %RU32 directory entries (requested %RU32 entries) -> %zu bytes, rc=%Rrc\n",
1437 cEntriesRead, cEntries, cbBufUsed, rc);
1438
1439 /* Directory reading done? */
1440 if ( cEntriesRead
1441 && rc == VERR_NO_MORE_FILES)
1442 rc = VINF_SUCCESS;
1443
1444 /* Note: Subsequent calls will return VERR_NO_MORE_FILES to the host. */
1445 }
1446 else
1447 {
1448 VGSvcError("Unsupported directory listing flags: %#x (all %#x)\n", (fFlags & ~GSTCTL_DIRLIST_F_VALID_MASK), fFlags);
1449 rc = VERR_NOT_SUPPORTED;
1450 }
1451 }
1452
1453 /*
1454 * Report result back to host.
1455 */
1456 int rc2 = VbglR3GuestCtrlDirCbList(pHostCtx, rc, cEntriesRead, pvBuf, (uint32_t)cbBufUsed);
1457 if (RT_FAILURE(rc2))
1458 VGSvcError("Failed to report directory listing (%Rrc), rc=%Rrc\n", rc, rc2);
1459
1460 if (rc == VERR_NO_MORE_FILES) /* Directory reading done? */
1461 rc = VINF_SUCCESS;
1462
1463 RTMemFree(pvBuf);
1464 }
1465 else
1466 {
1467 VGSvcError("Error fetching parameters for directory listing operation: %Rrc\n", rc);
1468 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1469 }
1470 VGSvcVerbose(5, "Listing directory returned rc=%Rrc\n", rc);
1471 return rc;
1472}
1473#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
1474
1475
1476static int vgsvcGstCtrlSessionHandlePathRename(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1477{
1478 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1479 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1480
1481 /*
1482 * Retrieve the request.
1483 */
1484 char szSource[RTPATH_MAX];
1485 char szDest[RTPATH_MAX];
1486 uint32_t fFlags = 0; /* PATHRENAME_FLAG_XXX */
1487 int rc = VbglR3GuestCtrlPathGetRename(pHostCtx, szSource, sizeof(szSource), szDest, sizeof(szDest), &fFlags);
1488 if (RT_SUCCESS(rc))
1489 {
1490 /*
1491 * Validate the flags (kudos for using the same as IPRT), then do the renaming.
1492 */
1493 AssertCompile(PATHRENAME_FLAG_NO_REPLACE == RTPATHRENAME_FLAGS_NO_REPLACE);
1494 AssertCompile(PATHRENAME_FLAG_REPLACE == RTPATHRENAME_FLAGS_REPLACE);
1495 AssertCompile(PATHRENAME_FLAG_NO_SYMLINKS == RTPATHRENAME_FLAGS_NO_SYMLINKS);
1496 AssertCompile(PATHRENAME_FLAG_VALID_MASK == (RTPATHRENAME_FLAGS_NO_REPLACE | RTPATHRENAME_FLAGS_REPLACE | RTPATHRENAME_FLAGS_NO_SYMLINKS));
1497 if (!(fFlags & ~PATHRENAME_FLAG_VALID_MASK))
1498 {
1499 VGSvcVerbose(4, "Renaming '%s' to '%s', fFlags=%#x, rc=%Rrc\n", szSource, szDest, fFlags, rc);
1500 rc = RTPathRename(szSource, szDest, fFlags);
1501 }
1502 else
1503 {
1504 VGSvcError("Invalid rename flags: %#x\n", fFlags);
1505 rc = VERR_NOT_SUPPORTED;
1506 }
1507
1508 /*
1509 * Report result back to host.
1510 */
1511 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
1512 if (RT_FAILURE(rc2))
1513 {
1514 VGSvcError("Failed to report renaming status, rc=%Rrc\n", rc2);
1515 if (RT_SUCCESS(rc))
1516 rc = rc2;
1517 }
1518 }
1519 else
1520 {
1521 VGSvcError("Error fetching parameters for rename operation: %Rrc\n", rc);
1522 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1523 }
1524 VGSvcVerbose(5, "Renaming '%s' to '%s' returned rc=%Rrc\n", szSource, szDest, rc);
1525 return rc;
1526}
1527
1528
1529/**
1530 * Handles getting the user's documents directory.
1531 *
1532 * @returns VBox status code.
1533 * @param pSession Guest session.
1534 * @param pHostCtx Host context.
1535 */
1536static int vgsvcGstCtrlSessionHandlePathUserDocuments(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1537{
1538 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1539 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1540
1541 /*
1542 * Retrieve the request.
1543 */
1544 int rc = VbglR3GuestCtrlPathGetUserDocuments(pHostCtx);
1545 if (RT_SUCCESS(rc))
1546 {
1547 /*
1548 * Get the path and pass it back to the host..
1549 */
1550 char szPath[RTPATH_MAX];
1551 rc = RTPathUserDocuments(szPath, sizeof(szPath));
1552#ifdef DEBUG
1553 VGSvcVerbose(2, "User documents is '%s', rc=%Rrc\n", szPath, rc);
1554#endif
1555
1556 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
1557 RT_SUCCESS(rc) ? (uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
1558 if (RT_FAILURE(rc2))
1559 {
1560 VGSvcError("Failed to report user documents, rc=%Rrc\n", rc2);
1561 if (RT_SUCCESS(rc))
1562 rc = rc2;
1563 }
1564 }
1565 else
1566 {
1567 VGSvcError("Error fetching parameters for user documents path request: %Rrc\n", rc);
1568 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1569 }
1570 return rc;
1571}
1572
1573
1574/**
1575 * Handles shutting down / rebooting the guest OS.
1576 *
1577 * @returns VBox status code.
1578 * @param pSession Guest session.
1579 * @param pHostCtx Host context.
1580 */
1581static int vgsvcGstCtrlSessionHandleShutdown(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1582{
1583 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1584 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1585
1586 /*
1587 * Retrieve the request.
1588 */
1589 uint32_t fAction;
1590 int rc = VbglR3GuestCtrlGetShutdown(pHostCtx, &fAction);
1591 if (RT_SUCCESS(rc))
1592 {
1593 VGSvcVerbose(1, "Host requested to %s system ...\n", (fAction & RTSYSTEM_SHUTDOWN_REBOOT) ? "reboot" : "shutdown");
1594
1595 /* Reply first to the host, in order to avoid host hangs when issuing the guest shutdown. */
1596 rc = VbglR3GuestCtrlMsgReply(pHostCtx, VINF_SUCCESS);
1597 if (RT_FAILURE(rc))
1598 {
1599 VGSvcError("Failed to reply to shutdown / reboot request, rc=%Rrc\n", rc);
1600 }
1601 else
1602 {
1603 int fSystemShutdown = RTSYSTEM_SHUTDOWN_PLANNED;
1604
1605 /* Translate SHUTDOWN_FLAG_ into RTSYSTEM_SHUTDOWN_ flags. */
1606 if (fAction & GUEST_SHUTDOWN_FLAG_REBOOT)
1607 fSystemShutdown |= RTSYSTEM_SHUTDOWN_REBOOT;
1608 else /* SHUTDOWN_FLAG_POWER_OFF */
1609 fSystemShutdown |= RTSYSTEM_SHUTDOWN_POWER_OFF;
1610
1611 if (fAction & GUEST_SHUTDOWN_FLAG_FORCE)
1612 fSystemShutdown |= RTSYSTEM_SHUTDOWN_FORCE;
1613
1614 rc = RTSystemShutdown(0 /*cMsDelay*/, fSystemShutdown, "VBoxService");
1615 if (RT_FAILURE(rc))
1616 VGSvcError("%s system failed with %Rrc\n",
1617 (fAction & RTSYSTEM_SHUTDOWN_REBOOT) ? "Rebooting" : "Shutting down", rc);
1618 }
1619 }
1620 else
1621 {
1622 VGSvcError("Error fetching parameters for shutdown / reboot request: %Rrc\n", rc);
1623 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1624 }
1625
1626 return rc;
1627}
1628
1629
1630/**
1631 * Handles getting the user's home directory.
1632 *
1633 * @returns VBox status code.
1634 * @param pSession Guest session.
1635 * @param pHostCtx Host context.
1636 */
1637static int vgsvcGstCtrlSessionHandlePathUserHome(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1638{
1639 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1640 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1641
1642 /*
1643 * Retrieve the request.
1644 */
1645 int rc = VbglR3GuestCtrlPathGetUserHome(pHostCtx);
1646 if (RT_SUCCESS(rc))
1647 {
1648 /*
1649 * Get the path and pass it back to the host..
1650 */
1651 char szPath[RTPATH_MAX];
1652 rc = RTPathUserHome(szPath, sizeof(szPath));
1653
1654#ifdef DEBUG
1655 VGSvcVerbose(2, "User home is '%s', rc=%Rrc\n", szPath, rc);
1656#endif
1657 /* Report back in any case. */
1658 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
1659 RT_SUCCESS(rc) ?(uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
1660 if (RT_FAILURE(rc2))
1661 {
1662 VGSvcError("Failed to report user home, rc=%Rrc\n", rc2);
1663 if (RT_SUCCESS(rc))
1664 rc = rc2;
1665 }
1666 }
1667 else
1668 {
1669 VGSvcError("Error fetching parameters for user home directory path request: %Rrc\n", rc);
1670 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1671 }
1672 return rc;
1673}
1674
1675/**
1676 * Handles starting a guest processes.
1677 *
1678 * @returns VBox status code.
1679 * @param pSession Guest session.
1680 * @param pHostCtx Host context.
1681 */
1682static int vgsvcGstCtrlSessionHandleProcExec(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1683{
1684 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1685 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1686
1687 /* Initialize maximum environment block size -- needed as input
1688 * parameter to retrieve the stuff from the host. On output this then
1689 * will contain the actual block size. */
1690 PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo;
1691 int rc = VbglR3GuestCtrlProcGetStart(pHostCtx, &pStartupInfo);
1692 if (RT_SUCCESS(rc))
1693 {
1694 VGSvcVerbose(3, "Request to start process szCmd=%s, fFlags=0x%x, szArgs=%s, szEnv=%s, szCwd=%s, uTimeout=%RU32\n",
1695 pStartupInfo->pszCmd, pStartupInfo->fFlags,
1696 pStartupInfo->cArgs ? pStartupInfo->pszArgs : "<None>",
1697 pStartupInfo->cEnvVars ? pStartupInfo->pszEnv : "<None>",
1698 pStartupInfo->cbCwd ? pStartupInfo->pszCwd : "<None>",
1699 pStartupInfo->uTimeLimitMS);
1700
1701 bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */
1702 rc = VGSvcGstCtrlSessionProcessStartAllowed(pSession, &fStartAllowed);
1703 if (RT_SUCCESS(rc))
1704 {
1705 vgsvcGstCtrlSessionCleanupProcesses(pSession);
1706
1707 if (fStartAllowed)
1708 rc = VGSvcGstCtrlProcessStart(pSession, pStartupInfo, pHostCtx->uContextID);
1709 else
1710 rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */
1711 }
1712
1713 /* We're responsible for signaling errors to the host (it will wait for ever otherwise). */
1714 if (RT_FAILURE(rc))
1715 {
1716 VGSvcError("Starting process failed with rc=%Rrc, protocol=%RU32, parameters=%RU32\n",
1717 rc, pHostCtx->uProtocol, pHostCtx->uNumParms);
1718 int rc2 = VbglR3GuestCtrlProcCbStatus(pHostCtx, 0 /*nil-PID*/, PROC_STS_ERROR, rc, NULL /*pvData*/, 0 /*cbData*/);
1719 if (RT_FAILURE(rc2))
1720 VGSvcError("Error sending start process status to host, rc=%Rrc\n", rc2);
1721 }
1722
1723 VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo);
1724 pStartupInfo = NULL;
1725 }
1726 else
1727 {
1728 VGSvcError("Failed to retrieve parameters for process start: %Rrc (cParms=%u)\n", rc, pHostCtx->uNumParms);
1729 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1730 }
1731
1732 return rc;
1733}
1734
1735
1736/**
1737 * Sends stdin input to a specific guest process.
1738 *
1739 * @returns VBox status code.
1740 * @param pSession The session which is in charge.
1741 * @param pHostCtx The host context to use.
1742 * @param ppvScratchBuf The scratch buffer, we may grow it.
1743 * @param pcbScratchBuf The scratch buffer size for retrieving the input
1744 * data.
1745 */
1746static int vgsvcGstCtrlSessionHandleProcInput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1747 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
1748{
1749 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1750 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1751
1752 /*
1753 * Retrieve the data from the host.
1754 */
1755 uint32_t uPID;
1756 uint32_t fFlags;
1757 uint32_t cbInput;
1758 int rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
1759 if ( rc == VERR_BUFFER_OVERFLOW
1760 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbInput))
1761 rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
1762 if (RT_SUCCESS(rc))
1763 {
1764 if (fFlags & GUEST_PROC_IN_FLAG_EOF)
1765 VGSvcVerbose(4, "Got last process input block for PID=%RU32 (%RU32 bytes) ...\n", uPID, cbInput);
1766
1767 /*
1768 * Locate the process and feed it.
1769 */
1770 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1771 if (pProcess)
1772 {
1773 rc = VGSvcGstCtrlProcessHandleInput(pProcess, pHostCtx, RT_BOOL(fFlags & GUEST_PROC_IN_FLAG_EOF),
1774 *ppvScratchBuf, RT_MIN(cbInput, *pcbScratchBuf));
1775 if (RT_FAILURE(rc))
1776 VGSvcError("Error handling input message for PID=%RU32, rc=%Rrc\n", uPID, rc);
1777 VGSvcGstCtrlProcessRelease(pProcess);
1778 }
1779 else
1780 {
1781 VGSvcError("Could not find PID %u for feeding %u bytes to it.\n", uPID, cbInput);
1782 rc = VERR_PROCESS_NOT_FOUND;
1783 VbglR3GuestCtrlProcCbStatusInput(pHostCtx, uPID, INPUT_STS_ERROR, rc, 0);
1784 }
1785 }
1786 else
1787 {
1788 VGSvcError("Failed to retrieve parameters for process input: %Rrc (scratch %u bytes)\n", rc, *pcbScratchBuf);
1789 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1790 }
1791
1792 VGSvcVerbose(6, "Feeding input to PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1793 return rc;
1794}
1795
1796
1797/**
1798 * Gets stdout/stderr output of a specific guest process.
1799 *
1800 * @returns VBox status code.
1801 * @param pSession The session which is in charge.
1802 * @param pHostCtx The host context to use.
1803 */
1804static int vgsvcGstCtrlSessionHandleProcOutput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1805{
1806 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1807 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1808
1809 /*
1810 * Retrieve the request.
1811 */
1812 uint32_t uPID;
1813 uint32_t uHandleID;
1814 uint32_t fFlags;
1815 int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &fFlags);
1816#ifdef DEBUG_andy
1817 VGSvcVerbose(4, "Getting output for PID=%RU32, CID=%RU32, uHandleID=%RU32, fFlags=%RU32\n",
1818 uPID, pHostCtx->uContextID, uHandleID, fFlags);
1819#endif
1820 if (RT_SUCCESS(rc))
1821 {
1822 /*
1823 * Locate the process and hand it the output request.
1824 */
1825 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1826 if (pProcess)
1827 {
1828 rc = VGSvcGstCtrlProcessHandleOutput(pProcess, pHostCtx, uHandleID, _64K /* cbToRead */, fFlags);
1829 if (RT_FAILURE(rc))
1830 VGSvcError("Error getting output for PID=%RU32, rc=%Rrc\n", uPID, rc);
1831 VGSvcGstCtrlProcessRelease(pProcess);
1832 }
1833 else
1834 {
1835 VGSvcError("Could not find PID %u for draining handle %u (%#x).\n", uPID, uHandleID, uHandleID);
1836 rc = VERR_PROCESS_NOT_FOUND;
1837/** @todo r=bird:
1838 *
1839 * No way to report status status code for output requests?
1840 *
1841 */
1842 }
1843 }
1844 else
1845 {
1846 VGSvcError("Error fetching parameters for process output request: %Rrc\n", rc);
1847 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1848 }
1849
1850#ifdef DEBUG_andy
1851 VGSvcVerbose(4, "Getting output for PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1852#endif
1853 return rc;
1854}
1855
1856
1857/**
1858 * Tells a guest process to terminate.
1859 *
1860 * @returns VBox status code.
1861 * @param pSession The session which is in charge.
1862 * @param pHostCtx The host context to use.
1863 */
1864static int vgsvcGstCtrlSessionHandleProcTerminate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1865{
1866 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1867 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1868
1869 /*
1870 * Retrieve the request.
1871 */
1872 uint32_t uPID;
1873 int rc = VbglR3GuestCtrlProcGetTerminate(pHostCtx, &uPID);
1874 if (RT_SUCCESS(rc))
1875 {
1876 /*
1877 * Locate the process and terminate it.
1878 */
1879 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1880 if (pProcess)
1881 {
1882 rc = VGSvcGstCtrlProcessHandleTerm(pProcess);
1883 if (RT_FAILURE(rc))
1884 VGSvcError("Error terminating PID=%RU32, rc=%Rrc\n", uPID, rc);
1885
1886 VGSvcGstCtrlProcessRelease(pProcess);
1887 }
1888 else
1889 {
1890 VGSvcError("Could not find PID %u for termination.\n", uPID);
1891 rc = VERR_PROCESS_NOT_FOUND;
1892 }
1893 }
1894 else
1895 {
1896 VGSvcError("Error fetching parameters for process termination request: %Rrc\n", rc);
1897 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1898 }
1899#ifdef DEBUG_andy
1900 VGSvcVerbose(4, "Terminating PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1901#endif
1902 return rc;
1903}
1904
1905
1906static int vgsvcGstCtrlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1907{
1908 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1909 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1910
1911 /*
1912 * Retrieve the request.
1913 */
1914 uint32_t uPID;
1915 uint32_t uWaitFlags;
1916 uint32_t uTimeoutMS;
1917 int rc = VbglR3GuestCtrlProcGetWaitFor(pHostCtx, &uPID, &uWaitFlags, &uTimeoutMS);
1918 if (RT_SUCCESS(rc))
1919 {
1920 /*
1921 * Locate the process and the realize that this call makes no sense
1922 * since we'll notify the host when a process terminates anyway and
1923 * hopefully don't need any additional encouragement.
1924 */
1925 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1926 if (pProcess)
1927 {
1928 rc = VERR_NOT_IMPLEMENTED; /** @todo */
1929 VGSvcGstCtrlProcessRelease(pProcess);
1930 }
1931 else
1932 rc = VERR_NOT_FOUND;
1933 }
1934 else
1935 {
1936 VGSvcError("Error fetching parameters for process wait request: %Rrc\n", rc);
1937 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1938 }
1939 return rc;
1940}
1941
1942
1943#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
1944static int vgsvcGstCtrlSessionHandleFsQueryInfo(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1945{
1946 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1947 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1948
1949 /*
1950 * Retrieve the request.
1951 */
1952 char szPath[RTPATH_MAX];
1953 GSTCTLFSOBJATTRADD enmAttrAdd;
1954 uint32_t fFlags;
1955 RTFSOBJINFO objInfoRuntime;
1956
1957 int rc = VbglR3GuestCtrlFsGetQueryInfo(pHostCtx, szPath, sizeof(szPath), &enmAttrAdd, &fFlags);
1958 if (RT_SUCCESS(rc))
1959 {
1960 uint32_t fFlagsRuntime = 0;
1961
1962 if (!(fFlags & ~GSTCTL_PATH_F_VALID_MASK))
1963 {
1964 if (fFlags & GSTCTL_PATH_F_ON_LINK)
1965 fFlagsRuntime |= RTPATH_F_ON_LINK;
1966 if (fFlags & GSTCTL_PATH_F_FOLLOW_LINK)
1967 fFlagsRuntime |= RTPATH_F_FOLLOW_LINK;
1968 if (fFlags & GSTCTL_PATH_F_NO_SYMLINKS)
1969 fFlagsRuntime |= RTPATH_F_NO_SYMLINKS;
1970
1971 if (!RTPATH_F_IS_VALID(fFlagsRuntime, 0))
1972 rc = VERR_INVALID_PARAMETER;
1973 }
1974 else
1975 rc = VERR_INVALID_PARAMETER;
1976
1977 if (RT_FAILURE(rc))
1978 VGSvcError("Invalid fsqueryinfo flags: %#x (%#x)\n", fFlags, fFlagsRuntime);
1979
1980 if (RT_SUCCESS(rc))
1981 {
1982#define CASE_ATTR_ADD_VAL(a_Val) \
1983 case GSTCTL##a_Val: enmAttrRuntime = RT##a_Val; break;
1984
1985 RTFSOBJATTRADD enmAttrRuntime;
1986 switch (enmAttrAdd)
1987 {
1988 CASE_ATTR_ADD_VAL(FSOBJATTRADD_NOTHING);
1989 CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX);
1990 CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX_OWNER);
1991 CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX_GROUP);
1992 CASE_ATTR_ADD_VAL(FSOBJATTRADD_EASIZE);
1993 default:
1994 enmAttrRuntime = RTFSOBJATTRADD_NOTHING;
1995 break;
1996 }
1997
1998#undef CASE_ATTR_ADD_VAL
1999
2000 /*
2001 * For now we ASSUME that RTFSOBJINFO == GSTCTLFSOBJINFO, which implies that we simply can cast RTFSOBJINFO
2002 * to GSTCTLFSOBJINFO. This might change in the future, however, so be extra cautious here.
2003 *
2004 * Ditto for RTFSOBJATTR == GSTCTLFSOBJATTR.
2005 */
2006 AssertCompileSize(objInfoRuntime, sizeof(GSTCTLFSOBJINFO));
2007 AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, cbObject) == RT_OFFSETOF(GSTCTLFSOBJINFO, cbObject));
2008 AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, Attr) == RT_OFFSETOF(GSTCTLFSOBJINFO, Attr));
2009 AssertCompileSize(RTFSOBJATTR, sizeof(GSTCTLFSOBJATTR));
2010
2011 rc = RTPathQueryInfoEx(szPath, &objInfoRuntime, enmAttrRuntime, fFlagsRuntime);
2012 }
2013
2014 PGSTCTLFSOBJINFO pObjInfo = (PGSTCTLFSOBJINFO)&objInfoRuntime;
2015
2016 const char *pszUser = VGSvcIdCacheGetUidName(&pSession->UidCache, pObjInfo->Attr.u.Unix.uid, szPath, NULL /* pszRelativeTo */);
2017 const char *pszGroup = VGSvcIdCacheGetGidName(&pSession->GidCache, pObjInfo->Attr.u.Unix.gid, szPath, NULL /* pszRelativeTo */);
2018
2019 int rc2 = VbglR3GuestCtrlFsCbQueryInfoEx(pHostCtx, rc, pObjInfo, pszUser, pszGroup);
2020 if (RT_FAILURE(rc2))
2021 {
2022 VGSvcError("Failed to reply to fsqueryinfo request %Rrc, rc=%Rrc\n", rc, rc2);
2023 if (RT_SUCCESS(rc))
2024 rc = rc2;
2025 }
2026 }
2027 else
2028 {
2029 VGSvcError("Error fetching parameters for fsqueryinfo operation: %Rrc\n", rc);
2030 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2031 }
2032 return rc;
2033}
2034
2035
2036static int vgsvcGstCtrlSessionHandleFsCreateTemp(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
2037{
2038 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2039 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2040
2041 /*
2042 * Retrieve the request.
2043 */
2044 char szTemplate[RTPATH_MAX];
2045 char szPath[RTPATH_MAX];
2046 uint32_t fFlags = GSTCTL_CREATETEMP_F_NONE;
2047 RTFMODE fMode = 0700;
2048 int rc = VbglR3GuestCtrlFsGetCreateTemp(pHostCtx, szTemplate, sizeof(szTemplate), szPath, sizeof(szPath), &fFlags, &fMode);
2049 if (RT_SUCCESS(rc))
2050 {
2051 if (!(fFlags & ~GSTCTL_CREATETEMP_F_VALID_MASK))
2052 {
2053 const char *pszWhat = fFlags & GSTCTL_CREATETEMP_F_DIRECTORY ? "directory" : "file";
2054
2055 /* Validate that the template is as IPRT requires (asserted by IPRT). */
2056 if ( RTPathHasPath(szTemplate)
2057 || ( !strstr(szTemplate, "XXX")
2058 && szTemplate[strlen(szTemplate) - 1] != 'X'))
2059 {
2060 VGSvcError("createtemp: Template '%s' should contain a file name with no path and at least three consecutive 'X' characters or ending in 'X'\n",
2061 szTemplate);
2062 rc = VERR_INVALID_PARAMETER;
2063 }
2064
2065 if ( RT_SUCCESS(rc)
2066 && szPath[0] != '\0' && !RTPathStartsWithRoot(szPath))
2067 {
2068 VGSvcError("createtemp: Path '%s' must be absolute\n", szPath);
2069 rc = VERR_INVALID_PARAMETER;
2070 }
2071
2072 if (RT_SUCCESS(rc))
2073 {
2074 char szTemplateWithPath[RTPATH_MAX] = "";
2075 if (szPath[0] != '\0')
2076 {
2077 rc = RTStrCopy(szTemplateWithPath, sizeof(szTemplateWithPath), szPath);
2078 if (RT_FAILURE(rc))
2079 {
2080 VGSvcError("createtemp: Path '%s' too long\n", szPath);
2081 rc = VERR_INVALID_PARAMETER;
2082 }
2083 }
2084 else
2085 {
2086 rc = RTPathTemp(szTemplateWithPath, sizeof(szTemplateWithPath));
2087 if (RT_FAILURE(rc))
2088 {
2089 VGSvcError("createtemp: Failed to get the temporary directory (%Rrc)", rc);
2090 rc = VERR_INVALID_PARAMETER;
2091 }
2092 }
2093
2094 if (RT_SUCCESS(rc))
2095 {
2096 rc = RTPathAppend(szTemplateWithPath, sizeof(szTemplateWithPath), szTemplate);
2097 if (RT_FAILURE(rc))
2098 {
2099 VGSvcError("createtemp: Template '%s' too long for path\n", szTemplate);
2100 rc = VERR_INVALID_PARAMETER;
2101 }
2102 else
2103 {
2104 bool const fSecure = RT_BOOL(fFlags & GSTCTL_CREATETEMP_F_SECURE);
2105 if (fFlags & GSTCTL_CREATETEMP_F_DIRECTORY)
2106 {
2107 if (fSecure)
2108 rc = RTDirCreateTempSecure(szTemplateWithPath); /* File mode is fixed to 0700. */
2109 else
2110 rc = RTDirCreateTemp(szTemplate, fMode);
2111 }
2112 else /* File */
2113 {
2114 if (fSecure)
2115 rc = RTFileCreateTempSecure(szTemplateWithPath); /* File mode is fixed to 0700. */
2116 else
2117 rc = RTFileCreateTemp(szTemplate, fMode);
2118 }
2119
2120 VGSvcVerbose(3, "Creating temporary %s (szTemplate='%s', fFlags=%#x, fMode=%#x) -> rc=%Rrc\n",
2121 pszWhat, szTemplate, fFlags, fMode, rc);
2122 }
2123 }
2124 }
2125 }
2126 else
2127 {
2128 VGSvcError("Invalid temporary directory/file creation flags: %#x\n", fFlags);
2129 rc = VERR_NOT_SUPPORTED;
2130 }
2131
2132 /*
2133 * Report result back to host.
2134 */
2135 int rc2 = VbglR3GuestCtrlFsCbCreateTemp(pHostCtx, rc, szTemplate);
2136 if (RT_FAILURE(rc2))
2137 {
2138 VGSvcError("Failed to report temporary file/directory creation status, rc=%Rrc\n", rc2);
2139 if (RT_SUCCESS(rc))
2140 rc = rc2;
2141 }
2142 }
2143 else
2144 {
2145 VGSvcError("Error fetching parameters for file/directory creation operation: %Rrc\n", rc);
2146 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2147 }
2148 VGSvcVerbose(3, "Creating temporary file/directory returned rc=%Rrc\n", rc);
2149 return rc;
2150}
2151#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
2152
2153
2154int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2155 void **ppvScratchBuf, uint32_t *pcbScratchBuf, volatile bool *pfShutdown)
2156{
2157 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2158 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2159 AssertPtrReturn(*ppvScratchBuf, VERR_INVALID_POINTER);
2160 AssertPtrReturn(pfShutdown, VERR_INVALID_POINTER);
2161
2162
2163 /*
2164 * Only anonymous sessions (that is, sessions which run with local
2165 * service privileges) or spawned session processes can do certain
2166 * operations.
2167 */
2168 bool const fImpersonated = RT_BOOL(pSession->fFlags & ( VBOXSERVICECTRLSESSION_FLAG_SPAWN
2169 | VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS));
2170 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
2171
2172 switch (uMsg)
2173 {
2174 case HOST_MSG_SESSION_CLOSE:
2175 /* Shutdown (this spawn). */
2176 rc = VGSvcGstCtrlSessionClose(pSession);
2177 *pfShutdown = true; /* Shutdown in any case. */
2178 break;
2179
2180 case HOST_MSG_EXEC_CMD:
2181 rc = vgsvcGstCtrlSessionHandleProcExec(pSession, pHostCtx);
2182 break;
2183
2184 case HOST_MSG_EXEC_SET_INPUT:
2185 rc = vgsvcGstCtrlSessionHandleProcInput(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2186 break;
2187
2188 case HOST_MSG_EXEC_GET_OUTPUT:
2189 rc = vgsvcGstCtrlSessionHandleProcOutput(pSession, pHostCtx);
2190 break;
2191
2192 case HOST_MSG_EXEC_TERMINATE:
2193 rc = vgsvcGstCtrlSessionHandleProcTerminate(pSession, pHostCtx);
2194 break;
2195
2196 case HOST_MSG_EXEC_WAIT_FOR:
2197 rc = vgsvcGstCtrlSessionHandleProcWaitFor(pSession, pHostCtx);
2198 break;
2199
2200#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2201 case HOST_MSG_FS_QUERY_INFO:
2202 if (fImpersonated)
2203 rc = vgsvcGstCtrlSessionHandleFsQueryInfo(pSession, pHostCtx);
2204 break;
2205
2206 case HOST_MSG_FS_CREATE_TEMP:
2207 if (fImpersonated)
2208 rc = vgsvcGstCtrlSessionHandleFsCreateTemp(pSession, pHostCtx);
2209 break;
2210#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
2211
2212 case HOST_MSG_FILE_OPEN:
2213 if (fImpersonated)
2214 rc = vgsvcGstCtrlSessionHandleFileOpen(pSession, pHostCtx);
2215 break;
2216
2217 case HOST_MSG_FILE_CLOSE:
2218 if (fImpersonated)
2219 rc = vgsvcGstCtrlSessionHandleFileClose(pSession, pHostCtx);
2220 break;
2221
2222 case HOST_MSG_FILE_READ:
2223 if (fImpersonated)
2224 rc = vgsvcGstCtrlSessionHandleFileRead(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2225 break;
2226
2227 case HOST_MSG_FILE_READ_AT:
2228 if (fImpersonated)
2229 rc = vgsvcGstCtrlSessionHandleFileReadAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2230 break;
2231
2232 case HOST_MSG_FILE_WRITE:
2233 if (fImpersonated)
2234 rc = vgsvcGstCtrlSessionHandleFileWrite(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2235 break;
2236
2237 case HOST_MSG_FILE_WRITE_AT:
2238 if (fImpersonated)
2239 rc = vgsvcGstCtrlSessionHandleFileWriteAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2240 break;
2241
2242 case HOST_MSG_FILE_SEEK:
2243 if (fImpersonated)
2244 rc = vgsvcGstCtrlSessionHandleFileSeek(pSession, pHostCtx);
2245 break;
2246
2247 case HOST_MSG_FILE_TELL:
2248 if (fImpersonated)
2249 rc = vgsvcGstCtrlSessionHandleFileTell(pSession, pHostCtx);
2250 break;
2251
2252 case HOST_MSG_FILE_SET_SIZE:
2253 if (fImpersonated)
2254 rc = vgsvcGstCtrlSessionHandleFileSetSize(pSession, pHostCtx);
2255 break;
2256
2257#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2258 case HOST_MSG_FILE_REMOVE:
2259 if (fImpersonated)
2260 rc = vgsvcGstCtrlSessionHandleFileRemove(pSession, pHostCtx);
2261 break;
2262
2263 case HOST_MSG_DIR_OPEN:
2264 if (fImpersonated)
2265 rc = vgsvcGstCtrlSessionHandleDirOpen(pSession, pHostCtx);
2266 break;
2267
2268 case HOST_MSG_DIR_CLOSE:
2269 if (fImpersonated)
2270 rc = vgsvcGstCtrlSessionHandleDirClose(pSession, pHostCtx);
2271 break;
2272
2273 case HOST_MSG_DIR_READ:
2274 if (fImpersonated)
2275 rc = vgsvcGstCtrlSessionHandleDirRead(pSession, pHostCtx);
2276 break;
2277
2278 case HOST_MSG_DIR_REWIND:
2279 if (fImpersonated)
2280 rc = vgsvcGstCtrlSessionHandleDirRewind(pSession, pHostCtx);
2281 break;
2282
2283 case HOST_MSG_DIR_CREATE:
2284 if (fImpersonated)
2285 rc = vgsvcGstCtrlSessionHandleDirCreate(pSession, pHostCtx);
2286 break;
2287
2288 case HOST_MSG_DIR_LIST:
2289 if (fImpersonated)
2290 rc = vgsvcGstCtrlSessionHandleDirList(pSession, pHostCtx);
2291 break;
2292#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
2293
2294 case HOST_MSG_DIR_REMOVE:
2295 if (fImpersonated)
2296 rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx);
2297 break;
2298
2299 case HOST_MSG_PATH_RENAME:
2300 if (fImpersonated)
2301 rc = vgsvcGstCtrlSessionHandlePathRename(pSession, pHostCtx);
2302 break;
2303
2304 case HOST_MSG_PATH_USER_DOCUMENTS:
2305 if (fImpersonated)
2306 rc = vgsvcGstCtrlSessionHandlePathUserDocuments(pSession, pHostCtx);
2307 break;
2308
2309 case HOST_MSG_PATH_USER_HOME:
2310 if (fImpersonated)
2311 rc = vgsvcGstCtrlSessionHandlePathUserHome(pSession, pHostCtx);
2312 break;
2313
2314 case HOST_MSG_SHUTDOWN:
2315 rc = vgsvcGstCtrlSessionHandleShutdown(pSession, pHostCtx);
2316 break;
2317
2318 default: /* Not supported, see next code block. */
2319 break;
2320 }
2321 if (RT_SUCCESS(rc))
2322 { /* likely */ }
2323 else if (rc != VERR_NOT_SUPPORTED) /* Note: Reply to host must must be sent by above handler. */
2324 VGSvcError("Error while handling message %s (%#x, cParms=%RU32), rc=%Rrc\n",
2325 GstCtrlHostMsgtoStr((eHostMsg)uMsg), uMsg, pHostCtx->uNumParms, rc);
2326 else
2327 {
2328 /* We must skip and notify host here as best we can... */
2329 VGSvcVerbose(1, "Unsupported message (uMsg=%RU32, cParms=%RU32) from host, skipping\n", uMsg, pHostCtx->uNumParms);
2330 if (VbglR3GuestCtrlSupportsOptimizations(pHostCtx->uClientID))
2331 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, VERR_NOT_SUPPORTED, uMsg);
2332 else
2333 VbglR3GuestCtrlMsgSkipOld(pHostCtx->uClientID);
2334 rc = VINF_SUCCESS;
2335 }
2336
2337 return rc;
2338}
2339
2340
2341/**
2342 * Thread main routine for a spawned guest session process.
2343 *
2344 * This thread runs in the main executable to control the spawned session process.
2345 *
2346 * @returns VBox status code.
2347 * @param hThreadSelf Thread handle.
2348 * @param pvUser Pointer to a VBOXSERVICECTRLSESSIONTHREAD structure.
2349 *
2350 */
2351static DECLCALLBACK(int) vgsvcGstCtrlSessionThread(RTTHREAD hThreadSelf, void *pvUser)
2352{
2353 PVBOXSERVICECTRLSESSIONTHREAD pThread = (PVBOXSERVICECTRLSESSIONTHREAD)pvUser;
2354 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2355
2356 uint32_t const idSession = pThread->pStartupInfo->uSessionID;
2357 uint32_t const idClient = g_idControlSvcClient;
2358 VGSvcVerbose(3, "Session ID=%RU32 thread running\n", idSession);
2359
2360 /* Let caller know that we're done initializing, regardless of the result. */
2361 int rc2 = RTThreadUserSignal(hThreadSelf);
2362 AssertRC(rc2);
2363
2364 /*
2365 * Wait for the child process to stop or the shutdown flag to be signalled.
2366 */
2367 RTPROCSTATUS ProcessStatus = { 0, RTPROCEXITREASON_NORMAL };
2368 bool fProcessAlive = true;
2369 bool fSessionCancelled = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
2370 uint32_t cMsShutdownTimeout = 30 * 1000; /** @todo Make this configurable. Later. */
2371 uint64_t msShutdownStart = 0;
2372 uint64_t const msStart = RTTimeMilliTS();
2373 size_t offSecretKey = 0;
2374 int rcWait;
2375 for (;;)
2376 {
2377 /* Secret key feeding. */
2378 if (offSecretKey < sizeof(pThread->abKey))
2379 {
2380 size_t cbWritten = 0;
2381 rc2 = RTPipeWrite(pThread->hKeyPipe, &pThread->abKey[offSecretKey], sizeof(pThread->abKey) - offSecretKey, &cbWritten);
2382 if (RT_SUCCESS(rc2))
2383 offSecretKey += cbWritten;
2384 }
2385
2386 /* Poll child process status. */
2387 rcWait = RTProcWaitNoResume(pThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
2388 if ( rcWait == VINF_SUCCESS
2389 || rcWait == VERR_PROCESS_NOT_FOUND)
2390 {
2391 fProcessAlive = false;
2392 break;
2393 }
2394 AssertMsgBreak(rcWait == VERR_PROCESS_RUNNING || rcWait == VERR_INTERRUPTED,
2395 ("Got unexpected rc=%Rrc while waiting for session process termination\n", rcWait));
2396
2397 /* Shutting down? */
2398 if (ASMAtomicReadBool(&pThread->fShutdown))
2399 {
2400 if (!msShutdownStart)
2401 {
2402 VGSvcVerbose(3, "Notifying guest session process (PID=%RU32, session ID=%RU32) ...\n",
2403 pThread->hProcess, idSession);
2404
2405 VBGLR3GUESTCTRLCMDCTX hostCtx =
2406 {
2407 /* .idClient = */ idClient,
2408 /* .idContext = */ VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
2409 /* .uProtocol = */ pThread->pStartupInfo->uProtocol,
2410 /* .cParams = */ 2
2411 };
2412 rc2 = VbglR3GuestCtrlSessionClose(&hostCtx, 0 /* fFlags */);
2413 if (RT_FAILURE(rc2))
2414 {
2415 VGSvcError("Unable to notify guest session process (PID=%RU32, session ID=%RU32), rc=%Rrc\n",
2416 pThread->hProcess, idSession, rc2);
2417
2418 if (rc2 == VERR_NOT_SUPPORTED)
2419 {
2420 /* Terminate guest session process in case it's not supported by a too old host. */
2421 rc2 = RTProcTerminate(pThread->hProcess);
2422 VGSvcVerbose(3, "Terminating guest session process (PID=%RU32) ended with rc=%Rrc\n",
2423 pThread->hProcess, rc2);
2424 }
2425 break;
2426 }
2427
2428 VGSvcVerbose(3, "Guest session ID=%RU32 thread was asked to terminate, waiting for session process to exit (%RU32 ms timeout) ...\n",
2429 idSession, cMsShutdownTimeout);
2430 msShutdownStart = RTTimeMilliTS();
2431 continue; /* Don't waste time on waiting. */
2432 }
2433 if (RTTimeMilliTS() - msShutdownStart > cMsShutdownTimeout)
2434 {
2435 VGSvcVerbose(3, "Guest session ID=%RU32 process did not shut down within time\n", idSession);
2436 break;
2437 }
2438 }
2439
2440 /* Cancel the prepared session stuff after 30 seconds. */
2441 if ( !fSessionCancelled
2442 && RTTimeMilliTS() - msStart >= 30000)
2443 {
2444 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
2445 fSessionCancelled = true;
2446 }
2447
2448/** @todo r=bird: This 100ms sleep is _extremely_ sucky! */
2449 RTThreadSleep(100); /* Wait a bit. */
2450 }
2451
2452 if (!fSessionCancelled)
2453 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
2454
2455 if (!fProcessAlive)
2456 {
2457 VGSvcVerbose(2, "Guest session process (ID=%RU32) terminated with rc=%Rrc, reason=%d, status=%d\n",
2458 idSession, rcWait, ProcessStatus.enmReason, ProcessStatus.iStatus);
2459 if (ProcessStatus.iStatus == RTEXITCODE_INIT)
2460 {
2461 VGSvcError("Guest session process (ID=%RU32) failed to initialize. Here some hints:\n", idSession);
2462 VGSvcError("- Is logging enabled and the output directory is read-only by the guest session user?\n");
2463 /** @todo Add more here. */
2464 }
2465 }
2466
2467 uint32_t uSessionStatus = GUEST_SESSION_NOTIFYTYPE_UNDEFINED;
2468 int32_t iSessionResult = VINF_SUCCESS;
2469
2470 if (fProcessAlive)
2471 {
2472 for (int i = 0; i < 3; i++)
2473 {
2474 if (i)
2475 RTThreadSleep(3000);
2476
2477 VGSvcVerbose(2, "Guest session ID=%RU32 process still alive, killing attempt %d/3\n", idSession, i + 1);
2478
2479 rc2 = RTProcTerminate(pThread->hProcess);
2480 if (RT_SUCCESS(rc2))
2481 break;
2482 }
2483
2484 VGSvcVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n", idSession, rc2);
2485 uSessionStatus = RT_SUCCESS(rc2) ? GUEST_SESSION_NOTIFYTYPE_TOK : GUEST_SESSION_NOTIFYTYPE_TOA;
2486 }
2487 else if (RT_SUCCESS(rcWait))
2488 {
2489 switch (ProcessStatus.enmReason)
2490 {
2491 case RTPROCEXITREASON_NORMAL:
2492 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
2493 iSessionResult = ProcessStatus.iStatus; /* Report back the session's exit code. */
2494 break;
2495
2496 case RTPROCEXITREASON_ABEND:
2497 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
2498 /* iSessionResult is undefined (0). */
2499 break;
2500
2501 case RTPROCEXITREASON_SIGNAL:
2502 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
2503 iSessionResult = ProcessStatus.iStatus; /* Report back the signal number. */
2504 break;
2505
2506 default:
2507 AssertMsgFailed(("Unhandled process termination reason (%d)\n", ProcessStatus.enmReason));
2508 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
2509 break;
2510 }
2511 }
2512 else
2513 {
2514 /* If we didn't find the guest process anymore, just assume it terminated normally. */
2515 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
2516 }
2517
2518 /* Make sure to set stopped state before we let the host know. */
2519 ASMAtomicWriteBool(&pThread->fStopped, true);
2520
2521 /* Report final status, regardless if we failed to wait above, so that the host knows what's going on. */
2522 VGSvcVerbose(3, "Reporting final status %RU32 of session ID=%RU32\n", uSessionStatus, idSession);
2523 Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED);
2524
2525 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
2526 0 /* uProtocol, unused */, 0 /* uNumParms, unused */ };
2527 rc2 = VbglR3GuestCtrlSessionNotify(&ctx, uSessionStatus, iSessionResult);
2528 if (RT_FAILURE(rc2))
2529 VGSvcError("Reporting final status of session ID=%RU32 failed with rc=%Rrc\n", idSession, rc2);
2530
2531 VGSvcVerbose(3, "Thread for session ID=%RU32 ended with sessionStatus=%#x (%RU32), sessionRc=%#x (%Rrc)\n",
2532 idSession, uSessionStatus, uSessionStatus, iSessionResult, iSessionResult);
2533
2534 return VINF_SUCCESS;
2535}
2536
2537/**
2538 * Reads the secret key the parent VBoxService instance passed us and pass it
2539 * along as a authentication token to the host service.
2540 *
2541 * For older hosts, this sets up the message filtering.
2542 *
2543 * @returns VBox status code.
2544 * @param idClient The HGCM client ID.
2545 * @param idSession The session ID.
2546 */
2547static int vgsvcGstCtrlSessionReadKeyAndAccept(uint32_t idClient, uint32_t idSession)
2548{
2549 /*
2550 * Read it.
2551 */
2552 RTHANDLE Handle;
2553 int rc = RTHandleGetStandard(RTHANDLESTD_INPUT, true /*fLeaveOpen*/, &Handle);
2554 if (RT_SUCCESS(rc))
2555 {
2556 if (Handle.enmType == RTHANDLETYPE_PIPE)
2557 {
2558 uint8_t abSecretKey[RT_SIZEOFMEMB(VBOXSERVICECTRLSESSIONTHREAD, abKey)];
2559 rc = RTPipeReadBlocking(Handle.u.hPipe, abSecretKey, sizeof(abSecretKey), NULL);
2560 if (RT_SUCCESS(rc))
2561 {
2562 VGSvcVerbose(3, "Got secret key from standard input.\n");
2563
2564 /*
2565 * Do the accepting, if appropriate.
2566 */
2567 if (g_fControlSupportsOptimizations)
2568 {
2569 rc = VbglR3GuestCtrlSessionAccept(idClient, idSession, abSecretKey, sizeof(abSecretKey));
2570 if (RT_SUCCESS(rc))
2571 VGSvcVerbose(3, "Session %u accepted (client ID %u)\n", idClient, idSession);
2572 else
2573 VGSvcError("Failed to accept session %u (client ID %u): %Rrc\n", idClient, idSession, rc);
2574 }
2575 else
2576 {
2577 /* For legacy hosts, we do the filtering thingy. */
2578 rc = VbglR3GuestCtrlMsgFilterSet(idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
2579 VBOX_GUESTCTRL_FILTER_BY_SESSION(idSession), 0);
2580 if (RT_SUCCESS(rc))
2581 VGSvcVerbose(3, "Session %u filtering successfully enabled\n", idSession);
2582 else
2583 VGSvcError("Failed to set session filter: %Rrc\n", rc);
2584 }
2585 }
2586 else
2587 VGSvcError("Error reading secret key from standard input: %Rrc\n", rc);
2588 }
2589 else
2590 {
2591 VGSvcError("Standard input is not a pipe!\n");
2592 rc = VERR_INVALID_HANDLE;
2593 }
2594 RTHandleClose(&Handle);
2595 }
2596 else
2597 VGSvcError("RTHandleGetStandard failed on standard input: %Rrc\n", rc);
2598 return rc;
2599}
2600
2601/**
2602 * Invalidates a guest session by updating all it's internal parameters like host features and stuff.
2603 *
2604 * @param pSession Session to invalidate.
2605 * @param idClient Client ID to use.
2606 */
2607static void vgsvcGstCtrlSessionInvalidate(PVBOXSERVICECTRLSESSION pSession, uint32_t idClient)
2608{
2609 RT_NOREF(pSession);
2610
2611 VGSvcVerbose(1, "Invalidating session %RU32 (client ID=%RU32)\n", idClient, pSession->StartupInfo.uSessionID);
2612
2613 int rc2 = VbglR3GuestCtrlQueryFeatures(idClient, &g_fControlHostFeatures0);
2614 if (RT_SUCCESS(rc2)) /* Querying host features is not fatal -- do not use rc here. */
2615 {
2616 VGSvcVerbose(1, "g_fControlHostFeatures0=%#x\n", g_fControlHostFeatures0);
2617 }
2618 else
2619 VGSvcVerbose(1, "Querying host features failed with %Rrc\n", rc2);
2620}
2621
2622/**
2623 * Main message handler for the guest control session process.
2624 *
2625 * @returns exit code.
2626 * @param pSession Pointer to g_Session.
2627 * @thread main.
2628 */
2629static RTEXITCODE vgsvcGstCtrlSessionSpawnWorker(PVBOXSERVICECTRLSESSION pSession)
2630{
2631 AssertPtrReturn(pSession, RTEXITCODE_FAILURE);
2632 VGSvcVerbose(0, "Hi, this is guest session ID=%RU32\n", pSession->StartupInfo.uSessionID);
2633
2634 /*
2635 * Connect to the host service.
2636 */
2637 uint32_t idClient;
2638 int rc = VbglR3GuestCtrlConnect(&idClient);
2639 if (RT_FAILURE(rc))
2640 return VGSvcError("Error connecting to guest control service, rc=%Rrc\n", rc);
2641 g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(idClient);
2642 g_idControlSvcClient = idClient;
2643
2644 VGSvcVerbose(1, "Using client ID=%RU32\n", idClient);
2645
2646 vgsvcGstCtrlSessionInvalidate(pSession, idClient);
2647
2648 rc = vgsvcGstCtrlSessionReadKeyAndAccept(idClient, pSession->StartupInfo.uSessionID);
2649 if (RT_SUCCESS(rc))
2650 {
2651 /*
2652 * Report started status.
2653 * If session status cannot be posted to the host for some reason, bail out.
2654 */
2655 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID),
2656 0 /* uProtocol, unused */, 0 /* uNumParms, unused */ };
2657 rc = VbglR3GuestCtrlSessionNotify(&ctx, GUEST_SESSION_NOTIFYTYPE_STARTED, VINF_SUCCESS);
2658 if (RT_SUCCESS(rc))
2659 {
2660 /*
2661 * Allocate a scratch buffer for messages which also send payload data with them.
2662 * This buffer may grow if the host sends us larger chunks of data.
2663 */
2664 uint32_t cbScratchBuf = _64K;
2665 void *pvScratchBuf = RTMemAlloc(cbScratchBuf);
2666 if (pvScratchBuf)
2667 {
2668 int cFailedMsgPeeks = 0;
2669
2670 /*
2671 * Message processing loop.
2672 */
2673 VBGLR3GUESTCTRLCMDCTX CtxHost = { idClient, 0 /* Context ID */, pSession->StartupInfo.uProtocol, 0 };
2674 for (;;)
2675 {
2676 VGSvcVerbose(3, "Waiting for host msg ...\n");
2677 uint32_t uMsg = 0;
2678 rc = VbglR3GuestCtrlMsgPeekWait(idClient, &uMsg, &CtxHost.uNumParms, NULL);
2679 if (RT_SUCCESS(rc))
2680 {
2681 VGSvcVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved (%Rrc)\n", uMsg, CtxHost.uNumParms, rc);
2682
2683 /*
2684 * Pass it on to the session handler.
2685 * Note! Only when handling HOST_SESSION_CLOSE is the rc used.
2686 */
2687 bool fShutdown = false;
2688 rc = VGSvcGstCtrlSessionHandler(pSession, uMsg, &CtxHost, &pvScratchBuf, &cbScratchBuf, &fShutdown);
2689 if (fShutdown)
2690 break;
2691
2692 cFailedMsgPeeks = 0;
2693
2694 /* Let others run (guests are often single CPU) ... */
2695 RTThreadYield();
2696 }
2697 /*
2698 * Handle restore notification from host. All the context IDs (sessions,
2699 * files, proceses, etc) are invalidated by a VM restore and must be closed.
2700 */
2701 else if (rc == VERR_VM_RESTORED)
2702 {
2703 VGSvcVerbose(1, "The VM session ID changed (i.e. restored), closing stale session %RU32\n",
2704 pSession->StartupInfo.uSessionID);
2705
2706 /* We currently don't serialize guest sessions, guest processes and other guest control objects
2707 * within saved states. So just close this session and report success to the parent process.
2708 *
2709 * Note: Not notifying the host here is intentional, as it wouldn't have any information
2710 * about what to do with it.
2711 */
2712 rc = VINF_SUCCESS; /* Report success as exit code. */
2713 break;
2714 }
2715 else
2716 {
2717 VGSvcVerbose(1, "Getting host message failed with %Rrc\n", rc);
2718
2719 if (cFailedMsgPeeks++ == 3)
2720 break;
2721
2722 RTThreadSleep(3 * RT_MS_1SEC);
2723
2724 /** @todo Shouldn't we have a plan for handling connection loss and such? */
2725 }
2726 }
2727
2728 /*
2729 * Shutdown.
2730 */
2731 RTMemFree(pvScratchBuf);
2732 }
2733 else
2734 rc = VERR_NO_MEMORY;
2735
2736 VGSvcVerbose(0, "Session %RU32 ended\n", pSession->StartupInfo.uSessionID);
2737 }
2738 else
2739 VGSvcError("Reporting session ID=%RU32 started status failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
2740 }
2741 else
2742 VGSvcError("Setting message filterAdd=0x%x failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
2743
2744 VGSvcVerbose(3, "Disconnecting client ID=%RU32 ...\n", idClient);
2745 VbglR3GuestCtrlDisconnect(idClient);
2746 g_idControlSvcClient = 0;
2747
2748 VGSvcVerbose(3, "Session worker returned with rc=%Rrc\n", rc);
2749 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2750}
2751
2752
2753/**
2754 * Finds a (formerly) started guest process given by its PID and increases its
2755 * reference count.
2756 *
2757 * Must be decreased by the caller with VGSvcGstCtrlProcessRelease().
2758 *
2759 * @returns Guest process if found, otherwise NULL.
2760 * @param pSession Pointer to guest session where to search process in.
2761 * @param uPID PID to search for.
2762 *
2763 * @note This does *not lock the process!
2764 */
2765PVBOXSERVICECTRLPROCESS VGSvcGstCtrlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID)
2766{
2767 AssertPtrReturn(pSession, NULL);
2768
2769 PVBOXSERVICECTRLPROCESS pProcess = NULL;
2770 int rc = RTCritSectEnter(&pSession->CritSect);
2771 if (RT_SUCCESS(rc))
2772 {
2773 PVBOXSERVICECTRLPROCESS pCurProcess;
2774 RTListForEach(&pSession->lstProcesses, pCurProcess, VBOXSERVICECTRLPROCESS, Node)
2775 {
2776 if (pCurProcess->uPID == uPID)
2777 {
2778 rc = RTCritSectEnter(&pCurProcess->CritSect);
2779 if (RT_SUCCESS(rc))
2780 {
2781 pCurProcess->cRefs++;
2782 rc = RTCritSectLeave(&pCurProcess->CritSect);
2783 AssertRC(rc);
2784 }
2785
2786 if (RT_SUCCESS(rc))
2787 pProcess = pCurProcess;
2788 break;
2789 }
2790 }
2791
2792 rc = RTCritSectLeave(&pSession->CritSect);
2793 AssertRC(rc);
2794 }
2795
2796 return pProcess;
2797}
2798
2799
2800int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession)
2801{
2802 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2803
2804 VGSvcVerbose(0, "Session %RU32 is about to close ...\n", pSession->StartupInfo.uSessionID);
2805
2806 int rc = RTCritSectEnter(&pSession->CritSect);
2807 if (RT_SUCCESS(rc))
2808 {
2809 /*
2810 * Close all guest processes.
2811 */
2812 VGSvcVerbose(0, "Stopping all guest processes ...\n");
2813
2814 /* Signal all guest processes in the active list that we want to shutdown. */
2815 PVBOXSERVICECTRLPROCESS pProcess;
2816 RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
2817 VGSvcGstCtrlProcessStop(pProcess);
2818
2819 VGSvcVerbose(1, "%RU32 guest processes were signalled to stop\n", pSession->cProcesses);
2820
2821 /* Wait for all active threads to shutdown and destroy the active thread list. */
2822 PVBOXSERVICECTRLPROCESS pProcessNext;
2823 RTListForEachSafe(&pSession->lstProcesses, pProcess, pProcessNext, VBOXSERVICECTRLPROCESS, Node)
2824 {
2825 int rc3 = RTCritSectLeave(&pSession->CritSect);
2826 AssertRC(rc3);
2827
2828 int rc2 = VGSvcGstCtrlProcessWait(pProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
2829
2830 rc3 = RTCritSectEnter(&pSession->CritSect);
2831 AssertRC(rc3);
2832
2833 if (RT_SUCCESS(rc2))
2834 {
2835 rc2 = vgsvcGstCtrlSessionProcessRemoveInternal(pSession, pProcess);
2836 if (RT_SUCCESS(rc2))
2837 {
2838 VGSvcGstCtrlProcessFree(pProcess);
2839 pProcess = NULL;
2840 }
2841 }
2842 }
2843
2844 AssertMsg(pSession->cProcesses == 0,
2845 ("Session process list still contains %RU32 when it should not\n", pSession->cProcesses));
2846 AssertMsg(RTListIsEmpty(&pSession->lstProcesses),
2847 ("Session process list is not empty when it should\n"));
2848
2849 /*
2850 * Close all left guest files.
2851 */
2852 VGSvcVerbose(0, "Closing all guest files ...\n");
2853
2854 PVBOXSERVICECTRLFILE pFile, pFileNext;
2855 RTListForEachSafe(&pSession->lstFiles, pFile, pFileNext, VBOXSERVICECTRLFILE, Node)
2856 {
2857 int rc2 = vgsvcGstCtrlSessionFileFree(pFile);
2858 if (RT_FAILURE(rc2))
2859 {
2860 VGSvcError("Unable to close file '%s'; rc=%Rrc\n", pFile->pszName, rc2);
2861 if (RT_SUCCESS(rc))
2862 rc = rc2;
2863 /* Keep going. */
2864 }
2865
2866 pFile = NULL; /* To make it obvious. */
2867 }
2868
2869#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2870 AssertMsg(pSession->cDirs == 0,
2871 ("Session directory list still contains %RU32 when it should not\n", pSession->cDirs));
2872 AssertMsg(RTListIsEmpty(&pSession->lstDirs),
2873 ("Session directory list is not empty when it should\n"));
2874#endif
2875 AssertMsg(pSession->cFiles == 0,
2876 ("Session file list still contains %RU32 when it should not\n", pSession->cFiles));
2877 AssertMsg(RTListIsEmpty(&pSession->lstFiles),
2878 ("Session file list is not empty when it should\n"));
2879
2880 int rc2 = RTCritSectLeave(&pSession->CritSect);
2881 if (RT_SUCCESS(rc))
2882 rc = rc2;
2883 }
2884
2885 return rc;
2886}
2887
2888
2889int VGSvcGstCtrlSessionDestroy(PVBOXSERVICECTRLSESSION pSession)
2890{
2891 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2892
2893 int rc = VGSvcGstCtrlSessionClose(pSession);
2894
2895 /* Destroy critical section. */
2896 RTCritSectDelete(&pSession->CritSect);
2897
2898 return rc;
2899}
2900
2901
2902int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t fFlags)
2903{
2904 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2905
2906 RTListInit(&pSession->lstProcesses);
2907#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2908 RTListInit(&pSession->lstDirs);
2909#endif
2910 RTListInit(&pSession->lstFiles);
2911
2912 pSession->cProcesses = 0;
2913 pSession->cFiles = 0;
2914
2915 pSession->fFlags = fFlags;
2916
2917#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2918 RT_ZERO(pSession->UidCache);
2919 RT_ZERO(pSession->GidCache);
2920#endif
2921
2922 /* Init critical section for protecting the thread lists. */
2923 int rc = RTCritSectInit(&pSession->CritSect);
2924 AssertRC(rc);
2925
2926 return rc;
2927}
2928
2929
2930/**
2931 * Adds a guest process to a session's process list.
2932 *
2933 * @return VBox status code.
2934 * @param pSession Guest session to add process to.
2935 * @param pProcess Guest process to add.
2936 */
2937int VGSvcGstCtrlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
2938{
2939 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2940 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2941
2942 int rc = RTCritSectEnter(&pSession->CritSect);
2943 if (RT_SUCCESS(rc))
2944 {
2945 VGSvcVerbose(3, "Adding process (PID %RU32) to session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
2946
2947 /* Add process to session list. */
2948 RTListAppend(&pSession->lstProcesses, &pProcess->Node);
2949
2950 pSession->cProcesses++;
2951 VGSvcVerbose(3, "Now session ID=%RU32 has %RU32 processes total\n",
2952 pSession->StartupInfo.uSessionID, pSession->cProcesses);
2953
2954 int rc2 = RTCritSectLeave(&pSession->CritSect);
2955 if (RT_SUCCESS(rc))
2956 rc = rc2;
2957 }
2958
2959 return VINF_SUCCESS;
2960}
2961
2962/**
2963 * Removes a guest process from a session's process list.
2964 * Internal version, does not do locking.
2965 *
2966 * @return VBox status code.
2967 * @param pSession Guest session to remove process from.
2968 * @param pProcess Guest process to remove.
2969 */
2970static int vgsvcGstCtrlSessionProcessRemoveInternal(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
2971{
2972 VGSvcVerbose(3, "Removing process (PID %RU32) from session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
2973 AssertReturn(pProcess->cRefs == 0, VERR_WRONG_ORDER);
2974
2975 RTListNodeRemove(&pProcess->Node);
2976
2977 AssertReturn(pSession->cProcesses, VERR_WRONG_ORDER);
2978 pSession->cProcesses--;
2979 VGSvcVerbose(3, "Now session ID=%RU32 has %RU32 processes total\n",
2980 pSession->StartupInfo.uSessionID, pSession->cProcesses);
2981
2982 return VINF_SUCCESS;
2983}
2984
2985/**
2986 * Removes a guest process from a session's process list.
2987 *
2988 * @return VBox status code.
2989 * @param pSession Guest session to remove process from.
2990 * @param pProcess Guest process to remove.
2991 */
2992int VGSvcGstCtrlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
2993{
2994 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2995 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2996
2997 int rc = RTCritSectEnter(&pSession->CritSect);
2998 if (RT_SUCCESS(rc))
2999 {
3000 rc = vgsvcGstCtrlSessionProcessRemoveInternal(pSession, pProcess);
3001
3002 int rc2 = RTCritSectLeave(&pSession->CritSect);
3003 if (RT_SUCCESS(rc))
3004 rc = rc2;
3005 }
3006
3007 return rc;
3008}
3009
3010
3011/**
3012 * Determines whether starting a new guest process according to the
3013 * maximum number of concurrent guest processes defined is allowed or not.
3014 *
3015 * @return VBox status code.
3016 * @param pSession The guest session.
3017 * @param pfAllowed \c True if starting (another) guest process
3018 * is allowed, \c false if not.
3019 */
3020int VGSvcGstCtrlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pfAllowed)
3021{
3022 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3023 AssertPtrReturn(pfAllowed, VERR_INVALID_POINTER);
3024
3025 int rc = RTCritSectEnter(&pSession->CritSect);
3026 if (RT_SUCCESS(rc))
3027 {
3028 /*
3029 * Check if we're respecting our memory policy by checking
3030 * how many guest processes are started and served already.
3031 */
3032 bool fLimitReached = false;
3033 if (pSession->uProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
3034 {
3035 VGSvcVerbose(3, "Maximum kept guest processes set to %RU32, acurrent=%RU32\n",
3036 pSession->uProcsMaxKept, pSession->cProcesses);
3037
3038 int32_t iProcsLeft = (pSession->uProcsMaxKept - pSession->cProcesses - 1);
3039 if (iProcsLeft < 0)
3040 {
3041 VGSvcVerbose(3, "Maximum running guest processes reached (%RU32)\n", pSession->uProcsMaxKept);
3042 fLimitReached = true;
3043 }
3044 }
3045
3046 *pfAllowed = !fLimitReached;
3047
3048 int rc2 = RTCritSectLeave(&pSession->CritSect);
3049 if (RT_SUCCESS(rc))
3050 rc = rc2;
3051 }
3052
3053 return rc;
3054}
3055
3056
3057/**
3058 * Cleans up stopped and no longer used processes.
3059 *
3060 * This will free and remove processes from the session's process list.
3061 *
3062 * @returns VBox status code.
3063 * @param pSession Session to clean up processes for.
3064 */
3065static int vgsvcGstCtrlSessionCleanupProcesses(const PVBOXSERVICECTRLSESSION pSession)
3066{
3067 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3068
3069 VGSvcVerbose(3, "Cleaning up stopped processes for session %RU32 ...\n", pSession->StartupInfo.uSessionID);
3070
3071 int rc2 = RTCritSectEnter(&pSession->CritSect);
3072 AssertRC(rc2);
3073
3074 int rc = VINF_SUCCESS;
3075
3076 PVBOXSERVICECTRLPROCESS pCurProcess, pNextProcess;
3077 RTListForEachSafe(&pSession->lstProcesses, pCurProcess, pNextProcess, VBOXSERVICECTRLPROCESS, Node)
3078 {
3079 if (ASMAtomicReadBool(&pCurProcess->fStopped))
3080 {
3081 rc2 = RTCritSectLeave(&pSession->CritSect);
3082 AssertRC(rc2);
3083
3084 rc = VGSvcGstCtrlProcessWait(pCurProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
3085 if (RT_SUCCESS(rc))
3086 {
3087 VGSvcGstCtrlSessionProcessRemove(pSession, pCurProcess);
3088 VGSvcGstCtrlProcessFree(pCurProcess);
3089 }
3090
3091 rc2 = RTCritSectEnter(&pSession->CritSect);
3092 AssertRC(rc2);
3093
3094 /* If failed, try next time we're being called. */
3095 }
3096 }
3097
3098 rc2 = RTCritSectLeave(&pSession->CritSect);
3099 AssertRC(rc2);
3100
3101 if (RT_FAILURE(rc))
3102 VGSvcError("Cleaning up stopped processes for session %RU32 failed with %Rrc\n", pSession->StartupInfo.uSessionID, rc);
3103
3104 return rc;
3105}
3106
3107
3108/**
3109 * Creates the process for a guest session.
3110 *
3111 * @return VBox status code.
3112 * @param pSessionStartupInfo Session startup info.
3113 * @param pSessionThread The session thread under construction.
3114 * @param uCtrlSessionThread The session thread debug ordinal.
3115 */
3116static int vgsvcVGSvcGstCtrlSessionThreadCreateProcess(const PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pSessionStartupInfo,
3117 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread, uint32_t uCtrlSessionThread)
3118{
3119 RT_NOREF(uCtrlSessionThread);
3120
3121 /*
3122 * Is this an anonymous session? Anonymous sessions run with the same
3123 * privileges as the main VBoxService executable.
3124 */
3125 bool const fAnonymous = pSessionThread->pStartupInfo->pszUser
3126 && pSessionThread->pStartupInfo->pszUser[0] == '\0';
3127 if (fAnonymous)
3128 {
3129 Assert(!strlen(pSessionThread->pStartupInfo->pszPassword));
3130 Assert(!strlen(pSessionThread->pStartupInfo->pszDomain));
3131
3132 VGSvcVerbose(3, "New anonymous guest session ID=%RU32 created, fFlags=%x, using protocol %RU32\n",
3133 pSessionStartupInfo->uSessionID,
3134 pSessionStartupInfo->fFlags,
3135 pSessionStartupInfo->uProtocol);
3136 }
3137 else
3138 {
3139 VGSvcVerbose(3, "Spawning new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, fFlags=%x, using protocol %RU32\n",
3140 pSessionStartupInfo->uSessionID,
3141 pSessionStartupInfo->pszUser,
3142#ifdef DEBUG
3143 pSessionStartupInfo->pszPassword,
3144#else
3145 "XXX", /* Never show passwords in release mode. */
3146#endif
3147 pSessionStartupInfo->pszDomain,
3148 pSessionStartupInfo->fFlags,
3149 pSessionStartupInfo->uProtocol);
3150 }
3151
3152 /*
3153 * Spawn a child process for doing the actual session handling.
3154 * Start by assembling the argument list.
3155 */
3156 char szExeName[RTPATH_MAX];
3157 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
3158 AssertPtrReturn(pszExeName, VERR_FILENAME_TOO_LONG);
3159
3160 char szParmSessionID[32];
3161 RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", pSessionThread->pStartupInfo->uSessionID);
3162
3163 char szParmSessionProto[32];
3164 RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
3165 pSessionThread->pStartupInfo->uProtocol);
3166#ifdef DEBUG
3167 char szParmThreadId[32];
3168 RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", uCtrlSessionThread);
3169#endif
3170 unsigned idxArg = 0; /* Next index in argument vector. */
3171 char const *apszArgs[24];
3172
3173 apszArgs[idxArg++] = pszExeName;
3174#ifdef VBOXSERVICE_ARG1_UTF8_ARGV
3175 apszArgs[idxArg++] = VBOXSERVICE_ARG1_UTF8_ARGV; Assert(idxArg == 2);
3176#endif
3177 apszArgs[idxArg++] = "guestsession";
3178 apszArgs[idxArg++] = szParmSessionID;
3179 apszArgs[idxArg++] = szParmSessionProto;
3180#ifdef DEBUG
3181 apszArgs[idxArg++] = szParmThreadId;
3182#endif
3183 if (!fAnonymous) /* Do we need to pass a user name? */
3184 {
3185 apszArgs[idxArg++] = "--user";
3186 apszArgs[idxArg++] = pSessionThread->pStartupInfo->pszUser;
3187
3188 if (strlen(pSessionThread->pStartupInfo->pszDomain))
3189 {
3190 apszArgs[idxArg++] = "--domain";
3191 apszArgs[idxArg++] = pSessionThread->pStartupInfo->pszDomain;
3192 }
3193 }
3194
3195 /* Add same verbose flags as parent process. */
3196 char szParmVerbose[32];
3197 if (g_cVerbosity > 0)
3198 {
3199 unsigned cVs = RT_MIN(g_cVerbosity, RT_ELEMENTS(szParmVerbose) - 2);
3200 szParmVerbose[0] = '-';
3201 memset(&szParmVerbose[1], 'v', cVs);
3202 szParmVerbose[1 + cVs] = '\0';
3203 apszArgs[idxArg++] = szParmVerbose;
3204 }
3205
3206 /* Add log file handling. Each session will have an own
3207 * log file, naming based on the parent log file. */
3208 char szParmLogFile[sizeof(g_szLogFile) + 128];
3209 if (g_szLogFile[0])
3210 {
3211 const char *pszSuffix = RTPathSuffix(g_szLogFile);
3212 if (!pszSuffix)
3213 pszSuffix = strchr(g_szLogFile, '\0');
3214 size_t cchBase = pszSuffix - g_szLogFile;
3215
3216 RTTIMESPEC Now;
3217 RTTimeNow(&Now);
3218 char szTime[64];
3219 RTTimeSpecToString(&Now, szTime, sizeof(szTime));
3220
3221 /* Replace out characters not allowed on Windows platforms, put in by RTTimeSpecToString(). */
3222 static const RTUNICP s_uszValidRangePairs[] =
3223 {
3224 ' ', ' ',
3225 '(', ')',
3226 '-', '.',
3227 '0', '9',
3228 'A', 'Z',
3229 'a', 'z',
3230 '_', '_',
3231 0xa0, 0xd7af,
3232 '\0'
3233 };
3234 ssize_t cReplaced = RTStrPurgeComplementSet(szTime, s_uszValidRangePairs, '_' /* chReplacement */);
3235 AssertReturn(cReplaced, VERR_INVALID_UTF8_ENCODING);
3236
3237#ifndef DEBUG
3238 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%s-%s%s",
3239 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, pSessionStartupInfo->pszUser, szTime, pszSuffix);
3240#else
3241 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%RU32-%s-%s%s",
3242 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, uCtrlSessionThread,
3243 pSessionStartupInfo->pszUser, szTime, pszSuffix);
3244#endif
3245 apszArgs[idxArg++] = "--logfile";
3246 apszArgs[idxArg++] = szParmLogFile;
3247 }
3248
3249#ifdef DEBUG
3250 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT)
3251 apszArgs[idxArg++] = "--dump-stdout";
3252 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR)
3253 apszArgs[idxArg++] = "--dump-stderr";
3254#endif
3255 apszArgs[idxArg] = NULL;
3256 Assert(idxArg < RT_ELEMENTS(apszArgs));
3257
3258 if (g_cVerbosity > 3)
3259 {
3260 VGSvcVerbose(4, "Spawning parameters:\n");
3261 for (idxArg = 0; apszArgs[idxArg]; idxArg++)
3262 VGSvcVerbose(4, " %s\n", apszArgs[idxArg]);
3263 }
3264
3265 /*
3266 * Flags.
3267 */
3268 uint32_t const fProcCreate = RTPROC_FLAGS_PROFILE
3269#ifdef RT_OS_WINDOWS
3270 | RTPROC_FLAGS_SERVICE
3271 | RTPROC_FLAGS_HIDDEN
3272#endif
3273 | VBOXSERVICE_PROC_F_UTF8_ARGV;
3274
3275 /*
3276 * Configure standard handles.
3277 */
3278 RTHANDLE hStdIn;
3279 int rc = RTPipeCreate(&hStdIn.u.hPipe, &pSessionThread->hKeyPipe, RTPIPE_C_INHERIT_READ);
3280 if (RT_SUCCESS(rc))
3281 {
3282 hStdIn.enmType = RTHANDLETYPE_PIPE;
3283
3284 RTHANDLE hStdOutAndErr;
3285 rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
3286 if (RT_SUCCESS(rc))
3287 {
3288 hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
3289
3290 /*
3291 * Windows: If a domain name is given, construct an UPN (User Principle Name)
3292 * with the domain name built-in, e.g. "[email protected]".
3293 */
3294 const char *pszUser = pSessionThread->pStartupInfo->pszUser;
3295#ifdef RT_OS_WINDOWS
3296 char *pszUserUPN = NULL;
3297 if (pSessionThread->pStartupInfo->pszDomain[0])
3298 {
3299 int cchbUserUPN = RTStrAPrintf(&pszUserUPN, "%s@%s",
3300 pSessionThread->pStartupInfo->pszUser,
3301 pSessionThread->pStartupInfo->pszDomain);
3302 if (cchbUserUPN > 0)
3303 {
3304 pszUser = pszUserUPN;
3305 VGSvcVerbose(3, "Using UPN: %s\n", pszUserUPN);
3306 }
3307 else
3308 rc = VERR_NO_STR_MEMORY;
3309 }
3310 if (RT_SUCCESS(rc))
3311#endif
3312 {
3313 /*
3314 * Finally, create the process.
3315 */
3316 rc = RTProcCreateEx(pszExeName, apszArgs, RTENV_DEFAULT, fProcCreate,
3317 &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
3318 !fAnonymous ? pszUser : NULL,
3319 !fAnonymous ? pSessionThread->pStartupInfo->pszPassword : NULL,
3320 NULL /*pvExtraData*/,
3321 &pSessionThread->hProcess);
3322 }
3323#ifdef RT_OS_WINDOWS
3324 RTStrFree(pszUserUPN);
3325#endif
3326 RTFileClose(hStdOutAndErr.u.hFile);
3327 }
3328
3329 RTPipeClose(hStdIn.u.hPipe);
3330 }
3331 return rc;
3332}
3333
3334
3335/**
3336 * Creates a guest session.
3337 *
3338 * This will spawn a new VBoxService.exe instance under behalf of the given user
3339 * which then will act as a session host. On successful open, the session will
3340 * be added to the given session thread list.
3341 *
3342 * @return VBox status code.
3343 * @param pList Which list to use to store the session thread in.
3344 * @param pSessionStartupInfo Session startup info.
3345 * @param ppSessionThread Returns newly created session thread on success.
3346 * Optional.
3347 */
3348int VGSvcGstCtrlSessionThreadCreate(PRTLISTANCHOR pList, const PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pSessionStartupInfo,
3349 PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
3350{
3351 AssertPtrReturn(pList, VERR_INVALID_POINTER);
3352 AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
3353 /* ppSessionThread is optional. */
3354
3355#ifdef VBOX_STRICT
3356 /* Check for existing session in debug mode. Should never happen because of
3357 * Main consistency. */
3358 PVBOXSERVICECTRLSESSIONTHREAD pSessionCur;
3359 RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node)
3360 {
3361 AssertMsgReturn( pSessionCur->fStopped == true
3362 || pSessionCur->pStartupInfo->uSessionID != pSessionStartupInfo->uSessionID,
3363 ("Guest session thread ID=%RU32 already exists (fStopped=%RTbool)\n",
3364 pSessionCur->pStartupInfo->uSessionID, pSessionCur->fStopped), VERR_ALREADY_EXISTS);
3365 }
3366#endif
3367
3368 /* Static counter to help tracking session thread <-> process relations. */
3369 static uint32_t s_uCtrlSessionThread = 0;
3370
3371 /*
3372 * Allocate and initialize the session thread structure.
3373 */
3374 int rc;
3375 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread = (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(*pSessionThread));
3376 if (pSessionThread)
3377 {
3378 //pSessionThread->fShutdown = false;
3379 //pSessionThread->fStarted = false;
3380 //pSessionThread->fStopped = false;
3381 pSessionThread->hKeyPipe = NIL_RTPIPE;
3382 pSessionThread->Thread = NIL_RTTHREAD;
3383 pSessionThread->hProcess = NIL_RTPROCESS;
3384
3385 /* Duplicate startup info. */
3386 pSessionThread->pStartupInfo = VbglR3GuestCtrlSessionStartupInfoDup(pSessionStartupInfo);
3387 AssertPtrReturn(pSessionThread->pStartupInfo, VERR_NO_MEMORY);
3388
3389 /* Generate the secret key. */
3390 RTRandBytes(pSessionThread->abKey, sizeof(pSessionThread->abKey));
3391
3392 rc = RTCritSectInit(&pSessionThread->CritSect);
3393 AssertRC(rc);
3394 if (RT_SUCCESS(rc))
3395 {
3396 /*
3397 * Give the session key to the host so it can validate the client.
3398 */
3399 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
3400 {
3401 for (uint32_t i = 0; i < 10; i++)
3402 {
3403 rc = VbglR3GuestCtrlSessionPrepare(g_idControlSvcClient, pSessionStartupInfo->uSessionID,
3404 pSessionThread->abKey, sizeof(pSessionThread->abKey));
3405 if (rc != VERR_OUT_OF_RESOURCES)
3406 break;
3407 RTThreadSleep(100);
3408 }
3409 }
3410 if (RT_SUCCESS(rc))
3411 {
3412 s_uCtrlSessionThread++;
3413
3414 /*
3415 * Start the session child process.
3416 */
3417 rc = vgsvcVGSvcGstCtrlSessionThreadCreateProcess(pSessionStartupInfo, pSessionThread, s_uCtrlSessionThread);
3418 if (RT_SUCCESS(rc))
3419 {
3420 /*
3421 * Start the session thread.
3422 */
3423 rc = RTThreadCreateF(&pSessionThread->Thread, vgsvcGstCtrlSessionThread, pSessionThread /*pvUser*/, 0 /*cbStack*/,
3424 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctls%RU32", s_uCtrlSessionThread);
3425 if (RT_SUCCESS(rc))
3426 {
3427 /* Wait for the thread to initialize. */
3428 rc = RTThreadUserWait(pSessionThread->Thread, RT_MS_1MIN);
3429 if ( RT_SUCCESS(rc)
3430 && !ASMAtomicReadBool(&pSessionThread->fShutdown))
3431 {
3432 VGSvcVerbose(2, "Thread for session ID=%RU32 started\n", pSessionThread->pStartupInfo->uSessionID);
3433
3434 ASMAtomicXchgBool(&pSessionThread->fStarted, true);
3435
3436 /* Add session to list. */
3437 RTListAppend(pList, &pSessionThread->Node);
3438 if (ppSessionThread) /* Return session if wanted. */
3439 *ppSessionThread = pSessionThread;
3440 return VINF_SUCCESS;
3441 }
3442
3443 /*
3444 * Bail out.
3445 */
3446 VGSvcError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
3447 pSessionThread->pStartupInfo->uSessionID, rc);
3448 if (RT_SUCCESS_NP(rc))
3449 rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
3450 }
3451 else
3452 VGSvcError("Creating session thread failed, rc=%Rrc\n", rc);
3453
3454 RTProcTerminate(pSessionThread->hProcess);
3455 uint32_t cMsWait = 1;
3456 while ( RTProcWait(pSessionThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL) == VERR_PROCESS_RUNNING
3457 && cMsWait <= 9) /* 1023 ms */
3458 {
3459 RTThreadSleep(cMsWait);
3460 cMsWait <<= 1;
3461 }
3462 }
3463
3464 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
3465 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, pSessionStartupInfo->uSessionID);
3466 }
3467 else
3468 VGSvcVerbose(3, "VbglR3GuestCtrlSessionPrepare failed: %Rrc\n", rc);
3469 RTPipeClose(pSessionThread->hKeyPipe);
3470 pSessionThread->hKeyPipe = NIL_RTPIPE;
3471 RTCritSectDelete(&pSessionThread->CritSect);
3472 }
3473 RTMemFree(pSessionThread);
3474 }
3475 else
3476 rc = VERR_NO_MEMORY;
3477
3478 VGSvcVerbose(3, "Spawning session thread returned returned rc=%Rrc\n", rc);
3479 return rc;
3480}
3481
3482
3483/**
3484 * Waits for a formerly opened guest session process to close.
3485 *
3486 * @return VBox status code.
3487 * @param pThread Guest session thread to wait for.
3488 * @param uTimeoutMS Waiting timeout (in ms).
3489 * @param fFlags Closing flags.
3490 */
3491int VGSvcGstCtrlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uTimeoutMS, uint32_t fFlags)
3492{
3493 RT_NOREF(fFlags);
3494 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
3495 /** @todo Validate closing flags. */
3496
3497 AssertMsgReturn(pThread->Thread != NIL_RTTHREAD,
3498 ("Guest session thread of session %p does not exist when it should\n", pThread),
3499 VERR_NOT_FOUND);
3500
3501 int rc = VINF_SUCCESS;
3502
3503 /*
3504 * The spawned session process should have received the same closing request,
3505 * so just wait for the process to close.
3506 */
3507 if (ASMAtomicReadBool(&pThread->fStarted))
3508 {
3509 /* Ask the thread to shutdown. */
3510 ASMAtomicXchgBool(&pThread->fShutdown, true);
3511
3512 VGSvcVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
3513 pThread->pStartupInfo->uSessionID, uTimeoutMS);
3514
3515 int rcThread;
3516 rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
3517 if (RT_SUCCESS(rc))
3518 {
3519 AssertMsg(pThread->fStopped, ("Thread of session ID=%RU32 not in stopped state when it should\n",
3520 pThread->pStartupInfo->uSessionID));
3521
3522 VGSvcVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n", pThread->pStartupInfo->uSessionID, rcThread);
3523 }
3524 else
3525 VGSvcError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n", pThread->pStartupInfo->uSessionID, rc);
3526 }
3527 else
3528 VGSvcVerbose(3, "Thread for session ID=%RU32 not in started state, skipping wait\n", pThread->pStartupInfo->uSessionID);
3529
3530 LogFlowFuncLeaveRC(rc);
3531 return rc;
3532}
3533
3534/**
3535 * Waits for the specified session thread to end and remove
3536 * it from the session thread list.
3537 *
3538 * @return VBox status code.
3539 * @param pThread Session thread to destroy.
3540 * @param fFlags Closing flags.
3541 */
3542int VGSvcGstCtrlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t fFlags)
3543{
3544 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
3545 AssertPtrReturn(pThread->pStartupInfo, VERR_WRONG_ORDER);
3546
3547 const uint32_t uSessionID = pThread->pStartupInfo->uSessionID;
3548
3549 VGSvcVerbose(3, "Destroying session ID=%RU32 ...\n", uSessionID);
3550
3551 int rc = VGSvcGstCtrlSessionThreadWait(pThread, 5 * 60 * 1000 /* 5 minutes timeout */, fFlags);
3552 if (RT_SUCCESS(rc))
3553 {
3554 VbglR3GuestCtrlSessionStartupInfoFree(pThread->pStartupInfo);
3555 pThread->pStartupInfo = NULL;
3556
3557 RTPipeClose(pThread->hKeyPipe);
3558 pThread->hKeyPipe = NIL_RTPIPE;
3559
3560 RTCritSectDelete(&pThread->CritSect);
3561
3562 /* Remove session from list and destroy object. */
3563 RTListNodeRemove(&pThread->Node);
3564
3565 RTMemFree(pThread);
3566 pThread = NULL;
3567 }
3568
3569 VGSvcVerbose(3, "Destroyed session ID=%RU32 with %Rrc\n", uSessionID, rc);
3570 return rc;
3571}
3572
3573/**
3574 * Close all open guest session threads.
3575 *
3576 * @note Caller is responsible for locking!
3577 *
3578 * @return VBox status code.
3579 * @param pList Which list to close the session threads for.
3580 * @param fFlags Closing flags.
3581 */
3582int VGSvcGstCtrlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t fFlags)
3583{
3584 AssertPtrReturn(pList, VERR_INVALID_POINTER);
3585
3586 int rc = VINF_SUCCESS;
3587
3588 /*int rc = VbglR3GuestCtrlClose
3589 if (RT_FAILURE(rc))
3590 VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/
3591
3592 PVBOXSERVICECTRLSESSIONTHREAD pSessIt;
3593 PVBOXSERVICECTRLSESSIONTHREAD pSessItNext;
3594 RTListForEachSafe(pList, pSessIt, pSessItNext, VBOXSERVICECTRLSESSIONTHREAD, Node)
3595 {
3596 int rc2 = VGSvcGstCtrlSessionThreadDestroy(pSessIt, fFlags);
3597 if (RT_FAILURE(rc2))
3598 {
3599 VGSvcError("Closing session thread '%s' failed with rc=%Rrc\n", RTThreadGetName(pSessIt->Thread), rc2);
3600 if (RT_SUCCESS(rc))
3601 rc = rc2;
3602 /* Keep going. */
3603 }
3604 }
3605
3606 VGSvcVerbose(4, "Destroying guest session threads ended with %Rrc\n", rc);
3607 return rc;
3608}
3609
3610
3611/**
3612 * Main function for the session process.
3613 *
3614 * @returns exit code.
3615 * @param argc Argument count.
3616 * @param argv Argument vector (UTF-8).
3617 */
3618RTEXITCODE VGSvcGstCtrlSessionSpawnInit(int argc, char **argv)
3619{
3620 static const RTGETOPTDEF s_aOptions[] =
3621 {
3622 { "--domain", VBOXSERVICESESSIONOPT_DOMAIN, RTGETOPT_REQ_STRING },
3623#ifdef DEBUG
3624 { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING },
3625 { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING },
3626#endif
3627 { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
3628 { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
3629 { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
3630 { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
3631#ifdef DEBUG
3632 { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 },
3633#endif /* DEBUG */
3634 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
3635 };
3636
3637 RTGETOPTSTATE GetState;
3638 RTGetOptInit(&GetState, argc, argv,
3639 s_aOptions, RT_ELEMENTS(s_aOptions),
3640 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3641
3642 uint32_t fSession = VBOXSERVICECTRLSESSION_FLAG_SPAWN;
3643
3644 /* Protocol and session ID must be specified explicitly. */
3645 g_Session.StartupInfo.uProtocol = UINT32_MAX;
3646 g_Session.StartupInfo.uSessionID = UINT32_MAX;
3647
3648 int ch;
3649 RTGETOPTUNION ValueUnion;
3650 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
3651 {
3652 /* For options that require an argument, ValueUnion has received the value. */
3653 switch (ch)
3654 {
3655 case VBOXSERVICESESSIONOPT_DOMAIN:
3656 /* Information not needed right now, skip. */
3657 break;
3658#ifdef DEBUG
3659 case VBOXSERVICESESSIONOPT_DUMP_STDOUT:
3660 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
3661 break;
3662
3663 case VBOXSERVICESESSIONOPT_DUMP_STDERR:
3664 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
3665 break;
3666#endif
3667 case VBOXSERVICESESSIONOPT_SESSION_ID:
3668 g_Session.StartupInfo.uSessionID = ValueUnion.u32;
3669 break;
3670
3671 case VBOXSERVICESESSIONOPT_SESSION_PROTO:
3672 g_Session.StartupInfo.uProtocol = ValueUnion.u32;
3673 break;
3674#ifdef DEBUG
3675 case VBOXSERVICESESSIONOPT_THREAD_ID:
3676 /* Not handled. Mainly for processs listing. */
3677 break;
3678#endif
3679 case VBOXSERVICESESSIONOPT_LOG_FILE:
3680 {
3681 int rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
3682 if (RT_FAILURE(rc))
3683 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error copying log file name: %Rrc", rc);
3684 break;
3685 }
3686
3687 case VBOXSERVICESESSIONOPT_USERNAME:
3688 /* Information not needed right now, skip. */
3689 break;
3690
3691 /** @todo Implement help? */
3692
3693 case 'v':
3694 g_cVerbosity++;
3695 break;
3696
3697 case VINF_GETOPT_NOT_OPTION:
3698 {
3699 if (!RTStrICmp(ValueUnion.psz, VBOXSERVICECTRLSESSION_GETOPT_PREFIX))
3700 break;
3701 /* else fall through and bail out. */
3702 RT_FALL_THROUGH();
3703 }
3704 default:
3705 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'", ValueUnion.psz);
3706 }
3707 }
3708
3709 /* Check that we've got all the required options. */
3710 if (g_Session.StartupInfo.uProtocol == UINT32_MAX)
3711 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified");
3712
3713 if (g_Session.StartupInfo.uSessionID == UINT32_MAX)
3714 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
3715
3716 /* Init the session object. */
3717 int rc = VGSvcGstCtrlSessionInit(&g_Session, fSession);
3718 if (RT_FAILURE(rc))
3719 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to initialize session object, rc=%Rrc\n", rc);
3720
3721 rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
3722 if (RT_FAILURE(rc))
3723 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to create log file '%s', rc=%Rrc\n",
3724 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
3725
3726 RTEXITCODE rcExit = vgsvcGstCtrlSessionSpawnWorker(&g_Session);
3727
3728 VGSvcLogDestroy();
3729 return rcExit;
3730}
3731
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