VirtualBox

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

Last change on this file since 99082 was 98831, checked in by vboxsync, 2 years ago

Guest Control: Made the directory entry reading really dynamic by letting the guest tell us the size it reports to the guest (limited by GSTCTL_DIRENTRY_MAX_SIZE). Re-introduced the #pragma pack(1) because we need the structures on mixed bitness (32 / 64 or vice versa) with the same size [build fix]. bugref:9783

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