VirtualBox

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

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

IPRT: Filename extension versus suffix cleanup, long overdue.

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