VirtualBox

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

Last change on this file since 102518 was 99262, checked in by vboxsync, 23 months ago

Guest Control: Implements IGuestSession::fsQueryInfo() and IGuestSession::fsQueryFreeSpace(). bugref:10414

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