VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp@ 92710

Last change on this file since 92710 was 92372, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added translation marks around messages of VBoxManage output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 91.1 KB
Line 
1/* $Id: VBoxManageMisc.cpp 92372 2021-11-11 14:45:18Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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#ifndef VBOX_ONLY_DOCS
23# include <VBox/com/com.h>
24# include <VBox/com/string.h>
25# include <VBox/com/Guid.h>
26# include <VBox/com/array.h>
27# include <VBox/com/ErrorInfo.h>
28# include <VBox/com/errorprint.h>
29# include <VBox/com/VirtualBox.h>
30# include <VBox/com/NativeEventQueue.h>
31#endif /* !VBOX_ONLY_DOCS */
32
33#include <iprt/asm.h>
34#include <iprt/buildconfig.h>
35#include <iprt/cidr.h>
36#include <iprt/ctype.h>
37#include <iprt/dir.h>
38#include <iprt/env.h>
39#include <iprt/file.h>
40#include <iprt/sha.h>
41#include <iprt/initterm.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/cpp/path.h>
45#include <iprt/stream.h>
46#include <iprt/string.h>
47#include <iprt/stdarg.h>
48#include <iprt/thread.h>
49#include <iprt/uuid.h>
50#include <iprt/getopt.h>
51#include <iprt/ctype.h>
52#include <VBox/version.h>
53#include <VBox/log.h>
54
55#include "VBoxManage.h"
56
57#include <list>
58
59using namespace com;
60
61DECLARE_TRANSLATION_CONTEXT(Misc);
62
63RTEXITCODE handleRegisterVM(HandlerArg *a)
64{
65 HRESULT rc;
66
67 if (a->argc != 1)
68 return errorSyntax(USAGE_REGISTERVM, Misc::tr("Incorrect number of parameters"));
69
70 ComPtr<IMachine> machine;
71 /** @todo Ugly hack to get both the API interpretation of relative paths
72 * and the client's interpretation of relative paths. Remove after the API
73 * has been redesigned. */
74 rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
75 machine.asOutParam());
76 if (rc == VBOX_E_FILE_ERROR)
77 {
78 char szVMFileAbs[RTPATH_MAX] = "";
79 int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
80 if (RT_FAILURE(vrc))
81 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Cannot convert filename \"%s\" to absolute path: %Rrc"),
82 a->argv[0], vrc);
83 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
84 machine.asOutParam()));
85 }
86 else if (FAILED(rc))
87 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
88 machine.asOutParam()));
89 if (SUCCEEDED(rc))
90 {
91 ASSERT(machine);
92 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
93 }
94 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
95}
96
97static const RTGETOPTDEF g_aUnregisterVMOptions[] =
98{
99 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
100 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
101};
102
103RTEXITCODE handleUnregisterVM(HandlerArg *a)
104{
105 HRESULT rc;
106 const char *VMName = NULL;
107 bool fDelete = false;
108
109 int c;
110 RTGETOPTUNION ValueUnion;
111 RTGETOPTSTATE GetState;
112 // start at 0 because main() has hacked both the argc and argv given to us
113 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
114 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
115 while ((c = RTGetOpt(&GetState, &ValueUnion)))
116 {
117 switch (c)
118 {
119 case 'd': // --delete
120 fDelete = true;
121 break;
122
123 case VINF_GETOPT_NOT_OPTION:
124 if (!VMName)
125 VMName = ValueUnion.psz;
126 else
127 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
128 break;
129
130 default:
131 if (c > 0)
132 {
133 if (RT_C_IS_PRINT(c))
134 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("Invalid option -%c"), c);
135 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("Invalid option case %i"), c);
136 }
137 if (c == VERR_GETOPT_UNKNOWN_OPTION)
138 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("unknown option: %s\n"), ValueUnion.psz);
139 if (ValueUnion.pDef)
140 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
141 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("error: %Rrs"), c);
142 }
143 }
144
145 /* check for required options */
146 if (!VMName)
147 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("VM name required"));
148
149 ComPtr<IMachine> machine;
150 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
151 machine.asOutParam()),
152 RTEXITCODE_FAILURE);
153 SafeIfaceArray<IMedium> aMedia;
154 CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
155 ComSafeArrayAsOutParam(aMedia)),
156 RTEXITCODE_FAILURE);
157 if (fDelete)
158 {
159 ComPtr<IProgress> pProgress;
160 CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()),
161 RTEXITCODE_FAILURE);
162
163 rc = showProgress(pProgress);
164 CHECK_PROGRESS_ERROR_RET(pProgress, (Misc::tr("Machine delete failed")), RTEXITCODE_FAILURE);
165 }
166 else
167 {
168 /* Note that the IMachine::Unregister method will return the medium
169 * reference in a sane order, which means that closing will normally
170 * succeed, unless there is still another machine which uses the
171 * medium. No harm done if we ignore the error. */
172 for (size_t i = 0; i < aMedia.size(); i++)
173 {
174 IMedium *pMedium = aMedia[i];
175 if (pMedium)
176 rc = pMedium->Close();
177 }
178 rc = S_OK;
179 }
180 return RTEXITCODE_SUCCESS;
181}
182
183static const RTGETOPTDEF g_aCreateVMOptions[] =
184{
185 { "--name", 'n', RTGETOPT_REQ_STRING },
186 { "-name", 'n', RTGETOPT_REQ_STRING },
187 { "--groups", 'g', RTGETOPT_REQ_STRING },
188 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
189 { "-basefolder", 'p', RTGETOPT_REQ_STRING },
190 { "--ostype", 'o', RTGETOPT_REQ_STRING },
191 { "-ostype", 'o', RTGETOPT_REQ_STRING },
192 { "--uuid", 'u', RTGETOPT_REQ_UUID },
193 { "-uuid", 'u', RTGETOPT_REQ_UUID },
194 { "--register", 'r', RTGETOPT_REQ_NOTHING },
195 { "-register", 'r', RTGETOPT_REQ_NOTHING },
196 { "--default", 'd', RTGETOPT_REQ_NOTHING },
197 { "-default", 'd', RTGETOPT_REQ_NOTHING },
198};
199
200RTEXITCODE handleCreateVM(HandlerArg *a)
201{
202 HRESULT rc;
203 Bstr bstrBaseFolder;
204 Bstr bstrName;
205 Bstr bstrOsTypeId;
206 Bstr bstrUuid;
207 bool fRegister = false;
208 bool fDefault = false;
209 /* TBD. Now not used */
210 Bstr bstrDefaultFlags;
211 com::SafeArray<BSTR> groups;
212
213 int c;
214 RTGETOPTUNION ValueUnion;
215 RTGETOPTSTATE GetState;
216 // start at 0 because main() has hacked both the argc and argv given to us
217 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
218 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
219 while ((c = RTGetOpt(&GetState, &ValueUnion)))
220 {
221 switch (c)
222 {
223 case 'n': // --name
224 bstrName = ValueUnion.psz;
225 break;
226
227 case 'g': // --groups
228 parseGroups(ValueUnion.psz, &groups);
229 break;
230
231 case 'p': // --basefolder
232 bstrBaseFolder = ValueUnion.psz;
233 break;
234
235 case 'o': // --ostype
236 bstrOsTypeId = ValueUnion.psz;
237 break;
238
239 case 'u': // --uuid
240 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
241 break;
242
243 case 'r': // --register
244 fRegister = true;
245 break;
246
247 case 'd': // --default
248 fDefault = true;
249 break;
250
251 default:
252 return errorGetOpt(USAGE_CREATEVM, c, &ValueUnion);
253 }
254 }
255
256 /* check for required options */
257 if (bstrName.isEmpty())
258 return errorSyntax(USAGE_CREATEVM, Misc::tr("Parameter --name is required"));
259
260 do
261 {
262 Bstr createFlags;
263 if (!bstrUuid.isEmpty())
264 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
265 Bstr bstrPrimaryGroup;
266 if (groups.size())
267 bstrPrimaryGroup = groups[0];
268 Bstr bstrSettingsFile;
269 CHECK_ERROR_BREAK(a->virtualBox,
270 ComposeMachineFilename(bstrName.raw(),
271 bstrPrimaryGroup.raw(),
272 createFlags.raw(),
273 bstrBaseFolder.raw(),
274 bstrSettingsFile.asOutParam()));
275 ComPtr<IMachine> machine;
276 CHECK_ERROR_BREAK(a->virtualBox,
277 CreateMachine(bstrSettingsFile.raw(),
278 bstrName.raw(),
279 ComSafeArrayAsInParam(groups),
280 bstrOsTypeId.raw(),
281 createFlags.raw(),
282 machine.asOutParam()));
283
284 CHECK_ERROR_BREAK(machine, SaveSettings());
285 if (fDefault)
286 {
287 /* ApplyDefaults assumes the machine is already registered */
288 CHECK_ERROR_BREAK(machine, ApplyDefaults(bstrDefaultFlags.raw()));
289 CHECK_ERROR_BREAK(machine, SaveSettings());
290 }
291 if (fRegister)
292 {
293 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
294 }
295
296 Bstr uuid;
297 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
298 Bstr settingsFile;
299 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
300 RTPrintf(Misc::tr("Virtual machine '%ls' is created%s.\n"
301 "UUID: %s\n"
302 "Settings file: '%ls'\n"),
303 bstrName.raw(), fRegister ? Misc::tr(" and registered") : "",
304 Utf8Str(uuid).c_str(), settingsFile.raw());
305 }
306 while (0);
307
308 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
309}
310
311static const RTGETOPTDEF g_aMoveVMOptions[] =
312{
313 { "--type", 't', RTGETOPT_REQ_STRING },
314 { "--folder", 'f', RTGETOPT_REQ_STRING },
315};
316
317RTEXITCODE handleMoveVM(HandlerArg *a)
318{
319 HRESULT rc;
320 const char *pszSrcName = NULL;
321 const char *pszType = NULL;
322 char szTargetFolder[RTPATH_MAX];
323
324 int c;
325 int vrc = VINF_SUCCESS;
326 RTGETOPTUNION ValueUnion;
327 RTGETOPTSTATE GetState;
328
329 // start at 0 because main() has hacked both the argc and argv given to us
330 RTGetOptInit(&GetState, a->argc, a->argv, g_aMoveVMOptions, RT_ELEMENTS(g_aMoveVMOptions),
331 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
332 while ((c = RTGetOpt(&GetState, &ValueUnion)))
333 {
334 switch (c)
335 {
336 case 't': // --type
337 pszType = ValueUnion.psz;
338 break;
339
340 case 'f': // --target folder
341 if (ValueUnion.psz && ValueUnion.psz[0] != '\0')
342 {
343 vrc = RTPathAbs(ValueUnion.psz, szTargetFolder, sizeof(szTargetFolder));
344 if (RT_FAILURE(vrc))
345 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with rc=%Rrc"),
346 ValueUnion.psz, vrc);
347 } else {
348 szTargetFolder[0] = '\0';
349 }
350 break;
351
352 case VINF_GETOPT_NOT_OPTION:
353 if (!pszSrcName)
354 pszSrcName = ValueUnion.psz;
355 else
356 return errorSyntax(USAGE_MOVEVM, Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
357 break;
358
359 default:
360 return errorGetOpt(USAGE_MOVEVM, c, &ValueUnion);
361 }
362 }
363
364
365 if (!pszType)
366 {
367 pszType = "basic";
368 }
369
370 /* Check for required options */
371 if (!pszSrcName)
372 return errorSyntax(USAGE_MOVEVM, Misc::tr("VM name required"));
373
374 /* Get the machine object */
375 ComPtr<IMachine> srcMachine;
376 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
377 srcMachine.asOutParam()),
378 RTEXITCODE_FAILURE);
379
380 if (srcMachine)
381 {
382 /* Start the moving */
383 ComPtr<IProgress> progress;
384
385 /* we have to open a session for this task */
386 CHECK_ERROR_RET(srcMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
387 ComPtr<IMachine> sessionMachine;
388
389 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
390 CHECK_ERROR_RET(sessionMachine,
391 MoveTo(Bstr(szTargetFolder).raw(),
392 Bstr(pszType).raw(),
393 progress.asOutParam()),
394 RTEXITCODE_FAILURE);
395 rc = showProgress(progress);
396 CHECK_PROGRESS_ERROR_RET(progress, (Misc::tr("Move VM failed")), RTEXITCODE_FAILURE);
397
398 sessionMachine.setNull();
399 CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE);
400
401 RTPrintf(Misc::tr("Machine has been successfully moved into %s\n"),
402 szTargetFolder[0] != '\0' ? szTargetFolder : Misc::tr("the same location"));
403 }
404
405 return RTEXITCODE_SUCCESS;
406}
407
408static const RTGETOPTDEF g_aCloneVMOptions[] =
409{
410 { "--snapshot", 's', RTGETOPT_REQ_STRING },
411 { "--name", 'n', RTGETOPT_REQ_STRING },
412 { "--groups", 'g', RTGETOPT_REQ_STRING },
413 { "--mode", 'm', RTGETOPT_REQ_STRING },
414 { "--options", 'o', RTGETOPT_REQ_STRING },
415 { "--register", 'r', RTGETOPT_REQ_NOTHING },
416 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
417 { "--uuid", 'u', RTGETOPT_REQ_UUID },
418};
419
420static int parseCloneMode(const char *psz, CloneMode_T *pMode)
421{
422 if (!RTStrICmp(psz, "machine"))
423 *pMode = CloneMode_MachineState;
424 else if (!RTStrICmp(psz, "machineandchildren"))
425 *pMode = CloneMode_MachineAndChildStates;
426 else if (!RTStrICmp(psz, "all"))
427 *pMode = CloneMode_AllStates;
428 else
429 return VERR_PARSE_ERROR;
430
431 return VINF_SUCCESS;
432}
433
434static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
435{
436 int rc = VINF_SUCCESS;
437 while (psz && *psz && RT_SUCCESS(rc))
438 {
439 size_t len;
440 const char *pszComma = strchr(psz, ',');
441 if (pszComma)
442 len = pszComma - psz;
443 else
444 len = strlen(psz);
445 if (len > 0)
446 {
447 if (!RTStrNICmp(psz, "KeepAllMACs", len))
448 options->push_back(CloneOptions_KeepAllMACs);
449 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
450 options->push_back(CloneOptions_KeepNATMACs);
451 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
452 options->push_back(CloneOptions_KeepDiskNames);
453 else if ( !RTStrNICmp(psz, "Link", len)
454 || !RTStrNICmp(psz, "Linked", len))
455 options->push_back(CloneOptions_Link);
456 else if ( !RTStrNICmp(psz, "KeepHwUUIDs", len)
457 || !RTStrNICmp(psz, "KeepHwUUID", len))
458 options->push_back(CloneOptions_KeepHwUUIDs);
459 else
460 rc = VERR_PARSE_ERROR;
461 }
462 if (pszComma)
463 psz += len + 1;
464 else
465 psz += len;
466 }
467
468 return rc;
469}
470
471RTEXITCODE handleCloneVM(HandlerArg *a)
472{
473 HRESULT rc;
474 const char *pszSrcName = NULL;
475 const char *pszSnapshotName = NULL;
476 CloneMode_T mode = CloneMode_MachineState;
477 com::SafeArray<CloneOptions_T> options;
478 const char *pszTrgName = NULL;
479 const char *pszTrgBaseFolder = NULL;
480 bool fRegister = false;
481 Bstr bstrUuid;
482 com::SafeArray<BSTR> groups;
483
484 int c;
485 RTGETOPTUNION ValueUnion;
486 RTGETOPTSTATE GetState;
487 // start at 0 because main() has hacked both the argc and argv given to us
488 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
489 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
490 while ((c = RTGetOpt(&GetState, &ValueUnion)))
491 {
492 switch (c)
493 {
494 case 's': // --snapshot
495 pszSnapshotName = ValueUnion.psz;
496 break;
497
498 case 'n': // --name
499 pszTrgName = ValueUnion.psz;
500 break;
501
502 case 'g': // --groups
503 parseGroups(ValueUnion.psz, &groups);
504 break;
505
506 case 'p': // --basefolder
507 pszTrgBaseFolder = ValueUnion.psz;
508 break;
509
510 case 'm': // --mode
511 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
512 return errorArgument(Misc::tr("Invalid clone mode '%s'\n"), ValueUnion.psz);
513 break;
514
515 case 'o': // --options
516 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
517 return errorArgument(Misc::tr("Invalid clone options '%s'\n"), ValueUnion.psz);
518 break;
519
520 case 'u': // --uuid
521 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
522 break;
523
524 case 'r': // --register
525 fRegister = true;
526 break;
527
528 case VINF_GETOPT_NOT_OPTION:
529 if (!pszSrcName)
530 pszSrcName = ValueUnion.psz;
531 else
532 return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
533 break;
534
535 default:
536 return errorGetOpt(c, &ValueUnion);
537 }
538 }
539
540 /* Check for required options */
541 if (!pszSrcName)
542 return errorSyntax(Misc::tr("VM name required"));
543
544 /* Get the machine object */
545 ComPtr<IMachine> srcMachine;
546 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
547 srcMachine.asOutParam()),
548 RTEXITCODE_FAILURE);
549
550 /* If a snapshot name/uuid was given, get the particular machine of this
551 * snapshot. */
552 if (pszSnapshotName)
553 {
554 ComPtr<ISnapshot> srcSnapshot;
555 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
556 srcSnapshot.asOutParam()),
557 RTEXITCODE_FAILURE);
558 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
559 RTEXITCODE_FAILURE);
560 }
561
562 /* Default name necessary? */
563 if (!pszTrgName)
564 pszTrgName = RTStrAPrintf2(Misc::tr("%s Clone"), pszSrcName);
565
566 Bstr createFlags;
567 if (!bstrUuid.isEmpty())
568 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
569 Bstr bstrPrimaryGroup;
570 if (groups.size())
571 bstrPrimaryGroup = groups[0];
572 Bstr bstrSettingsFile;
573 CHECK_ERROR_RET(a->virtualBox,
574 ComposeMachineFilename(Bstr(pszTrgName).raw(),
575 bstrPrimaryGroup.raw(),
576 createFlags.raw(),
577 Bstr(pszTrgBaseFolder).raw(),
578 bstrSettingsFile.asOutParam()),
579 RTEXITCODE_FAILURE);
580
581 ComPtr<IMachine> trgMachine;
582 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
583 Bstr(pszTrgName).raw(),
584 ComSafeArrayAsInParam(groups),
585 NULL,
586 createFlags.raw(),
587 trgMachine.asOutParam()),
588 RTEXITCODE_FAILURE);
589
590 /* Start the cloning */
591 ComPtr<IProgress> progress;
592 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
593 mode,
594 ComSafeArrayAsInParam(options),
595 progress.asOutParam()),
596 RTEXITCODE_FAILURE);
597 rc = showProgress(progress);
598 CHECK_PROGRESS_ERROR_RET(progress, (Misc::tr("Clone VM failed")), RTEXITCODE_FAILURE);
599
600 if (fRegister)
601 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
602
603 Bstr bstrNewName;
604 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
605 RTPrintf(Misc::tr("Machine has been successfully cloned as \"%ls\"\n"), bstrNewName.raw());
606
607 return RTEXITCODE_SUCCESS;
608}
609
610RTEXITCODE handleStartVM(HandlerArg *a)
611{
612 HRESULT rc = S_OK;
613 std::list<const char *> VMs;
614 Bstr sessionType;
615 com::SafeArray<IN_BSTR> aBstrEnv;
616
617#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
618 /* make sure the VM process will by default start on the same display as VBoxManage */
619 {
620 const char *pszDisplay = RTEnvGet("DISPLAY");
621 if (pszDisplay)
622 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
623 const char *pszXAuth = RTEnvGet("XAUTHORITY");
624 if (pszXAuth)
625 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
626 }
627#endif
628
629 static const RTGETOPTDEF s_aStartVMOptions[] =
630 {
631 { "--type", 't', RTGETOPT_REQ_STRING },
632 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
633 { "--putenv", 'E', RTGETOPT_REQ_STRING },
634 };
635 int c;
636 RTGETOPTUNION ValueUnion;
637 RTGETOPTSTATE GetState;
638 // start at 0 because main() has hacked both the argc and argv given to us
639 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
640 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
641 while ((c = RTGetOpt(&GetState, &ValueUnion)))
642 {
643 switch (c)
644 {
645 case 't': // --type
646 if (!RTStrICmp(ValueUnion.psz, "gui"))
647 {
648 sessionType = "gui";
649 }
650#ifdef VBOX_WITH_VBOXSDL
651 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
652 {
653 sessionType = "sdl";
654 }
655#endif
656#ifdef VBOX_WITH_HEADLESS
657 else if (!RTStrICmp(ValueUnion.psz, "capture"))
658 {
659 sessionType = "capture";
660 }
661 else if (!RTStrICmp(ValueUnion.psz, "headless"))
662 {
663 sessionType = "headless";
664 }
665#endif
666 else
667 sessionType = ValueUnion.psz;
668 break;
669
670 case 'E': // --putenv
671 if (!RTStrStr(ValueUnion.psz, "\n"))
672 aBstrEnv.push_back(Bstr(ValueUnion.psz).raw());
673 else
674 return errorSyntax(USAGE_STARTVM,
675 Misc::tr("Parameter to option --putenv must not contain any newline character"));
676 break;
677
678 case VINF_GETOPT_NOT_OPTION:
679 VMs.push_back(ValueUnion.psz);
680 break;
681
682 default:
683 if (c > 0)
684 {
685 if (RT_C_IS_PRINT(c))
686 return errorSyntax(USAGE_STARTVM, Misc::tr("Invalid option -%c"), c);
687 else
688 return errorSyntax(USAGE_STARTVM, Misc::tr("Invalid option case %i"), c);
689 }
690 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
691 return errorSyntax(USAGE_STARTVM, Misc::tr("unknown option: %s\n"), ValueUnion.psz);
692 else if (ValueUnion.pDef)
693 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
694 else
695 return errorSyntax(USAGE_STARTVM, Misc::tr("error: %Rrs"), c);
696 }
697 }
698
699 /* check for required options */
700 if (VMs.empty())
701 return errorSyntax(USAGE_STARTVM, Misc::tr("at least one VM name or uuid required"));
702
703 for (std::list<const char *>::const_iterator it = VMs.begin();
704 it != VMs.end();
705 ++it)
706 {
707 HRESULT rc2 = rc;
708 const char *pszVM = *it;
709 ComPtr<IMachine> machine;
710 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
711 machine.asOutParam()));
712 if (machine)
713 {
714 ComPtr<IProgress> progress;
715 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
716 ComSafeArrayAsInParam(aBstrEnv), progress.asOutParam()));
717 if (SUCCEEDED(rc) && !progress.isNull())
718 {
719 RTPrintf(Misc::tr("Waiting for VM \"%s\" to power on...\n"), pszVM);
720 CHECK_ERROR(progress, WaitForCompletion(-1));
721 if (SUCCEEDED(rc))
722 {
723 BOOL completed = true;
724 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
725 if (SUCCEEDED(rc))
726 {
727 ASSERT(completed);
728
729 LONG iRc;
730 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
731 if (SUCCEEDED(rc))
732 {
733 if (SUCCEEDED(iRc))
734 RTPrintf(Misc::tr("VM \"%s\" has been successfully started.\n"), pszVM);
735 else
736 {
737 ProgressErrorInfo info(progress);
738 com::GluePrintErrorInfo(info);
739 }
740 rc = iRc;
741 }
742 }
743 }
744 }
745 }
746
747 /* it's important to always close sessions */
748 a->session->UnlockMachine();
749
750 /* make sure that we remember the failed state */
751 if (FAILED(rc2))
752 rc = rc2;
753 }
754
755 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
756}
757
758RTEXITCODE handleDiscardState(HandlerArg *a)
759{
760 HRESULT rc;
761
762 if (a->argc != 1)
763 return errorSyntax(USAGE_DISCARDSTATE, Misc::tr("Incorrect number of parameters"));
764
765 ComPtr<IMachine> machine;
766 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
767 machine.asOutParam()));
768 if (machine)
769 {
770 do
771 {
772 /* we have to open a session for this task */
773 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
774 do
775 {
776 ComPtr<IMachine> sessionMachine;
777 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
778 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
779 } while (0);
780 CHECK_ERROR_BREAK(a->session, UnlockMachine());
781 } while (0);
782 }
783
784 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
785}
786
787RTEXITCODE handleAdoptState(HandlerArg *a)
788{
789 HRESULT rc;
790
791 if (a->argc != 2)
792 return errorSyntax(USAGE_ADOPTSTATE, Misc::tr("Incorrect number of parameters"));
793
794 ComPtr<IMachine> machine;
795 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
796 machine.asOutParam()));
797 if (machine)
798 {
799 char szStateFileAbs[RTPATH_MAX] = "";
800 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
801 if (RT_FAILURE(vrc))
802 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Cannot convert filename \"%s\" to absolute path: %Rrc"),
803 a->argv[0], vrc);
804
805 do
806 {
807 /* we have to open a session for this task */
808 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
809 do
810 {
811 ComPtr<IMachine> sessionMachine;
812 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
813 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
814 } while (0);
815 CHECK_ERROR_BREAK(a->session, UnlockMachine());
816 } while (0);
817 }
818
819 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
820}
821
822RTEXITCODE handleGetExtraData(HandlerArg *a)
823{
824 HRESULT rc = S_OK;
825
826 if (a->argc > 2 || a->argc < 1)
827 return errorSyntax(USAGE_GETEXTRADATA, Misc::tr("Incorrect number of parameters"));
828
829 /* global data? */
830 if (!strcmp(a->argv[0], "global"))
831 {
832 /* enumeration? */
833 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
834 {
835 SafeArray<BSTR> aKeys;
836 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
837
838 for (size_t i = 0;
839 i < aKeys.size();
840 ++i)
841 {
842 Bstr bstrKey(aKeys[i]);
843 Bstr bstrValue;
844 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
845 bstrValue.asOutParam()));
846
847 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
848 }
849 }
850 else
851 {
852 Bstr value;
853 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
854 value.asOutParam()));
855 if (!value.isEmpty())
856 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
857 else
858 RTPrintf(Misc::tr("No value set!\n"));
859 }
860 }
861 else
862 {
863 ComPtr<IMachine> machine;
864 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
865 machine.asOutParam()));
866 if (machine)
867 {
868 /* enumeration? */
869 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
870 {
871 SafeArray<BSTR> aKeys;
872 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
873
874 for (size_t i = 0;
875 i < aKeys.size();
876 ++i)
877 {
878 Bstr bstrKey(aKeys[i]);
879 Bstr bstrValue;
880 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
881 bstrValue.asOutParam()));
882
883 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
884 }
885 }
886 else
887 {
888 Bstr value;
889 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
890 value.asOutParam()));
891 if (!value.isEmpty())
892 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
893 else
894 RTPrintf(Misc::tr("No value set!\n"));
895 }
896 }
897 }
898 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
899}
900
901RTEXITCODE handleSetExtraData(HandlerArg *a)
902{
903 HRESULT rc = S_OK;
904
905 if (a->argc < 2)
906 return errorSyntax(USAGE_SETEXTRADATA, Misc::tr("Not enough parameters"));
907
908 /* global data? */
909 if (!strcmp(a->argv[0], "global"))
910 {
911 /** @todo passing NULL is deprecated */
912 if (a->argc < 3)
913 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
914 NULL));
915 else if (a->argc == 3)
916 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
917 Bstr(a->argv[2]).raw()));
918 else
919 return errorSyntax(USAGE_SETEXTRADATA, Misc::tr("Too many parameters"));
920 }
921 else
922 {
923 ComPtr<IMachine> machine;
924 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
925 machine.asOutParam()));
926 if (machine)
927 {
928 /* open an existing session for the VM */
929 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
930 /* get the session machine */
931 ComPtr<IMachine> sessionMachine;
932 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
933 /** @todo passing NULL is deprecated */
934 if (a->argc < 3)
935 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
936 NULL));
937 else if (a->argc == 3)
938 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
939 Bstr(a->argv[2]).raw()));
940 else
941 return errorSyntax(USAGE_SETEXTRADATA, Misc::tr("Too many parameters"));
942 }
943 }
944 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
945}
946
947RTEXITCODE handleSetProperty(HandlerArg *a)
948{
949 HRESULT rc;
950
951 /* there must be two arguments: property name and value */
952 if (a->argc != 2)
953 return errorSyntax(USAGE_SETPROPERTY, Misc::tr("Incorrect number of parameters"));
954
955 ComPtr<ISystemProperties> systemProperties;
956 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
957
958 if (!strcmp(a->argv[0], "machinefolder"))
959 {
960 /* reset to default? */
961 if (!strcmp(a->argv[1], "default"))
962 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
963 else
964 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
965 }
966 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
967 {
968 bool fHwVirtExclusive;
969
970 if (!strcmp(a->argv[1], "on"))
971 fHwVirtExclusive = true;
972 else if (!strcmp(a->argv[1], "off"))
973 fHwVirtExclusive = false;
974 else
975 return errorArgument(Misc::tr("Invalid hwvirtexclusive argument '%s'"), a->argv[1]);
976 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
977 }
978 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
979 || !strcmp(a->argv[0], "vrdpauthlibrary"))
980 {
981 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
982 RTStrmPrintf(g_pStdErr, Misc::tr("Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n"));
983
984 /* reset to default? */
985 if (!strcmp(a->argv[1], "default"))
986 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
987 else
988 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
989 }
990 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
991 {
992 /* reset to default? */
993 if (!strcmp(a->argv[1], "default"))
994 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
995 else
996 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
997 }
998 else if (!strcmp(a->argv[0], "vrdeextpack"))
999 {
1000 /* disable? */
1001 if (!strcmp(a->argv[1], "null"))
1002 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
1003 else
1004 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
1005 }
1006 else if (!strcmp(a->argv[0], "loghistorycount"))
1007 {
1008 uint32_t uVal;
1009 int vrc;
1010 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
1011 if (vrc != VINF_SUCCESS)
1012 return errorArgument(Misc::tr("Error parsing Log history count '%s'"), a->argv[1]);
1013 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
1014 }
1015 else if (!strcmp(a->argv[0], "autostartdbpath"))
1016 {
1017 /* disable? */
1018 if (!strcmp(a->argv[1], "null"))
1019 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
1020 else
1021 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
1022 }
1023 else if (!strcmp(a->argv[0], "defaultfrontend"))
1024 {
1025 Bstr bstrDefaultFrontend(a->argv[1]);
1026 if (!strcmp(a->argv[1], "default"))
1027 bstrDefaultFrontend.setNull();
1028 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
1029 }
1030 else if (!strcmp(a->argv[0], "logginglevel"))
1031 {
1032 Bstr bstrLoggingLevel(a->argv[1]);
1033 if (!strcmp(a->argv[1], "default"))
1034 bstrLoggingLevel.setNull();
1035 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
1036 }
1037 else if (!strcmp(a->argv[0], "proxymode"))
1038 {
1039 ProxyMode_T enmProxyMode;
1040 if (!RTStrICmpAscii(a->argv[1], "system"))
1041 enmProxyMode = ProxyMode_System;
1042 else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
1043 enmProxyMode = ProxyMode_NoProxy;
1044 else if (!RTStrICmpAscii(a->argv[1], "manual"))
1045 enmProxyMode = ProxyMode_Manual;
1046 else
1047 return errorArgument(Misc::tr("Unknown proxy mode: '%s'"), a->argv[1]);
1048 CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
1049 }
1050 else if (!strcmp(a->argv[0], "proxyurl"))
1051 {
1052 Bstr bstrProxyUrl(a->argv[1]);
1053 CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
1054 }
1055#ifdef VBOX_WITH_MAIN_NLS
1056 else if (!strcmp(a->argv[0], "language"))
1057 {
1058 Bstr bstrLanguage(a->argv[1]);
1059 CHECK_ERROR(systemProperties, COMSETTER(LanguageId)(bstrLanguage.raw()));
1060
1061 /* Kudge alert! Make sure the language change notification is processed,
1062 otherwise it may arrive as (XP)COM shuts down and cause
1063 trouble in debug builds. */
1064# ifdef DEBUG
1065 uint64_t const tsStart = RTTimeNanoTS();
1066# endif
1067 unsigned cMsgs = 0;
1068 int vrc;
1069 while ( RT_SUCCESS(vrc = NativeEventQueue::getMainEventQueue()->processEventQueue(32 /*ms*/))
1070 || vrc == VERR_INTERRUPTED)
1071 cMsgs++;
1072# ifdef DEBUG
1073 RTPrintf("vrc=%Rrc cMsgs=%u nsElapsed=%'RU64\n", vrc, cMsgs, RTTimeNanoTS() - tsStart);
1074# endif
1075 }
1076#endif
1077 else
1078 return errorSyntax(USAGE_SETPROPERTY, Misc::tr("Invalid parameter '%s'"), a->argv[0]);
1079
1080 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1081}
1082
1083/**
1084 * sharedfolder add
1085 */
1086static RTEXITCODE handleSharedFolderAdd(HandlerArg *a)
1087{
1088 /*
1089 * Parse arguments (argv[0] == subcommand).
1090 */
1091 static const RTGETOPTDEF s_aAddOptions[] =
1092 {
1093 { "--name", 'n', RTGETOPT_REQ_STRING },
1094 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1095 { "--hostpath", 'p', RTGETOPT_REQ_STRING },
1096 { "-hostpath", 'p', RTGETOPT_REQ_STRING }, // deprecated
1097 { "--readonly", 'r', RTGETOPT_REQ_NOTHING },
1098 { "-readonly", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
1099 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1100 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1101 { "--automount", 'a', RTGETOPT_REQ_NOTHING },
1102 { "-automount", 'a', RTGETOPT_REQ_NOTHING }, // deprecated
1103 { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING },
1104 };
1105 const char *pszMachineName = NULL;
1106 const char *pszName = NULL;
1107 const char *pszHostPath = NULL;
1108 bool fTransient = false;
1109 bool fWritable = true;
1110 bool fAutoMount = false;
1111 const char *pszAutoMountPoint = "";
1112
1113 RTGETOPTSTATE GetState;
1114 RTGetOptInit(&GetState, a->argc, a->argv, s_aAddOptions, RT_ELEMENTS(s_aAddOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1115 int c;
1116 RTGETOPTUNION ValueUnion;
1117 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1118 {
1119 switch (c)
1120 {
1121 case 'n':
1122 pszName = ValueUnion.psz;
1123 break;
1124 case 'p':
1125 pszHostPath = ValueUnion.psz;
1126 break;
1127 case 'r':
1128 fWritable = false;
1129 break;
1130 case 't':
1131 fTransient = true;
1132 break;
1133 case 'a':
1134 fAutoMount = true;
1135 break;
1136 case 'm':
1137 pszAutoMountPoint = ValueUnion.psz;
1138 break;
1139 case VINF_GETOPT_NOT_OPTION:
1140 if (pszMachineName)
1141 return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"),
1142 pszMachineName, ValueUnion.psz);
1143 pszMachineName = ValueUnion.psz;
1144 break;
1145 default:
1146 return errorGetOpt(c, &ValueUnion);
1147 }
1148 }
1149
1150 if (!pszMachineName)
1151 return errorSyntax(Misc::tr("No machine was specified"));
1152
1153 if (!pszName)
1154 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
1155 if (strchr(pszName, ' '))
1156 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains space"), pszName);
1157 if (strchr(pszName, '\t'))
1158 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains tabs"), pszName);
1159 if (strchr(pszName, '\n') || strchr(pszName, '\r'))
1160 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains newline"), pszName);
1161
1162 if (!pszHostPath)
1163 return errorSyntax(Misc::tr("No host path (--hostpath) was given"));
1164 char szAbsHostPath[RTPATH_MAX];
1165 int vrc = RTPathAbs(pszHostPath, szAbsHostPath, sizeof(szAbsHostPath));
1166 if (RT_FAILURE(vrc))
1167 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTAbsPath failed on '%s': %Rrc"), pszHostPath, vrc);
1168
1169 /*
1170 * Done parsing, do some work.
1171 */
1172 ComPtr<IMachine> ptrMachine;
1173 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1174 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1175
1176 HRESULT hrc;
1177 if (fTransient)
1178 {
1179 /* open an existing session for the VM */
1180 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1181
1182 /* get the session machine */
1183 ComPtr<IMachine> ptrSessionMachine;
1184 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1185
1186 /* get the session console */
1187 ComPtr<IConsole> ptrConsole;
1188 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1189 if (ptrConsole.isNull())
1190 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running."), pszMachineName);
1191
1192 CHECK_ERROR2(hrc, ptrConsole, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1193 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1194 a->session->UnlockMachine();
1195 }
1196 else
1197 {
1198 /* open a session for the VM */
1199 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1200
1201 /* get the mutable session machine */
1202 ComPtr<IMachine> ptrSessionMachine;
1203 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1204
1205 CHECK_ERROR2(hrc, ptrSessionMachine, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1206 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1207 if (SUCCEEDED(hrc))
1208 {
1209 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1210 }
1211
1212 a->session->UnlockMachine();
1213 }
1214
1215 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1216}
1217
1218/**
1219 * sharedfolder remove
1220 */
1221static RTEXITCODE handleSharedFolderRemove(HandlerArg *a)
1222{
1223 /*
1224 * Parse arguments (argv[0] == subcommand).
1225 */
1226 static const RTGETOPTDEF s_aRemoveOptions[] =
1227 {
1228 { "--name", 'n', RTGETOPT_REQ_STRING },
1229 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1230 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1231 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1232 };
1233 const char *pszMachineName = NULL;
1234 const char *pszName = NULL;
1235 bool fTransient = false;
1236
1237 RTGETOPTSTATE GetState;
1238 RTGetOptInit(&GetState, a->argc, a->argv, s_aRemoveOptions, RT_ELEMENTS(s_aRemoveOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1239 int c;
1240 RTGETOPTUNION ValueUnion;
1241 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1242 {
1243 switch (c)
1244 {
1245 case 'n':
1246 pszName = ValueUnion.psz;
1247 break;
1248 case 't':
1249 fTransient = true;
1250 break;
1251 case VINF_GETOPT_NOT_OPTION:
1252 if (pszMachineName)
1253 return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"),
1254 pszMachineName, ValueUnion.psz);
1255 pszMachineName = ValueUnion.psz;
1256 break;
1257 default:
1258 return errorGetOpt(c, &ValueUnion);
1259 }
1260 }
1261
1262 if (!pszMachineName)
1263 return errorSyntax(Misc::tr("No machine was specified"));
1264 if (!pszName)
1265 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
1266
1267 /*
1268 * Done parsing, do some real work.
1269 */
1270 ComPtr<IMachine> ptrMachine;
1271 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1272 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1273
1274 HRESULT hrc;
1275 if (fTransient)
1276 {
1277 /* open an existing session for the VM */
1278 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1279 /* get the session machine */
1280 ComPtr<IMachine> ptrSessionMachine;
1281 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1282 /* get the session console */
1283 ComPtr<IConsole> ptrConsole;
1284 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1285 if (ptrConsole.isNull())
1286 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running.\n"), pszMachineName);
1287
1288 CHECK_ERROR2(hrc, ptrConsole, RemoveSharedFolder(Bstr(pszName).raw()));
1289
1290 a->session->UnlockMachine();
1291 }
1292 else
1293 {
1294 /* open a session for the VM */
1295 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1296
1297 /* get the mutable session machine */
1298 ComPtr<IMachine> ptrSessionMachine;
1299 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1300
1301 CHECK_ERROR2(hrc, ptrSessionMachine, RemoveSharedFolder(Bstr(pszName).raw()));
1302
1303 /* commit and close the session */
1304 if (SUCCEEDED(hrc))
1305 {
1306 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1307 }
1308 a->session->UnlockMachine();
1309 }
1310
1311 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1312}
1313
1314
1315RTEXITCODE handleSharedFolder(HandlerArg *a)
1316{
1317 if (a->argc < 1)
1318 return errorSyntax(Misc::tr("Not enough parameters"));
1319
1320 if (!strcmp(a->argv[0], "add"))
1321 {
1322 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_ADD);
1323 return handleSharedFolderAdd(a);
1324 }
1325
1326 if (!strcmp(a->argv[0], "remove"))
1327 {
1328 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_REMOVE);
1329 return handleSharedFolderRemove(a);
1330 }
1331
1332 return errorUnknownSubcommand(a->argv[0]);
1333}
1334
1335RTEXITCODE handleExtPack(HandlerArg *a)
1336{
1337 if (a->argc < 1)
1338 return errorNoSubcommand();
1339
1340 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1341 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1342
1343 RTGETOPTSTATE GetState;
1344 RTGETOPTUNION ValueUnion;
1345 int ch;
1346 HRESULT hrc = S_OK;
1347
1348 if (!strcmp(a->argv[0], "install"))
1349 {
1350 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1351 const char *pszName = NULL;
1352 bool fReplace = false;
1353
1354 static const RTGETOPTDEF s_aInstallOptions[] =
1355 {
1356 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1357 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1358 };
1359
1360 RTCList<RTCString> lstLicenseHashes;
1361 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1362 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1363 {
1364 switch (ch)
1365 {
1366 case 'r':
1367 fReplace = true;
1368 break;
1369
1370 case 'a':
1371 lstLicenseHashes.append(ValueUnion.psz);
1372 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1373 break;
1374
1375 case VINF_GETOPT_NOT_OPTION:
1376 if (pszName)
1377 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
1378 pszName = ValueUnion.psz;
1379 break;
1380
1381 default:
1382 return errorGetOpt(ch, &ValueUnion);
1383 }
1384 }
1385 if (!pszName)
1386 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack install\""));
1387
1388 char szPath[RTPATH_MAX];
1389 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1390 if (RT_FAILURE(vrc))
1391 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with rc=%Rrc"), pszName, vrc);
1392
1393 Bstr bstrTarball(szPath);
1394 Bstr bstrName;
1395 ComPtr<IExtPackFile> ptrExtPackFile;
1396 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1397 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1398 BOOL fShowLicense = true;
1399 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1400 if (fShowLicense)
1401 {
1402 Bstr bstrLicense;
1403 CHECK_ERROR2I_RET(ptrExtPackFile,
1404 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1405 Bstr("").raw() /* PreferredLanguage */,
1406 Bstr("txt").raw() /* Format */,
1407 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1408 Utf8Str strLicense(bstrLicense);
1409 uint8_t abHash[RTSHA256_HASH_SIZE];
1410 char szDigest[RTSHA256_DIGEST_LEN + 1];
1411 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1412 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1413 AssertRCStmt(vrc, szDigest[0] = '\0');
1414 if (lstLicenseHashes.contains(szDigest))
1415 RTPrintf(Misc::tr("License accepted.\n"));
1416 else
1417 {
1418 RTPrintf("%s\n", strLicense.c_str());
1419 RTPrintf(Misc::tr("Do you agree to these license terms and conditions (y/n)? "));
1420 ch = RTStrmGetCh(g_pStdIn);
1421 RTPrintf("\n");
1422 if (ch != 'y' && ch != 'Y')
1423 {
1424 RTPrintf(Misc::tr("Installation of \"%ls\" aborted.\n"), bstrName.raw());
1425 return RTEXITCODE_FAILURE;
1426 }
1427 if (szDigest[0])
1428 RTPrintf(Misc::tr("License accepted. For batch installation add\n"
1429 "--accept-license=%s\n"
1430 "to the VBoxManage command line.\n\n"), szDigest);
1431 }
1432 }
1433 ComPtr<IProgress> ptrProgress;
1434 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1435 hrc = showProgress(ptrProgress);
1436 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to install \"%s\""), szPath), RTEXITCODE_FAILURE);
1437
1438 RTPrintf(Misc::tr("Successfully installed \"%ls\".\n"), bstrName.raw());
1439 }
1440 else if (!strcmp(a->argv[0], "uninstall"))
1441 {
1442 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1443 const char *pszName = NULL;
1444 bool fForced = false;
1445
1446 static const RTGETOPTDEF s_aUninstallOptions[] =
1447 {
1448 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1449 };
1450
1451 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1452 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1453 {
1454 switch (ch)
1455 {
1456 case 'f':
1457 fForced = true;
1458 break;
1459
1460 case VINF_GETOPT_NOT_OPTION:
1461 if (pszName)
1462 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
1463 pszName = ValueUnion.psz;
1464 break;
1465
1466 default:
1467 return errorGetOpt(ch, &ValueUnion);
1468 }
1469 }
1470 if (!pszName)
1471 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack uninstall\""));
1472
1473 Bstr bstrName(pszName);
1474 ComPtr<IProgress> ptrProgress;
1475 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1476 hrc = showProgress(ptrProgress);
1477 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to uninstall \"%s\""), pszName), RTEXITCODE_FAILURE);
1478
1479 RTPrintf(Misc::tr("Successfully uninstalled \"%s\".\n"), pszName);
1480 }
1481 else if (!strcmp(a->argv[0], "cleanup"))
1482 {
1483 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1484 if (a->argc > 1)
1485 return errorTooManyParameters(&a->argv[1]);
1486 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1487 RTPrintf(Misc::tr("Successfully performed extension pack cleanup\n"));
1488 }
1489 else
1490 return errorUnknownSubcommand(a->argv[0]);
1491
1492 return RTEXITCODE_SUCCESS;
1493}
1494
1495RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1496{
1497 HRESULT hrc;
1498
1499 /*
1500 * Options. We work directly on an IUnattended instace while parsing
1501 * the options. This saves a lot of extra clutter.
1502 */
1503 bool fMachineReadable = false;
1504 char szIsoPath[RTPATH_MAX];
1505 szIsoPath[0] = '\0';
1506
1507 /*
1508 * Parse options.
1509 */
1510 static const RTGETOPTDEF s_aOptions[] =
1511 {
1512 { "--iso", 'i', RTGETOPT_REQ_STRING },
1513 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1514 };
1515
1516 RTGETOPTSTATE GetState;
1517 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1518 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1519
1520 int c;
1521 RTGETOPTUNION ValueUnion;
1522 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1523 {
1524 switch (c)
1525 {
1526 case 'i': // --iso
1527 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1528 if (RT_FAILURE(vrc))
1529 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1530 break;
1531
1532 case 'M': // --machine-readable.
1533 fMachineReadable = true;
1534 break;
1535
1536 default:
1537 return errorGetOpt(c, &ValueUnion);
1538 }
1539 }
1540
1541 /*
1542 * Check for required stuff.
1543 */
1544 if (szIsoPath[0] == '\0')
1545 return errorSyntax(Misc::tr("No ISO specified"));
1546
1547 /*
1548 * Do the job.
1549 */
1550 ComPtr<IUnattended> ptrUnattended;
1551 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1552 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1553 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1554 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1555
1556 /*
1557 * Retrieve the results.
1558 */
1559 Bstr bstrDetectedOSTypeId;
1560 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1561 Bstr bstrDetectedVersion;
1562 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1563 Bstr bstrDetectedFlavor;
1564 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1565 Bstr bstrDetectedLanguages;
1566 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1567 Bstr bstrDetectedHints;
1568 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1569 if (fMachineReadable)
1570 RTPrintf("OSTypeId=\"%ls\"\n"
1571 "OSVersion=\"%ls\"\n"
1572 "OSFlavor=\"%ls\"\n"
1573 "OSLanguages=\"%ls\"\n"
1574 "OSHints=\"%ls\"\n",
1575 bstrDetectedOSTypeId.raw(),
1576 bstrDetectedVersion.raw(),
1577 bstrDetectedFlavor.raw(),
1578 bstrDetectedLanguages.raw(),
1579 bstrDetectedHints.raw());
1580 else
1581 {
1582 RTMsgInfo(Misc::tr("Detected '%s' to be:\n"), szIsoPath);
1583 RTPrintf(Misc::tr(" OS TypeId = %ls\n"
1584 " OS Version = %ls\n"
1585 " OS Flavor = %ls\n"
1586 " OS Languages = %ls\n"
1587 " OS Hints = %ls\n"),
1588 bstrDetectedOSTypeId.raw(),
1589 bstrDetectedVersion.raw(),
1590 bstrDetectedFlavor.raw(),
1591 bstrDetectedLanguages.raw(),
1592 bstrDetectedHints.raw());
1593 }
1594
1595 return rcExit;
1596}
1597
1598RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1599{
1600 HRESULT hrc;
1601 char szAbsPath[RTPATH_MAX];
1602
1603 /*
1604 * Options. We work directly on an IUnattended instance while parsing
1605 * the options. This saves a lot of extra clutter.
1606 */
1607 ComPtr<IUnattended> ptrUnattended;
1608 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1609 RTCList<RTCString> arrPackageSelectionAdjustments;
1610 ComPtr<IMachine> ptrMachine;
1611 bool fDryRun = false;
1612 const char *pszSessionType = "none";
1613
1614 /*
1615 * Parse options.
1616 */
1617 static const RTGETOPTDEF s_aOptions[] =
1618 {
1619 { "--iso", 'i', RTGETOPT_REQ_STRING },
1620 { "--user", 'u', RTGETOPT_REQ_STRING },
1621 { "--password", 'p', RTGETOPT_REQ_STRING },
1622 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1623 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1624 { "--key", 'k', RTGETOPT_REQ_STRING },
1625 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1626 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1627 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1628 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1629 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1630 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1631 { "--locale", 'l', RTGETOPT_REQ_STRING },
1632 { "--country", 'Y', RTGETOPT_REQ_STRING },
1633 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1634 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1635 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1636 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1637 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1638 // advance options:
1639 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1640 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1641 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1642 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1643 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1644 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1645 { "--language", 'L', RTGETOPT_REQ_STRING },
1646 // start vm related options:
1647 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1648 /** @todo Add a --wait option too for waiting for the VM to shut down or
1649 * something like that...? */
1650 };
1651
1652 RTGETOPTSTATE GetState;
1653 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1654 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1655
1656 int c;
1657 RTGETOPTUNION ValueUnion;
1658 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1659 {
1660 switch (c)
1661 {
1662 case VINF_GETOPT_NOT_OPTION:
1663 if (ptrMachine.isNotNull())
1664 return errorSyntax(Misc::tr("VM name/UUID given more than once!"));
1665 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1666 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1667 break;
1668
1669 case 'i': // --iso
1670 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1671 if (RT_FAILURE(vrc))
1672 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1673 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1674 break;
1675
1676 case 'u': // --user
1677 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1678 break;
1679
1680 case 'p': // --password
1681 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1682 break;
1683
1684 case 'X': // --password-file
1685 {
1686 Utf8Str strPassword;
1687 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1688 if (rcExit != RTEXITCODE_SUCCESS)
1689 return rcExit;
1690 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1691 break;
1692 }
1693
1694 case 'U': // --full-user-name
1695 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1696 break;
1697
1698 case 'k': // --key
1699 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1700 break;
1701
1702 case 'A': // --install-additions
1703 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1704 break;
1705 case 'N': // --no-install-additions
1706 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1707 break;
1708 case 'a': // --additions-iso
1709 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1710 if (RT_FAILURE(vrc))
1711 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1712 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1713 break;
1714
1715 case 't': // --install-txs
1716 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1717 break;
1718 case 'T': // --no-install-txs
1719 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1720 break;
1721 case 'K': // --valiation-kit-iso
1722 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1723 if (RT_FAILURE(vrc))
1724 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1725 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1726 break;
1727
1728 case 'l': // --locale
1729 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1730 break;
1731
1732 case 'Y': // --country
1733 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1734 break;
1735
1736 case 'z': // --time-zone;
1737 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1738 break;
1739
1740 case 'y': // --proxy
1741 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1742 break;
1743
1744 case 'H': // --hostname
1745 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1746 break;
1747
1748 case 's': // --package-selection-adjustment
1749 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1750 break;
1751
1752 case 'D':
1753 fDryRun = true;
1754 break;
1755
1756 case 'x': // --auxiliary-base-path
1757 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1758 if (RT_FAILURE(vrc))
1759 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1760 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1761 break;
1762
1763 case 'm': // --image-index
1764 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1765 break;
1766
1767 case 'c': // --script-template
1768 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1769 if (RT_FAILURE(vrc))
1770 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1771 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1772 break;
1773
1774 case 'C': // --post-install-script-template
1775 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1776 if (RT_FAILURE(vrc))
1777 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1778 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1779 break;
1780
1781 case 'P': // --post-install-command.
1782 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1783 break;
1784
1785 case 'I': // --extra-install-kernel-parameters
1786 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1787 break;
1788
1789 case 'L': // --language
1790 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1791 break;
1792
1793 case 'S': // --start-vm
1794 pszSessionType = ValueUnion.psz;
1795 break;
1796
1797 default:
1798 return errorGetOpt(c, &ValueUnion);
1799 }
1800 }
1801
1802 /*
1803 * Check for required stuff.
1804 */
1805 if (ptrMachine.isNull())
1806 return errorSyntax(Misc::tr("Missing VM name/UUID"));
1807
1808 /*
1809 * Set accumulative attributes.
1810 */
1811 if (arrPackageSelectionAdjustments.size() == 1)
1812 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1813 RTEXITCODE_FAILURE);
1814 else if (arrPackageSelectionAdjustments.size() > 1)
1815 {
1816 RTCString strAdjustments;
1817 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1818 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1819 }
1820
1821 /*
1822 * Get details about the machine so we can display them below.
1823 */
1824 Bstr bstrMachineName;
1825 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1826 Bstr bstrUuid;
1827 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1828 BSTR bstrInstalledOS;
1829 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1830 Utf8Str strInstalledOS(bstrInstalledOS);
1831
1832 /*
1833 * Temporarily lock the machine to check whether it's running or not.
1834 * We take this opportunity to disable the first run wizard.
1835 */
1836 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1837 {
1838 ComPtr<IConsole> ptrConsole;
1839 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1840
1841 if ( ptrConsole.isNull()
1842 && SUCCEEDED(hrc)
1843 && ( RTStrICmp(pszSessionType, "gui") == 0
1844 || RTStrICmp(pszSessionType, "none") == 0))
1845 {
1846 ComPtr<IMachine> ptrSessonMachine;
1847 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1848 if (ptrSessonMachine.isNotNull())
1849 {
1850 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1851 }
1852 }
1853
1854 a->session->UnlockMachine();
1855 if (FAILED(hrc))
1856 return RTEXITCODE_FAILURE;
1857 if (ptrConsole.isNotNull())
1858 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%ls' is currently running"), bstrMachineName.raw());
1859 }
1860
1861 /*
1862 * Do the work.
1863 */
1864 RTMsgInfo(Misc::tr("%s unattended installation of %s in machine '%ls' (%ls).\n"),
1865 RTStrICmp(pszSessionType, "none") == 0 ? Misc::tr("Preparing") : Misc::tr("Starting"),
1866 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1867
1868 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1869 if (!fDryRun)
1870 {
1871 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1872 CHECK_ERROR2_RET(hrc, ptrUnattended, ReconfigureVM(), RTEXITCODE_FAILURE);
1873 }
1874
1875 /*
1876 * Retrieve and display the parameters actually used.
1877 */
1878 RTMsgInfo(Misc::tr("Using values:\n"));
1879#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1880 a_Type Value; \
1881 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1882 if (SUCCEEDED(hrc2)) \
1883 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1884 else \
1885 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
1886 } while (0)
1887#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1888 Bstr bstrString; \
1889 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1890 if (SUCCEEDED(hrc2)) \
1891 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1892 else \
1893 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
1894 } while (0)
1895
1896 SHOW_STR_ATTR(IsoPath, "isoPath");
1897 SHOW_STR_ATTR(User, "user");
1898 SHOW_STR_ATTR(Password, "password");
1899 SHOW_STR_ATTR(FullUserName, "fullUserName");
1900 SHOW_STR_ATTR(ProductKey, "productKey");
1901 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1902 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1903 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1904 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1905 SHOW_STR_ATTR(Locale, "locale");
1906 SHOW_STR_ATTR(Country, "country");
1907 SHOW_STR_ATTR(TimeZone, "timeZone");
1908 SHOW_STR_ATTR(Proxy, "proxy");
1909 SHOW_STR_ATTR(Hostname, "hostname");
1910 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1911 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1912 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1913 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1914 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1915 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1916 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1917 SHOW_STR_ATTR(Language, "language");
1918 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1919 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1920 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1921 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1922 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1923
1924#undef SHOW_STR_ATTR
1925#undef SHOW_ATTR
1926
1927 /* We can drop the IUnatteded object now. */
1928 ptrUnattended.setNull();
1929
1930 /*
1931 * Start the VM if requested.
1932 */
1933 if ( fDryRun
1934 || RTStrICmp(pszSessionType, "none") == 0)
1935 {
1936 if (!fDryRun)
1937 RTMsgInfo(Misc::tr("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n"), bstrMachineName.raw(), bstrUuid.raw());
1938 hrc = S_OK;
1939 }
1940 else
1941 {
1942 com::SafeArray<IN_BSTR> aBstrEnv;
1943#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1944 /* make sure the VM process will start on the same display as VBoxManage */
1945 const char *pszDisplay = RTEnvGet("DISPLAY");
1946 if (pszDisplay)
1947 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
1948 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1949 if (pszXAuth)
1950 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
1951#endif
1952 ComPtr<IProgress> ptrProgress;
1953 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam()));
1954 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1955 {
1956 RTMsgInfo(Misc::tr("Waiting for VM '%ls' to power on...\n"), bstrMachineName.raw());
1957 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1958 if (SUCCEEDED(hrc))
1959 {
1960 BOOL fCompleted = true;
1961 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1962 if (SUCCEEDED(hrc))
1963 {
1964 ASSERT(fCompleted);
1965
1966 LONG iRc;
1967 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
1968 if (SUCCEEDED(hrc))
1969 {
1970 if (SUCCEEDED(iRc))
1971 RTMsgInfo(Misc::tr("VM '%ls' (%ls) has been successfully started.\n"),
1972 bstrMachineName.raw(), bstrUuid.raw());
1973 else
1974 {
1975 ProgressErrorInfo info(ptrProgress);
1976 com::GluePrintErrorInfo(info);
1977 }
1978 hrc = iRc;
1979 }
1980 }
1981 }
1982 }
1983
1984 /*
1985 * Do we wait for the VM to power down?
1986 */
1987 }
1988
1989 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1990}
1991
1992
1993RTEXITCODE handleUnattended(HandlerArg *a)
1994{
1995 /*
1996 * Sub-command switch.
1997 */
1998 if (a->argc < 1)
1999 return errorNoSubcommand();
2000
2001 if (!strcmp(a->argv[0], "detect"))
2002 {
2003 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
2004 return handleUnattendedDetect(a);
2005 }
2006
2007 if (!strcmp(a->argv[0], "install"))
2008 {
2009 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
2010 return handleUnattendedInstall(a);
2011 }
2012
2013 /* Consider some kind of create-vm-and-install-guest-os command. */
2014 return errorUnknownSubcommand(a->argv[0]);
2015}
2016
2017/**
2018 * Common Cloud profile options.
2019 */
2020typedef struct
2021{
2022 const char *pszProviderName;
2023 const char *pszProfileName;
2024} CLOUDPROFILECOMMONOPT;
2025typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
2026
2027/**
2028 * Sets the properties of cloud profile
2029 *
2030 * @returns 0 on success, 1 on failure
2031 */
2032
2033static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2034{
2035
2036 HRESULT hrc = S_OK;
2037
2038 Bstr bstrProvider(pCommonOpts->pszProviderName);
2039 Bstr bstrProfile(pCommonOpts->pszProfileName);
2040
2041 /*
2042 * Parse options.
2043 */
2044 static const RTGETOPTDEF s_aOptions[] =
2045 {
2046 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2047 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2048 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2049 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2050 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2051 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2052 { "--region", 'r', RTGETOPT_REQ_STRING }
2053 };
2054
2055 RTGETOPTSTATE GetState;
2056 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2057 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2058
2059 com::SafeArray<BSTR> names;
2060 com::SafeArray<BSTR> values;
2061
2062 int c;
2063 RTGETOPTUNION ValueUnion;
2064 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2065 {
2066 switch (c)
2067 {
2068 case 'u': // --clouduser
2069 Bstr("user").detachTo(names.appendedRaw());
2070 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2071 break;
2072 case 'p': // --fingerprint
2073 Bstr("fingerprint").detachTo(names.appendedRaw());
2074 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2075 break;
2076 case 'k': // --keyfile
2077 Bstr("key_file").detachTo(names.appendedRaw());
2078 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2079 break;
2080 case 'P': // --passphrase
2081 Bstr("pass_phrase").detachTo(names.appendedRaw());
2082 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2083 break;
2084 case 't': // --tenancy
2085 Bstr("tenancy").detachTo(names.appendedRaw());
2086 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2087 break;
2088 case 'c': // --compartment
2089 Bstr("compartment").detachTo(names.appendedRaw());
2090 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2091 break;
2092 case 'r': // --region
2093 Bstr("region").detachTo(names.appendedRaw());
2094 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2095 break;
2096 default:
2097 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2098 }
2099 }
2100
2101 /* check for required options */
2102 if (bstrProvider.isEmpty())
2103 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2104 if (bstrProfile.isEmpty())
2105 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2106
2107 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2108
2109 ComPtr<ICloudProviderManager> pCloudProviderManager;
2110 CHECK_ERROR2_RET(hrc, pVirtualBox,
2111 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2112 RTEXITCODE_FAILURE);
2113
2114 ComPtr<ICloudProvider> pCloudProvider;
2115
2116 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2117 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2118 RTEXITCODE_FAILURE);
2119
2120 ComPtr<ICloudProfile> pCloudProfile;
2121
2122 if (pCloudProvider)
2123 {
2124 CHECK_ERROR2_RET(hrc, pCloudProvider,
2125 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2126 RTEXITCODE_FAILURE);
2127 CHECK_ERROR2_RET(hrc, pCloudProfile,
2128 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2129 RTEXITCODE_FAILURE);
2130 }
2131
2132 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2133
2134 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was updated.\n"),bstrProvider.raw(), bstrProfile.raw());
2135
2136 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2137}
2138
2139/**
2140 * Gets the properties of cloud profile
2141 *
2142 * @returns 0 on success, 1 on failure
2143 */
2144static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2145{
2146 HRESULT hrc = S_OK;
2147
2148 Bstr bstrProvider(pCommonOpts->pszProviderName);
2149 Bstr bstrProfile(pCommonOpts->pszProfileName);
2150
2151 /* check for required options */
2152 if (bstrProvider.isEmpty())
2153 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2154 if (bstrProfile.isEmpty())
2155 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2156
2157 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2158 ComPtr<ICloudProviderManager> pCloudProviderManager;
2159 CHECK_ERROR2_RET(hrc, pVirtualBox,
2160 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2161 RTEXITCODE_FAILURE);
2162 ComPtr<ICloudProvider> pCloudProvider;
2163 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2164 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2165 RTEXITCODE_FAILURE);
2166
2167 ComPtr<ICloudProfile> pCloudProfile;
2168 if (pCloudProvider)
2169 {
2170 CHECK_ERROR2_RET(hrc, pCloudProvider,
2171 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2172 RTEXITCODE_FAILURE);
2173
2174 Bstr bstrProviderID;
2175 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
2176 RTPrintf(Misc::tr("Provider GUID: %ls\n"), bstrProviderID.raw());
2177
2178 com::SafeArray<BSTR> names;
2179 com::SafeArray<BSTR> values;
2180 CHECK_ERROR2_RET(hrc, pCloudProfile,
2181 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2182 RTEXITCODE_FAILURE);
2183 size_t cNames = names.size();
2184 size_t cValues = values.size();
2185 bool fFirst = true;
2186 for (size_t k = 0; k < cNames; k++)
2187 {
2188 Bstr value;
2189 if (k < cValues)
2190 value = values[k];
2191 RTPrintf("%s%ls=%ls\n",
2192 fFirst ? Misc::tr("Property: ") : " ",
2193 names[k], value.raw());
2194 fFirst = false;
2195 }
2196
2197 RTPrintf("\n");
2198 }
2199
2200 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2201}
2202
2203static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2204{
2205 HRESULT hrc = S_OK;
2206
2207 Bstr bstrProvider(pCommonOpts->pszProviderName);
2208 Bstr bstrProfile(pCommonOpts->pszProfileName);
2209
2210
2211 /* check for required options */
2212 if (bstrProvider.isEmpty())
2213 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2214 if (bstrProfile.isEmpty())
2215 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2216
2217 /*
2218 * Parse options.
2219 */
2220 static const RTGETOPTDEF s_aOptions[] =
2221 {
2222 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2223 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2224 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2225 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2226 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2227 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2228 { "--region", 'r', RTGETOPT_REQ_STRING }
2229 };
2230
2231 RTGETOPTSTATE GetState;
2232 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2233 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2234
2235 com::SafeArray<BSTR> names;
2236 com::SafeArray<BSTR> values;
2237
2238 int c;
2239 RTGETOPTUNION ValueUnion;
2240 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2241 {
2242 switch (c)
2243 {
2244 case 'u': // --clouduser
2245 Bstr("user").detachTo(names.appendedRaw());
2246 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2247 break;
2248 case 'p': // --fingerprint
2249 Bstr("fingerprint").detachTo(names.appendedRaw());
2250 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2251 break;
2252 case 'k': // --keyfile
2253 Bstr("key_file").detachTo(names.appendedRaw());
2254 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2255 break;
2256 case 'P': // --passphrase
2257 Bstr("pass_phrase").detachTo(names.appendedRaw());
2258 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2259 break;
2260 case 't': // --tenancy
2261 Bstr("tenancy").detachTo(names.appendedRaw());
2262 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2263 break;
2264 case 'c': // --compartment
2265 Bstr("compartment").detachTo(names.appendedRaw());
2266 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2267 break;
2268 case 'r': // --region
2269 Bstr("region").detachTo(names.appendedRaw());
2270 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2271 break;
2272 default:
2273 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2274 }
2275 }
2276
2277 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2278
2279 ComPtr<ICloudProviderManager> pCloudProviderManager;
2280 CHECK_ERROR2_RET(hrc, pVirtualBox,
2281 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2282 RTEXITCODE_FAILURE);
2283
2284 ComPtr<ICloudProvider> pCloudProvider;
2285 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2286 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2287 RTEXITCODE_FAILURE);
2288
2289 CHECK_ERROR2_RET(hrc, pCloudProvider,
2290 CreateProfile(bstrProfile.raw(),
2291 ComSafeArrayAsInParam(names),
2292 ComSafeArrayAsInParam(values)),
2293 RTEXITCODE_FAILURE);
2294
2295 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2296
2297 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was added.\n"),bstrProvider.raw(), bstrProfile.raw());
2298
2299 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2300}
2301
2302static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2303{
2304 HRESULT hrc = S_OK;
2305
2306 Bstr bstrProvider(pCommonOpts->pszProviderName);
2307 Bstr bstrProfile(pCommonOpts->pszProfileName);
2308
2309 /* check for required options */
2310 if (bstrProvider.isEmpty())
2311 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2312 if (bstrProfile.isEmpty())
2313 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2314
2315 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2316 ComPtr<ICloudProviderManager> pCloudProviderManager;
2317 CHECK_ERROR2_RET(hrc, pVirtualBox,
2318 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2319 RTEXITCODE_FAILURE);
2320 ComPtr<ICloudProvider> pCloudProvider;
2321 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2322 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2323 RTEXITCODE_FAILURE);
2324
2325 ComPtr<ICloudProfile> pCloudProfile;
2326 if (pCloudProvider)
2327 {
2328 CHECK_ERROR2_RET(hrc, pCloudProvider,
2329 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2330 RTEXITCODE_FAILURE);
2331
2332 CHECK_ERROR2_RET(hrc, pCloudProfile,
2333 Remove(),
2334 RTEXITCODE_FAILURE);
2335
2336 CHECK_ERROR2_RET(hrc, pCloudProvider,
2337 SaveProfiles(),
2338 RTEXITCODE_FAILURE);
2339
2340 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was deleted.\n"), bstrProvider.raw(), bstrProfile.raw());
2341 }
2342
2343 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2344}
2345
2346RTEXITCODE handleCloudProfile(HandlerArg *a)
2347{
2348 if (a->argc < 1)
2349 return errorNoSubcommand();
2350
2351 static const RTGETOPTDEF s_aOptions[] =
2352 {
2353 /* common options */
2354 { "--provider", 'v', RTGETOPT_REQ_STRING },
2355 { "--profile", 'f', RTGETOPT_REQ_STRING },
2356 /* subcommands */
2357 { "add", 1000, RTGETOPT_REQ_NOTHING },
2358 { "show", 1001, RTGETOPT_REQ_NOTHING },
2359 { "update", 1002, RTGETOPT_REQ_NOTHING },
2360 { "delete", 1003, RTGETOPT_REQ_NOTHING },
2361 };
2362
2363 RTGETOPTSTATE GetState;
2364 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2365 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2366
2367 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
2368 int c;
2369 RTGETOPTUNION ValueUnion;
2370 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2371 {
2372 switch (c)
2373 {
2374 case 'v': // --provider
2375 CommonOpts.pszProviderName = ValueUnion.psz;
2376 break;
2377 case 'f': // --profile
2378 CommonOpts.pszProfileName = ValueUnion.psz;
2379 break;
2380 /* Sub-commands: */
2381 case 1000:
2382 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
2383 return addCloudProfile(a, GetState.iNext, &CommonOpts);
2384 case 1001:
2385 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
2386 return showCloudProfileProperties(a, &CommonOpts);
2387 case 1002:
2388 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
2389 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
2390 case 1003:
2391 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
2392 return deleteCloudProfile(a, &CommonOpts);
2393 case VINF_GETOPT_NOT_OPTION:
2394 return errorUnknownSubcommand(ValueUnion.psz);
2395
2396 default:
2397 return errorGetOpt(c, &ValueUnion);
2398 }
2399 }
2400
2401 return errorNoSubcommand();
2402}
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