VirtualBox

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

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

Additions: s/BYTE/uint8_t/g

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