VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp

Last change on this file was 102831, checked in by vboxsync, 4 months ago

Guest Control: Added a new guest feature flag to indicate Guest Additions support for retrieving guest mount points. bugref:10415

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.3 KB
RevLine 
[27865]1/* $Id: VBoxServiceControl.cpp 102831 2024-01-11 08:56:53Z vboxsync $ */
2/** @file
3 * VBoxServiceControl - Host-driven Guest Control.
4 */
5
6/*
[98103]7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
[27865]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[27865]26 */
27
[58033]28/** @page pg_vgsvc_gstctrl VBoxService - Guest Control
29 *
30 * The Guest Control subservice helps implementing the IGuest APIs.
31 *
[58052]32 * The communication between this service (and its children) and IGuest goes
33 * over the HGCM GuestControl service.
34 *
[58033]35 * The IGuest APIs provides means to manipulate (control) files, directories,
36 * symbolic links and processes within the guest. Most of these means requires
37 * credentials of a guest OS user to operate, though some restricted ones
38 * operates directly as the VBoxService user (root / system service account).
39 *
40 * The current design is that a subprocess is spawned for handling operations as
41 * a given user. This process is represented as IGuestSession in the API. The
42 * subprocess will be spawned as the given use, giving up the privileges the
43 * parent subservice had.
44 *
45 * It will try handle as many of the operations directly from within the
46 * subprocess, but for more complicated things (or things that haven't yet been
47 * converted), it will spawn a helper process that does the actual work.
48 *
49 * These helpers are the typically modeled on similar unix core utilities, like
50 * mkdir, rm, rmdir, cat and so on. The helper tools can also be launched
51 * directly from VBoxManage by the user by prepending the 'vbox_' prefix to the
52 * unix command.
53 *
54 */
[27865]55
[58033]56
[57358]57/*********************************************************************************************************************************
58* Header Files *
59*********************************************************************************************************************************/
[38157]60#include <iprt/asm.h>
[27865]61#include <iprt/assert.h>
[44863]62#include <iprt/env.h>
[40059]63#include <iprt/file.h>
[27926]64#include <iprt/getopt.h>
[27865]65#include <iprt/mem.h>
[40059]66#include <iprt/path.h>
[44863]67#include <iprt/process.h>
[27865]68#include <iprt/semaphore.h>
69#include <iprt/thread.h>
[76419]70#include <VBox/err.h>
[27865]71#include <VBox/VBoxGuestLib.h>
72#include <VBox/HostServices/GuestControlSvc.h>
73#include "VBoxServiceInternal.h"
[44863]74#include "VBoxServiceControl.h"
[27865]75#include "VBoxServiceUtils.h"
76
77using namespace guestControl;
78
[57358]79
80/*********************************************************************************************************************************
81* Global Variables *
82*********************************************************************************************************************************/
[33540]83/** The control interval (milliseconds). */
[58029]84static uint32_t g_msControlInterval = 0;
[39279]85/** The semaphore we're blocking our main control thread on. */
[39427]86static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI;
[44863]87/** The VM session ID. Changes whenever the VM is restored or reset. */
88static uint64_t g_idControlSession;
[38157]89/** The guest control service client ID. */
[75807]90uint32_t g_idControlSvcClient = 0;
[79296]91/** VBOX_GUESTCTRL_HF_XXX */
92uint64_t g_fControlHostFeatures0 = 0;
[62882]93#if 0 /** @todo process limit */
[38157]94/** How many started guest processes are kept into memory for supplying
[42738]95 * information to the host. Default is 256 processes. If 0 is specified,
[38157]96 * the maximum number of processes is unlimited. */
[42738]97static uint32_t g_uControlProcsMaxKept = 256;
[62882]98#endif
[44963]99/** List of guest control session threads (VBOXSERVICECTRLSESSIONTHREAD).
100 * A guest session thread represents a forked guest session process
101 * of VBoxService. */
102RTLISTANCHOR g_lstControlSessionThreads;
103/** The local session object used for handling all session-related stuff.
104 * When using the legacy guest control protocol (< 2), this session runs
105 * under behalf of the VBoxService main process. On newer protocol versions
106 * each session is a forked version of VBoxService using the appropriate
[45010]107 * user credentials for opening a guest session. These forked sessions then
108 * are kept in VBOXSERVICECTRLSESSIONTHREAD structures. */
[44963]109VBOXSERVICECTRLSESSION g_Session;
[75807]110/** Copy of VbglR3GuestCtrlSupportsOptimizations().*/
111bool g_fControlSupportsOptimizations = true;
[27865]112
[57358]113
114/*********************************************************************************************************************************
115* Internal Functions *
116*********************************************************************************************************************************/
[58029]117static int vgsvcGstCtrlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
118static int vgsvcGstCtrlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
[84209]119static int vgsvcGstCtrlInvalidate(void);
[58029]120static void vgsvcGstCtrlShutdown(void);
[39279]121
[39427]122
[58029]123/**
124 * @interface_method_impl{VBOXSERVICE,pfnPreInit}
125 */
126static DECLCALLBACK(int) vgsvcGstCtrlPreInit(void)
[27865]127{
[47551]128 int rc;
[38157]129#ifdef VBOX_WITH_GUEST_PROPS
130 /*
131 * Read the service options from the VM's guest properties.
132 * Note that these options can be overridden by the command line options later.
133 */
134 uint32_t uGuestPropSvcClientID;
[47551]135 rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
[38157]136 if (RT_FAILURE(rc))
137 {
138 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
139 {
[58029]140 VGSvcVerbose(0, "Guest property service is not available, skipping\n");
[38157]141 rc = VINF_SUCCESS;
142 }
143 else
[58029]144 VGSvcError("Failed to connect to the guest property service, rc=%Rrc\n", rc);
[38157]145 }
146 else
147 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
148
149 if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
150 rc = VINF_SUCCESS;
151#else
152 /* Nothing to do here yet. */
[47551]153 rc = VINF_SUCCESS;
[38157]154#endif
[47551]155
156 if (RT_SUCCESS(rc))
157 {
158 /* Init session object. */
[58029]159 rc = VGSvcGstCtrlSessionInit(&g_Session, 0 /* Flags */);
[47551]160 }
161
162 return rc;
[27865]163}
164
165
[58029]166/**
167 * @interface_method_impl{VBOXSERVICE,pfnOption}
168 */
169static DECLCALLBACK(int) vgsvcGstCtrlOption(const char **ppszShort, int argc, char **argv, int *pi)
[27865]170{
171 int rc = -1;
172 if (ppszShort)
173 /* no short options */;
174 else if (!strcmp(argv[*pi], "--control-interval"))
[58029]175 rc = VGSvcArgUInt32(argc, argv, "", pi,
176 &g_msControlInterval, 1, UINT32_MAX - 1);
[40059]177#ifdef DEBUG
[44963]178 else if (!strcmp(argv[*pi], "--control-dump-stdout"))
[40059]179 {
[57659]180 g_Session.fFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
[40059]181 rc = 0; /* Flag this command as parsed. */
182 }
[44963]183 else if (!strcmp(argv[*pi], "--control-dump-stderr"))
[40059]184 {
[57659]185 g_Session.fFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
[40059]186 rc = 0; /* Flag this command as parsed. */
187 }
188#endif
[27865]189 return rc;
190}
191
192
[58029]193/**
194 * @interface_method_impl{VBOXSERVICE,pfnInit}
195 */
196static DECLCALLBACK(int) vgsvcGstCtrlInit(void)
[27865]197{
198 /*
199 * If not specified, find the right interval default.
200 * Then create the event sem to block on.
201 */
[58029]202 if (!g_msControlInterval)
203 g_msControlInterval = 1000;
[27865]204
205 int rc = RTSemEventMultiCreate(&g_hControlEvent);
206 AssertRCReturn(rc, rc);
207
[75807]208 VbglR3GetSessionId(&g_idControlSession); /* The status code is ignored as this information is not available with VBox < 3.2.10. */
[44863]209
[75807]210 RTListInit(&g_lstControlSessionThreads);
211
212 /*
213 * Try connect to the host service and tell it we want to be master (if supported).
214 */
215 rc = VbglR3GuestCtrlConnect(&g_idControlSvcClient);
[27865]216 if (RT_SUCCESS(rc))
[29202]217 {
[84209]218 rc = vgsvcGstCtrlInvalidate();
[75807]219 if (RT_SUCCESS(rc))
[84209]220 return rc;
[29202]221 }
[27865]222 else
223 {
[29345]224 /* If the service was not found, we disable this service without
225 causing VBoxService to fail. */
[29316]226 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
[29345]227 {
[58029]228 VGSvcVerbose(0, "Guest control service is not available\n");
[29345]229 rc = VERR_SERVICE_DISABLED;
230 }
[29202]231 else
[58029]232 VGSvcError("Failed to connect to the guest control service! Error: %Rrc\n", rc);
[27865]233 }
[75807]234 RTSemEventMultiDestroy(g_hControlEvent);
235 g_hControlEvent = NIL_RTSEMEVENTMULTI;
236 g_idControlSvcClient = 0;
[27865]237 return rc;
238}
239
[84209]240static int vgsvcGstCtrlInvalidate(void)
241{
242 VGSvcVerbose(1, "Invalidating configuration ...\n");
[27926]243
[84209]244 int rc = VINF_SUCCESS;
245
246 g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
247 if (g_fControlSupportsOptimizations)
248 rc = VbglR3GuestCtrlMakeMeMaster(g_idControlSvcClient);
249 if (RT_SUCCESS(rc))
250 {
251 VGSvcVerbose(3, "Guest control service client ID=%RU32%s\n",
252 g_idControlSvcClient, g_fControlSupportsOptimizations ? " w/ optimizations" : "");
253
254 /*
255 * Report features to the host.
256 */
257 const uint64_t fGuestFeatures = VBOX_GUESTCTRL_GF_0_SET_SIZE
[84243]258 | VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0
[84548]259 | VBOX_GUESTCTRL_GF_0_PROCESS_DYNAMIC_SIZES
[99120]260 | VBOX_GUESTCTRL_GF_0_PROCESS_CWD
[98526]261#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
262 | VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS
263#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
[102831]264 | VBOX_GUESTCTRL_GF_0_SHUTDOWN
265 | VBOX_GUESTCTRL_GF_0_MOUNT_POINTS_ENUM;
[84209]266 rc = VbglR3GuestCtrlReportFeatures(g_idControlSvcClient, fGuestFeatures, &g_fControlHostFeatures0);
267 if (RT_SUCCESS(rc))
268 VGSvcVerbose(3, "Host features: %#RX64\n", g_fControlHostFeatures0);
269 else
[102831]270 VGSvcVerbose(1, "Warning! Feature reporting failed: %Rrc\n", rc);
[84209]271
272 return VINF_SUCCESS;
273 }
274 VGSvcError("Failed to become guest control master: %Rrc\n", rc);
275 VbglR3GuestCtrlDisconnect(g_idControlSvcClient);
276
277 return rc;
278}
279
[58029]280/**
281 * @interface_method_impl{VBOXSERVICE,pfnWorker}
282 */
283static DECLCALLBACK(int) vgsvcGstCtrlWorker(bool volatile *pfShutdown)
[27865]284{
285 /*
[75807]286 * Tell the control thread that it can continue spawning services.
[27865]287 */
288 RTThreadUserSignal(RTThreadSelf());
[75803]289 Assert(g_idControlSvcClient > 0);
[27865]290
[76958]291 /* Allocate a scratch buffer for messages which also send
[42846]292 * payload data with them. */
293 uint32_t cbScratchBuf = _64K; /** @todo Make buffer size configurable via guest properties/argv! */
294 AssertReturn(RT_IS_POWER_OF_TWO(cbScratchBuf), VERR_INVALID_PARAMETER);
295 uint8_t *pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf);
[54944]296 AssertReturn(pvScratchBuf, VERR_NO_MEMORY);
[42846]297
[75803]298 int rc = VINF_SUCCESS; /* (shut up compiler warnings) */
299 int cRetrievalFailed = 0; /* Number of failed message retrievals in a row. */
300 while (!*pfShutdown)
[27865]301 {
[75722]302 VGSvcVerbose(3, "GstCtrl: Waiting for host msg ...\n");
[75853]303 VBGLR3GUESTCTRLCMDCTX ctxHost = { g_idControlSvcClient, 0 /*idContext*/, 2 /*uProtocol*/, 0 /*cParms*/ };
304 uint32_t idMsg = 0;
305 rc = VbglR3GuestCtrlMsgPeekWait(g_idControlSvcClient, &idMsg, &ctxHost.uNumParms, &g_idControlSession);
[27865]306 if (RT_SUCCESS(rc))
307 {
[50810]308 cRetrievalFailed = 0; /* Reset failed retrieval count. */
[75853]309 VGSvcVerbose(4, "idMsg=%RU32 (%s) (%RU32 parms) retrieved\n",
[76958]310 idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), ctxHost.uNumParms);
[47817]311
[75803]312 /*
313 * Handle the host message.
314 */
315 switch (idMsg)
[27865]316 {
[76958]317 case HOST_MSG_CANCEL_PENDING_WAITS:
[58029]318 VGSvcVerbose(1, "We were asked to quit ...\n");
[29438]319 break;
320
[76958]321 case HOST_MSG_SESSION_CREATE:
[58029]322 rc = vgsvcGstCtrlHandleSessionOpen(&ctxHost);
[27865]323 break;
324
[75807]325 /* This message is also sent to the child session process (by the host). */
[76958]326 case HOST_MSG_SESSION_CLOSE:
[58029]327 rc = vgsvcGstCtrlHandleSessionClose(&ctxHost);
[28557]328 break;
329
[27865]330 default:
[75807]331 if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
332 {
[75824]333 rc = VbglR3GuestCtrlMsgSkip(g_idControlSvcClient, VERR_NOT_SUPPORTED, idMsg);
[75807]334 VGSvcVerbose(1, "Skipped unexpected message idMsg=%RU32 (%s), cParms=%RU32 (rc=%Rrc)\n",
[76958]335 idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), ctxHost.uNumParms, rc);
[75807]336 }
[45010]337 else
338 {
[75803]339 rc = VbglR3GuestCtrlMsgSkipOld(g_idControlSvcClient);
[75853]340 VGSvcVerbose(3, "Skipped idMsg=%RU32, cParms=%RU32, rc=%Rrc\n", idMsg, ctxHost.uNumParms, rc);
[45010]341 }
[27865]342 break;
343 }
[75803]344
345 /* Do we need to shutdown? */
[76958]346 if (idMsg == HOST_MSG_CANCEL_PENDING_WAITS)
[75803]347 break;
348
349 /* Let's sleep for a bit and let others run ... */
350 RTThreadYield();
[27865]351 }
[75853]352 /*
353 * Handle restore notification from host. All the context IDs (sessions,
354 * files, proceses, etc) are invalidated by a VM restore and must be closed.
355 */
356 else if (rc == VERR_VM_RESTORED)
357 {
[95505]358 VGSvcVerbose(1, "The VM session ID changed (i.e. restored), closing stale root session\n");
359
360 /* Make sure that all other session threads are gone.
361 * This is necessary, as the new VM session (NOT to be confused with guest session!) will re-use
362 * the guest session IDs. */
363 int rc2 = VGSvcGstCtrlSessionThreadDestroyAll(&g_lstControlSessionThreads, 0 /* Flags */);
364 if (RT_FAILURE(rc2))
365 VGSvcError("Closing session threads failed with rc=%Rrc\n", rc2);
366
367 /* Make sure to also close the root session (session 0). */
368 rc2 = VGSvcGstCtrlSessionClose(&g_Session);
[75853]369 AssertRC(rc2);
[84209]370
371 rc2 = VbglR3GuestCtrlSessionHasChanged(g_idControlSvcClient, g_idControlSession);
372 AssertRC(rc2);
373
374 /* Invalidate the internal state to match the current host we got restored from. */
375 rc2 = vgsvcGstCtrlInvalidate();
376 AssertRC(rc2);
[75853]377 }
[75803]378 else
379 {
380 /* Note: VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
381 /** @todo r=bird: Above comment makes no sense. How can you get a timeout in a blocking HGCM call? */
382 VGSvcError("GstCtrl: Getting host message failed with %Rrc\n", rc);
[27976]383
[75803]384 /* Check for VM session change. */
[75853]385 /** @todo We don't need to check the host here. */
[75803]386 uint64_t idNewSession = g_idControlSession;
387 int rc2 = VbglR3GetSessionId(&idNewSession);
388 if ( RT_SUCCESS(rc2)
389 && (idNewSession != g_idControlSession))
390 {
391 VGSvcVerbose(1, "GstCtrl: The VM session ID changed\n");
392 g_idControlSession = idNewSession;
393
394 /* Close all opened guest sessions -- all context IDs, sessions etc.
395 * are now invalid. */
396 rc2 = VGSvcGstCtrlSessionClose(&g_Session);
397 AssertRC(rc2);
398
399 /* Do a reconnect. */
400 VGSvcVerbose(1, "Reconnecting to HGCM service ...\n");
401 rc2 = VbglR3GuestCtrlConnect(&g_idControlSvcClient);
402 if (RT_SUCCESS(rc2))
403 {
404 VGSvcVerbose(3, "Guest control service client ID=%RU32\n", g_idControlSvcClient);
405 cRetrievalFailed = 0;
406 continue; /* Skip waiting. */
407 }
408 VGSvcError("Unable to re-connect to HGCM service, rc=%Rrc, bailing out\n", rc);
409 break;
410 }
411
412 if (rc == VERR_INTERRUPTED)
413 RTThreadYield(); /* To be on the safe side... */
414 else if (++cRetrievalFailed <= 16) /** @todo Make this configurable? */
415 RTThreadSleep(1000); /* Wait a bit before retrying. */
416 else
417 {
418 VGSvcError("Too many failed attempts in a row to get next message, bailing out\n");
419 break;
420 }
[28598]421 }
[27865]422 }
423
[58029]424 VGSvcVerbose(0, "Guest control service stopped\n");
[44863]425
[42846]426 /* Delete scratch buffer. */
427 if (pvScratchBuf)
428 RTMemFree(pvScratchBuf);
429
[58029]430 VGSvcVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc);
[27865]431 return rc;
432}
433
434
[58029]435static int vgsvcGstCtrlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
[44863]436{
437 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
438
[75807]439 /*
440 * Retrieve the message parameters.
441 */
[84215]442 PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo;
443 int rc = VbglR3GuestCtrlSessionGetOpen(pHostCtx, &pStartupInfo);
[44863]444 if (RT_SUCCESS(rc))
445 {
[75807]446 /*
447 * Flat out refuse to work with protocol v1 hosts.
448 */
[84215]449 if (pStartupInfo->uProtocol == 2)
[75807]450 {
[84215]451 pHostCtx->uProtocol = pStartupInfo->uProtocol;
[75807]452 VGSvcVerbose(3, "Client ID=%RU32 now is using protocol %RU32\n", pHostCtx->uClientID, pHostCtx->uProtocol);
[44863]453
[75807]454/** @todo Someone explain why this code isn't in this file too? v1 support? */
[84215]455 rc = VGSvcGstCtrlSessionThreadCreate(&g_lstControlSessionThreads, pStartupInfo, NULL /* ppSessionThread */);
[75807]456 /* Report failures to the host (successes are taken care of by the session thread). */
457 }
458 else
459 {
[84215]460 VGSvcError("The host wants to use protocol v%u, we only support v2!\n", pStartupInfo->uProtocol);
[75807]461 rc = VERR_VERSION_MISMATCH;
462 }
463 if (RT_FAILURE(rc))
464 {
465 int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx, GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
466 if (RT_FAILURE(rc2))
467 VGSvcError("Reporting session error status on open failed with rc=%Rrc\n", rc2);
468 }
[44863]469 }
[75807]470 else
[44863]471 {
[75824]472 VGSvcError("Error fetching parameters for opening guest session: %Rrc\n", rc);
473 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
[44863]474 }
[84147]475
[84215]476 VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo);
477 pStartupInfo = NULL;
[84147]478
[58029]479 VGSvcVerbose(3, "Opening a new guest session returned rc=%Rrc\n", rc);
[44863]480 return rc;
481}
482
483
[58029]484static int vgsvcGstCtrlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
[44863]485{
486 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
487
[75824]488 uint32_t idSession;
[58029]489 uint32_t fFlags;
[75824]490 int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &fFlags, &idSession);
[44863]491 if (RT_SUCCESS(rc))
492 {
493 rc = VERR_NOT_FOUND;
494
[45010]495 PVBOXSERVICECTRLSESSIONTHREAD pThread;
496 RTListForEach(&g_lstControlSessionThreads, pThread, VBOXSERVICECTRLSESSIONTHREAD, Node)
[44863]497 {
[84147]498 if ( pThread->pStartupInfo
499 && pThread->pStartupInfo->uSessionID == idSession)
[44863]500 {
[58029]501 rc = VGSvcGstCtrlSessionThreadDestroy(pThread, fFlags);
[44863]502 break;
503 }
504 }
[75824]505
506#if 0 /** @todo A bit of a mess here as this message goes to both to this process (master) and the session process. */
[45010]507 if (RT_FAILURE(rc))
508 {
509 /* Report back on failure. On success this will be done
510 * by the forked session thread. */
[45109]511 int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
[45010]512 GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
513 if (RT_FAILURE(rc2))
514 {
[58029]515 VGSvcError("Reporting session error status on close failed with rc=%Rrc\n", rc2);
[45010]516 if (RT_SUCCESS(rc))
517 rc = rc2;
518 }
519 }
[45415]520#endif
[75824]521 VGSvcVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n", idSession, rc);
[44863]522 }
[45415]523 else
[75824]524 {
525 VGSvcError("Error fetching parameters for closing guest session: %Rrc\n", rc);
526 VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
527 }
[44863]528 return rc;
529}
530
531
[58029]532/**
533 * @interface_method_impl{VBOXSERVICE,pfnStop}
534 */
535static DECLCALLBACK(void) vgsvcGstCtrlStop(void)
[27865]536{
[58029]537 VGSvcVerbose(3, "Stopping ...\n");
[29438]538
[39279]539 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
[27865]540 * annoying call since doesn't support timeouts in the posix world. */
[39300]541 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
542 RTSemEventMultiSignal(g_hControlEvent);
[29438]543
544 /*
[49349]545 * Ask the host service to cancel all pending requests for the main
546 * control thread so that we can shutdown properly here.
[29438]547 */
[75803]548 if (g_idControlSvcClient)
[29438]549 {
[58029]550 VGSvcVerbose(3, "Cancelling pending waits (client ID=%u) ...\n",
[75803]551 g_idControlSvcClient);
[39300]552
[75803]553 int rc = VbglR3GuestCtrlCancelPendingWaits(g_idControlSvcClient);
[29438]554 if (RT_FAILURE(rc))
[58029]555 VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);
[29438]556 }
[27865]557}
558
[38587]559
[39418]560/**
[44863]561 * Destroys all guest process threads which are still active.
562 */
[58029]563static void vgsvcGstCtrlShutdown(void)
[44863]564{
[58029]565 VGSvcVerbose(2, "Shutting down ...\n");
[42846]566
[58029]567 int rc2 = VGSvcGstCtrlSessionThreadDestroyAll(&g_lstControlSessionThreads, 0 /* Flags */);
[45010]568 if (RT_FAILURE(rc2))
[58029]569 VGSvcError("Closing session threads failed with rc=%Rrc\n", rc2);
[42846]570
[58029]571 rc2 = VGSvcGstCtrlSessionClose(&g_Session);
[45010]572 if (RT_FAILURE(rc2))
[58029]573 VGSvcError("Closing session failed with rc=%Rrc\n", rc2);
[45010]574
[58029]575 VGSvcVerbose(2, "Shutting down complete\n");
[38157]576}
577
578
[58029]579/**
580 * @interface_method_impl{VBOXSERVICE,pfnTerm}
581 */
582static DECLCALLBACK(void) vgsvcGstCtrlTerm(void)
[27865]583{
[58029]584 VGSvcVerbose(3, "Terminating ...\n");
[29438]585
[58029]586 vgsvcGstCtrlShutdown();
[28420]587
[75803]588 VGSvcVerbose(3, "Disconnecting client ID=%u ...\n", g_idControlSvcClient);
589 VbglR3GuestCtrlDisconnect(g_idControlSvcClient);
590 g_idControlSvcClient = 0;
[27865]591
592 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
593 {
594 RTSemEventMultiDestroy(g_hControlEvent);
595 g_hControlEvent = NIL_RTSEMEVENTMULTI;
596 }
597}
598
599
[39300]600/**
[27865]601 * The 'vminfo' service description.
602 */
603VBOXSERVICE g_Control =
604{
605 /* pszName. */
606 "control",
607 /* pszDescription. */
608 "Host-driven Guest Control",
609 /* pszUsage. */
[40059]610#ifdef DEBUG
[83974]611 " [--control-dump-stderr] [--control-dump-stdout]\n"
[40059]612#endif
[83974]613 " [--control-interval <ms>]"
[27865]614 ,
615 /* pszOptions. */
[40060]616#ifdef DEBUG
[40059]617 " --control-dump-stderr Dumps all guest proccesses stderr data to the\n"
618 " temporary directory.\n"
619 " --control-dump-stdout Dumps all guest proccesses stdout data to the\n"
620 " temporary directory.\n"
[40060]621#endif
[29594]622 " --control-interval Specifies the interval at which to check for\n"
[76958]623 " new control messages. The default is 1000 ms.\n"
[27865]624 ,
625 /* methods */
[58029]626 vgsvcGstCtrlPreInit,
627 vgsvcGstCtrlOption,
628 vgsvcGstCtrlInit,
629 vgsvcGstCtrlWorker,
630 vgsvcGstCtrlStop,
631 vgsvcGstCtrlTerm
[27865]632};
633
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use