VirtualBox

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

Last change on this file since 52002 was 50063, checked in by vboxsync, 11 years ago

VBoxService/VBoxServiceControlSession.cpp: Added a to-do.

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