VirtualBox

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

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

VBoxService/VBoxServiceControlSession: Close guest file on opening error.

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