VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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