VirtualBox

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

Last change on this file since 45547 was 45415, checked in by vboxsync, 12 years ago

GuestCtrl: Implemented using (public) VirtualBox events instead of own callback mechanisms. Bugfixes for testcases (still work in progress).

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