VirtualBox

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

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

VBoxService/VBoxServiceControlSession.cpp: Fixed logging / shadowed variables.

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