VirtualBox

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

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

VBoxManage/GuestControl: Refactoring.

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