VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 142.7 KB
Line 
1/* $Id: VBoxServiceControlSession.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxServiceControlSession - Guest session handling. Also handles the spawned session processes.
4 */
5
6/*
7 * Copyright (C) 2013-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#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 {
1072 /* Save reading parameters for subsequent directory entry read calls later. */
1073 pDir->fRead = fReadFlags;
1074 pDir->enmReadAttrAdd = enmAttrAdd;
1075
1076 pDir->cbDirEntryEx = GSTCTL_DIRENTRY_MAX_SIZE;
1077 pDir->pDirEntryEx = (PRTDIRENTRYEX)RTMemAlloc(pDir->cbDirEntryEx);
1078 if (pDir->pDirEntryEx)
1079 {
1080 rc = RTDirOpenFiltered(&pDir->hDir, pDir->pszPathAbs, (RTDIRFILTER)enmFilter, fFlags);
1081 if (RT_SUCCESS(rc))
1082 {
1083 uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID);
1084 pDir->uHandle = uHandle;
1085 RTListAppend(&pSession->lstDirs, &pDir->Node);
1086 VGSvcVerbose(2, "[Dir %s] Opened (ID=%RU32)\n", pDir->pszPathAbs, pDir->uHandle);
1087 }
1088 }
1089 else
1090 rc = VERR_NO_MEMORY;
1091 }
1092 else
1093 rc = VERR_NO_MEMORY;
1094 }
1095 else
1096 {
1097 VGSvcError("Opening directory failed: Empty path!\n");
1098 rc = VERR_INVALID_NAME;
1099 }
1100
1101 /* Clean up if we failed. */
1102 if (RT_FAILURE(rc))
1103 {
1104 RTStrFree(pDir->pszPathAbs);
1105 pDir->pszPathAbs = NULL;
1106 if (pDir->hDir != NIL_RTDIR)
1107 RTDirClose(pDir->hDir);
1108 RTMemFree(pDir);
1109 pDir = NULL;
1110 }
1111
1112 /*
1113 * Report result back to host.
1114 */
1115 int rc2 = VbglR3GuestCtrlDirCbOpen(pHostCtx, rc, uHandle);
1116 if (RT_FAILURE(rc2))
1117 {
1118 VGSvcError("[Dir %s]: Failed to report directory open status, rc=%Rrc\n", szPath, rc2);
1119 if (RT_SUCCESS(rc))
1120 rc = rc2;
1121 }
1122 }
1123 else
1124 {
1125 VGSvcError("Error fetching parameters for directory open operation: %Rrc\n", rc);
1126 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1127 }
1128
1129 VGSvcVerbose(4, "[Dir %s] Opening (flags=%#x, filter flags=%#x) returned rc=%Rrc\n",
1130 szPath, fFlags, enmFilter, rc);
1131 return rc;
1132}
1133
1134
1135static int vgsvcGstCtrlSessionHandleDirClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1136{
1137 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1138 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1139
1140 /*
1141 * Retrieve the message.
1142 */
1143 uint32_t uHandle = 0;
1144 int rc = VbglR3GuestCtrlDirGetClose(pHostCtx, &uHandle /* Dir handle to close */);
1145 if (RT_SUCCESS(rc))
1146 {
1147 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1148 if (pDir)
1149 {
1150 VGSvcVerbose(2, "[Dir %s] Closing (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle);
1151 rc = vgsvcGstCtrlSessionDirFree(pDir);
1152 }
1153 else
1154 {
1155 VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle);
1156 rc = VERR_NOT_FOUND;
1157 }
1158
1159 /*
1160 * Report result back to host.
1161 */
1162 int rc2 = VbglR3GuestCtrlDirCbClose(pHostCtx, rc);
1163 if (RT_FAILURE(rc2))
1164 {
1165 VGSvcError("Failed to report directory close status, rc=%Rrc\n", rc2);
1166 if (RT_SUCCESS(rc))
1167 rc = rc2;
1168 }
1169 }
1170 else
1171 {
1172 VGSvcError("Error fetching parameters for directory close operation: %Rrc\n", rc);
1173 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1174 }
1175 return rc;
1176}
1177
1178
1179/**
1180 * Worker function for reading a next guest directory entry.
1181 *
1182 * @returns VBox status code.
1183 * @param pSession Guest Control session to use.
1184 * @param pDir Guest directory to read the next entry for.
1185 * @param ppDirEntryEx Where to store the pointer to the next guest directory entry on success.
1186 * @param pcbDirEntryEx Where to return the size (in bytes) of \a ppGstCtlDirEntryEx on success.
1187 * @param ppszUser Where to return the resolved user name string.
1188 * @param ppszGroups Where to return the resolved groups string.
1189 */
1190static int vgsvcGstCtrlSessionDirReadNext(const PVBOXSERVICECTRLSESSION pSession,
1191 PVBOXSERVICECTRLDIR pDir,
1192 PGSTCTLDIRENTRYEX *ppDirEntryEx, size_t *pcbDirEntryEx,
1193 const char **ppszUser, const char **ppszGroups)
1194{
1195 PRTDIRENTRYEX pDirEntryEx = pDir->pDirEntryEx;
1196
1197 size_t cbDirEntryEx = pDir->cbDirEntryEx;
1198 int rc = RTDirReadEx(pDir->hDir, pDir->pDirEntryEx, &cbDirEntryEx, (RTFSOBJATTRADD)pDir->enmReadAttrAdd, pDir->fRead);
1199
1200 VGSvcVerbose(2, "[Dir %s] Read next entry '%s' -> %Rrc (%zu bytes)\n",
1201 pDir->pszPathAbs, RT_SUCCESS(rc) ? pDirEntryEx->szName : "<None>", rc, cbDirEntryEx);
1202
1203 if (RT_FAILURE(rc))
1204 return rc;
1205
1206 /* Paranoia. */
1207 AssertReturn(cbDirEntryEx <= pDir->cbDirEntryEx, VERR_BUFFER_OVERFLOW);
1208
1209 *ppszUser = VGSvcIdCacheGetUidName(&pSession->UidCache,
1210 pDirEntryEx->Info.Attr.u.Unix.uid, pDirEntryEx->szName, pDir->pszPathAbs);
1211 *ppszGroups = VGSvcIdCacheGetGidName(&pSession->GidCache,
1212 pDirEntryEx->Info.Attr.u.Unix.gid, pDirEntryEx->szName, pDir->pszPathAbs);
1213
1214 VGSvcVerbose(2, "[Dir %s] Entry '%s': %zu bytes, uid=%s (%d), gid=%s (%d)\n",
1215 pDir->pszPathAbs, pDirEntryEx->szName, pDirEntryEx->Info.cbObject,
1216 *ppszUser, pDirEntryEx->Info.Attr.u.UnixOwner.uid,
1217 *ppszGroups, pDirEntryEx->Info.Attr.u.UnixGroup.gid);
1218
1219 /*
1220 * For now we ASSUME that RTDIRENTRYEX == GSTCTLDIRENTRYEX, which implies that we simply can cast RTDIRENTRYEX
1221 * to GSTCTLDIRENTRYEX. This might change in the future, however, so be extra cautious here.
1222 *
1223 * Ditto for RTFSOBJATTRADD == GSTCTLFSOBJATTRADD.
1224 */
1225 AssertCompile(sizeof(GSTCTLDIRENTRYEX) == sizeof(RTDIRENTRYEX));
1226 AssertCompile(RT_OFFSETOF(GSTCTLDIRENTRYEX, Info) == RT_OFFSETOF(RTDIRENTRYEX, Info));
1227 AssertCompile(RT_OFFSETOF(GSTCTLDIRENTRYEX, cbName) == RT_OFFSETOF(RTDIRENTRYEX, cbName));
1228 AssertCompile(RT_OFFSETOF(GSTCTLDIRENTRYEX, szName) == RT_OFFSETOF(RTDIRENTRYEX, szName));
1229 AssertCompile(RT_OFFSETOF(GSTCTLFSOBJINFO, Attr) == RT_OFFSETOF(RTFSOBJINFO, Attr));
1230
1231 if (RT_SUCCESS(rc))
1232 {
1233 *ppDirEntryEx = (PGSTCTLDIRENTRYEX)pDirEntryEx;
1234 *pcbDirEntryEx = cbDirEntryEx;
1235 }
1236
1237 return rc;
1238}
1239
1240
1241static int vgsvcGstCtrlSessionHandleDirRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1242{
1243 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1244 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1245
1246 /*
1247 * Retrieve the message.
1248 */
1249 uint32_t uHandle;
1250 int rc = VbglR3GuestCtrlDirGetRead(pHostCtx, &uHandle);
1251 if (RT_SUCCESS(rc))
1252 {
1253 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1254 if (pDir)
1255 {
1256 PGSTCTLDIRENTRYEX pDirEntryEx;
1257 size_t cbDirEntryEx;
1258 const char *pszUser = NULL;
1259 const char *pszGroups = NULL;
1260 rc = vgsvcGstCtrlSessionDirReadNext(pSession, pDir,
1261 &pDirEntryEx, &cbDirEntryEx, &pszUser, &pszGroups);
1262
1263 VGSvcVerbose(2, "[Dir %s] %Rrc\n", pDir->pszPathAbs, rc);
1264
1265 int rc2;
1266 if (RT_SUCCESS(rc))
1267 rc2 = VbglR3GuestCtrlDirCbReadEx(pHostCtx, rc, pDirEntryEx, (uint32_t)cbDirEntryEx, pszUser, pszGroups);
1268 else
1269 rc2 = VbglR3GuestCtrlDirCbRead(pHostCtx, rc, NULL /* pEntry */, 0 /* cbSize */);
1270 if (RT_FAILURE(rc2))
1271 VGSvcError("Failed to report directory read status (%Rrc), rc=%Rrc\n", rc, rc2);
1272
1273 VGSvcVerbose(2, "[Dir %s] 2 %Rrc\n", pDir->pszPathAbs, rc);
1274
1275 if (rc == VERR_NO_MORE_FILES) /* Directory reading done. */
1276 rc = VINF_SUCCESS;
1277
1278 vgsvcGstCtrlSessionDirRelease(pDir);
1279 }
1280 else
1281 {
1282 VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle);
1283 rc = VERR_NOT_FOUND;
1284 }
1285 }
1286 else
1287 {
1288 VGSvcError("Error fetching parameters for directory read operation: %Rrc\n", rc);
1289 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1290 }
1291 return rc;
1292}
1293
1294
1295static int vgsvcGstCtrlSessionHandleDirRewind(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1296{
1297 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1298 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1299
1300 /*
1301 * Retrieve the message.
1302 */
1303 uint32_t uHandle = 0;
1304 int rc = VbglR3GuestCtrlDirGetRewind(pHostCtx, &uHandle);
1305 if (RT_SUCCESS(rc))
1306 {
1307 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1308 if (pDir)
1309 {
1310 VGSvcVerbose(2, "[Dir %s] Rewinding (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle);
1311
1312 rc = RTDirRewind(pDir->hDir);
1313
1314 vgsvcGstCtrlSessionDirRelease(pDir);
1315 }
1316 else
1317 {
1318 VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle);
1319 rc = VERR_NOT_FOUND;
1320 }
1321
1322 /*
1323 * Report result back to host.
1324 */
1325 int rc2 = VbglR3GuestCtrlDirCbRewind(pHostCtx, rc);
1326 if (RT_FAILURE(rc2))
1327 {
1328 VGSvcError("Failed to report directory rewind status, rc=%Rrc\n", rc2);
1329 if (RT_SUCCESS(rc))
1330 rc = rc2;
1331 }
1332 }
1333 else
1334 {
1335 VGSvcError("Error fetching parameters for directory rewind operation: %Rrc\n", rc);
1336 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1337 }
1338 return rc;
1339}
1340
1341
1342static int vgsvcGstCtrlSessionHandleDirCreate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1343{
1344 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1345 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1346
1347 /*
1348 * Retrieve the request.
1349 */
1350 char szPath[RTPATH_MAX];
1351 RTFMODE fMode;
1352 uint32_t fCreate;
1353 int rc = VbglR3GuestCtrlDirGetCreate(pHostCtx, szPath, sizeof(szPath), &fMode, &fCreate);
1354 if (RT_SUCCESS(rc))
1355 {
1356 if (!(fCreate & ~GSTCTL_CREATEDIRECTORY_F_VALID_MASK))
1357 {
1358 /* Translate flags. */
1359 int fCreateRuntime = 0; /* RTDIRCREATE_FLAGS_XXX */
1360 if (fCreate & GSTCTL_CREATEDIRECTORY_F_NO_SYMLINKS)
1361 fCreateRuntime |= RTDIRCREATE_FLAGS_NO_SYMLINKS;
1362 if (fCreate & GSTCTL_CREATEDIRECTORY_F_IGNORE_UMASK)
1363 fCreateRuntime |= RTDIRCREATE_FLAGS_IGNORE_UMASK;
1364
1365 if (fCreate & GSTCTL_CREATEDIRECTORY_F_PARENTS)
1366 rc = RTDirCreateFullPath(szPath, fMode);
1367 else
1368 rc = RTDirCreate(szPath, fMode, fCreateRuntime);
1369
1370 VGSvcVerbose(4, "Creating directory (szPath='%s', fMode=%#x, fCreate=%#x) -> rc=%Rrc\n", szPath, fMode, fCreate, rc);
1371 }
1372 else
1373 {
1374 VGSvcError("Invalid directory creation flags: %#x\n", fCreate);
1375 rc = VERR_NOT_SUPPORTED;
1376 }
1377
1378 /*
1379 * Report result back to host.
1380 */
1381 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
1382 if (RT_FAILURE(rc2))
1383 {
1384 VGSvcError("Failed to report directory creation status, rc=%Rrc\n", rc2);
1385 if (RT_SUCCESS(rc))
1386 rc = rc2;
1387 }
1388 }
1389 else
1390 {
1391 VGSvcError("Error fetching parameters for directory creation operation: %Rrc\n", rc);
1392 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1393 }
1394 VGSvcVerbose(5, "Creating directory returned rc=%Rrc\n", rc);
1395 return rc;
1396}
1397
1398static int vgsvcGstCtrlSessionHandleDirList(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1399{
1400 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1401 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1402
1403 /*
1404 * Retrieve the request.
1405 */
1406 uint32_t uHandle;
1407 uint32_t cEntries;
1408 uint32_t cEntriesRead = 0;
1409 uint32_t fFlags;
1410 int rc = VbglR3GuestCtrlDirGetList(pHostCtx, &uHandle, &cEntries, &fFlags);
1411 if (RT_SUCCESS(rc))
1412 {
1413 size_t cbGrowSize = _64K;
1414 void *pvBuf = RTMemAlloc(cbGrowSize);
1415 if (!pvBuf)
1416 rc = VERR_NO_MEMORY;
1417 size_t cbBufUsed = 0;
1418 if (RT_SUCCESS(rc))
1419 {
1420 if (!(fFlags & ~GSTCTL_DIRLIST_F_VALID_MASK))
1421 {
1422 size_t cbBufSize = cbGrowSize;
1423
1424 PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle);
1425 if (pDir)
1426 {
1427 PGSTCTLDIRENTRYEX pDirEntryEx;
1428 size_t cbDirEntryEx;
1429 const char *pszUser = NULL;
1430 const char *pszGroups = NULL;
1431
1432 while (cEntries--)
1433 {
1434 rc = vgsvcGstCtrlSessionDirReadNext(pSession, pDir,
1435 &pDirEntryEx, &cbDirEntryEx, &pszUser, &pszGroups);
1436 if (RT_FAILURE(rc)) /* Might be VERR_NO_MORE_FILES. */
1437 break;
1438
1439 size_t const cbHdr = sizeof(GSTCTLDIRENTRYLISTHDR);
1440 size_t const cbUser = strlen(pszUser) + 1; /* Include terminator. */
1441 size_t const cbGroups = strlen(pszGroups) + 1; /* Ditto. */
1442 size_t const cbTotal = cbHdr + cbDirEntryEx + cbUser + cbGroups;
1443
1444 if (cbBufSize - cbBufUsed < cbTotal) /* Grow buffer, if needed. */
1445 {
1446 AssertBreakStmt(cbTotal <= cbGrowSize, rc = VERR_BUFFER_OVERFLOW);
1447 pvBuf = RTMemRealloc(pvBuf, cbBufSize + cbGrowSize);
1448 AssertPtrBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
1449 cbBufSize += cbGrowSize;
1450 }
1451
1452 GSTCTLDIRENTRYLISTHDR Hdr;
1453 Hdr.cbDirEntryEx = (uint32_t)cbDirEntryEx;
1454 Hdr.cbUser = (uint32_t)cbUser;
1455 Hdr.cbGroups = (uint32_t)cbGroups;
1456
1457 memcpy((uint8_t *)pvBuf + cbBufUsed, &Hdr, cbHdr);
1458 cbBufUsed += cbHdr;
1459 memcpy((uint8_t *)pvBuf + cbBufUsed, pDirEntryEx, cbDirEntryEx);
1460 cbBufUsed += cbDirEntryEx;
1461 memcpy((uint8_t *)pvBuf + cbBufUsed, pszUser, cbUser);
1462 cbBufUsed += cbUser;
1463 memcpy((uint8_t *)pvBuf + cbBufUsed, pszGroups, cbGroups);
1464 cbBufUsed += cbGroups;
1465
1466 Assert(cbBufUsed <= cbBufSize);
1467 cEntriesRead++;
1468 }
1469
1470 vgsvcGstCtrlSessionDirRelease(pDir);
1471 }
1472
1473 VGSvcVerbose(4, "Read %RU32 directory entries (requested %RU32 entries) -> %zu bytes, rc=%Rrc\n",
1474 cEntriesRead, cEntries, cbBufUsed, rc);
1475
1476 /* Directory reading done? */
1477 if ( cEntriesRead
1478 && rc == VERR_NO_MORE_FILES)
1479 rc = VINF_SUCCESS;
1480
1481 /* Note: Subsequent calls will return VERR_NO_MORE_FILES to the host. */
1482 }
1483 else
1484 {
1485 VGSvcError("Unsupported directory listing flags: %#x (all %#x)\n", (fFlags & ~GSTCTL_DIRLIST_F_VALID_MASK), fFlags);
1486 rc = VERR_NOT_SUPPORTED;
1487 }
1488 }
1489
1490 /*
1491 * Report result back to host.
1492 */
1493 int rc2 = VbglR3GuestCtrlDirCbList(pHostCtx, rc, cEntriesRead, pvBuf, (uint32_t)cbBufUsed);
1494 if (RT_FAILURE(rc2))
1495 VGSvcError("Failed to report directory listing (%Rrc), rc=%Rrc\n", rc, rc2);
1496
1497 if (rc == VERR_NO_MORE_FILES) /* Directory reading done? */
1498 rc = VINF_SUCCESS;
1499
1500 RTMemFree(pvBuf);
1501 }
1502 else
1503 {
1504 VGSvcError("Error fetching parameters for directory listing operation: %Rrc\n", rc);
1505 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1506 }
1507 VGSvcVerbose(5, "Listing directory returned rc=%Rrc\n", rc);
1508 return rc;
1509}
1510#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
1511
1512
1513static int vgsvcGstCtrlSessionHandlePathRename(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1514{
1515 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1516 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1517
1518 /*
1519 * Retrieve the request.
1520 */
1521 char szSource[RTPATH_MAX];
1522 char szDest[RTPATH_MAX];
1523 uint32_t fFlags = 0; /* PATHRENAME_FLAG_XXX */
1524 int rc = VbglR3GuestCtrlPathGetRename(pHostCtx, szSource, sizeof(szSource), szDest, sizeof(szDest), &fFlags);
1525 if (RT_SUCCESS(rc))
1526 {
1527 /*
1528 * Validate the flags (kudos for using the same as IPRT), then do the renaming.
1529 */
1530 AssertCompile(PATHRENAME_FLAG_NO_REPLACE == RTPATHRENAME_FLAGS_NO_REPLACE);
1531 AssertCompile(PATHRENAME_FLAG_REPLACE == RTPATHRENAME_FLAGS_REPLACE);
1532 AssertCompile(PATHRENAME_FLAG_NO_SYMLINKS == RTPATHRENAME_FLAGS_NO_SYMLINKS);
1533 AssertCompile(PATHRENAME_FLAG_VALID_MASK == (RTPATHRENAME_FLAGS_NO_REPLACE | RTPATHRENAME_FLAGS_REPLACE | RTPATHRENAME_FLAGS_NO_SYMLINKS));
1534 if (!(fFlags & ~PATHRENAME_FLAG_VALID_MASK))
1535 {
1536 VGSvcVerbose(4, "Renaming '%s' to '%s', fFlags=%#x, rc=%Rrc\n", szSource, szDest, fFlags, rc);
1537 rc = RTPathRename(szSource, szDest, fFlags);
1538 }
1539 else
1540 {
1541 VGSvcError("Invalid rename flags: %#x\n", fFlags);
1542 rc = VERR_NOT_SUPPORTED;
1543 }
1544
1545 /*
1546 * Report result back to host.
1547 */
1548 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
1549 if (RT_FAILURE(rc2))
1550 {
1551 VGSvcError("Failed to report renaming status, rc=%Rrc\n", rc2);
1552 if (RT_SUCCESS(rc))
1553 rc = rc2;
1554 }
1555 }
1556 else
1557 {
1558 VGSvcError("Error fetching parameters for rename operation: %Rrc\n", rc);
1559 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1560 }
1561 VGSvcVerbose(5, "Renaming '%s' to '%s' returned rc=%Rrc\n", szSource, szDest, rc);
1562 return rc;
1563}
1564
1565
1566/**
1567 * Handles getting the user's documents directory.
1568 *
1569 * @returns VBox status code.
1570 * @param pSession Guest session.
1571 * @param pHostCtx Host context.
1572 */
1573static int vgsvcGstCtrlSessionHandlePathUserDocuments(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1574{
1575 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1576 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1577
1578 /*
1579 * Retrieve the request.
1580 */
1581 int rc = VbglR3GuestCtrlPathGetUserDocuments(pHostCtx);
1582 if (RT_SUCCESS(rc))
1583 {
1584 /*
1585 * Get the path and pass it back to the host..
1586 */
1587 char szPath[RTPATH_MAX];
1588 rc = RTPathUserDocuments(szPath, sizeof(szPath));
1589#ifdef DEBUG
1590 VGSvcVerbose(2, "User documents is '%s', rc=%Rrc\n", szPath, rc);
1591#endif
1592
1593 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
1594 RT_SUCCESS(rc) ? (uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
1595 if (RT_FAILURE(rc2))
1596 {
1597 VGSvcError("Failed to report user documents, rc=%Rrc\n", rc2);
1598 if (RT_SUCCESS(rc))
1599 rc = rc2;
1600 }
1601 }
1602 else
1603 {
1604 VGSvcError("Error fetching parameters for user documents path request: %Rrc\n", rc);
1605 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1606 }
1607 return rc;
1608}
1609
1610
1611/**
1612 * Handles shutting down / rebooting the guest OS.
1613 *
1614 * @returns VBox status code.
1615 * @param pSession Guest session.
1616 * @param pHostCtx Host context.
1617 */
1618static int vgsvcGstCtrlSessionHandleShutdown(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1619{
1620 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1621 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1622
1623 /*
1624 * Retrieve the request.
1625 */
1626 uint32_t fAction;
1627 int rc = VbglR3GuestCtrlGetShutdown(pHostCtx, &fAction);
1628 if (RT_SUCCESS(rc))
1629 {
1630 VGSvcVerbose(1, "Host requested to %s system ...\n", (fAction & RTSYSTEM_SHUTDOWN_REBOOT) ? "reboot" : "shutdown");
1631
1632 /* Reply first to the host, in order to avoid host hangs when issuing the guest shutdown. */
1633 rc = VbglR3GuestCtrlMsgReply(pHostCtx, VINF_SUCCESS);
1634 if (RT_FAILURE(rc))
1635 {
1636 VGSvcError("Failed to reply to shutdown / reboot request, rc=%Rrc\n", rc);
1637 }
1638 else
1639 {
1640 uint32_t fSystemShutdown = RTSYSTEM_SHUTDOWN_PLANNED;
1641
1642 /* Translate SHUTDOWN_FLAG_ into RTSYSTEM_SHUTDOWN_ flags. */
1643 if (fAction & GUEST_SHUTDOWN_FLAG_REBOOT)
1644 fSystemShutdown |= RTSYSTEM_SHUTDOWN_REBOOT;
1645 else /* SHUTDOWN_FLAG_POWER_OFF */
1646 fSystemShutdown |= RTSYSTEM_SHUTDOWN_POWER_OFF;
1647
1648 if (fAction & GUEST_SHUTDOWN_FLAG_FORCE)
1649 fSystemShutdown |= RTSYSTEM_SHUTDOWN_FORCE;
1650
1651 rc = RTSystemShutdown(0 /*cMsDelay*/, fSystemShutdown, "VBoxService");
1652 if (RT_FAILURE(rc))
1653 VGSvcError("%s system failed with %Rrc\n",
1654 (fAction & RTSYSTEM_SHUTDOWN_REBOOT) ? "Rebooting" : "Shutting down", rc);
1655 }
1656 }
1657 else
1658 {
1659 VGSvcError("Error fetching parameters for shutdown / reboot request: %Rrc\n", rc);
1660 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1661 }
1662
1663 return rc;
1664}
1665
1666
1667/**
1668 * Handles getting the user's home directory.
1669 *
1670 * @returns VBox status code.
1671 * @param pSession Guest session.
1672 * @param pHostCtx Host context.
1673 */
1674static int vgsvcGstCtrlSessionHandlePathUserHome(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1675{
1676 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1677 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1678
1679 /*
1680 * Retrieve the request.
1681 */
1682 int rc = VbglR3GuestCtrlPathGetUserHome(pHostCtx);
1683 if (RT_SUCCESS(rc))
1684 {
1685 /*
1686 * Get the path and pass it back to the host..
1687 */
1688 char szPath[RTPATH_MAX];
1689 rc = RTPathUserHome(szPath, sizeof(szPath));
1690
1691#ifdef DEBUG
1692 VGSvcVerbose(2, "User home is '%s', rc=%Rrc\n", szPath, rc);
1693#endif
1694 /* Report back in any case. */
1695 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
1696 RT_SUCCESS(rc) ?(uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
1697 if (RT_FAILURE(rc2))
1698 {
1699 VGSvcError("Failed to report user home, rc=%Rrc\n", rc2);
1700 if (RT_SUCCESS(rc))
1701 rc = rc2;
1702 }
1703 }
1704 else
1705 {
1706 VGSvcError("Error fetching parameters for user home directory path request: %Rrc\n", rc);
1707 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1708 }
1709 return rc;
1710}
1711
1712/**
1713 * Structure for keeping a mount point enumeration context.
1714 */
1715typedef struct VGSVCMOUNTPOINTENUMCTX
1716{
1717 /** Mount points as strings, delimited with a string terminator ('\0').
1718 * List ends with an additional string terminator. Might get re-allocated, so don't rely on this pointer! */
1719 char *psz;
1720 /** Current size of \a psz in bytes. Includes ending terminator. */
1721 size_t cb;
1722 /** Total allocation Size of \a psz in bytes. */
1723 size_t cbAlloc;
1724} VGSVCMOUNTPOINTENUMCTX;
1725/** Pointer to a structure for keeping a mount point enumeration context. */
1726typedef VGSVCMOUNTPOINTENUMCTX *PVGSVCMOUNTPOINTENUMCTX;
1727
1728/**
1729 * Enumeration callback for storing the mount points.
1730 *
1731 * @returns VBox status code.
1732 * @param pszMountpoint Mount point to handle.
1733 * @param pvUser Pointer of type PVGSVCMOUNTPOINTENUMCTX.
1734 */
1735static DECLCALLBACK(int) vgsvcGstCtrlSessionHandleMountPointsEnumCallback(const char *pszMountpoint, void *pvUser)
1736{
1737 PVGSVCMOUNTPOINTENUMCTX pCtx = (PVGSVCMOUNTPOINTENUMCTX)pvUser;
1738 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1739
1740 size_t const cch = strlen(pszMountpoint) + 1 /* Entry terminator */;
1741 AssertReturn(cch < RTPATH_MAX, VERR_INVALID_PARAMETER); /* Paranoia. */
1742 if (cch > pCtx->cbAlloc - pCtx->cb - 1 /* Ending terminator */)
1743 {
1744 int rc2 = RTStrRealloc(&pCtx->psz, pCtx->cbAlloc + RT_MAX(_4K, cch));
1745 AssertRCReturn(rc2, rc2);
1746 }
1747
1748 memcpy(&pCtx->psz[pCtx->cb], pszMountpoint, cch);
1749 pCtx->cb += cch;
1750 AssertReturn(pCtx->cb <= pCtx->cbAlloc, VERR_BUFFER_OVERFLOW); /* Paranoia. */
1751
1752 return VINF_SUCCESS;
1753}
1754
1755/**
1756 * Handles getting the current mount points.
1757 *
1758 * @returns VBox status code.
1759 * @param pSession Guest session.
1760 * @param pHostCtx Host context.
1761 */
1762static int vgsvcGstCtrlSessionHandleMountPoints(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1763{
1764 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1765 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1766
1767 /*
1768 * Retrieve the request.
1769 */
1770 uint32_t fFlags;
1771 int rc = VbglR3GuestCtrlGetMountPoints(pHostCtx, &fFlags);
1772 if (RT_SUCCESS(rc))
1773 {
1774 /* Note: fFlags is currently unused, so we simply ignore this here. */
1775
1776 VGSVCMOUNTPOINTENUMCTX Ctx;
1777 Ctx.cb = 0;
1778 Ctx.cbAlloc = _4K; /* Start with something sensible. */
1779 Ctx.psz = RTStrAlloc(Ctx.cbAlloc);
1780 if (!Ctx.psz)
1781 rc = VERR_NO_MEMORY;
1782
1783 if (RT_SUCCESS(rc))
1784 rc = RTFsMountpointsEnum(vgsvcGstCtrlSessionHandleMountPointsEnumCallback, &Ctx);
1785
1786 /* Report back in any case. */
1787 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, Ctx.psz,
1788 RT_SUCCESS(rc) ? (uint32_t)Ctx.cb : 0);
1789 if (RT_FAILURE(rc2))
1790 {
1791 VGSvcError("Failed to report mount points, rc=%Rrc\n", rc2);
1792 if (RT_SUCCESS(rc))
1793 rc = rc2;
1794 }
1795
1796 RTStrFree(Ctx.psz);
1797 Ctx.psz = NULL;
1798 }
1799 else
1800 {
1801 VGSvcError("Error fetching parameters for getting mount points request: %Rrc\n", rc);
1802 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1803 }
1804 return rc;
1805}
1806
1807
1808/**
1809 * Handles starting a guest processes.
1810 *
1811 * @returns VBox status code.
1812 * @param pSession Guest session.
1813 * @param pHostCtx Host context.
1814 */
1815static int vgsvcGstCtrlSessionHandleProcExec(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1816{
1817 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1818 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1819
1820 /* Initialize maximum environment block size -- needed as input
1821 * parameter to retrieve the stuff from the host. On output this then
1822 * will contain the actual block size. */
1823 PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo;
1824 int rc = VbglR3GuestCtrlProcGetStart(pHostCtx, &pStartupInfo);
1825 if (RT_SUCCESS(rc))
1826 {
1827 VGSvcVerbose(3, "Request to start process szCmd=%s, fFlags=0x%x, szArgs=%s, szEnv=%s, szCwd=%s, uTimeout=%RU32\n",
1828 pStartupInfo->pszCmd, pStartupInfo->fFlags,
1829 pStartupInfo->cArgs ? pStartupInfo->pszArgs : "<None>",
1830 pStartupInfo->cEnvVars ? pStartupInfo->pszEnv : "<None>",
1831 pStartupInfo->cbCwd ? pStartupInfo->pszCwd : "<None>",
1832 pStartupInfo->uTimeLimitMS);
1833
1834 bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */
1835 rc = VGSvcGstCtrlSessionProcessStartAllowed(pSession, &fStartAllowed);
1836 if (RT_SUCCESS(rc))
1837 {
1838 vgsvcGstCtrlSessionCleanupProcesses(pSession);
1839
1840 if (fStartAllowed)
1841 rc = VGSvcGstCtrlProcessStart(pSession, pStartupInfo, pHostCtx->uContextID);
1842 else
1843 rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */
1844 }
1845
1846 /* We're responsible for signaling errors to the host (it will wait for ever otherwise). */
1847 if (RT_FAILURE(rc))
1848 {
1849 VGSvcError("Starting process failed with rc=%Rrc, protocol=%RU32, parameters=%RU32\n",
1850 rc, pHostCtx->uProtocol, pHostCtx->uNumParms);
1851 int rc2 = VbglR3GuestCtrlProcCbStatus(pHostCtx, 0 /*nil-PID*/, PROC_STS_ERROR, rc, NULL /*pvData*/, 0 /*cbData*/);
1852 if (RT_FAILURE(rc2))
1853 VGSvcError("Error sending start process status to host, rc=%Rrc\n", rc2);
1854 }
1855
1856 VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo);
1857 pStartupInfo = NULL;
1858 }
1859 else
1860 {
1861 VGSvcError("Failed to retrieve parameters for process start: %Rrc (cParms=%u)\n", rc, pHostCtx->uNumParms);
1862 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1863 }
1864
1865 return rc;
1866}
1867
1868
1869/**
1870 * Sends stdin input to a specific guest process.
1871 *
1872 * @returns VBox status code.
1873 * @param pSession The session which is in charge.
1874 * @param pHostCtx The host context to use.
1875 * @param ppvScratchBuf The scratch buffer, we may grow it.
1876 * @param pcbScratchBuf The scratch buffer size for retrieving the input
1877 * data.
1878 */
1879static int vgsvcGstCtrlSessionHandleProcInput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1880 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
1881{
1882 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1883 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1884
1885 /*
1886 * Retrieve the data from the host.
1887 */
1888 uint32_t uPID;
1889 uint32_t fFlags;
1890 uint32_t cbInput;
1891 int rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
1892 if ( rc == VERR_BUFFER_OVERFLOW
1893 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbInput))
1894 rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
1895 if (RT_SUCCESS(rc))
1896 {
1897 if (fFlags & GUEST_PROC_IN_FLAG_EOF)
1898 VGSvcVerbose(4, "Got last process input block for PID=%RU32 (%RU32 bytes) ...\n", uPID, cbInput);
1899
1900 /*
1901 * Locate the process and feed it.
1902 */
1903 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1904 if (pProcess)
1905 {
1906 rc = VGSvcGstCtrlProcessHandleInput(pProcess, pHostCtx, RT_BOOL(fFlags & GUEST_PROC_IN_FLAG_EOF),
1907 *ppvScratchBuf, RT_MIN(cbInput, *pcbScratchBuf));
1908 if (RT_FAILURE(rc))
1909 VGSvcError("Error handling input message for PID=%RU32, rc=%Rrc\n", uPID, rc);
1910 VGSvcGstCtrlProcessRelease(pProcess);
1911 }
1912 else
1913 {
1914 VGSvcError("Could not find PID %u for feeding %u bytes to it.\n", uPID, cbInput);
1915 rc = VERR_PROCESS_NOT_FOUND;
1916 VbglR3GuestCtrlProcCbStatusInput(pHostCtx, uPID, INPUT_STS_ERROR, rc, 0);
1917 }
1918 }
1919 else
1920 {
1921 VGSvcError("Failed to retrieve parameters for process input: %Rrc (scratch %u bytes)\n", rc, *pcbScratchBuf);
1922 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1923 }
1924
1925 VGSvcVerbose(6, "Feeding input to PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1926 return rc;
1927}
1928
1929
1930/**
1931 * Gets stdout/stderr output of a specific guest process.
1932 *
1933 * @returns VBox status code.
1934 * @param pSession The session which is in charge.
1935 * @param pHostCtx The host context to use.
1936 */
1937static int vgsvcGstCtrlSessionHandleProcOutput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1938{
1939 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1940 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1941
1942 /*
1943 * Retrieve the request.
1944 */
1945 uint32_t uPID;
1946 uint32_t uHandleID;
1947 uint32_t fFlags;
1948 int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &fFlags);
1949#ifdef DEBUG_andy
1950 VGSvcVerbose(4, "Getting output for PID=%RU32, CID=%RU32, uHandleID=%RU32, fFlags=%RU32\n",
1951 uPID, pHostCtx->uContextID, uHandleID, fFlags);
1952#endif
1953 if (RT_SUCCESS(rc))
1954 {
1955 /*
1956 * Locate the process and hand it the output request.
1957 */
1958 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1959 if (pProcess)
1960 {
1961 rc = VGSvcGstCtrlProcessHandleOutput(pProcess, pHostCtx, uHandleID, _64K /* cbToRead */, fFlags);
1962 if (RT_FAILURE(rc))
1963 VGSvcError("Error getting output for PID=%RU32, rc=%Rrc\n", uPID, rc);
1964 VGSvcGstCtrlProcessRelease(pProcess);
1965 }
1966 else
1967 {
1968 VGSvcError("Could not find PID %u for draining handle %u (%#x).\n", uPID, uHandleID, uHandleID);
1969 rc = VERR_PROCESS_NOT_FOUND;
1970/** @todo r=bird:
1971 *
1972 * No way to report status status code for output requests?
1973 *
1974 */
1975 }
1976 }
1977 else
1978 {
1979 VGSvcError("Error fetching parameters for process output request: %Rrc\n", rc);
1980 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1981 }
1982
1983#ifdef DEBUG_andy
1984 VGSvcVerbose(4, "Getting output for PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1985#endif
1986 return rc;
1987}
1988
1989
1990/**
1991 * Tells a guest process to terminate.
1992 *
1993 * @returns VBox status code.
1994 * @param pSession The session which is in charge.
1995 * @param pHostCtx The host context to use.
1996 */
1997static int vgsvcGstCtrlSessionHandleProcTerminate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1998{
1999 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2000 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2001
2002 /*
2003 * Retrieve the request.
2004 */
2005 uint32_t uPID;
2006 int rc = VbglR3GuestCtrlProcGetTerminate(pHostCtx, &uPID);
2007 if (RT_SUCCESS(rc))
2008 {
2009 /*
2010 * Locate the process and terminate it.
2011 */
2012 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
2013 if (pProcess)
2014 {
2015 rc = VGSvcGstCtrlProcessHandleTerm(pProcess);
2016 if (RT_FAILURE(rc))
2017 VGSvcError("Error terminating PID=%RU32, rc=%Rrc\n", uPID, rc);
2018
2019 VGSvcGstCtrlProcessRelease(pProcess);
2020 }
2021 else
2022 {
2023 VGSvcError("Could not find PID %u for termination.\n", uPID);
2024 rc = VERR_PROCESS_NOT_FOUND;
2025 }
2026 }
2027 else
2028 {
2029 VGSvcError("Error fetching parameters for process termination request: %Rrc\n", rc);
2030 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2031 }
2032#ifdef DEBUG_andy
2033 VGSvcVerbose(4, "Terminating PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
2034#endif
2035 return rc;
2036}
2037
2038
2039static int vgsvcGstCtrlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
2040{
2041 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2042 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2043
2044 /*
2045 * Retrieve the request.
2046 */
2047 uint32_t uPID;
2048 uint32_t uWaitFlags;
2049 uint32_t uTimeoutMS;
2050 int rc = VbglR3GuestCtrlProcGetWaitFor(pHostCtx, &uPID, &uWaitFlags, &uTimeoutMS);
2051 if (RT_SUCCESS(rc))
2052 {
2053 /*
2054 * Locate the process and the realize that this call makes no sense
2055 * since we'll notify the host when a process terminates anyway and
2056 * hopefully don't need any additional encouragement.
2057 */
2058 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
2059 if (pProcess)
2060 {
2061 rc = VERR_NOT_IMPLEMENTED; /** @todo */
2062 VGSvcGstCtrlProcessRelease(pProcess);
2063 }
2064 else
2065 rc = VERR_NOT_FOUND;
2066 }
2067 else
2068 {
2069 VGSvcError("Error fetching parameters for process wait request: %Rrc\n", rc);
2070 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2071 }
2072 return rc;
2073}
2074
2075
2076#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2077static int vgsvcGstCtrlSessionHandleFsQueryInfo(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
2078{
2079 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2080 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2081
2082 /*
2083 * Retrieve the request.
2084 */
2085 char szPath[RTPATH_MAX];
2086 int rc = VbglR3GuestCtrlFsGetQueryInfo(pHostCtx, szPath, sizeof(szPath));
2087 if (RT_SUCCESS(rc))
2088 {
2089 GSTCTLFSINFO fsInfo;
2090 RT_ZERO(fsInfo);
2091
2092 /* Query as much as we can; ignore any errors and continue. */
2093 int rc2 = RTFsQuerySizes(szPath, (RTFOFF *)&fsInfo.cbTotalSize, (RTFOFF *)&fsInfo.cbFree,
2094 &fsInfo.cbBlockSize, &fsInfo.cbSectorSize);
2095 if (RT_FAILURE(rc2))
2096 VGSvcError("Error calling RTFsQuerySizes() for fsqueryinfo operation: %Rrc\n", rc2);
2097
2098 RTFSPROPERTIES fsProps;
2099 rc2 = RTFsQueryProperties(szPath, &fsProps);
2100 if (RT_SUCCESS(rc2))
2101 {
2102 /* Regular (status) flags. */
2103 fsInfo.cMaxComponent = fsProps.cbMaxComponent;
2104 if (fsProps.fRemote)
2105 fsInfo.fFlags |= GSTCTLFSINFO_F_IS_REMOTE;
2106 if (fsProps.fCaseSensitive)
2107 fsInfo.fFlags |= GSTCTLFSINFO_F_IS_CASE_SENSITIVE;
2108 if (fsProps.fReadOnly)
2109 fsInfo.fFlags |= GSTCTLFSINFO_F_IS_READ_ONLY;
2110 if (fsProps.fCompressed)
2111 fsInfo.fFlags |= GSTCTLFSINFO_F_IS_COMPRESSED;
2112
2113 /* Feature flags. */
2114 if (fsProps.fSupportsUnicode)
2115 fsInfo.fFeatures |= GSTCTLFSINFO_FEATURE_F_UNICODE;
2116 if (fsProps.fFileCompression)
2117 fsInfo.fFeatures |= GSTCTLFSINFO_FEATURE_F_FILE_COMPRESSION;
2118 }
2119 else
2120 VGSvcError("Error calling RTFsQueryProperties() for fsqueryinfo operation: %Rrc\n", rc2);
2121
2122 rc2 = RTFsQuerySerial(szPath, &fsInfo.uSerialNumber);
2123 if (RT_FAILURE(rc2))
2124 VGSvcError("Error calling RTFsQuerySerial() for fsqueryinfo operation: %Rrc\n", rc2);
2125
2126#if 0 /** @todo Enable as soon as RTFsQueryLabel() is implemented. */
2127 rc2 = RTFsQueryLabel(szPath, fsInfo.szLabel, sizeof(fsInfo.szLabel));
2128 if (RT_FAILURE(rc2))
2129 VGSvcError("Error calling RTFsQueryLabel() for fsqueryinfo operation: %Rrc\n", rc2);
2130#endif
2131
2132 RTFSTYPE enmFsType;
2133 rc2 = RTFsQueryType(szPath, &enmFsType);
2134 if (RT_SUCCESS(rc2))
2135 {
2136 if (RTStrPrintf2(fsInfo.szName, sizeof(fsInfo.szName), "%s", RTFsTypeName(enmFsType)) <= 0)
2137 VGSvcError("Error printing type returned by RTFsQueryType()\n");
2138 }
2139 else
2140 VGSvcError("Error calling RTFsQueryType() for fsqueryinfo operation: %Rrc\n", rc2);
2141
2142#if 0 /** @todo Enable as soon as RTFsQueryMountpoint() is implemented. */
2143 char szMountpoint[RTPATH_MAX];
2144 rc2 = RTFsQueryMountpoint(szPath, szMountpoint, sizeof(szMountpoint));
2145 if (RT_SUCCESS(rc2))
2146 {
2147 #error "Implement me"
2148 }
2149 else
2150 VGSvcError("Error calling RTFsQueryMountpoint() for fsqueryinfo operation: %Rrc\n", rc2);
2151#endif
2152
2153 if (RT_SUCCESS(rc))
2154 {
2155 VGSvcVerbose(3, "cbTotalSize=%RU64, cbFree=%RU64, cbBlockSize=%RU32, cbSectorSize=%RU32, fFlags=%#x, fFeatures=%#x\n",
2156 fsInfo.cbTotalSize, fsInfo.cbFree, fsInfo.cbBlockSize, fsInfo.cbSectorSize,
2157 fsInfo.fFlags, fsInfo.fFeatures);
2158 VGSvcVerbose(3, "szName=%s, szLabel=%s\n", fsInfo.szName, fsInfo.szLabel);
2159 }
2160
2161 uint32_t const cbFsInfo = sizeof(GSTCTLFSINFO); /** @todo Needs tweaking as soon as we resolve the mountpoint above. */
2162
2163 rc2 = VbglR3GuestCtrlFsCbQueryInfo(pHostCtx, rc, &fsInfo, cbFsInfo);
2164 if (RT_FAILURE(rc2))
2165 {
2166 VGSvcError("Failed to reply to fsobjquerinfo request %Rrc, rc=%Rrc\n", rc, rc2);
2167 if (RT_SUCCESS(rc))
2168 rc = rc2;
2169 }
2170 }
2171 else
2172 {
2173 VGSvcError("Error fetching parameters for fsqueryinfo operation: %Rrc\n", rc);
2174 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2175 }
2176 return rc;
2177}
2178
2179static int vgsvcGstCtrlSessionHandleFsObjQueryInfo(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
2180{
2181 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2182 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2183
2184 /*
2185 * Retrieve the request.
2186 */
2187 char szPath[RTPATH_MAX];
2188 GSTCTLFSOBJATTRADD enmAttrAdd;
2189 uint32_t fFlags;
2190 RTFSOBJINFO objInfoRuntime;
2191
2192 int rc = VbglR3GuestCtrlFsObjGetQueryInfo(pHostCtx, szPath, sizeof(szPath), &enmAttrAdd, &fFlags);
2193 if (RT_SUCCESS(rc))
2194 {
2195 uint32_t fFlagsRuntime = 0;
2196
2197 if (!(fFlags & ~GSTCTL_PATH_F_VALID_MASK))
2198 {
2199 if (fFlags & GSTCTL_PATH_F_ON_LINK)
2200 fFlagsRuntime |= RTPATH_F_ON_LINK;
2201 if (fFlags & GSTCTL_PATH_F_FOLLOW_LINK)
2202 fFlagsRuntime |= RTPATH_F_FOLLOW_LINK;
2203 if (fFlags & GSTCTL_PATH_F_NO_SYMLINKS)
2204 fFlagsRuntime |= RTPATH_F_NO_SYMLINKS;
2205
2206 if (!RTPATH_F_IS_VALID(fFlagsRuntime, 0))
2207 rc = VERR_INVALID_PARAMETER;
2208 }
2209 else
2210 rc = VERR_INVALID_PARAMETER;
2211
2212 if (RT_FAILURE(rc))
2213 VGSvcError("Invalid fsobjqueryinfo flags: %#x (%#x)\n", fFlags, fFlagsRuntime);
2214
2215 if (RT_SUCCESS(rc))
2216 {
2217#define CASE_ATTR_ADD_VAL(a_Val) \
2218 case GSTCTL##a_Val: enmAttrRuntime = RT##a_Val; break;
2219
2220 RTFSOBJATTRADD enmAttrRuntime;
2221 switch (enmAttrAdd)
2222 {
2223 CASE_ATTR_ADD_VAL(FSOBJATTRADD_NOTHING);
2224 CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX);
2225 CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX_OWNER);
2226 CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX_GROUP);
2227 CASE_ATTR_ADD_VAL(FSOBJATTRADD_EASIZE);
2228 default:
2229 enmAttrRuntime = RTFSOBJATTRADD_NOTHING;
2230 break;
2231 }
2232
2233#undef CASE_ATTR_ADD_VAL
2234
2235 /*
2236 * For now we ASSUME that RTFSOBJINFO == GSTCTLFSOBJINFO, which implies that we simply can cast RTFSOBJINFO
2237 * to GSTCTLFSOBJINFO. This might change in the future, however, so be extra cautious here.
2238 *
2239 * Ditto for RTFSOBJATTR == GSTCTLFSOBJATTR.
2240 */
2241 AssertCompileSize(objInfoRuntime, sizeof(GSTCTLFSOBJINFO));
2242 AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, cbObject) == RT_OFFSETOF(GSTCTLFSOBJINFO, cbObject));
2243 AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, Attr) == RT_OFFSETOF(GSTCTLFSOBJINFO, Attr));
2244 AssertCompileSize(RTFSOBJATTR, sizeof(GSTCTLFSOBJATTR));
2245
2246 rc = RTPathQueryInfoEx(szPath, &objInfoRuntime, enmAttrRuntime, fFlagsRuntime);
2247 }
2248
2249 PGSTCTLFSOBJINFO pObjInfo = (PGSTCTLFSOBJINFO)&objInfoRuntime;
2250
2251 const char *pszUser = VGSvcIdCacheGetUidName(&pSession->UidCache, pObjInfo->Attr.u.Unix.uid, szPath, NULL /* pszRelativeTo */);
2252 const char *pszGroup = VGSvcIdCacheGetGidName(&pSession->GidCache, pObjInfo->Attr.u.Unix.gid, szPath, NULL /* pszRelativeTo */);
2253
2254 int rc2 = VbglR3GuestCtrlFsObjCbQueryInfoEx(pHostCtx, rc, pObjInfo, pszUser, pszGroup);
2255 if (RT_FAILURE(rc2))
2256 {
2257 VGSvcError("Failed to reply to fsobjquerinfo request %Rrc, rc=%Rrc\n", rc, rc2);
2258 if (RT_SUCCESS(rc))
2259 rc = rc2;
2260 }
2261 }
2262 else
2263 {
2264 VGSvcError("Error fetching parameters for fsobjqueryinfo operation: %Rrc\n", rc);
2265 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2266 }
2267 return rc;
2268}
2269
2270
2271static int vgsvcGstCtrlSessionHandleFsCreateTemp(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
2272{
2273 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2274 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2275
2276 /*
2277 * Retrieve the request.
2278 */
2279 char szTemplate[RTPATH_MAX];
2280 char szTempPathAbs[RTPATH_MAX] = ""; /* Absolute path to temporary directory / file created. */
2281 char szPath[RTPATH_MAX];
2282 uint32_t fFlags = GSTCTL_CREATETEMP_F_NONE;
2283 RTFMODE fMode = 0700;
2284 int rc = VbglR3GuestCtrlFsGetCreateTemp(pHostCtx, szTemplate, sizeof(szTemplate), szPath, sizeof(szPath), &fFlags, &fMode);
2285 if (RT_SUCCESS(rc))
2286 {
2287 if (!(fFlags & ~GSTCTL_CREATETEMP_F_VALID_MASK))
2288 {
2289 const char *pszWhat = fFlags & GSTCTL_CREATETEMP_F_DIRECTORY ? "directory" : "file";
2290
2291 /* Validate that the template is as IPRT requires (asserted by IPRT). */
2292 if ( RTPathHasPath(szTemplate)
2293 || ( !strstr(szTemplate, "XXX")
2294 && szTemplate[strlen(szTemplate) - 1] != 'X'))
2295 {
2296 VGSvcError("createtemp: Template '%s' should contain a file name with no path and at least three consecutive 'X' characters or ending in 'X'\n",
2297 szTemplate);
2298 rc = VERR_INVALID_PARAMETER;
2299 }
2300
2301 if ( RT_SUCCESS(rc)
2302 && szPath[0] != '\0' && !RTPathStartsWithRoot(szPath))
2303 {
2304 VGSvcError("createtemp: Path '%s' must be absolute\n", szPath);
2305 rc = VERR_INVALID_PARAMETER;
2306 }
2307
2308 if (RT_SUCCESS(rc))
2309 {
2310 if (szPath[0] != '\0')
2311 {
2312 rc = RTStrCopy(szTempPathAbs, sizeof(szTempPathAbs), szPath);
2313 if (RT_FAILURE(rc))
2314 {
2315 VGSvcError("createtemp: Path '%s' too long\n", szPath);
2316 rc = VERR_INVALID_PARAMETER;
2317 }
2318 }
2319 else
2320 {
2321 rc = RTPathTemp(szTempPathAbs, sizeof(szTempPathAbs));
2322 if (RT_FAILURE(rc))
2323 {
2324 VGSvcError("createtemp: Failed to get the temporary directory (%Rrc)", rc);
2325 rc = VERR_INVALID_PARAMETER;
2326 }
2327 }
2328
2329 if (RT_SUCCESS(rc))
2330 {
2331 rc = RTPathAppend(szTempPathAbs, sizeof(szTempPathAbs), szTemplate);
2332 if (RT_FAILURE(rc))
2333 {
2334 VGSvcError("createtemp: Template '%s' too long for path\n", szTemplate);
2335 rc = VERR_INVALID_PARAMETER;
2336 }
2337 else
2338 {
2339 bool const fSecure = RT_BOOL(fFlags & GSTCTL_CREATETEMP_F_SECURE);
2340 if (fFlags & GSTCTL_CREATETEMP_F_DIRECTORY)
2341 {
2342 if (fSecure)
2343 rc = RTDirCreateTempSecure(szTempPathAbs); /* File mode is fixed to 0700. */
2344 else
2345 rc = RTDirCreateTemp(szTempPathAbs, fMode);
2346 }
2347 else /* File */
2348 {
2349 if (fSecure)
2350 rc = RTFileCreateTempSecure(szTempPathAbs); /* File mode is fixed to 0700. */
2351 else
2352 rc = RTFileCreateTemp(szTempPathAbs, fMode);
2353 }
2354
2355 VGSvcVerbose(3, "Creating temporary %s (szTemplate='%s', fFlags=%#x, fMode=%#x) -> %s, rc=%Rrc\n",
2356 pszWhat, szTemplate, fFlags, fMode, szTempPathAbs, rc);
2357 }
2358 }
2359 }
2360 }
2361 else
2362 {
2363 VGSvcError("Invalid temporary directory/file creation flags: %#x\n", fFlags);
2364 rc = VERR_NOT_SUPPORTED;
2365 }
2366
2367 /*
2368 * Report result back to host.
2369 */
2370 int rc2 = VbglR3GuestCtrlFsCbCreateTemp(pHostCtx, rc, szTempPathAbs);
2371 if (RT_FAILURE(rc2))
2372 {
2373 VGSvcError("Failed to report temporary file/directory creation status, rc=%Rrc\n", rc2);
2374 if (RT_SUCCESS(rc))
2375 rc = rc2;
2376 }
2377 }
2378 else
2379 {
2380 VGSvcError("Error fetching parameters for file/directory creation operation: %Rrc\n", rc);
2381 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
2382 }
2383 VGSvcVerbose(3, "Creating temporary file/directory returned rc=%Rrc\n", rc);
2384 return rc;
2385}
2386#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
2387
2388
2389int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
2390 void **ppvScratchBuf, uint32_t *pcbScratchBuf, volatile bool *pfShutdown)
2391{
2392 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2393 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
2394 AssertPtrReturn(*ppvScratchBuf, VERR_INVALID_POINTER);
2395 AssertPtrReturn(pfShutdown, VERR_INVALID_POINTER);
2396
2397
2398 /*
2399 * Only anonymous sessions (that is, sessions which run with local
2400 * service privileges) or spawned session processes can do certain
2401 * operations.
2402 */
2403 bool const fImpersonated = RT_BOOL(pSession->fFlags & ( VBOXSERVICECTRLSESSION_FLAG_SPAWN
2404 | VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS));
2405 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
2406
2407 switch (uMsg)
2408 {
2409 case HOST_MSG_SESSION_CLOSE:
2410 /* Shutdown (this spawn). */
2411 rc = VGSvcGstCtrlSessionClose(pSession);
2412 *pfShutdown = true; /* Shutdown in any case. */
2413 break;
2414
2415 case HOST_MSG_EXEC_CMD:
2416 rc = vgsvcGstCtrlSessionHandleProcExec(pSession, pHostCtx);
2417 break;
2418
2419 case HOST_MSG_EXEC_SET_INPUT:
2420 rc = vgsvcGstCtrlSessionHandleProcInput(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2421 break;
2422
2423 case HOST_MSG_EXEC_GET_OUTPUT:
2424 rc = vgsvcGstCtrlSessionHandleProcOutput(pSession, pHostCtx);
2425 break;
2426
2427 case HOST_MSG_EXEC_TERMINATE:
2428 rc = vgsvcGstCtrlSessionHandleProcTerminate(pSession, pHostCtx);
2429 break;
2430
2431 case HOST_MSG_EXEC_WAIT_FOR:
2432 rc = vgsvcGstCtrlSessionHandleProcWaitFor(pSession, pHostCtx);
2433 break;
2434
2435#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2436 case HOST_MSG_FS_OBJ_QUERY_INFO:
2437 if (fImpersonated)
2438 rc = vgsvcGstCtrlSessionHandleFsObjQueryInfo(pSession, pHostCtx);
2439 break;
2440
2441 case HOST_MSG_FS_CREATE_TEMP:
2442 if (fImpersonated)
2443 rc = vgsvcGstCtrlSessionHandleFsCreateTemp(pSession, pHostCtx);
2444 break;
2445
2446 case HOST_MSG_FS_QUERY_INFO:
2447 if (fImpersonated)
2448 rc = vgsvcGstCtrlSessionHandleFsQueryInfo(pSession, pHostCtx);
2449 break;
2450#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
2451
2452 case HOST_MSG_FILE_OPEN:
2453 if (fImpersonated)
2454 rc = vgsvcGstCtrlSessionHandleFileOpen(pSession, pHostCtx);
2455 break;
2456
2457 case HOST_MSG_FILE_CLOSE:
2458 if (fImpersonated)
2459 rc = vgsvcGstCtrlSessionHandleFileClose(pSession, pHostCtx);
2460 break;
2461
2462 case HOST_MSG_FILE_READ:
2463 if (fImpersonated)
2464 rc = vgsvcGstCtrlSessionHandleFileRead(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2465 break;
2466
2467 case HOST_MSG_FILE_READ_AT:
2468 if (fImpersonated)
2469 rc = vgsvcGstCtrlSessionHandleFileReadAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2470 break;
2471
2472 case HOST_MSG_FILE_WRITE:
2473 if (fImpersonated)
2474 rc = vgsvcGstCtrlSessionHandleFileWrite(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2475 break;
2476
2477 case HOST_MSG_FILE_WRITE_AT:
2478 if (fImpersonated)
2479 rc = vgsvcGstCtrlSessionHandleFileWriteAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
2480 break;
2481
2482 case HOST_MSG_FILE_SEEK:
2483 if (fImpersonated)
2484 rc = vgsvcGstCtrlSessionHandleFileSeek(pSession, pHostCtx);
2485 break;
2486
2487 case HOST_MSG_FILE_TELL:
2488 if (fImpersonated)
2489 rc = vgsvcGstCtrlSessionHandleFileTell(pSession, pHostCtx);
2490 break;
2491
2492 case HOST_MSG_FILE_SET_SIZE:
2493 if (fImpersonated)
2494 rc = vgsvcGstCtrlSessionHandleFileSetSize(pSession, pHostCtx);
2495 break;
2496
2497#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
2498 case HOST_MSG_FILE_REMOVE:
2499 if (fImpersonated)
2500 rc = vgsvcGstCtrlSessionHandleFileRemove(pSession, pHostCtx);
2501 break;
2502
2503 case HOST_MSG_DIR_OPEN:
2504 if (fImpersonated)
2505 rc = vgsvcGstCtrlSessionHandleDirOpen(pSession, pHostCtx);
2506 break;
2507
2508 case HOST_MSG_DIR_CLOSE:
2509 if (fImpersonated)
2510 rc = vgsvcGstCtrlSessionHandleDirClose(pSession, pHostCtx);
2511 break;
2512
2513 case HOST_MSG_DIR_READ:
2514 if (fImpersonated)
2515 rc = vgsvcGstCtrlSessionHandleDirRead(pSession, pHostCtx);
2516 break;
2517
2518 case HOST_MSG_DIR_REWIND:
2519 if (fImpersonated)
2520 rc = vgsvcGstCtrlSessionHandleDirRewind(pSession, pHostCtx);
2521 break;
2522
2523 case HOST_MSG_DIR_CREATE:
2524 if (fImpersonated)
2525 rc = vgsvcGstCtrlSessionHandleDirCreate(pSession, pHostCtx);
2526 break;
2527
2528 case HOST_MSG_DIR_LIST:
2529 if (fImpersonated)
2530 rc = vgsvcGstCtrlSessionHandleDirList(pSession, pHostCtx);
2531 break;
2532#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
2533
2534 case HOST_MSG_DIR_REMOVE:
2535 if (fImpersonated)
2536 rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx);
2537 break;
2538
2539 case HOST_MSG_PATH_RENAME:
2540 if (fImpersonated)
2541 rc = vgsvcGstCtrlSessionHandlePathRename(pSession, pHostCtx);
2542 break;
2543
2544 case HOST_MSG_PATH_USER_DOCUMENTS:
2545 if (fImpersonated)
2546 rc = vgsvcGstCtrlSessionHandlePathUserDocuments(pSession, pHostCtx);
2547 break;
2548
2549 case HOST_MSG_PATH_USER_HOME:
2550 if (fImpersonated)
2551 rc = vgsvcGstCtrlSessionHandlePathUserHome(pSession, pHostCtx);
2552 break;
2553
2554 case HOST_MSG_MOUNT_POINTS:
2555 if (fImpersonated)
2556 rc = vgsvcGstCtrlSessionHandleMountPoints(pSession, pHostCtx);
2557 break;
2558
2559 case HOST_MSG_SHUTDOWN:
2560 rc = vgsvcGstCtrlSessionHandleShutdown(pSession, pHostCtx);
2561 break;
2562
2563 default: /* Not supported, see next code block. */
2564 break;
2565 }
2566 if (RT_SUCCESS(rc))
2567 { /* likely */ }
2568 else if (rc != VERR_NOT_SUPPORTED) /* Note: Reply to host must must be sent by above handler. */
2569 VGSvcError("Error while handling message %s (%#x, cParms=%RU32), rc=%Rrc\n",
2570 GstCtrlHostMsgtoStr((eHostMsg)uMsg), uMsg, pHostCtx->uNumParms, rc);
2571 else
2572 {
2573 /* We must skip and notify host here as best we can... */
2574 VGSvcVerbose(1, "Unsupported message (uMsg=%RU32, cParms=%RU32) from host, skipping\n", uMsg, pHostCtx->uNumParms);
2575 if (VbglR3GuestCtrlSupportsOptimizations(pHostCtx->uClientID))
2576 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, VERR_NOT_SUPPORTED, uMsg);
2577 else
2578 VbglR3GuestCtrlMsgSkipOld(pHostCtx->uClientID);
2579 rc = VINF_SUCCESS;
2580 }
2581
2582 return rc;
2583}
2584
2585
2586/**
2587 * Thread main routine for a spawned guest session process.
2588 *
2589 * This thread runs in the main executable to control the spawned session process.
2590 *
2591 * @returns VBox status code.
2592 * @param hThreadSelf Thread handle.
2593 * @param pvUser Pointer to a VBOXSERVICECTRLSESSIONTHREAD structure.
2594 *
2595 */
2596static DECLCALLBACK(int) vgsvcGstCtrlSessionThread(RTTHREAD hThreadSelf, void *pvUser)
2597{
2598 PVBOXSERVICECTRLSESSIONTHREAD pThread = (PVBOXSERVICECTRLSESSIONTHREAD)pvUser;
2599 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2600
2601 uint32_t const idSession = pThread->pStartupInfo->uSessionID;
2602 uint32_t const idClient = g_idControlSvcClient;
2603 VGSvcVerbose(3, "Session ID=%RU32 thread running\n", idSession);
2604
2605 /* Let caller know that we're done initializing, regardless of the result. */
2606 int rc2 = RTThreadUserSignal(hThreadSelf);
2607 AssertRC(rc2);
2608
2609 /*
2610 * Wait for the child process to stop or the shutdown flag to be signalled.
2611 */
2612 RTPROCSTATUS ProcessStatus = { 0, RTPROCEXITREASON_NORMAL };
2613 bool fProcessAlive = true;
2614 bool fSessionCancelled = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
2615 uint32_t cMsShutdownTimeout = 30 * 1000; /** @todo Make this configurable. Later. */
2616 uint64_t msShutdownStart = 0;
2617 uint64_t const msStart = RTTimeMilliTS();
2618 size_t offSecretKey = 0;
2619 int rcWait;
2620 for (;;)
2621 {
2622 /* Secret key feeding. */
2623 if (offSecretKey < sizeof(pThread->abKey))
2624 {
2625 size_t cbWritten = 0;
2626 rc2 = RTPipeWrite(pThread->hKeyPipe, &pThread->abKey[offSecretKey], sizeof(pThread->abKey) - offSecretKey, &cbWritten);
2627 if (RT_SUCCESS(rc2))
2628 offSecretKey += cbWritten;
2629 }
2630
2631 /* Poll child process status. */
2632 rcWait = RTProcWaitNoResume(pThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
2633 if ( rcWait == VINF_SUCCESS
2634 || rcWait == VERR_PROCESS_NOT_FOUND)
2635 {
2636 fProcessAlive = false;
2637 break;
2638 }
2639 AssertMsgBreak(rcWait == VERR_PROCESS_RUNNING || rcWait == VERR_INTERRUPTED,
2640 ("Got unexpected rc=%Rrc while waiting for session process termination\n", rcWait));
2641
2642 /* Shutting down? */
2643 if (ASMAtomicReadBool(&pThread->fShutdown))
2644 {
2645 if (!msShutdownStart)
2646 {
2647 VGSvcVerbose(3, "Notifying guest session process (PID=%RU32, session ID=%RU32) ...\n",
2648 pThread->hProcess, idSession);
2649
2650 VBGLR3GUESTCTRLCMDCTX hostCtx =
2651 {
2652 /* .idClient = */ idClient,
2653 /* .idContext = */ VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
2654 /* .uProtocol = */ pThread->pStartupInfo->uProtocol,
2655 /* .cParams = */ 2
2656 };
2657 rc2 = VbglR3GuestCtrlSessionClose(&hostCtx, 0 /* fFlags */);
2658 if (RT_FAILURE(rc2))
2659 {
2660 VGSvcError("Unable to notify guest session process (PID=%RU32, session ID=%RU32), rc=%Rrc\n",
2661 pThread->hProcess, idSession, rc2);
2662
2663 if (rc2 == VERR_NOT_SUPPORTED)
2664 {
2665 /* Terminate guest session process in case it's not supported by a too old host. */
2666 rc2 = RTProcTerminate(pThread->hProcess);
2667 VGSvcVerbose(3, "Terminating guest session process (PID=%RU32) ended with rc=%Rrc\n",
2668 pThread->hProcess, rc2);
2669 }
2670 break;
2671 }
2672
2673 VGSvcVerbose(3, "Guest session ID=%RU32 thread was asked to terminate, waiting for session process to exit (%RU32 ms timeout) ...\n",
2674 idSession, cMsShutdownTimeout);
2675 msShutdownStart = RTTimeMilliTS();
2676 continue; /* Don't waste time on waiting. */
2677 }
2678 if (RTTimeMilliTS() - msShutdownStart > cMsShutdownTimeout)
2679 {
2680 VGSvcVerbose(3, "Guest session ID=%RU32 process did not shut down within time\n", idSession);
2681 break;
2682 }
2683 }
2684
2685 /* Cancel the prepared session stuff after 30 seconds. */
2686 if ( !fSessionCancelled
2687 && RTTimeMilliTS() - msStart >= 30000)
2688 {
2689 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
2690 fSessionCancelled = true;
2691 }
2692
2693/** @todo r=bird: This 100ms sleep is _extremely_ sucky! */
2694 RTThreadSleep(100); /* Wait a bit. */
2695 }
2696
2697 if (!fSessionCancelled)
2698 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
2699
2700 if (!fProcessAlive)
2701 {
2702 VGSvcVerbose(2, "Guest session process (ID=%RU32) terminated with rc=%Rrc, reason=%d, status=%d\n",
2703 idSession, rcWait, ProcessStatus.enmReason, ProcessStatus.iStatus);
2704 if (ProcessStatus.iStatus == RTEXITCODE_INIT)
2705 {
2706 VGSvcError("Guest session process (ID=%RU32) failed to initialize. Here some hints:\n", idSession);
2707 VGSvcError("- Is logging enabled and the output directory is read-only by the guest session user?\n");
2708 /** @todo Add more here. */
2709 }
2710 }
2711
2712 uint32_t uSessionStatus = GUEST_SESSION_NOTIFYTYPE_UNDEFINED;
2713 int32_t iSessionResult = VINF_SUCCESS;
2714
2715 if (fProcessAlive)
2716 {
2717 for (int i = 0; i < 3; i++)
2718 {
2719 if (i)
2720 RTThreadSleep(3000);
2721
2722 VGSvcVerbose(2, "Guest session ID=%RU32 process still alive, killing attempt %d/3\n", idSession, i + 1);
2723
2724 rc2 = RTProcTerminate(pThread->hProcess);
2725 if (RT_SUCCESS(rc2))
2726 break;
2727 }
2728
2729 VGSvcVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n", idSession, rc2);
2730 uSessionStatus = RT_SUCCESS(rc2) ? GUEST_SESSION_NOTIFYTYPE_TOK : GUEST_SESSION_NOTIFYTYPE_TOA;
2731 }
2732 else if (RT_SUCCESS(rcWait))
2733 {
2734 switch (ProcessStatus.enmReason)
2735 {
2736 case RTPROCEXITREASON_NORMAL:
2737 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
2738 iSessionResult = ProcessStatus.iStatus; /* Report back the session's exit code. */
2739 break;
2740
2741 case RTPROCEXITREASON_ABEND:
2742 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
2743 /* iSessionResult is undefined (0). */
2744 break;
2745
2746 case RTPROCEXITREASON_SIGNAL:
2747 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
2748 iSessionResult = ProcessStatus.iStatus; /* Report back the signal number. */
2749 break;
2750
2751 default:
2752 AssertMsgFailed(("Unhandled process termination reason (%d)\n", ProcessStatus.enmReason));
2753 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
2754 break;
2755 }
2756 }
2757 else
2758 {
2759 /* If we didn't find the guest process anymore, just assume it terminated normally. */
2760 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
2761 }
2762
2763 /* Make sure to set stopped state before we let the host know. */
2764 ASMAtomicWriteBool(&pThread->fStopped, true);
2765
2766 /* Report final status, regardless if we failed to wait above, so that the host knows what's going on. */
2767 VGSvcVerbose(3, "Reporting final status %RU32 of session ID=%RU32\n", uSessionStatus, idSession);
2768 Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED);
2769
2770 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
2771 0 /* uProtocol, unused */, 0 /* uNumParms, unused */ };
2772 rc2 = VbglR3GuestCtrlSessionNotify(&ctx, uSessionStatus, iSessionResult);
2773 if (RT_FAILURE(rc2))
2774 VGSvcError("Reporting final status of session ID=%RU32 failed with rc=%Rrc\n", idSession, rc2);
2775
2776 VGSvcVerbose(3, "Thread for session ID=%RU32 ended with sessionStatus=%#x (%RU32), sessionRc=%#x (%Rrc)\n",
2777 idSession, uSessionStatus, uSessionStatus, iSessionResult, iSessionResult);
2778
2779 return VINF_SUCCESS;
2780}
2781
2782/**
2783 * Reads the secret key the parent VBoxService instance passed us and pass it
2784 * along as a authentication token to the host service.
2785 *
2786 * For older hosts, this sets up the message filtering.
2787 *
2788 * @returns VBox status code.
2789 * @param idClient The HGCM client ID.
2790 * @param idSession The session ID.
2791 */
2792static int vgsvcGstCtrlSessionReadKeyAndAccept(uint32_t idClient, uint32_t idSession)
2793{
2794 /*
2795 * Read it.
2796 */
2797 RTHANDLE Handle;
2798 int rc = RTHandleGetStandard(RTHANDLESTD_INPUT, true /*fLeaveOpen*/, &Handle);
2799 if (RT_SUCCESS(rc))
2800 {
2801 if (Handle.enmType == RTHANDLETYPE_PIPE)
2802 {
2803 uint8_t abSecretKey[RT_SIZEOFMEMB(VBOXSERVICECTRLSESSIONTHREAD, abKey)];
2804 rc = RTPipeReadBlocking(Handle.u.hPipe, abSecretKey, sizeof(abSecretKey), NULL);
2805 if (RT_SUCCESS(rc))
2806 {
2807 VGSvcVerbose(3, "Got secret key from standard input.\n");
2808
2809 /*
2810 * Do the accepting, if appropriate.
2811 */
2812 if (g_fControlSupportsOptimizations)
2813 {
2814 rc = VbglR3GuestCtrlSessionAccept(idClient, idSession, abSecretKey, sizeof(abSecretKey));
2815 if (RT_SUCCESS(rc))
2816 VGSvcVerbose(3, "Session %u accepted (client ID %u)\n", idClient, idSession);
2817 else
2818 VGSvcError("Failed to accept session %u (client ID %u): %Rrc\n", idClient, idSession, rc);
2819 }
2820 else
2821 {
2822 /* For legacy hosts, we do the filtering thingy. */
2823 rc = VbglR3GuestCtrlMsgFilterSet(idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
2824 VBOX_GUESTCTRL_FILTER_BY_SESSION(idSession), 0);
2825 if (RT_SUCCESS(rc))
2826 VGSvcVerbose(3, "Session %u filtering successfully enabled\n", idSession);
2827 else
2828 VGSvcError("Failed to set session filter: %Rrc\n", rc);
2829 }
2830 }
2831 else
2832 VGSvcError("Error reading secret key from standard input: %Rrc\n", rc);
2833 }
2834 else
2835 {
2836 VGSvcError("Standard input is not a pipe!\n");
2837 rc = VERR_INVALID_HANDLE;
2838 }
2839 RTHandleClose(&Handle);
2840 }
2841 else
2842 VGSvcError("RTHandleGetStandard failed on standard input: %Rrc\n", rc);
2843 return rc;
2844}
2845
2846/**
2847 * Invalidates a guest session by updating all it's internal parameters like host features and stuff.
2848 *
2849 * @param pSession Session to invalidate.
2850 * @param idClient Client ID to use.
2851 */
2852static void vgsvcGstCtrlSessionInvalidate(PVBOXSERVICECTRLSESSION pSession, uint32_t idClient)
2853{
2854 RT_NOREF(pSession);
2855
2856 VGSvcVerbose(1, "Invalidating session %RU32 (client ID=%RU32)\n", idClient, pSession->StartupInfo.uSessionID);
2857
2858 int rc2 = VbglR3GuestCtrlQueryFeatures(idClient, &g_fControlHostFeatures0);
2859 if (RT_SUCCESS(rc2)) /* Querying host features is not fatal -- do not use rc here. */
2860 {
2861 VGSvcVerbose(1, "g_fControlHostFeatures0=%#x\n", g_fControlHostFeatures0);
2862 }
2863 else
2864 VGSvcVerbose(1, "Querying host features failed with %Rrc\n", rc2);
2865}
2866
2867/**
2868 * Main message handler for the guest control session process.
2869 *
2870 * @returns exit code.
2871 * @param pSession Pointer to g_Session.
2872 * @thread main.
2873 */
2874static RTEXITCODE vgsvcGstCtrlSessionSpawnWorker(PVBOXSERVICECTRLSESSION pSession)
2875{
2876 AssertPtrReturn(pSession, RTEXITCODE_FAILURE);
2877 VGSvcVerbose(0, "Hi, this is guest session ID=%RU32\n", pSession->StartupInfo.uSessionID);
2878
2879 /*
2880 * Connect to the host service.
2881 */
2882 uint32_t idClient;
2883 int rc = VbglR3GuestCtrlConnect(&idClient);
2884 if (RT_FAILURE(rc))
2885 return VGSvcError("Error connecting to guest control service, rc=%Rrc\n", rc);
2886 g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(idClient);
2887 g_idControlSvcClient = idClient;
2888
2889 VGSvcVerbose(1, "Using client ID=%RU32\n", idClient);
2890
2891 vgsvcGstCtrlSessionInvalidate(pSession, idClient);
2892
2893 rc = vgsvcGstCtrlSessionReadKeyAndAccept(idClient, pSession->StartupInfo.uSessionID);
2894 if (RT_SUCCESS(rc))
2895 {
2896 /*
2897 * Report started status.
2898 * If session status cannot be posted to the host for some reason, bail out.
2899 */
2900 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID),
2901 0 /* uProtocol, unused */, 0 /* uNumParms, unused */ };
2902 rc = VbglR3GuestCtrlSessionNotify(&ctx, GUEST_SESSION_NOTIFYTYPE_STARTED, VINF_SUCCESS);
2903 if (RT_SUCCESS(rc))
2904 {
2905 /*
2906 * Allocate a scratch buffer for messages which also send payload data with them.
2907 * This buffer may grow if the host sends us larger chunks of data.
2908 */
2909 uint32_t cbScratchBuf = _64K;
2910 void *pvScratchBuf = RTMemAlloc(cbScratchBuf);
2911 if (pvScratchBuf)
2912 {
2913 int cFailedMsgPeeks = 0;
2914
2915 /*
2916 * Message processing loop.
2917 */
2918 VBGLR3GUESTCTRLCMDCTX CtxHost = { idClient, 0 /* Context ID */, pSession->StartupInfo.uProtocol, 0 };
2919 for (;;)
2920 {
2921 VGSvcVerbose(3, "Waiting for host msg ...\n");
2922 uint32_t uMsg = 0;
2923 rc = VbglR3GuestCtrlMsgPeekWait(idClient, &uMsg, &CtxHost.uNumParms, NULL);
2924 if (RT_SUCCESS(rc))
2925 {
2926 VGSvcVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved (%Rrc)\n", uMsg, CtxHost.uNumParms, rc);
2927
2928 /*
2929 * Pass it on to the session handler.
2930 * Note! Only when handling HOST_SESSION_CLOSE is the rc used.
2931 */
2932 bool fShutdown = false;
2933 rc = VGSvcGstCtrlSessionHandler(pSession, uMsg, &CtxHost, &pvScratchBuf, &cbScratchBuf, &fShutdown);
2934 if (fShutdown)
2935 break;
2936
2937 cFailedMsgPeeks = 0;
2938
2939 /* Let others run (guests are often single CPU) ... */
2940 RTThreadYield();
2941 }
2942 /*
2943 * Handle restore notification from host. All the context IDs (sessions,
2944 * files, proceses, etc) are invalidated by a VM restore and must be closed.
2945 */
2946 else if (rc == VERR_VM_RESTORED)
2947 {
2948 VGSvcVerbose(1, "The VM session ID changed (i.e. restored), closing stale session %RU32\n",
2949 pSession->StartupInfo.uSessionID);
2950
2951 /* We currently don't serialize guest sessions, guest processes and other guest control objects
2952 * within saved states. So just close this session and report success to the parent process.
2953 *
2954 * Note: Not notifying the host here is intentional, as it wouldn't have any information
2955 * about what to do with it.
2956 */
2957 rc = VINF_SUCCESS; /* Report success as exit code. */
2958 break;
2959 }
2960 else
2961 {
2962 VGSvcVerbose(1, "Getting host message failed with %Rrc\n", rc);
2963
2964 if (cFailedMsgPeeks++ == 3)
2965 break;
2966
2967 RTThreadSleep(3 * RT_MS_1SEC);
2968
2969 /** @todo Shouldn't we have a plan for handling connection loss and such? */
2970 }
2971 }
2972
2973 /*
2974 * Shutdown.
2975 */
2976 RTMemFree(pvScratchBuf);
2977 }
2978 else
2979 rc = VERR_NO_MEMORY;
2980
2981 VGSvcVerbose(0, "Session %RU32 ended\n", pSession->StartupInfo.uSessionID);
2982 }
2983 else
2984 VGSvcError("Reporting session ID=%RU32 started status failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
2985 }
2986 else
2987 VGSvcError("Setting message filterAdd=0x%x failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
2988
2989 VGSvcVerbose(3, "Disconnecting client ID=%RU32 ...\n", idClient);
2990 VbglR3GuestCtrlDisconnect(idClient);
2991 g_idControlSvcClient = 0;
2992
2993 VGSvcVerbose(3, "Session worker returned with rc=%Rrc\n", rc);
2994 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2995}
2996
2997
2998/**
2999 * Finds a (formerly) started guest process given by its PID and increases its
3000 * reference count.
3001 *
3002 * Must be decreased by the caller with VGSvcGstCtrlProcessRelease().
3003 *
3004 * @returns Guest process if found, otherwise NULL.
3005 * @param pSession Pointer to guest session where to search process in.
3006 * @param uPID PID to search for.
3007 *
3008 * @note This does *not lock the process!
3009 */
3010PVBOXSERVICECTRLPROCESS VGSvcGstCtrlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID)
3011{
3012 AssertPtrReturn(pSession, NULL);
3013
3014 PVBOXSERVICECTRLPROCESS pProcess = NULL;
3015 int rc = RTCritSectEnter(&pSession->CritSect);
3016 if (RT_SUCCESS(rc))
3017 {
3018 PVBOXSERVICECTRLPROCESS pCurProcess;
3019 RTListForEach(&pSession->lstProcesses, pCurProcess, VBOXSERVICECTRLPROCESS, Node)
3020 {
3021 if (pCurProcess->uPID == uPID)
3022 {
3023 rc = RTCritSectEnter(&pCurProcess->CritSect);
3024 if (RT_SUCCESS(rc))
3025 {
3026 pCurProcess->cRefs++;
3027 rc = RTCritSectLeave(&pCurProcess->CritSect);
3028 AssertRC(rc);
3029 }
3030
3031 if (RT_SUCCESS(rc))
3032 pProcess = pCurProcess;
3033 break;
3034 }
3035 }
3036
3037 rc = RTCritSectLeave(&pSession->CritSect);
3038 AssertRC(rc);
3039 }
3040
3041 return pProcess;
3042}
3043
3044
3045int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession)
3046{
3047 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3048
3049 VGSvcVerbose(0, "Session %RU32 is about to close ...\n", pSession->StartupInfo.uSessionID);
3050
3051 int rc = RTCritSectEnter(&pSession->CritSect);
3052 if (RT_SUCCESS(rc))
3053 {
3054 /*
3055 * Close all guest processes.
3056 */
3057 VGSvcVerbose(0, "Stopping all guest processes ...\n");
3058
3059 /* Signal all guest processes in the active list that we want to shutdown. */
3060 PVBOXSERVICECTRLPROCESS pProcess;
3061 RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
3062 VGSvcGstCtrlProcessStop(pProcess);
3063
3064 VGSvcVerbose(1, "%RU32 guest processes were signalled to stop\n", pSession->cProcesses);
3065
3066 /* Wait for all active threads to shutdown and destroy the active thread list. */
3067 PVBOXSERVICECTRLPROCESS pProcessNext;
3068 RTListForEachSafe(&pSession->lstProcesses, pProcess, pProcessNext, VBOXSERVICECTRLPROCESS, Node)
3069 {
3070 int rc3 = RTCritSectLeave(&pSession->CritSect);
3071 AssertRC(rc3);
3072
3073 int rc2 = VGSvcGstCtrlProcessWait(pProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
3074
3075 rc3 = RTCritSectEnter(&pSession->CritSect);
3076 AssertRC(rc3);
3077
3078 if (RT_SUCCESS(rc2))
3079 {
3080 rc2 = vgsvcGstCtrlSessionProcessRemoveInternal(pSession, pProcess);
3081 if (RT_SUCCESS(rc2))
3082 {
3083 VGSvcGstCtrlProcessFree(pProcess);
3084 pProcess = NULL;
3085 }
3086 }
3087 }
3088
3089 AssertMsg(pSession->cProcesses == 0,
3090 ("Session process list still contains %RU32 when it should not\n", pSession->cProcesses));
3091 AssertMsg(RTListIsEmpty(&pSession->lstProcesses),
3092 ("Session process list is not empty when it should\n"));
3093
3094 /*
3095 * Close all left guest files.
3096 */
3097 VGSvcVerbose(0, "Closing all guest files ...\n");
3098
3099 PVBOXSERVICECTRLFILE pFile, pFileNext;
3100 RTListForEachSafe(&pSession->lstFiles, pFile, pFileNext, VBOXSERVICECTRLFILE, Node)
3101 {
3102 int rc2 = vgsvcGstCtrlSessionFileFree(pFile);
3103 if (RT_FAILURE(rc2))
3104 {
3105 VGSvcError("Unable to close file '%s'; rc=%Rrc\n", pFile->pszName, rc2);
3106 if (RT_SUCCESS(rc))
3107 rc = rc2;
3108 /* Keep going. */
3109 }
3110
3111 pFile = NULL; /* To make it obvious. */
3112 }
3113
3114#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
3115 AssertMsg(pSession->cDirs == 0,
3116 ("Session directory list still contains %RU32 when it should not\n", pSession->cDirs));
3117 AssertMsg(RTListIsEmpty(&pSession->lstDirs),
3118 ("Session directory list is not empty when it should\n"));
3119#endif
3120 AssertMsg(pSession->cFiles == 0,
3121 ("Session file list still contains %RU32 when it should not\n", pSession->cFiles));
3122 AssertMsg(RTListIsEmpty(&pSession->lstFiles),
3123 ("Session file list is not empty when it should\n"));
3124
3125 int rc2 = RTCritSectLeave(&pSession->CritSect);
3126 if (RT_SUCCESS(rc))
3127 rc = rc2;
3128 }
3129
3130 return rc;
3131}
3132
3133
3134int VGSvcGstCtrlSessionDestroy(PVBOXSERVICECTRLSESSION pSession)
3135{
3136 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3137
3138 int rc = VGSvcGstCtrlSessionClose(pSession);
3139
3140 /* Destroy critical section. */
3141 RTCritSectDelete(&pSession->CritSect);
3142
3143 return rc;
3144}
3145
3146
3147int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t fFlags)
3148{
3149 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3150
3151 RTListInit(&pSession->lstProcesses);
3152#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
3153 RTListInit(&pSession->lstDirs);
3154#endif
3155 RTListInit(&pSession->lstFiles);
3156
3157 pSession->cProcesses = 0;
3158 pSession->cFiles = 0;
3159
3160 pSession->fFlags = fFlags;
3161
3162#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
3163 RT_ZERO(pSession->UidCache);
3164 RT_ZERO(pSession->GidCache);
3165#endif
3166
3167 /* Init critical section for protecting the thread lists. */
3168 int rc = RTCritSectInit(&pSession->CritSect);
3169 AssertRC(rc);
3170
3171 return rc;
3172}
3173
3174
3175/**
3176 * Adds a guest process to a session's process list.
3177 *
3178 * @return VBox status code.
3179 * @param pSession Guest session to add process to.
3180 * @param pProcess Guest process to add.
3181 */
3182int VGSvcGstCtrlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
3183{
3184 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3185 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
3186
3187 int rc = RTCritSectEnter(&pSession->CritSect);
3188 if (RT_SUCCESS(rc))
3189 {
3190 VGSvcVerbose(3, "Adding process (PID %RU32) to session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
3191
3192 /* Add process to session list. */
3193 RTListAppend(&pSession->lstProcesses, &pProcess->Node);
3194
3195 pSession->cProcesses++;
3196 VGSvcVerbose(3, "Now session ID=%RU32 has %RU32 processes total\n",
3197 pSession->StartupInfo.uSessionID, pSession->cProcesses);
3198
3199 int rc2 = RTCritSectLeave(&pSession->CritSect);
3200 if (RT_SUCCESS(rc))
3201 rc = rc2;
3202 }
3203
3204 return VINF_SUCCESS;
3205}
3206
3207/**
3208 * Removes a guest process from a session's process list.
3209 * Internal version, does not do locking.
3210 *
3211 * @return VBox status code.
3212 * @param pSession Guest session to remove process from.
3213 * @param pProcess Guest process to remove.
3214 */
3215static int vgsvcGstCtrlSessionProcessRemoveInternal(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
3216{
3217 VGSvcVerbose(3, "Removing process (PID %RU32) from session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
3218 AssertReturn(pProcess->cRefs == 0, VERR_WRONG_ORDER);
3219
3220 RTListNodeRemove(&pProcess->Node);
3221
3222 AssertReturn(pSession->cProcesses, VERR_WRONG_ORDER);
3223 pSession->cProcesses--;
3224 VGSvcVerbose(3, "Now session ID=%RU32 has %RU32 processes total\n",
3225 pSession->StartupInfo.uSessionID, pSession->cProcesses);
3226
3227 return VINF_SUCCESS;
3228}
3229
3230/**
3231 * Removes a guest process from a session's process list.
3232 *
3233 * @return VBox status code.
3234 * @param pSession Guest session to remove process from.
3235 * @param pProcess Guest process to remove.
3236 */
3237int VGSvcGstCtrlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
3238{
3239 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3240 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
3241
3242 int rc = RTCritSectEnter(&pSession->CritSect);
3243 if (RT_SUCCESS(rc))
3244 {
3245 rc = vgsvcGstCtrlSessionProcessRemoveInternal(pSession, pProcess);
3246
3247 int rc2 = RTCritSectLeave(&pSession->CritSect);
3248 if (RT_SUCCESS(rc))
3249 rc = rc2;
3250 }
3251
3252 return rc;
3253}
3254
3255
3256/**
3257 * Determines whether starting a new guest process according to the
3258 * maximum number of concurrent guest processes defined is allowed or not.
3259 *
3260 * @return VBox status code.
3261 * @param pSession The guest session.
3262 * @param pfAllowed \c True if starting (another) guest process
3263 * is allowed, \c false if not.
3264 */
3265int VGSvcGstCtrlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pfAllowed)
3266{
3267 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3268 AssertPtrReturn(pfAllowed, VERR_INVALID_POINTER);
3269
3270 int rc = RTCritSectEnter(&pSession->CritSect);
3271 if (RT_SUCCESS(rc))
3272 {
3273 /*
3274 * Check if we're respecting our memory policy by checking
3275 * how many guest processes are started and served already.
3276 */
3277 bool fLimitReached = false;
3278 if (pSession->uProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
3279 {
3280 VGSvcVerbose(3, "Maximum kept guest processes set to %RU32, acurrent=%RU32\n",
3281 pSession->uProcsMaxKept, pSession->cProcesses);
3282
3283 int32_t iProcsLeft = (pSession->uProcsMaxKept - pSession->cProcesses - 1);
3284 if (iProcsLeft < 0)
3285 {
3286 VGSvcVerbose(3, "Maximum running guest processes reached (%RU32)\n", pSession->uProcsMaxKept);
3287 fLimitReached = true;
3288 }
3289 }
3290
3291 *pfAllowed = !fLimitReached;
3292
3293 int rc2 = RTCritSectLeave(&pSession->CritSect);
3294 if (RT_SUCCESS(rc))
3295 rc = rc2;
3296 }
3297
3298 return rc;
3299}
3300
3301
3302/**
3303 * Cleans up stopped and no longer used processes.
3304 *
3305 * This will free and remove processes from the session's process list.
3306 *
3307 * @returns VBox status code.
3308 * @param pSession Session to clean up processes for.
3309 */
3310static int vgsvcGstCtrlSessionCleanupProcesses(const PVBOXSERVICECTRLSESSION pSession)
3311{
3312 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
3313
3314 VGSvcVerbose(3, "Cleaning up stopped processes for session %RU32 ...\n", pSession->StartupInfo.uSessionID);
3315
3316 int rc2 = RTCritSectEnter(&pSession->CritSect);
3317 AssertRC(rc2);
3318
3319 int rc = VINF_SUCCESS;
3320
3321 PVBOXSERVICECTRLPROCESS pCurProcess, pNextProcess;
3322 RTListForEachSafe(&pSession->lstProcesses, pCurProcess, pNextProcess, VBOXSERVICECTRLPROCESS, Node)
3323 {
3324 if (ASMAtomicReadBool(&pCurProcess->fStopped))
3325 {
3326 rc2 = RTCritSectLeave(&pSession->CritSect);
3327 AssertRC(rc2);
3328
3329 rc = VGSvcGstCtrlProcessWait(pCurProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
3330 if (RT_SUCCESS(rc))
3331 {
3332 VGSvcGstCtrlSessionProcessRemove(pSession, pCurProcess);
3333 VGSvcGstCtrlProcessFree(pCurProcess);
3334 }
3335
3336 rc2 = RTCritSectEnter(&pSession->CritSect);
3337 AssertRC(rc2);
3338
3339 /* If failed, try next time we're being called. */
3340 }
3341 }
3342
3343 rc2 = RTCritSectLeave(&pSession->CritSect);
3344 AssertRC(rc2);
3345
3346 if (RT_FAILURE(rc))
3347 VGSvcError("Cleaning up stopped processes for session %RU32 failed with %Rrc\n", pSession->StartupInfo.uSessionID, rc);
3348
3349 return rc;
3350}
3351
3352
3353/**
3354 * Creates the process for a guest session.
3355 *
3356 * @return VBox status code.
3357 * @param pSessionStartupInfo Session startup info.
3358 * @param pSessionThread The session thread under construction.
3359 * @param uCtrlSessionThread The session thread debug ordinal.
3360 */
3361static int vgsvcVGSvcGstCtrlSessionThreadCreateProcess(const PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pSessionStartupInfo,
3362 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread, uint32_t uCtrlSessionThread)
3363{
3364 RT_NOREF(uCtrlSessionThread);
3365
3366 /*
3367 * Is this an anonymous session? Anonymous sessions run with the same
3368 * privileges as the main VBoxService executable.
3369 */
3370 bool const fAnonymous = pSessionThread->pStartupInfo->pszUser
3371 && pSessionThread->pStartupInfo->pszUser[0] == '\0';
3372 if (fAnonymous)
3373 {
3374 Assert(!strlen(pSessionThread->pStartupInfo->pszPassword));
3375 Assert(!strlen(pSessionThread->pStartupInfo->pszDomain));
3376
3377 VGSvcVerbose(3, "New anonymous guest session ID=%RU32 created, fFlags=%x, using protocol %RU32\n",
3378 pSessionStartupInfo->uSessionID,
3379 pSessionStartupInfo->fFlags,
3380 pSessionStartupInfo->uProtocol);
3381 }
3382 else
3383 {
3384 VGSvcVerbose(3, "Spawning new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, fFlags=%x, using protocol %RU32\n",
3385 pSessionStartupInfo->uSessionID,
3386 pSessionStartupInfo->pszUser,
3387#ifdef DEBUG
3388 pSessionStartupInfo->pszPassword,
3389#else
3390 "XXX", /* Never show passwords in release mode. */
3391#endif
3392 pSessionStartupInfo->pszDomain,
3393 pSessionStartupInfo->fFlags,
3394 pSessionStartupInfo->uProtocol);
3395 }
3396
3397 /*
3398 * Spawn a child process for doing the actual session handling.
3399 * Start by assembling the argument list.
3400 */
3401 char szExeName[RTPATH_MAX];
3402 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
3403 AssertPtrReturn(pszExeName, VERR_FILENAME_TOO_LONG);
3404
3405 char szParmSessionID[32];
3406 RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", pSessionThread->pStartupInfo->uSessionID);
3407
3408 char szParmSessionProto[32];
3409 RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
3410 pSessionThread->pStartupInfo->uProtocol);
3411#ifdef DEBUG
3412 char szParmThreadId[32];
3413 RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", uCtrlSessionThread);
3414#endif
3415 unsigned idxArg = 0; /* Next index in argument vector. */
3416 char const *apszArgs[24];
3417
3418 apszArgs[idxArg++] = pszExeName;
3419#ifdef VBOXSERVICE_ARG1_UTF8_ARGV
3420 apszArgs[idxArg++] = VBOXSERVICE_ARG1_UTF8_ARGV; Assert(idxArg == 2);
3421#endif
3422 apszArgs[idxArg++] = "guestsession";
3423 apszArgs[idxArg++] = szParmSessionID;
3424 apszArgs[idxArg++] = szParmSessionProto;
3425#ifdef DEBUG
3426 apszArgs[idxArg++] = szParmThreadId;
3427#endif
3428 if (!fAnonymous) /* Do we need to pass a user name? */
3429 {
3430 apszArgs[idxArg++] = "--user";
3431 apszArgs[idxArg++] = pSessionThread->pStartupInfo->pszUser;
3432
3433 if (strlen(pSessionThread->pStartupInfo->pszDomain))
3434 {
3435 apszArgs[idxArg++] = "--domain";
3436 apszArgs[idxArg++] = pSessionThread->pStartupInfo->pszDomain;
3437 }
3438 }
3439
3440 /* Add same verbose flags as parent process. */
3441 char szParmVerbose[32];
3442 if (g_cVerbosity > 0)
3443 {
3444 unsigned cVs = RT_MIN(g_cVerbosity, RT_ELEMENTS(szParmVerbose) - 2);
3445 szParmVerbose[0] = '-';
3446 memset(&szParmVerbose[1], 'v', cVs);
3447 szParmVerbose[1 + cVs] = '\0';
3448 apszArgs[idxArg++] = szParmVerbose;
3449 }
3450
3451 /* Add log file handling. Each session will have an own
3452 * log file, naming based on the parent log file. */
3453 char szParmLogFile[sizeof(g_szLogFile) + 128];
3454 if (g_szLogFile[0])
3455 {
3456 const char *pszSuffix = RTPathSuffix(g_szLogFile);
3457 if (!pszSuffix)
3458 pszSuffix = strchr(g_szLogFile, '\0');
3459 size_t cchBase = pszSuffix - g_szLogFile;
3460
3461 RTTIMESPEC Now;
3462 RTTimeNow(&Now);
3463 char szTime[64];
3464 RTTimeSpecToString(&Now, szTime, sizeof(szTime));
3465
3466 /* Replace out characters not allowed on Windows platforms, put in by RTTimeSpecToString(). */
3467 static const RTUNICP s_uszValidRangePairs[] =
3468 {
3469 ' ', ' ',
3470 '(', ')',
3471 '-', '.',
3472 '0', '9',
3473 'A', 'Z',
3474 'a', 'z',
3475 '_', '_',
3476 0xa0, 0xd7af,
3477 '\0'
3478 };
3479 ssize_t cReplaced = RTStrPurgeComplementSet(szTime, s_uszValidRangePairs, '_' /* chReplacement */);
3480 AssertReturn(cReplaced, VERR_INVALID_UTF8_ENCODING);
3481
3482#ifndef DEBUG
3483 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%s-%s%s",
3484 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, pSessionStartupInfo->pszUser, szTime, pszSuffix);
3485#else
3486 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%RU32-%s-%s%s",
3487 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, uCtrlSessionThread,
3488 pSessionStartupInfo->pszUser, szTime, pszSuffix);
3489#endif
3490 apszArgs[idxArg++] = "--logfile";
3491 apszArgs[idxArg++] = szParmLogFile;
3492 }
3493
3494#ifdef DEBUG
3495 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT)
3496 apszArgs[idxArg++] = "--dump-stdout";
3497 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR)
3498 apszArgs[idxArg++] = "--dump-stderr";
3499#endif
3500 apszArgs[idxArg] = NULL;
3501 Assert(idxArg < RT_ELEMENTS(apszArgs));
3502
3503 if (g_cVerbosity > 3)
3504 {
3505 VGSvcVerbose(4, "Spawning parameters:\n");
3506 for (idxArg = 0; apszArgs[idxArg]; idxArg++)
3507 VGSvcVerbose(4, " %s\n", apszArgs[idxArg]);
3508 }
3509
3510 /*
3511 * Flags.
3512 */
3513 uint32_t const fProcCreate = RTPROC_FLAGS_PROFILE
3514#ifdef RT_OS_WINDOWS
3515 | RTPROC_FLAGS_SERVICE
3516 | RTPROC_FLAGS_HIDDEN
3517#endif
3518 | VBOXSERVICE_PROC_F_UTF8_ARGV;
3519
3520 /*
3521 * Configure standard handles.
3522 */
3523 RTHANDLE hStdIn;
3524 int rc = RTPipeCreate(&hStdIn.u.hPipe, &pSessionThread->hKeyPipe, RTPIPE_C_INHERIT_READ);
3525 if (RT_SUCCESS(rc))
3526 {
3527 hStdIn.enmType = RTHANDLETYPE_PIPE;
3528
3529 RTHANDLE hStdOutAndErr;
3530 rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
3531 if (RT_SUCCESS(rc))
3532 {
3533 hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
3534
3535 /*
3536 * Windows: If a domain name is given, construct an UPN (User Principle Name)
3537 * with the domain name built-in, e.g. "joedoe@example.com".
3538 */
3539 const char *pszUser = pSessionThread->pStartupInfo->pszUser;
3540#ifdef RT_OS_WINDOWS
3541 char *pszUserUPN = NULL;
3542 if (pSessionThread->pStartupInfo->pszDomain[0])
3543 {
3544 int cchbUserUPN = RTStrAPrintf(&pszUserUPN, "%s@%s",
3545 pSessionThread->pStartupInfo->pszUser,
3546 pSessionThread->pStartupInfo->pszDomain);
3547 if (cchbUserUPN > 0)
3548 {
3549 pszUser = pszUserUPN;
3550 VGSvcVerbose(3, "Using UPN: %s\n", pszUserUPN);
3551 }
3552 else
3553 rc = VERR_NO_STR_MEMORY;
3554 }
3555 if (RT_SUCCESS(rc))
3556#endif
3557 {
3558 /*
3559 * Finally, create the process.
3560 */
3561 rc = RTProcCreateEx(pszExeName, apszArgs, RTENV_DEFAULT, fProcCreate,
3562 &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
3563 !fAnonymous ? pszUser : NULL,
3564 !fAnonymous ? pSessionThread->pStartupInfo->pszPassword : NULL,
3565 NULL /*pvExtraData*/,
3566 &pSessionThread->hProcess);
3567 }
3568#ifdef RT_OS_WINDOWS
3569 RTStrFree(pszUserUPN);
3570#endif
3571 RTFileClose(hStdOutAndErr.u.hFile);
3572 }
3573
3574 RTPipeClose(hStdIn.u.hPipe);
3575 }
3576 return rc;
3577}
3578
3579
3580/**
3581 * Creates a guest session.
3582 *
3583 * This will spawn a new VBoxService.exe instance under behalf of the given user
3584 * which then will act as a session host. On successful open, the session will
3585 * be added to the given session thread list.
3586 *
3587 * @return VBox status code.
3588 * @param pList Which list to use to store the session thread in.
3589 * @param pSessionStartupInfo Session startup info.
3590 * @param ppSessionThread Returns newly created session thread on success.
3591 * Optional.
3592 */
3593int VGSvcGstCtrlSessionThreadCreate(PRTLISTANCHOR pList, const PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pSessionStartupInfo,
3594 PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
3595{
3596 AssertPtrReturn(pList, VERR_INVALID_POINTER);
3597 AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
3598 /* ppSessionThread is optional. */
3599
3600#ifdef VBOX_STRICT
3601 /* Check for existing session in debug mode. Should never happen because of
3602 * Main consistency. */
3603 PVBOXSERVICECTRLSESSIONTHREAD pSessionCur;
3604 RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node)
3605 {
3606 AssertMsgReturn( pSessionCur->fStopped == true
3607 || pSessionCur->pStartupInfo->uSessionID != pSessionStartupInfo->uSessionID,
3608 ("Guest session thread ID=%RU32 already exists (fStopped=%RTbool)\n",
3609 pSessionCur->pStartupInfo->uSessionID, pSessionCur->fStopped), VERR_ALREADY_EXISTS);
3610 }
3611#endif
3612
3613 /* Static counter to help tracking session thread <-> process relations. */
3614 static uint32_t s_uCtrlSessionThread = 0;
3615
3616 /*
3617 * Allocate and initialize the session thread structure.
3618 */
3619 int rc;
3620 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread = (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(*pSessionThread));
3621 if (pSessionThread)
3622 {
3623 //pSessionThread->fShutdown = false;
3624 //pSessionThread->fStarted = false;
3625 //pSessionThread->fStopped = false;
3626 pSessionThread->hKeyPipe = NIL_RTPIPE;
3627 pSessionThread->Thread = NIL_RTTHREAD;
3628 pSessionThread->hProcess = NIL_RTPROCESS;
3629
3630 /* Duplicate startup info. */
3631 pSessionThread->pStartupInfo = VbglR3GuestCtrlSessionStartupInfoDup(pSessionStartupInfo);
3632 AssertPtrReturn(pSessionThread->pStartupInfo, VERR_NO_MEMORY);
3633
3634 /* Generate the secret key. */
3635 RTRandBytes(pSessionThread->abKey, sizeof(pSessionThread->abKey));
3636
3637 rc = RTCritSectInit(&pSessionThread->CritSect);
3638 AssertRC(rc);
3639 if (RT_SUCCESS(rc))
3640 {
3641 /*
3642 * Give the session key to the host so it can validate the client.
3643 */
3644 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
3645 {
3646 for (uint32_t i = 0; i < 10; i++)
3647 {
3648 rc = VbglR3GuestCtrlSessionPrepare(g_idControlSvcClient, pSessionStartupInfo->uSessionID,
3649 pSessionThread->abKey, sizeof(pSessionThread->abKey));
3650 if (rc != VERR_OUT_OF_RESOURCES)
3651 break;
3652 RTThreadSleep(100);
3653 }
3654 }
3655 if (RT_SUCCESS(rc))
3656 {
3657 s_uCtrlSessionThread++;
3658
3659 /*
3660 * Start the session child process.
3661 */
3662 rc = vgsvcVGSvcGstCtrlSessionThreadCreateProcess(pSessionStartupInfo, pSessionThread, s_uCtrlSessionThread);
3663 if (RT_SUCCESS(rc))
3664 {
3665 /*
3666 * Start the session thread.
3667 */
3668 rc = RTThreadCreateF(&pSessionThread->Thread, vgsvcGstCtrlSessionThread, pSessionThread /*pvUser*/, 0 /*cbStack*/,
3669 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctls%RU32", s_uCtrlSessionThread);
3670 if (RT_SUCCESS(rc))
3671 {
3672 /* Wait for the thread to initialize. */
3673 rc = RTThreadUserWait(pSessionThread->Thread, RT_MS_1MIN);
3674 if ( RT_SUCCESS(rc)
3675 && !ASMAtomicReadBool(&pSessionThread->fShutdown))
3676 {
3677 VGSvcVerbose(2, "Thread for session ID=%RU32 started\n", pSessionThread->pStartupInfo->uSessionID);
3678
3679 ASMAtomicXchgBool(&pSessionThread->fStarted, true);
3680
3681 /* Add session to list. */
3682 RTListAppend(pList, &pSessionThread->Node);
3683 if (ppSessionThread) /* Return session if wanted. */
3684 *ppSessionThread = pSessionThread;
3685 return VINF_SUCCESS;
3686 }
3687
3688 /*
3689 * Bail out.
3690 */
3691 VGSvcError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
3692 pSessionThread->pStartupInfo->uSessionID, rc);
3693 if (RT_SUCCESS_NP(rc))
3694 rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
3695 }
3696 else
3697 VGSvcError("Creating session thread failed, rc=%Rrc\n", rc);
3698
3699 RTProcTerminate(pSessionThread->hProcess);
3700 uint32_t cMsWait = 1;
3701 while ( RTProcWait(pSessionThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL) == VERR_PROCESS_RUNNING
3702 && cMsWait <= 9) /* 1023 ms */
3703 {
3704 RTThreadSleep(cMsWait);
3705 cMsWait <<= 1;
3706 }
3707 }
3708
3709 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
3710 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, pSessionStartupInfo->uSessionID);
3711 }
3712 else
3713 VGSvcVerbose(3, "VbglR3GuestCtrlSessionPrepare failed: %Rrc\n", rc);
3714 RTPipeClose(pSessionThread->hKeyPipe);
3715 pSessionThread->hKeyPipe = NIL_RTPIPE;
3716 RTCritSectDelete(&pSessionThread->CritSect);
3717 }
3718 RTMemFree(pSessionThread);
3719 }
3720 else
3721 rc = VERR_NO_MEMORY;
3722
3723 VGSvcVerbose(3, "Spawning session thread returned returned rc=%Rrc\n", rc);
3724 return rc;
3725}
3726
3727
3728/**
3729 * Waits for a formerly opened guest session process to close.
3730 *
3731 * @return VBox status code.
3732 * @param pThread Guest session thread to wait for.
3733 * @param uTimeoutMS Waiting timeout (in ms).
3734 * @param fFlags Closing flags.
3735 */
3736static int VGSvcGstCtrlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uTimeoutMS, uint32_t fFlags)
3737{
3738 RT_NOREF(fFlags);
3739 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
3740 /** @todo Validate closing flags. */
3741
3742 AssertMsgReturn(pThread->Thread != NIL_RTTHREAD,
3743 ("Guest session thread of session %p does not exist when it should\n", pThread),
3744 VERR_NOT_FOUND);
3745
3746 int rc = VINF_SUCCESS;
3747
3748 /*
3749 * The spawned session process should have received the same closing request,
3750 * so just wait for the process to close.
3751 */
3752 if (ASMAtomicReadBool(&pThread->fStarted))
3753 {
3754 /* Ask the thread to shutdown. */
3755 ASMAtomicXchgBool(&pThread->fShutdown, true);
3756
3757 VGSvcVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
3758 pThread->pStartupInfo->uSessionID, uTimeoutMS);
3759
3760 int rcThread;
3761 rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
3762 if (RT_SUCCESS(rc))
3763 {
3764 AssertMsg(pThread->fStopped, ("Thread of session ID=%RU32 not in stopped state when it should\n",
3765 pThread->pStartupInfo->uSessionID));
3766
3767 VGSvcVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n", pThread->pStartupInfo->uSessionID, rcThread);
3768 }
3769 else
3770 VGSvcError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n", pThread->pStartupInfo->uSessionID, rc);
3771 }
3772 else
3773 VGSvcVerbose(3, "Thread for session ID=%RU32 not in started state, skipping wait\n", pThread->pStartupInfo->uSessionID);
3774
3775 LogFlowFuncLeaveRC(rc);
3776 return rc;
3777}
3778
3779/**
3780 * Waits for the specified session thread to end and remove
3781 * it from the session thread list.
3782 *
3783 * @return VBox status code.
3784 * @param pThread Session thread to destroy.
3785 * @param fFlags Closing flags.
3786 */
3787int VGSvcGstCtrlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t fFlags)
3788{
3789 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
3790 AssertPtrReturn(pThread->pStartupInfo, VERR_WRONG_ORDER);
3791
3792 const uint32_t uSessionID = pThread->pStartupInfo->uSessionID;
3793
3794 VGSvcVerbose(3, "Destroying session ID=%RU32 ...\n", uSessionID);
3795
3796 int rc = VGSvcGstCtrlSessionThreadWait(pThread, 5 * 60 * 1000 /* 5 minutes timeout */, fFlags);
3797 if (RT_SUCCESS(rc))
3798 {
3799 VbglR3GuestCtrlSessionStartupInfoFree(pThread->pStartupInfo);
3800 pThread->pStartupInfo = NULL;
3801
3802 RTPipeClose(pThread->hKeyPipe);
3803 pThread->hKeyPipe = NIL_RTPIPE;
3804
3805 RTCritSectDelete(&pThread->CritSect);
3806
3807 /* Remove session from list and destroy object. */
3808 RTListNodeRemove(&pThread->Node);
3809
3810 RTMemFree(pThread);
3811 pThread = NULL;
3812 }
3813
3814 VGSvcVerbose(3, "Destroyed session ID=%RU32 with %Rrc\n", uSessionID, rc);
3815 return rc;
3816}
3817
3818/**
3819 * Close all open guest session threads.
3820 *
3821 * @note Caller is responsible for locking!
3822 *
3823 * @return VBox status code.
3824 * @param pList Which list to close the session threads for.
3825 * @param fFlags Closing flags.
3826 */
3827int VGSvcGstCtrlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t fFlags)
3828{
3829 AssertPtrReturn(pList, VERR_INVALID_POINTER);
3830
3831 int rc = VINF_SUCCESS;
3832
3833 /*int rc = VbglR3GuestCtrlClose
3834 if (RT_FAILURE(rc))
3835 VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/
3836
3837 PVBOXSERVICECTRLSESSIONTHREAD pSessIt;
3838 PVBOXSERVICECTRLSESSIONTHREAD pSessItNext;
3839 RTListForEachSafe(pList, pSessIt, pSessItNext, VBOXSERVICECTRLSESSIONTHREAD, Node)
3840 {
3841 int rc2 = VGSvcGstCtrlSessionThreadDestroy(pSessIt, fFlags);
3842 if (RT_FAILURE(rc2))
3843 {
3844 VGSvcError("Closing session thread '%s' failed with rc=%Rrc\n", RTThreadGetName(pSessIt->Thread), rc2);
3845 if (RT_SUCCESS(rc))
3846 rc = rc2;
3847 /* Keep going. */
3848 }
3849 }
3850
3851 VGSvcVerbose(4, "Destroying guest session threads ended with %Rrc\n", rc);
3852 return rc;
3853}
3854
3855
3856/**
3857 * Main function for the session process.
3858 *
3859 * @returns exit code.
3860 * @param argc Argument count.
3861 * @param argv Argument vector (UTF-8).
3862 */
3863RTEXITCODE VGSvcGstCtrlSessionSpawnInit(int argc, char **argv)
3864{
3865 static const RTGETOPTDEF s_aOptions[] =
3866 {
3867 { "--domain", VBOXSERVICESESSIONOPT_DOMAIN, RTGETOPT_REQ_STRING },
3868#ifdef DEBUG
3869 { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING },
3870 { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING },
3871#endif
3872 { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
3873 { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
3874 { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
3875 { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
3876#ifdef DEBUG
3877 { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 },
3878#endif /* DEBUG */
3879 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
3880 };
3881
3882 RTGETOPTSTATE GetState;
3883 RTGetOptInit(&GetState, argc, argv,
3884 s_aOptions, RT_ELEMENTS(s_aOptions),
3885 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3886
3887 uint32_t fSession = VBOXSERVICECTRLSESSION_FLAG_SPAWN;
3888
3889 /* Protocol and session ID must be specified explicitly. */
3890 g_Session.StartupInfo.uProtocol = UINT32_MAX;
3891 g_Session.StartupInfo.uSessionID = UINT32_MAX;
3892
3893 int ch;
3894 RTGETOPTUNION ValueUnion;
3895 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
3896 {
3897 /* For options that require an argument, ValueUnion has received the value. */
3898 switch (ch)
3899 {
3900 case VBOXSERVICESESSIONOPT_DOMAIN:
3901 /* Information not needed right now, skip. */
3902 break;
3903#ifdef DEBUG
3904 case VBOXSERVICESESSIONOPT_DUMP_STDOUT:
3905 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
3906 break;
3907
3908 case VBOXSERVICESESSIONOPT_DUMP_STDERR:
3909 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
3910 break;
3911#endif
3912 case VBOXSERVICESESSIONOPT_SESSION_ID:
3913 g_Session.StartupInfo.uSessionID = ValueUnion.u32;
3914 break;
3915
3916 case VBOXSERVICESESSIONOPT_SESSION_PROTO:
3917 g_Session.StartupInfo.uProtocol = ValueUnion.u32;
3918 break;
3919#ifdef DEBUG
3920 case VBOXSERVICESESSIONOPT_THREAD_ID:
3921 /* Not handled. Mainly for processs listing. */
3922 break;
3923#endif
3924 case VBOXSERVICESESSIONOPT_LOG_FILE:
3925 {
3926 int rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
3927 if (RT_FAILURE(rc))
3928 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error copying log file name: %Rrc", rc);
3929 break;
3930 }
3931
3932 case VBOXSERVICESESSIONOPT_USERNAME:
3933 /* Information not needed right now, skip. */
3934 break;
3935
3936 /** @todo Implement help? */
3937
3938 case 'v':
3939 g_cVerbosity++;
3940 break;
3941
3942 case VINF_GETOPT_NOT_OPTION:
3943 {
3944 if (!RTStrICmp(ValueUnion.psz, VBOXSERVICECTRLSESSION_GETOPT_PREFIX))
3945 break;
3946 /* else fall through and bail out. */
3947 RT_FALL_THROUGH();
3948 }
3949 default:
3950 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'", ValueUnion.psz);
3951 }
3952 }
3953
3954 /* Check that we've got all the required options. */
3955 if (g_Session.StartupInfo.uProtocol == UINT32_MAX)
3956 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified");
3957
3958 if (g_Session.StartupInfo.uSessionID == UINT32_MAX)
3959 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
3960
3961 /* Init the session object. */
3962 int rc = VGSvcGstCtrlSessionInit(&g_Session, fSession);
3963 if (RT_FAILURE(rc))
3964 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to initialize session object, rc=%Rrc\n", rc);
3965
3966 rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
3967 if (RT_FAILURE(rc))
3968 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to create log file '%s', rc=%Rrc\n",
3969 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
3970
3971 RTEXITCODE rcExit = vgsvcGstCtrlSessionSpawnWorker(&g_Session);
3972
3973 VGSvcLogDestroy();
3974 return rcExit;
3975}
3976
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