VirtualBox

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

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

VBoxManage/GuestCtrl: Fix for process completion w/o waiting for output.

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