VirtualBox

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

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

GuestCtrl: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 69.2 KB
Line 
1/* $Id: VBoxManageGuestCtrl.cpp 38290 2011-08-03 09:22:12Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of guestcontrol command.
4 */
5
6/*
7 * Copyright (C) 2010-2011 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#include <VBox/com/VirtualBox.h>
32#include <VBox/com/EventQueue.h>
33
34#include <VBox/err.h>
35#include <VBox/log.h>
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#include <map>
47#include <vector>
48
49#ifdef USE_XPCOM_QUEUE
50# include <sys/select.h>
51# include <errno.h>
52#endif
53
54#include <signal.h>
55
56#ifdef RT_OS_DARWIN
57# include <CoreFoundation/CFRunLoop.h>
58#endif
59
60using namespace com;
61
62/**
63 * IVirtualBoxCallback implementation for handling the GuestControlCallback in
64 * relation to the "guestcontrol * wait" command.
65 */
66/** @todo */
67
68/** Set by the signal handler. */
69static volatile bool g_fGuestCtrlCanceled = false;
70
71typedef struct COPYCONTEXT
72{
73 IGuest *pGuest;
74 bool fVerbose;
75 bool fHostToGuest;
76 char *pszUsername;
77 char *pszPassword;
78} COPYCONTEXT, *PCOPYCONTEXT;
79
80/**
81 * An entry for a source element, including an optional filter.
82 */
83typedef struct SOURCEFILEENTRY
84{
85 SOURCEFILEENTRY(const char *pszSource, const char *pszFilter)
86 : mSource(pszSource),
87 mFilter(pszFilter) {}
88 SOURCEFILEENTRY(const char *pszSource)
89 : mSource(pszSource)
90 {
91 if ( !RTFileExists(pszSource)
92 && !RTDirExists(pszSource))
93 {
94 /* No file and no directory -- maybe a filter? */
95 if (NULL != strpbrk(RTPathFilename(pszSource), "*?"))
96 {
97 /* Yep, get the actual filter part. */
98 mFilter = RTPathFilename(pszSource);
99 /* Remove the filter from actual sourcec directory name. */
100 RTPathStripFilename(mSource.mutableRaw());
101 mSource.jolt();
102 }
103 }
104 }
105 Utf8Str mSource;
106 Utf8Str mFilter;
107} SOURCEFILEENTRY, *PSOURCEFILEENTRY;
108typedef std::vector<SOURCEFILEENTRY> SOURCEVEC, *PSOURCEVEC;
109
110/**
111 * An entry for an element which needs to be copied/created to/on the guest.
112 */
113typedef struct DESTFILEENTRY
114{
115 DESTFILEENTRY(Utf8Str strFileName) : mFileName(strFileName) {}
116 Utf8Str mFileName;
117} DESTFILEENTRY, *PDESTFILEENTRY;
118/*
119 * Map for holding destination entires, whereas the key is the destination
120 * directory and the mapped value is a vector holding all elements for this directoy.
121 */
122typedef std::map< Utf8Str, std::vector<DESTFILEENTRY> > DESTDIRMAP, *PDESTDIRMAP;
123typedef std::map< Utf8Str, std::vector<DESTFILEENTRY> >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER;
124
125/**
126 * Special exit codes for returning errors/information of a
127 * started guest process to the command line VBoxManage was started from.
128 * Useful for e.g. scripting.
129 *
130 * @note These are frozen as of 4.1.0.
131 */
132enum EXITCODEEXEC
133{
134 EXITCODEEXEC_SUCCESS = RTEXITCODE_SUCCESS,
135 /* Process exited normally but with an exit code <> 0. */
136 EXITCODEEXEC_CODE = 16,
137 EXITCODEEXEC_FAILED = 17,
138 EXITCODEEXEC_TERM_SIGNAL = 18,
139 EXITCODEEXEC_TERM_ABEND = 19,
140 EXITCODEEXEC_TIMEOUT = 20,
141 EXITCODEEXEC_DOWN = 21,
142 EXITCODEEXEC_CANCELED = 22
143};
144
145/**
146 * RTGetOpt-IDs for the guest execution control command line.
147 */
148enum GETOPTDEF_EXEC
149{
150 GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES = 1000,
151 GETOPTDEF_EXEC_NO_PROFILE,
152 GETOPTDEF_EXEC_OUTPUTFORMAT,
153 GETOPTDEF_EXEC_DOS2UNIX,
154 GETOPTDEF_EXEC_UNIX2DOS,
155 GETOPTDEF_EXEC_WAITFOREXIT,
156 GETOPTDEF_EXEC_WAITFORSTDOUT,
157 GETOPTDEF_EXEC_WAITFORSTDERR
158};
159
160enum GETOPTDEF_COPYFROM
161{
162 GETOPTDEF_COPYFROM_DRYRUN = 1000,
163 GETOPTDEF_COPYFROM_FOLLOW,
164 GETOPTDEF_COPYFROM_PASSWORD,
165 GETOPTDEF_COPYFROM_TARGETDIR,
166 GETOPTDEF_COPYFROM_USERNAME
167};
168
169enum GETOPTDEF_COPYTO
170{
171 GETOPTDEF_COPYTO_DRYRUN = 1000,
172 GETOPTDEF_COPYTO_FOLLOW,
173 GETOPTDEF_COPYTO_PASSWORD,
174 GETOPTDEF_COPYTO_TARGETDIR,
175 GETOPTDEF_COPYTO_USERNAME
176};
177
178enum GETOPTDEF_MKDIR
179{
180 GETOPTDEF_MKDIR_PASSWORD = 1000,
181 GETOPTDEF_MKDIR_USERNAME
182};
183
184enum GETOPTDEF_STAT
185{
186 GETOPTDEF_STAT_PASSWORD = 1000,
187 GETOPTDEF_STAT_USERNAME
188};
189
190enum OUTPUTTYPE
191{
192 OUTPUTTYPE_UNDEFINED = 0,
193 OUTPUTTYPE_DOS2UNIX = 10,
194 OUTPUTTYPE_UNIX2DOS = 20
195};
196
197#endif /* VBOX_ONLY_DOCS */
198
199void usageGuestControl(PRTSTREAM pStrm)
200{
201 RTStrmPrintf(pStrm,
202 "VBoxManage guestcontrol <vmname>|<uuid>\n"
203 " exec[ute]\n"
204 " --image <path to program>\n"
205 " --username <name> --password <password>\n"
206 " [--dos2unix]\n"
207 " [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n"
208 " [--timeout <msec>] [--unix2dos] [--verbose]\n"
209 " [--wait-exit] [--wait-stdout] [--wait-stderr]\n"
210 " [-- [<argument1>] ... [<argumentN>]]\n"
211 /** @todo Add a "--" parameter (has to be last parameter) to directly execute
212 * stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */
213 "\n"
214 " copyfrom\n"
215 " <source on guest> <destination on host>\n"
216 " --username <name> --password <password>\n"
217 " [--dryrun] [--follow] [--recursive] [--verbose]\n"
218 "\n"
219 " copyto|cp\n"
220 " <source on host> <destination on guest>\n"
221 " --username <name> --password <password>\n"
222 " [--dryrun] [--follow] [--recursive] [--verbose]\n"
223 "\n"
224 " createdir[ectory]|mkdir|md\n"
225 " <director[y|ies] to create on guest>\n"
226 " --username <name> --password <password>\n"
227 " [--parents] [--mode <mode>] [--verbose]\n"
228 "\n"
229 " stat\n"
230 " <file element(s) to check on guest>\n"
231 " --username <name> --password <password>\n"
232 " [--verbose]\n"
233 "\n"
234 " updateadditions\n"
235 " [--source <guest additions .ISO>] [--verbose]\n"
236 "\n");
237}
238
239#ifndef VBOX_ONLY_DOCS
240
241/**
242 * Signal handler that sets g_fGuestCtrlCanceled.
243 *
244 * This can be executed on any thread in the process, on Windows it may even be
245 * a thread dedicated to delivering this signal. Do not doing anything
246 * unnecessary here.
247 */
248static void guestCtrlSignalHandler(int iSignal)
249{
250 NOREF(iSignal);
251 ASMAtomicWriteBool(&g_fGuestCtrlCanceled, true);
252}
253
254/**
255 * Installs a custom signal handler to get notified
256 * whenever the user wants to intercept the program.
257 */
258static void ctrlSignalHandlerInstall()
259{
260 signal(SIGINT, guestCtrlSignalHandler);
261#ifdef SIGBREAK
262 signal(SIGBREAK, guestCtrlSignalHandler);
263#endif
264}
265
266/**
267 * Uninstalls a previously installed signal handler.
268 */
269static void ctrlSignalHandlerUninstall()
270{
271 signal(SIGINT, SIG_DFL);
272#ifdef SIGBREAK
273 signal(SIGBREAK, SIG_DFL);
274#endif
275}
276
277/**
278 * Translates a process status to a human readable
279 * string.
280 */
281static const char *ctrlExecProcessStatusToText(ExecuteProcessStatus_T enmStatus)
282{
283 switch (enmStatus)
284 {
285 case ExecuteProcessStatus_Started:
286 return "started";
287 case ExecuteProcessStatus_TerminatedNormally:
288 return "successfully terminated";
289 case ExecuteProcessStatus_TerminatedSignal:
290 return "terminated by signal";
291 case ExecuteProcessStatus_TerminatedAbnormally:
292 return "abnormally aborted";
293 case ExecuteProcessStatus_TimedOutKilled:
294 return "timed out";
295 case ExecuteProcessStatus_TimedOutAbnormally:
296 return "timed out, hanging";
297 case ExecuteProcessStatus_Down:
298 return "killed";
299 case ExecuteProcessStatus_Error:
300 return "error";
301 default:
302 break;
303 }
304 return "unknown";
305}
306
307static int ctrlExecProcessStatusToExitCode(ExecuteProcessStatus_T enmStatus, ULONG uExitCode)
308{
309 int rc = EXITCODEEXEC_SUCCESS;
310 switch (enmStatus)
311 {
312 case ExecuteProcessStatus_Started:
313 rc = EXITCODEEXEC_SUCCESS;
314 break;
315 case ExecuteProcessStatus_TerminatedNormally:
316 rc = !uExitCode ? EXITCODEEXEC_SUCCESS : EXITCODEEXEC_CODE;
317 break;
318 case ExecuteProcessStatus_TerminatedSignal:
319 rc = EXITCODEEXEC_TERM_SIGNAL;
320 break;
321 case ExecuteProcessStatus_TerminatedAbnormally:
322 rc = EXITCODEEXEC_TERM_ABEND;
323 break;
324 case ExecuteProcessStatus_TimedOutKilled:
325 rc = EXITCODEEXEC_TIMEOUT;
326 break;
327 case ExecuteProcessStatus_TimedOutAbnormally:
328 rc = EXITCODEEXEC_TIMEOUT;
329 break;
330 case ExecuteProcessStatus_Down:
331 /* Service/OS is stopping, process was killed, so
332 * not exactly an error of the started process ... */
333 rc = EXITCODEEXEC_DOWN;
334 break;
335 case ExecuteProcessStatus_Error:
336 rc = EXITCODEEXEC_FAILED;
337 break;
338 default:
339 AssertMsgFailed(("Unknown exit code (%u) from guest process returned!\n", enmStatus));
340 break;
341 }
342 return rc;
343}
344
345static int ctrlPrintError(com::ErrorInfo &errorInfo)
346{
347 if ( errorInfo.isFullAvailable()
348 || errorInfo.isBasicAvailable())
349 {
350 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
351 * because it contains more accurate info about what went wrong. */
352 if (errorInfo.getResultCode() == VBOX_E_IPRT_ERROR)
353 RTMsgError("%ls.", errorInfo.getText().raw());
354 else
355 {
356 RTMsgError("Error details:");
357 GluePrintErrorInfo(errorInfo);
358 }
359 return VERR_GENERAL_FAILURE; /** @todo */
360 }
361 AssertMsgFailedReturn(("Object has indicated no error (%Rrc)!?\n", errorInfo.getResultCode()),
362 VERR_INVALID_PARAMETER);
363}
364
365static int ctrlPrintError(IUnknown *pObj, const GUID &aIID)
366{
367 com::ErrorInfo ErrInfo(pObj, aIID);
368 return ctrlPrintError(ErrInfo);
369}
370
371static int ctrlPrintProgressError(ComPtr<IProgress> progress)
372{
373 int rc;
374 BOOL fCanceled;
375 if ( SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
376 && fCanceled)
377 {
378 rc = VERR_CANCELLED;
379 }
380 else
381 {
382 com::ProgressErrorInfo ErrInfo(progress);
383 rc = ctrlPrintError(ErrInfo);
384 }
385 return rc;
386}
387
388/**
389 * Un-initializes the VM after guest control usage.
390 */
391static void ctrlUninitVM(HandlerArg *pArg)
392{
393 AssertPtrReturnVoid(pArg);
394 if (pArg->session)
395 pArg->session->UnlockMachine();
396}
397
398/**
399 * Initializes the VM for IGuest operations.
400 *
401 * That is, checks whether it's up and running, if it can be locked (shared
402 * only) and returns a valid IGuest pointer on success.
403 *
404 * @return IPRT status code.
405 * @param pArg Our command line argument structure.
406 * @param pszNameOrId The VM's name or UUID.
407 * @param pGuest Where to return the IGuest interface pointer.
408 */
409static int ctrlInitVM(HandlerArg *pArg, const char *pszNameOrId, ComPtr<IGuest> *pGuest)
410{
411 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
412 AssertPtrReturn(pszNameOrId, VERR_INVALID_PARAMETER);
413
414 /* Lookup VM. */
415 ComPtr<IMachine> machine;
416 /* Assume it's an UUID. */
417 HRESULT rc;
418 CHECK_ERROR(pArg->virtualBox, FindMachine(Bstr(pszNameOrId).raw(),
419 machine.asOutParam()));
420 if (FAILED(rc))
421 return VERR_NOT_FOUND;
422
423 /* Machine is running? */
424 MachineState_T machineState;
425 CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), 1);
426 if (machineState != MachineState_Running)
427 {
428 RTMsgError("Machine \"%s\" is not running (currently %s)!\n",
429 pszNameOrId, machineStateToName(machineState, false));
430 return VERR_VM_INVALID_VM_STATE;
431 }
432
433 do
434 {
435 /* Open a session for the VM. */
436 CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared));
437 /* Get the associated console. */
438 ComPtr<IConsole> console;
439 CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam()));
440 /* ... and session machine. */
441 ComPtr<IMachine> sessionMachine;
442 CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
443 /* Get IGuest interface. */
444 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest->asOutParam()));
445 } while (0);
446
447 if (FAILED(rc))
448 ctrlUninitVM(pArg);
449 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
450}
451
452/* <Missing docuemntation> */
453static int handleCtrlExecProgram(ComPtr<IGuest> guest, HandlerArg *pArg)
454{
455 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
456
457 /*
458 * Parse arguments.
459 */
460 if (pArg->argc < 2) /* At least the command we want to execute in the guest should be present :-). */
461 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
462
463 static const RTGETOPTDEF s_aOptions[] =
464 {
465 { "--dos2unix", GETOPTDEF_EXEC_DOS2UNIX, RTGETOPT_REQ_NOTHING },
466 { "--environment", 'e', RTGETOPT_REQ_STRING },
467 { "--flags", 'f', RTGETOPT_REQ_STRING },
468 { "--ignore-operhaned-processes", GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES, RTGETOPT_REQ_NOTHING },
469 { "--image", 'i', RTGETOPT_REQ_STRING },
470 { "--no-profile", GETOPTDEF_EXEC_NO_PROFILE, RTGETOPT_REQ_NOTHING },
471 { "--password", 'p', RTGETOPT_REQ_STRING },
472 { "--timeout", 't', RTGETOPT_REQ_UINT32 },
473 { "--unix2dos", GETOPTDEF_EXEC_UNIX2DOS, RTGETOPT_REQ_NOTHING },
474 { "--username", 'u', RTGETOPT_REQ_STRING },
475 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
476 { "--wait-exit", GETOPTDEF_EXEC_WAITFOREXIT, RTGETOPT_REQ_NOTHING },
477 { "--wait-stdout", GETOPTDEF_EXEC_WAITFORSTDOUT, RTGETOPT_REQ_NOTHING },
478 { "--wait-stderr", GETOPTDEF_EXEC_WAITFORSTDERR, RTGETOPT_REQ_NOTHING }
479 };
480
481 int ch;
482 RTGETOPTUNION ValueUnion;
483 RTGETOPTSTATE GetState;
484 RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
485
486 Utf8Str Utf8Cmd;
487 uint32_t fExecFlags = ExecuteProcessFlag_None;
488 uint32_t fOutputFlags = ProcessOutputFlag_None;
489 com::SafeArray<IN_BSTR> args;
490 com::SafeArray<IN_BSTR> env;
491 Utf8Str Utf8UserName;
492 Utf8Str Utf8Password;
493 uint32_t cMsTimeout = 0;
494 OUTPUTTYPE eOutputType = OUTPUTTYPE_UNDEFINED;
495 bool fOutputBinary = false;
496 bool fWaitForExit = false;
497 bool fWaitForStdOut = false;
498 bool fVerbose = false;
499
500 int vrc = VINF_SUCCESS;
501 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
502 && RT_SUCCESS(vrc))
503 {
504 /* For options that require an argument, ValueUnion has received the value. */
505 switch (ch)
506 {
507 case GETOPTDEF_EXEC_DOS2UNIX:
508 if (eOutputType != OUTPUTTYPE_UNDEFINED)
509 return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!");
510 eOutputType = OUTPUTTYPE_DOS2UNIX;
511 break;
512
513 case 'e': /* Environment */
514 {
515 char **papszArg;
516 int cArgs;
517
518 vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL);
519 if (RT_FAILURE(vrc))
520 return errorSyntax(USAGE_GUESTCONTROL, "Failed to parse environment value, rc=%Rrc", vrc);
521 for (int j = 0; j < cArgs; j++)
522 env.push_back(Bstr(papszArg[j]).raw());
523
524 RTGetOptArgvFree(papszArg);
525 break;
526 }
527
528 case GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES:
529 fExecFlags |= ExecuteProcessFlag_IgnoreOrphanedProcesses;
530 break;
531
532 case GETOPTDEF_EXEC_NO_PROFILE:
533 fExecFlags |= ExecuteProcessFlag_NoProfile;
534 break;
535
536 case 'i':
537 Utf8Cmd = ValueUnion.psz;
538 break;
539
540 /** @todo Add a hidden flag. */
541
542 case 'p': /* Password */
543 Utf8Password = ValueUnion.psz;
544 break;
545
546 case 't': /* Timeout */
547 cMsTimeout = ValueUnion.u32;
548 break;
549
550 case GETOPTDEF_EXEC_UNIX2DOS:
551 if (eOutputType != OUTPUTTYPE_UNDEFINED)
552 return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!");
553 eOutputType = OUTPUTTYPE_UNIX2DOS;
554 break;
555
556 case 'u': /* User name */
557 Utf8UserName = ValueUnion.psz;
558 break;
559
560 case 'v': /* Verbose */
561 fVerbose = true;
562 break;
563
564 case GETOPTDEF_EXEC_WAITFOREXIT:
565 fWaitForExit = true;
566 break;
567
568 case GETOPTDEF_EXEC_WAITFORSTDOUT:
569 fWaitForExit = true;
570 fWaitForStdOut = true;
571 break;
572
573 case GETOPTDEF_EXEC_WAITFORSTDERR:
574 fWaitForExit = (fOutputFlags |= ProcessOutputFlag_StdErr) ? true : false;
575 break;
576
577 case VINF_GETOPT_NOT_OPTION:
578 {
579 if (args.size() == 0 && Utf8Cmd.isEmpty())
580 Utf8Cmd = ValueUnion.psz;
581 else
582 args.push_back(Bstr(ValueUnion.psz).raw());
583 break;
584 }
585
586 default:
587 return RTGetOptPrintError(ch, &ValueUnion);
588 }
589 }
590
591 if (Utf8Cmd.isEmpty())
592 return errorSyntax(USAGE_GUESTCONTROL, "No command to execute specified!");
593
594 if (Utf8UserName.isEmpty())
595 return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
596
597 /* Any output conversion not supported yet! */
598 if (eOutputType != OUTPUTTYPE_UNDEFINED)
599 return errorSyntax(USAGE_GUESTCONTROL, "Output conversion not implemented yet!");
600
601 /*
602 * <missing comment indicating that we're done parsing args and started doing something else>
603 */
604 HRESULT rc = S_OK;
605 if (fVerbose)
606 {
607 if (cMsTimeout == 0)
608 RTPrintf("Waiting for guest to start process ...\n");
609 else
610 RTPrintf("Waiting for guest to start process (within %ums)\n", cMsTimeout);
611 }
612
613 /* Get current time stamp to later calculate rest of timeout left. */
614 uint64_t u64StartMS = RTTimeMilliTS();
615
616 /* Execute the process. */
617 int rcProc = RTEXITCODE_FAILURE;
618 ComPtr<IProgress> progress;
619 ULONG uPID = 0;
620 rc = guest->ExecuteProcess(Bstr(Utf8Cmd).raw(),
621 fExecFlags,
622 ComSafeArrayAsInParam(args),
623 ComSafeArrayAsInParam(env),
624 Bstr(Utf8UserName).raw(),
625 Bstr(Utf8Password).raw(),
626 cMsTimeout,
627 &uPID,
628 progress.asOutParam());
629 if (FAILED(rc))
630 return ctrlPrintError(guest, COM_IIDOF(IGuest));
631
632 if (fVerbose)
633 RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID);
634 if (fWaitForExit)
635 {
636 if (fVerbose)
637 {
638 if (cMsTimeout) /* Wait with a certain timeout. */
639 {
640 /* Calculate timeout value left after process has been started. */
641 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
642 /* Is timeout still bigger than current difference? */
643 if (cMsTimeout > u64Elapsed)
644 RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed);
645 else
646 RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */
647 }
648 else /* Wait forever. */
649 RTPrintf("Waiting for process to exit ...\n");
650 }
651
652 /* Setup signal handling if cancelable. */
653 ASSERT(progress);
654 bool fCanceledAlready = false;
655 BOOL fCancelable;
656 HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
657 if (FAILED(hrc))
658 fCancelable = FALSE;
659 if (fCancelable)
660 ctrlSignalHandlerInstall();
661
662 /* Wait for process to exit ... */
663 BOOL fCompleted = FALSE;
664 BOOL fCanceled = FALSE;
665 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
666 {
667 SafeArray<BYTE> aOutputData;
668 ULONG cbOutputData = 0;
669
670 /*
671 * Some data left to output?
672 */
673 if (fOutputFlags || fWaitForStdOut)
674 {
675 /** @todo r=bird: The timeout argument is bogus in several
676 * ways:
677 * 1. RT_MAX will evaluate the arguments twice, which may
678 * result in different values because RTTimeMilliTS()
679 * returns a higher value the 2nd time. Worst case:
680 * Imagine when RT_MAX calculates the remaining time
681 * out (first expansion) there is say 60 ms left. Then
682 * we're preempted and rescheduled after, say, 120 ms.
683 * We call RTTimeMilliTS() again and ends up with a
684 * value -60 ms, which translate to a UINT32_MAX - 59
685 * ms timeout.
686 *
687 * 2. When the period expires, we will wait forever since
688 * both 0 and -1 mean indefinite timeout with this API,
689 * at least that's one way of reading the main code.
690 *
691 * 3. There is a signed/unsigned ambiguity in the
692 * RT_MAX expression. The left hand side is signed
693 * integer (0), the right side is unsigned 64-bit. From
694 * what I can tell, the compiler will treat this as
695 * unsigned 64-bit and never return 0.
696 */
697 rc = guest->GetProcessOutput(uPID, fOutputFlags,
698 RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
699 _64K, ComSafeArrayAsOutParam(aOutputData));
700 if (FAILED(rc))
701 {
702 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
703 cbOutputData = 0;
704 }
705 else
706 {
707 cbOutputData = aOutputData.size();
708 if (cbOutputData > 0)
709 {
710 /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a
711 * generic problem and the new VFS APIs will handle it more
712 * transparently. (requires writing dos2unix/unix2dos filters ofc) */
713
714 /*
715 * If aOutputData is text data from the guest process' stdout or stderr,
716 * it has a platform dependent line ending. So standardize on
717 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
718 * Windows. Otherwise we end up with CR/CR/LF on Windows.
719 */
720 ULONG cbOutputDataPrint = cbOutputData;
721 for (BYTE *s = aOutputData.raw(), *d = s;
722 s - aOutputData.raw() < (ssize_t)cbOutputData;
723 s++, d++)
724 {
725 if (*s == '\r')
726 {
727 /* skip over CR, adjust destination */
728 d--;
729 cbOutputDataPrint--;
730 }
731 else if (s != d)
732 *d = *s;
733 }
734 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
735 }
736 }
737 }
738
739 /* No more output data left? */
740 if (cbOutputData <= 0)
741 {
742 /* Only break out from process handling loop if we processed (displayed)
743 * all output data or if there simply never was output data and the process
744 * has been marked as complete. */
745 if (fCompleted)
746 break;
747 }
748
749 /* Process async cancelation */
750 if (g_fGuestCtrlCanceled && !fCanceledAlready)
751 {
752 hrc = progress->Cancel();
753 if (SUCCEEDED(hrc))
754 fCanceledAlready = TRUE;
755 else
756 g_fGuestCtrlCanceled = false;
757 }
758
759 /* Progress canceled by Main API? */
760 if ( SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
761 && fCanceled)
762 break;
763
764 /* Did we run out of time? */
765 if ( cMsTimeout
766 && RTTimeMilliTS() - u64StartMS > cMsTimeout)
767 {
768 progress->Cancel();
769 break;
770 }
771 } /* while */
772
773 /* Undo signal handling */
774 if (fCancelable)
775 ctrlSignalHandlerUninstall();
776
777 /* Report status back to the user. */
778 if (fCanceled)
779 {
780 if (fVerbose)
781 RTPrintf("Process execution canceled!\n");
782 rcProc = EXITCODEEXEC_CANCELED;
783 }
784 else if ( fCompleted
785 && SUCCEEDED(rc)) /* The GetProcessOutput rc. */
786 {
787 LONG iRc;
788 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
789 if (FAILED(iRc))
790 vrc = ctrlPrintProgressError(progress);
791 else
792 {
793 ExecuteProcessStatus_T retStatus;
794 ULONG uRetExitCode, uRetFlags;
795 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
796 if (SUCCEEDED(rc) && fVerbose)
797 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags);
798 rcProc = ctrlExecProcessStatusToExitCode(retStatus, uRetExitCode);
799 }
800 }
801 else
802 {
803 if (fVerbose)
804 RTPrintf("Process execution aborted!\n");
805 rcProc = EXITCODEEXEC_TERM_ABEND;
806 }
807 }
808
809 if (RT_FAILURE(vrc) || FAILED(rc))
810 return RTEXITCODE_FAILURE;
811 return rcProc;
812}
813
814static int ctrlCopyContextCreate(IGuest *pGuest, bool fVerbose, bool fHostToGuest,
815 const char *pszUsername, const char *pszPassword,
816 PCOPYCONTEXT *ppContext)
817{
818 AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
819 AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
820 AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
821
822 PCOPYCONTEXT pContext = (PCOPYCONTEXT)RTMemAlloc(sizeof(COPYCONTEXT));
823 AssertPtrReturn(pContext, VERR_NO_MEMORY);
824 pContext->pGuest = pGuest;
825 pContext->fVerbose = fVerbose;
826 pContext->fHostToGuest = fHostToGuest;
827
828 pContext->pszUsername = RTStrDup(pszUsername);
829 if (!pContext->pszUsername)
830 {
831 RTMemFree(pContext);
832 return VERR_NO_MEMORY;
833 }
834
835 pContext->pszPassword = RTStrDup(pszPassword);
836 if (!pContext->pszPassword)
837 {
838 RTStrFree(pContext->pszUsername);
839 RTMemFree(pContext);
840 return VERR_NO_MEMORY;
841 }
842
843 *ppContext = pContext;
844
845 return VINF_SUCCESS;
846}
847
848static void ctrlCopyContextFree(PCOPYCONTEXT pContext)
849{
850 if (pContext)
851 {
852 RTStrFree(pContext->pszUsername);
853 RTStrFree(pContext->pszPassword);
854 RTMemFree(pContext);
855 }
856}
857
858/*
859 * Source Dest Translated
860 * c:\foo.txt c:\from_host c:\from_host\foo.txt
861 * c:\asdf\foo c:\qwer c:\qwer\foo
862 * c:\bar\baz.txt d:\users\ d:\users\baz.txt
863 * c:\*.dll e:\baz e:\baz
864 */
865static int ctrlCopyTranslatePath(PCOPYCONTEXT pContext,
866 const char *pszSourceRoot, const char *pszSource,
867 const char *pszDest, char **ppszTranslated)
868{
869 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
870 AssertPtrReturn(pszSourceRoot, VERR_INVALID_POINTER);
871 AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
872 AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
873 AssertPtrReturn(ppszTranslated, VERR_INVALID_POINTER);
874
875 /* Source path must contain the source root! */
876 if (!RTPathStartsWith(pszSource, pszSourceRoot))
877 return VERR_INVALID_PARAMETER;
878
879 /* Construct the relative dest destination path by "subtracting" the
880 * source from the source root, e.g.
881 *
882 * source root path = "e:\foo\", source = "e:\foo\bar"
883 * dest = "d:\baz\"
884 * translated = "d:\baz\bar\"
885 */
886
887 size_t lenRoot = strlen(pszSourceRoot);
888 AssertReturn(lenRoot, VERR_INVALID_PARAMETER);
889 char *pszTranslated = RTStrDup(pszDest);
890 AssertReturn(pszTranslated, VERR_NO_MEMORY);
891 int vrc = RTStrAAppend(&pszTranslated, &pszSource[lenRoot]);
892 if (RT_FAILURE(vrc))
893 return vrc;
894
895 *ppszTranslated = pszTranslated;
896
897#ifdef DEBUG
898 if (pContext->fVerbose)
899 RTPrintf("Translating root=%s, source=%s, dest=%s -> %s\n",
900 pszSourceRoot, pszSource, pszDest, *ppszTranslated);
901#endif
902
903 return vrc;
904}
905
906static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir)
907{
908 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
909 AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
910
911 int rc = VINF_SUCCESS;
912 if (pContext->fHostToGuest) /* We want to create directories on the guest. */
913 {
914 HRESULT hrc = pContext->pGuest->DirectoryCreate(Bstr(pszDir).raw(),
915 Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
916 700, DirectoryCreateFlag_Parents);
917 if (FAILED(hrc))
918 rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
919 }
920 else /* ... or on the host. */
921 {
922 rc = RTDirCreate(pszDir, 700);
923 if (rc == VERR_ALREADY_EXISTS)
924 rc = VINF_SUCCESS;
925 }
926 return rc;
927}
928
929static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest,
930 const char *pszDir, bool *fExists)
931{
932 AssertPtrReturn(pContext, false);
933 AssertPtrReturn(pszDir, false);
934 AssertPtrReturn(fExists, false);
935
936 int rc = VINF_SUCCESS;
937 if (bGuest)
938 {
939 BOOL fDirExists = FALSE;
940 HRESULT hr = pContext->pGuest->FileExists(Bstr(pszDir).raw(),
941 Bstr(pContext->pszUsername).raw(),
942 Bstr(pContext->pszPassword).raw(), &fDirExists);
943 if (FAILED(hr))
944 rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
945 else
946 *fExists = fDirExists ? true : false;
947 }
948 else
949 *fExists = RTDirExists(pszDir);
950 return rc;
951}
952
953static int ctrlCopyDirExistsOnDest(PCOPYCONTEXT pContext, const char *pszDir,
954 bool *fExists)
955{
956 return ctrlCopyDirExists(pContext, pContext->fHostToGuest,
957 pszDir, fExists);
958}
959
960static int ctrlCopyDirExistsOnSource(PCOPYCONTEXT pContext, const char *pszDir,
961 bool *fExists)
962{
963 return ctrlCopyDirExists(pContext, !pContext->fHostToGuest,
964 pszDir, fExists);
965}
966
967static int ctrlCopyFileExists(PCOPYCONTEXT pContext, bool bOnGuest,
968 const char *pszFile, bool *fExists)
969{
970 AssertPtrReturn(pContext, false);
971 AssertPtrReturn(pszFile, false);
972 AssertPtrReturn(fExists, false);
973
974 int rc = VINF_SUCCESS;
975 if (bOnGuest)
976 {
977 BOOL fFileExists = FALSE;
978 HRESULT hr = pContext->pGuest->FileExists(Bstr(pszFile).raw(),
979 Bstr(pContext->pszUsername).raw(),
980 Bstr(pContext->pszPassword).raw(), &fFileExists);
981 if (FAILED(hr))
982 rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
983 else
984 *fExists = fFileExists ? true : false;
985 }
986 else
987 *fExists = RTFileExists(pszFile);
988 return rc;
989}
990
991static int ctrlCopyFileExistsOnDest(PCOPYCONTEXT pContext, const char *pszFile,
992 bool *fExists)
993{
994 return ctrlCopyFileExists(pContext, pContext->fHostToGuest,
995 pszFile, fExists);
996}
997
998static int ctrlCopyFileExistsOnSource(PCOPYCONTEXT pContext, const char *pszFile,
999 bool *fExists)
1000{
1001 return ctrlCopyFileExists(pContext, !pContext->fHostToGuest,
1002 pszFile, fExists);
1003}
1004
1005static int ctrlCopyFileToTarget(PCOPYCONTEXT pContext, const char *pszFileSource,
1006 const char *pszFileDest, uint32_t fFlags)
1007{
1008 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
1009 AssertPtrReturn(pszFileSource, VERR_INVALID_POINTER);
1010 AssertPtrReturn(pszFileDest, VERR_INVALID_POINTER);
1011
1012 if (pContext->fVerbose)
1013 {
1014 RTPrintf("Copying \"%s\" to \"%s\" ...\n",
1015 pszFileSource, pszFileDest);
1016 }
1017
1018 int vrc = VINF_SUCCESS;
1019 ComPtr<IProgress> progress;
1020 HRESULT hr;
1021 if (pContext->fHostToGuest)
1022 {
1023 hr = pContext->pGuest->CopyToGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
1024 Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
1025 fFlags, progress.asOutParam());
1026 }
1027 else
1028 {
1029 hr = pContext->pGuest->CopyFromGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
1030 Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
1031 fFlags, progress.asOutParam());
1032 }
1033
1034 if (FAILED(hr))
1035 vrc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
1036 else
1037 {
1038 hr = showProgress(progress);
1039 if (FAILED(hr))
1040 vrc = ctrlPrintProgressError(progress);
1041 }
1042
1043 return vrc;
1044}
1045
1046static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext,
1047 const char *pszSource, const char *pszFilter,
1048 const char *pszDest, uint32_t fFlags,
1049 const char *pszSubDir /* For recursion */)
1050{
1051 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
1052 AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
1053 /* Filter is optional. */
1054 AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
1055 /* Sub directory is optional. */
1056
1057 /*
1058 * Construct current path.
1059 */
1060 char szCurDir[RTPATH_MAX];
1061 int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
1062 if (RT_SUCCESS(rc) && pszSubDir)
1063 rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
1064
1065 /* Flag indicating whether the current directory was created on the
1066 * target or not. */
1067 bool fDirCreated = false;
1068
1069 /*
1070 * Open directory without a filter - RTDirOpenFiltered unfortunately
1071 * cannot handle sub directories so we have to do the filtering ourselves.
1072 */
1073 PRTDIR pDir = NULL;
1074 if (RT_SUCCESS(rc))
1075 {
1076 rc = RTDirOpen(&pDir, szCurDir);
1077 if (RT_FAILURE(rc))
1078 pDir = NULL;
1079 }
1080 if (RT_SUCCESS(rc))
1081 {
1082 /*
1083 * Enumerate the directory tree.
1084 */
1085 while (RT_SUCCESS(rc))
1086 {
1087 RTDIRENTRY DirEntry;
1088 rc = RTDirRead(pDir, &DirEntry, NULL);
1089 if (RT_FAILURE(rc))
1090 {
1091 if (rc == VERR_NO_MORE_FILES)
1092 rc = VINF_SUCCESS;
1093 break;
1094 }
1095 switch (DirEntry.enmType)
1096 {
1097 case RTDIRENTRYTYPE_DIRECTORY:
1098 {
1099 /* Skip "." and ".." entries. */
1100 if ( !strcmp(DirEntry.szName, ".")
1101 || !strcmp(DirEntry.szName, ".."))
1102 break;
1103
1104 if (fFlags & CopyFileFlag_Recursive)
1105 {
1106 char *pszNewSub = NULL;
1107 if (pszSubDir)
1108 RTStrAPrintf(&pszNewSub, "%s/%s", pszSubDir, DirEntry.szName);
1109 else
1110 RTStrAPrintf(&pszNewSub, "%s", DirEntry.szName);
1111
1112 if (pszNewSub)
1113 {
1114 rc = ctrlCopyDirToGuest(pContext,
1115 pszSource, pszFilter,
1116 pszDest, fFlags, pszNewSub);
1117 RTStrFree(pszNewSub);
1118 }
1119 else
1120 rc = VERR_NO_MEMORY;
1121 }
1122 break;
1123 }
1124
1125 case RTDIRENTRYTYPE_SYMLINK:
1126 if ( (fFlags & CopyFileFlag_Recursive)
1127 && (fFlags & CopyFileFlag_FollowLinks))
1128 {
1129 /* Fall through to next case is intentional. */
1130 }
1131 else
1132 break;
1133
1134 case RTDIRENTRYTYPE_FILE:
1135 {
1136 if ( !pszFilter
1137 || RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
1138 {
1139 if (!fDirCreated)
1140 {
1141 char *pszDestDir;
1142 rc = ctrlCopyTranslatePath(pContext, pszSource, szCurDir,
1143 pszDest, &pszDestDir);
1144 if (RT_SUCCESS(rc))
1145 {
1146 rc = ctrlCopyDirCreate(pContext, pszDestDir);
1147 RTStrFree(pszDestDir);
1148
1149 fDirCreated = true;
1150 }
1151 }
1152
1153 if (RT_SUCCESS(rc))
1154 {
1155 char *pszFileSource;
1156 if (RTStrAPrintf(&pszFileSource, "%s/%s",
1157 szCurDir, DirEntry.szName))
1158 {
1159 char *pszFileDest;
1160 rc = ctrlCopyTranslatePath(pContext, pszSource, pszFileSource,
1161 pszDest, &pszFileDest);
1162 if (RT_SUCCESS(rc))
1163 {
1164 rc = ctrlCopyFileToTarget(pContext, pszFileSource,
1165 pszFileDest, 0 /* Flags? */);
1166 RTStrFree(pszFileDest);
1167 }
1168 RTStrFree(pszFileSource);
1169 }
1170 }
1171 }
1172 break;
1173 }
1174
1175 default:
1176 break;
1177 }
1178 if (RT_FAILURE(rc))
1179 break;
1180 }
1181
1182 RTDirClose(pDir);
1183 }
1184 return rc;
1185}
1186
1187static int ctrlCopyDirToHost(PCOPYCONTEXT pContext,
1188 const char *pszSource, const char *pszFilter,
1189 const char *pszDest, uint32_t fFlags,
1190 const char *pszSubDir /* For recursion */)
1191{
1192 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
1193 AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
1194 /* Filter is optional. */
1195 AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
1196 /* Sub directory is optional. */
1197
1198 /*
1199 * Construct current path.
1200 */
1201 char szCurDir[RTPATH_MAX];
1202 int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
1203 if (RT_SUCCESS(rc) && pszSubDir)
1204 rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
1205
1206 if (RT_FAILURE(rc))
1207 return rc;
1208
1209 /* Flag indicating whether the current directory was created on the
1210 * target or not. */
1211 bool fDirCreated = false;
1212
1213 ULONG uDirHandle;
1214 HRESULT hr = pContext->pGuest->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(),
1215 fFlags,
1216 Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
1217 &uDirHandle);
1218 if (FAILED(hr))
1219 rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
1220 else
1221 {
1222 ComPtr <IGuestDirEntry> dirEntry;
1223 while (SUCCEEDED(hr = pContext->pGuest->DirectoryRead(uDirHandle, dirEntry.asOutParam())))
1224 {
1225 GuestDirEntryType_T enmType;
1226 dirEntry->COMGETTER(Type)(&enmType);
1227
1228 Bstr strName;
1229 dirEntry->COMGETTER(Name)(strName.asOutParam());
1230
1231 switch (enmType)
1232 {
1233 case GuestDirEntryType_Directory:
1234 {
1235 /* Skip "." and ".." entries. */
1236 if ( !strName.compare(Bstr("."))
1237 || !strName.compare(Bstr("..")))
1238 break;
1239
1240 if (fFlags & CopyFileFlag_Recursive)
1241 {
1242 Utf8Str strDir(strName);
1243 char *pszNewSub = NULL;
1244 if (pszSubDir)
1245 RTStrAPrintf(&pszNewSub, "%s/%s", pszSubDir, strDir.c_str());
1246 else
1247 RTStrAPrintf(&pszNewSub, "%s", strDir.c_str());
1248
1249 if (pszNewSub)
1250 {
1251 rc = ctrlCopyDirToHost(pContext,
1252 pszSource, pszFilter,
1253 pszDest, fFlags, pszNewSub);
1254 RTStrFree(pszNewSub);
1255 }
1256 else
1257 rc = VERR_NO_MEMORY;
1258 }
1259 break;
1260 }
1261
1262 case GuestDirEntryType_Symlink:
1263 if ( (fFlags & CopyFileFlag_Recursive)
1264 && (fFlags & CopyFileFlag_FollowLinks))
1265 {
1266 /* Fall through to next case is intentional. */
1267 }
1268 else
1269 break;
1270
1271 case GuestDirEntryType_File:
1272 {
1273 const char *pszName = Utf8Str(strName).c_str();
1274 if ( !pszFilter
1275 || RTStrSimplePatternMatch(pszFilter, pszName))
1276 {
1277 if (!fDirCreated)
1278 {
1279 char *pszDestDir;
1280 rc = ctrlCopyTranslatePath(pContext, pszSource, szCurDir,
1281 pszDest, &pszDestDir);
1282 if (RT_SUCCESS(rc))
1283 {
1284 rc = ctrlCopyDirCreate(pContext, pszDestDir);
1285 RTStrFree(pszDestDir);
1286
1287 fDirCreated = true;
1288 }
1289 }
1290
1291 if (RT_SUCCESS(rc))
1292 {
1293 Utf8Str strDir(strName);
1294 char *pszFileSource;
1295 if (RTStrAPrintf(&pszFileSource, "%s/%s",
1296 szCurDir, strDir.c_str()))
1297 {
1298 char *pszFileDest;
1299 rc = ctrlCopyTranslatePath(pContext, pszSource, pszFileSource,
1300 pszDest, &pszFileDest);
1301 if (RT_SUCCESS(rc))
1302 {
1303 rc = ctrlCopyFileToTarget(pContext, pszFileSource,
1304 pszFileDest, 0 /* Flags? */);
1305 RTStrFree(pszFileDest);
1306 }
1307 RTStrFree(pszFileSource);
1308 }
1309 }
1310 }
1311 break;
1312 }
1313
1314 default:
1315 break;
1316 }
1317 }
1318
1319 hr = pContext->pGuest->DirectoryClose(uDirHandle);
1320 if (FAILED(rc))
1321 rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
1322 }
1323
1324 return rc;
1325}
1326
1327static int ctrlCopyDirToTarget(PCOPYCONTEXT pContext,
1328 const char *pszSource, const char *pszFilter,
1329 const char *pszDest, uint32_t fFlags,
1330 const char *pszSubDir /* For recursion */)
1331{
1332 if (pContext->fHostToGuest)
1333 return ctrlCopyDirToGuest(pContext, pszSource, pszFilter,
1334 pszDest, fFlags, pszSubDir);
1335 return ctrlCopyDirToHost(pContext, pszSource, pszFilter,
1336 pszDest, fFlags, pszSubDir);
1337}
1338
1339static int ctrlCopyCreateSourceRoot(const char *pszSource, char **ppszSourceRoot)
1340{
1341 AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
1342 AssertPtrReturn(ppszSourceRoot, VERR_INVALID_POINTER);
1343
1344 char *pszNewRoot = RTStrDup(pszSource);
1345 AssertPtrReturn(pszNewRoot, VERR_NO_MEMORY);
1346
1347 size_t lenRoot = strlen(pszNewRoot);
1348 if ( lenRoot
1349 && pszNewRoot[lenRoot - 1] == '/'
1350 && pszNewRoot[lenRoot - 1] == '\\'
1351 && lenRoot > 1
1352 && pszNewRoot[lenRoot - 2] == '/'
1353 && pszNewRoot[lenRoot - 2] == '\\')
1354 {
1355 *ppszSourceRoot = pszNewRoot;
1356 if (lenRoot > 1)
1357 *ppszSourceRoot[lenRoot - 2] = '\0';
1358 *ppszSourceRoot[lenRoot - 1] = '\0';
1359 }
1360 else
1361 {
1362 /* If there's anything (like a file name or a filter),
1363 * strip it! */
1364 RTPathStripFilename(pszNewRoot);
1365 *ppszSourceRoot = pszNewRoot;
1366 }
1367
1368 return VINF_SUCCESS;
1369}
1370
1371static void ctrlCopyFreeSourceRoot(char *pszSourceRoot)
1372{
1373 RTStrFree(pszSourceRoot);
1374}
1375
1376static int handleCtrlCopyTo(ComPtr<IGuest> guest, HandlerArg *pArg,
1377 bool fHostToGuest)
1378{
1379 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
1380
1381 /** @todo r=bird: This command isn't very unix friendly in general. mkdir
1382 * is much better (partly because it is much simpler of course). The main
1383 * arguments against this is that (1) all but two options conflicts with
1384 * what 'man cp' tells me on a GNU/Linux system, (2) wildchar matching is
1385 * done windows CMD style (though not in a 100% compatible way), and (3)
1386 * that only one source is allowed - efficiently sabotaging default
1387 * wildcard expansion by a unix shell. The best solution here would be
1388 * two different variant, one windowsy (xcopy) and one unixy (gnu cp). */
1389
1390 /*
1391 * IGuest::CopyToGuest is kept as simple as possible to let the developer choose
1392 * what and how to implement the file enumeration/recursive lookup, like VBoxManage
1393 * does in here.
1394 */
1395
1396 static const RTGETOPTDEF s_aOptions[] =
1397 {
1398 { "--dryrun", GETOPTDEF_COPYTO_DRYRUN, RTGETOPT_REQ_NOTHING },
1399 { "--follow", GETOPTDEF_COPYTO_FOLLOW, RTGETOPT_REQ_NOTHING },
1400 { "--password", GETOPTDEF_COPYTO_PASSWORD, RTGETOPT_REQ_STRING },
1401 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1402 { "--target-directory", GETOPTDEF_COPYTO_TARGETDIR, RTGETOPT_REQ_STRING },
1403 { "--username", GETOPTDEF_COPYTO_USERNAME, RTGETOPT_REQ_STRING },
1404 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1405 };
1406
1407 int ch;
1408 RTGETOPTUNION ValueUnion;
1409 RTGETOPTSTATE GetState;
1410 RTGetOptInit(&GetState, pArg->argc, pArg->argv,
1411 s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1412
1413 Utf8Str Utf8Source;
1414 Utf8Str Utf8Dest;
1415 Utf8Str Utf8UserName;
1416 Utf8Str Utf8Password;
1417 uint32_t fFlags = CopyFileFlag_None;
1418 bool fVerbose = false;
1419 bool fCopyRecursive = false;
1420 bool fDryRun = false;
1421
1422 SOURCEVEC vecSources;
1423
1424 int vrc = VINF_SUCCESS;
1425 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1426 {
1427 /* For options that require an argument, ValueUnion has received the value. */
1428 switch (ch)
1429 {
1430 case GETOPTDEF_COPYTO_DRYRUN:
1431 fDryRun = true;
1432 break;
1433
1434 case GETOPTDEF_COPYTO_FOLLOW:
1435 fFlags |= CopyFileFlag_FollowLinks;
1436 break;
1437
1438 case GETOPTDEF_COPYTO_PASSWORD:
1439 Utf8Password = ValueUnion.psz;
1440 break;
1441
1442 case 'R': /* Recursive processing */
1443 fFlags |= CopyFileFlag_Recursive;
1444 break;
1445
1446 case GETOPTDEF_COPYTO_TARGETDIR:
1447 Utf8Dest = ValueUnion.psz;
1448 break;
1449
1450 case GETOPTDEF_COPYTO_USERNAME:
1451 Utf8UserName = ValueUnion.psz;
1452 break;
1453
1454 case 'v': /* Verbose */
1455 fVerbose = true;
1456 break;
1457
1458 case VINF_GETOPT_NOT_OPTION:
1459 {
1460 /* Last argument and no destination specified with
1461 * --target-directory yet? Then use the current argument
1462 * as destination. */
1463 if ( pArg->argc == GetState.iNext
1464 && Utf8Dest.isEmpty())
1465 {
1466 Utf8Dest = ValueUnion.psz;
1467 }
1468 else
1469 {
1470 /* Save the source directory. */
1471 vecSources.push_back(SOURCEFILEENTRY(ValueUnion.psz));
1472 }
1473 break;
1474 }
1475
1476 default:
1477 return RTGetOptPrintError(ch, &ValueUnion);
1478 }
1479 }
1480
1481 if (!vecSources.size())
1482 return errorSyntax(USAGE_GUESTCONTROL,
1483 "No source(s) specified!");
1484
1485 if (Utf8Dest.isEmpty())
1486 return errorSyntax(USAGE_GUESTCONTROL,
1487 "No destination specified!");
1488
1489 if (Utf8UserName.isEmpty())
1490 return errorSyntax(USAGE_GUESTCONTROL,
1491 "No user name specified!");
1492
1493 /*
1494 * Done parsing arguments, do some more preparations.
1495 */
1496 if (fVerbose)
1497 {
1498 if (fHostToGuest)
1499 RTPrintf("Copying from host to guest ...\n");
1500 else
1501 RTPrintf("Copying from guest to host ...\n");
1502 if (fDryRun)
1503 RTPrintf("Dry run - no files copied!\n");
1504 }
1505
1506 /* Create the copy context -- it contains all information
1507 * the routines need to know when handling the actual copying. */
1508 PCOPYCONTEXT pContext;
1509 vrc = ctrlCopyContextCreate(guest, fVerbose, fHostToGuest,
1510 Utf8UserName.c_str(), Utf8Password.c_str(),
1511 &pContext);
1512 if (RT_FAILURE(vrc))
1513 {
1514 RTMsgError("Unable to create copy context, rc=%Rrc\n", vrc);
1515 return RTEXITCODE_FAILURE;
1516 }
1517
1518 /* If the destination is a path, (try to) create it. */
1519 const char *pszDest = Utf8Dest.c_str();
1520 AssertPtr(pszDest);
1521 size_t lenDest = strlen(pszDest);
1522 if ( lenDest
1523 ||pszDest[lenDest - 1] == '/'
1524 || pszDest[lenDest - 1] == '\\')
1525 {
1526 vrc = ctrlCopyDirCreate(pContext, pszDest);
1527 }
1528
1529 if (RT_SUCCESS(vrc))
1530 {
1531 /*
1532 * Here starts the actual fun!
1533 * Handle all given sources one by one.
1534 */
1535 for (unsigned long s = 0; s < vecSources.size(); s++)
1536 {
1537 const char *pszSource = vecSources[s].mSource.c_str();
1538 const char *pszFilter = vecSources[s].mFilter.c_str();
1539
1540 char *pszSourceRoot;
1541 vrc = ctrlCopyCreateSourceRoot(pszSource, &pszSourceRoot);
1542 if (RT_FAILURE(vrc))
1543 {
1544 RTMsgError("Unable to create source root, rc=%Rrc\n", vrc);
1545 break;
1546 }
1547
1548 if (fVerbose)
1549 RTPrintf("Source: %s\n", pszSource);
1550
1551 /* @todo Files with filter?? */
1552 bool fExists;
1553 vrc = ctrlCopyFileExistsOnSource(pContext, pszSource, &fExists);
1554 if (RT_SUCCESS(vrc))
1555 {
1556 if (fExists)
1557 {
1558 /* Single file. */
1559 char *pszDest;
1560 vrc = ctrlCopyTranslatePath(pContext, pszSourceRoot, pszSource,
1561 Utf8Dest.c_str(), &pszDest);
1562 if (RT_SUCCESS(vrc))
1563 {
1564 vrc = ctrlCopyFileToTarget(pContext, pszSource,
1565 pszDest, fFlags);
1566 RTStrFree(pszDest);
1567 }
1568 else
1569 {
1570 RTMsgError("Unable to translate path for \"%s\", rc=%Rrc\n",
1571 pszSource, vrc);
1572 }
1573 }
1574 else
1575 {
1576 if ( (RT_SUCCESS(ctrlCopyDirExistsOnSource(pContext, pszSource, &fExists))
1577 && fExists)
1578 || ( RT_SUCCESS(ctrlCopyDirExistsOnSource(pContext, pszSourceRoot, &fExists))
1579 && fExists
1580 && pszFilter)
1581 )
1582 {
1583 /* Directory (with filter?). */
1584 vrc = ctrlCopyDirToTarget(pContext, pszSource, pszFilter,
1585 Utf8Dest.c_str(), fFlags, NULL /* Subdir */);
1586 }
1587 }
1588 }
1589
1590 ctrlCopyFreeSourceRoot(pszSourceRoot);
1591
1592 if ( RT_SUCCESS(vrc)
1593 && !fExists)
1594 {
1595 RTMsgError("Warning: Source \"%s\" does not exist, skipping!\n",
1596 pszSource);
1597 continue;
1598 }
1599
1600 if (RT_FAILURE(vrc))
1601 {
1602 RTMsgError("Error processing \"%s\", rc=%Rrc\n",
1603 pszSource, vrc);
1604 break;
1605 }
1606 }
1607 }
1608
1609 ctrlCopyContextFree(pContext);
1610
1611 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1612}
1613
1614static int handleCtrlCreateDirectory(ComPtr<IGuest> guest, HandlerArg *pArg)
1615{
1616 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
1617
1618 /*
1619 * Parse arguments.
1620 *
1621 * Note! No direct returns here, everyone must go thru the cleanup at the
1622 * end of this function.
1623 */
1624 static const RTGETOPTDEF s_aOptions[] =
1625 {
1626 { "--mode", 'm', RTGETOPT_REQ_UINT32 },
1627 { "--parents", 'P', RTGETOPT_REQ_NOTHING },
1628 { "--password", GETOPTDEF_MKDIR_PASSWORD, RTGETOPT_REQ_STRING },
1629 { "--username", GETOPTDEF_MKDIR_USERNAME, RTGETOPT_REQ_STRING },
1630 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1631 };
1632
1633 int ch;
1634 RTGETOPTUNION ValueUnion;
1635 RTGETOPTSTATE GetState;
1636 RTGetOptInit(&GetState, pArg->argc, pArg->argv,
1637 s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1638
1639 Utf8Str Utf8UserName;
1640 Utf8Str Utf8Password;
1641 uint32_t fFlags = DirectoryCreateFlag_None;
1642 uint32_t fDirMode = 0; /* Default mode. */
1643 bool fVerbose = false;
1644
1645 DESTDIRMAP mapDirs;
1646
1647 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1648 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1649 && rcExit == RTEXITCODE_SUCCESS)
1650 {
1651 /* For options that require an argument, ValueUnion has received the value. */
1652 switch (ch)
1653 {
1654 case 'm': /* Mode */
1655 fDirMode = ValueUnion.u32;
1656 break;
1657
1658 case 'P': /* Create parents */
1659 fFlags |= DirectoryCreateFlag_Parents;
1660 break;
1661
1662 case GETOPTDEF_MKDIR_PASSWORD: /* Password */
1663 Utf8Password = ValueUnion.psz;
1664 break;
1665
1666 case GETOPTDEF_MKDIR_USERNAME: /* User name */
1667 Utf8UserName = ValueUnion.psz;
1668 break;
1669
1670 case 'v': /* Verbose */
1671 fVerbose = true;
1672 break;
1673
1674 case VINF_GETOPT_NOT_OPTION:
1675 {
1676 mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
1677 break;
1678 }
1679
1680 default:
1681 rcExit = RTGetOptPrintError(ch, &ValueUnion);
1682 break;
1683 }
1684 }
1685
1686 uint32_t cDirs = mapDirs.size();
1687 if (rcExit == RTEXITCODE_SUCCESS && !cDirs)
1688 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No directory to create specified!");
1689
1690 if (rcExit == RTEXITCODE_SUCCESS && Utf8UserName.isEmpty())
1691 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
1692
1693 if (rcExit == RTEXITCODE_SUCCESS)
1694 {
1695 /*
1696 * Create the directories.
1697 */
1698 HRESULT hrc = S_OK;
1699 if (fVerbose && cDirs)
1700 RTPrintf("Creating %u directories ...\n", cDirs);
1701
1702 DESTDIRMAPITER it = mapDirs.begin();
1703 while (it != mapDirs.end())
1704 {
1705 if (fVerbose)
1706 RTPrintf("Creating directory \"%s\" ...\n", it->first.c_str());
1707
1708 hrc = guest->DirectoryCreate(Bstr(it->first).raw(),
1709 Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
1710 fDirMode, fFlags);
1711 if (FAILED(hrc))
1712 {
1713 ctrlPrintError(guest, COM_IIDOF(IGuest)); /* Return code ignored, save original rc. */
1714 break;
1715 }
1716
1717 it++;
1718 }
1719
1720 if (FAILED(hrc))
1721 rcExit = RTEXITCODE_FAILURE;
1722 }
1723
1724 return rcExit;
1725}
1726
1727static int handleCtrlStat(ComPtr<IGuest> guest, HandlerArg *pArg)
1728{
1729 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
1730
1731 static const RTGETOPTDEF s_aOptions[] =
1732 {
1733 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1734 { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
1735 { "--format", 'c', RTGETOPT_REQ_STRING },
1736 { "--password", GETOPTDEF_STAT_PASSWORD, RTGETOPT_REQ_STRING },
1737 { "--terse", 't', RTGETOPT_REQ_NOTHING },
1738 { "--username", GETOPTDEF_STAT_USERNAME, RTGETOPT_REQ_STRING },
1739 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1740 };
1741
1742 int ch;
1743 RTGETOPTUNION ValueUnion;
1744 RTGETOPTSTATE GetState;
1745 RTGetOptInit(&GetState, pArg->argc, pArg->argv,
1746 s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1747
1748 Utf8Str Utf8UserName;
1749 Utf8Str Utf8Password;
1750
1751 bool fVerbose = false;
1752 DESTDIRMAP mapObjs;
1753
1754 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1755 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1756 && rcExit == RTEXITCODE_SUCCESS)
1757 {
1758 /* For options that require an argument, ValueUnion has received the value. */
1759 switch (ch)
1760 {
1761 case GETOPTDEF_STAT_PASSWORD: /* Password */
1762 Utf8Password = ValueUnion.psz;
1763 break;
1764
1765 case GETOPTDEF_STAT_USERNAME: /* User name */
1766 Utf8UserName = ValueUnion.psz;
1767 break;
1768
1769 case 'L': /* Dereference */
1770 case 'f': /* File-system */
1771 case 'c': /* Format */
1772 case 't': /* Terse */
1773 return errorSyntax(USAGE_GUESTCONTROL, "Command \"%s\" not implemented yet!",
1774 ValueUnion.psz);
1775 break; /* Never reached. */
1776
1777 case 'v': /* Verbose */
1778 fVerbose = true;
1779 break;
1780
1781 case VINF_GETOPT_NOT_OPTION:
1782 {
1783 mapObjs[ValueUnion.psz]; /* Add element to check to map. */
1784 break;
1785 }
1786
1787 default:
1788 return RTGetOptPrintError(ch, &ValueUnion);
1789 break; /* Never reached. */
1790 }
1791 }
1792
1793 uint32_t cObjs = mapObjs.size();
1794 if (rcExit == RTEXITCODE_SUCCESS && !cObjs)
1795 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No element(s) to check specified!");
1796
1797 if (rcExit == RTEXITCODE_SUCCESS && Utf8UserName.isEmpty())
1798 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
1799
1800 if (rcExit == RTEXITCODE_SUCCESS)
1801 {
1802 /*
1803 * Create the directories.
1804 */
1805 HRESULT hrc = S_OK;
1806
1807 DESTDIRMAPITER it = mapObjs.begin();
1808 while (it != mapObjs.end())
1809 {
1810 if (fVerbose)
1811 RTPrintf("Checking for element \"%s\" ...\n", it->first.c_str());
1812
1813 BOOL fExists;
1814 hrc = guest->FileExists(Bstr(it->first).raw(),
1815 Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
1816 &fExists);
1817 if (FAILED(hrc))
1818 {
1819 ctrlPrintError(guest, COM_IIDOF(IGuest)); /* Return code ignored, save original rc. */
1820 break;
1821 }
1822 else
1823 {
1824 /** @todo: Output vbox_stat's stdout output to get more information about
1825 * what happened. */
1826
1827 /* If there's at least one element which does not exist on the guest,
1828 * drop out with exitcode 1. */
1829 if (!fExists)
1830 {
1831 RTPrintf("Cannot stat for element \"%s\": No such file or directory.\n",
1832 it->first.c_str());
1833 rcExit = RTEXITCODE_FAILURE;
1834 }
1835 }
1836
1837 it++;
1838 }
1839
1840 if (FAILED(hrc))
1841 rcExit = RTEXITCODE_FAILURE;
1842 }
1843
1844 return rcExit;
1845}
1846
1847static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg)
1848{
1849 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
1850
1851 /*
1852 * Check the syntax. We can deduce the correct syntax from the number of
1853 * arguments.
1854 */
1855 Utf8Str Utf8Source;
1856 bool fVerbose = false;
1857
1858 static const RTGETOPTDEF s_aOptions[] =
1859 {
1860 { "--source", 's', RTGETOPT_REQ_STRING },
1861 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1862 };
1863
1864 int ch;
1865 RTGETOPTUNION ValueUnion;
1866 RTGETOPTSTATE GetState;
1867 RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
1868
1869 int vrc = VINF_SUCCESS;
1870 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1871 && RT_SUCCESS(vrc))
1872 {
1873 switch (ch)
1874 {
1875 case 's':
1876 Utf8Source = ValueUnion.psz;
1877 break;
1878
1879 case 'v':
1880 fVerbose = true;
1881 break;
1882
1883 default:
1884 return RTGetOptPrintError(ch, &ValueUnion);
1885 }
1886 }
1887
1888 if (fVerbose)
1889 RTPrintf("Updating Guest Additions ...\n");
1890
1891#ifdef DEBUG_andy
1892 if (Utf8Source.isEmpty())
1893 Utf8Source = "c:\\Downloads\\VBoxGuestAdditions-r67158.iso";
1894#endif
1895
1896 /* Determine source if not set yet. */
1897 if (Utf8Source.isEmpty())
1898 {
1899 char strTemp[RTPATH_MAX];
1900 vrc = RTPathAppPrivateNoArch(strTemp, sizeof(strTemp));
1901 AssertRC(vrc);
1902 Utf8Str Utf8Src1 = Utf8Str(strTemp).append("/VBoxGuestAdditions.iso");
1903
1904 vrc = RTPathExecDir(strTemp, sizeof(strTemp));
1905 AssertRC(vrc);
1906 Utf8Str Utf8Src2 = Utf8Str(strTemp).append("/additions/VBoxGuestAdditions.iso");
1907
1908 /* Check the standard image locations */
1909 if (RTFileExists(Utf8Src1.c_str()))
1910 Utf8Source = Utf8Src1;
1911 else if (RTFileExists(Utf8Src2.c_str()))
1912 Utf8Source = Utf8Src2;
1913 else
1914 {
1915 RTMsgError("Source could not be determined! Please use --source to specify a valid source.\n");
1916 vrc = VERR_FILE_NOT_FOUND;
1917 }
1918 }
1919 else if (!RTFileExists(Utf8Source.c_str()))
1920 {
1921 RTMsgError("Source \"%s\" does not exist!\n", Utf8Source.c_str());
1922 vrc = VERR_FILE_NOT_FOUND;
1923 }
1924
1925 if (RT_SUCCESS(vrc))
1926 {
1927 if (fVerbose)
1928 RTPrintf("Using source: %s\n", Utf8Source.c_str());
1929
1930 HRESULT rc = S_OK;
1931 ComPtr<IProgress> progress;
1932 CHECK_ERROR(guest, UpdateGuestAdditions(Bstr(Utf8Source).raw(),
1933 /* Wait for whole update process to complete. */
1934 AdditionsUpdateFlag_None,
1935 progress.asOutParam()));
1936 if (FAILED(rc))
1937 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
1938 else
1939 {
1940 rc = showProgress(progress);
1941 if (FAILED(rc))
1942 vrc = ctrlPrintProgressError(progress);
1943 else if (fVerbose)
1944 RTPrintf("Guest Additions update successful.\n");
1945 }
1946 }
1947
1948 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1949}
1950
1951/**
1952 * Access the guest control store.
1953 *
1954 * @returns program exit code.
1955 * @note see the command line API description for parameters
1956 */
1957int handleGuestControl(HandlerArg *pArg)
1958{
1959 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
1960
1961 HandlerArg arg = *pArg;
1962 arg.argc = pArg->argc - 2; /* Skip VM name and sub command. */
1963 arg.argv = pArg->argv + 2; /* Same here. */
1964
1965 ComPtr<IGuest> guest;
1966 int vrc = ctrlInitVM(pArg, pArg->argv[0] /* VM Name */, &guest);
1967 if (RT_SUCCESS(vrc))
1968 {
1969 int rcExit;
1970 if ( !strcmp(pArg->argv[1], "exec")
1971 || !strcmp(pArg->argv[1], "execute"))
1972 {
1973 rcExit = handleCtrlExecProgram(guest, &arg);
1974 }
1975 else if (!strcmp(pArg->argv[1], "copyfrom"))
1976 {
1977 rcExit = handleCtrlCopyTo(guest, &arg,
1978 false /* Guest to host */);
1979 }
1980 else if ( !strcmp(pArg->argv[1], "copyto")
1981 || !strcmp(pArg->argv[1], "cp"))
1982 {
1983 rcExit = handleCtrlCopyTo(guest, &arg,
1984 true /* Host to guest */);
1985 }
1986 else if ( !strcmp(pArg->argv[1], "createdirectory")
1987 || !strcmp(pArg->argv[1], "createdir")
1988 || !strcmp(pArg->argv[1], "mkdir")
1989 || !strcmp(pArg->argv[1], "md"))
1990 {
1991 rcExit = handleCtrlCreateDirectory(guest, &arg);
1992 }
1993 else if ( !strcmp(pArg->argv[1], "stat"))
1994 {
1995 rcExit = handleCtrlStat(guest, &arg);
1996 }
1997 else if ( !strcmp(pArg->argv[1], "updateadditions")
1998 || !strcmp(pArg->argv[1], "updateadds"))
1999 {
2000 rcExit = handleCtrlUpdateAdditions(guest, &arg);
2001 }
2002 else
2003 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No sub command specified!");
2004
2005 ctrlUninitVM(pArg);
2006 return rcExit;
2007 }
2008 return RTEXITCODE_FAILURE;
2009}
2010
2011#endif /* !VBOX_ONLY_DOCS */
2012
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