VirtualBox

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

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

*: scm cleanup run.

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