VirtualBox

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

最後變更 在這個檔案從95851是 95505,由 vboxsync 提交於 3 年 前

Guest Control/VBoxService: Handle new VM sessions (i.e. when restoring from VM snapshot) more gracefully. bugref:10157

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette