VirtualBox

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

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

GuestCtrl: More code for guest session infrastructure handling (untested, work in progress).

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