1 | /* $Id: VBoxServiceExec.cpp 22586 2009-08-31 06:43:23Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBoxServiceExec - In-VM Command Execution Service.
|
---|
4 | */
|
---|
5 | /** @todo r=bird: Why is this called VMExec while the filename is VBoxServiceExec.cpp? See VMInfo... */
|
---|
6 | /** @todo r=bird: Use svn-ps.[sh|cmd] -a when adding new files, please. Then the EOLs and $Id: VBoxServiceExec.cpp 22586 2009-08-31 06:43:23Z vboxsync $ won't be messed up all the time. */
|
---|
7 |
|
---|
8 | /*
|
---|
9 | * Copyright (C) 2009 Sun Microsystems, Inc.
|
---|
10 | *
|
---|
11 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
12 | * available from http://www.virtualbox.org. This file is free software;
|
---|
13 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
14 | * General Public License (GPL) as published by the Free Software
|
---|
15 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
16 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
17 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
18 | *
|
---|
19 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
|
---|
20 | * Clara, CA 95054 USA or visit http://www.sun.com if you need
|
---|
21 | * additional information or have any questions.
|
---|
22 | */
|
---|
23 |
|
---|
24 |
|
---|
25 | /*******************************************************************************
|
---|
26 | * Header Files *
|
---|
27 | *******************************************************************************/
|
---|
28 | #include <iprt/assert.h>
|
---|
29 | #include <iprt/env.h>
|
---|
30 | #include <iprt/file.h>
|
---|
31 | #include <iprt/mem.h>
|
---|
32 | #include <iprt/process.h>
|
---|
33 | #include <iprt/string.h>
|
---|
34 | #include <iprt/semaphore.h>
|
---|
35 | #include <iprt/thread.h>
|
---|
36 | #include <VBox/version.h>
|
---|
37 | #include <VBox/VBoxGuestLib.h>
|
---|
38 | #include "VBoxServiceInternal.h"
|
---|
39 | #include "VBoxServiceUtils.h"
|
---|
40 |
|
---|
41 |
|
---|
42 | /*******************************************************************************
|
---|
43 | * Global Variables *
|
---|
44 | *******************************************************************************/
|
---|
45 | /** The vminfo interval (millseconds). */
|
---|
46 | uint32_t g_VMExecInterval = 0;
|
---|
47 | /** The semaphore we're blocking on. */
|
---|
48 | static RTSEMEVENTMULTI g_VMExecEvent = NIL_RTSEMEVENTMULTI;
|
---|
49 | /** The guest property service client ID. */
|
---|
50 | static uint32_t g_VMExecGuestPropSvcClientID = 0;
|
---|
51 |
|
---|
52 |
|
---|
53 | /** @copydoc VBOXSERVICE::pfnPreInit */
|
---|
54 | static DECLCALLBACK(int) VBoxServiceExecPreInit(void)
|
---|
55 | {
|
---|
56 | return VINF_SUCCESS;
|
---|
57 | }
|
---|
58 |
|
---|
59 |
|
---|
60 | /** @copydoc VBOXSERVICE::pfnOption */
|
---|
61 | static DECLCALLBACK(int) VBoxServiceExecOption(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], "--vmexec-interval"))
|
---|
67 | rc = VBoxServiceArgUInt32(argc, argv, "", pi, &g_VMExecInterval, 1, UINT32_MAX - 1);
|
---|
68 | return rc;
|
---|
69 | }
|
---|
70 |
|
---|
71 |
|
---|
72 | /** @copydoc VBOXSERVICE::pfnInit */
|
---|
73 | static DECLCALLBACK(int) VBoxServiceExecInit(void)
|
---|
74 | {
|
---|
75 | /*
|
---|
76 | * If not specified, find the right interval default.
|
---|
77 | * Then create the event sem to block on.
|
---|
78 | */
|
---|
79 | if (!g_VMExecInterval)
|
---|
80 | g_VMExecInterval = g_DefaultInterval * 1000;
|
---|
81 | if (!g_VMExecInterval)
|
---|
82 | g_VMExecInterval = 10 * 1000;
|
---|
83 |
|
---|
84 | int rc = RTSemEventMultiCreate(&g_VMExecEvent);
|
---|
85 | AssertRCReturn(rc, rc);
|
---|
86 |
|
---|
87 | rc = VbglR3GuestPropConnect(&g_VMExecGuestPropSvcClientID);
|
---|
88 | if (RT_SUCCESS(rc))
|
---|
89 | VBoxServiceVerbose(3, "Property Service Client ID: %#x\n", g_VMExecGuestPropSvcClientID);
|
---|
90 | else
|
---|
91 | {
|
---|
92 | VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
|
---|
93 | RTSemEventMultiDestroy(g_VMExecEvent);
|
---|
94 | g_VMExecEvent = NIL_RTSEMEVENTMULTI;
|
---|
95 | }
|
---|
96 |
|
---|
97 | return rc;
|
---|
98 | }
|
---|
99 |
|
---|
100 |
|
---|
101 | /** @copydoc VBOXSERVICE::pfnWorker */
|
---|
102 | DECLCALLBACK(int) VBoxServiceExecWorker(bool volatile *pfShutdown)
|
---|
103 | {
|
---|
104 | int rc = VINF_SUCCESS;
|
---|
105 |
|
---|
106 | /*
|
---|
107 | * Tell the control thread that it can continue
|
---|
108 | * spawning services.
|
---|
109 | */
|
---|
110 | RTThreadUserSignal(RTThreadSelf());
|
---|
111 |
|
---|
112 | /*
|
---|
113 | * Execution loop.
|
---|
114 | */
|
---|
115 | #ifdef FULL_FEATURED_EXEC
|
---|
116 | uint64_t u64TimestampPrev = UINT64_MAX;
|
---|
117 | #endif
|
---|
118 | bool fSysprepDone = false;
|
---|
119 | for (;;)
|
---|
120 | {
|
---|
121 | #ifndef TARGET_NT4 /** @todo r=bird: Add comment explaining why please. */
|
---|
122 | /*
|
---|
123 | * The thread at the moment does nothing but checking for one specific guest property
|
---|
124 | * for triggering a hard coded sysprep command with parameters given by the host. This
|
---|
125 | * feature was required by the VDI guys.
|
---|
126 | *
|
---|
127 | * Later this thread could become a general host->guest executor (remote shell?).
|
---|
128 | */
|
---|
129 | if (!fSysprepDone)
|
---|
130 | {
|
---|
131 | /* Get arguments. */
|
---|
132 | /** @todo r=bird: How exactly does this work wrt. enabled/disabled? In
|
---|
133 | * ConsoleImpl2.cpp it's now always set to "", which means it will
|
---|
134 | * always be executed. So, if someone adds a bogus
|
---|
135 | * c:\\sysprep\\sysprep.exe to their system and install the latest
|
---|
136 | * additions this will be executed everytime the system starts now - if
|
---|
137 | * I understand the code correctly. Is this intentional? */
|
---|
138 | char *pszArgs;
|
---|
139 | rc = VbglR3GuestPropReadValueAlloc(g_VMExecGuestPropSvcClientID, "/VirtualBox/HostGuest/SysprepArgs", &pszArgs);
|
---|
140 | if (RT_FAILURE(rc))
|
---|
141 | VBoxServiceVerbose(2, "Sysprep guest property not found or broken communication. Error: %Rrc\n", pszArgs, rc);
|
---|
142 |
|
---|
143 | /** @todo r=bird: You must check that the flags, you should require it to the
|
---|
144 | * TRANSIENT and RDONLYGUEST. Otherwise, we'll have a potential priviledge
|
---|
145 | * escalation issue inside the guest if the variable is removed. */
|
---|
146 |
|
---|
147 | static char *s_pszSysprepImage = "c:\\sysprep\\sysprep.exe";
|
---|
148 | if ( RT_SUCCESS(rc)
|
---|
149 | && !RTFileExists(s_pszSysprepImage))
|
---|
150 | {
|
---|
151 | VBoxServiceError("Sysprep executable not found! Search path=%s\n", s_pszSysprepImage);
|
---|
152 | rc = VERR_FILE_NOT_FOUND;
|
---|
153 | }
|
---|
154 |
|
---|
155 | if (RT_SUCCESS(rc))
|
---|
156 | {
|
---|
157 | RTPROCESS pid;
|
---|
158 | const char *papszArgs[6] = { s_pszSysprepImage, "-quiet", "-reseal", "-mini", "-activated", NULL};
|
---|
159 |
|
---|
160 | /** @todo append the arguments in SysprepArgs/pszArgs. */
|
---|
161 | VBoxServiceVerbose(3, "Executing sysprep ...\n");
|
---|
162 | rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT, 0, &pid);
|
---|
163 | if (RT_SUCCESS(rc))
|
---|
164 | {
|
---|
165 | RTPROCSTATUS Status;
|
---|
166 | rc = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, &Status);
|
---|
167 |
|
---|
168 | VBoxServiceVerbose(3, "Sysprep returned: %d\n", Status.iStatus);
|
---|
169 | if (RT_SUCCESS(rc))
|
---|
170 | {
|
---|
171 | if ( Status.iStatus == 0
|
---|
172 | && Status.enmReason == RTPROCEXITREASON_NORMAL)
|
---|
173 | {
|
---|
174 | rc = VINF_SUCCESS;
|
---|
175 | fSysprepDone = true; /** r=bird: So, if sysprep fails, the idea is to continue executing the code every 10 seconds? */
|
---|
176 | }
|
---|
177 | }
|
---|
178 |
|
---|
179 | /* Set return value so the host knows what happend. */
|
---|
180 | rc = VbglR3GuestPropWriteValueF(g_VMExecGuestPropSvcClientID, "/VirtualBox/HostGuest/SysprepRet", "%d", Status.iStatus);
|
---|
181 | if (RT_FAILURE(rc))
|
---|
182 | VBoxServiceError("Failed to write SysprepRet: rc=%Rrc\n", rc);
|
---|
183 | }
|
---|
184 | }
|
---|
185 |
|
---|
186 | VbglR3GuestPropReadValueFree(pszArgs);
|
---|
187 | }
|
---|
188 | #endif /* !TARGET_NT4 */
|
---|
189 |
|
---|
190 | #ifdef FULL_FEATURED_EXEC
|
---|
191 | 1. Read the command - value, timestamp and flags.
|
---|
192 | 2. Check that the flags indicates that the guest cannot write to it and that it's transient.
|
---|
193 | 3. Check if the timestamp changed.
|
---|
194 | 4. Get the arguments and other stuff.
|
---|
195 | 5. Execute it. This may involve grabbing the output (stderr and/or stdout) and pushing into
|
---|
196 | values afterwards. It may also entail redirecting input to a file containing text from a guest prop value.
|
---|
197 | 6. Set the result values (there will be three, one IPRT style one for everything up to
|
---|
198 | and including RTProcWait and two that mirrors Status.iStatus and Status.enmReason (stringified)).
|
---|
199 | #endif
|
---|
200 |
|
---|
201 | /*
|
---|
202 | * Block for a while.
|
---|
203 | *
|
---|
204 | * The event semaphore takes care of ignoring interruptions and it
|
---|
205 | * allows us to implement service wakeup later.
|
---|
206 | */
|
---|
207 | if (*pfShutdown)
|
---|
208 | break;
|
---|
209 | #ifdef FULL_FEATURED_EXEC
|
---|
210 | Wait for changes to the command value. If that fails for some reason other than timeout / interrupt, fall back on the semaphore.
|
---|
211 | #else
|
---|
212 | int rc2 = RTSemEventMultiWait(g_VMExecEvent, g_VMExecInterval);
|
---|
213 | #endif
|
---|
214 | if (*pfShutdown)
|
---|
215 | break;
|
---|
216 | if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
|
---|
217 | {
|
---|
218 | VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
|
---|
219 | rc = rc2;
|
---|
220 | break;
|
---|
221 | }
|
---|
222 | }
|
---|
223 |
|
---|
224 | RTSemEventMultiDestroy(g_VMExecEvent);
|
---|
225 | g_VMExecEvent = NIL_RTSEMEVENTMULTI;
|
---|
226 | return rc;
|
---|
227 | }
|
---|
228 |
|
---|
229 |
|
---|
230 | /** @copydoc VBOXSERVICE::pfnStop */
|
---|
231 | static DECLCALLBACK(void) VBoxServiceExecStop(void)
|
---|
232 | {
|
---|
233 | /** @todo Later, figure what to do if we're in RTProcWait(). it's a very
|
---|
234 | * annoying call since doesn't support timeouts in the posix world. */
|
---|
235 | RTSemEventMultiSignal(g_VMExecEvent);
|
---|
236 | #ifdef FULL_FEATURED_EXEC
|
---|
237 | Interrupts waits.
|
---|
238 | #endif
|
---|
239 | }
|
---|
240 |
|
---|
241 |
|
---|
242 | /** @copydoc VBOXSERVICE::pfnTerm */
|
---|
243 | static DECLCALLBACK(void) VBoxServiceExecTerm(void)
|
---|
244 | {
|
---|
245 | /* Nothing here yet. */
|
---|
246 | /** @todo r=bird: Shouldn't you do the following here:
|
---|
247 | * VbglR3GuestPropDisconnect(g_VMExecGuestPropSvcClientID);
|
---|
248 | * g_VMExecGuestPropSvcClientID = 0;
|
---|
249 | * And quite possibly:
|
---|
250 | * RTSemEventMultiDestroy(g_hVMExecEvent);
|
---|
251 | * g_hVMExecEvent = NIL_RTSEMEVENTMULTI;
|
---|
252 | */
|
---|
253 | }
|
---|
254 |
|
---|
255 |
|
---|
256 | /**
|
---|
257 | * The 'vminfo' service description.
|
---|
258 | */
|
---|
259 | VBOXSERVICE g_VMExec =
|
---|
260 | {
|
---|
261 | /* pszName. */
|
---|
262 | "vmexec",
|
---|
263 | /* pszDescription. */
|
---|
264 | "Virtual Machine Remote Execution",
|
---|
265 | /* pszUsage. */
|
---|
266 | "[--vmexec-interval <ms>]"
|
---|
267 | ,
|
---|
268 | /* pszOptions. */
|
---|
269 | " --vmexec-interval Specifies the interval at which to check for new\n"
|
---|
270 | " remote execution commands. The default is 10000 ms.\n"
|
---|
271 | ,
|
---|
272 | /* methods */
|
---|
273 | VBoxServiceExecPreInit,
|
---|
274 | VBoxServiceExecOption,
|
---|
275 | VBoxServiceExecInit,
|
---|
276 | VBoxServiceExecWorker,
|
---|
277 | VBoxServiceExecStop,
|
---|
278 | VBoxServiceExecTerm
|
---|
279 | };
|
---|
280 |
|
---|