VirtualBox

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

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

VBoxServiceControl.cpp: file header.

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