VirtualBox

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

Last change on this file since 59134 was 59134, checked in by vboxsync, 9 years ago

Guest Control/VBoxService: Build up a UPN (User Principal Name) if a domain is given. Untested.

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