VirtualBox

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

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

VBoxService/GuestCtrl: More message filtering for guest session + process threads; message processing not needed there, logging.

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