VirtualBox

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

Last change on this file since 32503 was 32431, checked in by vboxsync, 14 years ago

scm cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: VBoxServiceControl.cpp 32431 2010-09-11 18:02:17Z vboxsync $ */
2/** @file
3 * VBoxServiceControl - Host-driven Guest Control.
4 */
5
6/*
7 * Copyright (C) 2010 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/getopt.h>
25#include <iprt/mem.h>
26#include <iprt/semaphore.h>
27#include <iprt/thread.h>
28#include <VBox/VBoxGuestLib.h>
29#include <VBox/HostServices/GuestControlSvc.h>
30#include "VBoxServiceInternal.h"
31#include "VBoxServiceUtils.h"
32
33using namespace guestControl;
34
35/*******************************************************************************
36* Global Variables *
37*******************************************************************************/
38/** The control interval (millseconds). */
39uint32_t g_ControlInterval = 0;
40/** The semaphore we're blocking on. */
41static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI;
42/** The guest property service client ID. */
43static uint32_t g_GuestControlSvcClientID = 0;
44/** List of spawned processes */
45RTLISTNODE g_GuestControlExecThreads;
46
47
48/** @copydoc VBOXSERVICE::pfnPreInit */
49static DECLCALLBACK(int) VBoxServiceControlPreInit(void)
50{
51 return VINF_SUCCESS;
52}
53
54
55/** @copydoc VBOXSERVICE::pfnOption */
56static DECLCALLBACK(int) VBoxServiceControlOption(const char **ppszShort, int argc, char **argv, int *pi)
57{
58 int rc = -1;
59 if (ppszShort)
60 /* no short options */;
61 else if (!strcmp(argv[*pi], "--control-interval"))
62 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
63 &g_ControlInterval, 1, UINT32_MAX - 1);
64 return rc;
65}
66
67
68/** @copydoc VBOXSERVICE::pfnInit */
69static DECLCALLBACK(int) VBoxServiceControlInit(void)
70{
71 /*
72 * If not specified, find the right interval default.
73 * Then create the event sem to block on.
74 */
75 if (!g_ControlInterval)
76 g_ControlInterval = 1000;
77
78 int rc = RTSemEventMultiCreate(&g_hControlEvent);
79 AssertRCReturn(rc, rc);
80
81 rc = VbglR3GuestCtrlConnect(&g_GuestControlSvcClientID);
82 if (RT_SUCCESS(rc))
83 {
84 VBoxServiceVerbose(3, "Control: Service Client ID: %#x\n", g_GuestControlSvcClientID);
85
86 /* Init thread list. */
87 RTListInit(&g_GuestControlExecThreads);
88 }
89 else
90 {
91 /* If the service was not found, we disable this service without
92 causing VBoxService to fail. */
93 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
94 {
95 VBoxServiceVerbose(0, "Control: Guest control service is not available\n");
96 rc = VERR_SERVICE_DISABLED;
97 }
98 else
99 VBoxServiceError("Control: Failed to connect to the guest control service! Error: %Rrc\n", rc);
100 RTSemEventMultiDestroy(g_hControlEvent);
101 g_hControlEvent = NIL_RTSEMEVENTMULTI;
102 }
103 return rc;
104}
105
106
107static int VBoxServiceControlHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
108{
109 uint32_t uContextID;
110 char szCmd[_1K];
111 uint32_t uFlags;
112 char szArgs[_1K];
113 uint32_t uNumArgs;
114 char szEnv[_64K];
115 uint32_t cbEnv = sizeof(szEnv);
116 uint32_t uNumEnvVars;
117 char szUser[128];
118 char szPassword[128];
119 uint32_t uTimeLimitMS;
120
121#if 0 /* for valgrind */
122 RT_ZERO(szCmd);
123 RT_ZERO(szArgs);
124 RT_ZERO(szEnv);
125 RT_ZERO(szUser);
126 RT_ZERO(szPassword);
127#endif
128
129 if (uNumParms != 11)
130 return VERR_INVALID_PARAMETER;
131
132 int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
133 uNumParms,
134 &uContextID,
135 /* Command */
136 szCmd, sizeof(szCmd),
137 /* Flags */
138 &uFlags,
139 /* Arguments */
140 szArgs, sizeof(szArgs), &uNumArgs,
141 /* Environment */
142 szEnv, &cbEnv, &uNumEnvVars,
143 /* Credentials */
144 szUser, sizeof(szUser),
145 szPassword, sizeof(szPassword),
146 /* Timelimit */
147 &uTimeLimitMS);
148 if (RT_FAILURE(rc))
149 {
150 VBoxServiceError("Control: Failed to retrieve exec start command! Error: %Rrc\n", rc);
151 }
152 else
153 {
154 rc = VBoxServiceControlExecProcess(uContextID, szCmd, uFlags, szArgs, uNumArgs,
155 szEnv, cbEnv, uNumEnvVars,
156 szUser, szPassword, uTimeLimitMS);
157 }
158
159 VBoxServiceVerbose(3, "Control: VBoxServiceControlHandleCmdStartProcess returned with %Rrc\n", rc);
160 return rc;
161}
162
163
164static int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
165{
166 uint32_t uContextID;
167 uint32_t uPID;
168 uint32_t uHandleID;
169 uint32_t uFlags;
170
171 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
172 &uContextID, &uPID, &uHandleID, &uFlags);
173 if (RT_FAILURE(rc))
174 {
175 VBoxServiceError("Control: Failed to retrieve exec output command! Error: %Rrc\n", rc);
176 }
177 else
178 {
179 /* Let's have a look if we have a running process with PID = uPID ... */
180 PVBOXSERVICECTRLTHREAD pNode;
181 bool fFound = false;
182 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
183 {
184 if ( pNode->fStarted
185 && pNode->enmType == VBoxServiceCtrlThreadDataExec)
186 {
187 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
188 if (pData && pData->uPID == uPID)
189 {
190 fFound = true;
191 break;
192 }
193 }
194 }
195
196 if (fFound)
197 {
198 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
199 AssertPtr(pData);
200
201 const uint32_t cbSize = _4K;
202 uint32_t cbRead = cbSize;
203 uint8_t *pBuf = (uint8_t*)RTMemAlloc(cbSize);
204 if (pBuf)
205 {
206 rc = VBoxServiceControlExecReadPipeBufferContent(&pData->stdOut, pBuf, cbSize, &cbRead);
207 if (RT_SUCCESS(rc))
208 {
209 /* cbRead now contains actual size. */
210 rc = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, 0 /* handle ID */, 0 /* flags */,
211 pBuf, cbRead);
212 }
213 RTMemFree(pBuf);
214 }
215 else
216 rc = VERR_NO_MEMORY;
217 }
218 else
219 rc = VERR_NOT_FOUND; /* PID not found! */
220 }
221 VBoxServiceVerbose(3, "Control: VBoxServiceControlHandleCmdGetOutput returned with %Rrc\n", rc);
222 return rc;
223}
224
225
226/** @copydoc VBOXSERVICE::pfnWorker */
227DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown)
228{
229 /*
230 * Tell the control thread that it can continue
231 * spawning services.
232 */
233 RTThreadUserSignal(RTThreadSelf());
234 Assert(g_GuestControlSvcClientID > 0);
235
236 int rc = VINF_SUCCESS;
237
238 /*
239 * Execution loop.
240 *
241 * @todo
242 */
243 for (;;)
244 {
245 uint32_t uMsg;
246 uint32_t uNumParms;
247 VBoxServiceVerbose(3, "Control: Waiting for host msg ...\n");
248 rc = VbglR3GuestCtrlGetHostMsg(g_GuestControlSvcClientID, &uMsg, &uNumParms);
249 if (RT_FAILURE(rc))
250 {
251 if (rc == VERR_TOO_MUCH_DATA)
252 {
253 VBoxServiceVerbose(4, "Control: Message requires %ld parameters, but only 2 supplied -- retrying request (no error!)...\n", uNumParms);
254 rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */
255 }
256 else
257 VBoxServiceVerbose(3, "Control: Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
258 }
259
260 if (RT_SUCCESS(rc))
261 {
262 VBoxServiceVerbose(3, "Control: Msg=%u (%u parms) retrieved\n", uMsg, uNumParms);
263 switch(uMsg)
264 {
265 case GETHOSTMSG_EXEC_HOST_CANCEL_WAIT:
266 VBoxServiceVerbose(3, "Control: Host asked us to quit ...\n");
267 break;
268
269 case GETHOSTMSG_EXEC_START_PROCESS:
270 rc = VBoxServiceControlHandleCmdStartProcess(g_GuestControlSvcClientID, uNumParms);
271 break;
272
273 case GETHOSTMSG_EXEC_GET_OUTPUT:
274 rc = VBoxServiceControlHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);
275 break;
276
277 default:
278 VBoxServiceVerbose(3, "Control: Unsupported message from host! Msg=%u\n", uMsg);
279 /* Don't terminate here; just wait for the next message. */
280 break;
281 }
282
283 if (RT_FAILURE(rc))
284 VBoxServiceVerbose(3, "Control: Message was processed with rc=%Rrc\n", rc);
285 }
286
287 /* Do we need to shutdown? */
288 if ( *pfShutdown
289 || uMsg == GETHOSTMSG_EXEC_HOST_CANCEL_WAIT)
290 {
291 rc = VINF_SUCCESS;
292 break;
293 }
294
295 /* Let's sleep for a bit and let others run ... */
296 RTThreadYield();
297 }
298
299 RTSemEventMultiDestroy(g_hControlEvent);
300 g_hControlEvent = NIL_RTSEMEVENTMULTI;
301 return rc;
302}
303
304
305/** @copydoc VBOXSERVICE::pfnStop */
306static DECLCALLBACK(void) VBoxServiceControlStop(void)
307{
308 VBoxServiceVerbose(3, "Control: Stopping ...\n");
309
310 /** @todo Later, figure what to do if we're in RTProcWait(). it's a very
311 * annoying call since doesn't support timeouts in the posix world. */
312 RTSemEventMultiSignal(g_hControlEvent);
313
314 /*
315 * Ask the host service to cancel all pending requests so that we can
316 * shutdown properly here.
317 */
318 if (g_GuestControlSvcClientID)
319 {
320 int rc = VbglR3GuestCtrlCancelPendingWaits(g_GuestControlSvcClientID);
321 if (RT_FAILURE(rc))
322 VBoxServiceError("Control: Cancelling pending waits failed; rc=%Rrc\n", rc);
323 }
324}
325
326
327/** @copydoc VBOXSERVICE::pfnTerm */
328static DECLCALLBACK(void) VBoxServiceControlTerm(void)
329{
330 VBoxServiceVerbose(3, "Control: Terminating ...\n");
331
332 /* Signal all threads that we want to shutdown. */
333 PVBOXSERVICECTRLTHREAD pNode;
334 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
335 ASMAtomicXchgBool(&pNode->fShutdown, true);
336
337 /* Wait for threads to shutdown. */
338 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
339 {
340 if (pNode->Thread != NIL_RTTHREAD)
341 {
342 /* Wait a bit ... */
343 int rc2 = RTThreadWait(pNode->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);
344 if (RT_FAILURE(rc2))
345 VBoxServiceError("Control: Thread failed to stop; rc2=%Rrc\n", rc2);
346 }
347
348 /* Destroy thread specific data. */
349 switch (pNode->enmType)
350 {
351 case VBoxServiceCtrlThreadDataExec:
352 VBoxServiceControlExecDestroyThreadData((PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData);
353 break;
354
355 default:
356 break;
357 }
358 }
359
360 /* Finally destroy thread list. */
361 pNode = RTListNodeGetFirst(&g_GuestControlExecThreads, VBOXSERVICECTRLTHREAD, Node);
362 while (pNode)
363 {
364 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node);
365 bool fLast = RTListNodeIsLast(&g_GuestControlExecThreads, &pNode->Node);
366
367 RTListNodeRemove(&pNode->Node);
368 RTMemFree(pNode);
369
370 if (fLast)
371 break;
372
373 pNode = pNext;
374 }
375
376 VbglR3GuestCtrlDisconnect(g_GuestControlSvcClientID);
377 g_GuestControlSvcClientID = 0;
378
379 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
380 {
381 RTSemEventMultiDestroy(g_hControlEvent);
382 g_hControlEvent = NIL_RTSEMEVENTMULTI;
383 }
384}
385
386
387/**
388 * The 'vminfo' service description.
389 */
390VBOXSERVICE g_Control =
391{
392 /* pszName. */
393 "control",
394 /* pszDescription. */
395 "Host-driven Guest Control",
396 /* pszUsage. */
397 " [--control-interval <ms>]"
398 ,
399 /* pszOptions. */
400 " --control-interval Specifies the interval at which to check for\n"
401 " new control commands. The default is 1000 ms.\n"
402 ,
403 /* methods */
404 VBoxServiceControlPreInit,
405 VBoxServiceControlOption,
406 VBoxServiceControlInit,
407 VBoxServiceControlWorker,
408 VBoxServiceControlStop,
409 VBoxServiceControlTerm
410};
411
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