VirtualBox

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

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

Guest Control/VBoxService: Also made attribute allocation of VBOXSERVICECTRLSESSIONSTARTUPINFO + VBOXSERVICECTRLPROCSTARTUPINFO dynamic. [build fix] bugref:9320

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