VirtualBox

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

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

IPRT: Renamed RTPROC_FLAGS_NO_PROFILE to RTPROC_FLAGS_PROFILE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.9 KB
Line 
1/* $Id: VBoxServiceControlSession.cpp 57753 2015-09-15 11:15:21Z 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 a guest session.
1747 *
1748 * This will spawn a new VBoxService.exe instance under behalf of the given user
1749 * which then will act as a session host. On successful open, the session will
1750 * be added to the given session thread list.
1751 *
1752 * @return IPRT status code.
1753 * @param pList Which list to use to store the session thread in.
1754 * @param pSessionStartupInfo Session startup info.
1755 * @param ppSessionThread Returns newly created session thread on success.
1756 * Optional.
1757 */
1758int GstCntlSessionThreadCreate(PRTLISTANCHOR pList,
1759 const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
1760 PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
1761{
1762 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1763 AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
1764 /* ppSessionThread is optional. */
1765
1766#ifdef DEBUG
1767 PVBOXSERVICECTRLSESSIONTHREAD pSessionCur;
1768 /* Check for existing session in debug mode. Should never happen because of
1769 * Main consistency. */
1770 RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node)
1771 {
1772 if (pSessionCur->StartupInfo.uSessionID == pSessionStartupInfo->uSessionID)
1773 {
1774 AssertMsgFailed(("Guest session thread ID=%RU32 (%p) already exists when it should not\n",
1775 pSessionCur->StartupInfo.uSessionID, pSessionCur));
1776 return VERR_ALREADY_EXISTS;
1777 }
1778 }
1779#endif
1780 int rc = VINF_SUCCESS;
1781
1782 /* Static counter to help tracking session thread <-> process relations. */
1783 static uint32_t s_uCtrlSessionThread = 0;
1784 if (s_uCtrlSessionThread++ == UINT32_MAX)
1785 s_uCtrlSessionThread = 0; /* Wrap around to not let IPRT freak out. */
1786
1787 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread =
1788 (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(VBOXSERVICECTRLSESSIONTHREAD));
1789 if (pSessionThread)
1790 {
1791 /* Copy over session startup info. */
1792 memcpy(&pSessionThread->StartupInfo, pSessionStartupInfo,
1793 sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
1794
1795 pSessionThread->fShutdown = false;
1796 pSessionThread->fStarted = false;
1797 pSessionThread->fStopped = false;
1798
1799 /* Is this an anonymous session? */
1800 /* Anonymous sessions run with the same privileges as the main VBoxService executable. */
1801 bool const fAnonymous = pSessionThread->StartupInfo.szUser[0] == '\0';
1802 if (fAnonymous)
1803 {
1804 Assert(!strlen(pSessionThread->StartupInfo.szPassword));
1805 Assert(!strlen(pSessionThread->StartupInfo.szDomain));
1806
1807 VBoxServiceVerbose(3, "New anonymous guest session ID=%RU32 created, uFlags=%x, using protocol %RU32\n",
1808 pSessionStartupInfo->uSessionID,
1809 pSessionStartupInfo->fFlags,
1810 pSessionStartupInfo->uProtocol);
1811 }
1812 else
1813 {
1814 VBoxServiceVerbose(3, "Spawning new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, uFlags=%x, using protocol %RU32\n",
1815 pSessionStartupInfo->uSessionID,
1816 pSessionStartupInfo->szUser,
1817#ifdef DEBUG
1818 pSessionStartupInfo->szPassword,
1819#else
1820 "XXX", /* Never show passwords in release mode. */
1821#endif
1822 pSessionStartupInfo->szDomain,
1823 pSessionStartupInfo->fFlags,
1824 pSessionStartupInfo->uProtocol);
1825 }
1826
1827 rc = RTCritSectInit(&pSessionThread->CritSect);
1828 AssertRC(rc);
1829
1830 /*
1831 * Spawn a child process for doing the actual session handling.
1832 */
1833 char szExeName[RTPATH_MAX];
1834 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
1835 if (pszExeName)
1836 {
1837 char szParmSessionID[32];
1838 RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", pSessionThread->StartupInfo.uSessionID);
1839
1840 char szParmSessionProto[32];
1841 RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
1842 pSessionThread->StartupInfo.uProtocol);
1843#ifdef DEBUG
1844 char szParmThreadId[32];
1845 RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", s_uCtrlSessionThread);
1846#endif
1847 char szParmUserName[GUESTPROCESS_MAX_USER_LEN + 32];
1848
1849 int iArgIdx = 0; /* Current index in argument vector. */
1850 char const *papszArgs[16];
1851
1852 papszArgs[iArgIdx++] = pszExeName;
1853 papszArgs[iArgIdx++] = "guestsession";
1854 papszArgs[iArgIdx++] = szParmSessionID;
1855 papszArgs[iArgIdx++] = szParmSessionProto;
1856#ifdef DEBUG
1857 papszArgs[iArgIdx++] = szParmThreadId;
1858#endif
1859 if (!fAnonymous) /* Do we need to pass a user name? */
1860 {
1861 RTStrPrintf(szParmUserName, sizeof(szParmUserName), "--user=%s", pSessionThread->StartupInfo.szUser);
1862 papszArgs[iArgIdx++] = szParmUserName;
1863 }
1864
1865 /* Add same verbose flags as parent process. */
1866 int rc2 = VINF_SUCCESS;
1867 char szParmVerbose[32] = { 0 };
1868 for (int i = 0; i < g_cVerbosity && RT_SUCCESS(rc2); i++)
1869 {
1870 if (i == 0)
1871 rc2 = RTStrCat(szParmVerbose, sizeof(szParmVerbose), "-");
1872 if (RT_FAILURE(rc2))
1873 break;
1874 rc2 = RTStrCat(szParmVerbose, sizeof(szParmVerbose), "v");
1875 }
1876 if (RT_SUCCESS(rc2))
1877 papszArgs[iArgIdx++] = szParmVerbose;
1878
1879 /* Add log file handling. Each session will have an own
1880 * log file, naming based on the parent log file. */
1881 char szParmLogFile[RTPATH_MAX];
1882 if ( RT_SUCCESS(rc2)
1883 && strlen(g_szLogFile))
1884 {
1885 char *pszLogFile = RTStrDup(g_szLogFile);
1886 if (pszLogFile)
1887 {
1888 char *pszLogSuff = NULL;
1889 if (RTPathHasSuffix(pszLogFile))
1890 pszLogSuff = RTStrDup(RTPathSuffix(pszLogFile));
1891 RTPathStripSuffix(pszLogFile);
1892 char *pszLogNewSuffix;
1893#ifndef DEBUG
1894 if (RTStrAPrintf(&pszLogNewSuffix, "-%RU32-%s",
1895 pSessionStartupInfo->uSessionID,
1896 pSessionStartupInfo->szUser) < 0)
1897 {
1898 rc2 = VERR_NO_MEMORY;
1899 }
1900#else /* DEBUG */
1901 /* Include the session thread ID in the log file name. */
1902 if (RTStrAPrintf(&pszLogNewSuffix, "-%RU32-%RU32-%s",
1903 pSessionStartupInfo->uSessionID,
1904 s_uCtrlSessionThread,
1905 pSessionStartupInfo->szUser) < 0)
1906 {
1907 rc2 = VERR_NO_MEMORY;
1908 }
1909#endif /* DEBUG */
1910 else
1911 {
1912 rc2 = RTStrAAppend(&pszLogFile, pszLogNewSuffix);
1913 if (RT_SUCCESS(rc2) && pszLogSuff)
1914 rc2 = RTStrAAppend(&pszLogFile, pszLogSuff);
1915 if (RT_SUCCESS(rc2))
1916 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "--logfile=%s", pszLogFile);
1917
1918 RTStrFree(pszLogNewSuffix);
1919 }
1920 if (RT_FAILURE(rc2))
1921 VBoxServiceError("Error building session logfile string for session %RU32 (user %s), rc=%Rrc\n",
1922 pSessionStartupInfo->uSessionID, pSessionStartupInfo->szUser, rc2);
1923 if (pszLogSuff)
1924 RTStrFree(pszLogSuff);
1925 RTStrFree(pszLogFile);
1926 }
1927 if (RT_SUCCESS(rc2))
1928 papszArgs[iArgIdx++] = szParmLogFile;
1929
1930 rc = rc2;
1931 }
1932 else if (RT_FAILURE(rc2))
1933 rc = rc2;
1934#ifdef DEBUG
1935 VBoxServiceVerbose(4, "Argv building rc=%Rrc, session flags=%x\n", rc, g_Session.fFlags);
1936 if (RT_SUCCESS(rc))
1937 {
1938 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT)
1939 papszArgs[iArgIdx++] = "--dump-stdout";
1940 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR)
1941 papszArgs[iArgIdx++] = "--dump-stderr";
1942 }
1943#endif
1944 papszArgs[iArgIdx++] = NULL;
1945
1946 if (g_cVerbosity > 3)
1947 {
1948 VBoxServiceVerbose(4, "Spawning parameters:\n");
1949
1950 iArgIdx = 0;
1951 while (papszArgs[iArgIdx])
1952 VBoxServiceVerbose(4, "\t%s\n", papszArgs[iArgIdx++]);
1953 }
1954
1955 uint32_t uProcFlags = RTPROC_FLAGS_SERVICE
1956#ifdef RT_OS_WINDOWS
1957 /* Make sure to also load the profile data on a Windows guest. */
1958 | RTPROC_FLAGS_PROFILE /** @todo Not implemented for non-Windows yet. */
1959#endif
1960 | RTPROC_FLAGS_HIDDEN; /** @todo More flags from startup info? */
1961 /*
1962 * Create the session process' environment block.
1963 */
1964 RTENV hEnv = NIL_RTENV;
1965 if (RT_SUCCESS(rc))
1966 {
1967 /** @todo At the moment a session process does not have the ability to use the
1968 * per-session environment variables itself, only the session's guest
1969 * processes do so. Implement that later, also needs tweaking of
1970 * VbglR3GuestCtrlSessionGetOpen(). */
1971 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
1972
1973 if (g_cVerbosity > 3)
1974 {
1975 VBoxServiceVerbose(4, "Environment variables:\n");
1976
1977 uint32_t cVars = RTEnvCountEx(hEnv);
1978 for (uint32_t iVar = 0; iVar < cVars; iVar++)
1979 {
1980 char szVar[_1K];
1981 char szValue[_16K];
1982 rc2 = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue));
1983 if (RT_SUCCESS(rc2))
1984 VBoxServiceVerbose(4, "\t%s=%s\n", szVar, szValue);
1985 else if (rc2 == VERR_BUFFER_OVERFLOW)
1986 VBoxServiceVerbose(4, "\t%s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue);
1987 else
1988 {
1989 VBoxServiceVerbose(4, "\tUnable to enumerate environment variable #%RU32: %Rrc\n", iVar, rc2);
1990 /* Keep going. */
1991 }
1992 }
1993 }
1994
1995#ifdef RT_OS_WINDOWS
1996 if (RT_SUCCESS(rc))
1997 {
1998 /*
1999 * On Windows, when VBoxService was started as local service via SCM, the environment variable
2000 * USERPROFILE was set to point to LocalService's user directory.
2001 *
2002 * As we want to make sure that USERPROFILE actually points to the directory of the user we want
2003 * to spawn the guest process for, unset the variable here before handing the environment block over
2004 * to RTProcCreateEx().
2005 *
2006 * Note: RTProcCreateEx() in turn will *not* overwrite _any_ of already set
2007 * environment variables by default! */
2008 RTEnvUnsetEx(hEnv, "USERPROFILE");
2009 }
2010#endif
2011 }
2012
2013#if 0 /* Pipe handling not needed (yet). */
2014 /* Setup pipes. */
2015 rc = GstcntlProcessSetupPipe("|", 0 /*STDIN_FILENO*/,
2016 &pSession->StdIn.hChild, &pSession->StdIn.phChild, &pSession->hStdInW);
2017 if (RT_SUCCESS(rc))
2018 {
2019 rc = GstcntlProcessSetupPipe("|", 1 /*STDOUT_FILENO*/,
2020 &pSession->StdOut.hChild, &pSession->StdOut.phChild, &pSession->hStdOutR);
2021 if (RT_SUCCESS(rc))
2022 {
2023 rc = GstcntlProcessSetupPipe("|", 2 /*STDERR_FILENO*/,
2024 &pSession->StdErr.hChild, &pSession->StdErr.phChild, &pSession->hStdErrR);
2025 if (RT_SUCCESS(rc))
2026 {
2027 rc = RTPollSetCreate(&pSession->hPollSet);
2028 if (RT_SUCCESS(rc))
2029 rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdInW, RTPOLL_EVT_ERROR,
2030 VBOXSERVICECTRLPIPEID_STDIN);
2031 if (RT_SUCCESS(rc))
2032 rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
2033 VBOXSERVICECTRLPIPEID_STDOUT);
2034 if (RT_SUCCESS(rc))
2035 rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
2036 VBOXSERVICECTRLPIPEID_STDERR);
2037 }
2038
2039 if (RT_SUCCESS(rc))
2040 rc = RTProcCreateEx(pszExeName, papszArgs, hEnv, uProcFlags,
2041 pSession->StdIn.phChild, pSession->StdOut.phChild, pSession->StdErr.phChild,
2042 !fAnonymous ? pSession->StartupInfo.szUser : NULL,
2043 !fAnonymous ? pSession->StartupInfo.szPassword : NULL,
2044 &pSession->hProcess);
2045
2046 if (RT_SUCCESS(rc))
2047 {
2048 /*
2049 * Close the child ends of any pipes and redirected files.
2050 */
2051 int rc2 = RTHandleClose(pSession->StdIn.phChild); AssertRC(rc2);
2052 pSession->StdIn.phChild = NULL;
2053 rc2 = RTHandleClose(pSession->StdOut.phChild); AssertRC(rc2);
2054 pSession->StdOut.phChild = NULL;
2055 rc2 = RTHandleClose(pSession->StdErr.phChild); AssertRC(rc2);
2056 pSession->StdErr.phChild = NULL;
2057 }
2058 }
2059 }
2060#else
2061 RTHANDLE hStdIn;
2062 if (RT_SUCCESS(rc))
2063 rc = RTFileOpenBitBucket(&hStdIn.u.hFile, RTFILE_O_READ);
2064 if (RT_SUCCESS(rc))
2065 {
2066 hStdIn.enmType = RTHANDLETYPE_FILE;
2067
2068 RTHANDLE hStdOutAndErr;
2069 rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
2070 if (RT_SUCCESS(rc))
2071 {
2072 hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
2073
2074 rc = RTProcCreateEx(pszExeName, papszArgs, hEnv, uProcFlags,
2075 &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
2076 !fAnonymous ? pSessionThread->StartupInfo.szUser : NULL,
2077 !fAnonymous ? pSessionThread->StartupInfo.szPassword : NULL,
2078 &pSessionThread->hProcess);
2079
2080 RTFileClose(hStdOutAndErr.u.hFile);
2081 }
2082
2083 RTFileClose(hStdIn.u.hFile);
2084 }
2085#endif
2086 if (hEnv != NIL_RTENV)
2087 RTEnvDestroy(hEnv);
2088 }
2089 else
2090 rc = VERR_FILE_NOT_FOUND;
2091
2092 if (RT_SUCCESS(rc))
2093 {
2094 /* Start session thread. */
2095 rc = RTThreadCreateF(&pSessionThread->Thread, gstcntlSessionThread,
2096 pSessionThread /*pvUser*/, 0 /*cbStack*/,
2097 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "sess%u", s_uCtrlSessionThread);
2098 if (RT_FAILURE(rc))
2099 {
2100 VBoxServiceError("Creating session thread failed, rc=%Rrc\n", rc);
2101 }
2102 else
2103 {
2104 /* Wait for the thread to initialize. */
2105 rc = RTThreadUserWait(pSessionThread->Thread, 60 * 1000 /* 60s timeout */);
2106 if ( ASMAtomicReadBool(&pSessionThread->fShutdown)
2107 || RT_FAILURE(rc))
2108 {
2109 VBoxServiceError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
2110 pSessionThread->StartupInfo.uSessionID, rc);
2111 if (RT_SUCCESS(rc))
2112 rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
2113 }
2114 else
2115 {
2116 VBoxServiceVerbose(2, "Thread for session ID=%RU32 started\n",
2117 pSessionThread->StartupInfo.uSessionID);
2118
2119 ASMAtomicXchgBool(&pSessionThread->fStarted, true);
2120
2121 /* Add session to list. */
2122 /* rc = */ RTListAppend(pList, &pSessionThread->Node);
2123 if (ppSessionThread) /* Return session if wanted. */
2124 *ppSessionThread = pSessionThread;
2125 }
2126 }
2127 }
2128
2129 if (RT_FAILURE(rc))
2130 {
2131 RTMemFree(pSessionThread);
2132 }
2133 }
2134 else
2135 rc = VERR_NO_MEMORY;
2136
2137 VBoxServiceVerbose(3, "Spawning session thread returned returned rc=%Rrc\n", rc);
2138 return rc;
2139}
2140
2141
2142/**
2143 * Waits for a formerly opened guest session process to close.
2144 *
2145 * @return IPRT status code.
2146 * @param pThread Guest session thread to wait for.
2147 * @param uTimeoutMS Waiting timeout (in ms).
2148 * @param uFlags Closing flags.
2149 */
2150int GstCntlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread,
2151 uint32_t uTimeoutMS, uint32_t uFlags)
2152{
2153 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2154 /** @todo Validate closing flags. */
2155
2156 if (pThread->Thread == NIL_RTTHREAD)
2157 {
2158 AssertMsgFailed(("Guest session thread of session %p does not exist when it should\n",
2159 pThread));
2160 return VERR_NOT_FOUND;
2161 }
2162
2163 int rc = VINF_SUCCESS;
2164
2165 /*
2166 * The spawned session process should have received the same closing request,
2167 * so just wait for the process to close.
2168 */
2169 if (ASMAtomicReadBool(&pThread->fStarted))
2170 {
2171 /* Ask the thread to shutdown. */
2172 ASMAtomicXchgBool(&pThread->fShutdown, true);
2173
2174 VBoxServiceVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
2175 pThread->StartupInfo.uSessionID, uTimeoutMS);
2176
2177 int rcThread;
2178 rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
2179 if (RT_FAILURE(rc))
2180 {
2181 VBoxServiceError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n",
2182 pThread->StartupInfo.uSessionID, rc);
2183 }
2184 else
2185 VBoxServiceVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n",
2186 pThread->StartupInfo.uSessionID, rcThread);
2187 }
2188
2189 return rc;
2190}
2191
2192/**
2193 * Waits for the specified session thread to end and remove
2194 * it from the session thread list.
2195 *
2196 * @return IPRT status code.
2197 * @param pThread Session thread to destroy.
2198 * @param uFlags Closing flags.
2199 */
2200int GstCntlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uFlags)
2201{
2202 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2203
2204 int rc = GstCntlSessionThreadWait(pThread,
2205 5 * 60 * 1000 /* 5 minutes timeout */, uFlags);
2206
2207 /* Remove session from list and destroy object. */
2208 RTListNodeRemove(&pThread->Node);
2209
2210 RTMemFree(pThread);
2211 pThread = NULL;
2212
2213 return rc;
2214}
2215
2216/**
2217 * Close all open guest session threads.
2218 *
2219 * @note Caller is responsible for locking!
2220 *
2221 * @return IPRT status code.
2222 * @param pList Which list to close the session threads for.
2223 * @param uFlags Closing flags.
2224 */
2225int GstCntlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t uFlags)
2226{
2227 AssertPtrReturn(pList, VERR_INVALID_POINTER);
2228
2229 int rc = VINF_SUCCESS;
2230
2231 /*int rc = VbglR3GuestCtrlClose
2232 if (RT_FAILURE(rc))
2233 VBoxServiceError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/
2234
2235 PVBOXSERVICECTRLSESSIONTHREAD pSessIt;
2236 PVBOXSERVICECTRLSESSIONTHREAD pSessItNext;
2237 RTListForEachSafe(pList, pSessIt, pSessItNext, VBOXSERVICECTRLSESSIONTHREAD, Node)
2238 {
2239 int rc2 = GstCntlSessionThreadDestroy(pSessIt, uFlags);
2240 if (RT_FAILURE(rc2))
2241 {
2242 VBoxServiceError("Closing session thread '%s' failed with rc=%Rrc\n", RTThreadGetName(pSessIt->Thread), rc2);
2243 if (RT_SUCCESS(rc))
2244 rc = rc2;
2245 /* Keep going. */
2246 }
2247 }
2248
2249 VBoxServiceVerbose(4, "Destroying guest session threads ended with %Rrc\n", rc);
2250 return rc;
2251}
2252
2253RTEXITCODE VBoxServiceControlSessionSpawnInit(int argc, char **argv)
2254{
2255 static const RTGETOPTDEF s_aOptions[] =
2256 {
2257#ifdef DEBUG
2258 { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING },
2259 { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING },
2260#endif
2261 { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
2262 { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
2263 { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
2264 { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
2265#ifdef DEBUG
2266 { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 },
2267#endif /* DEBUG */
2268 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
2269 };
2270
2271 int ch;
2272 RTGETOPTUNION ValueUnion;
2273 RTGETOPTSTATE GetState;
2274 RTGetOptInit(&GetState, argc, argv,
2275 s_aOptions, RT_ELEMENTS(s_aOptions),
2276 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2277
2278 uint32_t uSessionFlags = VBOXSERVICECTRLSESSION_FLAG_SPAWN;
2279
2280 /* Protocol and session ID must be specified explicitly. */
2281 g_Session.StartupInfo.uProtocol = UINT32_MAX;
2282 g_Session.StartupInfo.uSessionID = UINT32_MAX;
2283
2284 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
2285 {
2286 /* For options that require an argument, ValueUnion has received the value. */
2287 switch (ch)
2288 {
2289 case VBOXSERVICESESSIONOPT_LOG_FILE:
2290 {
2291 int rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
2292 if (RT_FAILURE(rc))
2293 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error copying log file name: %Rrc", rc);
2294 break;
2295 }
2296#ifdef DEBUG
2297 case VBOXSERVICESESSIONOPT_DUMP_STDOUT:
2298 uSessionFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
2299 break;
2300
2301 case VBOXSERVICESESSIONOPT_DUMP_STDERR:
2302 uSessionFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
2303 break;
2304#endif
2305 case VBOXSERVICESESSIONOPT_USERNAME:
2306 /* Information not needed right now, skip. */
2307 break;
2308
2309 case VBOXSERVICESESSIONOPT_SESSION_ID:
2310 g_Session.StartupInfo.uSessionID = ValueUnion.u32;
2311 break;
2312
2313 case VBOXSERVICESESSIONOPT_SESSION_PROTO:
2314 g_Session.StartupInfo.uProtocol = ValueUnion.u32;
2315 break;
2316
2317#ifdef DEBUG
2318 case VBOXSERVICESESSIONOPT_THREAD_ID:
2319 /* Not handled. Mainly for processs listing. */
2320 break;
2321#endif
2322 /** @todo Implement help? */
2323
2324 case 'v':
2325 g_cVerbosity++;
2326 break;
2327
2328 case VINF_GETOPT_NOT_OPTION:
2329 /* Ignore; might be "guestsession" main command. */
2330 /** @todo r=bird: We DO NOT ignore stuff on the command line! */
2331 break;
2332
2333 default:
2334 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown command '%s'", ValueUnion.psz);
2335 }
2336 }
2337
2338 /* Check that we've got all the required options. */
2339 if (g_Session.StartupInfo.uProtocol == UINT32_MAX)
2340 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified");
2341
2342 if (g_Session.StartupInfo.uSessionID == UINT32_MAX)
2343 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
2344
2345 /* Init the session object. */
2346 int rc = GstCntlSessionInit(&g_Session, uSessionFlags);
2347 if (RT_FAILURE(rc))
2348 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to initialize session object, rc=%Rrc\n", rc);
2349
2350 rc = VBoxServiceLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
2351 if (RT_FAILURE(rc))
2352 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to create log file \"%s\", rc=%Rrc\n",
2353 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
2354
2355 RTEXITCODE rcExit = gstcntlSessionSpawnWorker(&g_Session);
2356
2357 VBoxServiceLogDestroy();
2358 return rcExit;
2359}
2360
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