VirtualBox

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

Last change on this file since 55628 was 55581, checked in by vboxsync, 10 years ago

VBoxServiceControlSession.cpp: A few more constructive code review comments.

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