VirtualBox

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

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

VBoxService: Don't use tabs in logging, looks weird on linux console.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.2 KB
Line 
1/* $Id: VBoxServiceControlSession.cpp 75890 2018-12-03 11:06:24Z vboxsync $ */
2/** @file
3 * VBoxServiceControlSession - Guest session handling. Also handles the spawned session processes.
4 */
5
6/*
7 * Copyright (C) 2013-2018 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 & ~DIRREMOVE_FLAG_VALID_MASK))
140 {
141 if (fFlags & DIRREMOVE_FLAG_RECURSIVE)
142 {
143 uint32_t fFlagsRemRec = RTDIRRMREC_F_CONTENT_AND_DIR; /* Set default. */
144 if (fFlags & DIRREMOVE_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 & ~DIRREMOVE_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 command 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_SESSION_CLOSE:
1132 /* Shutdown (this spawn). */
1133 rc = VGSvcGstCtrlSessionClose(pSession);
1134 *pfShutdown = true; /* Shutdown in any case. */
1135 break;
1136
1137 case HOST_DIR_REMOVE:
1138 if (fImpersonated)
1139 rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx);
1140 break;
1141
1142 case HOST_EXEC_CMD:
1143 rc = vgsvcGstCtrlSessionHandleProcExec(pSession, pHostCtx);
1144 break;
1145
1146 case HOST_EXEC_SET_INPUT:
1147 rc = vgsvcGstCtrlSessionHandleProcInput(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1148 break;
1149
1150 case HOST_EXEC_GET_OUTPUT:
1151 rc = vgsvcGstCtrlSessionHandleProcOutput(pSession, pHostCtx);
1152 break;
1153
1154 case HOST_EXEC_TERMINATE:
1155 rc = vgsvcGstCtrlSessionHandleProcTerminate(pSession, pHostCtx);
1156 break;
1157
1158 case HOST_EXEC_WAIT_FOR:
1159 rc = vgsvcGstCtrlSessionHandleProcWaitFor(pSession, pHostCtx);
1160 break;
1161
1162 case HOST_FILE_OPEN:
1163 if (fImpersonated)
1164 rc = vgsvcGstCtrlSessionHandleFileOpen(pSession, pHostCtx);
1165 break;
1166
1167 case HOST_FILE_CLOSE:
1168 if (fImpersonated)
1169 rc = vgsvcGstCtrlSessionHandleFileClose(pSession, pHostCtx);
1170 break;
1171
1172 case HOST_FILE_READ:
1173 if (fImpersonated)
1174 rc = vgsvcGstCtrlSessionHandleFileRead(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1175 break;
1176
1177 case HOST_FILE_READ_AT:
1178 if (fImpersonated)
1179 rc = vgsvcGstCtrlSessionHandleFileReadAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1180 break;
1181
1182 case HOST_FILE_WRITE:
1183 if (fImpersonated)
1184 rc = vgsvcGstCtrlSessionHandleFileWrite(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1185 break;
1186
1187 case HOST_FILE_WRITE_AT:
1188 if (fImpersonated)
1189 rc = vgsvcGstCtrlSessionHandleFileWriteAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1190 break;
1191
1192 case HOST_FILE_SEEK:
1193 if (fImpersonated)
1194 rc = vgsvcGstCtrlSessionHandleFileSeek(pSession, pHostCtx);
1195 break;
1196
1197 case HOST_FILE_TELL:
1198 if (fImpersonated)
1199 rc = vgsvcGstCtrlSessionHandleFileTell(pSession, pHostCtx);
1200 break;
1201
1202 case HOST_PATH_RENAME:
1203 if (fImpersonated)
1204 rc = vgsvcGstCtrlSessionHandlePathRename(pSession, pHostCtx);
1205 break;
1206
1207 case HOST_PATH_USER_DOCUMENTS:
1208 if (fImpersonated)
1209 rc = vgsvcGstCtrlSessionHandlePathUserDocuments(pSession, pHostCtx);
1210 break;
1211
1212 case HOST_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(3, "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 {
1232 /*
1233 * !!! HACK ALERT BEGIN !!!
1234 * As peeking for the current message by VbglR3GuestCtrlMsgWaitFor() / GUEST_MSG_WAIT only gives us the message type and
1235 * the number of parameters, but *not* the actual context ID the message is bound to, try to retrieve it here.
1236 *
1237 * This is needed in order to reply to the host with the current context ID, without breaking existing clients.
1238 * Not doing this isn't fatal, but will make host clients wait longer (timing out) for not implemented messages. */
1239 /** @todo Get rid of this as soon as we have a protocol bump (v4). */
1240 struct HGCMMsgSkip
1241 {
1242 VBGLIOCHGCMCALL hdr;
1243 /** UInt32: Context ID. */
1244 HGCMFunctionParameter context;
1245 };
1246
1247 HGCMMsgSkip Msg;
1248 VBGL_HGCM_HDR_INIT(&Msg.hdr, pHostCtx->uClientID, GUEST_MSG_WAIT, pHostCtx->uNumParms);
1249 Msg.context.SetUInt32(0);
1250
1251 /* Retrieve the context ID of the message which is not supported and put it in pHostCtx. */
1252 int rc2 = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1253 if (RT_SUCCESS(rc2))
1254 Msg.context.GetUInt32(&pHostCtx->uContextID);
1255
1256 /* Now fake a reply to the message. */
1257 rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, VERR_NOT_SUPPORTED);
1258 AssertRC(rc2);
1259
1260 /* !!! !!!
1261 * !!! HACK ALERT END !!!
1262 * !!! !!! */
1263
1264 /* Tell the host service to skip the message. */
1265 VbglR3GuestCtrlMsgSkipOld(pHostCtx->uClientID);
1266
1267 rc = VINF_SUCCESS;
1268 }
1269 }
1270
1271 if (RT_FAILURE(rc))
1272 VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc);
1273
1274 return rc;
1275}
1276
1277
1278/**
1279 * Thread main routine for a spawned guest session process.
1280 *
1281 * This thread runs in the main executable to control the spawned session process.
1282 *
1283 * @returns VBox status code.
1284 * @param hThreadSelf Thread handle.
1285 * @param pvUser Pointer to a VBOXSERVICECTRLSESSIONTHREAD structure.
1286 *
1287 */
1288static DECLCALLBACK(int) vgsvcGstCtrlSessionThread(RTTHREAD hThreadSelf, void *pvUser)
1289{
1290 PVBOXSERVICECTRLSESSIONTHREAD pThread = (PVBOXSERVICECTRLSESSIONTHREAD)pvUser;
1291 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1292
1293 uint32_t const idSession = pThread->StartupInfo.uSessionID;
1294 uint32_t const idClient = g_idControlSvcClient;
1295 VGSvcVerbose(3, "Session ID=%RU32 thread running\n", idSession);
1296
1297 /* Let caller know that we're done initializing, regardless of the result. */
1298 int rc2 = RTThreadUserSignal(hThreadSelf);
1299 AssertRC(rc2);
1300
1301 /*
1302 * Wait for the child process to stop or the shutdown flag to be signalled.
1303 */
1304 RTPROCSTATUS ProcessStatus = { 0, RTPROCEXITREASON_NORMAL };
1305 bool fProcessAlive = true;
1306 bool fSessionCancelled = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
1307 uint32_t cMsShutdownTimeout = 30 * 1000; /** @todo Make this configurable. Later. */
1308 uint64_t msShutdownStart = 0;
1309 uint64_t const msStart = RTTimeMilliTS();
1310 size_t offSecretKey = 0;
1311 int rcWait;
1312 for (;;)
1313 {
1314 /* Secret key feeding. */
1315 if (offSecretKey < sizeof(pThread->abKey))
1316 {
1317 size_t cbWritten = 0;
1318 rc2 = RTPipeWrite(pThread->hKeyPipe, &pThread->abKey[offSecretKey], sizeof(pThread->abKey) - offSecretKey, &cbWritten);
1319 if (RT_SUCCESS(rc2))
1320 offSecretKey += cbWritten;
1321 }
1322
1323 /* Poll child process status. */
1324 rcWait = RTProcWaitNoResume(pThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
1325 if ( rcWait == VINF_SUCCESS
1326 || rcWait == VERR_PROCESS_NOT_FOUND)
1327 {
1328 fProcessAlive = false;
1329 break;
1330 }
1331 AssertMsgBreak(rcWait == VERR_PROCESS_RUNNING || rcWait == VERR_INTERRUPTED,
1332 ("Got unexpected rc=%Rrc while waiting for session process termination\n", rcWait));
1333
1334 /* Shutting down? */
1335 if (ASMAtomicReadBool(&pThread->fShutdown))
1336 {
1337 if (!msShutdownStart)
1338 {
1339 VGSvcVerbose(3, "Notifying guest session process (PID=%RU32, session ID=%RU32) ...\n",
1340 pThread->hProcess, idSession);
1341
1342 VBGLR3GUESTCTRLCMDCTX hostCtx =
1343 {
1344 /* .idClient = */ idClient,
1345 /* .idContext = */ VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
1346 /* .uProtocol = */ pThread->StartupInfo.uProtocol,
1347 /* .cParams = */ 2
1348 };
1349 rc2 = VbglR3GuestCtrlSessionClose(&hostCtx, 0 /* fFlags */);
1350 if (RT_FAILURE(rc2))
1351 {
1352 VGSvcError("Unable to notify guest session process (PID=%RU32, session ID=%RU32), rc=%Rrc\n",
1353 pThread->hProcess, idSession, rc2);
1354
1355 if (rc2 == VERR_NOT_SUPPORTED)
1356 {
1357 /* Terminate guest session process in case it's not supported by a too old host. */
1358 rc2 = RTProcTerminate(pThread->hProcess);
1359 VGSvcVerbose(3, "Terminating guest session process (PID=%RU32) ended with rc=%Rrc\n",
1360 pThread->hProcess, rc2);
1361 }
1362 break;
1363 }
1364
1365 VGSvcVerbose(3, "Guest session ID=%RU32 thread was asked to terminate, waiting for session process to exit (%RU32 ms timeout) ...\n",
1366 idSession, cMsShutdownTimeout);
1367 msShutdownStart = RTTimeMilliTS();
1368 continue; /* Don't waste time on waiting. */
1369 }
1370 if (RTTimeMilliTS() - msShutdownStart > cMsShutdownTimeout)
1371 {
1372 VGSvcVerbose(3, "Guest session ID=%RU32 process did not shut down within time\n", idSession);
1373 break;
1374 }
1375 }
1376
1377 /* Cancel the prepared session stuff after 30 seconds. */
1378 if ( !fSessionCancelled
1379 && RTTimeMilliTS() - msStart >= 30000)
1380 {
1381 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
1382 fSessionCancelled = true;
1383 }
1384
1385/** @todo r=bird: This 100ms sleep is _extremely_ sucky! */
1386 RTThreadSleep(100); /* Wait a bit. */
1387 }
1388
1389 if (!fSessionCancelled)
1390 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
1391
1392 if (!fProcessAlive)
1393 {
1394 VGSvcVerbose(2, "Guest session process (ID=%RU32) terminated with rc=%Rrc, reason=%d, status=%d\n",
1395 idSession, rcWait, ProcessStatus.enmReason, ProcessStatus.iStatus);
1396 if (ProcessStatus.iStatus == RTEXITCODE_INIT)
1397 {
1398 VGSvcError("Guest session process (ID=%RU32) failed to initialize. Here some hints:\n", idSession);
1399 VGSvcError("- Is logging enabled and the output directory is read-only by the guest session user?\n");
1400 /** @todo Add more here. */
1401 }
1402 }
1403
1404 uint32_t uSessionStatus = GUEST_SESSION_NOTIFYTYPE_UNDEFINED;
1405 uint32_t uSessionRc = VINF_SUCCESS; /** uint32_t vs. int. */
1406
1407 if (fProcessAlive)
1408 {
1409 for (int i = 0; i < 3; i++)
1410 {
1411 VGSvcVerbose(2, "Guest session ID=%RU32 process still alive, killing attempt %d/3\n", idSession, i + 1);
1412
1413 rc2 = RTProcTerminate(pThread->hProcess);
1414 if (RT_SUCCESS(rc2))
1415 break;
1416 /** @todo r=bird: What's the point of sleeping 3 second after the last attempt? */
1417 RTThreadSleep(3000);
1418 }
1419
1420 VGSvcVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n", idSession, rc2);
1421 uSessionStatus = RT_SUCCESS(rc2) ? GUEST_SESSION_NOTIFYTYPE_TOK : GUEST_SESSION_NOTIFYTYPE_TOA;
1422 }
1423 else if (RT_SUCCESS(rcWait))
1424 {
1425 switch (ProcessStatus.enmReason)
1426 {
1427 case RTPROCEXITREASON_NORMAL:
1428 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
1429 break;
1430
1431 case RTPROCEXITREASON_ABEND:
1432 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
1433 break;
1434
1435 case RTPROCEXITREASON_SIGNAL:
1436 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
1437 break;
1438
1439 default:
1440 AssertMsgFailed(("Unhandled process termination reason (%d)\n", ProcessStatus.enmReason));
1441 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
1442 break;
1443 }
1444 }
1445 else
1446 {
1447 /* If we didn't find the guest process anymore, just assume it terminated normally. */
1448 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
1449 }
1450
1451 VGSvcVerbose(3, "Guest session ID=%RU32 thread ended with sessionStatus=%RU32, sessionRc=%Rrc\n",
1452 idSession, uSessionStatus, uSessionRc);
1453
1454 /*
1455 * Report final status.
1456 */
1457 Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED);
1458 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession) };
1459 rc2 = VbglR3GuestCtrlSessionNotify(&ctx, uSessionStatus, uSessionRc);
1460 if (RT_FAILURE(rc2))
1461 VGSvcError("Reporting session ID=%RU32 final status failed with rc=%Rrc\n", idSession, rc2);
1462
1463 VGSvcVerbose(3, "Session ID=%RU32 thread ending\n", idSession);
1464 return VINF_SUCCESS;
1465}
1466
1467/**
1468 * Reads the secret key the parent VBoxService instance passed us and pass it
1469 * along as a authentication token to the host service.
1470 *
1471 * For older hosts, this sets up the message filtering.
1472 *
1473 * @returns VBox status code.
1474 * @param idClient The HGCM client ID.
1475 * @param idSession The session ID.
1476 */
1477static int vgsvcGstCtrlSessionReadKeyAndAccept(uint32_t idClient, uint32_t idSession)
1478{
1479 /*
1480 * Read it.
1481 */
1482 RTHANDLE Handle;
1483 int rc = RTHandleGetStandard(RTHANDLESTD_INPUT, &Handle);
1484 if (RT_SUCCESS(rc))
1485 {
1486 if (Handle.enmType == RTHANDLETYPE_PIPE)
1487 {
1488 uint8_t abSecretKey[RT_SIZEOFMEMB(VBOXSERVICECTRLSESSIONTHREAD, abKey)];
1489 rc = RTPipeReadBlocking(Handle.u.hPipe, abSecretKey, sizeof(abSecretKey), NULL);
1490 if (RT_SUCCESS(rc))
1491 {
1492 VGSvcVerbose(3, "Got secret key from standard input.\n");
1493
1494 /*
1495 * Do the accepting, if appropriate.
1496 */
1497 if (g_fControlSupportsOptimizations)
1498 {
1499 rc = VbglR3GuestCtrlSessionAccept(idClient, idSession, abSecretKey, sizeof(abSecretKey));
1500 if (RT_SUCCESS(rc))
1501 VGSvcVerbose(3, "Session %u accepted (client ID %u)\n", idClient, idSession);
1502 else
1503 VGSvcError("Failed to accept session %u (client ID %u): %Rrc\n", idClient, idSession, rc);
1504 }
1505 else
1506 {
1507 /* For legacy hosts, we do the filtering thingy. */
1508 rc = VbglR3GuestCtrlMsgFilterSet(idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
1509 VBOX_GUESTCTRL_FILTER_BY_SESSION(idSession), 0);
1510 if (RT_SUCCESS(rc))
1511 VGSvcVerbose(3, "Session %u filtering successfully enabled\n", idSession);
1512 else
1513 VGSvcError("Failed to set session filter: %Rrc\n", rc);
1514 }
1515 }
1516 else
1517 VGSvcError("Error reading secret key from standard input: %Rrc\n", rc);
1518 }
1519 else
1520 {
1521 VGSvcError("Standard input is not a pipe!\n");
1522 rc = VERR_INVALID_HANDLE;
1523 }
1524 RTHandleClose(&Handle);
1525 }
1526 else
1527 VGSvcError("RTHandleGetStandard failed on standard input: %Rrc\n", rc);
1528 return rc;
1529}
1530
1531/**
1532 * Main message handler for the guest control session process.
1533 *
1534 * @returns exit code.
1535 * @param pSession Pointer to g_Session.
1536 * @thread main.
1537 */
1538static RTEXITCODE vgsvcGstCtrlSessionSpawnWorker(PVBOXSERVICECTRLSESSION pSession)
1539{
1540 AssertPtrReturn(pSession, RTEXITCODE_FAILURE);
1541 VGSvcVerbose(0, "Hi, this is guest session ID=%RU32\n", pSession->StartupInfo.uSessionID);
1542
1543 /*
1544 * Connect to the host service.
1545 */
1546 uint32_t idClient;
1547 int rc = VbglR3GuestCtrlConnect(&idClient);
1548 if (RT_FAILURE(rc))
1549 return VGSvcError("Error connecting to guest control service, rc=%Rrc\n", rc);
1550 g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(idClient);
1551 g_idControlSvcClient = idClient;
1552
1553 rc = vgsvcGstCtrlSessionReadKeyAndAccept(idClient, pSession->StartupInfo.uSessionID);
1554 if (RT_SUCCESS(rc))
1555 {
1556 VGSvcVerbose(1, "Using client ID=%RU32\n", idClient);
1557
1558 /*
1559 * Report started status.
1560 * If session status cannot be posted to the host for some reason, bail out.
1561 */
1562 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID) };
1563 rc = VbglR3GuestCtrlSessionNotify(&ctx, GUEST_SESSION_NOTIFYTYPE_STARTED, VINF_SUCCESS);
1564 if (RT_SUCCESS(rc))
1565 {
1566 /*
1567 * Allocate a scratch buffer for commands which also send payload data with them.
1568 * This buffer may grow if the host sends us larger chunks of data.
1569 */
1570 uint32_t cbScratchBuf = _64K;
1571 void *pvScratchBuf = RTMemAlloc(cbScratchBuf);
1572 if (pvScratchBuf)
1573 {
1574 /*
1575 * Message processing loop.
1576 */
1577 VBGLR3GUESTCTRLCMDCTX CtxHost = { idClient, 0 /* Context ID */, pSession->StartupInfo.uProtocol, 0 };
1578 for (;;)
1579 {
1580 VGSvcVerbose(3, "Waiting for host msg ...\n");
1581 uint32_t uMsg = 0;
1582 rc = VbglR3GuestCtrlMsgPeekWait(idClient, &uMsg, &CtxHost.uNumParms, NULL);
1583 if (RT_SUCCESS(rc))
1584 {
1585 VGSvcVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved (%Rrc)\n", uMsg, CtxHost.uNumParms, rc);
1586
1587 /*
1588 * Pass it on to the session handler.
1589 * Note! Only when handling HOST_SESSION_CLOSE is the rc used.
1590 */
1591 bool fShutdown = false;
1592 rc = VGSvcGstCtrlSessionHandler(pSession, uMsg, &CtxHost, &pvScratchBuf, &cbScratchBuf, &fShutdown);
1593 if (fShutdown)
1594 break;
1595 }
1596 else /** @todo Shouldn't we have a plan for handling connection loss and such? Now, we'll just spin like crazy. */
1597 VGSvcVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
1598
1599 /* Let others run (guests are often single CPU) ... */
1600 RTThreadYield();
1601 }
1602
1603 /*
1604 * Shutdown.
1605 */
1606 RTMemFree(pvScratchBuf);
1607 }
1608 else
1609 rc = VERR_NO_MEMORY;
1610
1611 VGSvcVerbose(0, "Session %RU32 ended\n", pSession->StartupInfo.uSessionID);
1612 }
1613 else
1614 VGSvcError("Reporting session ID=%RU32 started status failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
1615 }
1616 else
1617 VGSvcError("Setting message filterAdd=0x%x failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
1618
1619 VGSvcVerbose(3, "Disconnecting client ID=%RU32 ...\n", idClient);
1620 VbglR3GuestCtrlDisconnect(idClient);
1621 g_idControlSvcClient = 0;
1622
1623 VGSvcVerbose(3, "Session worker returned with rc=%Rrc\n", rc);
1624 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1625}
1626
1627
1628/**
1629 * Finds a (formerly) started guest process given by its PID and increases its
1630 * reference count.
1631 *
1632 * Must be decreased by the caller with VGSvcGstCtrlProcessRelease().
1633 *
1634 * @returns Guest process if found, otherwise NULL.
1635 * @param pSession Pointer to guest session where to search process in.
1636 * @param uPID PID to search for.
1637 *
1638 * @note This does *not lock the process!
1639 */
1640PVBOXSERVICECTRLPROCESS VGSvcGstCtrlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID)
1641{
1642 AssertPtrReturn(pSession, NULL);
1643
1644 PVBOXSERVICECTRLPROCESS pProcess = NULL;
1645 int rc = RTCritSectEnter(&pSession->CritSect);
1646 if (RT_SUCCESS(rc))
1647 {
1648 PVBOXSERVICECTRLPROCESS pCurProcess;
1649 RTListForEach(&pSession->lstProcesses, pCurProcess, VBOXSERVICECTRLPROCESS, Node)
1650 {
1651 if (pCurProcess->uPID == uPID)
1652 {
1653 rc = RTCritSectEnter(&pCurProcess->CritSect);
1654 if (RT_SUCCESS(rc))
1655 {
1656 pCurProcess->cRefs++;
1657 rc = RTCritSectLeave(&pCurProcess->CritSect);
1658 AssertRC(rc);
1659 }
1660
1661 if (RT_SUCCESS(rc))
1662 pProcess = pCurProcess;
1663 break;
1664 }
1665 }
1666
1667 rc = RTCritSectLeave(&pSession->CritSect);
1668 AssertRC(rc);
1669 }
1670
1671 return pProcess;
1672}
1673
1674
1675int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession)
1676{
1677 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1678
1679 VGSvcVerbose(0, "Session %RU32 is about to close ...\n", pSession->StartupInfo.uSessionID);
1680
1681 int rc = RTCritSectEnter(&pSession->CritSect);
1682 if (RT_SUCCESS(rc))
1683 {
1684 /*
1685 * Close all guest processes.
1686 */
1687 VGSvcVerbose(0, "Stopping all guest processes ...\n");
1688
1689 /* Signal all guest processes in the active list that we want to shutdown. */
1690 size_t cProcesses = 0;
1691 PVBOXSERVICECTRLPROCESS pProcess;
1692 RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
1693 {
1694 VGSvcGstCtrlProcessStop(pProcess);
1695 cProcesses++;
1696 }
1697
1698 VGSvcVerbose(1, "%zu guest processes were signalled to stop\n", cProcesses);
1699
1700 /* Wait for all active threads to shutdown and destroy the active thread list. */
1701 pProcess = RTListGetFirst(&pSession->lstProcesses, VBOXSERVICECTRLPROCESS, Node);
1702 while (pProcess)
1703 {
1704 PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node);
1705 bool fLast = RTListNodeIsLast(&pSession->lstProcesses, &pProcess->Node);
1706
1707 int rc2 = RTCritSectLeave(&pSession->CritSect);
1708 AssertRC(rc2);
1709
1710 rc2 = VGSvcGstCtrlProcessWait(pProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
1711
1712 int rc3 = RTCritSectEnter(&pSession->CritSect);
1713 AssertRC(rc3);
1714
1715 if (RT_SUCCESS(rc2))
1716 VGSvcGstCtrlProcessFree(pProcess);
1717
1718 if (fLast)
1719 break;
1720
1721 pProcess = pNext;
1722 }
1723
1724#ifdef DEBUG
1725 pProcess = RTListGetFirst(&pSession->lstProcesses, VBOXSERVICECTRLPROCESS, Node);
1726 while (pProcess)
1727 {
1728 PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node);
1729 bool fLast = RTListNodeIsLast(&pSession->lstProcesses, &pProcess->Node);
1730
1731 VGSvcVerbose(1, "Process %p (PID %RU32) still in list\n", pProcess, pProcess->uPID);
1732 if (fLast)
1733 break;
1734
1735 pProcess = pNext;
1736 }
1737#endif
1738 AssertMsg(RTListIsEmpty(&pSession->lstProcesses),
1739 ("Guest process list still contains entries when it should not\n"));
1740
1741 /*
1742 * Close all left guest files.
1743 */
1744 VGSvcVerbose(0, "Closing all guest files ...\n");
1745
1746 PVBOXSERVICECTRLFILE pFile;
1747 pFile = RTListGetFirst(&pSession->lstFiles, VBOXSERVICECTRLFILE, Node);
1748 while (pFile)
1749 {
1750 PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node);
1751 bool fLast = RTListNodeIsLast(&pSession->lstFiles, &pFile->Node);
1752
1753 int rc2 = vgsvcGstCtrlSessionFileDestroy(pFile);
1754 if (RT_FAILURE(rc2))
1755 {
1756 VGSvcError("Unable to close file '%s'; rc=%Rrc\n", pFile->szName, rc2);
1757 if (RT_SUCCESS(rc))
1758 rc = rc2;
1759 /* Keep going. */
1760 }
1761
1762 if (fLast)
1763 break;
1764
1765 pFile = pNext;
1766 }
1767
1768 AssertMsg(RTListIsEmpty(&pSession->lstFiles), ("Guest file list still contains entries when it should not\n"));
1769
1770 int rc2 = RTCritSectLeave(&pSession->CritSect);
1771 if (RT_SUCCESS(rc))
1772 rc = rc2;
1773 }
1774
1775 return rc;
1776}
1777
1778
1779int VGSvcGstCtrlSessionDestroy(PVBOXSERVICECTRLSESSION pSession)
1780{
1781 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1782
1783 int rc = VGSvcGstCtrlSessionClose(pSession);
1784
1785 /* Destroy critical section. */
1786 RTCritSectDelete(&pSession->CritSect);
1787
1788 return rc;
1789}
1790
1791
1792int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t fFlags)
1793{
1794 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1795
1796 RTListInit(&pSession->lstProcesses);
1797 RTListInit(&pSession->lstFiles);
1798
1799 pSession->fFlags = fFlags;
1800
1801 /* Init critical section for protecting the thread lists. */
1802 int rc = RTCritSectInit(&pSession->CritSect);
1803 AssertRC(rc);
1804
1805 return rc;
1806}
1807
1808
1809/**
1810 * Adds a guest process to a session's process list.
1811 *
1812 * @return VBox status code.
1813 * @param pSession Guest session to add process to.
1814 * @param pProcess Guest process to add.
1815 */
1816int VGSvcGstCtrlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
1817{
1818 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1819 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1820
1821 int rc = RTCritSectEnter(&pSession->CritSect);
1822 if (RT_SUCCESS(rc))
1823 {
1824 VGSvcVerbose( 3, "Adding process (PID %RU32) to session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
1825
1826 /* Add process to session list. */
1827 RTListAppend(&pSession->lstProcesses, &pProcess->Node);
1828
1829 int rc2 = RTCritSectLeave(&pSession->CritSect);
1830 if (RT_SUCCESS(rc))
1831 rc = rc2;
1832 }
1833
1834 return VINF_SUCCESS;
1835}
1836
1837
1838/**
1839 * Removes a guest process from a session's process list.
1840 *
1841 * @return VBox status code.
1842 * @param pSession Guest session to remove process from.
1843 * @param pProcess Guest process to remove.
1844 */
1845int VGSvcGstCtrlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
1846{
1847 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1848 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1849
1850 int rc = RTCritSectEnter(&pSession->CritSect);
1851 if (RT_SUCCESS(rc))
1852 {
1853 VGSvcVerbose(3, "Removing process (PID %RU32) from session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
1854 Assert(pProcess->cRefs == 0);
1855
1856 RTListNodeRemove(&pProcess->Node);
1857
1858 int rc2 = RTCritSectLeave(&pSession->CritSect);
1859 if (RT_SUCCESS(rc))
1860 rc = rc2;
1861 }
1862
1863 return VINF_SUCCESS;
1864}
1865
1866
1867/**
1868 * Determines whether starting a new guest process according to the
1869 * maximum number of concurrent guest processes defined is allowed or not.
1870 *
1871 * @return VBox status code.
1872 * @param pSession The guest session.
1873 * @param pbAllowed True if starting (another) guest process
1874 * is allowed, false if not.
1875 */
1876int VGSvcGstCtrlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pbAllowed)
1877{
1878 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1879 AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
1880
1881 int rc = RTCritSectEnter(&pSession->CritSect);
1882 if (RT_SUCCESS(rc))
1883 {
1884 /*
1885 * Check if we're respecting our memory policy by checking
1886 * how many guest processes are started and served already.
1887 */
1888 bool fLimitReached = false;
1889 if (pSession->uProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
1890 {
1891 uint32_t uProcsRunning = 0;
1892 PVBOXSERVICECTRLPROCESS pProcess;
1893 RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
1894 uProcsRunning++;
1895
1896 VGSvcVerbose(3, "Maximum served guest processes set to %u, running=%u\n", pSession->uProcsMaxKept, uProcsRunning);
1897
1898 int32_t iProcsLeft = (pSession->uProcsMaxKept - uProcsRunning - 1);
1899 if (iProcsLeft < 0)
1900 {
1901 VGSvcVerbose(3, "Maximum running guest processes reached (%u)\n", pSession->uProcsMaxKept);
1902 fLimitReached = true;
1903 }
1904 }
1905
1906 *pbAllowed = !fLimitReached;
1907
1908 int rc2 = RTCritSectLeave(&pSession->CritSect);
1909 if (RT_SUCCESS(rc))
1910 rc = rc2;
1911 }
1912
1913 return rc;
1914}
1915
1916
1917/**
1918 * Creates the process for a guest session.
1919 *
1920 * @return VBox status code.
1921 * @param pSessionStartupInfo Session startup info.
1922 * @param pSessionThread The session thread under construction.
1923 * @param uCtrlSessionThread The session thread debug ordinal.
1924 */
1925static int vgsvcVGSvcGstCtrlSessionThreadCreateProcess(const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
1926 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread, uint32_t uCtrlSessionThread)
1927{
1928 RT_NOREF(uCtrlSessionThread);
1929
1930 /*
1931 * Is this an anonymous session? Anonymous sessions run with the same
1932 * privileges as the main VBoxService executable.
1933 */
1934 bool const fAnonymous = pSessionThread->StartupInfo.szUser[0] == '\0';
1935 if (fAnonymous)
1936 {
1937 Assert(!strlen(pSessionThread->StartupInfo.szPassword));
1938 Assert(!strlen(pSessionThread->StartupInfo.szDomain));
1939
1940 VGSvcVerbose(3, "New anonymous guest session ID=%RU32 created, fFlags=%x, using protocol %RU32\n",
1941 pSessionStartupInfo->uSessionID,
1942 pSessionStartupInfo->fFlags,
1943 pSessionStartupInfo->uProtocol);
1944 }
1945 else
1946 {
1947 VGSvcVerbose(3, "Spawning new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, fFlags=%x, using protocol %RU32\n",
1948 pSessionStartupInfo->uSessionID,
1949 pSessionStartupInfo->szUser,
1950#ifdef DEBUG
1951 pSessionStartupInfo->szPassword,
1952#else
1953 "XXX", /* Never show passwords in release mode. */
1954#endif
1955 pSessionStartupInfo->szDomain,
1956 pSessionStartupInfo->fFlags,
1957 pSessionStartupInfo->uProtocol);
1958 }
1959
1960 /*
1961 * Spawn a child process for doing the actual session handling.
1962 * Start by assembling the argument list.
1963 */
1964 char szExeName[RTPATH_MAX];
1965 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
1966 AssertReturn(pszExeName, VERR_FILENAME_TOO_LONG);
1967
1968 char szParmSessionID[32];
1969 RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", pSessionThread->StartupInfo.uSessionID);
1970
1971 char szParmSessionProto[32];
1972 RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
1973 pSessionThread->StartupInfo.uProtocol);
1974#ifdef DEBUG
1975 char szParmThreadId[32];
1976 RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", uCtrlSessionThread);
1977#endif
1978 unsigned idxArg = 0; /* Next index in argument vector. */
1979 char const *apszArgs[24];
1980
1981 apszArgs[idxArg++] = pszExeName;
1982 apszArgs[idxArg++] = "guestsession";
1983 apszArgs[idxArg++] = szParmSessionID;
1984 apszArgs[idxArg++] = szParmSessionProto;
1985#ifdef DEBUG
1986 apszArgs[idxArg++] = szParmThreadId;
1987#endif
1988 if (!fAnonymous) /* Do we need to pass a user name? */
1989 {
1990 apszArgs[idxArg++] = "--user";
1991 apszArgs[idxArg++] = pSessionThread->StartupInfo.szUser;
1992
1993 if (strlen(pSessionThread->StartupInfo.szDomain))
1994 {
1995 apszArgs[idxArg++] = "--domain";
1996 apszArgs[idxArg++] = pSessionThread->StartupInfo.szDomain;
1997 }
1998 }
1999
2000 /* Add same verbose flags as parent process. */
2001 char szParmVerbose[32];
2002 if (g_cVerbosity > 0)
2003 {
2004 unsigned cVs = RT_MIN(g_cVerbosity, RT_ELEMENTS(szParmVerbose) - 2);
2005 szParmVerbose[0] = '-';
2006 memset(&szParmVerbose[1], 'v', cVs);
2007 szParmVerbose[1 + cVs] = '\0';
2008 apszArgs[idxArg++] = szParmVerbose;
2009 }
2010
2011 /* Add log file handling. Each session will have an own
2012 * log file, naming based on the parent log file. */
2013 char szParmLogFile[sizeof(g_szLogFile) + 128];
2014 if (g_szLogFile[0])
2015 {
2016 const char *pszSuffix = RTPathSuffix(g_szLogFile);
2017 if (!pszSuffix)
2018 pszSuffix = strchr(g_szLogFile, '\0');
2019 size_t cchBase = pszSuffix - g_szLogFile;
2020#ifndef DEBUG
2021 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%s%s",
2022 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, pSessionStartupInfo->szUser, pszSuffix);
2023#else
2024 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%RU32-%s%s",
2025 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, uCtrlSessionThread,
2026 pSessionStartupInfo->szUser, pszSuffix);
2027#endif
2028 apszArgs[idxArg++] = "--logfile";
2029 apszArgs[idxArg++] = szParmLogFile;
2030 }
2031
2032#ifdef DEBUG
2033 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT)
2034 apszArgs[idxArg++] = "--dump-stdout";
2035 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR)
2036 apszArgs[idxArg++] = "--dump-stderr";
2037#endif
2038 apszArgs[idxArg] = NULL;
2039 Assert(idxArg < RT_ELEMENTS(apszArgs));
2040
2041 if (g_cVerbosity > 3)
2042 {
2043 VGSvcVerbose(4, "Spawning parameters:\n");
2044 for (idxArg = 0; apszArgs[idxArg]; idxArg++)
2045 VGSvcVerbose(4, " %s\n", apszArgs[idxArg]);
2046 }
2047
2048 /*
2049 * Flags.
2050 */
2051 uint32_t const fProcCreate = RTPROC_FLAGS_PROFILE
2052#ifdef RT_OS_WINDOWS
2053 | RTPROC_FLAGS_SERVICE
2054 | RTPROC_FLAGS_HIDDEN
2055#endif
2056 ;
2057
2058 /*
2059 * Configure standard handles.
2060 */
2061 RTHANDLE hStdIn;
2062 int rc = RTPipeCreate(&hStdIn.u.hPipe, &pSessionThread->hKeyPipe, RTPIPE_C_INHERIT_READ);
2063 if (RT_SUCCESS(rc))
2064 {
2065 hStdIn.enmType = RTHANDLETYPE_PIPE;
2066
2067 RTHANDLE hStdOutAndErr;
2068 rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
2069 if (RT_SUCCESS(rc))
2070 {
2071 hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
2072
2073 /*
2074 * Windows: If a domain name is given, construct an UPN (User Principle Name)
2075 * with the domain name built-in, e.g. "joedoe@example.com".
2076 */
2077 const char *pszUser = pSessionThread->StartupInfo.szUser;
2078#ifdef RT_OS_WINDOWS
2079 char *pszUserUPN = NULL;
2080 if (pSessionThread->StartupInfo.szDomain[0])
2081 {
2082 int cchbUserUPN = RTStrAPrintf(&pszUserUPN, "%s@%s",
2083 pSessionThread->StartupInfo.szUser,
2084 pSessionThread->StartupInfo.szDomain);
2085 if (cchbUserUPN > 0)
2086 {
2087 pszUser = pszUserUPN;
2088 VGSvcVerbose(3, "Using UPN: %s\n", pszUserUPN);
2089 }
2090 else
2091 rc = VERR_NO_STR_MEMORY;
2092 }
2093 if (RT_SUCCESS(rc))
2094#endif
2095 {
2096 /*
2097 * Finally, create the process.
2098 */
2099 rc = RTProcCreateEx(pszExeName, apszArgs, RTENV_DEFAULT, fProcCreate,
2100 &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
2101 !fAnonymous ? pszUser : NULL,
2102 !fAnonymous ? pSessionThread->StartupInfo.szPassword : NULL,
2103 &pSessionThread->hProcess);
2104 }
2105#ifdef RT_OS_WINDOWS
2106 RTStrFree(pszUserUPN);
2107#endif
2108 RTFileClose(hStdOutAndErr.u.hFile);
2109 }
2110
2111 RTPipeClose(hStdIn.u.hPipe);
2112 }
2113 return rc;
2114}
2115
2116
2117/**
2118 * Creates a guest session.
2119 *
2120 * This will spawn a new VBoxService.exe instance under behalf of the given user
2121 * which then will act as a session host. On successful open, the session will
2122 * be added to the given session thread list.
2123 *
2124 * @return VBox status code.
2125 * @param pList Which list to use to store the session thread in.
2126 * @param pSessionStartupInfo Session startup info.
2127 * @param ppSessionThread Returns newly created session thread on success.
2128 * Optional.
2129 */
2130int VGSvcGstCtrlSessionThreadCreate(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
2131 PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
2132{
2133 AssertPtrReturn(pList, VERR_INVALID_POINTER);
2134 AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
2135 /* ppSessionThread is optional. */
2136
2137#ifdef VBOX_STRICT
2138 /* Check for existing session in debug mode. Should never happen because of
2139 * Main consistency. */
2140 PVBOXSERVICECTRLSESSIONTHREAD pSessionCur;
2141 RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node)
2142 {
2143 AssertMsgReturn(pSessionCur->StartupInfo.uSessionID != pSessionStartupInfo->uSessionID,
2144 ("Guest session thread ID=%RU32 (%p) already exists when it should not\n",
2145 pSessionCur->StartupInfo.uSessionID, pSessionCur), VERR_ALREADY_EXISTS);
2146 }
2147#endif
2148
2149 /* Static counter to help tracking session thread <-> process relations. */
2150 static uint32_t s_uCtrlSessionThread = 0;
2151#if 1
2152 if (++s_uCtrlSessionThread == 100000)
2153#else /* This must be some joke, right? ;-) */
2154 if (s_uCtrlSessionThread++ == UINT32_MAX)
2155#endif
2156 s_uCtrlSessionThread = 0; /* Wrap around to not let IPRT freak out. */
2157
2158 /*
2159 * Allocate and initialize the session thread structure.
2160 */
2161 int rc;
2162 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread = (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(*pSessionThread));
2163 if (pSessionThread)
2164 {
2165 //pSessionThread->fShutdown = false;
2166 //pSessionThread->fStarted = false;
2167 //pSessionThread->fStopped = false;
2168 pSessionThread->hKeyPipe = NIL_RTPIPE;
2169 pSessionThread->Thread = NIL_RTTHREAD;
2170 pSessionThread->hProcess = NIL_RTPROCESS;
2171
2172 /* Copy over session startup info. */
2173 memcpy(&pSessionThread->StartupInfo, pSessionStartupInfo, sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
2174
2175 /* Generate the secret key. */
2176 RTRandBytes(pSessionThread->abKey, sizeof(pSessionThread->abKey));
2177
2178 rc = RTCritSectInit(&pSessionThread->CritSect);
2179 AssertRC(rc);
2180 if (RT_SUCCESS(rc))
2181 {
2182 /*
2183 * Give the session key to the host so it can validate the client.
2184 */
2185 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
2186 {
2187 for (uint32_t i = 0; i < 10; i++)
2188 {
2189 rc = VbglR3GuestCtrlSessionPrepare(g_idControlSvcClient, pSessionStartupInfo->uSessionID,
2190 pSessionThread->abKey, sizeof(pSessionThread->abKey));
2191 if (rc != VERR_OUT_OF_RESOURCES)
2192 break;
2193 RTThreadSleep(100);
2194 }
2195 }
2196 if (RT_SUCCESS(rc))
2197 {
2198 /*
2199 * Start the session child process.
2200 */
2201 rc = vgsvcVGSvcGstCtrlSessionThreadCreateProcess(pSessionStartupInfo, pSessionThread, s_uCtrlSessionThread);
2202 if (RT_SUCCESS(rc))
2203 {
2204 /*
2205 * Start the session thread.
2206 */
2207 rc = RTThreadCreateF(&pSessionThread->Thread, vgsvcGstCtrlSessionThread, pSessionThread /*pvUser*/, 0 /*cbStack*/,
2208 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CtrlSess%u", s_uCtrlSessionThread);
2209 if (RT_SUCCESS(rc))
2210 {
2211 /* Wait for the thread to initialize. */
2212 rc = RTThreadUserWait(pSessionThread->Thread, RT_MS_1MIN);
2213 if ( RT_SUCCESS(rc)
2214 && !ASMAtomicReadBool(&pSessionThread->fShutdown))
2215 {
2216 VGSvcVerbose(2, "Thread for session ID=%RU32 started\n", pSessionThread->StartupInfo.uSessionID);
2217
2218 ASMAtomicXchgBool(&pSessionThread->fStarted, true);
2219
2220 /* Add session to list. */
2221 RTListAppend(pList, &pSessionThread->Node);
2222 if (ppSessionThread) /* Return session if wanted. */
2223 *ppSessionThread = pSessionThread;
2224 return VINF_SUCCESS;
2225 }
2226
2227 /*
2228 * Bail out.
2229 */
2230 VGSvcError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
2231 pSessionThread->StartupInfo.uSessionID, rc);
2232 if (RT_SUCCESS_NP(rc))
2233 rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
2234 }
2235 else
2236 VGSvcError("Creating session thread failed, rc=%Rrc\n", rc);
2237
2238 RTProcTerminate(pSessionThread->hProcess);
2239 uint32_t cMsWait = 1;
2240 while ( RTProcWait(pSessionThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL) == VERR_PROCESS_RUNNING
2241 && cMsWait <= 9) /* 1023 ms */
2242 {
2243 RTThreadSleep(cMsWait);
2244 cMsWait <<= 1;
2245 }
2246 }
2247
2248 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
2249 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, pSessionStartupInfo->uSessionID);
2250 }
2251 else
2252 VGSvcVerbose(3, "VbglR3GuestCtrlSessionPrepare failed: %Rrc\n", rc);
2253 RTPipeClose(pSessionThread->hKeyPipe);
2254 pSessionThread->hKeyPipe = NIL_RTPIPE;
2255 RTCritSectDelete(&pSessionThread->CritSect);
2256 }
2257 RTMemFree(pSessionThread);
2258 }
2259 else
2260 rc = VERR_NO_MEMORY;
2261
2262 VGSvcVerbose(3, "Spawning session thread returned returned rc=%Rrc\n", rc);
2263 return rc;
2264}
2265
2266
2267/**
2268 * Waits for a formerly opened guest session process to close.
2269 *
2270 * @return VBox status code.
2271 * @param pThread Guest session thread to wait for.
2272 * @param uTimeoutMS Waiting timeout (in ms).
2273 * @param fFlags Closing flags.
2274 */
2275int VGSvcGstCtrlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uTimeoutMS, uint32_t fFlags)
2276{
2277 RT_NOREF(fFlags);
2278 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2279 /** @todo Validate closing flags. */
2280
2281 AssertMsgReturn(pThread->Thread != NIL_RTTHREAD,
2282 ("Guest session thread of session %p does not exist when it should\n", pThread),
2283 VERR_NOT_FOUND);
2284
2285 int rc = VINF_SUCCESS;
2286
2287 /*
2288 * The spawned session process should have received the same closing request,
2289 * so just wait for the process to close.
2290 */
2291 if (ASMAtomicReadBool(&pThread->fStarted))
2292 {
2293 /* Ask the thread to shutdown. */
2294 ASMAtomicXchgBool(&pThread->fShutdown, true);
2295
2296 VGSvcVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
2297 pThread->StartupInfo.uSessionID, uTimeoutMS);
2298
2299 int rcThread;
2300 rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
2301 if (RT_SUCCESS(rc))
2302 VGSvcVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n", pThread->StartupInfo.uSessionID, rcThread);
2303 else
2304 VGSvcError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n", pThread->StartupInfo.uSessionID, rc);
2305 }
2306
2307 return rc;
2308}
2309
2310/**
2311 * Waits for the specified session thread to end and remove
2312 * it from the session thread list.
2313 *
2314 * @return VBox status code.
2315 * @param pThread Session thread to destroy.
2316 * @param fFlags Closing flags.
2317 */
2318int VGSvcGstCtrlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t fFlags)
2319{
2320 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2321
2322 int rc = VGSvcGstCtrlSessionThreadWait(pThread, 5 * 60 * 1000 /* 5 minutes timeout */, fFlags);
2323
2324 /* Remove session from list and destroy object. */
2325 RTListNodeRemove(&pThread->Node);
2326
2327 RTMemFree(pThread);
2328 pThread = NULL;
2329
2330 return rc;
2331}
2332
2333/**
2334 * Close all open guest session threads.
2335 *
2336 * @note Caller is responsible for locking!
2337 *
2338 * @return VBox status code.
2339 * @param pList Which list to close the session threads for.
2340 * @param fFlags Closing flags.
2341 */
2342int VGSvcGstCtrlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t fFlags)
2343{
2344 AssertPtrReturn(pList, VERR_INVALID_POINTER);
2345
2346 int rc = VINF_SUCCESS;
2347
2348 /*int rc = VbglR3GuestCtrlClose
2349 if (RT_FAILURE(rc))
2350 VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/
2351
2352 PVBOXSERVICECTRLSESSIONTHREAD pSessIt;
2353 PVBOXSERVICECTRLSESSIONTHREAD pSessItNext;
2354 RTListForEachSafe(pList, pSessIt, pSessItNext, VBOXSERVICECTRLSESSIONTHREAD, Node)
2355 {
2356 int rc2 = VGSvcGstCtrlSessionThreadDestroy(pSessIt, fFlags);
2357 if (RT_FAILURE(rc2))
2358 {
2359 VGSvcError("Closing session thread '%s' failed with rc=%Rrc\n", RTThreadGetName(pSessIt->Thread), rc2);
2360 if (RT_SUCCESS(rc))
2361 rc = rc2;
2362 /* Keep going. */
2363 }
2364 }
2365
2366 VGSvcVerbose(4, "Destroying guest session threads ended with %Rrc\n", rc);
2367 return rc;
2368}
2369
2370
2371/**
2372 * Main function for the session process.
2373 *
2374 * @returns exit code.
2375 * @param argc Argument count.
2376 * @param argv Argument vector (UTF-8).
2377 */
2378RTEXITCODE VGSvcGstCtrlSessionSpawnInit(int argc, char **argv)
2379{
2380 static const RTGETOPTDEF s_aOptions[] =
2381 {
2382 { "--domain", VBOXSERVICESESSIONOPT_DOMAIN, RTGETOPT_REQ_STRING },
2383#ifdef DEBUG
2384 { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING },
2385 { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING },
2386#endif
2387 { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
2388 { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
2389 { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
2390 { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
2391#ifdef DEBUG
2392 { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 },
2393#endif /* DEBUG */
2394 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
2395 };
2396
2397 RTGETOPTSTATE GetState;
2398 RTGetOptInit(&GetState, argc, argv,
2399 s_aOptions, RT_ELEMENTS(s_aOptions),
2400 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2401
2402 uint32_t fSession = VBOXSERVICECTRLSESSION_FLAG_SPAWN;
2403
2404 /* Protocol and session ID must be specified explicitly. */
2405 g_Session.StartupInfo.uProtocol = UINT32_MAX;
2406 g_Session.StartupInfo.uSessionID = UINT32_MAX;
2407
2408 int ch;
2409 RTGETOPTUNION ValueUnion;
2410 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
2411 {
2412 /* For options that require an argument, ValueUnion has received the value. */
2413 switch (ch)
2414 {
2415 case VBOXSERVICESESSIONOPT_DOMAIN:
2416 /* Information not needed right now, skip. */
2417 break;
2418#ifdef DEBUG
2419 case VBOXSERVICESESSIONOPT_DUMP_STDOUT:
2420 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
2421 break;
2422
2423 case VBOXSERVICESESSIONOPT_DUMP_STDERR:
2424 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
2425 break;
2426#endif
2427 case VBOXSERVICESESSIONOPT_SESSION_ID:
2428 g_Session.StartupInfo.uSessionID = ValueUnion.u32;
2429 break;
2430
2431 case VBOXSERVICESESSIONOPT_SESSION_PROTO:
2432 g_Session.StartupInfo.uProtocol = ValueUnion.u32;
2433 break;
2434#ifdef DEBUG
2435 case VBOXSERVICESESSIONOPT_THREAD_ID:
2436 /* Not handled. Mainly for processs listing. */
2437 break;
2438#endif
2439 case VBOXSERVICESESSIONOPT_LOG_FILE:
2440 {
2441 int rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
2442 if (RT_FAILURE(rc))
2443 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error copying log file name: %Rrc", rc);
2444 break;
2445 }
2446
2447 case VBOXSERVICESESSIONOPT_USERNAME:
2448 /* Information not needed right now, skip. */
2449 break;
2450
2451 /** @todo Implement help? */
2452
2453 case 'v':
2454 g_cVerbosity++;
2455 break;
2456
2457 case VINF_GETOPT_NOT_OPTION:
2458 /* Ignore; might be "guestsession" main command. */
2459 /** @todo r=bird: We DO NOT ignore stuff on the command line! */
2460 break;
2461
2462 default:
2463 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown command '%s'", ValueUnion.psz);
2464 }
2465 }
2466
2467 /* Check that we've got all the required options. */
2468 if (g_Session.StartupInfo.uProtocol == UINT32_MAX)
2469 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified");
2470
2471 if (g_Session.StartupInfo.uSessionID == UINT32_MAX)
2472 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
2473
2474 /* Init the session object. */
2475 int rc = VGSvcGstCtrlSessionInit(&g_Session, fSession);
2476 if (RT_FAILURE(rc))
2477 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to initialize session object, rc=%Rrc\n", rc);
2478
2479 rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
2480 if (RT_FAILURE(rc))
2481 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to create log file '%s', rc=%Rrc\n",
2482 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
2483
2484 RTEXITCODE rcExit = vgsvcGstCtrlSessionSpawnWorker(&g_Session);
2485
2486 VGSvcLogDestroy();
2487 return rcExit;
2488}
2489
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