VirtualBox

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

Last change on this file since 29316 was 29316, checked in by vboxsync, 15 years ago

VBoxService: More generic way for letting services disable themselves; logging adjustments, some todos.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: VBoxServiceControl.cpp 29316 2010-05-11 08:12:08Z 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 (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
92 VBoxServiceVerbose(0, "Control: Guest control service is not available\n");
93 else
94 VBoxServiceError("Control: Failed to connect to the guest control service! Error: %Rrc\n", rc);
95 RTSemEventMultiDestroy(g_hControlEvent);
96 g_hControlEvent = NIL_RTSEMEVENTMULTI;
97
98 /*
99 * Not having the guest control service on the host renders this whole service
100 * unusable, so report that we are not able to continue.
101 */
102 rc = VERR_NOT_SUPPORTED;
103 }
104 return rc;
105}
106
107
108static int VBoxServiceControlHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
109{
110 uint32_t uContextID;
111 char szCmd[_1K];
112 uint32_t uFlags;
113 char szArgs[_1K];
114 uint32_t uNumArgs;
115 char szEnv[_64K];
116 uint32_t cbEnv = sizeof(szEnv);
117 uint32_t uNumEnvVars;
118 char szStdIn[_1K];
119 char szStdOut[_1K];
120 char szStdErr[_1K];
121 char szUser[128];
122 char szPassword[128];
123 uint32_t uTimeLimitMS;
124
125 if (uNumParms != 14)
126 return VERR_INVALID_PARAMETER;
127
128 int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
129 uNumParms,
130 &uContextID,
131 /* Command */
132 szCmd, sizeof(szCmd),
133 /* Flags */
134 &uFlags,
135 /* Arguments */
136 szArgs, sizeof(szArgs), &uNumArgs,
137 /* Environment */
138 szEnv, &cbEnv, &uNumEnvVars,
139 /* Pipes */
140 szStdIn, sizeof(szStdIn),
141 szStdOut, sizeof(szStdOut),
142 szStdErr, sizeof(szStdErr),
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 szStdIn, szStdOut, szStdErr,
157 szUser, szPassword, uTimeLimitMS);
158 }
159
160 VBoxServiceVerbose(3, "Control: VBoxServiceControlHandleCmdStartProcess returned with %Rrc\n", rc);
161 return rc;
162}
163
164
165static int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
166{
167 uint32_t uContextID;
168 uint32_t uPID;
169 uint32_t uHandleID;
170 uint32_t uFlags;
171
172 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
173 &uContextID, &uPID, &uHandleID, &uFlags);
174 if (RT_FAILURE(rc))
175 {
176 VBoxServiceError("Control: Failed to retrieve exec output command! Error: %Rrc\n", rc);
177 }
178 else
179 {
180 /* Let's have a look if we have a running process with PID = uPID ... */
181 PVBOXSERVICECTRLTHREAD pNode;
182 bool bFound = false;
183 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
184 {
185 if ( pNode->fStarted
186 && pNode->enmType == VBoxServiceCtrlThreadDataExec)
187 {
188 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
189 if (pData && pData->uPID == uPID)
190 {
191 bFound = true;
192 break;
193 }
194 }
195 }
196
197 if (bFound)
198 {
199 PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
200 AssertPtr(pData);
201
202 uint32_t cbSize = _4K;
203 uint32_t cbRead = cbSize;
204 uint8_t *pBuf = (uint8_t*)RTMemAlloc(cbSize);
205 if (pBuf)
206 {
207 rc = VBoxServiceControlExecReadPipeBufferContent(&pData->stdOut, pBuf, cbSize, &cbRead);
208 if (RT_SUCCESS(rc))
209 {
210 AssertPtr(pBuf);
211 /* cbRead now contains actual size. */
212 rc = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, 0 /* handle ID */, 0 /* flags */,
213 pBuf, cbRead);
214 }
215 RTMemFree(pBuf);
216 }
217 else
218 rc = VERR_NO_MEMORY;
219 }
220 else
221 rc = VERR_NOT_FOUND; /* PID not found! */
222 }
223 VBoxServiceVerbose(3, "Control: VBoxServiceControlHandleCmdGetOutput returned with %Rrc\n", rc);
224 return rc;
225}
226
227
228/** @copydoc VBOXSERVICE::pfnWorker */
229DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown)
230{
231 /*
232 * Tell the control thread that it can continue
233 * spawning services.
234 */
235 RTThreadUserSignal(RTThreadSelf());
236 Assert(g_GuestControlSvcClientID > 0);
237
238 int rc = VINF_SUCCESS;
239
240 /*
241 * Execution loop.
242 *
243 * @todo
244 */
245 for (;;)
246 {
247 uint32_t uMsg;
248 uint32_t uNumParms;
249 VBoxServiceVerbose(3, "Control: Waiting for host msg ...\n");
250 rc = VbglR3GuestCtrlGetHostMsg(g_GuestControlSvcClientID, &uMsg, &uNumParms, 1000 /* 1s timeout */);
251 if (RT_FAILURE(rc))
252 {
253 if (rc == VERR_TOO_MUCH_DATA)
254 {
255 VBoxServiceVerbose(3, "Control: Message requires %ld parameters, but only 2 supplied -- retrying request ...\n", uNumParms);
256 rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */
257 }
258 else
259 VBoxServiceVerbose(3, "Control: Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
260 }
261
262 if (RT_SUCCESS(rc))
263 {
264 VBoxServiceVerbose(3, "Control: Msg=%u (%u parms) retrieved\n", uMsg, uNumParms);
265 switch(uMsg)
266 {
267 case GETHOSTMSG_EXEC_START_PROCESS:
268 rc = VBoxServiceControlHandleCmdStartProcess(g_GuestControlSvcClientID, uNumParms);
269 break;
270
271 case GETHOSTMSG_EXEC_GET_OUTPUT:
272 rc = VBoxServiceControlHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);
273 break;
274
275 default:
276 VBoxServiceVerbose(3, "Control: Unsupported message from host! Msg=%u\n", uMsg);
277 /* Don't terminate here; just wait for the next message. */
278 break;
279 }
280
281 if (RT_FAILURE(rc))
282 VBoxServiceVerbose(3, "Control: Message was processed with rc=%Rrc\n", rc);
283 }
284
285 /* Do we need to shutdown? */
286 if (*pfShutdown)
287 {
288 rc = 0;
289 break;
290 }
291
292 /* Let's sleep for a bit and let others run ... */
293 RTThreadYield();
294 }
295
296 RTSemEventMultiDestroy(g_hControlEvent);
297 g_hControlEvent = NIL_RTSEMEVENTMULTI;
298 return rc;
299}
300
301
302/** @copydoc VBOXSERVICE::pfnStop */
303static DECLCALLBACK(void) VBoxServiceControlStop(void)
304{
305 /** @todo Later, figure what to do if we're in RTProcWait(). it's a very
306 * annoying call since doesn't support timeouts in the posix world. */
307 RTSemEventMultiSignal(g_hControlEvent);
308}
309
310
311/** @copydoc VBOXSERVICE::pfnTerm */
312static DECLCALLBACK(void) VBoxServiceControlTerm(void)
313{
314 /* Signal all threads that we want to shutdown. */
315 PVBOXSERVICECTRLTHREAD pNode;
316 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
317 ASMAtomicXchgBool(&pNode->fShutdown, true);
318
319 /* Wait for threads to shutdown. */
320 RTListForEach(&g_GuestControlExecThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
321 {
322 if (pNode->Thread != NIL_RTTHREAD)
323 {
324 int rc2 = RTThreadWait(pNode->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);
325 if (RT_FAILURE(rc2))
326 VBoxServiceError("Control: Thread failed to stop; rc2=%Rrc\n", rc2);
327 }
328
329 /* Destroy thread specific data. */
330 switch (pNode->enmType)
331 {
332 case VBoxServiceCtrlThreadDataExec:
333 VBoxServiceControlExecDestroyThreadData((PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData);
334 break;
335
336 default:
337 break;
338 }
339 }
340
341 /* Finally destroy thread list. */
342 pNode = RTListNodeGetFirst(&g_GuestControlExecThreads, VBOXSERVICECTRLTHREAD, Node);
343 while (pNode)
344 {
345 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node);
346
347 RTListNodeRemove(&pNode->Node);
348 RTMemFree(pNode);
349
350 if (pNext && RTListNodeIsLast(&g_GuestControlExecThreads, &pNext->Node))
351 break;
352
353 pNode = pNext;
354 }
355
356 VbglR3GuestCtrlDisconnect(g_GuestControlSvcClientID);
357 g_GuestControlSvcClientID = 0;
358
359 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
360 {
361 RTSemEventMultiDestroy(g_hControlEvent);
362 g_hControlEvent = NIL_RTSEMEVENTMULTI;
363 }
364}
365
366
367/**
368 * The 'vminfo' service description.
369 */
370VBOXSERVICE g_Control =
371{
372 /* pszName. */
373 "control",
374 /* pszDescription. */
375 "Host-driven Guest Control",
376 /* pszUsage. */
377 "[--control-interval <ms>]"
378 ,
379 /* pszOptions. */
380 " --control-interval Specifies the interval at which to check for\n"
381 " new control commands. The default is 1000 ms.\n"
382 ,
383 /* methods */
384 VBoxServiceControlPreInit,
385 VBoxServiceControlOption,
386 VBoxServiceControlInit,
387 VBoxServiceControlWorker,
388 VBoxServiceControlStop,
389 VBoxServiceControlTerm
390};
391
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