VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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