VirtualBox

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

Last change on this file since 84005 was 83612, checked in by vboxsync, 5 years ago

Guest Control/VBoxSerivce: Include a timestamp in the logfile format to make logs more unique across multiple runs. bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.2 KB
Line 
1/* $Id: VBoxServiceControlSession.cpp 83612 2020-04-07 17:17:25Z vboxsync $ */
2/** @file
3 * VBoxServiceControlSession - Guest session handling. Also handles the spawned session processes.
4 */
5
6/*
7 * Copyright (C) 2013-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/asm.h>
23#include <iprt/assert.h>
24#include <iprt/dir.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#include <iprt/rand.h>
36
37#include "VBoxServiceInternal.h"
38#include "VBoxServiceUtils.h"
39#include "VBoxServiceControl.h"
40
41using namespace guestControl;
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47/** Generic option indices for session spawn arguments. */
48enum
49{
50 VBOXSERVICESESSIONOPT_FIRST = 1000, /* For initialization. */
51 VBOXSERVICESESSIONOPT_DOMAIN,
52#ifdef DEBUG
53 VBOXSERVICESESSIONOPT_DUMP_STDOUT,
54 VBOXSERVICESESSIONOPT_DUMP_STDERR,
55#endif
56 VBOXSERVICESESSIONOPT_LOG_FILE,
57 VBOXSERVICESESSIONOPT_USERNAME,
58 VBOXSERVICESESSIONOPT_SESSION_ID,
59 VBOXSERVICESESSIONOPT_SESSION_PROTO,
60 VBOXSERVICESESSIONOPT_THREAD_ID
61};
62
63
64static int vgsvcGstCtrlSessionCleanupProcesses(const PVBOXSERVICECTRLSESSION pSession);
65static int vgsvcGstCtrlSessionProcessRemoveInternal(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess);
66
67
68/**
69 * Helper that grows the scratch buffer.
70 * @returns Success indicator.
71 */
72static bool vgsvcGstCtrlSessionGrowScratchBuf(void **ppvScratchBuf, uint32_t *pcbScratchBuf, uint32_t cbMinBuf)
73{
74 uint32_t cbNew = *pcbScratchBuf * 2;
75 if ( cbNew <= VMMDEV_MAX_HGCM_DATA_SIZE
76 && cbMinBuf <= VMMDEV_MAX_HGCM_DATA_SIZE)
77 {
78 while (cbMinBuf > cbNew)
79 cbNew *= 2;
80 void *pvNew = RTMemRealloc(*ppvScratchBuf, cbNew);
81 if (pvNew)
82 {
83 *ppvScratchBuf = pvNew;
84 *pcbScratchBuf = cbNew;
85 return true;
86 }
87 }
88 return false;
89}
90
91
92
93static int vgsvcGstCtrlSessionFileFree(PVBOXSERVICECTRLFILE pFile)
94{
95 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
96
97 int rc = RTFileClose(pFile->hFile);
98 if (RT_SUCCESS(rc))
99 {
100 /* Remove file entry in any case. */
101 RTListNodeRemove(&pFile->Node);
102 /* Destroy this object. */
103 RTMemFree(pFile);
104 }
105
106 return rc;
107}
108
109
110/** @todo No locking done yet! */
111static PVBOXSERVICECTRLFILE vgsvcGstCtrlSessionFileGetLocked(const PVBOXSERVICECTRLSESSION pSession, uint32_t uHandle)
112{
113 AssertPtrReturn(pSession, NULL);
114
115 /** @todo Use a map later! */
116 PVBOXSERVICECTRLFILE pFileCur;
117 RTListForEach(&pSession->lstFiles, pFileCur, VBOXSERVICECTRLFILE, Node)
118 {
119 if (pFileCur->uHandle == uHandle)
120 return pFileCur;
121 }
122
123 return NULL;
124}
125
126
127/**
128 * Recursion worker for vgsvcGstCtrlSessionHandleDirRemove.
129 * Only (recursively) removes directory structures which are not empty. Will fail if not empty.
130 *
131 * @returns IPRT status code.
132 * @param pszDir The directory buffer, RTPATH_MAX in length.
133 * Contains the abs path to the directory to
134 * recurse into. Trailing slash.
135 * @param cchDir The length of the directory we're recursing into,
136 * including the trailing slash.
137 * @param pDirEntry The dir entry buffer. (Shared to save stack.)
138 */
139static int vgsvcGstCtrlSessionHandleDirRemoveSub(char *pszDir, size_t cchDir, PRTDIRENTRY pDirEntry)
140{
141 RTDIR hDir;
142 int rc = RTDirOpen(&hDir, pszDir);
143 if (RT_FAILURE(rc))
144 {
145 /* Ignore non-existing directories like RTDirRemoveRecursive does: */
146 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
147 return VINF_SUCCESS;
148 return rc;
149 }
150
151 for (;;)
152 {
153 rc = RTDirRead(hDir, pDirEntry, NULL);
154 if (RT_FAILURE(rc))
155 {
156 if (rc == VERR_NO_MORE_FILES)
157 rc = VINF_SUCCESS;
158 break;
159 }
160
161 if (!RTDirEntryIsStdDotLink(pDirEntry))
162 {
163 /* Construct the full name of the entry. */
164 if (cchDir + pDirEntry->cbName + 1 /* dir slash */ < RTPATH_MAX)
165 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
166 else
167 {
168 rc = VERR_FILENAME_TOO_LONG;
169 break;
170 }
171
172 /* Make sure we've got the entry type. */
173 if (pDirEntry->enmType == RTDIRENTRYTYPE_UNKNOWN)
174 RTDirQueryUnknownType(pszDir, false /*fFollowSymlinks*/, &pDirEntry->enmType);
175
176 /* Recurse into subdirs and remove them: */
177 if (pDirEntry->enmType == RTDIRENTRYTYPE_DIRECTORY)
178 {
179 size_t cchSubDir = cchDir + pDirEntry->cbName;
180 pszDir[cchSubDir++] = RTPATH_SLASH;
181 pszDir[cchSubDir] = '\0';
182 rc = vgsvcGstCtrlSessionHandleDirRemoveSub(pszDir, cchSubDir, pDirEntry);
183 if (RT_SUCCESS(rc))
184 {
185 pszDir[cchSubDir] = '\0';
186 rc = RTDirRemove(pszDir);
187 if (RT_FAILURE(rc))
188 break;
189 }
190 else
191 break;
192 }
193 /* Not a subdirectory - fail: */
194 else
195 {
196 rc = VERR_DIR_NOT_EMPTY;
197 break;
198 }
199 }
200 }
201
202 RTDirClose(hDir);
203 return rc;
204}
205
206
207static int vgsvcGstCtrlSessionHandleDirRemove(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
208{
209 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
210 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
211
212 /*
213 * Retrieve the message.
214 */
215 char szDir[RTPATH_MAX];
216 uint32_t fFlags; /* DIRREMOVE_FLAG_XXX */
217 int rc = VbglR3GuestCtrlDirGetRemove(pHostCtx, szDir, sizeof(szDir), &fFlags);
218 if (RT_SUCCESS(rc))
219 {
220 /*
221 * Do some validating before executing the job.
222 */
223 if (!(fFlags & ~DIRREMOVEREC_FLAG_VALID_MASK))
224 {
225 if (fFlags & DIRREMOVEREC_FLAG_RECURSIVE)
226 {
227 if (fFlags & (DIRREMOVEREC_FLAG_CONTENT_AND_DIR | DIRREMOVEREC_FLAG_CONTENT_ONLY))
228 {
229 uint32_t fFlagsRemRec = fFlags & DIRREMOVEREC_FLAG_CONTENT_AND_DIR
230 ? RTDIRRMREC_F_CONTENT_AND_DIR : RTDIRRMREC_F_CONTENT_ONLY;
231 rc = RTDirRemoveRecursive(szDir, fFlagsRemRec);
232 }
233 else /* Only remove empty directory structures. Will fail if non-empty. */
234 {
235 RTDIRENTRY DirEntry;
236 RTPathEnsureTrailingSeparator(szDir, sizeof(szDir));
237 rc = vgsvcGstCtrlSessionHandleDirRemoveSub(szDir, strlen(szDir), &DirEntry);
238 }
239 VGSvcVerbose(4, "[Dir %s]: rmdir /s (%#x) -> rc=%Rrc\n", szDir, fFlags, rc);
240 }
241 else
242 {
243 /* Only delete directory if not empty. */
244 rc = RTDirRemove(szDir);
245 VGSvcVerbose(4, "[Dir %s]: rmdir (%#x), rc=%Rrc\n", szDir, fFlags, rc);
246 }
247 }
248 else
249 {
250 VGSvcError("[Dir %s]: Unsupported flags: %#x (all %#x)\n", szDir, (fFlags & ~DIRREMOVEREC_FLAG_VALID_MASK), fFlags);
251 rc = VERR_NOT_SUPPORTED;
252 }
253
254 /*
255 * Report result back to host.
256 */
257 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
258 if (RT_FAILURE(rc2))
259 {
260 VGSvcError("[Dir %s]: Failed to report removing status, rc=%Rrc\n", szDir, rc2);
261 if (RT_SUCCESS(rc))
262 rc = rc2;
263 }
264 }
265 else
266 {
267 VGSvcError("Error fetching parameters for rmdir operation: %Rrc\n", rc);
268 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
269 }
270
271 VGSvcVerbose(6, "Removing directory '%s' returned rc=%Rrc\n", szDir, rc);
272 return rc;
273}
274
275
276static int vgsvcGstCtrlSessionHandleFileOpen(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
277{
278 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
279 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
280
281 /*
282 * Retrieve the message.
283 */
284 char szFile[RTPATH_MAX];
285 char szAccess[64];
286 char szDisposition[64];
287 char szSharing[64];
288 uint32_t uCreationMode = 0;
289 uint64_t offOpen = 0;
290 uint32_t uHandle = 0;
291 int rc = VbglR3GuestCtrlFileGetOpen(pHostCtx,
292 /* File to open. */
293 szFile, sizeof(szFile),
294 /* Open mode. */
295 szAccess, sizeof(szAccess),
296 /* Disposition. */
297 szDisposition, sizeof(szDisposition),
298 /* Sharing. */
299 szSharing, sizeof(szSharing),
300 /* Creation mode. */
301 &uCreationMode,
302 /* Offset. */
303 &offOpen);
304 VGSvcVerbose(4, "[File %s]: szAccess=%s, szDisposition=%s, szSharing=%s, offOpen=%RU64, rc=%Rrc\n",
305 szFile, szAccess, szDisposition, szSharing, offOpen, rc);
306 if (RT_SUCCESS(rc))
307 {
308 PVBOXSERVICECTRLFILE pFile = (PVBOXSERVICECTRLFILE)RTMemAllocZ(sizeof(VBOXSERVICECTRLFILE));
309 if (pFile)
310 {
311 pFile->hFile = NIL_RTFILE; /* Not zero or NULL! */
312 if (szFile[0])
313 {
314 RTStrCopy(pFile->szName, sizeof(pFile->szName), szFile);
315
316/** @todo
317 * Implement szSharing!
318 */
319 uint64_t fFlags;
320 rc = RTFileModeToFlagsEx(szAccess, szDisposition, NULL /* pszSharing, not used yet */, &fFlags);
321 VGSvcVerbose(4, "[File %s] Opening with fFlags=%#RX64 -> rc=%Rrc\n", pFile->szName, fFlags, rc);
322 if (RT_SUCCESS(rc))
323 {
324 fFlags |= (uCreationMode << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
325 /* If we're opening a file in read-only mode, strip truncation mode.
326 * rtFileRecalcAndValidateFlags() will validate it anyway, but avoid asserting in debug builds. */
327 if (fFlags & RTFILE_O_READ)
328 fFlags &= ~RTFILE_O_TRUNCATE;
329 rc = RTFileOpen(&pFile->hFile, pFile->szName, fFlags);
330 if (RT_SUCCESS(rc))
331 {
332 RTFSOBJINFO objInfo;
333 rc = RTFileQueryInfo(pFile->hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
334 if (RT_SUCCESS(rc))
335 {
336 /* Make sure that we only open stuff we really support.
337 * Only POSIX / UNIX we could open stuff like directories and sockets as well. */
338 if ( RT_LIKELY(RTFS_IS_FILE(objInfo.Attr.fMode))
339 || RTFS_IS_SYMLINK(objInfo.Attr.fMode))
340 {
341 /* Seeking is optional. However, the whole operation
342 * will fail if we don't succeed seeking to the wanted position. */
343 if (offOpen)
344 rc = RTFileSeek(pFile->hFile, (int64_t)offOpen, RTFILE_SEEK_BEGIN, NULL /* Current offset */);
345 if (RT_SUCCESS(rc))
346 {
347 /*
348 * Succeeded!
349 */
350 uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID);
351 pFile->uHandle = uHandle;
352 pFile->fOpen = fFlags;
353 RTListAppend(&pSession->lstFiles, &pFile->Node);
354 VGSvcVerbose(2, "[File %s] Opened (ID=%RU32)\n", pFile->szName, pFile->uHandle);
355 }
356 else
357 VGSvcError("[File %s] Seeking to offset %RU64 failed: rc=%Rrc\n", pFile->szName, offOpen, rc);
358 }
359 else
360 {
361 VGSvcError("[File %s] Unsupported mode %#x\n", pFile->szName, objInfo.Attr.fMode);
362 rc = VERR_NOT_SUPPORTED;
363 }
364 }
365 else
366 VGSvcError("[File %s] Getting mode failed with rc=%Rrc\n", pFile->szName, rc);
367 }
368 else
369 VGSvcError("[File %s] Opening failed with rc=%Rrc\n", pFile->szName, rc);
370 }
371 }
372 else
373 {
374 VGSvcError("[File %s] empty filename!\n", szFile);
375 rc = VERR_INVALID_NAME;
376 }
377
378 /* clean up if we failed. */
379 if (RT_FAILURE(rc))
380 {
381 if (pFile->hFile != NIL_RTFILE)
382 RTFileClose(pFile->hFile);
383 RTMemFree(pFile);
384 }
385 }
386 else
387 rc = VERR_NO_MEMORY;
388
389 /*
390 * Report result back to host.
391 */
392 int rc2 = VbglR3GuestCtrlFileCbOpen(pHostCtx, rc, uHandle);
393 if (RT_FAILURE(rc2))
394 {
395 VGSvcError("[File %s]: Failed to report file open status, rc=%Rrc\n", szFile, rc2);
396 if (RT_SUCCESS(rc))
397 rc = rc2;
398 }
399 }
400 else
401 {
402 VGSvcError("Error fetching parameters for open file operation: %Rrc\n", rc);
403 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
404 }
405
406 VGSvcVerbose(4, "[File %s] Opening (open mode='%s', disposition='%s', creation mode=0x%x) returned rc=%Rrc\n",
407 szFile, szAccess, szDisposition, uCreationMode, rc);
408 return rc;
409}
410
411
412static int vgsvcGstCtrlSessionHandleFileClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
413{
414 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
415 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
416
417 /*
418 * Retrieve the message.
419 */
420 uint32_t uHandle = 0;
421 int rc = VbglR3GuestCtrlFileGetClose(pHostCtx, &uHandle /* File handle to close */);
422 if (RT_SUCCESS(rc))
423 {
424 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
425 if (pFile)
426 {
427 VGSvcVerbose(2, "[File %s] Closing (handle=%RU32)\n", pFile ? pFile->szName : "<Not found>", uHandle);
428 rc = vgsvcGstCtrlSessionFileFree(pFile);
429 }
430 else
431 {
432 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
433 rc = VERR_NOT_FOUND;
434 }
435
436 /*
437 * Report result back to host.
438 */
439 int rc2 = VbglR3GuestCtrlFileCbClose(pHostCtx, rc);
440 if (RT_FAILURE(rc2))
441 {
442 VGSvcError("Failed to report file close status, rc=%Rrc\n", rc2);
443 if (RT_SUCCESS(rc))
444 rc = rc2;
445 }
446 }
447 else
448 {
449 VGSvcError("Error fetching parameters for close file operation: %Rrc\n", rc);
450 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
451 }
452 return rc;
453}
454
455
456static int vgsvcGstCtrlSessionHandleFileRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
457 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
458{
459 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
460 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
461
462 /*
463 * Retrieve the request.
464 */
465 uint32_t uHandle = 0;
466 uint32_t cbToRead;
467 int rc = VbglR3GuestCtrlFileGetRead(pHostCtx, &uHandle, &cbToRead);
468 if (RT_SUCCESS(rc))
469 {
470 /*
471 * Locate the file and do the reading.
472 *
473 * If the request is larger than our scratch buffer, try grow it - just
474 * ignore failure as the host better respect our buffer limits.
475 */
476 uint32_t offNew = 0;
477 size_t cbRead = 0;
478 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
479 if (pFile)
480 {
481 if (*pcbScratchBuf < cbToRead)
482 vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToRead);
483
484 rc = RTFileRead(pFile->hFile, *ppvScratchBuf, RT_MIN(cbToRead, *pcbScratchBuf), &cbRead);
485 offNew = (int64_t)RTFileTell(pFile->hFile);
486 VGSvcVerbose(5, "[File %s] Read %zu/%RU32 bytes, rc=%Rrc, offNew=%RI64\n", pFile->szName, cbRead, cbToRead, rc, offNew);
487 }
488 else
489 {
490 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
491 rc = VERR_NOT_FOUND;
492 }
493
494 /*
495 * Report result and data back to the host.
496 */
497 int rc2;
498 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
499 rc2 = VbglR3GuestCtrlFileCbReadOffset(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead, offNew);
500 else
501 rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead);
502 if (RT_FAILURE(rc2))
503 {
504 VGSvcError("Failed to report file read status, rc=%Rrc\n", rc2);
505 if (RT_SUCCESS(rc))
506 rc = rc2;
507 }
508 }
509 else
510 {
511 VGSvcError("Error fetching parameters for file read operation: %Rrc\n", rc);
512 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
513 }
514 return rc;
515}
516
517
518static int vgsvcGstCtrlSessionHandleFileReadAt(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
519 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
520{
521 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
522 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
523
524 /*
525 * Retrieve the request.
526 */
527 uint32_t uHandle = 0;
528 uint32_t cbToRead;
529 uint64_t offReadAt;
530 int rc = VbglR3GuestCtrlFileGetReadAt(pHostCtx, &uHandle, &cbToRead, &offReadAt);
531 if (RT_SUCCESS(rc))
532 {
533 /*
534 * Locate the file and do the reading.
535 *
536 * If the request is larger than our scratch buffer, try grow it - just
537 * ignore failure as the host better respect our buffer limits.
538 */
539 int64_t offNew = 0;
540 size_t cbRead = 0;
541 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
542 if (pFile)
543 {
544 if (*pcbScratchBuf < cbToRead)
545 vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToRead);
546
547 rc = RTFileReadAt(pFile->hFile, (RTFOFF)offReadAt, *ppvScratchBuf, RT_MIN(cbToRead, *pcbScratchBuf), &cbRead);
548 if (RT_SUCCESS(rc))
549 {
550 offNew = offReadAt + cbRead;
551 RTFileSeek(pFile->hFile, offNew, RTFILE_SEEK_BEGIN, NULL); /* RTFileReadAt does not always change position. */
552 }
553 else
554 offNew = (int64_t)RTFileTell(pFile->hFile);
555 VGSvcVerbose(5, "[File %s] Read %zu bytes @ %RU64, rc=%Rrc, offNew=%RI64\n", pFile->szName, cbRead, offReadAt, rc, offNew);
556 }
557 else
558 {
559 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
560 rc = VERR_NOT_FOUND;
561 }
562
563 /*
564 * Report result and data back to the host.
565 */
566 int rc2;
567 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
568 rc2 = VbglR3GuestCtrlFileCbReadOffset(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead, offNew);
569 else
570 rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, *ppvScratchBuf, (uint32_t)cbRead);
571 if (RT_FAILURE(rc2))
572 {
573 VGSvcError("Failed to report file read at status, rc=%Rrc\n", rc2);
574 if (RT_SUCCESS(rc))
575 rc = rc2;
576 }
577 }
578 else
579 {
580 VGSvcError("Error fetching parameters for file read at operation: %Rrc\n", rc);
581 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
582 }
583 return rc;
584}
585
586
587static int vgsvcGstCtrlSessionHandleFileWrite(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
588 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
589{
590 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
591 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
592
593 /*
594 * Retrieve the request and data to write.
595 */
596 uint32_t uHandle = 0;
597 uint32_t cbToWrite;
598 int rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite);
599 if ( rc == VERR_BUFFER_OVERFLOW
600 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToWrite))
601 rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite);
602 if (RT_SUCCESS(rc))
603 {
604 /*
605 * Locate the file and do the writing.
606 */
607 int64_t offNew = 0;
608 size_t cbWritten = 0;
609 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
610 if (pFile)
611 {
612 rc = RTFileWrite(pFile->hFile, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), &cbWritten);
613 offNew = (int64_t)RTFileTell(pFile->hFile);
614 VGSvcVerbose(5, "[File %s] Writing %p LB %RU32 => %Rrc, cbWritten=%zu, offNew=%RI64\n",
615 pFile->szName, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), rc, cbWritten, offNew);
616 }
617 else
618 {
619 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
620 rc = VERR_NOT_FOUND;
621 }
622
623 /*
624 * Report result back to host.
625 */
626 int rc2;
627 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
628 rc2 = VbglR3GuestCtrlFileCbWriteOffset(pHostCtx, rc, (uint32_t)cbWritten, offNew);
629 else
630 rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten);
631 if (RT_FAILURE(rc2))
632 {
633 VGSvcError("Failed to report file write status, rc=%Rrc\n", rc2);
634 if (RT_SUCCESS(rc))
635 rc = rc2;
636 }
637 }
638 else
639 {
640 VGSvcError("Error fetching parameters for file write operation: %Rrc\n", rc);
641 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
642 }
643 return rc;
644}
645
646
647static int vgsvcGstCtrlSessionHandleFileWriteAt(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
648 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
649{
650 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
651 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
652
653 /*
654 * Retrieve the request and data to write.
655 */
656 uint32_t uHandle = 0;
657 uint32_t cbToWrite;
658 uint64_t offWriteAt;
659 int rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite, &offWriteAt);
660 if ( rc == VERR_BUFFER_OVERFLOW
661 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbToWrite))
662 rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, *ppvScratchBuf, *pcbScratchBuf, &cbToWrite, &offWriteAt);
663 if (RT_SUCCESS(rc))
664 {
665 /*
666 * Locate the file and do the writing.
667 */
668 int64_t offNew = 0;
669 size_t cbWritten = 0;
670 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
671 if (pFile)
672 {
673 rc = RTFileWriteAt(pFile->hFile, (RTFOFF)offWriteAt, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), &cbWritten);
674 if (RT_SUCCESS(rc))
675 {
676 offNew = offWriteAt + cbWritten;
677
678 /* RTFileWriteAt does not always change position: */
679 if (!(pFile->fOpen & RTFILE_O_APPEND))
680 RTFileSeek(pFile->hFile, offNew, RTFILE_SEEK_BEGIN, NULL);
681 else
682 RTFileSeek(pFile->hFile, 0, RTFILE_SEEK_END, (uint64_t *)&offNew);
683 }
684 else
685 offNew = (int64_t)RTFileTell(pFile->hFile);
686 VGSvcVerbose(5, "[File %s] Writing %p LB %RU32 @ %RU64 => %Rrc, cbWritten=%zu, offNew=%RI64\n",
687 pFile->szName, *ppvScratchBuf, RT_MIN(cbToWrite, *pcbScratchBuf), offWriteAt, rc, cbWritten, offNew);
688 }
689 else
690 {
691 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
692 rc = VERR_NOT_FOUND;
693 }
694
695 /*
696 * Report result back to host.
697 */
698 int rc2;
699 if (g_fControlHostFeatures0 & VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET)
700 rc2 = VbglR3GuestCtrlFileCbWriteOffset(pHostCtx, rc, (uint32_t)cbWritten, offNew);
701 else
702 rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten);
703 if (RT_FAILURE(rc2))
704 {
705 VGSvcError("Failed to report file write status, rc=%Rrc\n", rc2);
706 if (RT_SUCCESS(rc))
707 rc = rc2;
708 }
709 }
710 else
711 {
712 VGSvcError("Error fetching parameters for file write at operation: %Rrc\n", rc);
713 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
714 }
715 return rc;
716}
717
718
719static int vgsvcGstCtrlSessionHandleFileSeek(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
720{
721 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
722 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
723
724 /*
725 * Retrieve the request.
726 */
727 uint32_t uHandle = 0;
728 uint32_t uSeekMethod;
729 uint64_t offSeek; /* Will be converted to int64_t. */
730 int rc = VbglR3GuestCtrlFileGetSeek(pHostCtx, &uHandle, &uSeekMethod, &offSeek);
731 if (RT_SUCCESS(rc))
732 {
733 uint64_t offActual = 0;
734
735 /*
736 * Validate and convert the seek method to IPRT speak.
737 */
738 static const uint8_t s_abMethods[GUEST_FILE_SEEKTYPE_END + 1] =
739 {
740 UINT8_MAX, RTFILE_SEEK_BEGIN, UINT8_MAX, UINT8_MAX, RTFILE_SEEK_CURRENT,
741 UINT8_MAX, UINT8_MAX, UINT8_MAX, RTFILE_SEEK_END
742 };
743 if ( uSeekMethod < RT_ELEMENTS(s_abMethods)
744 && s_abMethods[uSeekMethod] != UINT8_MAX)
745 {
746 /*
747 * Locate the file and do the seek.
748 */
749 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
750 if (pFile)
751 {
752 rc = RTFileSeek(pFile->hFile, (int64_t)offSeek, s_abMethods[uSeekMethod], &offActual);
753 VGSvcVerbose(5, "[File %s]: Seeking to offSeek=%RI64, uSeekMethodIPRT=%u, rc=%Rrc\n",
754 pFile->szName, offSeek, s_abMethods[uSeekMethod], rc);
755 }
756 else
757 {
758 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
759 rc = VERR_NOT_FOUND;
760 }
761 }
762 else
763 {
764 VGSvcError("Invalid seek method: %#x\n", uSeekMethod);
765 rc = VERR_NOT_SUPPORTED;
766 }
767
768 /*
769 * Report result back to host.
770 */
771 int rc2 = VbglR3GuestCtrlFileCbSeek(pHostCtx, rc, offActual);
772 if (RT_FAILURE(rc2))
773 {
774 VGSvcError("Failed to report file seek status, rc=%Rrc\n", rc2);
775 if (RT_SUCCESS(rc))
776 rc = rc2;
777 }
778 }
779 else
780 {
781 VGSvcError("Error fetching parameters for file seek operation: %Rrc\n", rc);
782 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
783 }
784 return rc;
785}
786
787
788static int vgsvcGstCtrlSessionHandleFileTell(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
789{
790 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
791 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
792
793 /*
794 * Retrieve the request.
795 */
796 uint32_t uHandle = 0;
797 int rc = VbglR3GuestCtrlFileGetTell(pHostCtx, &uHandle);
798 if (RT_SUCCESS(rc))
799 {
800 /*
801 * Locate the file and ask for the current position.
802 */
803 uint64_t offCurrent = 0;
804 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
805 if (pFile)
806 {
807 offCurrent = RTFileTell(pFile->hFile);
808 VGSvcVerbose(5, "[File %s]: Telling offCurrent=%RU64\n", pFile->szName, offCurrent);
809 }
810 else
811 {
812 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
813 rc = VERR_NOT_FOUND;
814 }
815
816 /*
817 * Report result back to host.
818 */
819 int rc2 = VbglR3GuestCtrlFileCbTell(pHostCtx, rc, offCurrent);
820 if (RT_FAILURE(rc2))
821 {
822 VGSvcError("Failed to report file tell status, rc=%Rrc\n", rc2);
823 if (RT_SUCCESS(rc))
824 rc = rc2;
825 }
826 }
827 else
828 {
829 VGSvcError("Error fetching parameters for file tell operation: %Rrc\n", rc);
830 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
831 }
832 return rc;
833}
834
835
836static int vgsvcGstCtrlSessionHandleFileSetSize(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
837{
838 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
839 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
840
841 /*
842 * Retrieve the request.
843 */
844 uint32_t uHandle = 0;
845 uint64_t cbNew = 0;
846 int rc = VbglR3GuestCtrlFileGetSetSize(pHostCtx, &uHandle, &cbNew);
847 if (RT_SUCCESS(rc))
848 {
849 /*
850 * Locate the file and ask for the current position.
851 */
852 PVBOXSERVICECTRLFILE pFile = vgsvcGstCtrlSessionFileGetLocked(pSession, uHandle);
853 if (pFile)
854 {
855 rc = RTFileSetSize(pFile->hFile, cbNew);
856 VGSvcVerbose(5, "[File %s]: Changing size to %RU64 (%#RX64), rc=%Rrc\n", pFile->szName, cbNew, cbNew, rc);
857 }
858 else
859 {
860 VGSvcError("File %u (%#x) not found!\n", uHandle, uHandle);
861 cbNew = UINT64_MAX;
862 rc = VERR_NOT_FOUND;
863 }
864
865 /*
866 * Report result back to host.
867 */
868 int rc2 = VbglR3GuestCtrlFileCbSetSize(pHostCtx, rc, cbNew);
869 if (RT_FAILURE(rc2))
870 {
871 VGSvcError("Failed to report file tell status, rc=%Rrc\n", rc2);
872 if (RT_SUCCESS(rc))
873 rc = rc2;
874 }
875 }
876 else
877 {
878 VGSvcError("Error fetching parameters for file tell operation: %Rrc\n", rc);
879 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
880 }
881 return rc;
882}
883
884
885static int vgsvcGstCtrlSessionHandlePathRename(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
886{
887 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
888 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
889
890 /*
891 * Retrieve the request.
892 */
893 char szSource[RTPATH_MAX];
894 char szDest[RTPATH_MAX];
895 uint32_t fFlags = 0; /* PATHRENAME_FLAG_XXX */
896 int rc = VbglR3GuestCtrlPathGetRename(pHostCtx, szSource, sizeof(szSource), szDest, sizeof(szDest), &fFlags);
897 if (RT_SUCCESS(rc))
898 {
899 /*
900 * Validate the flags (kudos for using the same as IPRT), then do the renaming.
901 */
902 AssertCompile(PATHRENAME_FLAG_NO_REPLACE == RTPATHRENAME_FLAGS_NO_REPLACE);
903 AssertCompile(PATHRENAME_FLAG_REPLACE == RTPATHRENAME_FLAGS_REPLACE);
904 AssertCompile(PATHRENAME_FLAG_NO_SYMLINKS == RTPATHRENAME_FLAGS_NO_SYMLINKS);
905 AssertCompile(PATHRENAME_FLAG_VALID_MASK == (RTPATHRENAME_FLAGS_NO_REPLACE | RTPATHRENAME_FLAGS_REPLACE | RTPATHRENAME_FLAGS_NO_SYMLINKS));
906 if (!(fFlags & ~PATHRENAME_FLAG_VALID_MASK))
907 {
908 VGSvcVerbose(4, "Renaming '%s' to '%s', fFlags=%#x, rc=%Rrc\n", szSource, szDest, fFlags, rc);
909 rc = RTPathRename(szSource, szDest, fFlags);
910 }
911 else
912 {
913 VGSvcError("Invalid rename flags: %#x\n", fFlags);
914 rc = VERR_NOT_SUPPORTED;
915 }
916
917 /*
918 * Report result back to host.
919 */
920 int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc);
921 if (RT_FAILURE(rc2))
922 {
923 VGSvcError("Failed to report renaming status, rc=%Rrc\n", rc2);
924 if (RT_SUCCESS(rc))
925 rc = rc2;
926 }
927 }
928 else
929 {
930 VGSvcError("Error fetching parameters for rename operation: %Rrc\n", rc);
931 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
932 }
933 VGSvcVerbose(5, "Renaming '%s' to '%s' returned rc=%Rrc\n", szSource, szDest, rc);
934 return rc;
935}
936
937
938/**
939 * Handles getting the user's documents directory.
940 *
941 * @returns VBox status code.
942 * @param pSession Guest session.
943 * @param pHostCtx Host context.
944 */
945static int vgsvcGstCtrlSessionHandlePathUserDocuments(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
946{
947 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
948 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
949
950 /*
951 * Retrieve the request.
952 */
953 int rc = VbglR3GuestCtrlPathGetUserDocuments(pHostCtx);
954 if (RT_SUCCESS(rc))
955 {
956 /*
957 * Get the path and pass it back to the host..
958 */
959 char szPath[RTPATH_MAX];
960 rc = RTPathUserDocuments(szPath, sizeof(szPath));
961#ifdef DEBUG
962 VGSvcVerbose(2, "User documents is '%s', rc=%Rrc\n", szPath, rc);
963#endif
964
965 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
966 RT_SUCCESS(rc) ? (uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
967 if (RT_FAILURE(rc2))
968 {
969 VGSvcError("Failed to report user documents, rc=%Rrc\n", rc2);
970 if (RT_SUCCESS(rc))
971 rc = rc2;
972 }
973 }
974 else
975 {
976 VGSvcError("Error fetching parameters for user documents path request: %Rrc\n", rc);
977 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
978 }
979 return rc;
980}
981
982
983/**
984 * Handles getting the user's home directory.
985 *
986 * @returns VBox status code.
987 * @param pSession Guest session.
988 * @param pHostCtx Host context.
989 */
990static int vgsvcGstCtrlSessionHandlePathUserHome(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
991{
992 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
993 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
994
995 /*
996 * Retrieve the request.
997 */
998 int rc = VbglR3GuestCtrlPathGetUserHome(pHostCtx);
999 if (RT_SUCCESS(rc))
1000 {
1001 /*
1002 * Get the path and pass it back to the host..
1003 */
1004 char szPath[RTPATH_MAX];
1005 rc = RTPathUserHome(szPath, sizeof(szPath));
1006
1007#ifdef DEBUG
1008 VGSvcVerbose(2, "User home is '%s', rc=%Rrc\n", szPath, rc);
1009#endif
1010 /* Report back in any case. */
1011 int rc2 = VbglR3GuestCtrlMsgReplyEx(pHostCtx, rc, 0 /* Type */, szPath,
1012 RT_SUCCESS(rc) ?(uint32_t)strlen(szPath) + 1 /* Include terminating zero */ : 0);
1013 if (RT_FAILURE(rc2))
1014 {
1015 VGSvcError("Failed to report user home, rc=%Rrc\n", rc2);
1016 if (RT_SUCCESS(rc))
1017 rc = rc2;
1018 }
1019 }
1020 else
1021 {
1022 VGSvcError("Error fetching parameters for user home directory path request: %Rrc\n", rc);
1023 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1024 }
1025 return rc;
1026}
1027
1028
1029/**
1030 * Handles starting a guest processes.
1031 *
1032 * @returns VBox status code.
1033 * @param pSession Guest session.
1034 * @param pHostCtx Host context.
1035 */
1036static int vgsvcGstCtrlSessionHandleProcExec(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1037{
1038 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1039 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1040
1041/** @todo this hardcoded stuff needs redoing. */
1042
1043 /* Initialize maximum environment block size -- needed as input
1044 * parameter to retrieve the stuff from the host. On output this then
1045 * will contain the actual block size. */
1046 VBOXSERVICECTRLPROCSTARTUPINFO startupInfo;
1047 RT_ZERO(startupInfo);
1048 startupInfo.cbEnv = sizeof(startupInfo.szEnv);
1049
1050 int rc = VbglR3GuestCtrlProcGetStart(pHostCtx,
1051 /* Command */
1052 startupInfo.szCmd, sizeof(startupInfo.szCmd),
1053 /* Flags */
1054 &startupInfo.uFlags,
1055 /* Arguments */
1056 startupInfo.szArgs, sizeof(startupInfo.szArgs), &startupInfo.uNumArgs,
1057 /* Environment */
1058 startupInfo.szEnv, &startupInfo.cbEnv, &startupInfo.uNumEnvVars,
1059 /* Credentials; for hosts with VBox < 4.3 (protocol version 1).
1060 * For protocol v2 and up the credentials are part of the session
1061 * opening call. */
1062 startupInfo.szUser, sizeof(startupInfo.szUser),
1063 startupInfo.szPassword, sizeof(startupInfo.szPassword),
1064 /* Timeout (in ms) */
1065 &startupInfo.uTimeLimitMS,
1066 /* Process priority */
1067 &startupInfo.uPriority,
1068 /* Process affinity */
1069 startupInfo.uAffinity, sizeof(startupInfo.uAffinity), &startupInfo.uNumAffinity);
1070 if (RT_SUCCESS(rc))
1071 {
1072 VGSvcVerbose(3, "Request to start process szCmd=%s, fFlags=0x%x, szArgs=%s, szEnv=%s, uTimeout=%RU32\n",
1073 startupInfo.szCmd, startupInfo.uFlags,
1074 startupInfo.uNumArgs ? startupInfo.szArgs : "<None>",
1075 startupInfo.uNumEnvVars ? startupInfo.szEnv : "<None>",
1076 startupInfo.uTimeLimitMS);
1077
1078 bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */
1079 rc = VGSvcGstCtrlSessionProcessStartAllowed(pSession, &fStartAllowed);
1080 if (RT_SUCCESS(rc))
1081 {
1082 vgsvcGstCtrlSessionCleanupProcesses(pSession);
1083
1084 if (fStartAllowed)
1085 rc = VGSvcGstCtrlProcessStart(pSession, &startupInfo, pHostCtx->uContextID);
1086 else
1087 rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */
1088 }
1089
1090 /* We're responsible for signaling errors to the host (it will wait for ever otherwise). */
1091 if (RT_FAILURE(rc))
1092 {
1093 VGSvcError("Starting process failed with rc=%Rrc, protocol=%RU32, parameters=%RU32\n",
1094 rc, pHostCtx->uProtocol, pHostCtx->uNumParms);
1095 int rc2 = VbglR3GuestCtrlProcCbStatus(pHostCtx, 0 /*nil-PID*/, PROC_STS_ERROR, rc, NULL /*pvData*/, 0 /*cbData*/);
1096 if (RT_FAILURE(rc2))
1097 VGSvcError("Error sending start process status to host, rc=%Rrc\n", rc2);
1098 }
1099 }
1100 else
1101 {
1102 VGSvcError("Failed to retrieve parameters for process start: %Rrc (cParms=%u)\n", rc, pHostCtx->uNumParms);
1103 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1104 }
1105 return rc;
1106}
1107
1108
1109/**
1110 * Sends stdin input to a specific guest process.
1111 *
1112 * @returns VBox status code.
1113 * @param pSession The session which is in charge.
1114 * @param pHostCtx The host context to use.
1115 * @param ppvScratchBuf The scratch buffer, we may grow it.
1116 * @param pcbScratchBuf The scratch buffer size for retrieving the input
1117 * data.
1118 */
1119static int vgsvcGstCtrlSessionHandleProcInput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1120 void **ppvScratchBuf, uint32_t *pcbScratchBuf)
1121{
1122 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1123 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1124
1125 /*
1126 * Retrieve the data from the host.
1127 */
1128 uint32_t uPID;
1129 uint32_t fFlags;
1130 uint32_t cbInput;
1131 int rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
1132 if ( rc == VERR_BUFFER_OVERFLOW
1133 && vgsvcGstCtrlSessionGrowScratchBuf(ppvScratchBuf, pcbScratchBuf, cbInput))
1134 rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &fFlags, *ppvScratchBuf, *pcbScratchBuf, &cbInput);
1135 if (RT_SUCCESS(rc))
1136 {
1137 if (fFlags & INPUT_FLAG_EOF)
1138 VGSvcVerbose(4, "Got last process input block for PID=%RU32 (%RU32 bytes) ...\n", uPID, cbInput);
1139
1140 /*
1141 * Locate the process and feed it.
1142 */
1143 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1144 if (pProcess)
1145 {
1146 rc = VGSvcGstCtrlProcessHandleInput(pProcess, pHostCtx, RT_BOOL(fFlags & INPUT_FLAG_EOF),
1147 *ppvScratchBuf, RT_MIN(cbInput, *pcbScratchBuf));
1148 if (RT_FAILURE(rc))
1149 VGSvcError("Error handling input message for PID=%RU32, rc=%Rrc\n", uPID, rc);
1150 VGSvcGstCtrlProcessRelease(pProcess);
1151 }
1152 else
1153 {
1154 VGSvcError("Could not find PID %u for feeding %u bytes to it.\n", uPID, cbInput);
1155 rc = VERR_PROCESS_NOT_FOUND;
1156 VbglR3GuestCtrlProcCbStatusInput(pHostCtx, uPID, INPUT_STS_ERROR, rc, 0);
1157 }
1158 }
1159 else
1160 {
1161 VGSvcError("Failed to retrieve parameters for process input: %Rrc (scratch %u bytes)\n", rc, *pcbScratchBuf);
1162 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1163 }
1164
1165 VGSvcVerbose(6, "Feeding input to PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1166 return rc;
1167}
1168
1169
1170/**
1171 * Gets stdout/stderr output of a specific guest process.
1172 *
1173 * @returns VBox status code.
1174 * @param pSession The session which is in charge.
1175 * @param pHostCtx The host context to use.
1176 */
1177static int vgsvcGstCtrlSessionHandleProcOutput(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1178{
1179 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1180 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1181
1182 /*
1183 * Retrieve the request.
1184 */
1185 uint32_t uPID;
1186 uint32_t uHandleID;
1187 uint32_t fFlags;
1188 int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &fFlags);
1189#ifdef DEBUG_andy
1190 VGSvcVerbose(4, "Getting output for PID=%RU32, CID=%RU32, uHandleID=%RU32, fFlags=%RU32\n",
1191 uPID, pHostCtx->uContextID, uHandleID, fFlags);
1192#endif
1193 if (RT_SUCCESS(rc))
1194 {
1195 /*
1196 * Locate the process and hand it the output request.
1197 */
1198 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1199 if (pProcess)
1200 {
1201 rc = VGSvcGstCtrlProcessHandleOutput(pProcess, pHostCtx, uHandleID, _64K /* cbToRead */, fFlags);
1202 if (RT_FAILURE(rc))
1203 VGSvcError("Error getting output for PID=%RU32, rc=%Rrc\n", uPID, rc);
1204 VGSvcGstCtrlProcessRelease(pProcess);
1205 }
1206 else
1207 {
1208 VGSvcError("Could not find PID %u for draining handle %u (%#x).\n", uPID, uHandleID, uHandleID);
1209 rc = VERR_PROCESS_NOT_FOUND;
1210/** @todo r=bird:
1211 *
1212 * No way to report status status code for output requests?
1213 *
1214 */
1215 }
1216 }
1217 else
1218 {
1219 VGSvcError("Error fetching parameters for process output request: %Rrc\n", rc);
1220 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1221 }
1222
1223#ifdef DEBUG_andy
1224 VGSvcVerbose(4, "Getting output for PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1225#endif
1226 return rc;
1227}
1228
1229
1230/**
1231 * Tells a guest process to terminate.
1232 *
1233 * @returns VBox status code.
1234 * @param pSession The session which is in charge.
1235 * @param pHostCtx The host context to use.
1236 */
1237static int vgsvcGstCtrlSessionHandleProcTerminate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1238{
1239 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1240 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1241
1242 /*
1243 * Retrieve the request.
1244 */
1245 uint32_t uPID;
1246 int rc = VbglR3GuestCtrlProcGetTerminate(pHostCtx, &uPID);
1247 if (RT_SUCCESS(rc))
1248 {
1249 /*
1250 * Locate the process and terminate it.
1251 */
1252 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1253 if (pProcess)
1254 {
1255 rc = VGSvcGstCtrlProcessHandleTerm(pProcess);
1256 if (RT_FAILURE(rc))
1257 VGSvcError("Error terminating PID=%RU32, rc=%Rrc\n", uPID, rc);
1258
1259 VGSvcGstCtrlProcessRelease(pProcess);
1260 }
1261 else
1262 {
1263 VGSvcError("Could not find PID %u for termination.\n", uPID);
1264 rc = VERR_PROCESS_NOT_FOUND;
1265 }
1266 }
1267 else
1268 {
1269 VGSvcError("Error fetching parameters for process termination request: %Rrc\n", rc);
1270 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1271 }
1272#ifdef DEBUG_andy
1273 VGSvcVerbose(4, "Terminating PID=%RU32 resulted in rc=%Rrc\n", uPID, rc);
1274#endif
1275 return rc;
1276}
1277
1278
1279static int vgsvcGstCtrlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx)
1280{
1281 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1282 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1283
1284 /*
1285 * Retrieve the request.
1286 */
1287 uint32_t uPID;
1288 uint32_t uWaitFlags;
1289 uint32_t uTimeoutMS;
1290 int rc = VbglR3GuestCtrlProcGetWaitFor(pHostCtx, &uPID, &uWaitFlags, &uTimeoutMS);
1291 if (RT_SUCCESS(rc))
1292 {
1293 /*
1294 * Locate the process and the realize that this call makes no sense
1295 * since we'll notify the host when a process terminates anyway and
1296 * hopefully don't need any additional encouragement.
1297 */
1298 PVBOXSERVICECTRLPROCESS pProcess = VGSvcGstCtrlSessionRetainProcess(pSession, uPID);
1299 if (pProcess)
1300 {
1301 rc = VERR_NOT_IMPLEMENTED; /** @todo */
1302 VGSvcGstCtrlProcessRelease(pProcess);
1303 }
1304 else
1305 rc = VERR_NOT_FOUND;
1306 }
1307 else
1308 {
1309 VGSvcError("Error fetching parameters for process wait request: %Rrc\n", rc);
1310 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
1311 }
1312 return rc;
1313}
1314
1315
1316int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx,
1317 void **ppvScratchBuf, uint32_t *pcbScratchBuf, volatile bool *pfShutdown)
1318{
1319 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1320 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
1321 AssertPtrReturn(*ppvScratchBuf, VERR_INVALID_POINTER);
1322 AssertPtrReturn(pfShutdown, VERR_INVALID_POINTER);
1323
1324
1325 /*
1326 * Only anonymous sessions (that is, sessions which run with local
1327 * service privileges) or spawned session processes can do certain
1328 * operations.
1329 */
1330 bool const fImpersonated = RT_BOOL(pSession->fFlags & ( VBOXSERVICECTRLSESSION_FLAG_SPAWN
1331 | VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS));
1332 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
1333
1334 switch (uMsg)
1335 {
1336 case HOST_MSG_SESSION_CLOSE:
1337 /* Shutdown (this spawn). */
1338 rc = VGSvcGstCtrlSessionClose(pSession);
1339 *pfShutdown = true; /* Shutdown in any case. */
1340 break;
1341
1342 case HOST_MSG_DIR_REMOVE:
1343 if (fImpersonated)
1344 rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx);
1345 break;
1346
1347 case HOST_MSG_EXEC_CMD:
1348 rc = vgsvcGstCtrlSessionHandleProcExec(pSession, pHostCtx);
1349 break;
1350
1351 case HOST_MSG_EXEC_SET_INPUT:
1352 rc = vgsvcGstCtrlSessionHandleProcInput(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1353 break;
1354
1355 case HOST_MSG_EXEC_GET_OUTPUT:
1356 rc = vgsvcGstCtrlSessionHandleProcOutput(pSession, pHostCtx);
1357 break;
1358
1359 case HOST_MSG_EXEC_TERMINATE:
1360 rc = vgsvcGstCtrlSessionHandleProcTerminate(pSession, pHostCtx);
1361 break;
1362
1363 case HOST_MSG_EXEC_WAIT_FOR:
1364 rc = vgsvcGstCtrlSessionHandleProcWaitFor(pSession, pHostCtx);
1365 break;
1366
1367 case HOST_MSG_FILE_OPEN:
1368 if (fImpersonated)
1369 rc = vgsvcGstCtrlSessionHandleFileOpen(pSession, pHostCtx);
1370 break;
1371
1372 case HOST_MSG_FILE_CLOSE:
1373 if (fImpersonated)
1374 rc = vgsvcGstCtrlSessionHandleFileClose(pSession, pHostCtx);
1375 break;
1376
1377 case HOST_MSG_FILE_READ:
1378 if (fImpersonated)
1379 rc = vgsvcGstCtrlSessionHandleFileRead(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1380 break;
1381
1382 case HOST_MSG_FILE_READ_AT:
1383 if (fImpersonated)
1384 rc = vgsvcGstCtrlSessionHandleFileReadAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1385 break;
1386
1387 case HOST_MSG_FILE_WRITE:
1388 if (fImpersonated)
1389 rc = vgsvcGstCtrlSessionHandleFileWrite(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1390 break;
1391
1392 case HOST_MSG_FILE_WRITE_AT:
1393 if (fImpersonated)
1394 rc = vgsvcGstCtrlSessionHandleFileWriteAt(pSession, pHostCtx, ppvScratchBuf, pcbScratchBuf);
1395 break;
1396
1397 case HOST_MSG_FILE_SEEK:
1398 if (fImpersonated)
1399 rc = vgsvcGstCtrlSessionHandleFileSeek(pSession, pHostCtx);
1400 break;
1401
1402 case HOST_MSG_FILE_TELL:
1403 if (fImpersonated)
1404 rc = vgsvcGstCtrlSessionHandleFileTell(pSession, pHostCtx);
1405 break;
1406
1407 case HOST_MSG_FILE_SET_SIZE:
1408 if (fImpersonated)
1409 rc = vgsvcGstCtrlSessionHandleFileSetSize(pSession, pHostCtx);
1410 break;
1411
1412 case HOST_MSG_PATH_RENAME:
1413 if (fImpersonated)
1414 rc = vgsvcGstCtrlSessionHandlePathRename(pSession, pHostCtx);
1415 break;
1416
1417 case HOST_MSG_PATH_USER_DOCUMENTS:
1418 if (fImpersonated)
1419 rc = vgsvcGstCtrlSessionHandlePathUserDocuments(pSession, pHostCtx);
1420 break;
1421
1422 case HOST_MSG_PATH_USER_HOME:
1423 if (fImpersonated)
1424 rc = vgsvcGstCtrlSessionHandlePathUserHome(pSession, pHostCtx);
1425 break;
1426
1427 default: /* Not supported, see next code block. */
1428 break;
1429 }
1430 if (RT_SUCCESS(rc))
1431 { /* likely */ }
1432 else if (rc != VERR_NOT_SUPPORTED) /* Note: Reply to host must must be sent by above handler. */
1433 VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc);
1434 else
1435 {
1436 /* We must skip and notify host here as best we can... */
1437 VGSvcVerbose(1, "Unsupported message (uMsg=%RU32, cParms=%RU32) from host, skipping\n", uMsg, pHostCtx->uNumParms);
1438 if (VbglR3GuestCtrlSupportsOptimizations(pHostCtx->uClientID))
1439 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, VERR_NOT_SUPPORTED, uMsg);
1440 else
1441 VbglR3GuestCtrlMsgSkipOld(pHostCtx->uClientID);
1442 rc = VINF_SUCCESS;
1443 }
1444
1445 if (RT_FAILURE(rc))
1446 VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc);
1447
1448 return rc;
1449}
1450
1451
1452/**
1453 * Thread main routine for a spawned guest session process.
1454 *
1455 * This thread runs in the main executable to control the spawned session process.
1456 *
1457 * @returns VBox status code.
1458 * @param hThreadSelf Thread handle.
1459 * @param pvUser Pointer to a VBOXSERVICECTRLSESSIONTHREAD structure.
1460 *
1461 */
1462static DECLCALLBACK(int) vgsvcGstCtrlSessionThread(RTTHREAD hThreadSelf, void *pvUser)
1463{
1464 PVBOXSERVICECTRLSESSIONTHREAD pThread = (PVBOXSERVICECTRLSESSIONTHREAD)pvUser;
1465 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
1466
1467 uint32_t const idSession = pThread->StartupInfo.uSessionID;
1468 uint32_t const idClient = g_idControlSvcClient;
1469 VGSvcVerbose(3, "Session ID=%RU32 thread running\n", idSession);
1470
1471 /* Let caller know that we're done initializing, regardless of the result. */
1472 int rc2 = RTThreadUserSignal(hThreadSelf);
1473 AssertRC(rc2);
1474
1475 /*
1476 * Wait for the child process to stop or the shutdown flag to be signalled.
1477 */
1478 RTPROCSTATUS ProcessStatus = { 0, RTPROCEXITREASON_NORMAL };
1479 bool fProcessAlive = true;
1480 bool fSessionCancelled = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
1481 uint32_t cMsShutdownTimeout = 30 * 1000; /** @todo Make this configurable. Later. */
1482 uint64_t msShutdownStart = 0;
1483 uint64_t const msStart = RTTimeMilliTS();
1484 size_t offSecretKey = 0;
1485 int rcWait;
1486 for (;;)
1487 {
1488 /* Secret key feeding. */
1489 if (offSecretKey < sizeof(pThread->abKey))
1490 {
1491 size_t cbWritten = 0;
1492 rc2 = RTPipeWrite(pThread->hKeyPipe, &pThread->abKey[offSecretKey], sizeof(pThread->abKey) - offSecretKey, &cbWritten);
1493 if (RT_SUCCESS(rc2))
1494 offSecretKey += cbWritten;
1495 }
1496
1497 /* Poll child process status. */
1498 rcWait = RTProcWaitNoResume(pThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
1499 if ( rcWait == VINF_SUCCESS
1500 || rcWait == VERR_PROCESS_NOT_FOUND)
1501 {
1502 fProcessAlive = false;
1503 break;
1504 }
1505 AssertMsgBreak(rcWait == VERR_PROCESS_RUNNING || rcWait == VERR_INTERRUPTED,
1506 ("Got unexpected rc=%Rrc while waiting for session process termination\n", rcWait));
1507
1508 /* Shutting down? */
1509 if (ASMAtomicReadBool(&pThread->fShutdown))
1510 {
1511 if (!msShutdownStart)
1512 {
1513 VGSvcVerbose(3, "Notifying guest session process (PID=%RU32, session ID=%RU32) ...\n",
1514 pThread->hProcess, idSession);
1515
1516 VBGLR3GUESTCTRLCMDCTX hostCtx =
1517 {
1518 /* .idClient = */ idClient,
1519 /* .idContext = */ VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
1520 /* .uProtocol = */ pThread->StartupInfo.uProtocol,
1521 /* .cParams = */ 2
1522 };
1523 rc2 = VbglR3GuestCtrlSessionClose(&hostCtx, 0 /* fFlags */);
1524 if (RT_FAILURE(rc2))
1525 {
1526 VGSvcError("Unable to notify guest session process (PID=%RU32, session ID=%RU32), rc=%Rrc\n",
1527 pThread->hProcess, idSession, rc2);
1528
1529 if (rc2 == VERR_NOT_SUPPORTED)
1530 {
1531 /* Terminate guest session process in case it's not supported by a too old host. */
1532 rc2 = RTProcTerminate(pThread->hProcess);
1533 VGSvcVerbose(3, "Terminating guest session process (PID=%RU32) ended with rc=%Rrc\n",
1534 pThread->hProcess, rc2);
1535 }
1536 break;
1537 }
1538
1539 VGSvcVerbose(3, "Guest session ID=%RU32 thread was asked to terminate, waiting for session process to exit (%RU32 ms timeout) ...\n",
1540 idSession, cMsShutdownTimeout);
1541 msShutdownStart = RTTimeMilliTS();
1542 continue; /* Don't waste time on waiting. */
1543 }
1544 if (RTTimeMilliTS() - msShutdownStart > cMsShutdownTimeout)
1545 {
1546 VGSvcVerbose(3, "Guest session ID=%RU32 process did not shut down within time\n", idSession);
1547 break;
1548 }
1549 }
1550
1551 /* Cancel the prepared session stuff after 30 seconds. */
1552 if ( !fSessionCancelled
1553 && RTTimeMilliTS() - msStart >= 30000)
1554 {
1555 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
1556 fSessionCancelled = true;
1557 }
1558
1559/** @todo r=bird: This 100ms sleep is _extremely_ sucky! */
1560 RTThreadSleep(100); /* Wait a bit. */
1561 }
1562
1563 if (!fSessionCancelled)
1564 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, idSession);
1565
1566 if (!fProcessAlive)
1567 {
1568 VGSvcVerbose(2, "Guest session process (ID=%RU32) terminated with rc=%Rrc, reason=%d, status=%d\n",
1569 idSession, rcWait, ProcessStatus.enmReason, ProcessStatus.iStatus);
1570 if (ProcessStatus.iStatus == RTEXITCODE_INIT)
1571 {
1572 VGSvcError("Guest session process (ID=%RU32) failed to initialize. Here some hints:\n", idSession);
1573 VGSvcError("- Is logging enabled and the output directory is read-only by the guest session user?\n");
1574 /** @todo Add more here. */
1575 }
1576 }
1577
1578 uint32_t uSessionStatus = GUEST_SESSION_NOTIFYTYPE_UNDEFINED;
1579 uint32_t uSessionRc = VINF_SUCCESS; /** uint32_t vs. int. */
1580
1581 if (fProcessAlive)
1582 {
1583 for (int i = 0; i < 3; i++)
1584 {
1585 if (i)
1586 RTThreadSleep(3000);
1587
1588 VGSvcVerbose(2, "Guest session ID=%RU32 process still alive, killing attempt %d/3\n", idSession, i + 1);
1589
1590 rc2 = RTProcTerminate(pThread->hProcess);
1591 if (RT_SUCCESS(rc2))
1592 break;
1593 }
1594
1595 VGSvcVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n", idSession, rc2);
1596 uSessionStatus = RT_SUCCESS(rc2) ? GUEST_SESSION_NOTIFYTYPE_TOK : GUEST_SESSION_NOTIFYTYPE_TOA;
1597 }
1598 else if (RT_SUCCESS(rcWait))
1599 {
1600 switch (ProcessStatus.enmReason)
1601 {
1602 case RTPROCEXITREASON_NORMAL:
1603 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
1604 break;
1605
1606 case RTPROCEXITREASON_ABEND:
1607 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
1608 break;
1609
1610 case RTPROCEXITREASON_SIGNAL:
1611 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
1612 break;
1613
1614 default:
1615 AssertMsgFailed(("Unhandled process termination reason (%d)\n", ProcessStatus.enmReason));
1616 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
1617 break;
1618 }
1619 }
1620 else
1621 {
1622 /* If we didn't find the guest process anymore, just assume it terminated normally. */
1623 uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
1624 }
1625
1626 /* Make sure to set stopped state before we let the host know. */
1627 ASMAtomicWriteBool(&pThread->fStopped, true);
1628
1629 /* Report final status, regardless if we failed to wait above, so that the host knows what's going on. */
1630 VGSvcVerbose(3, "Reporting final status %RU32 of session ID=%RU32\n", uSessionStatus, idSession);
1631 Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED);
1632
1633 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
1634 0 /* uProtocol, unused */, 0 /* uNumParms, unused */ };
1635 rc2 = VbglR3GuestCtrlSessionNotify(&ctx, uSessionStatus, uSessionRc);
1636 if (RT_FAILURE(rc2))
1637 VGSvcError("Reporting final status of session ID=%RU32 failed with rc=%Rrc\n", idSession, rc2);
1638
1639 VGSvcVerbose(3, "Thread for session ID=%RU32 ended with sessionStatus=%RU32, sessionRc=%Rrc\n",
1640 idSession, uSessionStatus, uSessionRc);
1641
1642 return VINF_SUCCESS;
1643}
1644
1645/**
1646 * Reads the secret key the parent VBoxService instance passed us and pass it
1647 * along as a authentication token to the host service.
1648 *
1649 * For older hosts, this sets up the message filtering.
1650 *
1651 * @returns VBox status code.
1652 * @param idClient The HGCM client ID.
1653 * @param idSession The session ID.
1654 */
1655static int vgsvcGstCtrlSessionReadKeyAndAccept(uint32_t idClient, uint32_t idSession)
1656{
1657 /*
1658 * Read it.
1659 */
1660 RTHANDLE Handle;
1661 int rc = RTHandleGetStandard(RTHANDLESTD_INPUT, &Handle);
1662 if (RT_SUCCESS(rc))
1663 {
1664 if (Handle.enmType == RTHANDLETYPE_PIPE)
1665 {
1666 uint8_t abSecretKey[RT_SIZEOFMEMB(VBOXSERVICECTRLSESSIONTHREAD, abKey)];
1667 rc = RTPipeReadBlocking(Handle.u.hPipe, abSecretKey, sizeof(abSecretKey), NULL);
1668 if (RT_SUCCESS(rc))
1669 {
1670 VGSvcVerbose(3, "Got secret key from standard input.\n");
1671
1672 /*
1673 * Do the accepting, if appropriate.
1674 */
1675 if (g_fControlSupportsOptimizations)
1676 {
1677 rc = VbglR3GuestCtrlSessionAccept(idClient, idSession, abSecretKey, sizeof(abSecretKey));
1678 if (RT_SUCCESS(rc))
1679 VGSvcVerbose(3, "Session %u accepted (client ID %u)\n", idClient, idSession);
1680 else
1681 VGSvcError("Failed to accept session %u (client ID %u): %Rrc\n", idClient, idSession, rc);
1682 }
1683 else
1684 {
1685 /* For legacy hosts, we do the filtering thingy. */
1686 rc = VbglR3GuestCtrlMsgFilterSet(idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(idSession),
1687 VBOX_GUESTCTRL_FILTER_BY_SESSION(idSession), 0);
1688 if (RT_SUCCESS(rc))
1689 VGSvcVerbose(3, "Session %u filtering successfully enabled\n", idSession);
1690 else
1691 VGSvcError("Failed to set session filter: %Rrc\n", rc);
1692 }
1693 }
1694 else
1695 VGSvcError("Error reading secret key from standard input: %Rrc\n", rc);
1696 }
1697 else
1698 {
1699 VGSvcError("Standard input is not a pipe!\n");
1700 rc = VERR_INVALID_HANDLE;
1701 }
1702 RTHandleClose(&Handle);
1703 }
1704 else
1705 VGSvcError("RTHandleGetStandard failed on standard input: %Rrc\n", rc);
1706 return rc;
1707}
1708
1709/**
1710 * Main message handler for the guest control session process.
1711 *
1712 * @returns exit code.
1713 * @param pSession Pointer to g_Session.
1714 * @thread main.
1715 */
1716static RTEXITCODE vgsvcGstCtrlSessionSpawnWorker(PVBOXSERVICECTRLSESSION pSession)
1717{
1718 AssertPtrReturn(pSession, RTEXITCODE_FAILURE);
1719 VGSvcVerbose(0, "Hi, this is guest session ID=%RU32\n", pSession->StartupInfo.uSessionID);
1720
1721 /*
1722 * Connect to the host service.
1723 */
1724 uint32_t idClient;
1725 int rc = VbglR3GuestCtrlConnect(&idClient);
1726 if (RT_FAILURE(rc))
1727 return VGSvcError("Error connecting to guest control service, rc=%Rrc\n", rc);
1728 g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(idClient);
1729 g_idControlSvcClient = idClient;
1730
1731 int rc2 = VbglR3GuestCtrlQueryFeatures(idClient, &g_fControlHostFeatures0);
1732 if (RT_FAILURE(rc2)) /* Querying host features is not fatal -- do not use rc here. */
1733 VGSvcVerbose(1, "Querying host features failed with %Rrc\n", rc2);
1734
1735 rc = vgsvcGstCtrlSessionReadKeyAndAccept(idClient, pSession->StartupInfo.uSessionID);
1736 if (RT_SUCCESS(rc))
1737 {
1738 VGSvcVerbose(1, "Using client ID=%RU32, g_fControlHostFeatures0=%#x\n", idClient, g_fControlHostFeatures0);
1739
1740 /*
1741 * Report started status.
1742 * If session status cannot be posted to the host for some reason, bail out.
1743 */
1744 VBGLR3GUESTCTRLCMDCTX ctx = { idClient, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID),
1745 0 /* uProtocol, unused */, 0 /* uNumParms, unused */ };
1746 rc = VbglR3GuestCtrlSessionNotify(&ctx, GUEST_SESSION_NOTIFYTYPE_STARTED, VINF_SUCCESS);
1747 if (RT_SUCCESS(rc))
1748 {
1749 /*
1750 * Allocate a scratch buffer for messages which also send payload data with them.
1751 * This buffer may grow if the host sends us larger chunks of data.
1752 */
1753 uint32_t cbScratchBuf = _64K;
1754 void *pvScratchBuf = RTMemAlloc(cbScratchBuf);
1755 if (pvScratchBuf)
1756 {
1757 int cFailedMsgPeeks = 0;
1758
1759 /*
1760 * Message processing loop.
1761 */
1762 VBGLR3GUESTCTRLCMDCTX CtxHost = { idClient, 0 /* Context ID */, pSession->StartupInfo.uProtocol, 0 };
1763 for (;;)
1764 {
1765 VGSvcVerbose(3, "Waiting for host msg ...\n");
1766 uint32_t uMsg = 0;
1767 rc = VbglR3GuestCtrlMsgPeekWait(idClient, &uMsg, &CtxHost.uNumParms, NULL);
1768 if (RT_SUCCESS(rc))
1769 {
1770 VGSvcVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved (%Rrc)\n", uMsg, CtxHost.uNumParms, rc);
1771
1772 /*
1773 * Pass it on to the session handler.
1774 * Note! Only when handling HOST_SESSION_CLOSE is the rc used.
1775 */
1776 bool fShutdown = false;
1777 rc = VGSvcGstCtrlSessionHandler(pSession, uMsg, &CtxHost, &pvScratchBuf, &cbScratchBuf, &fShutdown);
1778 if (fShutdown)
1779 break;
1780
1781 cFailedMsgPeeks = 0;
1782
1783 /* Let others run (guests are often single CPU) ... */
1784 RTThreadYield();
1785 }
1786 else
1787 {
1788 VGSvcVerbose(1, "Getting host message failed with %Rrc\n", rc);
1789
1790 if (cFailedMsgPeeks++ == 3)
1791 break;
1792
1793 RTThreadSleep(3 * RT_MS_1SEC);
1794
1795 /** @todo Shouldn't we have a plan for handling connection loss and such? */
1796 }
1797 }
1798
1799 /*
1800 * Shutdown.
1801 */
1802 RTMemFree(pvScratchBuf);
1803 }
1804 else
1805 rc = VERR_NO_MEMORY;
1806
1807 VGSvcVerbose(0, "Session %RU32 ended\n", pSession->StartupInfo.uSessionID);
1808 }
1809 else
1810 VGSvcError("Reporting session ID=%RU32 started status failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
1811 }
1812 else
1813 VGSvcError("Setting message filterAdd=0x%x failed with rc=%Rrc\n", pSession->StartupInfo.uSessionID, rc);
1814
1815 VGSvcVerbose(3, "Disconnecting client ID=%RU32 ...\n", idClient);
1816 VbglR3GuestCtrlDisconnect(idClient);
1817 g_idControlSvcClient = 0;
1818
1819 VGSvcVerbose(3, "Session worker returned with rc=%Rrc\n", rc);
1820 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1821}
1822
1823
1824/**
1825 * Finds a (formerly) started guest process given by its PID and increases its
1826 * reference count.
1827 *
1828 * Must be decreased by the caller with VGSvcGstCtrlProcessRelease().
1829 *
1830 * @returns Guest process if found, otherwise NULL.
1831 * @param pSession Pointer to guest session where to search process in.
1832 * @param uPID PID to search for.
1833 *
1834 * @note This does *not lock the process!
1835 */
1836PVBOXSERVICECTRLPROCESS VGSvcGstCtrlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID)
1837{
1838 AssertPtrReturn(pSession, NULL);
1839
1840 PVBOXSERVICECTRLPROCESS pProcess = NULL;
1841 int rc = RTCritSectEnter(&pSession->CritSect);
1842 if (RT_SUCCESS(rc))
1843 {
1844 PVBOXSERVICECTRLPROCESS pCurProcess;
1845 RTListForEach(&pSession->lstProcesses, pCurProcess, VBOXSERVICECTRLPROCESS, Node)
1846 {
1847 if (pCurProcess->uPID == uPID)
1848 {
1849 rc = RTCritSectEnter(&pCurProcess->CritSect);
1850 if (RT_SUCCESS(rc))
1851 {
1852 pCurProcess->cRefs++;
1853 rc = RTCritSectLeave(&pCurProcess->CritSect);
1854 AssertRC(rc);
1855 }
1856
1857 if (RT_SUCCESS(rc))
1858 pProcess = pCurProcess;
1859 break;
1860 }
1861 }
1862
1863 rc = RTCritSectLeave(&pSession->CritSect);
1864 AssertRC(rc);
1865 }
1866
1867 return pProcess;
1868}
1869
1870
1871int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession)
1872{
1873 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1874
1875 VGSvcVerbose(0, "Session %RU32 is about to close ...\n", pSession->StartupInfo.uSessionID);
1876
1877 int rc = RTCritSectEnter(&pSession->CritSect);
1878 if (RT_SUCCESS(rc))
1879 {
1880 /*
1881 * Close all guest processes.
1882 */
1883 VGSvcVerbose(0, "Stopping all guest processes ...\n");
1884
1885 /* Signal all guest processes in the active list that we want to shutdown. */
1886 PVBOXSERVICECTRLPROCESS pProcess;
1887 RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node)
1888 VGSvcGstCtrlProcessStop(pProcess);
1889
1890 VGSvcVerbose(1, "%RU32 guest processes were signalled to stop\n", pSession->cProcesses);
1891
1892 /* Wait for all active threads to shutdown and destroy the active thread list. */
1893 PVBOXSERVICECTRLPROCESS pProcessNext;
1894 RTListForEachSafe(&pSession->lstProcesses, pProcess, pProcessNext, VBOXSERVICECTRLPROCESS, Node)
1895 {
1896 int rc3 = RTCritSectLeave(&pSession->CritSect);
1897 AssertRC(rc3);
1898
1899 int rc2 = VGSvcGstCtrlProcessWait(pProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
1900
1901 rc3 = RTCritSectEnter(&pSession->CritSect);
1902 AssertRC(rc3);
1903
1904 if (RT_SUCCESS(rc2))
1905 {
1906 rc2 = vgsvcGstCtrlSessionProcessRemoveInternal(pSession, pProcess);
1907 if (RT_SUCCESS(rc2))
1908 {
1909 VGSvcGstCtrlProcessFree(pProcess);
1910 pProcess = NULL;
1911 }
1912 }
1913 }
1914
1915 AssertMsg(pSession->cProcesses == 0,
1916 ("Session process list still contains %RU32 when it should not\n", pSession->cProcesses));
1917 AssertMsg(RTListIsEmpty(&pSession->lstProcesses),
1918 ("Session process list is not empty when it should\n"));
1919
1920 /*
1921 * Close all left guest files.
1922 */
1923 VGSvcVerbose(0, "Closing all guest files ...\n");
1924
1925 PVBOXSERVICECTRLFILE pFile, pFileNext;
1926 RTListForEachSafe(&pSession->lstFiles, pFile, pFileNext, VBOXSERVICECTRLFILE, Node)
1927 {
1928 int rc2 = vgsvcGstCtrlSessionFileFree(pFile);
1929 if (RT_FAILURE(rc2))
1930 {
1931 VGSvcError("Unable to close file '%s'; rc=%Rrc\n", pFile->szName, rc2);
1932 if (RT_SUCCESS(rc))
1933 rc = rc2;
1934 /* Keep going. */
1935 }
1936
1937 pFile = NULL; /* To make it obvious. */
1938 }
1939
1940 AssertMsg(pSession->cFiles == 0,
1941 ("Session file list still contains %RU32 when it should not\n", pSession->cFiles));
1942 AssertMsg(RTListIsEmpty(&pSession->lstFiles),
1943 ("Session file list is not empty when it should\n"));
1944
1945 int rc2 = RTCritSectLeave(&pSession->CritSect);
1946 if (RT_SUCCESS(rc))
1947 rc = rc2;
1948 }
1949
1950 return rc;
1951}
1952
1953
1954int VGSvcGstCtrlSessionDestroy(PVBOXSERVICECTRLSESSION pSession)
1955{
1956 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1957
1958 int rc = VGSvcGstCtrlSessionClose(pSession);
1959
1960 /* Destroy critical section. */
1961 RTCritSectDelete(&pSession->CritSect);
1962
1963 return rc;
1964}
1965
1966
1967int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t fFlags)
1968{
1969 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1970
1971 RTListInit(&pSession->lstProcesses);
1972 RTListInit(&pSession->lstFiles);
1973
1974 pSession->cProcesses = 0;
1975 pSession->cFiles = 0;
1976
1977 pSession->fFlags = fFlags;
1978
1979 /* Init critical section for protecting the thread lists. */
1980 int rc = RTCritSectInit(&pSession->CritSect);
1981 AssertRC(rc);
1982
1983 return rc;
1984}
1985
1986
1987/**
1988 * Adds a guest process to a session's process list.
1989 *
1990 * @return VBox status code.
1991 * @param pSession Guest session to add process to.
1992 * @param pProcess Guest process to add.
1993 */
1994int VGSvcGstCtrlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
1995{
1996 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1997 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1998
1999 int rc = RTCritSectEnter(&pSession->CritSect);
2000 if (RT_SUCCESS(rc))
2001 {
2002 VGSvcVerbose(3, "Adding process (PID %RU32) to session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
2003
2004 /* Add process to session list. */
2005 RTListAppend(&pSession->lstProcesses, &pProcess->Node);
2006
2007 pSession->cProcesses++;
2008 VGSvcVerbose(3, "Now session ID=%RU32 has %RU32 processes total\n",
2009 pSession->StartupInfo.uSessionID, pSession->cProcesses);
2010
2011 int rc2 = RTCritSectLeave(&pSession->CritSect);
2012 if (RT_SUCCESS(rc))
2013 rc = rc2;
2014 }
2015
2016 return VINF_SUCCESS;
2017}
2018
2019/**
2020 * Removes a guest process from a session's process list.
2021 * Internal version, does not do locking.
2022 *
2023 * @return VBox status code.
2024 * @param pSession Guest session to remove process from.
2025 * @param pProcess Guest process to remove.
2026 */
2027static int vgsvcGstCtrlSessionProcessRemoveInternal(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
2028{
2029 VGSvcVerbose(3, "Removing process (PID %RU32) from session ID=%RU32\n", pProcess->uPID, pSession->StartupInfo.uSessionID);
2030 AssertReturn(pProcess->cRefs == 0, VERR_WRONG_ORDER);
2031
2032 RTListNodeRemove(&pProcess->Node);
2033
2034 AssertReturn(pSession->cProcesses, VERR_WRONG_ORDER);
2035 pSession->cProcesses--;
2036 VGSvcVerbose(3, "Now session ID=%RU32 has %RU32 processes total\n",
2037 pSession->StartupInfo.uSessionID, pSession->cProcesses);
2038
2039 return VINF_SUCCESS;
2040}
2041
2042/**
2043 * Removes a guest process from a session's process list.
2044 *
2045 * @return VBox status code.
2046 * @param pSession Guest session to remove process from.
2047 * @param pProcess Guest process to remove.
2048 */
2049int VGSvcGstCtrlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess)
2050{
2051 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2052 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2053
2054 int rc = RTCritSectEnter(&pSession->CritSect);
2055 if (RT_SUCCESS(rc))
2056 {
2057 rc = vgsvcGstCtrlSessionProcessRemoveInternal(pSession, pProcess);
2058
2059 int rc2 = RTCritSectLeave(&pSession->CritSect);
2060 if (RT_SUCCESS(rc))
2061 rc = rc2;
2062 }
2063
2064 return rc;
2065}
2066
2067
2068/**
2069 * Determines whether starting a new guest process according to the
2070 * maximum number of concurrent guest processes defined is allowed or not.
2071 *
2072 * @return VBox status code.
2073 * @param pSession The guest session.
2074 * @param pfAllowed \c True if starting (another) guest process
2075 * is allowed, \c false if not.
2076 */
2077int VGSvcGstCtrlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pfAllowed)
2078{
2079 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2080 AssertPtrReturn(pfAllowed, VERR_INVALID_POINTER);
2081
2082 int rc = RTCritSectEnter(&pSession->CritSect);
2083 if (RT_SUCCESS(rc))
2084 {
2085 /*
2086 * Check if we're respecting our memory policy by checking
2087 * how many guest processes are started and served already.
2088 */
2089 bool fLimitReached = false;
2090 if (pSession->uProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
2091 {
2092 VGSvcVerbose(3, "Maximum kept guest processes set to %RU32, acurrent=%RU32\n",
2093 pSession->uProcsMaxKept, pSession->cProcesses);
2094
2095 int32_t iProcsLeft = (pSession->uProcsMaxKept - pSession->cProcesses - 1);
2096 if (iProcsLeft < 0)
2097 {
2098 VGSvcVerbose(3, "Maximum running guest processes reached (%RU32)\n", pSession->uProcsMaxKept);
2099 fLimitReached = true;
2100 }
2101 }
2102
2103 *pfAllowed = !fLimitReached;
2104
2105 int rc2 = RTCritSectLeave(&pSession->CritSect);
2106 if (RT_SUCCESS(rc))
2107 rc = rc2;
2108 }
2109
2110 return rc;
2111}
2112
2113
2114/**
2115 * Cleans up stopped and no longer used processes.
2116 *
2117 * This will free and remove processes from the session's process list.
2118 *
2119 * @returns VBox status code.
2120 * @param pSession Session to clean up processes for.
2121 */
2122static int vgsvcGstCtrlSessionCleanupProcesses(const PVBOXSERVICECTRLSESSION pSession)
2123{
2124 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2125
2126 VGSvcVerbose(3, "Cleaning up stopped processes for session %RU32 ...\n", pSession->StartupInfo.uSessionID);
2127
2128 int rc2 = RTCritSectEnter(&pSession->CritSect);
2129 AssertRC(rc2);
2130
2131 int rc = VINF_SUCCESS;
2132
2133 PVBOXSERVICECTRLPROCESS pCurProcess, pNextProcess;
2134 RTListForEachSafe(&pSession->lstProcesses, pCurProcess, pNextProcess, VBOXSERVICECTRLPROCESS, Node)
2135 {
2136 if (ASMAtomicReadBool(&pCurProcess->fStopped))
2137 {
2138 rc2 = RTCritSectLeave(&pSession->CritSect);
2139 AssertRC(rc2);
2140
2141 rc = VGSvcGstCtrlProcessWait(pCurProcess, 30 * 1000 /* Wait 30 seconds max. */, NULL /* rc */);
2142 if (RT_SUCCESS(rc))
2143 {
2144 VGSvcGstCtrlSessionProcessRemove(pSession, pCurProcess);
2145 VGSvcGstCtrlProcessFree(pCurProcess);
2146 }
2147
2148 rc2 = RTCritSectEnter(&pSession->CritSect);
2149 AssertRC(rc2);
2150
2151 /* If failed, try next time we're being called. */
2152 }
2153 }
2154
2155 rc2 = RTCritSectLeave(&pSession->CritSect);
2156 AssertRC(rc2);
2157
2158 if (RT_FAILURE(rc))
2159 VGSvcError("Cleaning up stopped processes for session %RU32 failed with %Rrc\n", pSession->StartupInfo.uSessionID, rc);
2160
2161 return rc;
2162}
2163
2164
2165/**
2166 * Creates the process for a guest session.
2167 *
2168 * @return VBox status code.
2169 * @param pSessionStartupInfo Session startup info.
2170 * @param pSessionThread The session thread under construction.
2171 * @param uCtrlSessionThread The session thread debug ordinal.
2172 */
2173static int vgsvcVGSvcGstCtrlSessionThreadCreateProcess(const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
2174 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread, uint32_t uCtrlSessionThread)
2175{
2176 RT_NOREF(uCtrlSessionThread);
2177
2178 /*
2179 * Is this an anonymous session? Anonymous sessions run with the same
2180 * privileges as the main VBoxService executable.
2181 */
2182 bool const fAnonymous = pSessionThread->StartupInfo.szUser[0] == '\0';
2183 if (fAnonymous)
2184 {
2185 Assert(!strlen(pSessionThread->StartupInfo.szPassword));
2186 Assert(!strlen(pSessionThread->StartupInfo.szDomain));
2187
2188 VGSvcVerbose(3, "New anonymous guest session ID=%RU32 created, fFlags=%x, using protocol %RU32\n",
2189 pSessionStartupInfo->uSessionID,
2190 pSessionStartupInfo->fFlags,
2191 pSessionStartupInfo->uProtocol);
2192 }
2193 else
2194 {
2195 VGSvcVerbose(3, "Spawning new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, fFlags=%x, using protocol %RU32\n",
2196 pSessionStartupInfo->uSessionID,
2197 pSessionStartupInfo->szUser,
2198#ifdef DEBUG
2199 pSessionStartupInfo->szPassword,
2200#else
2201 "XXX", /* Never show passwords in release mode. */
2202#endif
2203 pSessionStartupInfo->szDomain,
2204 pSessionStartupInfo->fFlags,
2205 pSessionStartupInfo->uProtocol);
2206 }
2207
2208 /*
2209 * Spawn a child process for doing the actual session handling.
2210 * Start by assembling the argument list.
2211 */
2212 char szExeName[RTPATH_MAX];
2213 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
2214 AssertReturn(pszExeName, VERR_FILENAME_TOO_LONG);
2215
2216 char szParmSessionID[32];
2217 RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", pSessionThread->StartupInfo.uSessionID);
2218
2219 char szParmSessionProto[32];
2220 RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
2221 pSessionThread->StartupInfo.uProtocol);
2222#ifdef DEBUG
2223 char szParmThreadId[32];
2224 RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", uCtrlSessionThread);
2225#endif
2226 unsigned idxArg = 0; /* Next index in argument vector. */
2227 char const *apszArgs[24];
2228
2229 apszArgs[idxArg++] = pszExeName;
2230 apszArgs[idxArg++] = "guestsession";
2231 apszArgs[idxArg++] = szParmSessionID;
2232 apszArgs[idxArg++] = szParmSessionProto;
2233#ifdef DEBUG
2234 apszArgs[idxArg++] = szParmThreadId;
2235#endif
2236 if (!fAnonymous) /* Do we need to pass a user name? */
2237 {
2238 apszArgs[idxArg++] = "--user";
2239 apszArgs[idxArg++] = pSessionThread->StartupInfo.szUser;
2240
2241 if (strlen(pSessionThread->StartupInfo.szDomain))
2242 {
2243 apszArgs[idxArg++] = "--domain";
2244 apszArgs[idxArg++] = pSessionThread->StartupInfo.szDomain;
2245 }
2246 }
2247
2248 /* Add same verbose flags as parent process. */
2249 char szParmVerbose[32];
2250 if (g_cVerbosity > 0)
2251 {
2252 unsigned cVs = RT_MIN(g_cVerbosity, RT_ELEMENTS(szParmVerbose) - 2);
2253 szParmVerbose[0] = '-';
2254 memset(&szParmVerbose[1], 'v', cVs);
2255 szParmVerbose[1 + cVs] = '\0';
2256 apszArgs[idxArg++] = szParmVerbose;
2257 }
2258
2259 /* Add log file handling. Each session will have an own
2260 * log file, naming based on the parent log file. */
2261 char szParmLogFile[sizeof(g_szLogFile) + 128];
2262 if (g_szLogFile[0])
2263 {
2264 const char *pszSuffix = RTPathSuffix(g_szLogFile);
2265 if (!pszSuffix)
2266 pszSuffix = strchr(g_szLogFile, '\0');
2267 size_t cchBase = pszSuffix - g_szLogFile;
2268
2269 RTTIMESPEC Now;
2270 RTTimeNow(&Now);
2271 char szTime[64];
2272 RTTimeSpecToString(&Now, szTime, sizeof(szTime));
2273
2274 /* Replace out characters not allowed on Windows platforms, put in by RTTimeSpecToString(). */
2275 static const RTUNICP s_uszValidRangePairs[] =
2276 {
2277 ' ', ' ',
2278 '(', ')',
2279 '-', '.',
2280 '0', '9',
2281 'A', 'Z',
2282 'a', 'z',
2283 '_', '_',
2284 0xa0, 0xd7af,
2285 '\0'
2286 };
2287 ssize_t cReplaced = RTStrPurgeComplementSet(szTime, s_uszValidRangePairs, '_' /* chReplacement */);
2288 AssertReturn(cReplaced, VERR_INVALID_UTF8_ENCODING);
2289
2290#ifndef DEBUG
2291 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%s-%s%s",
2292 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, pSessionStartupInfo->szUser, szTime, pszSuffix);
2293#else
2294 RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), "%.*s-%RU32-%RU32-%s-%s%s",
2295 cchBase, g_szLogFile, pSessionStartupInfo->uSessionID, uCtrlSessionThread,
2296 pSessionStartupInfo->szUser, szTime, pszSuffix);
2297#endif
2298 apszArgs[idxArg++] = "--logfile";
2299 apszArgs[idxArg++] = szParmLogFile;
2300 }
2301
2302#ifdef DEBUG
2303 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT)
2304 apszArgs[idxArg++] = "--dump-stdout";
2305 if (g_Session.fFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR)
2306 apszArgs[idxArg++] = "--dump-stderr";
2307#endif
2308 apszArgs[idxArg] = NULL;
2309 Assert(idxArg < RT_ELEMENTS(apszArgs));
2310
2311 if (g_cVerbosity > 3)
2312 {
2313 VGSvcVerbose(4, "Spawning parameters:\n");
2314 for (idxArg = 0; apszArgs[idxArg]; idxArg++)
2315 VGSvcVerbose(4, " %s\n", apszArgs[idxArg]);
2316 }
2317
2318 /*
2319 * Flags.
2320 */
2321 uint32_t const fProcCreate = RTPROC_FLAGS_PROFILE
2322#ifdef RT_OS_WINDOWS
2323 | RTPROC_FLAGS_SERVICE
2324 | RTPROC_FLAGS_HIDDEN
2325#endif
2326 ;
2327
2328 /*
2329 * Configure standard handles.
2330 */
2331 RTHANDLE hStdIn;
2332 int rc = RTPipeCreate(&hStdIn.u.hPipe, &pSessionThread->hKeyPipe, RTPIPE_C_INHERIT_READ);
2333 if (RT_SUCCESS(rc))
2334 {
2335 hStdIn.enmType = RTHANDLETYPE_PIPE;
2336
2337 RTHANDLE hStdOutAndErr;
2338 rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
2339 if (RT_SUCCESS(rc))
2340 {
2341 hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
2342
2343 /*
2344 * Windows: If a domain name is given, construct an UPN (User Principle Name)
2345 * with the domain name built-in, e.g. "[email protected]".
2346 */
2347 const char *pszUser = pSessionThread->StartupInfo.szUser;
2348#ifdef RT_OS_WINDOWS
2349 char *pszUserUPN = NULL;
2350 if (pSessionThread->StartupInfo.szDomain[0])
2351 {
2352 int cchbUserUPN = RTStrAPrintf(&pszUserUPN, "%s@%s",
2353 pSessionThread->StartupInfo.szUser,
2354 pSessionThread->StartupInfo.szDomain);
2355 if (cchbUserUPN > 0)
2356 {
2357 pszUser = pszUserUPN;
2358 VGSvcVerbose(3, "Using UPN: %s\n", pszUserUPN);
2359 }
2360 else
2361 rc = VERR_NO_STR_MEMORY;
2362 }
2363 if (RT_SUCCESS(rc))
2364#endif
2365 {
2366 /*
2367 * Finally, create the process.
2368 */
2369 rc = RTProcCreateEx(pszExeName, apszArgs, RTENV_DEFAULT, fProcCreate,
2370 &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
2371 !fAnonymous ? pszUser : NULL,
2372 !fAnonymous ? pSessionThread->StartupInfo.szPassword : NULL,
2373 NULL /*pvExtraData*/,
2374 &pSessionThread->hProcess);
2375 }
2376#ifdef RT_OS_WINDOWS
2377 RTStrFree(pszUserUPN);
2378#endif
2379 RTFileClose(hStdOutAndErr.u.hFile);
2380 }
2381
2382 RTPipeClose(hStdIn.u.hPipe);
2383 }
2384 return rc;
2385}
2386
2387
2388/**
2389 * Creates a guest session.
2390 *
2391 * This will spawn a new VBoxService.exe instance under behalf of the given user
2392 * which then will act as a session host. On successful open, the session will
2393 * be added to the given session thread list.
2394 *
2395 * @return VBox status code.
2396 * @param pList Which list to use to store the session thread in.
2397 * @param pSessionStartupInfo Session startup info.
2398 * @param ppSessionThread Returns newly created session thread on success.
2399 * Optional.
2400 */
2401int VGSvcGstCtrlSessionThreadCreate(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
2402 PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
2403{
2404 AssertPtrReturn(pList, VERR_INVALID_POINTER);
2405 AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER);
2406 /* ppSessionThread is optional. */
2407
2408#ifdef VBOX_STRICT
2409 /* Check for existing session in debug mode. Should never happen because of
2410 * Main consistency. */
2411 PVBOXSERVICECTRLSESSIONTHREAD pSessionCur;
2412 RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node)
2413 {
2414 AssertMsgReturn( pSessionCur->fStopped == true
2415 || pSessionCur->StartupInfo.uSessionID != pSessionStartupInfo->uSessionID,
2416 ("Guest session thread ID=%RU32 already exists (fStopped=%RTbool)\n",
2417 pSessionCur->StartupInfo.uSessionID, pSessionCur->fStopped), VERR_ALREADY_EXISTS);
2418 }
2419#endif
2420
2421 /* Static counter to help tracking session thread <-> process relations. */
2422 static uint32_t s_uCtrlSessionThread = 0;
2423
2424 /*
2425 * Allocate and initialize the session thread structure.
2426 */
2427 int rc;
2428 PVBOXSERVICECTRLSESSIONTHREAD pSessionThread = (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(*pSessionThread));
2429 if (pSessionThread)
2430 {
2431 //pSessionThread->fShutdown = false;
2432 //pSessionThread->fStarted = false;
2433 //pSessionThread->fStopped = false;
2434 pSessionThread->hKeyPipe = NIL_RTPIPE;
2435 pSessionThread->Thread = NIL_RTTHREAD;
2436 pSessionThread->hProcess = NIL_RTPROCESS;
2437
2438 /* Copy over session startup info. */
2439 memcpy(&pSessionThread->StartupInfo, pSessionStartupInfo, sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
2440
2441 /* Generate the secret key. */
2442 RTRandBytes(pSessionThread->abKey, sizeof(pSessionThread->abKey));
2443
2444 rc = RTCritSectInit(&pSessionThread->CritSect);
2445 AssertRC(rc);
2446 if (RT_SUCCESS(rc))
2447 {
2448 /*
2449 * Give the session key to the host so it can validate the client.
2450 */
2451 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
2452 {
2453 for (uint32_t i = 0; i < 10; i++)
2454 {
2455 rc = VbglR3GuestCtrlSessionPrepare(g_idControlSvcClient, pSessionStartupInfo->uSessionID,
2456 pSessionThread->abKey, sizeof(pSessionThread->abKey));
2457 if (rc != VERR_OUT_OF_RESOURCES)
2458 break;
2459 RTThreadSleep(100);
2460 }
2461 }
2462 if (RT_SUCCESS(rc))
2463 {
2464 s_uCtrlSessionThread++;
2465
2466 /*
2467 * Start the session child process.
2468 */
2469 rc = vgsvcVGSvcGstCtrlSessionThreadCreateProcess(pSessionStartupInfo, pSessionThread, s_uCtrlSessionThread);
2470 if (RT_SUCCESS(rc))
2471 {
2472 /*
2473 * Start the session thread.
2474 */
2475 rc = RTThreadCreateF(&pSessionThread->Thread, vgsvcGstCtrlSessionThread, pSessionThread /*pvUser*/, 0 /*cbStack*/,
2476 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctls%RU32", s_uCtrlSessionThread);
2477 if (RT_SUCCESS(rc))
2478 {
2479 /* Wait for the thread to initialize. */
2480 rc = RTThreadUserWait(pSessionThread->Thread, RT_MS_1MIN);
2481 if ( RT_SUCCESS(rc)
2482 && !ASMAtomicReadBool(&pSessionThread->fShutdown))
2483 {
2484 VGSvcVerbose(2, "Thread for session ID=%RU32 started\n", pSessionThread->StartupInfo.uSessionID);
2485
2486 ASMAtomicXchgBool(&pSessionThread->fStarted, true);
2487
2488 /* Add session to list. */
2489 RTListAppend(pList, &pSessionThread->Node);
2490 if (ppSessionThread) /* Return session if wanted. */
2491 *ppSessionThread = pSessionThread;
2492 return VINF_SUCCESS;
2493 }
2494
2495 /*
2496 * Bail out.
2497 */
2498 VGSvcError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
2499 pSessionThread->StartupInfo.uSessionID, rc);
2500 if (RT_SUCCESS_NP(rc))
2501 rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
2502 }
2503 else
2504 VGSvcError("Creating session thread failed, rc=%Rrc\n", rc);
2505
2506 RTProcTerminate(pSessionThread->hProcess);
2507 uint32_t cMsWait = 1;
2508 while ( RTProcWait(pSessionThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL) == VERR_PROCESS_RUNNING
2509 && cMsWait <= 9) /* 1023 ms */
2510 {
2511 RTThreadSleep(cMsWait);
2512 cMsWait <<= 1;
2513 }
2514 }
2515
2516 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
2517 VbglR3GuestCtrlSessionCancelPrepared(g_idControlSvcClient, pSessionStartupInfo->uSessionID);
2518 }
2519 else
2520 VGSvcVerbose(3, "VbglR3GuestCtrlSessionPrepare failed: %Rrc\n", rc);
2521 RTPipeClose(pSessionThread->hKeyPipe);
2522 pSessionThread->hKeyPipe = NIL_RTPIPE;
2523 RTCritSectDelete(&pSessionThread->CritSect);
2524 }
2525 RTMemFree(pSessionThread);
2526 }
2527 else
2528 rc = VERR_NO_MEMORY;
2529
2530 VGSvcVerbose(3, "Spawning session thread returned returned rc=%Rrc\n", rc);
2531 return rc;
2532}
2533
2534
2535/**
2536 * Waits for a formerly opened guest session process to close.
2537 *
2538 * @return VBox status code.
2539 * @param pThread Guest session thread to wait for.
2540 * @param uTimeoutMS Waiting timeout (in ms).
2541 * @param fFlags Closing flags.
2542 */
2543int VGSvcGstCtrlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uTimeoutMS, uint32_t fFlags)
2544{
2545 RT_NOREF(fFlags);
2546 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2547 /** @todo Validate closing flags. */
2548
2549 AssertMsgReturn(pThread->Thread != NIL_RTTHREAD,
2550 ("Guest session thread of session %p does not exist when it should\n", pThread),
2551 VERR_NOT_FOUND);
2552
2553 int rc = VINF_SUCCESS;
2554
2555 /*
2556 * The spawned session process should have received the same closing request,
2557 * so just wait for the process to close.
2558 */
2559 if (ASMAtomicReadBool(&pThread->fStarted))
2560 {
2561 /* Ask the thread to shutdown. */
2562 ASMAtomicXchgBool(&pThread->fShutdown, true);
2563
2564 VGSvcVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
2565 pThread->StartupInfo.uSessionID, uTimeoutMS);
2566
2567 int rcThread;
2568 rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
2569 if (RT_SUCCESS(rc))
2570 {
2571 AssertMsg(pThread->fStopped, ("Thread of session ID=%RU32 not in stopped state when it should\n",
2572 pThread->StartupInfo.uSessionID));
2573
2574 VGSvcVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n", pThread->StartupInfo.uSessionID, rcThread);
2575 }
2576 else
2577 VGSvcError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n", pThread->StartupInfo.uSessionID, rc);
2578 }
2579 else
2580 VGSvcVerbose(3, "Thread for session ID=%RU32 not in started state, skipping wait\n", pThread->StartupInfo.uSessionID);
2581
2582 LogFlowFuncLeaveRC(rc);
2583 return rc;
2584}
2585
2586/**
2587 * Waits for the specified session thread to end and remove
2588 * it from the session thread list.
2589 *
2590 * @return VBox status code.
2591 * @param pThread Session thread to destroy.
2592 * @param fFlags Closing flags.
2593 */
2594int VGSvcGstCtrlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t fFlags)
2595{
2596 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
2597
2598 const uint32_t uSessionID = pThread->StartupInfo.uSessionID;
2599
2600 VGSvcVerbose(3, "Destroying session ID=%RU32 ...\n", uSessionID);
2601
2602 int rc = VGSvcGstCtrlSessionThreadWait(pThread, 5 * 60 * 1000 /* 5 minutes timeout */, fFlags);
2603 if (RT_SUCCESS(rc))
2604 {
2605 /* Remove session from list and destroy object. */
2606 RTListNodeRemove(&pThread->Node);
2607
2608 RTMemFree(pThread);
2609 pThread = NULL;
2610 }
2611
2612 VGSvcVerbose(3, "Destroyed session ID=%RU32 with %Rrc\n", uSessionID, rc);
2613 return rc;
2614}
2615
2616/**
2617 * Close all open guest session threads.
2618 *
2619 * @note Caller is responsible for locking!
2620 *
2621 * @return VBox status code.
2622 * @param pList Which list to close the session threads for.
2623 * @param fFlags Closing flags.
2624 */
2625int VGSvcGstCtrlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t fFlags)
2626{
2627 AssertPtrReturn(pList, VERR_INVALID_POINTER);
2628
2629 int rc = VINF_SUCCESS;
2630
2631 /*int rc = VbglR3GuestCtrlClose
2632 if (RT_FAILURE(rc))
2633 VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/
2634
2635 PVBOXSERVICECTRLSESSIONTHREAD pSessIt;
2636 PVBOXSERVICECTRLSESSIONTHREAD pSessItNext;
2637 RTListForEachSafe(pList, pSessIt, pSessItNext, VBOXSERVICECTRLSESSIONTHREAD, Node)
2638 {
2639 int rc2 = VGSvcGstCtrlSessionThreadDestroy(pSessIt, fFlags);
2640 if (RT_FAILURE(rc2))
2641 {
2642 VGSvcError("Closing session thread '%s' failed with rc=%Rrc\n", RTThreadGetName(pSessIt->Thread), rc2);
2643 if (RT_SUCCESS(rc))
2644 rc = rc2;
2645 /* Keep going. */
2646 }
2647 }
2648
2649 VGSvcVerbose(4, "Destroying guest session threads ended with %Rrc\n", rc);
2650 return rc;
2651}
2652
2653
2654/**
2655 * Main function for the session process.
2656 *
2657 * @returns exit code.
2658 * @param argc Argument count.
2659 * @param argv Argument vector (UTF-8).
2660 */
2661RTEXITCODE VGSvcGstCtrlSessionSpawnInit(int argc, char **argv)
2662{
2663 static const RTGETOPTDEF s_aOptions[] =
2664 {
2665 { "--domain", VBOXSERVICESESSIONOPT_DOMAIN, RTGETOPT_REQ_STRING },
2666#ifdef DEBUG
2667 { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING },
2668 { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING },
2669#endif
2670 { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING },
2671 { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING },
2672 { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 },
2673 { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 },
2674#ifdef DEBUG
2675 { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 },
2676#endif /* DEBUG */
2677 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
2678 };
2679
2680 RTGETOPTSTATE GetState;
2681 RTGetOptInit(&GetState, argc, argv,
2682 s_aOptions, RT_ELEMENTS(s_aOptions),
2683 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2684
2685 uint32_t fSession = VBOXSERVICECTRLSESSION_FLAG_SPAWN;
2686
2687 /* Protocol and session ID must be specified explicitly. */
2688 g_Session.StartupInfo.uProtocol = UINT32_MAX;
2689 g_Session.StartupInfo.uSessionID = UINT32_MAX;
2690
2691 int ch;
2692 RTGETOPTUNION ValueUnion;
2693 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
2694 {
2695 /* For options that require an argument, ValueUnion has received the value. */
2696 switch (ch)
2697 {
2698 case VBOXSERVICESESSIONOPT_DOMAIN:
2699 /* Information not needed right now, skip. */
2700 break;
2701#ifdef DEBUG
2702 case VBOXSERVICESESSIONOPT_DUMP_STDOUT:
2703 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
2704 break;
2705
2706 case VBOXSERVICESESSIONOPT_DUMP_STDERR:
2707 fSession |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
2708 break;
2709#endif
2710 case VBOXSERVICESESSIONOPT_SESSION_ID:
2711 g_Session.StartupInfo.uSessionID = ValueUnion.u32;
2712 break;
2713
2714 case VBOXSERVICESESSIONOPT_SESSION_PROTO:
2715 g_Session.StartupInfo.uProtocol = ValueUnion.u32;
2716 break;
2717#ifdef DEBUG
2718 case VBOXSERVICESESSIONOPT_THREAD_ID:
2719 /* Not handled. Mainly for processs listing. */
2720 break;
2721#endif
2722 case VBOXSERVICESESSIONOPT_LOG_FILE:
2723 {
2724 int rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
2725 if (RT_FAILURE(rc))
2726 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error copying log file name: %Rrc", rc);
2727 break;
2728 }
2729
2730 case VBOXSERVICESESSIONOPT_USERNAME:
2731 /* Information not needed right now, skip. */
2732 break;
2733
2734 /** @todo Implement help? */
2735
2736 case 'v':
2737 g_cVerbosity++;
2738 break;
2739
2740 case VINF_GETOPT_NOT_OPTION:
2741 {
2742 if (!RTStrICmp(ValueUnion.psz, VBOXSERVICECTRLSESSION_GETOPT_PREFIX))
2743 break;
2744 /* else fall through and bail out. */
2745 RT_FALL_THROUGH();
2746 }
2747 default:
2748 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'", ValueUnion.psz);
2749 }
2750 }
2751
2752 /* Check that we've got all the required options. */
2753 if (g_Session.StartupInfo.uProtocol == UINT32_MAX)
2754 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified");
2755
2756 if (g_Session.StartupInfo.uSessionID == UINT32_MAX)
2757 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified");
2758
2759 /* Init the session object. */
2760 int rc = VGSvcGstCtrlSessionInit(&g_Session, fSession);
2761 if (RT_FAILURE(rc))
2762 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to initialize session object, rc=%Rrc\n", rc);
2763
2764 rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
2765 if (RT_FAILURE(rc))
2766 return RTMsgErrorExit(RTEXITCODE_INIT, "Failed to create log file '%s', rc=%Rrc\n",
2767 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
2768
2769 RTEXITCODE rcExit = vgsvcGstCtrlSessionSpawnWorker(&g_Session);
2770
2771 VGSvcLogDestroy();
2772 return rcExit;
2773}
2774
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