VirtualBox

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

最後變更 在這個檔案從34858是 34858,由 vboxsync 提交於 14 年 前

GuestControl/VBoxManage: Removed not used arguments, updated syntax diagram.

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

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