VirtualBox

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

Last change on this file since 106243 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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