VirtualBox

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

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

Forgot header.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette