VirtualBox

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

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

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/* $Id: VBoxServiceControlSession.cpp 44869 2013-02-28 15:42:02Z 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/assert.h>
24#include <iprt/env.h>
25#include <iprt/file.h>
26#include <iprt/getopt.h>
27#include <iprt/handle.h>
28#include <iprt/mem.h>
29#include <iprt/message.h>
30#include <iprt/path.h>
31#include <iprt/pipe.h>
32#include <iprt/poll.h>
33#include <iprt/process.h>
34
35#include "VBoxServiceInternal.h"
36#include "VBoxServiceUtils.h"
37#include "VBoxServiceControl.h"
38
39using namespace guestControl;
40
41/** List of guest control sessions (VBOXSERVICECTRLSESSION). */
42extern RTLISTANCHOR g_lstControlSessions;
43extern int VBoxServiceLogCreate(const char *pszLogFile);
44extern void VBoxServiceLogDestroy(void);
45
46/*******************************************************************************
47* Internal Functions *
48*******************************************************************************/
49static int gstcntlSessionFileDestroy(PVBOXSERVICECTRLFILE pFile);
50static int gstcntlSessionForkShutdown(uint32_t uClientId, uint32_t cParms);
51static PVBOXSERVICECTRLFILE gstcntlSessionGetFile(uint32_t uHandle);
52static int gstcntlSessionHandleFileOpen(uint32_t uClientId, uint32_t cParms);
53static int gstcntlSessionHandleFileClose(uint32_t uClientId, uint32_t cParms);
54static int gstcntlSessionHandleFileRead(uint32_t uClientId, uint32_t cParms);
55static int gstcntlSessionHandleFileWrite(uint32_t uClientId, uint32_t cParms, void *pvScratchBuf, size_t cbScratchBuf);
56static int gstcntlSessionHandleFileSeek(uint32_t uClientId, uint32_t cParms);
57static int gstcntlSessionHandleFileTell(uint32_t uClientId, uint32_t cParms);
58
59/** The session ID of the forked session process. */
60static uint32_t g_uSessionID = UINT32_MAX;
61/** The session HGCM protocol of the forked session process. */
62static uint32_t g_uSessionProto = 1; /* Use the legacy protocol by default (< VBox 4.3). */
63/** List of guest control files (VBOXSERVICECTRLFILE). */
64static RTLISTANCHOR g_lstSessionFiles;
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};
74
75
76static int gstcntlSessionFileDestroy(PVBOXSERVICECTRLFILE pFile)
77{
78 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
79
80 int rc = RTFileClose(pFile->hFile);
81 if (RT_SUCCESS(rc))
82 {
83 /* Remove file entry in any case. */
84 RTListNodeRemove(&pFile->Node);
85 /* Destroy this object. */
86 RTMemFree(pFile);
87 }
88
89 return rc;
90}
91
92
93static PVBOXSERVICECTRLFILE gstcntlSessionGetFile(uint32_t uHandle)
94{
95 PVBOXSERVICECTRLFILE pFileCur = NULL;
96 /** @todo Use a map later! */
97 RTListForEach(&g_lstSessionFiles, pFileCur, VBOXSERVICECTRLFILE, Node)
98 {
99 if (pFileCur->uHandle == uHandle)
100 return pFileCur;
101 }
102
103 return NULL;
104}
105
106
107static int gstcntlSessionHandleFileOpen(uint32_t uClientId, uint32_t cParms)
108{
109 char szFile[RTPATH_MAX];
110 char szOpenMode[64];
111 char szDisposition[64];
112 uint32_t uCreationMode = 0;
113 uint64_t uOffset = 0;
114
115 uint32_t uHandle = 0;
116 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
117 int rc = VbglR3GuestCtrlFileGetOpen(&ctx,
118 /* File to open. */
119 szFile, sizeof(szFile),
120 /* Open mode. */
121 szOpenMode, sizeof(szOpenMode),
122 /* Disposition. */
123 szDisposition, sizeof(szDisposition),
124 /* Creation mode. */
125 &uCreationMode,
126 /* Offset. */
127 &uOffset);
128 if (RT_SUCCESS(rc))
129 {
130 PVBOXSERVICECTRLFILE pFile = (PVBOXSERVICECTRLFILE)RTMemAlloc(sizeof(VBOXSERVICECTRLFILE));
131 if (pFile)
132 {
133 if (!RTStrPrintf(pFile->szName, sizeof(pFile->szName), "%s", szFile))
134 rc = VERR_BUFFER_OVERFLOW;
135
136 if (RT_SUCCESS(rc))
137 {
138 uint64_t fFlags = RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; /** @todo Modes! */
139 rc = RTFileOpen(&pFile->hFile, pFile->szName, fFlags);
140 if ( RT_SUCCESS(rc)
141 && uOffset)
142 {
143 /* Seeking is optional. */
144 int rc2 = RTFileSeek(pFile->hFile, (int64_t)uOffset, RTFILE_SEEK_BEGIN, NULL /* Current offset */);
145 if (RT_FAILURE(rc2))
146 VBoxServiceVerbose(3, "[File %s]: Seeking to offset %RU64 failed; rc=%Rrc\n",
147 pFile->szName, uOffset, rc);
148 }
149 else
150 VBoxServiceVerbose(3, "[File %s]: Opening failed; rc=%Rrc\n",
151 pFile->szName, rc);
152 }
153
154 if (RT_SUCCESS(rc))
155 {
156 uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(ctx.uContextID);
157 pFile->uHandle = uHandle;
158 /* rc = */ RTListAppend(&g_lstSessionFiles, &pFile->Node);
159
160 VBoxServiceVerbose(3, "[File %s]: Opened (ID=%RU32)\n",
161 pFile->szName, pFile->uHandle);
162 }
163
164 if (RT_FAILURE(rc))
165 RTMemFree(pFile);
166 }
167 else
168 rc = VERR_NO_MEMORY;
169
170 /* Report back in any case. */
171 CALLBACKPAYLOAD_FILE_NOTFIY_OPEN cplOpen = { rc, uHandle };
172 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_OPEN,
173 &cplOpen, sizeof(cplOpen));
174 if (RT_FAILURE(rc2))
175 VBoxServiceError("[File %s]: Failed to report file open status, rc=%Rrc\n",
176 szFile, rc2);
177 if (RT_SUCCESS(rc))
178 rc = rc2;
179 }
180
181 return rc;
182}
183
184
185static int gstcntlSessionHandleFileClose(uint32_t uClientId, uint32_t cParms)
186{
187 uint32_t uHandle;
188
189 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
190 int rc = VbglR3GuestCtrlFileGetClose(&ctx, &uHandle /* File handle to close */);
191 if (RT_SUCCESS(rc))
192 {
193 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
194 if (pFile)
195 {
196 rc = gstcntlSessionFileDestroy(pFile);
197 }
198 else
199 rc = VERR_NOT_FOUND;
200
201 /* Report back in any case. */
202 CALLBACKPAYLOAD_FILE_NOTFIY_CLOSE cplClose = { rc };
203 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_CLOSE,
204 &cplClose, sizeof(cplClose));
205 if (RT_FAILURE(rc2))
206 VBoxServiceError("Failed to report file close status, rc=%Rrc\n", rc2);
207 if (RT_SUCCESS(rc))
208 rc = rc2;
209 }
210 return rc;
211}
212
213
214static int gstcntlSessionHandleFileRead(uint32_t uClientId, uint32_t cParms,
215 void *pvScratchBuf, size_t cbScratchBuf)
216{
217 uint32_t uHandle;
218 uint32_t cbToRead;
219
220 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
221 int rc = VbglR3GuestCtrlFileGetRead(&ctx, &uHandle, &cbToRead);
222 if (RT_SUCCESS(rc))
223 {
224 void *pvDataRead = pvScratchBuf;
225 size_t cbRead = 0;
226
227 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
228 if (pFile)
229 {
230 if (cbToRead)
231 {
232 if (cbToRead > cbScratchBuf)
233 {
234 pvDataRead = RTMemAlloc(cbToRead);
235 if (!pvDataRead)
236 rc = VERR_NO_MEMORY;
237 }
238
239 if (RT_LIKELY(RT_SUCCESS(rc)))
240 rc = RTFileRead(pFile->hFile, pvDataRead, cbToRead, &cbRead);
241 }
242 else
243 rc = VERR_BUFFER_UNDERFLOW;
244 }
245 else
246 rc = VERR_NOT_FOUND;
247
248 /* Report back in any case. */
249 CALLBACKPAYLOAD_FILE_NOTFIY_READ cplRead = { rc, cbRead, pvDataRead };
250 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_READ,
251 &cplRead, sizeof(cplRead));
252 if ( cbToRead > cbScratchBuf
253 && pvDataRead)
254 RTMemFree(pvDataRead);
255
256 if (RT_FAILURE(rc2))
257 VBoxServiceError("Failed to report file read status, rc=%Rrc\n", rc2);
258 if (RT_SUCCESS(rc))
259 rc = rc2;
260 }
261 return rc;
262}
263
264
265static int gstcntlSessionHandleFileReadAt(uint32_t uClientId, uint32_t cParms,
266 void *pvScratchBuf, size_t cbScratchBuf)
267{
268 uint32_t uHandle;
269 uint32_t cbToRead; int64_t iOffset;
270
271 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
272 int rc = VbglR3GuestCtrlFileGetReadAt(&ctx, &uHandle, &cbToRead, (uint64_t*)&iOffset);
273 if (RT_SUCCESS(rc))
274 {
275 void *pvDataRead = pvScratchBuf;
276 size_t cbRead = 0;
277
278 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
279 if (pFile)
280 {
281 if (cbToRead)
282 {
283 if (cbToRead > cbScratchBuf)
284 {
285 pvDataRead = RTMemAlloc(cbToRead);
286 if (!pvDataRead)
287 rc = VERR_NO_MEMORY;
288 }
289
290 if (RT_LIKELY(RT_SUCCESS(rc)))
291 rc = RTFileReadAt(pFile->hFile, iOffset, pvDataRead, cbToRead, &cbRead);
292 }
293 else
294 rc = VERR_BUFFER_UNDERFLOW;
295 }
296 else
297 rc = VERR_NOT_FOUND;
298
299 /* Report back in any case. */
300 CALLBACKPAYLOAD_FILE_NOTFIY_READ cplRead = { rc, cbRead, pvDataRead };
301 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_READ,
302 &cplRead, sizeof(cplRead));
303 if ( cbToRead > cbScratchBuf
304 && pvDataRead)
305 RTMemFree(pvDataRead);
306
307 if (RT_FAILURE(rc2))
308 VBoxServiceError("Failed to report file read status, rc=%Rrc\n", rc2);
309 if (RT_SUCCESS(rc))
310 rc = rc2;
311 }
312 return rc;
313}
314
315
316static int gstcntlSessionHandleFileWrite(uint32_t uClientId, uint32_t cParms,
317 void *pvScratchBuf, size_t cbScratchBuf)
318{
319 AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER);
320 AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER);
321
322 uint32_t uHandle;
323 uint32_t cbToWrite;
324
325 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
326 int rc = VbglR3GuestCtrlFileGetWrite(&ctx, &uHandle,
327 pvScratchBuf, cbScratchBuf,
328 &cbToWrite);
329 if (RT_SUCCESS(rc))
330 {
331 size_t cbWritten = 0;
332 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
333 if (pFile)
334 {
335 rc = RTFileWrite(pFile->hFile, pvScratchBuf, cbScratchBuf, &cbWritten);
336 }
337 else
338 rc = VERR_NOT_FOUND;
339
340 /* Report back in any case. */
341 CALLBACKPAYLOAD_FILE_NOTFIY_WRITE cplWrite = { rc, (uint32_t)cbWritten };
342 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_WRITE,
343 &cplWrite, sizeof(cplWrite));
344 if (RT_FAILURE(rc2))
345 VBoxServiceError("Failed to report file write status, rc=%Rrc\n", rc2);
346 if (RT_SUCCESS(rc))
347 rc = rc2;
348 }
349 return rc;
350}
351
352
353static int gstcntlSessionHandleFileWriteAt(uint32_t uClientId, uint32_t cParms,
354 void *pvScratchBuf, size_t cbScratchBuf)
355{
356 AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER);
357 AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER);
358
359 uint32_t uHandle;
360 uint32_t cbToWrite; int64_t iOffset;
361
362 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
363 int rc = VbglR3GuestCtrlFileGetWriteAt(&ctx, &uHandle,
364 pvScratchBuf, cbScratchBuf,
365 &cbToWrite, (uint64_t*)&iOffset);
366 if (RT_SUCCESS(rc))
367 {
368 size_t cbWritten = 0;
369 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
370 if (pFile)
371 {
372 rc = RTFileWriteAt(pFile->hFile, iOffset,
373 pvScratchBuf, cbScratchBuf, &cbWritten);
374 }
375 else
376 rc = VERR_NOT_FOUND;
377
378 /* Report back in any case. */
379 CALLBACKPAYLOAD_FILE_NOTFIY_WRITE cplWrite = { rc, (uint32_t)cbWritten };
380 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_WRITE,
381 &cplWrite, sizeof(cplWrite));
382 if (RT_FAILURE(rc2))
383 VBoxServiceError("Failed to report file write status, rc=%Rrc\n", rc2);
384 if (RT_SUCCESS(rc))
385 rc = rc2;
386 }
387 return rc;
388}
389
390
391static int gstcntlSessionHandleFileSeek(uint32_t uClientId, uint32_t cParms)
392{
393 uint32_t uHandle;
394 uint32_t uSeekMethod;
395 uint64_t uOffset; /* Will be converted to int64_t. */
396
397 uint64_t uOffsetActual = 0;
398
399 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
400 int rc = VbglR3GuestCtrlFileGetSeek(&ctx, &uHandle,
401 &uSeekMethod, &uOffset);
402 if (RT_SUCCESS(rc))
403 {
404 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
405 if (pFile)
406 {
407 unsigned uSeekMethodIPRT;
408 switch (uSeekMethod)
409 {
410 case GUEST_FILE_SEEKTYPE_BEGIN:
411 uSeekMethodIPRT = RTFILE_SEEK_BEGIN;
412 break;
413
414 case GUEST_FILE_SEEKTYPE_CURRENT:
415 uSeekMethodIPRT = RTFILE_SEEK_CURRENT;
416 break;
417
418 case GUEST_FILE_SEEKTYPE_END:
419 uSeekMethodIPRT = RTFILE_SEEK_END;
420 break;
421
422 default:
423 rc = VERR_NOT_SUPPORTED;
424 break;
425 }
426
427 if (RT_SUCCESS(rc))
428 rc = RTFileSeek(pFile->hFile, (int64_t)uOffset,
429 uSeekMethodIPRT, &uOffsetActual);
430 }
431 else
432 rc = VERR_NOT_FOUND;
433
434 /* Report back in any case. */
435 CALLBACKPAYLOAD_FILE_NOTFIY_SEEK cplSeek = { rc, uOffsetActual };
436 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_SEEK,
437 &cplSeek, sizeof(cplSeek));
438 if (RT_FAILURE(rc2))
439 VBoxServiceError("Failed to report file seek status, rc=%Rrc\n", rc2);
440 if (RT_SUCCESS(rc))
441 rc = rc2;
442 }
443 return rc;
444}
445
446
447static int gstcntlSessionHandleFileTell(uint32_t uClientId, uint32_t cParms)
448{
449 uint32_t uHandle;
450 uint64_t uOffsetActual = 0;
451
452 VBGLR3GUESTCTRLHOSTCTX ctx = { uClientId, cParms };
453 int rc = VbglR3GuestCtrlFileGetTell(&ctx, &uHandle);
454 if (RT_SUCCESS(rc))
455 {
456 PVBOXSERVICECTRLFILE pFile = gstcntlSessionGetFile(uHandle);
457 if (pFile)
458 {
459 uOffsetActual = RTFileTell(pFile->hFile);
460 }
461 else
462 rc = VERR_NOT_FOUND;
463
464 /* Report back in any case. */
465 CALLBACKPAYLOAD_FILE_NOTFIY_TELL cplTell = { rc, uOffsetActual };
466 int rc2 = VbglR3GuestCtrlFileNotify(uClientId, ctx.uContextID, GUEST_FILE_NOTIFYTYPE_TELL,
467 &cplTell, sizeof(cplTell));
468 if (RT_FAILURE(rc2))
469 VBoxServiceError("Failed to report file tell status, rc=%Rrc\n", rc2);
470 if (RT_SUCCESS(rc))
471 rc = rc2;
472 }
473 return rc;
474}
475
476
477RTEXITCODE gstcntlSessionWorker(void)
478{
479 bool fSessionFilter = true;
480
481 uint32_t uClientID;
482 int rc = VbglR3GuestCtrlConnect(&uClientID);
483 if (RT_SUCCESS(rc))
484 {
485 /* Set session filter. */
486 uint32_t uFilterAdd = VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(g_uSessionID);
487
488 rc = VbglR3GuestCtrlMsgSetFilter(uClientID, uFilterAdd, 0 /* Filter remove */);
489 if ( RT_FAILURE(rc)
490 && rc == VERR_NOT_SUPPORTED)
491 {
492 /* No session filter available. Skip. */
493 fSessionFilter = false;
494
495 rc = VINF_SUCCESS;
496 }
497 }
498
499 /* Allocate a scratch buffer for commands which also send
500 * payload data with them. */
501 uint32_t cbScratchBuf = _64K; /** @todo Make buffer size configurable via guest properties/argv! */
502 AssertReturn(RT_IS_POWER_OF_TWO(cbScratchBuf), RTEXITCODE_FAILURE);
503 uint8_t *pvScratchBuf = NULL;
504
505 if (RT_SUCCESS(rc))
506 {
507 RTListInit(&g_lstSessionFiles);
508 pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf);
509 if (!pvScratchBuf)
510 rc = VERR_NO_MEMORY;
511 }
512
513 if (RT_SUCCESS(rc))
514 {
515 bool fShutdown = false;
516
517 for (;;)
518 {
519 VBoxServiceVerbose(3, "Waiting for host msg ...\n");
520 uint32_t uMsg = 0;
521 uint32_t cParms = 0;
522 rc = VbglR3GuestCtrlMsgWaitFor(uClientID, &uMsg, &cParms);
523 if (rc == VERR_TOO_MUCH_DATA)
524 {
525 VBoxServiceVerbose(4, "Message requires %RU32 parameters, but only 2 supplied -- retrying request (no error!)...\n", cParms);
526 rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */
527 }
528 else if (RT_FAILURE(rc))
529 VBoxServiceVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
530 if (RT_SUCCESS(rc))
531 {
532 VBoxServiceVerbose(3, "Msg=%RU32 (%RU32 parms) retrieved\n", uMsg, cParms);
533 }
534
535 VBoxServiceVerbose(3, "Msg=%RU32 (%RU32 parms) retrieved\n", uMsg, cParms);
536
537 /** @todo Guest session ID change detection? */
538
539 switch (uMsg)
540 {
541 case HOST_CANCEL_PENDING_WAITS:
542 /* Fall thru is intentional. */
543 case HOST_SESSION_CLOSE:
544 /* Shutdown this fork. */
545 rc = gstcntlSessionForkShutdown(uClientID, cParms);
546 fShutdown = true; /* Shutdown in any case. */
547 break;
548
549 case HOST_FILE_OPEN:
550 rc = gstcntlSessionHandleFileOpen(uClientID, cParms);
551 break;
552
553 case HOST_FILE_CLOSE:
554 rc = gstcntlSessionHandleFileClose(uClientID, cParms);
555 break;
556
557 case HOST_FILE_READ:
558 rc = gstcntlSessionHandleFileRead(uClientID, cParms,
559 pvScratchBuf, cbScratchBuf);
560 break;
561
562 case HOST_FILE_READ_AT:
563 rc = gstcntlSessionHandleFileReadAt(uClientID, cParms,
564 pvScratchBuf, cbScratchBuf);
565 break;
566
567 case HOST_FILE_WRITE:
568 rc = gstcntlSessionHandleFileWrite(uClientID, cParms,
569 pvScratchBuf, cbScratchBuf);
570 break;
571
572 case HOST_FILE_WRITE_AT:
573 rc = gstcntlSessionHandleFileWriteAt(uClientID, cParms,
574 pvScratchBuf, cbScratchBuf);
575 break;
576
577 case HOST_FILE_SEEK:
578 rc = gstcntlSessionHandleFileSeek(uClientID, cParms);
579 break;
580
581 case HOST_FILE_TELL:
582 rc = gstcntlSessionHandleFileTell(uClientID, cParms);
583 break;
584
585 default:
586 VBoxServiceVerbose(3, "Unsupported message from host, uMsg=%RU32, cParms=%RU32\n",
587 uMsg, cParms);
588 /* Don't terminate here; just wait for the next message. */
589 break;
590 }
591
592 if (fShutdown)
593 break;
594 }
595 }
596
597 VBoxServiceVerbose(0, "Session %RU32 ended\n", g_uSessionID);
598
599 if (pvScratchBuf)
600 RTMemFree(pvScratchBuf);
601
602 VBoxServiceVerbose(3, "Disconnecting client ID=%RU32 ...\n", uClientID);
603 VbglR3GuestCtrlDisconnect(uClientID);
604
605 VBoxServiceVerbose(3, "Session worker returned with rc=%Rrc\n", rc);
606 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
607}
608
609
610/**
611 * Creates a guest session. This will spawn a new VBoxService.exe instance under
612 * behalf of the given user which then will act as a session host.
613 *
614 * @return IPRT status code.
615 * @param pSessionStartupInfo Session startup info.
616 * @param pNode Returns newly created session node on success.
617 * Optional.
618 */
619int GstCntlSessionOpen(const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
620 PRTLISTNODE pNode)
621{
622 AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
623 /* pNode is optional. */
624
625#ifdef DEBUG
626 PVBOXSERVICECTRLSESSION pSessionCur;
627 /* Check for existing session in debug mode. Should never happen because of
628 * Main consistency. */
629 RTListForEach(&g_lstControlSessions, pSessionCur, VBOXSERVICECTRLSESSION, Node)
630 {
631 if (pSessionCur->StartupInfo.uSessionID == pSessionStartupInfo->uSessionID)
632 {
633 AssertMsgFailed(("Guest session %RU32 (%p) already exists when it should not\n",
634 pSessionCur->StartupInfo.uSessionID, pSessionCur));
635 return VERR_ALREADY_EXISTS;
636 }
637 }
638#endif
639 int rc = VINF_SUCCESS;
640
641 PVBOXSERVICECTRLSESSION pSession = (PVBOXSERVICECTRLSESSION)RTMemAllocZ(sizeof(VBOXSERVICECTRLSESSION));
642 if (pSession)
643 {
644 /* Copy over session startup info. */
645 memcpy(&pSession->StartupInfo, pSessionStartupInfo, sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
646
647 VBoxServiceVerbose(3, "Forking new guest session szUser=%s, szPassword=%s, szDomain=%s, uFlags=%x, using protocol %RU32\n",
648 pSessionStartupInfo->szUser,
649#ifdef DEBUG
650 pSessionStartupInfo->szPassword,
651#else
652 "XXX", /* Never show passwords in release mode. */
653#endif
654 pSessionStartupInfo->szDomain,
655 pSessionStartupInfo->uFlags,
656 pSessionStartupInfo->uProtocol);
657
658 rc = RTCritSectInit(&pSession->CritSect);
659 AssertRC(rc);
660
661 /* Fork child doing the actual session handling. */
662 char szExeName[RTPATH_MAX];
663 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
664 if (pszExeName)
665 {
666 char szParmUserName[GUESTPROCESS_MAX_USER_LEN + 32];
667 if (!RTStrPrintf(szParmUserName, sizeof(szParmUserName), "--username=%s", pSession->StartupInfo.szUser))
668 rc = VERR_BUFFER_OVERFLOW;
669 char szParmSessionID[32];
670 if (RT_SUCCESS(rc) && !RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32",
671 pSession->StartupInfo.uSessionID))
672 {
673 rc = VERR_BUFFER_OVERFLOW;
674 }
675 char szParmSessionProto[32];
676 if (RT_SUCCESS(rc) && !RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
677 pSession->StartupInfo.uProtocol))
678 {
679 rc = VERR_BUFFER_OVERFLOW;
680 }
681
682 if (RT_SUCCESS(rc))
683 {
684 int iOptIdx = 0; /* Current index in argument vector. */
685
686 char const *papszArgs[8];
687 papszArgs[iOptIdx++] = pszExeName;
688 papszArgs[iOptIdx++] = "guestsession";
689 papszArgs[iOptIdx++] = szParmSessionID;
690 papszArgs[iOptIdx++] = szParmSessionProto;
691 papszArgs[iOptIdx++] = szParmUserName;
692
693 /* Add same verbose flags as parent process. */
694 int rc2 = VINF_SUCCESS;
695 char szParmVerbose[32] = { 0 };
696 for (int i = 0; i < g_cVerbosity && RT_SUCCESS(rc2); i++)
697 {
698 if (i == 0)
699 rc2 = RTStrCat(szParmVerbose, sizeof(szParmVerbose), "-");
700 if (RT_FAILURE(rc2))
701 break;
702 rc2 = RTStrCat(szParmVerbose, sizeof(szParmVerbose), "v");
703 }
704 if (RT_SUCCESS(rc2))
705 papszArgs[iOptIdx++] = szParmVerbose;
706
707 /* Add log file handling. Each session will have an own
708 * log file, naming based on the parent log file. */
709 char szParmLogFile[RTPATH_MAX];
710 if ( RT_SUCCESS(rc2)
711 && strlen(g_szLogFile))
712 {
713 char *pszLogFile = RTStrDup(g_szLogFile);
714 if (pszLogFile)
715 {
716 char *pszLogExt = NULL;
717 if (RTPathHasExt(pszLogFile))
718 pszLogExt = RTStrDup(RTPathExt(pszLogFile));
719 RTPathStripExt(pszLogFile);
720 char *pszLogSuffix;
721 if (RTStrAPrintf(&pszLogSuffix, "-%RU32-%s",
722 pSessionStartupInfo->uSessionID,
723 pSessionStartupInfo->szUser) < 0)
724 {
725 rc2 = VERR_NO_MEMORY;
726 }
727 else
728 {
729 rc2 = RTStrAAppend(&pszLogFile, pszLogSuffix);
730 if (RT_SUCCESS(rc2) && pszLogExt)
731 rc2 = RTStrAAppend(&pszLogFile, pszLogExt);
732 if (RT_SUCCESS(rc2))
733 {
734 if (!RTStrPrintf(szParmLogFile, sizeof(szParmLogFile),
735 "--logfile %s", pszLogFile))
736 {
737 rc2 = VERR_BUFFER_OVERFLOW;
738 }
739 }
740 RTStrFree(pszLogSuffix);
741 }
742 if (RT_FAILURE(rc2))
743 VBoxServiceError("Error building session logfile string for session %RU32 (user %s), rc=%Rrc\n",
744 pSessionStartupInfo->uSessionID, pSessionStartupInfo->szUser, rc2);
745 if (pszLogExt)
746 RTStrFree(pszLogExt);
747 RTStrFree(pszLogFile);
748 }
749 if (RT_SUCCESS(rc2))
750 papszArgs[iOptIdx++] = szParmLogFile;
751 papszArgs[iOptIdx++] = NULL;
752 }
753 else
754 papszArgs[iOptIdx++] = NULL;
755
756 if (g_cVerbosity > 3)
757 {
758 VBoxServiceVerbose(4, "Forking parameters:\n");
759
760 iOptIdx = 0;
761 while (papszArgs[iOptIdx])
762 VBoxServiceVerbose(4, "\t%s\n", papszArgs[iOptIdx++]);
763 }
764
765 uint32_t uProcFlags = RTPROC_FLAGS_SERVICE
766 | RTPROC_FLAGS_HIDDEN; /** @todo More flags from startup info? */
767
768#if 0 /* Pipe handling not needed (yet). */
769 /* Setup pipes. */
770 rc = GstcntlProcessSetupPipe("|", 0 /*STDIN_FILENO*/,
771 &pSession->StdIn.hChild, &pSession->StdIn.phChild, &pSession->hStdInW);
772 if (RT_SUCCESS(rc))
773 {
774 rc = GstcntlProcessSetupPipe("|", 1 /*STDOUT_FILENO*/,
775 &pSession->StdOut.hChild, &pSession->StdOut.phChild, &pSession->hStdOutR);
776 if (RT_SUCCESS(rc))
777 {
778 rc = GstcntlProcessSetupPipe("|", 2 /*STDERR_FILENO*/,
779 &pSession->StdErr.hChild, &pSession->StdErr.phChild, &pSession->hStdErrR);
780 if (RT_SUCCESS(rc))
781 {
782 rc = RTPollSetCreate(&pSession->hPollSet);
783 if (RT_SUCCESS(rc))
784 rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdInW, RTPOLL_EVT_ERROR,
785 VBOXSERVICECTRLPIPEID_STDIN);
786 if (RT_SUCCESS(rc))
787 rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
788 VBOXSERVICECTRLPIPEID_STDOUT);
789 if (RT_SUCCESS(rc))
790 rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
791 VBOXSERVICECTRLPIPEID_STDERR);
792 }
793
794 if (RT_SUCCESS(rc))
795 {
796 /* Fork the thing. */
797 /** @todo Do we need a custom environment block? */
798 rc = RTProcCreateEx(pszExeName, papszArgs, RTENV_DEFAULT, uProcFlags,
799 pSession->StdIn.phChild, pSession->StdOut.phChild, pSession->StdErr.phChild,
800 pSession->StartupInfo.szUser,
801 pSession->StartupInfo.szPassword,
802 &pSession->hProcess);
803 }
804
805 if (RT_SUCCESS(rc))
806 {
807 /*
808 * Close the child ends of any pipes and redirected files.
809 */
810 int rc2 = RTHandleClose(pSession->StdIn.phChild); AssertRC(rc2);
811 pSession->StdIn.phChild = NULL;
812 rc2 = RTHandleClose(pSession->StdOut.phChild); AssertRC(rc2);
813 pSession->StdOut.phChild = NULL;
814 rc2 = RTHandleClose(pSession->StdErr.phChild); AssertRC(rc2);
815 pSession->StdErr.phChild = NULL;
816 }
817 }
818 }
819#else
820 /** @todo Do we need a custom environment block? */
821 rc = RTProcCreateEx(pszExeName, papszArgs, RTENV_DEFAULT, uProcFlags,
822 NULL /* StdIn */, NULL /* StdOut */, NULL /* StdErr */,
823 pSession->StartupInfo.szUser,
824 pSession->StartupInfo.szPassword,
825 &pSession->hProcess);
826#endif
827 }
828 }
829 else
830 rc = VERR_FILE_NOT_FOUND;
831
832 if (RT_SUCCESS(rc))
833 {
834 /* Add session to list. */
835 /* rc = */ RTListAppend(&g_lstControlSessions, &pSession->Node);
836 if (pNode) /* Return node if wanted. */
837 pNode = &pSession->Node;
838 }
839 else
840 {
841 RTMemFree(pSession);
842 }
843 }
844 else
845 rc = VERR_NO_MEMORY;
846
847 VBoxServiceVerbose(3, "Forking returned returned rc=%Rrc\n", rc);
848 return rc;
849}
850
851
852/**
853 * Closes a formerly opened guest session.
854 *
855 * @return IPRT status code.
856 * @param pSession Guest session to close.
857 * @param uFlags Termination flags.
858 */
859int GstCntlSessionClose(PVBOXSERVICECTRLSESSION pSession, uint32_t uFlags)
860{
861 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
862
863 /** @todo Implement session termination. */
864 return VERR_NOT_IMPLEMENTED;
865}
866
867
868/**
869 * Close all formerly opened guest sessions.
870 *
871 * @return IPRT status code.
872 * @param uFlags Termination flags.
873 */
874int GstCntlSessionCloseAll(uint32_t uFlags)
875{
876 int rc = VINF_SUCCESS;
877
878 VBoxServiceVerbose(3, "GstCntlSessionCloseAll\n");
879
880 PVBOXSERVICECTRLSESSION pSessionCur;
881 RTListForEach(&g_lstControlSessions, pSessionCur, VBOXSERVICECTRLSESSION, Node)
882 {
883 int rc2 = GstCntlSessionClose(pSessionCur, uFlags);
884 if (RT_SUCCESS(rc))
885 {
886 rc = rc2;
887 /* Keep going. */
888 }
889 }
890
891 return rc;
892}
893
894
895RTEXITCODE VBoxServiceControlSessionForkInit(int argc, char **argv)
896{
897 static const RTGETOPTDEF s_aOptions[] =
898 {
899 { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
900 { "--username", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
901 { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
902 { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
903 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
904 };
905
906 int ch;
907 RTGETOPTUNION ValueUnion;
908 RTGETOPTSTATE GetState;
909 RTGetOptInit(&GetState, argc, argv,
910 s_aOptions, RT_ELEMENTS(s_aOptions),
911 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
912
913 int rc = VINF_SUCCESS;
914
915 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
916 && RT_SUCCESS(rc))
917 {
918 /* For options that require an argument, ValueUnion has received the value. */
919 switch (ch)
920 {
921 case VBOXSERVICESESSIONOPT_LOG_FILE:
922 if (!RTStrPrintf(g_szLogFile, sizeof(g_szLogFile), "%s", ValueUnion.psz))
923 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unable to set logfile name to '%s'",
924 ValueUnion.psz);
925 break;
926
927 case VBOXSERVICESESSIONOPT_USERNAME:
928 /** @todo. */
929 break;
930
931 case VBOXSERVICESESSIONOPT_SESSION_ID:
932 g_uSessionID = ValueUnion.u32;
933 break;
934
935 case VBOXSERVICESESSIONOPT_SESSION_PROTO:
936 g_uSessionProto = ValueUnion.u32;
937 break;
938
939 /** @todo Implement help? */
940
941 case 'v':
942 g_cVerbosity++;
943 break;
944
945 case VINF_GETOPT_NOT_OPTION:
946 /* Ignore; might be "guestsession" main command. */
947 break;
948
949 default:
950 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown command '%s'", ValueUnion.psz);
951 break; /* Never reached. */
952 }
953 }
954
955 if (RT_FAILURE(rc))
956 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Initialization failed with rc=%Rrc", rc);
957
958 if (g_uSessionID == UINT32_MAX)
959 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
960
961 rc = VBoxServiceLogCreate(strlen(g_szLogFile) ? g_szLogFile : NULL);
962 if (RT_FAILURE(rc))
963 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)",
964 strlen(g_szLogFile) ? g_szLogFile : "<None>", rc);
965
966 RTEXITCODE rcExit = gstcntlSessionWorker();
967
968 VBoxServiceLogDestroy();
969 return rcExit;
970}
971
972static int gstcntlSessionForkShutdown(uint32_t uClientId, uint32_t cParms)
973{
974 VBoxServiceVerbose(0, "Session %RU32 is going to shutdown ...\n", g_uSessionID);
975
976 /* Close all left guest files. */
977 PVBOXSERVICECTRLFILE pFile;
978 pFile = RTListGetFirst(&g_lstSessionFiles, VBOXSERVICECTRLFILE, Node);
979 while (pFile)
980 {
981 PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node);
982 bool fLast = RTListNodeIsLast(&g_lstSessionFiles, &pFile->Node);
983
984 int rc2 = gstcntlSessionFileDestroy(pFile);
985 if (RT_FAILURE(rc2))
986 {
987 VBoxServiceError("Unable to close file \"%s\"; rc=%Rrc\n",
988 pFile->szName, rc2);
989 /* Keep going. */
990 }
991
992 if (fLast)
993 break;
994
995 pFile = pNext;
996 }
997
998 AssertMsg(RTListIsEmpty(&g_lstSessionFiles),
999 ("Guest file list still contains entries when it should not\n"));
1000
1001 /** @todo Add guest process termination here. Later. */
1002
1003 return VINF_SUCCESS; /** @todo */
1004}
1005
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