VirtualBox

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

Last change on this file since 33796 was 33796, checked in by vboxsync, 14 years ago

Additional check.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.5 KB
Line 
1/* $Id: VBoxManageGuestCtrl.cpp 33796 2010-11-05 16:03:20Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of 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#ifndef VBOX_ONLY_DOCS
25
26#include <VBox/com/com.h>
27#include <VBox/com/string.h>
28#include <VBox/com/array.h>
29#include <VBox/com/ErrorInfo.h>
30#include <VBox/com/errorprint.h>
31
32#include <VBox/com/VirtualBox.h>
33#include <VBox/com/EventQueue.h>
34
35#include <VBox/HostServices/GuestControlSvc.h> /* for PROC_STS_XXX */
36
37#include <iprt/asm.h>
38#include <iprt/dir.h>
39#include <iprt/file.h>
40#include <iprt/isofs.h>
41#include <iprt/getopt.h>
42#include <iprt/list.h>
43#include <iprt/path.h>
44
45#ifdef USE_XPCOM_QUEUE
46# include <sys/select.h>
47# include <errno.h>
48#endif
49
50#include <signal.h>
51
52#ifdef RT_OS_DARWIN
53# include <CoreFoundation/CFRunLoop.h>
54#endif
55
56using namespace com;
57
58/**
59 * IVirtualBoxCallback implementation for handling the GuestControlCallback in
60 * relation to the "guestcontrol * wait" command.
61 */
62/** @todo */
63
64/** Set by the signal handler. */
65static volatile bool g_fExecCanceled = false;
66static volatile bool g_fCopyCanceled = false;
67
68/*
69 * Structure holding a directory entry.
70 */
71typedef struct DIRECTORYENTRY
72{
73 char *pszSourcePath;
74 char *pszDestPath;
75 RTLISTNODE Node;
76} DIRECTORYENTRY, *PDIRECTORYENTRY;
77
78#endif /* VBOX_ONLY_DOCS */
79
80void usageGuestControl(PRTSTREAM pStrm)
81{
82 RTStrmPrintf(pStrm,
83 "VBoxManage guestcontrol execute <vmname>|<uuid>\n"
84 " <path to program>\n"
85 " --username <name> --password <password>\n"
86 " [--arguments \"<arguments>\"]\n"
87 " [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n"
88 " [--flags <flags>] [--timeout <msec>]\n"
89 " [--verbose] [--wait-for exit,stdout,stderr||]\n"
90 /** @todo Add a "--" parameter (has to be last parameter) to directly execute
91 * stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */
92 "\n"
93 " copyto <vmname>|<uuid>\n"
94 " <source on host> <destination on guest>\n"
95 " --username <name> --password <password>\n"
96 " [--dryrun] [--recursive] [--verbose] [--flags <flags>]\n"
97 "\n"
98 " updateadditions <vmname>|<uuid>\n"
99 " [--source <guest additions .ISO file to use>] [--verbose]\n"
100 "\n");
101}
102
103#ifndef VBOX_ONLY_DOCS
104
105/**
106 * Signal handler that sets g_fCanceled.
107 *
108 * This can be executed on any thread in the process, on Windows it may even be
109 * a thread dedicated to delivering this signal. Do not doing anything
110 * unnecessary here.
111 */
112static void ctrlExecProcessSignalHandler(int iSignal)
113{
114 NOREF(iSignal);
115 ASMAtomicWriteBool(&g_fExecCanceled, true);
116}
117
118static const char *ctrlExecGetStatus(ULONG uStatus)
119{
120 switch (uStatus)
121 {
122 case guestControl::PROC_STS_STARTED:
123 return "started";
124 case guestControl::PROC_STS_TEN:
125 return "successfully terminated";
126 case guestControl::PROC_STS_TES:
127 return "terminated by signal";
128 case guestControl::PROC_STS_TEA:
129 return "abnormally aborted";
130 case guestControl::PROC_STS_TOK:
131 return "timed out";
132 case guestControl::PROC_STS_TOA:
133 return "timed out, hanging";
134 case guestControl::PROC_STS_DWN:
135 return "killed";
136 case guestControl::PROC_STS_ERROR:
137 return "error";
138 default:
139 return "unknown";
140 }
141}
142
143static int handleCtrlExecProgram(HandlerArg *a)
144{
145 /*
146 * Check the syntax. We can deduce the correct syntax from the number of
147 * arguments.
148 */
149 if (a->argc < 2) /* At least the command we want to execute in the guest should be present :-). */
150 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
151
152 Utf8Str Utf8Cmd(a->argv[1]);
153 uint32_t uFlags = 0;
154 /* Note: this uses IN_BSTR as it must be BSTR on COM and CBSTR on XPCOM */
155 com::SafeArray<IN_BSTR> args;
156 com::SafeArray<IN_BSTR> env;
157 Utf8Str Utf8UserName;
158 Utf8Str Utf8Password;
159 uint32_t u32TimeoutMS = 0;
160 bool fWaitForExit = false;
161 bool fWaitForStdOut = false;
162 bool fWaitForStdErr = false;
163 bool fVerbose = false;
164 bool fTimeout = false;
165
166 /* Always use the actual command line as argv[0]. */
167 args.push_back(Bstr(Utf8Cmd).raw());
168
169 /* Iterate through all possible commands (if available). */
170 bool usageOK = true;
171 for (int i = 2; usageOK && i < a->argc; i++)
172 {
173 if ( !strcmp(a->argv[i], "--arguments")
174 || !strcmp(a->argv[i], "--args")
175 || !strcmp(a->argv[i], "--arg"))
176 {
177 if (i + 1 >= a->argc)
178 usageOK = false;
179 else
180 {
181 char **papszArg;
182 int cArgs;
183
184 int vrc = RTGetOptArgvFromString(&papszArg, &cArgs, a->argv[i + 1], NULL);
185 if (RT_SUCCESS(vrc))
186 {
187 for (int j = 0; j < cArgs; j++)
188 args.push_back(Bstr(papszArg[j]).raw());
189
190 RTGetOptArgvFree(papszArg);
191 }
192 ++i;
193 }
194 }
195 else if ( !strcmp(a->argv[i], "--environment")
196 || !strcmp(a->argv[i], "--env"))
197 {
198 if (i + 1 >= a->argc)
199 usageOK = false;
200 else
201 {
202 char **papszArg;
203 int cArgs;
204
205 int vrc = RTGetOptArgvFromString(&papszArg, &cArgs, a->argv[i + 1], NULL);
206 if (RT_SUCCESS(vrc))
207 {
208 for (int j = 0; j < cArgs; j++)
209 env.push_back(Bstr(papszArg[j]).raw());
210
211 RTGetOptArgvFree(papszArg);
212 }
213 ++i;
214 }
215 }
216 else if (!strcmp(a->argv[i], "--flags"))
217 {
218 if (i + 1 >= a->argc)
219 usageOK = false;
220 else
221 {
222 /** @todo Needs a bit better processing as soon as we have more flags. */
223 if (!strcmp(a->argv[i + 1], "ignoreorphanedprocesses"))
224 uFlags |= ExecuteProcessFlag_IgnoreOrphanedProcesses;
225 else
226 usageOK = false;
227 ++i;
228 }
229 }
230 else if ( !strcmp(a->argv[i], "--username")
231 || !strcmp(a->argv[i], "--user"))
232 {
233 if (i + 1 >= a->argc)
234 usageOK = false;
235 else
236 {
237 Utf8UserName = a->argv[i + 1];
238 ++i;
239 }
240 }
241 else if ( !strcmp(a->argv[i], "--password")
242 || !strcmp(a->argv[i], "--pwd"))
243 {
244 if (i + 1 >= a->argc)
245 usageOK = false;
246 else
247 {
248 Utf8Password = a->argv[i + 1];
249 ++i;
250 }
251 }
252 else if (!strcmp(a->argv[i], "--timeout"))
253 {
254 if ( i + 1 >= a->argc
255 || RTStrToUInt32Full(a->argv[i + 1], 10, &u32TimeoutMS) != VINF_SUCCESS
256 || u32TimeoutMS == 0)
257 {
258 usageOK = false;
259 }
260 else
261 {
262 fTimeout = true;
263 ++i;
264 }
265 }
266 else if (!strcmp(a->argv[i], "--wait-for"))
267 {
268 if (i + 1 >= a->argc)
269 usageOK = false;
270 else
271 {
272 if (!strcmp(a->argv[i + 1], "exit"))
273 fWaitForExit = true;
274 else if (!strcmp(a->argv[i + 1], "stdout"))
275 {
276 fWaitForExit = true;
277 fWaitForStdOut = true;
278 }
279 else if (!strcmp(a->argv[i + 1], "stderr"))
280 {
281 fWaitForExit = true;
282 fWaitForStdErr = true;
283 }
284 else
285 usageOK = false;
286 ++i;
287 }
288 }
289 else if (!strcmp(a->argv[i], "--verbose"))
290 fVerbose = true;
291 /** @todo Add fancy piping stuff here. */
292 else
293 return errorSyntax(USAGE_GUESTCONTROL,
294 "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
295 }
296
297 if (!usageOK)
298 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
299
300 if (Utf8UserName.isEmpty())
301 return errorSyntax(USAGE_GUESTCONTROL,
302 "No user name specified!");
303
304 /* Lookup VM. */
305 ComPtr<IMachine> machine;
306 /* Assume it's an UUID. */
307 HRESULT rc;
308 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
309 machine.asOutParam()));
310 if (FAILED(rc))
311 return 1;
312
313 /* Machine is running? */
314 MachineState_T machineState;
315 CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), 1);
316 if (machineState != MachineState_Running)
317 {
318 RTMsgError("Machine \"%s\" is not running!\n", a->argv[0]);
319 return 1;
320 }
321
322 /* Open a session for the VM. */
323 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
324
325 do
326 {
327 /* Get the associated console. */
328 ComPtr<IConsole> console;
329 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
330 /* ... and session machine */
331 ComPtr<IMachine> sessionMachine;
332 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
333
334 ComPtr<IGuest> guest;
335 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam()));
336
337 ComPtr<IProgress> progress;
338 ULONG uPID = 0;
339
340 if (fVerbose)
341 {
342 if (u32TimeoutMS == 0)
343 RTPrintf("Waiting for guest to start process ...\n");
344 else
345 RTPrintf("Waiting for guest to start process (within %ums)\n", u32TimeoutMS);
346 }
347
348 /* Get current time stamp to later calculate rest of timeout left. */
349 uint64_t u64StartMS = RTTimeMilliTS();
350
351 /* Execute the process. */
352 rc = guest->ExecuteProcess(Bstr(Utf8Cmd).raw(), uFlags,
353 ComSafeArrayAsInParam(args),
354 ComSafeArrayAsInParam(env),
355 Bstr(Utf8UserName).raw(),
356 Bstr(Utf8Password).raw(), u32TimeoutMS,
357 &uPID, progress.asOutParam());
358 if (FAILED(rc))
359 {
360 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
361 * because it contains more accurate info about what went wrong. */
362 ErrorInfo info(guest, COM_IIDOF(IGuest));
363 if (info.isFullAvailable())
364 {
365 if (rc == VBOX_E_IPRT_ERROR)
366 RTMsgError("%ls.", info.getText().raw());
367 else
368 RTMsgError("%ls (%Rhrc).", info.getText().raw(), info.getResultCode());
369 }
370 break;
371 }
372 if (fVerbose)
373 RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID);
374 if (fWaitForExit)
375 {
376 if (fTimeout)
377 {
378 /* Calculate timeout value left after process has been started. */
379 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
380 /* Is timeout still bigger than current difference? */
381 if (u32TimeoutMS > u64Elapsed)
382 {
383 u32TimeoutMS -= (uint32_t)u64Elapsed;
384 if (fVerbose)
385 RTPrintf("Waiting for process to exit (%ums left) ...\n", u32TimeoutMS);
386 }
387 else
388 {
389 if (fVerbose)
390 RTPrintf("No time left to wait for process!\n");
391 }
392 }
393 else if (fVerbose)
394 RTPrintf("Waiting for process to exit ...\n");
395
396 /* Setup signal handling if cancelable. */
397 ASSERT(progress);
398 bool fCanceledAlready = false;
399 BOOL fCancelable;
400 HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
401 if (FAILED(hrc))
402 fCancelable = FALSE;
403 if (fCancelable)
404 {
405 signal(SIGINT, ctrlExecProcessSignalHandler);
406 #ifdef SIGBREAK
407 signal(SIGBREAK, ctrlExecProcessSignalHandler);
408 #endif
409 }
410
411 /* Wait for process to exit ... */
412 BOOL fCompleted = FALSE;
413 BOOL fCanceled = FALSE;
414 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
415 {
416 SafeArray<BYTE> aOutputData;
417 ULONG cbOutputData = 0;
418
419 /*
420 * Some data left to output?
421 */
422 if ( fWaitForStdOut
423 || fWaitForStdErr)
424 {
425 rc = guest->GetProcessOutput(uPID, 0 /* aFlags */,
426 u32TimeoutMS, _64K, ComSafeArrayAsOutParam(aOutputData));
427 if (FAILED(rc))
428 {
429 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
430 * because it contains more accurate info about what went wrong. */
431 ErrorInfo info(guest, COM_IIDOF(IGuest));
432 if (info.isFullAvailable())
433 {
434 if (rc == VBOX_E_IPRT_ERROR)
435 RTMsgError("%ls.", info.getText().raw());
436 else
437 RTMsgError("%ls (%Rhrc).", info.getText().raw(), info.getResultCode());
438 }
439 cbOutputData = 0;
440 fCompleted = true; /* rc contains a failure, so we'll go into aborted state down below. */
441 }
442 else
443 {
444 cbOutputData = aOutputData.size();
445 if (cbOutputData > 0)
446 {
447 /* aOutputData has a platform dependent line ending, standardize on
448 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
449 * Windows. Otherwise we end up with CR/CR/LF on Windows. */
450 ULONG cbOutputDataPrint = cbOutputData;
451 for (BYTE *s = aOutputData.raw(), *d = s;
452 s - aOutputData.raw() < (ssize_t)cbOutputData;
453 s++, d++)
454 {
455 if (*s == '\r')
456 {
457 /* skip over CR, adjust destination */
458 d--;
459 cbOutputDataPrint--;
460 }
461 else if (s != d)
462 *d = *s;
463 }
464 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
465 }
466 }
467 }
468 if (cbOutputData <= 0) /* No more output data left? */
469 {
470 if (fCompleted)
471 break;
472
473 if ( fTimeout
474 && RTTimeMilliTS() - u64StartMS > u32TimeoutMS + 5000)
475 {
476 progress->Cancel();
477 break;
478 }
479 }
480
481 /* Process async cancelation */
482 if (g_fExecCanceled && !fCanceledAlready)
483 {
484 hrc = progress->Cancel();
485 if (SUCCEEDED(hrc))
486 fCanceledAlready = TRUE;
487 else
488 g_fExecCanceled = false;
489 }
490
491 /* Progress canceled by Main API? */
492 if ( SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
493 && fCanceled)
494 {
495 break;
496 }
497 }
498
499 /* Undo signal handling */
500 if (fCancelable)
501 {
502 signal(SIGINT, SIG_DFL);
503 #ifdef SIGBREAK
504 signal(SIGBREAK, SIG_DFL);
505 #endif
506 }
507
508 if (fCanceled)
509 {
510 if (fVerbose)
511 RTPrintf("Process execution canceled!\n");
512 }
513 else if ( fCompleted
514 && SUCCEEDED(rc))
515 {
516 LONG iRc = false;
517 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
518 if (FAILED(iRc))
519 {
520 com::ProgressErrorInfo info(progress);
521 if ( info.isFullAvailable()
522 || info.isBasicAvailable())
523 {
524 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
525 * because it contains more accurate info about what went wrong. */
526 if (info.getResultCode() == VBOX_E_IPRT_ERROR)
527 RTMsgError("%ls.", info.getText().raw());
528 else
529 {
530 RTMsgError("Process error details:");
531 GluePrintErrorInfo(info);
532 }
533 }
534 else
535 com::GluePrintRCMessage(iRc);
536 }
537 else if (fVerbose)
538 {
539 ULONG uRetStatus, uRetExitCode, uRetFlags;
540 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
541 if (SUCCEEDED(rc))
542 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, uRetStatus, ctrlExecGetStatus(uRetStatus), uRetFlags);
543 }
544 }
545 else
546 {
547 if (fVerbose)
548 RTPrintf("Process execution aborted!\n");
549 }
550 }
551 a->session->UnlockMachine();
552 } while (0);
553 return SUCCEEDED(rc) ? 0 : 1;
554}
555
556/**
557 * Signal handler that sets g_fCopyCanceled.
558 *
559 * This can be executed on any thread in the process, on Windows it may even be
560 * a thread dedicated to delivering this signal. Do not doing anything
561 * unnecessary here.
562 */
563static void ctrlCopySignalHandler(int iSignal)
564{
565 NOREF(iSignal);
566 ASMAtomicWriteBool(&g_fCopyCanceled, true);
567}
568
569/**
570 * Appends a new to-copy object to a copy list.
571 *
572 * @return IPRT status code.
573 * @param pszFileSource Full qualified source path of file to copy.
574 * @param pszFileDest Full qualified destination path.
575 * @param pList Copy list used for insertion.
576 */
577int ctrlCopyDirectoryEntryAppend(const char *pszFileSource, const char *pszFileDest,
578 PRTLISTNODE pList)
579{
580 AssertPtrReturn(pszFileSource, VERR_INVALID_POINTER);
581 AssertPtrReturn(pszFileDest, VERR_INVALID_POINTER);
582 AssertPtrReturn(pList, VERR_INVALID_POINTER);
583
584 PDIRECTORYENTRY pNode = (PDIRECTORYENTRY)RTMemAlloc(sizeof(DIRECTORYENTRY));
585 if (pNode == NULL)
586 return VERR_NO_MEMORY;
587
588 pNode->pszSourcePath = NULL;
589 pNode->pszDestPath = NULL;
590 if (RT_SUCCESS(RTStrAAppend(&pNode->pszSourcePath, pszFileSource)))
591 {
592 if (RT_SUCCESS(RTStrAAppend(&pNode->pszDestPath, pszFileDest)))
593 {
594 pNode->Node.pPrev = NULL;
595 pNode->Node.pNext = NULL;
596 RTListAppend(pList, &pNode->Node);
597 return VINF_SUCCESS;
598 }
599 return VERR_NO_MEMORY;
600 }
601 return VERR_NO_MEMORY;
602}
603
604/**
605 * Reads a specified directory (recursively) based on the copy flags
606 * and appends all matching entries to the supplied list.
607 *
608 * @return IPRT status code.
609 * @param pszRootDir Directory to start with. Must end with
610 * a trailing slash and must be absolute.
611 * @param pszSubDir Sub directory part relative to the root
612 * directory; needed for recursion.
613 * @param pszFilter Search filter (e.g. *.pdf).
614 * @param pszDest Destination directory.
615 * @param uFlags Copy flags.
616 * @param pcObjects Where to store the overall objects to
617 * copy found.
618 * @param pList Pointer to the object list to use.
619 */
620int ctrlCopyDirectoryRead(const char *pszRootDir, const char *pszSubDir,
621 const char *pszFilter, const char *pszDest,
622 uint32_t uFlags, uint32_t *pcObjects, PRTLISTNODE pList)
623{
624 AssertPtrReturn(pszRootDir, VERR_INVALID_POINTER);
625 /* Sub directory is optional. */
626 /* Filter directory is optional. */
627 AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
628 AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
629 AssertPtrReturn(pList, VERR_INVALID_POINTER);
630
631 PRTDIR pDir = NULL;
632
633 int rc = VINF_SUCCESS;
634 char szCurDir[RTPATH_MAX];
635 /* Construct current path. */
636 if (RTStrPrintf(szCurDir, sizeof(szCurDir), pszRootDir))
637 {
638 if (pszSubDir != NULL)
639 rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
640 }
641 else
642 rc = VERR_NO_MEMORY;
643
644 if (RT_SUCCESS(rc))
645 {
646 /* Open directory without a filter - RTDirOpenFiltered unfortunately
647 * cannot handle sub directories so we have to do the filtering ourselves. */
648 rc = RTDirOpen(&pDir, szCurDir);
649 for (;RT_SUCCESS(rc);)
650 {
651 RTDIRENTRY DirEntry;
652 rc = RTDirRead(pDir, &DirEntry, NULL);
653 if (RT_FAILURE(rc))
654 {
655 if (rc == VERR_NO_MORE_FILES)
656 rc = VINF_SUCCESS;
657 break;
658 }
659 switch (DirEntry.enmType)
660 {
661 case RTDIRENTRYTYPE_DIRECTORY:
662 /* Skip "." and ".." entrires. */
663 if ( !strcmp(DirEntry.szName, ".")
664 || !strcmp(DirEntry.szName, ".."))
665 {
666 break;
667 }
668 if (uFlags & CopyFileFlag_Recursive)
669 {
670 char *pszNewSub = NULL;
671 if (pszSubDir)
672 RTStrAPrintf(&pszNewSub, "%s%s/", pszSubDir, DirEntry.szName);
673 else
674 RTStrAPrintf(&pszNewSub, "%s/", DirEntry.szName);
675
676 if (pszNewSub)
677 {
678 rc = ctrlCopyDirectoryRead(pszRootDir, pszNewSub,
679 pszFilter, pszDest,
680 uFlags, pcObjects, pList);
681 RTStrFree(pszNewSub);
682 }
683 else
684 rc = VERR_NO_MEMORY;
685 }
686 break;
687
688 case RTDIRENTRYTYPE_SYMLINK:
689 if ( (uFlags & CopyFileFlag_Recursive)
690 && (uFlags & CopyFileFlag_FollowLinks))
691 {
692 /* Fall through to next case is intentional. */
693 }
694 else
695 break;
696
697 case RTDIRENTRYTYPE_FILE:
698 {
699 bool fProcess = false;
700 if (pszFilter && RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
701 fProcess = true;
702 else if (!pszFilter)
703 fProcess = true;
704
705 if (fProcess)
706 {
707 char *pszFileSource = NULL;
708 char *pszFileDest = NULL;
709 if (RTStrAPrintf(&pszFileSource, "%s%s%s",
710 pszRootDir, pszSubDir ? pszSubDir : "",
711 DirEntry.szName) >= 0)
712 {
713 if (RTStrAPrintf(&pszFileDest, "%s%s%s",
714 pszDest, pszSubDir ? pszSubDir : "",
715 DirEntry.szName) <= 0)
716 {
717 rc = VERR_NO_MEMORY;
718 }
719 }
720 else
721 rc = VERR_NO_MEMORY;
722
723 if (RT_SUCCESS(rc))
724 {
725 rc = ctrlCopyDirectoryEntryAppend(pszFileSource, pszFileDest, pList);
726 if (RT_SUCCESS(rc))
727 *pcObjects = *pcObjects + 1;
728 }
729
730 if (pszFileSource)
731 RTStrFree(pszFileSource);
732 if (pszFileDest)
733 RTStrFree(pszFileDest);
734 }
735 }
736 break;
737
738 default:
739 break;
740 }
741 if (RT_FAILURE(rc))
742 break;
743 }
744 }
745
746 if (pDir)
747 RTDirClose(pDir);
748 return rc;
749}
750
751/**
752 * Initializes the copy process and builds up an object list
753 * with all required information to start the actual copy process.
754 *
755 * @return IPRT status code.
756 * @param pszSource Source path on host to use.
757 * @param pszDest Destination path on guest to use.
758 * @param uFlags Copy flags.
759 * @param pcObjects Where to store the count of objects to be copied.
760 * @param pList Where to store the object list.
761 */
762int ctrlCopyInit(const char *pszSource, const char *pszDest, uint32_t uFlags,
763 uint32_t *pcObjects, PRTLISTNODE pList)
764{
765 AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
766 AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
767 AssertPtrReturn(pcObjects, VERR_INVALID_PARAMETER);
768 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
769
770 int rc = VINF_SUCCESS;
771 char *pszSourceAbs = RTPathAbsDup(pszSource);
772 if (pszSourceAbs)
773 {
774 if ( RTPathFilename(pszSourceAbs)
775 && RTFileExists(pszSourceAbs)) /* We have a single file ... */
776 {
777 char *pszDestAbs = RTStrDup(pszDest);
778 if (pszDestAbs)
779 {
780 RTPathStripFilename(pszDestAbs);
781 rc = RTStrAAppend(&pszDestAbs, RTPATH_SLASH_STR);
782 if (RT_SUCCESS(rc))
783 rc = RTStrAAppend(&pszDestAbs, RTPathFilename(pszSourceAbs));
784
785 if (RT_SUCCESS(rc))
786 {
787 RTListInit(pList);
788 rc = ctrlCopyDirectoryEntryAppend(pszSourceAbs, pszDestAbs, pList);
789 *pcObjects = 1;
790 }
791 RTStrFree(pszDestAbs);
792 }
793 else
794 rc = VERR_NO_MEMORY;
795 }
796 else /* ... or a directory. */
797 {
798 /* Append trailing slash to absolute directory. */
799 if (RTDirExists(pszSourceAbs))
800 RTStrAAppend(&pszSourceAbs, RTPATH_SLASH_STR);
801
802 /* Extract directory filter (e.g. "*.exe"). */
803 char *pszFilter = RTPathFilename(pszSourceAbs);
804 char *pszSourceAbsRoot = RTStrDup(pszSourceAbs);
805 char *pszDestAbs = RTStrDup(pszDest);
806 if ( pszSourceAbsRoot
807 && pszDestAbs)
808 {
809 if (pszFilter)
810 {
811 RTPathStripFilename(pszSourceAbsRoot);
812 RTStrAAppend(&pszSourceAbsRoot, RTPATH_SLASH_STR);
813 }
814 else
815 {
816 /*
817 * If we have more than one file to copy, make sure that we have
818 * a trailing slash so that we can construct a full path name
819 * (e.g. "foo.txt" -> "c:/foo/temp.txt") as destination.
820 */
821 size_t cch = strlen(pszSourceAbsRoot);
822 if ( cch > 1
823 && !RTPATH_IS_SLASH(pszSourceAbsRoot[cch - 1])
824 && !RTPATH_IS_SLASH(pszSourceAbsRoot[cch - 2]))
825 {
826 rc = RTStrAAppend(&pszSourceAbsRoot, RTPATH_SLASH_STR);
827 }
828 }
829
830 if (RT_SUCCESS(rc))
831 {
832 /*
833 * Make sure we have a valid destination path. All we can do
834 * here is to check whether we have a trailing slash -- the rest
835 * (i.e. path creation, rights etc.) needs to be done inside the guest.
836 */
837 size_t cch = strlen(pszDestAbs);
838 if ( cch > 1
839 && !RTPATH_IS_SLASH(pszDestAbs[cch - 1])
840 && !RTPATH_IS_SLASH(pszDestAbs[cch - 2]))
841 {
842 rc = RTStrAAppend(&pszDestAbs, RTPATH_SLASH_STR);
843 }
844 }
845
846 if (RT_SUCCESS(rc))
847 {
848 RTListInit(pList);
849 rc = ctrlCopyDirectoryRead(pszSourceAbsRoot, NULL /* Sub directory */,
850 pszFilter, pszDestAbs,
851 uFlags, pcObjects, pList);
852 if (RT_SUCCESS(rc) && *pcObjects == 0)
853 rc = VERR_NOT_FOUND;
854 }
855
856 if (pszDestAbs)
857 RTStrFree(pszDestAbs);
858 if (pszSourceAbsRoot)
859 RTStrFree(pszSourceAbsRoot);
860 }
861 else
862 rc = VERR_NO_MEMORY;
863 }
864 RTStrFree(pszSourceAbs);
865 }
866 else
867 rc = VERR_NO_MEMORY;
868 return rc;
869}
870
871/**
872 * Destroys a copy list.
873 */
874void ctrlCopyDestroy(PRTLISTNODE pList)
875{
876 AssertPtr(pList);
877
878 /* Destroy file list. */
879 PDIRECTORYENTRY pNode = RTListNodeGetFirst(pList, DIRECTORYENTRY, Node);
880 while (pNode)
881 {
882 PDIRECTORYENTRY pNext = RTListNodeGetNext(&pNode->Node, DIRECTORYENTRY, Node);
883 bool fLast = RTListNodeIsLast(pList, &pNode->Node);
884
885 if (pNode->pszSourcePath)
886 RTStrFree(pNode->pszSourcePath);
887 if (pNode->pszDestPath)
888 RTStrFree(pNode->pszDestPath);
889 RTListNodeRemove(&pNode->Node);
890 RTMemFree(pNode);
891
892 if (fLast)
893 break;
894
895 pNode = pNext;
896 }
897}
898
899/**
900 * Copys a file from host to the guest.
901 *
902 * @return IPRT status code.
903 * @param pGuest IGuest interface pointer.
904 * @param pszSource Source path of existing host file to copy.
905 * @param pszDest Destination path on guest to copy the file to.
906 * @param pszUserName User name on guest to use for the copy operation.
907 * @param pszPassword Password of user account.
908 * @param uFlags Copy flags.
909 */
910int ctrlCopyFileToGuest(IGuest *pGuest, const char *pszSource, const char *pszDest,
911 const char *pszUserName, const char *pszPassword,
912 uint32_t uFlags)
913{
914 AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
915 AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
916 AssertPtrReturn(pszUserName, VERR_INVALID_PARAMETER);
917 AssertPtrReturn(pszPassword, VERR_INVALID_PARAMETER);
918
919 int vrc = VINF_SUCCESS;
920 ComPtr<IProgress> progress;
921 HRESULT rc = pGuest->CopyToGuest(Bstr(pszSource).raw(), Bstr(pszDest).raw(),
922 Bstr(pszUserName).raw(), Bstr(pszPassword).raw(),
923 uFlags, progress.asOutParam());
924 if (FAILED(rc))
925 {
926 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
927 * because it contains more accurate info about what went wrong. */
928 ErrorInfo info(pGuest, COM_IIDOF(IGuest));
929 if (info.isFullAvailable())
930 {
931 if (rc == VBOX_E_IPRT_ERROR)
932 RTMsgError("%ls.", info.getText().raw());
933 else
934 RTMsgError("%ls (%Rhrc).", info.getText().raw(), info.getResultCode());
935 }
936 vrc = VERR_GENERAL_FAILURE;
937 }
938 else
939 {
940 /* Setup signal handling if cancelable. */
941 ASSERT(progress);
942 bool fCanceledAlready = false;
943 BOOL fCancelable;
944 HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
945 if (FAILED(hrc))
946 fCancelable = FALSE;
947 if (fCancelable)
948 {
949 signal(SIGINT, ctrlCopySignalHandler);
950 #ifdef SIGBREAK
951 signal(SIGBREAK, ctrlCopySignalHandler);
952 #endif
953 }
954
955 /* Wait for process to exit ... */
956 BOOL fCompleted = FALSE;
957 BOOL fCanceled = FALSE;
958 while ( SUCCEEDED(progress->COMGETTER(Completed(&fCompleted)))
959 && !fCompleted)
960 {
961 /* Process async cancelation */
962 if (g_fCopyCanceled && !fCanceledAlready)
963 {
964 hrc = progress->Cancel();
965 if (SUCCEEDED(hrc))
966 fCanceledAlready = TRUE;
967 else
968 g_fCopyCanceled = false;
969 }
970
971 /* Progress canceled by Main API? */
972 if ( SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
973 && fCanceled)
974 {
975 break;
976 }
977 }
978
979 /* Undo signal handling */
980 if (fCancelable)
981 {
982 signal(SIGINT, SIG_DFL);
983 #ifdef SIGBREAK
984 signal(SIGBREAK, SIG_DFL);
985 #endif
986 }
987
988 if (fCanceled)
989 {
990 //RTPrintf("Copy operation canceled!\n");
991 }
992 else if ( fCompleted
993 && SUCCEEDED(rc))
994 {
995 LONG iRc = false;
996 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
997 if (FAILED(iRc))
998 {
999 com::ProgressErrorInfo info(progress);
1000 if ( info.isFullAvailable()
1001 || info.isBasicAvailable())
1002 {
1003 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
1004 * because it contains more accurate info about what went wrong. */
1005 if (info.getResultCode() == VBOX_E_IPRT_ERROR)
1006 RTMsgError("%ls.", info.getText().raw());
1007 else
1008 {
1009 RTMsgError("Copy operation error details:");
1010 GluePrintErrorInfo(info);
1011 }
1012 }
1013 else
1014 {
1015 if (RT_FAILURE(vrc))
1016 RTMsgError("Error while looking up error code, rc=%Rrc\n", vrc);
1017 else
1018 com::GluePrintRCMessage(iRc);
1019 }
1020 vrc = VERR_GENERAL_FAILURE;
1021 }
1022 }
1023 }
1024 return vrc;
1025}
1026
1027static int handleCtrlCopyTo(HandlerArg *a)
1028{
1029 /*
1030 * Check the syntax. We can deduce the correct syntax from the number of
1031 * arguments.
1032 */
1033 if (a->argc < 3) /* At least the source + destination should be present :-). */
1034 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1035
1036 Utf8Str Utf8Source(a->argv[1]);
1037 Utf8Str Utf8Dest(a->argv[2]);
1038 Utf8Str Utf8UserName;
1039 Utf8Str Utf8Password;
1040 uint32_t uFlags = CopyFileFlag_None;
1041 bool fVerbose = false;
1042 bool fCopyRecursive = false;
1043 bool fDryRun = false;
1044
1045 /* Iterate through all possible commands (if available). */
1046 bool usageOK = true;
1047 for (int i = 3; usageOK && i < a->argc; i++)
1048 {
1049 if ( !strcmp(a->argv[i], "--username")
1050 || !strcmp(a->argv[i], "--user"))
1051 {
1052 if (i + 1 >= a->argc)
1053 usageOK = false;
1054 else
1055 {
1056 Utf8UserName = a->argv[i + 1];
1057 ++i;
1058 }
1059 }
1060 else if ( !strcmp(a->argv[i], "--password")
1061 || !strcmp(a->argv[i], "--pwd"))
1062 {
1063 if (i + 1 >= a->argc)
1064 usageOK = false;
1065 else
1066 {
1067 Utf8Password = a->argv[i + 1];
1068 ++i;
1069 }
1070 }
1071 else if (!strcmp(a->argv[i], "--dryrun"))
1072 {
1073 fDryRun = true;
1074 }
1075 else if (!strcmp(a->argv[i], "--flags"))
1076 {
1077 if (i + 1 >= a->argc)
1078 usageOK = false;
1079 else
1080 {
1081 /* Nothing to do here yet. */
1082 ++i;
1083 }
1084 }
1085 else if ( !strcmp(a->argv[i], "--recursive")
1086 || !strcmp(a->argv[i], "--r"))
1087 {
1088 uFlags |= CopyFileFlag_Recursive;
1089 }
1090 else if ( !strcmp(a->argv[i], "--update")
1091 || !strcmp(a->argv[i], "--u"))
1092 {
1093 uFlags |= CopyFileFlag_Update;
1094 }
1095 else if ( !strcmp(a->argv[i], "--follow")
1096 || !strcmp(a->argv[i], "--f"))
1097 {
1098 uFlags |= CopyFileFlag_FollowLinks;
1099 }
1100 /** @todo Add force flag for overwriting existing stuff. */
1101 else if (!strcmp(a->argv[i], "--verbose"))
1102 fVerbose = true;
1103 else
1104 return errorSyntax(USAGE_GUESTCONTROL,
1105 "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1106 }
1107
1108 if (!usageOK)
1109 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1110
1111 if (Utf8Source.isEmpty())
1112 return errorSyntax(USAGE_GUESTCONTROL,
1113 "No source specified!");
1114
1115 if (Utf8Dest.isEmpty())
1116 return errorSyntax(USAGE_GUESTCONTROL,
1117 "No destination specified!");
1118
1119 if (Utf8UserName.isEmpty())
1120 return errorSyntax(USAGE_GUESTCONTROL,
1121 "No user name specified!");
1122
1123 /* Lookup VM. */
1124 ComPtr<IMachine> machine;
1125 /* Assume it's an UUID. */
1126 HRESULT rc;
1127 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1128 machine.asOutParam()));
1129 if (FAILED(rc))
1130 return 1;
1131
1132 /* Machine is running? */
1133 MachineState_T machineState;
1134 CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), 1);
1135 if (machineState != MachineState_Running)
1136 {
1137 RTMsgError("Machine \"%s\" is not running!\n", a->argv[0]);
1138 return 1;
1139 }
1140
1141 /* Open a session for the VM. */
1142 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
1143
1144 do
1145 {
1146 /* Get the associated console. */
1147 ComPtr<IConsole> console;
1148 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
1149 /* ... and session machine */
1150 ComPtr<IMachine> sessionMachine;
1151 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
1152
1153 ComPtr<IGuest> guest;
1154 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam()));
1155
1156 if (fVerbose)
1157 {
1158 if (fDryRun)
1159 RTPrintf("Dry run - no files copied!\n");
1160 RTPrintf("Gathering file information ...\n");
1161 }
1162
1163 RTLISTNODE listToCopy;
1164 uint32_t cObjects = 0;
1165 int vrc = ctrlCopyInit(Utf8Source.c_str(), Utf8Dest.c_str(), uFlags,
1166 &cObjects, &listToCopy);
1167 if (RT_FAILURE(vrc))
1168 {
1169 switch (vrc)
1170 {
1171 case VERR_NOT_FOUND:
1172 RTMsgError("No files to copy found!\n");
1173 break;
1174
1175 case VERR_PATH_NOT_FOUND:
1176 RTMsgError("Source path \"%s\" not found!\n", Utf8Source.c_str());
1177 break;
1178
1179 default:
1180 RTMsgError("Failed to initialize, rc=%Rrc\n", vrc);
1181 break;
1182 }
1183 }
1184 else
1185 {
1186 if (RT_SUCCESS(vrc) && fVerbose)
1187 {
1188 if (fCopyRecursive)
1189 RTPrintf("Recursively copying \"%s\" to \"%s\" (%u file(s)) ...\n",
1190 Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
1191 else
1192 RTPrintf("Copying \"%s\" to \"%s\" (%u file(s)) ...\n",
1193 Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
1194 }
1195
1196 if (RT_SUCCESS(vrc))
1197 {
1198 PDIRECTORYENTRY pNode;
1199 uint32_t uCurObject = 1;
1200 char szDest[RTPATH_MAX];
1201 RTListForEach(&listToCopy, pNode, DIRECTORYENTRY, Node)
1202 {
1203 /*
1204 * Build final destination path: Append the relative path
1205 * stored in the directory node to the destination directory
1206 * specified on the command line.
1207 */
1208 szDest[0] = '\0'; /* Terminate string, needed for RTPathAppend(). */
1209 vrc = RTPathAppend(szDest, sizeof(szDest), Utf8Dest.c_str());
1210 if (RT_SUCCESS(vrc))
1211 vrc = RTPathAppend(szDest, sizeof(szDest), pNode->pszDestPath);
1212
1213 if (RT_SUCCESS(vrc))
1214 {
1215 if (fVerbose)
1216#ifndef DEBUG
1217 RTPrintf("Copying \"%s\" (%u/%u) ...\n",
1218 pNode->pszSourcePath, uCurObject, cObjects);
1219#else
1220 RTPrintf("Copying \"%s\" to \"%s\" (%u/%u) ...\n",
1221 pNode->pszSourcePath, pNode->pszDestPath, uCurObject, cObjects);
1222#endif
1223 /* Finally copy the desired file (if no dry run selected). */
1224 if (!fDryRun)
1225 vrc = ctrlCopyFileToGuest(guest, pNode->pszSourcePath, szDest,
1226 Utf8UserName.c_str(), Utf8Password.c_str(), uFlags);
1227 }
1228 else
1229 RTMsgError("Error building destination file name, rc=%Rrc\n", vrc);
1230 if (RT_FAILURE(vrc))
1231 break;
1232 uCurObject++;
1233 }
1234 if (RT_SUCCESS(vrc) && fVerbose)
1235 RTPrintf("Copy operation successful!\n");
1236 }
1237 ctrlCopyDestroy(&listToCopy);
1238 }
1239 a->session->UnlockMachine();
1240 } while (0);
1241 return SUCCEEDED(rc) ? 0 : 1;
1242}
1243
1244static int handleCtrlUpdateAdditions(HandlerArg *a)
1245{
1246 /*
1247 * Check the syntax. We can deduce the correct syntax from the number of
1248 * arguments.
1249 */
1250 if (a->argc < 1) /* At least the VM name should be present :-). */
1251 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1252
1253 Utf8Str Utf8Source;
1254 bool fVerbose = false;
1255
1256 /* Iterate through all possible commands (if available). */
1257 bool usageOK = true;
1258 for (int i = 1; usageOK && i < a->argc; i++)
1259 {
1260 if (!strcmp(a->argv[i], "--source"))
1261 {
1262 if (i + 1 >= a->argc)
1263 usageOK = false;
1264 else
1265 {
1266 Utf8Source = a->argv[i + 1];
1267 ++i;
1268 }
1269 }
1270 else if (!strcmp(a->argv[i], "--verbose"))
1271 fVerbose = true;
1272 else
1273 return errorSyntax(USAGE_GUESTCONTROL,
1274 "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1275 }
1276
1277 if (!usageOK)
1278 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1279
1280 /* Lookup VM. */
1281 ComPtr<IMachine> machine;
1282 /* Assume it's an UUID. */
1283 HRESULT rc;
1284 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1285 machine.asOutParam()));
1286 if (FAILED(rc))
1287 return 1;
1288
1289 /* Machine is running? */
1290 MachineState_T machineState;
1291 CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), 1);
1292 if (machineState != MachineState_Running)
1293 {
1294 RTMsgError("Machine \"%s\" is not running!\n", a->argv[0]);
1295 return 1;
1296 }
1297
1298 /* Open a session for the VM. */
1299 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
1300
1301 do
1302 {
1303 /* Get the associated console. */
1304 ComPtr<IConsole> console;
1305 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
1306 /* ... and session machine */
1307 ComPtr<IMachine> sessionMachine;
1308 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
1309
1310 ComPtr<IGuest> guest;
1311 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam()));
1312
1313 if (fVerbose)
1314 RTPrintf("Updating Guest Additions of machine \"%s\" ...\n", a->argv[0]);
1315
1316#ifdef DEBUG_andy
1317 if (Utf8Source.isEmpty())
1318 Utf8Source = "c:\\Downloads\\VBoxGuestAdditions-r67158.iso";
1319#endif
1320 /* Determine source if not set yet. */
1321 if (Utf8Source.isEmpty())
1322 {
1323 char strTemp[RTPATH_MAX];
1324 int vrc = RTPathAppPrivateNoArch(strTemp, sizeof(strTemp));
1325 AssertRC(vrc);
1326 Utf8Str Utf8Src1 = Utf8Str(strTemp).append("/VBoxGuestAdditions.iso");
1327
1328 vrc = RTPathExecDir(strTemp, sizeof(strTemp));
1329 AssertRC(vrc);
1330 Utf8Str Utf8Src2 = Utf8Str(strTemp).append("/additions/VBoxGuestAdditions.iso");
1331
1332 /* Check the standard image locations */
1333 if (RTFileExists(Utf8Src1.c_str()))
1334 Utf8Source = Utf8Src1;
1335 else if (RTFileExists(Utf8Src2.c_str()))
1336 Utf8Source = Utf8Src2;
1337 else
1338 {
1339 RTMsgError("Source could not be determined! Please use --source to specify a valid source.\n");
1340 break;
1341 }
1342 }
1343 else if (!RTFileExists(Utf8Source.c_str()))
1344 {
1345 RTMsgError("Source \"%s\" does not exist!\n", Utf8Source.c_str());
1346 break;
1347 }
1348 if (fVerbose)
1349 RTPrintf("Using source: %s\n", Utf8Source.c_str());
1350
1351 ComPtr<IProgress> progress;
1352 CHECK_ERROR_BREAK(guest, UpdateGuestAdditions(Bstr(Utf8Source).raw(),
1353 0 /* Flags, not used. */,
1354 progress.asOutParam()));
1355 rc = showProgress(progress);
1356 if (FAILED(rc))
1357 {
1358 com::ProgressErrorInfo info(progress);
1359 if (info.isBasicAvailable())
1360 RTMsgError("Failed to start Guest Additions update. Error message: %lS\n", info.getText().raw());
1361 else
1362 RTMsgError("Failed to start Guest Additions update. No error message available!\n");
1363 }
1364 else
1365 {
1366 if (fVerbose)
1367 RTPrintf("Guest Additions installer successfully copied and started.\n");
1368 }
1369 a->session->UnlockMachine();
1370 } while (0);
1371 return SUCCEEDED(rc) ? 0 : 1;
1372}
1373
1374/**
1375 * Access the guest control store.
1376 *
1377 * @returns 0 on success, 1 on failure
1378 * @note see the command line API description for parameters
1379 */
1380int handleGuestControl(HandlerArg *a)
1381{
1382 HandlerArg arg = *a;
1383 arg.argc = a->argc - 1;
1384 arg.argv = a->argv + 1;
1385
1386 if (a->argc == 0)
1387 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1388
1389 /* switch (cmd) */
1390 if ( !strcmp(a->argv[0], "exec")
1391 || !strcmp(a->argv[0], "execute"))
1392 {
1393 return handleCtrlExecProgram(&arg);
1394 }
1395 else if (!strcmp(a->argv[0], "copyto"))
1396 {
1397 return handleCtrlCopyTo(&arg);
1398 }
1399 else if ( !strcmp(a->argv[0], "updateadditions")
1400 || !strcmp(a->argv[0], "updateadds"))
1401 {
1402 return handleCtrlUpdateAdditions(&arg);
1403 }
1404
1405 /* default: */
1406 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1407}
1408
1409#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette