VirtualBox

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

Last change on this file since 98709 was 98709, checked in by vboxsync, 22 months ago

Guest Control: Implemented directory handling / walking as non-toolbox variants. bugref:9783

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