VirtualBox

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

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

Main/Frontends: Also use facilities for guest features (seamless, graphics), added facility-state-to-name to VBoxManage, some renaming.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 KB
Line 
1/* $Id: VBoxManageGuestCtrl.cpp 35907 2011-02-09 11:20:31Z 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, machineStateToName(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 int cMilliesSleep = 0;
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 RT_MAX(0, u32TimeoutMS - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
508 _64K, ComSafeArrayAsOutParam(aOutputData));
509 if (FAILED(rc))
510 {
511 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
512 cbOutputData = 0;
513 }
514 else
515 {
516 cbOutputData = aOutputData.size();
517 if (cbOutputData > 0)
518 {
519 /* aOutputData has a platform dependent line ending, standardize on
520 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
521 * Windows. Otherwise we end up with CR/CR/LF on Windows. */
522 ULONG cbOutputDataPrint = cbOutputData;
523 for (BYTE *s = aOutputData.raw(), *d = s;
524 s - aOutputData.raw() < (ssize_t)cbOutputData;
525 s++, d++)
526 {
527 if (*s == '\r')
528 {
529 /* skip over CR, adjust destination */
530 d--;
531 cbOutputDataPrint--;
532 }
533 else if (s != d)
534 *d = *s;
535 }
536 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
537 }
538 }
539 }
540
541 /* No more output data left? */
542 if (cbOutputData <= 0)
543 {
544 /* Only break out from process handling loop if we processed (displayed)
545 * all output data or if there simply never was output data and the process
546 * has been marked as complete. */
547 if (fCompleted)
548 break;
549
550 /* Then wait a little while ... */
551 progress->WaitForCompletion(1 /* ms */);
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 /* Did we run out of time? */
572 if ( u32TimeoutMS
573 && RTTimeMilliTS() - u64StartMS > u32TimeoutMS)
574 {
575 progress->Cancel();
576 break;
577 }
578
579 /* Don't hog the CPU in a busy loop! */
580 if (cbOutputData <= 0)
581 {
582 if (cMilliesSleep < 100)
583 cMilliesSleep++;
584 RTThreadSleep(cMilliesSleep);
585 }
586 else
587 cMilliesSleep = 0;
588 }
589
590 /* Undo signal handling */
591 if (fCancelable)
592 ctrlSignalHandlerUninstall();
593
594 if (fCanceled)
595 {
596 if (fVerbose)
597 RTPrintf("Process execution canceled!\n");
598 }
599 else if ( fCompleted
600 && SUCCEEDED(rc))
601 {
602 LONG iRc;
603 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
604 if (FAILED(iRc))
605 {
606 vrc = ctrlPrintProgressError(progress);
607 }
608 else if (fVerbose)
609 {
610 ULONG uRetStatus, uRetExitCode, uRetFlags;
611 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
612 if (SUCCEEDED(rc))
613 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, uRetStatus, ctrlExecGetStatus(uRetStatus), uRetFlags);
614 }
615 }
616 else
617 {
618 if (fVerbose)
619 RTPrintf("Process execution aborted!\n");
620 }
621 }
622 }
623 ctrlUninitVM(a);
624 }
625
626 if (RT_FAILURE(vrc))
627 rc = VBOX_E_IPRT_ERROR;
628 return SUCCEEDED(rc) ? 0 : 1;
629}
630
631/**
632 * Appends a new file/directory entry to a given list.
633 *
634 * @return IPRT status code.
635 * @param pszFileSource Full qualified source path of file to copy (optional).
636 * @param pszFileDest Full qualified destination path (optional).
637 * @param pList Copy list used for insertion.
638 */
639static int ctrlDirectoryEntryAppend(const char *pszFileSource, const char *pszFileDest,
640 PRTLISTNODE pList)
641{
642 AssertPtrReturn(pList, VERR_INVALID_POINTER);
643 AssertReturn(pszFileSource || pszFileDest, VERR_INVALID_PARAMETER);
644
645 PDIRECTORYENTRY pNode = (PDIRECTORYENTRY)RTMemAlloc(sizeof(DIRECTORYENTRY));
646 if (pNode == NULL)
647 return VERR_NO_MEMORY;
648
649 pNode->pszSourcePath = NULL;
650 pNode->pszDestPath = NULL;
651
652 if (pszFileSource)
653 {
654 pNode->pszSourcePath = RTStrDup(pszFileSource);
655 AssertPtrReturn(pNode->pszSourcePath, VERR_NO_MEMORY);
656 }
657 if (pszFileDest)
658 {
659 pNode->pszDestPath = RTStrDup(pszFileDest);
660 AssertPtrReturn(pNode->pszDestPath, VERR_NO_MEMORY);
661 }
662
663 pNode->Node.pPrev = NULL;
664 pNode->Node.pNext = NULL;
665 RTListAppend(pList, &pNode->Node);
666 return VINF_SUCCESS;
667}
668
669/**
670 * Destroys a directory list.
671 *
672 * @param pList Pointer to list to destroy.
673 */
674static void ctrlDirectoryListDestroy(PRTLISTNODE pList)
675{
676 AssertPtr(pList);
677
678 /* Destroy file list. */
679 PDIRECTORYENTRY pNode = RTListGetFirst(pList, DIRECTORYENTRY, Node);
680 while (pNode)
681 {
682 PDIRECTORYENTRY pNext = RTListNodeGetNext(&pNode->Node, DIRECTORYENTRY, Node);
683 bool fLast = RTListNodeIsLast(pList, &pNode->Node);
684
685 if (pNode->pszSourcePath)
686 RTStrFree(pNode->pszSourcePath);
687 if (pNode->pszDestPath)
688 RTStrFree(pNode->pszDestPath);
689 RTListNodeRemove(&pNode->Node);
690 RTMemFree(pNode);
691
692 if (fLast)
693 break;
694
695 pNode = pNext;
696 }
697}
698
699
700/**
701 * Reads a specified directory (recursively) based on the copy flags
702 * and appends all matching entries to the supplied list.
703 *
704 * @return IPRT status code.
705 * @param pszRootDir Directory to start with. Must end with
706 * a trailing slash and must be absolute.
707 * @param pszSubDir Sub directory part relative to the root
708 * directory; needed for recursion.
709 * @param pszFilter Search filter (e.g. *.pdf).
710 * @param pszDest Destination directory.
711 * @param uFlags Copy flags.
712 * @param pcObjects Where to store the overall objects to
713 * copy found.
714 * @param pList Pointer to the object list to use.
715 */
716static int ctrlCopyDirectoryRead(const char *pszRootDir, const char *pszSubDir,
717 const char *pszFilter, const char *pszDest,
718 uint32_t uFlags, uint32_t *pcObjects, PRTLISTNODE pList)
719{
720 AssertPtrReturn(pszRootDir, VERR_INVALID_POINTER);
721 /* Sub directory is optional. */
722 /* Filter directory is optional. */
723 AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
724 AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
725 AssertPtrReturn(pList, VERR_INVALID_POINTER);
726
727 PRTDIR pDir = NULL;
728
729 int rc = VINF_SUCCESS;
730 char szCurDir[RTPATH_MAX];
731 /* Construct current path. */
732 if (RTStrPrintf(szCurDir, sizeof(szCurDir), pszRootDir))
733 {
734 if (pszSubDir != NULL)
735 rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
736 }
737 else
738 rc = VERR_NO_MEMORY;
739
740 if (RT_SUCCESS(rc))
741 {
742 /* Open directory without a filter - RTDirOpenFiltered unfortunately
743 * cannot handle sub directories so we have to do the filtering ourselves. */
744 rc = RTDirOpen(&pDir, szCurDir);
745 for (;RT_SUCCESS(rc);)
746 {
747 RTDIRENTRY DirEntry;
748 rc = RTDirRead(pDir, &DirEntry, NULL);
749 if (RT_FAILURE(rc))
750 {
751 if (rc == VERR_NO_MORE_FILES)
752 rc = VINF_SUCCESS;
753 break;
754 }
755 switch (DirEntry.enmType)
756 {
757 case RTDIRENTRYTYPE_DIRECTORY:
758 /* Skip "." and ".." entrires. */
759 if ( !strcmp(DirEntry.szName, ".")
760 || !strcmp(DirEntry.szName, ".."))
761 {
762 break;
763 }
764 if (uFlags & CopyFileFlag_Recursive)
765 {
766 char *pszNewSub = NULL;
767 if (pszSubDir)
768 RTStrAPrintf(&pszNewSub, "%s%s/", pszSubDir, DirEntry.szName);
769 else
770 RTStrAPrintf(&pszNewSub, "%s/", DirEntry.szName);
771
772 if (pszNewSub)
773 {
774 rc = ctrlCopyDirectoryRead(pszRootDir, pszNewSub,
775 pszFilter, pszDest,
776 uFlags, pcObjects, pList);
777 RTStrFree(pszNewSub);
778 }
779 else
780 rc = VERR_NO_MEMORY;
781 }
782 break;
783
784 case RTDIRENTRYTYPE_SYMLINK:
785 if ( (uFlags & CopyFileFlag_Recursive)
786 && (uFlags & CopyFileFlag_FollowLinks))
787 {
788 /* Fall through to next case is intentional. */
789 }
790 else
791 break;
792
793 case RTDIRENTRYTYPE_FILE:
794 {
795 bool fProcess = false;
796 if (pszFilter && RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
797 fProcess = true;
798 else if (!pszFilter)
799 fProcess = true;
800
801 if (fProcess)
802 {
803 char *pszFileSource = NULL;
804 char *pszFileDest = NULL;
805 if (RTStrAPrintf(&pszFileSource, "%s%s%s",
806 pszRootDir, pszSubDir ? pszSubDir : "",
807 DirEntry.szName) >= 0)
808 {
809 if (RTStrAPrintf(&pszFileDest, "%s%s%s",
810 pszDest, pszSubDir ? pszSubDir : "",
811 DirEntry.szName) <= 0)
812 {
813 rc = VERR_NO_MEMORY;
814 }
815 }
816 else
817 rc = VERR_NO_MEMORY;
818
819 if (RT_SUCCESS(rc))
820 {
821 rc = ctrlDirectoryEntryAppend(pszFileSource, pszFileDest, pList);
822 if (RT_SUCCESS(rc))
823 *pcObjects = *pcObjects + 1;
824 }
825
826 if (pszFileSource)
827 RTStrFree(pszFileSource);
828 if (pszFileDest)
829 RTStrFree(pszFileDest);
830 }
831 }
832 break;
833
834 default:
835 break;
836 }
837 if (RT_FAILURE(rc))
838 break;
839 }
840 }
841
842 if (pDir)
843 RTDirClose(pDir);
844 return rc;
845}
846
847/**
848 * Initializes the copy process and builds up an object list
849 * with all required information to start the actual copy process.
850 *
851 * @return IPRT status code.
852 * @param pszSource Source path on host to use.
853 * @param pszDest Destination path on guest to use.
854 * @param uFlags Copy flags.
855 * @param pcObjects Where to store the count of objects to be copied.
856 * @param pList Where to store the object list.
857 */
858static int ctrlCopyInit(const char *pszSource, const char *pszDest, uint32_t uFlags,
859 uint32_t *pcObjects, PRTLISTNODE pList)
860{
861 AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
862 AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
863 AssertPtrReturn(pcObjects, VERR_INVALID_PARAMETER);
864 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
865
866 int rc = VINF_SUCCESS;
867 char *pszSourceAbs = RTPathAbsDup(pszSource);
868 if (pszSourceAbs)
869 {
870 if ( RTPathFilename(pszSourceAbs)
871 && RTFileExists(pszSourceAbs)) /* We have a single file ... */
872 {
873 char *pszDestAbs = RTStrDup(pszDest);
874 if (pszDestAbs)
875 {
876 /* Do we have a trailing slash for the destination?
877 * Then this is a directory ... */
878 size_t cch = strlen(pszDestAbs);
879 if ( cch > 1
880 && ( RTPATH_IS_SLASH(pszDestAbs[cch - 1])
881 || RTPATH_IS_SLASH(pszDestAbs[cch - 2])
882 )
883 )
884 {
885 rc = RTStrAAppend(&pszDestAbs, RTPathFilename(pszSourceAbs));
886 }
887 else
888 {
889 /* Since the desetination seems not to be a directory,
890 * we assume that this is the absolute path to the destination
891 * file -> nothing to do here ... */
892 }
893
894 if (RT_SUCCESS(rc))
895 {
896 RTListInit(pList);
897 rc = ctrlDirectoryEntryAppend(pszSourceAbs, pszDestAbs, pList);
898 *pcObjects = 1;
899 }
900 RTStrFree(pszDestAbs);
901 }
902 else
903 rc = VERR_NO_MEMORY;
904 }
905 else /* ... or a directory. */
906 {
907 /* Append trailing slash to absolute directory. */
908 if (RTDirExists(pszSourceAbs))
909 RTStrAAppend(&pszSourceAbs, RTPATH_SLASH_STR);
910
911 /* Extract directory filter (e.g. "*.exe"). */
912 char *pszFilter = RTPathFilename(pszSourceAbs);
913 char *pszSourceAbsRoot = RTStrDup(pszSourceAbs);
914 char *pszDestAbs = RTStrDup(pszDest);
915 if ( pszSourceAbsRoot
916 && pszDestAbs)
917 {
918 if (pszFilter)
919 {
920 RTPathStripFilename(pszSourceAbsRoot);
921 rc = RTStrAAppend(&pszSourceAbsRoot, RTPATH_SLASH_STR);
922 }
923 else
924 {
925 /*
926 * If we have more than one file to copy, make sure that we have
927 * a trailing slash so that we can construct a full path name
928 * (e.g. "foo.txt" -> "c:/foo/temp.txt") as destination.
929 */
930 size_t cch = strlen(pszSourceAbsRoot);
931 if ( cch > 1
932 && !RTPATH_IS_SLASH(pszSourceAbsRoot[cch - 1])
933 && !RTPATH_IS_SLASH(pszSourceAbsRoot[cch - 2]))
934 {
935 rc = RTStrAAppend(&pszSourceAbsRoot, RTPATH_SLASH_STR);
936 }
937 }
938
939 if (RT_SUCCESS(rc))
940 {
941 /*
942 * Make sure we have a valid destination path. All we can do
943 * here is to check whether we have a trailing slash -- the rest
944 * (i.e. path creation, rights etc.) needs to be done inside the guest.
945 */
946 size_t cch = strlen(pszDestAbs);
947 if ( cch > 1
948 && !RTPATH_IS_SLASH(pszDestAbs[cch - 1])
949 && !RTPATH_IS_SLASH(pszDestAbs[cch - 2]))
950 {
951 rc = RTStrAAppend(&pszDestAbs, RTPATH_SLASH_STR);
952 }
953 }
954
955 if (RT_SUCCESS(rc))
956 {
957 RTListInit(pList);
958 rc = ctrlCopyDirectoryRead(pszSourceAbsRoot, NULL /* Sub directory */,
959 pszFilter, pszDestAbs,
960 uFlags, pcObjects, pList);
961 if (RT_SUCCESS(rc) && *pcObjects == 0)
962 rc = VERR_NOT_FOUND;
963 }
964
965 if (pszDestAbs)
966 RTStrFree(pszDestAbs);
967 if (pszSourceAbsRoot)
968 RTStrFree(pszSourceAbsRoot);
969 }
970 else
971 rc = VERR_NO_MEMORY;
972 }
973 RTStrFree(pszSourceAbs);
974 }
975 else
976 rc = VERR_NO_MEMORY;
977 return rc;
978}
979
980/**
981 * Copys a file from host to the guest.
982 *
983 * @return IPRT status code.
984 * @param pGuest IGuest interface pointer.
985 * @param fVerbose Verbose flag.
986 * @param pszSource Source path of existing host file to copy.
987 * @param pszDest Destination path on guest to copy the file to.
988 * @param pszUserName User name on guest to use for the copy operation.
989 * @param pszPassword Password of user account.
990 * @param uFlags Copy flags.
991 */
992static int ctrlCopyFileToGuest(IGuest *pGuest, bool fVerbose, const char *pszSource, const char *pszDest,
993 const char *pszUserName, const char *pszPassword,
994 uint32_t uFlags)
995{
996 AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
997 AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
998 AssertPtrReturn(pszUserName, VERR_INVALID_PARAMETER);
999 AssertPtrReturn(pszPassword, VERR_INVALID_PARAMETER);
1000
1001 int vrc = VINF_SUCCESS;
1002 ComPtr<IProgress> progress;
1003 HRESULT rc = pGuest->CopyToGuest(Bstr(pszSource).raw(), Bstr(pszDest).raw(),
1004 Bstr(pszUserName).raw(), Bstr(pszPassword).raw(),
1005 uFlags, progress.asOutParam());
1006 if (FAILED(rc))
1007 vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
1008 else
1009 {
1010 rc = showProgress(progress);
1011 if (FAILED(rc))
1012 vrc = ctrlPrintProgressError(progress);
1013 }
1014 return vrc;
1015}
1016
1017static int handleCtrlCopyTo(HandlerArg *a)
1018{
1019 /*
1020 * Check the syntax. We can deduce the correct syntax from the number of
1021 * arguments.
1022 */
1023 if (a->argc < 3) /* At least the source + destination should be present :-). */
1024 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1025
1026 static const RTGETOPTDEF s_aOptions[] =
1027 {
1028 { "--dryrun", 'd', RTGETOPT_REQ_NOTHING },
1029 { "--follow", 'F', RTGETOPT_REQ_NOTHING },
1030 { "--password", 'p', RTGETOPT_REQ_STRING },
1031 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1032 { "--username", 'u', RTGETOPT_REQ_STRING },
1033 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1034 };
1035
1036 int ch;
1037 RTGETOPTUNION ValueUnion;
1038 RTGETOPTSTATE GetState;
1039 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1040
1041 Utf8Str Utf8Source;
1042 Utf8Str Utf8Dest;
1043 Utf8Str Utf8UserName;
1044 Utf8Str Utf8Password;
1045 uint32_t uFlags = CopyFileFlag_None;
1046 bool fVerbose = false;
1047 bool fCopyRecursive = false;
1048 bool fDryRun = false;
1049
1050 int vrc = VINF_SUCCESS;
1051 uint32_t uNoOptionIdx = 0;
1052 bool fUsageOK = true;
1053 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1054 && RT_SUCCESS(vrc))
1055 {
1056 /* For options that require an argument, ValueUnion has received the value. */
1057 switch (ch)
1058 {
1059 case 'd': /* Dry run */
1060 fDryRun = true;
1061 break;
1062
1063 case 'F': /* Follow symlinks */
1064 uFlags |= CopyFileFlag_FollowLinks;
1065 break;
1066
1067 case 'p': /* Password */
1068 Utf8Password = ValueUnion.psz;
1069 break;
1070
1071 case 'R': /* Recursive processing */
1072 uFlags |= CopyFileFlag_Recursive;
1073 break;
1074
1075 case 'u': /* User name */
1076 Utf8UserName = ValueUnion.psz;
1077 break;
1078
1079 case 'v': /* Verbose */
1080 fVerbose = true;
1081 break;
1082
1083 case VINF_GETOPT_NOT_OPTION:
1084 {
1085 /* Get the actual source + destination. */
1086 switch (uNoOptionIdx)
1087 {
1088 case 0:
1089 Utf8Source = ValueUnion.psz;
1090 break;
1091
1092 case 1:
1093 Utf8Dest = ValueUnion.psz;
1094 break;
1095
1096 default:
1097 break;
1098 }
1099 uNoOptionIdx++;
1100 if (uNoOptionIdx == UINT32_MAX)
1101 {
1102 RTMsgError("Too many files specified! Aborting.\n");
1103 vrc = VERR_TOO_MUCH_DATA;
1104 }
1105 break;
1106 }
1107
1108 default:
1109 return RTGetOptPrintError(ch, &ValueUnion);
1110 }
1111 }
1112
1113 if (!fUsageOK)
1114 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1115
1116 if (Utf8Source.isEmpty())
1117 return errorSyntax(USAGE_GUESTCONTROL,
1118 "No source specified!");
1119
1120 if (Utf8Dest.isEmpty())
1121 return errorSyntax(USAGE_GUESTCONTROL,
1122 "No destination specified!");
1123
1124 if (Utf8UserName.isEmpty())
1125 return errorSyntax(USAGE_GUESTCONTROL,
1126 "No user name specified!");
1127 HRESULT rc = S_OK;
1128 ComPtr<IGuest> guest;
1129 vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
1130 if (RT_SUCCESS(vrc))
1131 {
1132 if (fVerbose)
1133 {
1134 if (fDryRun)
1135 RTPrintf("Dry run - no files copied!\n");
1136 RTPrintf("Gathering file information ...\n");
1137 }
1138
1139 RTLISTNODE listToCopy;
1140 uint32_t cObjects = 0;
1141 vrc = ctrlCopyInit(Utf8Source.c_str(), Utf8Dest.c_str(), uFlags,
1142 &cObjects, &listToCopy);
1143 if (RT_FAILURE(vrc))
1144 {
1145 switch (vrc)
1146 {
1147 case VERR_NOT_FOUND:
1148 RTMsgError("No files to copy found!\n");
1149 break;
1150
1151 case VERR_FILE_NOT_FOUND:
1152 RTMsgError("Source path \"%s\" not found!\n", Utf8Source.c_str());
1153 break;
1154
1155 default:
1156 RTMsgError("Failed to initialize, rc=%Rrc\n", vrc);
1157 break;
1158 }
1159 }
1160 else
1161 {
1162 PDIRECTORYENTRY pNode;
1163 if (RT_SUCCESS(vrc))
1164 {
1165 if (fVerbose)
1166 {
1167 if (fCopyRecursive)
1168 RTPrintf("Recursively copying \"%s\" to \"%s\" (%u file(s)) ...\n",
1169 Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
1170 else
1171 RTPrintf("Copying \"%s\" to \"%s\" (%u file(s)) ...\n",
1172 Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
1173 }
1174
1175 uint32_t uCurObject = 1;
1176 RTListForEach(&listToCopy, pNode, DIRECTORYENTRY, Node)
1177 {
1178 if (!fDryRun)
1179 {
1180 if (fVerbose)
1181 RTPrintf("Copying \"%s\" to \"%s\" (%u/%u) ...\n",
1182 pNode->pszSourcePath, pNode->pszDestPath, uCurObject, cObjects);
1183 /* Finally copy the desired file (if no dry run selected). */
1184 if (!fDryRun)
1185 vrc = ctrlCopyFileToGuest(guest, fVerbose, pNode->pszSourcePath, pNode->pszDestPath,
1186 Utf8UserName.c_str(), Utf8Password.c_str(), uFlags);
1187 }
1188 if (RT_FAILURE(vrc))
1189 break;
1190 uCurObject++;
1191 }
1192 if (RT_SUCCESS(vrc) && fVerbose)
1193 RTPrintf("Copy operation successful!\n");
1194 }
1195 ctrlDirectoryListDestroy(&listToCopy);
1196 }
1197 ctrlUninitVM(a);
1198 }
1199
1200 if (RT_FAILURE(vrc))
1201 rc = VBOX_E_IPRT_ERROR;
1202 return SUCCEEDED(rc) ? 0 : 1;
1203}
1204
1205static int handleCtrlCreateDirectory(HandlerArg *a)
1206{
1207 /*
1208 * Check the syntax. We can deduce the correct syntax from the number of
1209 * arguments.
1210 */
1211 if (a->argc < 2) /* At least the directory we want to create should be present :-). */
1212 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1213
1214 static const RTGETOPTDEF s_aOptions[] =
1215 {
1216 { "--mode", 'm', RTGETOPT_REQ_UINT32 },
1217 { "--parents", 'P', RTGETOPT_REQ_NOTHING },
1218 { "--password", 'p', RTGETOPT_REQ_STRING },
1219 { "--username", 'u', RTGETOPT_REQ_STRING },
1220 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1221 };
1222
1223 int ch;
1224 RTGETOPTUNION ValueUnion;
1225 RTGETOPTSTATE GetState;
1226 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1227
1228 Utf8Str Utf8UserName;
1229 Utf8Str Utf8Password;
1230 uint32_t uFlags = CreateDirectoryFlag_None;
1231 uint32_t uMode = 0;
1232 bool fVerbose = false;
1233
1234 RTLISTNODE listDirs;
1235 uint32_t uNumDirs = 0;
1236 RTListInit(&listDirs);
1237
1238 int vrc = VINF_SUCCESS;
1239 bool fUsageOK = true;
1240 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1241 && RT_SUCCESS(vrc))
1242 {
1243 /* For options that require an argument, ValueUnion has received the value. */
1244 switch (ch)
1245 {
1246 case 'm': /* Mode */
1247 uMode = ValueUnion.u32;
1248 break;
1249
1250 case 'P': /* Create parents */
1251 uFlags |= CreateDirectoryFlag_Parents;
1252 break;
1253
1254 case 'p': /* Password */
1255 Utf8Password = ValueUnion.psz;
1256 break;
1257
1258 case 'u': /* User name */
1259 Utf8UserName = ValueUnion.psz;
1260 break;
1261
1262 case 'v': /* Verbose */
1263 fVerbose = true;
1264 break;
1265
1266 case VINF_GETOPT_NOT_OPTION:
1267 {
1268 vrc = ctrlDirectoryEntryAppend(NULL, /* No source given */
1269 ValueUnion.psz, /* Destination */
1270 &listDirs);
1271 if (RT_SUCCESS(vrc))
1272 {
1273 uNumDirs++;
1274 if (uNumDirs == UINT32_MAX)
1275 {
1276 RTMsgError("Too many directories specified! Aborting.\n");
1277 vrc = VERR_TOO_MUCH_DATA;
1278 }
1279 }
1280 break;
1281 }
1282
1283 default:
1284 return RTGetOptPrintError(ch, &ValueUnion);
1285 }
1286 }
1287
1288 if (!fUsageOK)
1289 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1290
1291 if (!uNumDirs)
1292 return errorSyntax(USAGE_GUESTCONTROL,
1293 "No directory to create specified!");
1294
1295 if (Utf8UserName.isEmpty())
1296 return errorSyntax(USAGE_GUESTCONTROL,
1297 "No user name specified!");
1298
1299 HRESULT rc = S_OK;
1300 ComPtr<IGuest> guest;
1301 vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
1302 if (RT_SUCCESS(vrc))
1303 {
1304 if (fVerbose && uNumDirs > 1)
1305 RTPrintf("Creating %ld directories ...\n", uNumDirs);
1306
1307 PDIRECTORYENTRY pNode;
1308 RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node)
1309 {
1310 if (fVerbose)
1311 RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath);
1312
1313 ComPtr<IProgress> progress;
1314 rc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(),
1315 Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
1316 uMode, uFlags, progress.asOutParam());
1317 if (FAILED(rc))
1318 {
1319 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
1320 break;
1321 }
1322 }
1323 ctrlUninitVM(a);
1324 }
1325 ctrlDirectoryListDestroy(&listDirs);
1326
1327 if (RT_FAILURE(vrc))
1328 rc = VBOX_E_IPRT_ERROR;
1329 return SUCCEEDED(rc) ? 0 : 1;
1330}
1331
1332static int handleCtrlUpdateAdditions(HandlerArg *a)
1333{
1334 /*
1335 * Check the syntax. We can deduce the correct syntax from the number of
1336 * arguments.
1337 */
1338 if (a->argc < 1) /* At least the VM name should be present :-). */
1339 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1340
1341 Utf8Str Utf8Source;
1342 bool fVerbose = false;
1343
1344/** @todo r=bird: Use RTGetOpt here, no new code using strcmp-if-switching! */
1345 /* Iterate through all possible commands (if available). */
1346 bool usageOK = true;
1347 for (int i = 1; usageOK && i < a->argc; i++)
1348 {
1349 if (!strcmp(a->argv[i], "--source"))
1350 {
1351 if (i + 1 >= a->argc)
1352 usageOK = false;
1353 else
1354 {
1355 Utf8Source = a->argv[i + 1];
1356 ++i;
1357 }
1358 }
1359 else if (!strcmp(a->argv[i], "--verbose"))
1360 fVerbose = true;
1361 else
1362 return errorSyntax(USAGE_GUESTCONTROL,
1363 "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1364 }
1365
1366 if (!usageOK)
1367 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1368
1369 HRESULT rc = S_OK;
1370 ComPtr<IGuest> guest;
1371 int vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
1372 if (RT_SUCCESS(vrc))
1373 {
1374 if (fVerbose)
1375 RTPrintf("Updating Guest Additions of machine \"%s\" ...\n", a->argv[0]);
1376
1377#ifdef DEBUG_andy
1378 if (Utf8Source.isEmpty())
1379 Utf8Source = "c:\\Downloads\\VBoxGuestAdditions-r67158.iso";
1380#endif
1381 /* Determine source if not set yet. */
1382 if (Utf8Source.isEmpty())
1383 {
1384 char strTemp[RTPATH_MAX];
1385 vrc = RTPathAppPrivateNoArch(strTemp, sizeof(strTemp));
1386 AssertRC(vrc);
1387 Utf8Str Utf8Src1 = Utf8Str(strTemp).append("/VBoxGuestAdditions.iso");
1388
1389 vrc = RTPathExecDir(strTemp, sizeof(strTemp));
1390 AssertRC(vrc);
1391 Utf8Str Utf8Src2 = Utf8Str(strTemp).append("/additions/VBoxGuestAdditions.iso");
1392
1393 /* Check the standard image locations */
1394 if (RTFileExists(Utf8Src1.c_str()))
1395 Utf8Source = Utf8Src1;
1396 else if (RTFileExists(Utf8Src2.c_str()))
1397 Utf8Source = Utf8Src2;
1398 else
1399 {
1400 RTMsgError("Source could not be determined! Please use --source to specify a valid source.\n");
1401 vrc = VERR_FILE_NOT_FOUND;
1402 }
1403 }
1404 else if (!RTFileExists(Utf8Source.c_str()))
1405 {
1406 RTMsgError("Source \"%s\" does not exist!\n", Utf8Source.c_str());
1407 vrc = VERR_FILE_NOT_FOUND;
1408 }
1409
1410 if (RT_SUCCESS(vrc))
1411 {
1412 if (fVerbose)
1413 RTPrintf("Using source: %s\n", Utf8Source.c_str());
1414
1415 ComPtr<IProgress> progress;
1416 CHECK_ERROR(guest, UpdateGuestAdditions(Bstr(Utf8Source).raw(),
1417 /* Wait for whole update process to complete. */
1418 AdditionsUpdateFlag_None,
1419 progress.asOutParam()));
1420 if (FAILED(rc))
1421 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
1422 else
1423 {
1424 rc = showProgress(progress);
1425 if (FAILED(rc))
1426 vrc = ctrlPrintProgressError(progress);
1427 else if (fVerbose)
1428 RTPrintf("Guest Additions update successful.\n");
1429 }
1430 }
1431 ctrlUninitVM(a);
1432 }
1433
1434 if (RT_FAILURE(vrc))
1435 rc = VBOX_E_IPRT_ERROR;
1436 return SUCCEEDED(rc) ? 0 : 1;
1437}
1438
1439/**
1440 * Access the guest control store.
1441 *
1442 * @returns 0 on success, 1 on failure
1443 * @note see the command line API description for parameters
1444 */
1445int handleGuestControl(HandlerArg *a)
1446{
1447 HandlerArg arg = *a;
1448 arg.argc = a->argc - 1;
1449 arg.argv = a->argv + 1;
1450
1451 if (a->argc <= 0)
1452 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1453
1454 /* switch (cmd) */
1455 if ( !RTStrICmp(a->argv[0], "exec")
1456 || !RTStrICmp(a->argv[0], "execute"))
1457 {
1458 return handleCtrlExecProgram(&arg);
1459 }
1460 else if ( !RTStrICmp(a->argv[0], "copyto")
1461 || !RTStrICmp(a->argv[0], "cp"))
1462 {
1463 return handleCtrlCopyTo(&arg);
1464 }
1465 else if ( !RTStrICmp(a->argv[0], "createdirectory")
1466 || !RTStrICmp(a->argv[0], "createdir")
1467 || !RTStrICmp(a->argv[0], "mkdir")
1468 || !RTStrICmp(a->argv[0], "md"))
1469 {
1470 return handleCtrlCreateDirectory(&arg);
1471 }
1472 else if ( !RTStrICmp(a->argv[0], "updateadditions")
1473 || !RTStrICmp(a->argv[0], "updateadds"))
1474 {
1475 return handleCtrlUpdateAdditions(&arg);
1476 }
1477
1478 /* default: */
1479 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
1480}
1481
1482#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