VirtualBox

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

Last change on this file since 92666 was 92662, checked in by vboxsync, 3 years ago

VBoxService: Sketch for a trick for passing UTF-8 argv to child VBoxServices process - disabled and untested. bugref:10153

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