VirtualBox

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

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