VirtualBox

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

Last change on this file since 47559 was 47551, checked in by vboxsync, 12 years ago

VBoxService/GuestCtrl: Re-added (optional debug) dumping of guest process streams, now as a (forked) per-session option.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette