VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp@ 28846

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

No dot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: VBoxManageGuestCtrl.cpp 28818 2010-04-27 13:14:40Z vboxsync $ */
2/** @file
3 * VBoxManage - The 'guestcontrol' command.
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 "VBoxManage.h"
23
24#include <VBox/com/com.h>
25#include <VBox/com/string.h>
26#include <VBox/com/array.h>
27#include <VBox/com/ErrorInfo.h>
28#include <VBox/com/errorprint.h>
29
30#include <VBox/com/VirtualBox.h>
31#include <VBox/com/EventQueue.h>
32
33#include <VBox/log.h>
34#include <iprt/asm.h>
35#include <iprt/getopt.h>
36#include <iprt/stream.h>
37#include <iprt/string.h>
38#include <iprt/time.h>
39#include <iprt/thread.h>
40
41#ifdef USE_XPCOM_QUEUE
42# include <sys/select.h>
43# include <errno.h>
44#endif
45
46#ifdef RT_OS_DARWIN
47# include <CoreFoundation/CFRunLoop.h>
48#endif
49
50using namespace com;
51
52/**
53 * IVirtualBoxCallback implementation for handling the GuestControlCallback in
54 * relation to the "guestcontrol * wait" command.
55 */
56/** @todo */
57
58void usageGuestControl(void)
59{
60 RTPrintf("VBoxManage guestcontrol execute <vmname>|<uuid>\n"
61 " <path to program> [--arguments \"<arguments>\"]\n"
62 " [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n"
63 " [--flags <flags>] [--timeout <msec>]\n"
64 " [--username <name> [--password <password>]]\n"
65 " [--verbose] [--wait-for exit,stdout,stderr||]\n"
66 "\n");
67}
68
69static int handleExecProgram(HandlerArg *a)
70{
71 /*
72 * Check the syntax. We can deduce the correct syntax from the number of
73 * arguments.
74 */
75 if (a->argc < 2) /* At least the command we want to execute in the guest should be present :-). */
76 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
77
78 Utf8Str Utf8Cmd(a->argv[1]);
79 uint32_t uFlags = 0;
80 com::SafeArray <BSTR> args;
81 com::SafeArray <BSTR> env;
82 Utf8Str Utf8StdIn;
83 Utf8Str Utf8StdOut;
84 Utf8Str Utf8StdErr;
85 Utf8Str Utf8UserName;
86 Utf8Str Utf8Password;
87 uint32_t u32TimeoutMS = 0;
88 bool waitForExit = false;
89 bool waitForStdOut = false;
90 bool waitForStdErr = false;
91 bool verbose = false;
92 bool have_timeout = false;
93
94 /* Always use the actual command line as argv[0]. */
95 args.push_back(Bstr(Utf8Cmd));
96
97 /* Iterate through all possible commands (if available). */
98 bool usageOK = true;
99 for (int i = 2; usageOK && i < a->argc; i++)
100 {
101 if ( !strcmp(a->argv[i], "--arguments")
102 || !strcmp(a->argv[i], "--args")
103 || !strcmp(a->argv[i], "--arg"))
104 {
105 if (i + 1 >= a->argc)
106 usageOK = false;
107 else
108 {
109 char **papszArg;
110 int cArgs;
111
112 int vrc = RTGetOptArgvFromString(&papszArg, &cArgs, a->argv[i + 1], NULL);
113 if (RT_SUCCESS(vrc))
114 {
115 for (int j = 0; j < cArgs; j++)
116 args.push_back(Bstr(papszArg[j]));
117
118 RTGetOptArgvFree(papszArg);
119 }
120 ++i;
121 }
122 }
123 else if ( !strcmp(a->argv[i], "--environment")
124 || !strcmp(a->argv[i], "--env"))
125 {
126 if (i + 1 >= a->argc)
127 usageOK = false;
128 else
129 {
130 char **papszArg;
131 int cArgs;
132
133 int vrc = RTGetOptArgvFromString(&papszArg, &cArgs, a->argv[i + 1], NULL);
134 if (RT_SUCCESS(vrc))
135 {
136 for (int j = 0; j < cArgs; j++)
137 env.push_back(Bstr(papszArg[j]));
138
139 RTGetOptArgvFree(papszArg);
140 }
141 ++i;
142 }
143 }
144 else if (!strcmp(a->argv[i], "--flags"))
145 {
146 if ( i + 1 >= a->argc
147 || RTStrToUInt32Full(a->argv[i + 1], 10, &uFlags) != VINF_SUCCESS)
148 usageOK = false;
149 else
150 ++i;
151 }
152 else if ( !strcmp(a->argv[i], "--username")
153 || !strcmp(a->argv[i], "--user"))
154 {
155 if (i + 1 >= a->argc)
156 usageOK = false;
157 else
158 {
159 Utf8UserName = a->argv[i + 1];
160 ++i;
161 }
162 }
163 else if ( !strcmp(a->argv[i], "--password")
164 || !strcmp(a->argv[i], "--pwd"))
165 {
166 if (i + 1 >= a->argc)
167 usageOK = false;
168 else
169 {
170 Utf8Password = a->argv[i + 1];
171 ++i;
172 }
173 }
174 else if (!strcmp(a->argv[i], "--timeout"))
175 {
176 if ( i + 1 >= a->argc
177 || RTStrToUInt32Full(a->argv[i + 1], 10, &u32TimeoutMS) != VINF_SUCCESS
178 || u32TimeoutMS == 0)
179 {
180 usageOK = false;
181 }
182 else
183 {
184 have_timeout = true;
185 ++i;
186 }
187 }
188 else if (!strcmp(a->argv[i], "--wait-for"))
189 {
190 if (i + 1 >= a->argc)
191 usageOK = false;
192 else
193 {
194 if (!strcmp(a->argv[i + 1], "exit"))
195 waitForExit = true;
196 else if (!strcmp(a->argv[i + 1], "stdout"))
197 {
198 waitForExit = true;
199 waitForStdOut = true;
200 }
201 else if (!strcmp(a->argv[i + 1], "stderr"))
202 {
203 waitForExit = true;
204 waitForStdErr = true;
205 }
206 else
207 usageOK = false;
208 ++i;
209 }
210 }
211 else if (!strcmp(a->argv[i], "--verbose"))
212 {
213 verbose = true;
214 }
215 /** @todo Add fancy piping stuff here. */
216 else
217 {
218 return errorSyntax(USAGE_GUESTCONTROL,
219 "Invalid parameter '%s'", Utf8Str(a->argv[i]).raw());
220 }
221 }
222
223 if (!usageOK)
224 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
225
226 /* If a password was specified, check if we also got a user name. */
227 if ( !Utf8Password.isEmpty()
228 && Utf8UserName.isEmpty())
229 {
230 return errorSyntax(USAGE_GUESTCONTROL,
231 "No user name for password specified!");
232 }
233
234 /* lookup VM. */
235 ComPtr<IMachine> machine;
236 /* assume it's an UUID */
237 HRESULT rc = a->virtualBox->GetMachine(Bstr(a->argv[0]), machine.asOutParam());
238 if (FAILED(rc) || !machine)
239 {
240 /* must be a name */
241 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]), machine.asOutParam()));
242 }
243
244 if (machine)
245 {
246 do
247 {
248 Bstr uuid;
249 machine->COMGETTER(Id)(uuid.asOutParam());
250
251 /* open an existing session for VM - so the VM has to be running */
252 CHECK_ERROR_BREAK(a->virtualBox, OpenExistingSession(a->session, uuid));
253
254 /* get the mutable session machine */
255 a->session->COMGETTER(Machine)(machine.asOutParam());
256
257 /* get the associated console */
258 ComPtr<IConsole> console;
259 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
260
261 ComPtr<IGuest> guest;
262 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam()));
263
264 ComPtr<IProgress> progress;
265 ULONG uPID = 0;
266
267 if (verbose)
268 {
269 if (u32TimeoutMS == 0)
270 RTPrintf("Waiting for guest to start process ...\n");
271 else
272 RTPrintf("Waiting for guest to start process (within %ums)\n", u32TimeoutMS);
273 }
274
275 /* Get current time stamp to later calculate rest of timeout left. */
276 uint64_t u64StartMS = RTTimeMilliTS();
277
278 /* Execute the process. */
279 CHECK_ERROR_BREAK(guest, ExecuteProcess(Bstr(Utf8Cmd), uFlags,
280 ComSafeArrayAsInParam(args), ComSafeArrayAsInParam(env),
281 Bstr(Utf8StdIn), Bstr(Utf8StdOut), Bstr(Utf8StdErr),
282 Bstr(Utf8UserName), Bstr(Utf8Password), u32TimeoutMS,
283 &uPID, progress.asOutParam()));
284 if (verbose) RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.raw(), uPID);
285 if (waitForExit)
286 {
287 if (have_timeout)
288 {
289 /* Calculate timeout value left after process has been started. */
290 uint64_t u64Diff = RTTimeMilliTS() - u64StartMS;
291 /** @todo Check for uint64_t vs. uint32_t here! */
292 if (u32TimeoutMS > u64Diff) /* Is timeout still bigger than current difference? */
293 {
294 u32TimeoutMS = u32TimeoutMS - u64Diff;
295 if (verbose)
296 RTPrintf("Waiting for process to exit (%ums left) ...\n", u32TimeoutMS);
297 }
298 else
299 {
300 if (verbose)
301 RTPrintf("No time left to wait for process!\n");
302 }
303 }
304 else if (verbose)
305 RTPrintf("Waiting for process to exit ...\n");
306
307 /* Wait for process to exit ... */
308 ASSERT(progress);
309 rc = progress->WaitForCompletion(have_timeout ?
310 (u32TimeoutMS + 5000) : /* Timeout value + safety counter */
311 -1 /* Wait forever */);
312 if (FAILED(rc))
313 {
314 if (u32TimeoutMS)
315 RTPrintf("Process '%s' (PID: %u) did not end within %ums! Wait aborted.\n",
316 Utf8Cmd.raw(), uPID, u32TimeoutMS);
317 break;
318 }
319 else
320 {
321 BOOL completed;
322 CHECK_ERROR_RET(progress, COMGETTER(Completed)(&completed), rc);
323 ASSERT(completed);
324
325 LONG iRc;
326 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
327 if (FAILED(iRc))
328 {
329 ComPtr<IVirtualBoxErrorInfo> execError;
330 rc = progress->COMGETTER(ErrorInfo)(execError.asOutParam());
331 com::ErrorInfo info (execError);
332 RTPrintf("Process error details:\n");
333 GluePrintErrorInfo(info);
334 RTPrintf("\n");
335 }
336
337 ULONG uStatus, uExitCode, uFlags;
338 CHECK_ERROR_BREAK(guest, GetProcessStatus(uPID, &uExitCode, &uFlags, &uStatus));
339 if (verbose)
340 RTPrintf("Exit code=%u (Status=%u, Flags=%u)\n", uExitCode, uStatus, uFlags);
341
342 /* Print output if wanted. */
343 if ( waitForStdOut
344 || waitForStdErr)
345 {
346 bool bFound = false;
347 while (true)
348 {
349 SafeArray<BYTE> aOutputData;
350 ULONG cbOutputData;
351 CHECK_ERROR_BREAK(guest, GetProcessOutput(uPID, 0 /* aFlags */,
352 u32TimeoutMS, _64K, ComSafeArrayAsOutParam(aOutputData)));
353 cbOutputData = aOutputData.size();
354 if (cbOutputData == 0)
355 break;
356
357 if (!bFound && verbose)
358 {
359 RTPrintf("Retrieving output data ...\n");
360 bFound = true;
361 }
362
363 /* aOutputData has a platform dependent line ending, standardize on
364 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
365 * Windows. Otherwise we end up with CR/CR/LF on Windows. */
366 ULONG cbOutputDataPrint = cbOutputData;
367 for (BYTE *s = aOutputData.raw(), *d = s;
368 s - aOutputData.raw() < (ssize_t)cbOutputData;
369 s++, d++)
370 {
371 if (*s == '\r')
372 {
373 /* skip over CR, adjust destination */
374 d--;
375 cbOutputDataPrint--;
376 }
377 else if (s != d)
378 *d = *s;
379 }
380 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
381 }
382 if (!bFound && verbose)
383 RTPrintf("No output data available\n");
384 }
385 }
386 }
387 a->session->Close();
388 } while (0);
389 }
390 return SUCCEEDED(rc) ? 0 : 1;
391}
392
393/**
394 * Access the guest control store.
395 *
396 * @returns 0 on success, 1 on failure
397 * @note see the command line API description for parameters
398 */
399int handleGuestControl(HandlerArg *a)
400{
401 HandlerArg arg = *a;
402 arg.argc = a->argc - 1;
403 arg.argv = a->argv + 1;
404
405 if (a->argc == 0)
406 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
407
408 /* switch (cmd) */
409 if ( strcmp(a->argv[0], "exec") == 0
410 || strcmp(a->argv[0], "execute") == 0)
411 return handleExecProgram(&arg);
412
413 /* default: */
414 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
415}
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