VirtualBox

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

Last change on this file since 78111 was 78111, checked in by vboxsync, 6 years ago

Guest Control/VBoxService: Added support for deleting empty directory structures only, and handle DIRREMOVEREC_FLAG_CONTENT_AND_DIR and DIRREMOVEREC_FLAG_CONTENT_ONLY separately (so that also directory content gets removed).

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