VirtualBox

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

Last change on this file since 57962 was 57962, checked in by vboxsync, 9 years ago

VBoxServiceControlSession.cpp: ditto.

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