VirtualBox

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

Last change on this file since 47545 was 47545, checked in by vboxsync, 12 years ago

VBoxService/GuestCtrl: Implemented asynchronous request queue, bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.4 KB
Line 
1/* $Id: VBoxServiceControl.cpp 47545 2013-08-05 19:09:25Z vboxsync $ */
2/** @file
3 * VBoxServiceControl - Host-driven Guest Control.
4 */
5
6/*
7 * Copyright (C) 2012-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/env.h>
25#include <iprt/file.h>
26#include <iprt/getopt.h>
27#include <iprt/mem.h>
28#include <iprt/path.h>
29#include <iprt/process.h>
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <VBox/VBoxGuestLib.h>
33#include <VBox/HostServices/GuestControlSvc.h>
34#include "VBoxServiceInternal.h"
35#include "VBoxServiceControl.h"
36#include "VBoxServiceUtils.h"
37
38using namespace guestControl;
39
40/*******************************************************************************
41* Global Variables *
42*******************************************************************************/
43/** The control interval (milliseconds). */
44static uint32_t g_uControlIntervalMS = 0;
45/** The semaphore we're blocking our main control thread on. */
46static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI;
47/** The VM session ID. Changes whenever the VM is restored or reset. */
48static uint64_t g_idControlSession;
49/** The guest control service client ID. */
50static uint32_t g_uControlSvcClientID = 0;
51/** How many started guest processes are kept into memory for supplying
52 * information to the host. Default is 256 processes. If 0 is specified,
53 * the maximum number of processes is unlimited. */
54static uint32_t g_uControlProcsMaxKept = 256;
55/** List of guest control session threads (VBOXSERVICECTRLSESSIONTHREAD).
56 * A guest session thread represents a forked guest session process
57 * of VBoxService. */
58RTLISTANCHOR g_lstControlSessionThreads;
59/** The local session object used for handling all session-related stuff.
60 * When using the legacy guest control protocol (< 2), this session runs
61 * under behalf of the VBoxService main process. On newer protocol versions
62 * each session is a forked version of VBoxService using the appropriate
63 * user credentials for opening a guest session. These forked sessions then
64 * are kept in VBOXSERVICECTRLSESSIONTHREAD structures. */
65VBOXSERVICECTRLSESSION g_Session;
66
67/*******************************************************************************
68* Internal Functions *
69*******************************************************************************/
70static int gstcntlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
71static int gstcntlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
72static void VBoxServiceControlShutdown(void);
73
74
75/** @copydoc VBOXSERVICE::pfnPreInit */
76static DECLCALLBACK(int) VBoxServiceControlPreInit(void)
77{
78#ifdef VBOX_WITH_GUEST_PROPS
79 /*
80 * Read the service options from the VM's guest properties.
81 * Note that these options can be overridden by the command line options later.
82 */
83 uint32_t uGuestPropSvcClientID;
84 int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
85 if (RT_FAILURE(rc))
86 {
87 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
88 {
89 VBoxServiceVerbose(0, "Guest property service is not available, skipping\n");
90 rc = VINF_SUCCESS;
91 }
92 else
93 VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
94 }
95 else
96 {
97 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
98 }
99
100 if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
101 rc = VINF_SUCCESS;
102 return rc;
103#else
104 /* Nothing to do here yet. */
105 return VINF_SUCCESS;
106#endif
107}
108
109
110/** @copydoc VBOXSERVICE::pfnOption */
111static DECLCALLBACK(int) VBoxServiceControlOption(const char **ppszShort, int argc, char **argv, int *pi)
112{
113 int rc = -1;
114 if (ppszShort)
115 /* no short options */;
116 else if (!strcmp(argv[*pi], "--control-interval"))
117 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
118 &g_uControlIntervalMS, 1, UINT32_MAX - 1);
119#ifdef DEBUG
120 else if (!strcmp(argv[*pi], "--control-dump-stdout"))
121 {
122 g_Session.uFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
123 rc = 0; /* Flag this command as parsed. */
124 }
125 else if (!strcmp(argv[*pi], "--control-dump-stderr"))
126 {
127 g_Session.uFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
128 rc = 0; /* Flag this command as parsed. */
129 }
130#endif
131 return rc;
132}
133
134
135/** @copydoc VBOXSERVICE::pfnInit */
136static DECLCALLBACK(int) VBoxServiceControlInit(void)
137{
138 /*
139 * If not specified, find the right interval default.
140 * Then create the event sem to block on.
141 */
142 if (!g_uControlIntervalMS)
143 g_uControlIntervalMS = 1000;
144
145 int rc = RTSemEventMultiCreate(&g_hControlEvent);
146 AssertRCReturn(rc, rc);
147
148 VbglR3GetSessionId(&g_idControlSession);
149 /* The status code is ignored as this information is not available with VBox < 3.2.10. */
150
151 /* Init session object. */
152 rc = GstCntlSessionInit(&g_Session, 0 /* Flags */);
153 if (RT_SUCCESS(rc))
154 rc = VbglR3GuestCtrlConnect(&g_uControlSvcClientID);
155 if (RT_SUCCESS(rc))
156 {
157 VBoxServiceVerbose(3, "Guest control service client ID=%RU32\n",
158 g_uControlSvcClientID);
159
160 /* Init session thread list. */
161 RTListInit(&g_lstControlSessionThreads);
162 }
163 else
164 {
165 /* If the service was not found, we disable this service without
166 causing VBoxService to fail. */
167 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
168 {
169 VBoxServiceVerbose(0, "Guest control service is not available\n");
170 rc = VERR_SERVICE_DISABLED;
171 }
172 else
173 VBoxServiceError("Failed to connect to the guest control service! Error: %Rrc\n", rc);
174 RTSemEventMultiDestroy(g_hControlEvent);
175 g_hControlEvent = NIL_RTSEMEVENTMULTI;
176 }
177 return rc;
178}
179
180
181/** @copydoc VBOXSERVICE::pfnWorker */
182DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown)
183{
184 /*
185 * Tell the control thread that it can continue
186 * spawning services.
187 */
188 RTThreadUserSignal(RTThreadSelf());
189 Assert(g_uControlSvcClientID > 0);
190
191 int rc = VINF_SUCCESS;
192
193 /* Allocate a scratch buffer for commands which also send
194 * payload data with them. */
195 uint32_t cbScratchBuf = _64K; /** @todo Make buffer size configurable via guest properties/argv! */
196 AssertReturn(RT_IS_POWER_OF_TWO(cbScratchBuf), VERR_INVALID_PARAMETER);
197 uint8_t *pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf);
198 AssertPtrReturn(pvScratchBuf, VERR_NO_MEMORY);
199
200 VBGLR3GUESTCTRLCMDCTX ctxHost = { g_uControlSvcClientID };
201 /* Set default protocol version to 1. */
202 ctxHost.uProtocol = 1;
203
204 for (;;)
205 {
206 VBoxServiceVerbose(3, "Waiting for host msg ...\n");
207 uint32_t uMsg = 0;
208 uint32_t cParms = 0;
209 rc = VbglR3GuestCtrlMsgWaitFor(g_uControlSvcClientID, &uMsg, &cParms);
210 if (rc == VERR_TOO_MUCH_DATA)
211 {
212#ifdef DEBUG
213 VBoxServiceVerbose(4, "Message requires %ld parameters, but only 2 supplied -- retrying request (no error!)...\n", cParms);
214#endif
215 rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */
216 }
217 else if (RT_FAILURE(rc))
218 VBoxServiceVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
219 if (RT_SUCCESS(rc))
220 {
221#ifdef DEBUG
222 VBoxServiceVerbose(3, "Msg=%RU32 (%RU32 parms) retrieved\n", uMsg, cParms);
223#endif
224 /* Set number of parameters for current host context. */
225 ctxHost.uNumParms = cParms;
226
227 /* Check for VM session change. */
228 uint64_t idNewSession = g_idControlSession;
229 int rc2 = VbglR3GetSessionId(&idNewSession);
230 if ( RT_SUCCESS(rc2)
231 && (idNewSession != g_idControlSession))
232 {
233 VBoxServiceVerbose(1, "The VM session ID changed\n");
234 g_idControlSession = idNewSession;
235
236 /* Close all opened guest sessions -- all context IDs, sessions etc.
237 * are now invalid. */
238 rc2 = GstCntlSessionClose(&g_Session);
239 AssertRC(rc2);
240 }
241
242 switch (uMsg)
243 {
244 case HOST_CANCEL_PENDING_WAITS:
245 VBoxServiceVerbose(1, "We were asked to quit ...\n");
246 break;
247
248 case HOST_SESSION_CREATE:
249 rc = gstcntlHandleSessionOpen(&ctxHost);
250 break;
251
252 case HOST_SESSION_CLOSE:
253 rc = gstcntlHandleSessionClose(&ctxHost);
254 break;
255
256 default:
257 {
258 /*
259 * Protocol v1 did not have support for (dedicated)
260 * guest sessions, so all actions need to be performed
261 * under behalf of VBoxService's main executable.
262 *
263 * The global session object then acts as a host for all
264 * started guest processes which bring all their
265 * credentials with them with the actual execution call.
266 */
267 if (ctxHost.uProtocol == 1)
268 {
269 rc = GstCntlSessionHandler(&g_Session, uMsg, &ctxHost,
270 pvScratchBuf, cbScratchBuf, pfShutdown);
271 }
272 else
273 {
274 /*
275 * ... on newer protocols handling all other commands is
276 * up to the guest session fork of VBoxService, so just
277 * skip all not wanted messages here.
278 */
279 rc = VbglR3GuestCtrlMsgSkip(g_uControlSvcClientID);
280 VBoxServiceVerbose(3, "Skipping uMsg=%RU32, cParms=%RU32, rc=%Rrc\n",
281 uMsg, cParms, rc);
282 }
283 break;
284 }
285 }
286 }
287
288 /* Do we need to shutdown? */
289 if ( *pfShutdown
290 || (RT_SUCCESS(rc) && uMsg == HOST_CANCEL_PENDING_WAITS))
291 {
292 break;
293 }
294
295 /* Let's sleep for a bit and let others run ... */
296 RTThreadYield();
297 }
298
299 VBoxServiceVerbose(0, "Guest control service stopped\n");
300
301 /* Delete scratch buffer. */
302 if (pvScratchBuf)
303 RTMemFree(pvScratchBuf);
304
305 VBoxServiceVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc);
306 return rc;
307}
308
309
310static int gstcntlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
311{
312 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
313
314 VBOXSERVICECTRLSESSIONSTARTUPINFO ssInfo = { 0 };
315 int rc = VbglR3GuestCtrlSessionGetOpen(pHostCtx,
316 &ssInfo.uProtocol,
317 ssInfo.szUser, sizeof(ssInfo.szUser),
318 ssInfo.szPassword, sizeof(ssInfo.szPassword),
319 ssInfo.szDomain, sizeof(ssInfo.szDomain),
320 &ssInfo.uFlags, &ssInfo.uSessionID);
321 if (RT_SUCCESS(rc))
322 {
323 /* The session open call has the protocol version the host
324 * wants to use. So update the current protocol version with the one the
325 * host wants to use in subsequent calls. */
326 pHostCtx->uProtocol = ssInfo.uProtocol;
327 VBoxServiceVerbose(3, "Client ID=%RU32 now is using protocol %RU32\n",
328 pHostCtx->uClientID, pHostCtx->uProtocol);
329
330 rc = GstCntlSessionThreadCreate(&g_lstControlSessionThreads,
331 &ssInfo, NULL /* Session */);
332 }
333
334 if (RT_FAILURE(rc))
335 {
336 /* Report back on failure. On success this will be done
337 * by the forked session thread. */
338 int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
339 GUEST_SESSION_NOTIFYTYPE_ERROR, rc /* uint32_t vs. int */);
340 if (RT_FAILURE(rc2))
341 {
342 VBoxServiceError("Reporting session error status on open failed with rc=%Rrc\n", rc2);
343 rc = rc2;
344 }
345 }
346
347 VBoxServiceVerbose(3, "Opening a new guest session returned rc=%Rrc\n", rc);
348 return rc;
349}
350
351
352static int gstcntlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
353{
354 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
355
356 uint32_t uSessionID, uFlags;
357 int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &uFlags, &uSessionID);
358 if (RT_SUCCESS(rc))
359 {
360 rc = VERR_NOT_FOUND;
361
362 PVBOXSERVICECTRLSESSIONTHREAD pThread;
363 RTListForEach(&g_lstControlSessionThreads, pThread, VBOXSERVICECTRLSESSIONTHREAD, Node)
364 {
365 if (pThread->StartupInfo.uSessionID == uSessionID)
366 {
367 rc = GstCntlSessionThreadDestroy(pThread, uFlags);
368 break;
369 }
370 }
371#if 0
372 if (RT_FAILURE(rc))
373 {
374 /* Report back on failure. On success this will be done
375 * by the forked session thread. */
376 int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
377 GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
378 if (RT_FAILURE(rc2))
379 {
380 VBoxServiceError("Reporting session error status on close failed with rc=%Rrc\n", rc2);
381 if (RT_SUCCESS(rc))
382 rc = rc2;
383 }
384 }
385#endif
386 VBoxServiceVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n",
387 uSessionID, rc);
388 }
389 else
390 VBoxServiceError("Closing guest session failed with rc=%Rrc\n", rc);
391
392 return rc;
393}
394
395
396/** @copydoc VBOXSERVICE::pfnStop */
397static DECLCALLBACK(void) VBoxServiceControlStop(void)
398{
399 VBoxServiceVerbose(3, "Stopping ...\n");
400
401 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
402 * annoying call since doesn't support timeouts in the posix world. */
403 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
404 RTSemEventMultiSignal(g_hControlEvent);
405
406 /*
407 * Ask the host service to cancel all pending requests so that we can
408 * shutdown properly here.
409 */
410 if (g_uControlSvcClientID)
411 {
412 VBoxServiceVerbose(3, "Cancelling pending waits (client ID=%u) ...\n",
413 g_uControlSvcClientID);
414
415 int rc = VbglR3GuestCtrlCancelPendingWaits(g_uControlSvcClientID);
416 if (RT_FAILURE(rc))
417 VBoxServiceError("Cancelling pending waits failed; rc=%Rrc\n", rc);
418 }
419}
420
421
422/**
423 * Destroys all guest process threads which are still active.
424 */
425static void VBoxServiceControlShutdown(void)
426{
427 VBoxServiceVerbose(2, "Shutting down ...\n");
428
429 int rc2 = GstCntlSessionThreadDestroyAll(&g_lstControlSessionThreads,
430 0 /* Flags */);
431 if (RT_FAILURE(rc2))
432 VBoxServiceError("Closing session threads failed with rc=%Rrc\n", rc2);
433
434 rc2 = GstCntlSessionClose(&g_Session);
435 if (RT_FAILURE(rc2))
436 VBoxServiceError("Closing session failed with rc=%Rrc\n", rc2);
437
438 VBoxServiceVerbose(2, "Shutting down complete\n");
439}
440
441
442/** @copydoc VBOXSERVICE::pfnTerm */
443static DECLCALLBACK(void) VBoxServiceControlTerm(void)
444{
445 VBoxServiceVerbose(3, "Terminating ...\n");
446
447 VBoxServiceControlShutdown();
448
449 VBoxServiceVerbose(3, "Disconnecting client ID=%u ...\n",
450 g_uControlSvcClientID);
451 VbglR3GuestCtrlDisconnect(g_uControlSvcClientID);
452 g_uControlSvcClientID = 0;
453
454 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
455 {
456 RTSemEventMultiDestroy(g_hControlEvent);
457 g_hControlEvent = NIL_RTSEMEVENTMULTI;
458 }
459}
460
461
462/**
463 * The 'vminfo' service description.
464 */
465VBOXSERVICE g_Control =
466{
467 /* pszName. */
468 "control",
469 /* pszDescription. */
470 "Host-driven Guest Control",
471 /* pszUsage. */
472#ifdef DEBUG
473 " [--control-dump-stderr] [--control-dump-stdout]\n"
474#endif
475 " [--control-interval <ms>]"
476 ,
477 /* pszOptions. */
478#ifdef DEBUG
479 " --control-dump-stderr Dumps all guest proccesses stderr data to the\n"
480 " temporary directory.\n"
481 " --control-dump-stdout Dumps all guest proccesses stdout data to the\n"
482 " temporary directory.\n"
483#endif
484 " --control-interval Specifies the interval at which to check for\n"
485 " new control commands. The default is 1000 ms.\n"
486 ,
487 /* methods */
488 VBoxServiceControlPreInit,
489 VBoxServiceControlOption,
490 VBoxServiceControlInit,
491 VBoxServiceControlWorker,
492 VBoxServiceControlStop,
493 VBoxServiceControlTerm
494};
495
Note: See TracBrowser for help on using the repository browser.

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