VirtualBox

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

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

GuestControl: Added and implemented IGuestSession::userHome and IGuestSession::userDocuments attributes. Untested.

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