VirtualBox

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

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

Build fix.

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