VirtualBox

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

Last change on this file since 72097 was 72097, checked in by vboxsync, 7 years ago

Guest Control/VBoxService: Added additional size check in vgsvcGstCtrlSessionHandleFileWrite().

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