VirtualBox

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

Last change on this file since 84215 was 84215, checked in by vboxsync, 5 years ago

Guest Control/VbglR3 + VBoxService: Moved code to retrieve and allocate session and process startup information into VbglR3; required as preparation dealing with longer command line arguments.

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