VirtualBox

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

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

GuestCtrl: Implemented using (public) VirtualBox events instead of own callback mechanisms. Bugfixes for testcases (still work in progress).

  • 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 45415 2013-04-08 21:40:42Z 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.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/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 VBoxServiceVerbose(4, "Message requires %ld parameters, but only 2 supplied -- retrying request (no error!)...\n", cParms);
213 rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */
214 }
215 else if (RT_FAILURE(rc))
216 VBoxServiceVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
217 if (RT_SUCCESS(rc))
218 {
219 VBoxServiceVerbose(3, "Msg=%RU32 (%RU32 parms) retrieved\n", uMsg, cParms);
220
221 /* Set number of parameters for current host context. */
222 ctxHost.uNumParms = cParms;
223
224 /* Check for VM session change. */
225 uint64_t idNewSession = g_idControlSession;
226 int rc2 = VbglR3GetSessionId(&idNewSession);
227 if ( RT_SUCCESS(rc2)
228 && (idNewSession != g_idControlSession))
229 {
230 VBoxServiceVerbose(1, "The VM session ID changed\n");
231 g_idControlSession = idNewSession;
232
233 /* Close all opened guest sessions -- all context IDs, sessions etc.
234 * are now invalid. */
235 rc2 = GstCntlSessionClose(&g_Session);
236 AssertRC(rc2);
237 }
238
239 switch (uMsg)
240 {
241 case HOST_CANCEL_PENDING_WAITS:
242 VBoxServiceVerbose(1, "We were asked to quit ...\n");
243 break;
244
245 case HOST_SESSION_CREATE:
246 rc = gstcntlHandleSessionOpen(&ctxHost);
247 break;
248
249 case HOST_SESSION_CLOSE:
250 rc = gstcntlHandleSessionClose(&ctxHost);
251 break;
252
253 default:
254 {
255 /*
256 * Protocol v1 did not have support for (dedicated)
257 * guest sessions, so all actions need to be performed
258 * under behalf of VBoxService's main executable.
259 *
260 * The global session object then acts as a host for all
261 * started guest processes which bring all their
262 * credentials with them with the actual execution call.
263 */
264 if (ctxHost.uProtocol == 1)
265 {
266 rc = GstCntlSessionHandler(&g_Session, uMsg, &ctxHost,
267 pvScratchBuf, cbScratchBuf, pfShutdown);
268 }
269 else
270 {
271 /*
272 * ... on newer protocols handling all other commands is
273 * up to the guest session fork of VBoxService, so just
274 * skip all not wanted messages here.
275 */
276 rc = VbglR3GuestCtrlMsgSkip(g_uControlSvcClientID);
277 VBoxServiceVerbose(3, "Skipping msg=%RU32, rc=%Rrc\n", uMsg, rc);
278 }
279 break;
280 }
281 }
282 }
283
284 /* Do we need to shutdown? */
285 if ( *pfShutdown
286 || (RT_SUCCESS(rc) && uMsg == HOST_CANCEL_PENDING_WAITS))
287 {
288 break;
289 }
290
291 /* Let's sleep for a bit and let others run ... */
292 RTThreadYield();
293 }
294
295 VBoxServiceVerbose(0, "Guest control service stopped\n");
296
297 /* Delete scratch buffer. */
298 if (pvScratchBuf)
299 RTMemFree(pvScratchBuf);
300
301 VBoxServiceVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc);
302 return rc;
303}
304
305
306static int gstcntlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
307{
308 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
309
310 VBOXSERVICECTRLSESSIONSTARTUPINFO ssInfo = { 0 };
311 int rc = VbglR3GuestCtrlSessionGetOpen(pHostCtx,
312 &ssInfo.uProtocol,
313 ssInfo.szUser, sizeof(ssInfo.szUser),
314 ssInfo.szPassword, sizeof(ssInfo.szPassword),
315 ssInfo.szDomain, sizeof(ssInfo.szDomain),
316 &ssInfo.uFlags, &ssInfo.uSessionID);
317 if (RT_SUCCESS(rc))
318 {
319 /* The session open call has the protocol version the host
320 * wants to use. So update the current protocol version with the one the
321 * host wants to use in subsequent calls. */
322 pHostCtx->uProtocol = ssInfo.uProtocol;
323 VBoxServiceVerbose(3, "Client ID=%RU32 now is using protocol %RU32\n",
324 pHostCtx->uClientID, pHostCtx->uProtocol);
325
326 rc = GstCntlSessionThreadCreate(&g_lstControlSessionThreads,
327 &ssInfo, NULL /* Session */);
328 }
329
330 if (RT_FAILURE(rc))
331 {
332 /* Report back on failure. On success this will be done
333 * by the forked session thread. */
334 int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
335 GUEST_SESSION_NOTIFYTYPE_ERROR, rc /* uint32_t vs. int */);
336 if (RT_FAILURE(rc2))
337 {
338 VBoxServiceError("Reporting session error status on open failed with rc=%Rrc\n", rc2);
339 rc = rc2;
340 }
341 }
342
343 VBoxServiceVerbose(3, "Opening a new guest session returned rc=%Rrc\n", rc);
344 return rc;
345}
346
347
348static int gstcntlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
349{
350 AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
351
352 uint32_t uSessionID, uFlags;
353 int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &uFlags, &uSessionID);
354 if (RT_SUCCESS(rc))
355 {
356 rc = VERR_NOT_FOUND;
357
358 PVBOXSERVICECTRLSESSIONTHREAD pThread;
359 RTListForEach(&g_lstControlSessionThreads, pThread, VBOXSERVICECTRLSESSIONTHREAD, Node)
360 {
361 if (pThread->StartupInfo.uSessionID == uSessionID)
362 {
363 rc = GstCntlSessionThreadDestroy(pThread, uFlags);
364 break;
365 }
366 }
367#if 0
368 if (RT_FAILURE(rc))
369 {
370 /* Report back on failure. On success this will be done
371 * by the forked session thread. */
372 int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
373 GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
374 if (RT_FAILURE(rc2))
375 {
376 VBoxServiceError("Reporting session error status on close failed with rc=%Rrc\n", rc2);
377 if (RT_SUCCESS(rc))
378 rc = rc2;
379 }
380 }
381#endif
382 VBoxServiceVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n",
383 uSessionID, rc);
384 }
385 else
386 VBoxServiceError("Closing guest session failed with rc=%Rrc\n", rc);
387
388 return rc;
389}
390
391
392/** @copydoc VBOXSERVICE::pfnStop */
393static DECLCALLBACK(void) VBoxServiceControlStop(void)
394{
395 VBoxServiceVerbose(3, "Stopping ...\n");
396
397 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
398 * annoying call since doesn't support timeouts in the posix world. */
399 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
400 RTSemEventMultiSignal(g_hControlEvent);
401
402 /*
403 * Ask the host service to cancel all pending requests so that we can
404 * shutdown properly here.
405 */
406 if (g_uControlSvcClientID)
407 {
408 VBoxServiceVerbose(3, "Cancelling pending waits (client ID=%u) ...\n",
409 g_uControlSvcClientID);
410
411 int rc = VbglR3GuestCtrlCancelPendingWaits(g_uControlSvcClientID);
412 if (RT_FAILURE(rc))
413 VBoxServiceError("Cancelling pending waits failed; rc=%Rrc\n", rc);
414 }
415}
416
417
418/**
419 * Destroys all guest process threads which are still active.
420 */
421static void VBoxServiceControlShutdown(void)
422{
423 VBoxServiceVerbose(2, "Shutting down ...\n");
424
425 int rc2 = GstCntlSessionThreadDestroyAll(&g_lstControlSessionThreads,
426 0 /* Flags */);
427 if (RT_FAILURE(rc2))
428 VBoxServiceError("Closing session threads failed with rc=%Rrc\n", rc2);
429
430 rc2 = GstCntlSessionClose(&g_Session);
431 if (RT_FAILURE(rc2))
432 VBoxServiceError("Closing session failed with rc=%Rrc\n", rc2);
433
434 VBoxServiceVerbose(2, "Shutting down complete\n");
435}
436
437
438/** @copydoc VBOXSERVICE::pfnTerm */
439static DECLCALLBACK(void) VBoxServiceControlTerm(void)
440{
441 VBoxServiceVerbose(3, "Terminating ...\n");
442
443 VBoxServiceControlShutdown();
444
445 VBoxServiceVerbose(3, "Disconnecting client ID=%u ...\n",
446 g_uControlSvcClientID);
447 VbglR3GuestCtrlDisconnect(g_uControlSvcClientID);
448 g_uControlSvcClientID = 0;
449
450 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
451 {
452 RTSemEventMultiDestroy(g_hControlEvent);
453 g_hControlEvent = NIL_RTSEMEVENTMULTI;
454 }
455}
456
457
458/**
459 * The 'vminfo' service description.
460 */
461VBOXSERVICE g_Control =
462{
463 /* pszName. */
464 "control",
465 /* pszDescription. */
466 "Host-driven Guest Control",
467 /* pszUsage. */
468#ifdef DEBUG
469 " [--control-dump-stderr] [--control-dump-stdout]\n"
470#endif
471 " [--control-interval <ms>]\n"
472 " [--control-procs-mem-std[in|out|err] <KB>]"
473 ,
474 /* pszOptions. */
475#ifdef DEBUG
476 " --control-dump-stderr Dumps all guest proccesses stderr data to the\n"
477 " temporary directory.\n"
478 " --control-dump-stdout Dumps all guest proccesses stdout data to the\n"
479 " temporary directory.\n"
480#endif
481 " --control-interval Specifies the interval at which to check for\n"
482 " new control commands. The default is 1000 ms.\n"
483 ,
484 /* methods */
485 VBoxServiceControlPreInit,
486 VBoxServiceControlOption,
487 VBoxServiceControlInit,
488 VBoxServiceControlWorker,
489 VBoxServiceControlStop,
490 VBoxServiceControlTerm
491};
492
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