VirtualBox

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

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

Guest Control/Main: Check flags of IGuestSession::directoryRemoveRecursive() and renamed DIRREMOVE_FLAG_ flags to DIRREMOVEREC_FLAG_ to match usage.

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