VirtualBox

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

Last change on this file since 35550 was 35541, checked in by vboxsync, 14 years ago

VBoxManage/GuestCtrl: Timeout adjustments.

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