VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageCloudMachine.cpp@ 94660

Last change on this file since 94660 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.8 KB
Line 
1/* $Id: VBoxManageCloudMachine.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VBoxManageCloudMachine - The cloud machine related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#include "VBoxManage.h"
19
20#include <VBox/log.h>
21
22#include <VBox/com/ErrorInfo.h>
23#include <VBox/com/Guid.h>
24#include <VBox/com/errorprint.h>
25
26#include <algorithm>
27#include <vector>
28
29DECLARE_TRANSLATION_CONTEXT(CloudMachine);
30
31
32struct CMachineHandlerArg
33 : public HandlerArg
34{
35 ComPtr<ICloudClient> pClient;
36
37 const char *pcszSpec; /* RTGETOPTUNION::psz, points inside argv */
38 enum { GUESS, ID, NAME } enmSpecKind;
39 ComPtr<ICloudMachine> pMachine;
40
41 explicit CMachineHandlerArg(const HandlerArg &a)
42 : HandlerArg(a), pcszSpec(NULL), enmSpecKind(GUESS) {}
43};
44
45
46static int selectCloudProvider(ComPtr<ICloudProvider> &pProvider,
47 const ComPtr<IVirtualBox> &pVirtualBox,
48 const char *pszProviderName);
49static int selectCloudProfile(ComPtr<ICloudProfile> &pProfile,
50 const ComPtr<ICloudProvider> &pProvider,
51 const char *pszProviderName);
52static int getCloudClient(CMachineHandlerArg &a,
53 const char *pcszProviderName,
54 const char *pcszProfileName);
55
56static HRESULT getMachineList(com::SafeIfaceArray<ICloudMachine> &aMachines,
57 const ComPtr<ICloudClient> &pClient);
58
59static HRESULT getMachineBySpec(CMachineHandlerArg *a);
60static HRESULT getMachineById(CMachineHandlerArg *a);
61static HRESULT getMachineByName(CMachineHandlerArg *a);
62static HRESULT getMachineByGuess(CMachineHandlerArg *a);
63
64static int checkMachineSpecArgument(CMachineHandlerArg *a,
65 int ch, const RTGETOPTUNION &Val);
66
67
68static RTEXITCODE handleCloudMachineImpl(CMachineHandlerArg *a, int iFirst);
69
70static RTEXITCODE handleCloudMachineStart(CMachineHandlerArg *a, int iFirst);
71static RTEXITCODE handleCloudMachineReboot(CMachineHandlerArg *a, int iFirst);
72static RTEXITCODE handleCloudMachineShutdown(CMachineHandlerArg *a, int iFirst);
73static RTEXITCODE handleCloudMachinePowerdown(CMachineHandlerArg *a, int iFirst);
74static RTEXITCODE handleCloudMachineTerminate(CMachineHandlerArg *a, int iFirst);
75
76static RTEXITCODE handleCloudMachineConsoleHistory(CMachineHandlerArg *a, int iFirst);
77
78static RTEXITCODE listCloudMachinesImpl(CMachineHandlerArg *a, int iFirst);
79static RTEXITCODE handleCloudMachineInfo(CMachineHandlerArg *a, int iFirst);
80
81static HRESULT printMachineInfo(const ComPtr<ICloudMachine> &pMachine);
82static HRESULT printFormValue(const ComPtr<IFormValue> &pValue);
83
84
85
86/*
87 * This is a temporary hack as I don't want to refactor "cloud"
88 * handling right now, as it's not yet clear to me what is the
89 * direction that we want to take with it.
90 *
91 * The problem with the way "cloud" command handling is currently
92 * written is that it's a bit schizophrenic about whether we have
93 * multiple cloud providers or not. OTOH it insists on --provider
94 * being mandatory, on the other it hardcodes the list of available
95 * subcommands, though in principle those can vary from provider to
96 * provider. If we do want to support multiple providers we might
97 * need to come up with a way to allow an extpack provider to supply
98 * its own VBoxManage command handler for "cloud" based on --provider
99 * as the selector.
100 *
101 * Processing of --provider and --profile should not be postponed
102 * until the leaf command handler, but rather happen immediately, so
103 * do this here at our earliest opportunity (without actually doing it
104 * in handleCloud).
105 */
106RTEXITCODE
107handleCloudMachine(HandlerArg *a, int iFirst,
108 const char *pcszProviderName,
109 const char *pcszProfileName)
110{
111 CMachineHandlerArg handlerArg(*a);
112 int rc = getCloudClient(handlerArg, pcszProviderName, pcszProfileName);
113 if (RT_FAILURE(rc))
114 return RTEXITCODE_FAILURE;
115
116 return handleCloudMachineImpl(&handlerArg, iFirst);
117}
118
119
120/*
121 * Select cloud provider to use based on the --provider option to the
122 * "cloud" command. The option is not mandatory if only a single
123 * provider is available.
124 */
125static int
126selectCloudProvider(ComPtr<ICloudProvider> &pProvider,
127 const ComPtr<IVirtualBox> &pVirtualBox,
128 const char *pcszProviderName)
129{
130 HRESULT hrc;
131
132 ComPtr<ICloudProviderManager> pCloudProviderManager;
133 CHECK_ERROR2_RET(hrc, pVirtualBox,
134 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
135 VERR_GENERAL_FAILURE);
136
137
138 /*
139 * If the provider is explicitly specified, just look it up and
140 * return.
141 */
142 if (pcszProviderName != NULL)
143 {
144 /*
145 * Should we also provide a way to specify the provider also
146 * by its id? Is it even useful? If so, should we use a
147 * different option or check if the provider name looks like
148 * an id and used a different getter?
149 */
150 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
151 GetProviderByShortName(com::Bstr(pcszProviderName).raw(),
152 pProvider.asOutParam()),
153 VERR_NOT_FOUND);
154
155 return VINF_SUCCESS;
156 }
157
158
159 /*
160 * We have only one provider and it's not clear if we will ever
161 * have more than one. Forcing the user to explicitly specify the
162 * only provider available is not very nice. So try to be
163 * friendly.
164 */
165 com::SafeIfaceArray<ICloudProvider> aProviders;
166 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
167 COMGETTER(Providers)(ComSafeArrayAsOutParam(aProviders)),
168 VERR_GENERAL_FAILURE);
169
170 if (aProviders.size() == 0)
171 {
172 RTMsgError(CloudMachine::tr("cloud: no providers available"));
173 return VERR_NOT_FOUND;
174 }
175
176 if (aProviders.size() > 1)
177 {
178 RTMsgError(CloudMachine::tr("cloud: multiple providers available,"
179 " '--provider' option is required"));
180 return VERR_MISSING;
181 }
182
183 /* Do RTMsgInfo telling the user which one was selected? */
184 pProvider = aProviders[0];
185 return VINF_SUCCESS;
186}
187
188
189/*
190 * Select cloud profile to use based on the --profile option to the
191 * "cloud" command. The option is not mandatory if only a single
192 * profile exists.
193 */
194static int
195selectCloudProfile(ComPtr<ICloudProfile> &pProfile,
196 const ComPtr<ICloudProvider> &pProvider,
197 const char *pcszProfileName)
198{
199 HRESULT hrc;
200
201 /*
202 * If the profile is explicitly specified, just look it up and
203 * return.
204 */
205 if (pcszProfileName != NULL)
206 {
207 CHECK_ERROR2_RET(hrc, pProvider,
208 GetProfileByName(com::Bstr(pcszProfileName).raw(),
209 pProfile.asOutParam()),
210 VERR_NOT_FOUND);
211
212 return VINF_SUCCESS;
213 }
214
215
216 /*
217 * If the user has just one profile for this provider, don't force
218 * them to specify it. I'm not entirely sure about this one,
219 * actually. It's nice for interactive use, but it might be not
220 * forward compatible if used in a script and then when another
221 * profile is created the script starts failing. I'd say, give
222 * them enough rope...
223 */
224 com::SafeIfaceArray<ICloudProfile> aProfiles;
225 CHECK_ERROR2_RET(hrc, pProvider,
226 COMGETTER(Profiles)(ComSafeArrayAsOutParam(aProfiles)),
227 VERR_GENERAL_FAILURE);
228
229 if (aProfiles.size() == 0)
230 {
231 RTMsgError(CloudMachine::tr("cloud: no profiles exist"));
232 return VERR_NOT_FOUND;
233 }
234
235 if (aProfiles.size() > 1)
236 {
237 RTMsgError(CloudMachine::tr("cloud: multiple profiles exist,"
238 " '--profile' option is required"));
239 return VERR_MISSING;
240 }
241
242 /* Do RTMsgInfo telling the user which one was selected? */
243 pProfile = aProfiles[0];
244 return VINF_SUCCESS;
245}
246
247
248static int
249getCloudClient(CMachineHandlerArg &a,
250 const char *pcszProviderName,
251 const char *pcszProfileName)
252{
253 HRESULT hrc;
254 int rc;
255
256 ComPtr<ICloudProvider> pProvider;
257 rc = selectCloudProvider(pProvider, a.virtualBox, pcszProviderName);
258 if (RT_FAILURE(rc))
259 return rc;
260
261 ComPtr<ICloudProfile> pProfile;
262 rc = selectCloudProfile(pProfile, pProvider, pcszProfileName);
263 if (RT_FAILURE(rc))
264 return rc;
265
266 ComPtr<ICloudClient> pCloudClient;
267 CHECK_ERROR2_RET(hrc, pProfile,
268 CreateCloudClient(pCloudClient.asOutParam()),
269 VERR_GENERAL_FAILURE);
270
271 a.pClient = pCloudClient;
272 return VINF_SUCCESS;
273}
274
275
276static HRESULT
277getMachineList(com::SafeIfaceArray<ICloudMachine> &aMachines,
278 const ComPtr<ICloudClient> &pClient)
279{
280 HRESULT hrc;
281
282 ComPtr<IProgress> pListProgress;
283 CHECK_ERROR2_RET(hrc, pClient,
284 ReadCloudMachineList(pListProgress.asOutParam()),
285 hrc);
286
287 hrc = showProgress(pListProgress, SHOW_PROGRESS_NONE);
288 if (FAILED(hrc))
289 return hrc;
290
291 CHECK_ERROR2_RET(hrc, pClient,
292 COMGETTER(CloudMachineList)(ComSafeArrayAsOutParam(aMachines)),
293 hrc);
294
295 return S_OK;
296}
297
298
299static HRESULT
300getMachineById(CMachineHandlerArg *a)
301{
302 HRESULT hrc;
303
304 ComPtr<ICloudMachine> pMachine;
305 CHECK_ERROR2_RET(hrc, a->pClient,
306 GetCloudMachine(com::Bstr(a->pcszSpec).raw(),
307 pMachine.asOutParam()), hrc);
308
309 ComPtr<IProgress> pRefreshProgress;
310 CHECK_ERROR2_RET(hrc, pMachine,
311 Refresh(pRefreshProgress.asOutParam()), hrc);
312
313 hrc = showProgress(pRefreshProgress, SHOW_PROGRESS_NONE);
314 if (FAILED(hrc))
315 return hrc;
316
317 a->pMachine = pMachine;
318 return S_OK;
319}
320
321
322static HRESULT
323getMachineByName(CMachineHandlerArg *a)
324{
325 HRESULT hrc;
326
327 com::SafeIfaceArray<ICloudMachine> aMachines;
328 hrc = getMachineList(aMachines, a->pClient);
329 if (FAILED(hrc))
330 return hrc;
331
332 const size_t cMachines = aMachines.size();
333 if (cMachines == 0)
334 return VBOX_E_OBJECT_NOT_FOUND;
335
336 ComPtr<ICloudMachine> pMachineFound;
337 for (size_t i = 0; i < cMachines; ++i)
338 {
339 const ComPtr<ICloudMachine> pMachine = aMachines[i];
340
341 com::Bstr bstrName;
342 CHECK_ERROR2_RET(hrc, pMachine,
343 COMGETTER(Name)(bstrName.asOutParam()),
344 hrc);
345
346 if (!bstrName.equals(a->pcszSpec))
347 continue;
348
349 if (pMachineFound.isNull())
350 {
351 pMachineFound = pMachine;
352 }
353 else
354 {
355 com::Bstr bstrId1, bstrId2;
356 CHECK_ERROR2_RET(hrc, pMachineFound,
357 COMGETTER(Id)(bstrId1.asOutParam()),
358 hrc);
359 CHECK_ERROR2_RET(hrc, pMachine,
360 COMGETTER(Id)(bstrId2.asOutParam()),
361 hrc);
362
363 RTMsgError(CloudMachine::tr("ambiguous name: %ls and %ls"), bstrId1.raw(), bstrId2.raw());
364 return VBOX_E_OBJECT_NOT_FOUND;
365 }
366 }
367
368 if (pMachineFound.isNull())
369 return VBOX_E_OBJECT_NOT_FOUND;
370
371 a->pMachine = pMachineFound;
372 return S_OK;
373}
374
375
376/*
377 * Try to find the machine refered by pcszWhatever. If the look up by
378 * id fails we might want to fallback to look up by name, b/c someone
379 * might want to use a uuid as a display name of a machine. But cloud
380 * lookups are not fast, so that would be incurring performance
381 * penalty for typos or for machines that are gone. Should provide
382 * explicit --id/--name options instead.
383 */
384static HRESULT
385getMachineByGuess(CMachineHandlerArg *a)
386{
387 HRESULT hrc;
388
389 RTUUID Uuid;
390 int rc = RTUuidFromStr(&Uuid, a->pcszSpec);
391 if (RT_SUCCESS(rc))
392 hrc = getMachineById(a);
393 else
394 hrc = getMachineByName(a);
395
396 if (FAILED(hrc))
397 return hrc;
398
399 return S_OK;
400}
401
402
403
404/*
405 * RTGETOPTINIT_FLAGS_NO_STD_OPTS recognizes both --help and --version
406 * and we don't want the latter. It's easier to add one line of this
407 * macro to the s_aOptions initializers than to filter out --version.
408 */
409#define CLOUD_MACHINE_RTGETOPTDEF_HELP \
410 { "--help", 'h', RTGETOPT_REQ_NOTHING }, \
411 { "-help", 'h', RTGETOPT_REQ_NOTHING }, \
412 { "help", 'h', RTGETOPT_REQ_NOTHING }, \
413 { "-?", 'h', RTGETOPT_REQ_NOTHING }
414
415static RTEXITCODE
416errThereCanBeOnlyOne()
417{
418 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
419 CloudMachine::tr("only one machine can be specified"));
420}
421
422
423#define CLOUD_MACHINE_RTGETOPTDEF_MACHINE \
424 { "--id", 'i', RTGETOPT_REQ_STRING }, \
425 { "--name", 'n', RTGETOPT_REQ_STRING }
426
427
428/*
429 * Almost all the cloud machine commands take a machine argument, so
430 * factor out the code to fish it out from the command line.
431 *
432 * ch - option should be processed by the caller.
433 * VINF_SUCCESS - option was processed.
434 * VERR_PARSE_ERROR - RTEXITCODE_SYNTAX
435 * Other IPRT errors - RTEXITCODE_FAILURE
436 */
437static int
438checkMachineSpecArgument(CMachineHandlerArg *a,
439 int ch, const RTGETOPTUNION &Val)
440{
441 int rc;
442
443 switch (ch)
444 {
445 /*
446 * Note that we don't used RTGETOPT_REQ_UUID here as it would
447 * be too limiting. First, we need the original string for
448 * the API call, not the UUID, and second, if the UUID has bad
449 * forward RTGetOptPrintError doesn't have access to the
450 * option argument for the error message. So do the format
451 * check ourselves.
452 */
453 case 'i': /* --id */
454 {
455 const char *pcszId = Val.psz;
456
457 if (a->pcszSpec != NULL)
458 {
459 errThereCanBeOnlyOne();
460 return VERR_PARSE_ERROR;
461 }
462
463 RTUUID Uuid;
464 rc = RTUuidFromStr(&Uuid, pcszId);
465 if (RT_FAILURE(rc))
466 {
467 RTMsgError(CloudMachine::tr("not a valid uuid: %s"), pcszId);
468 return VERR_PARSE_ERROR;
469 }
470
471 a->pcszSpec = pcszId;
472 a->enmSpecKind = CMachineHandlerArg::ID;
473 return VINF_SUCCESS;
474 }
475
476 case 'n': /* --name */
477 {
478 const char *pcszName = Val.psz;
479
480 if (a->pcszSpec != NULL)
481 {
482 errThereCanBeOnlyOne();
483 return VERR_PARSE_ERROR;
484 }
485
486 a->pcszSpec = pcszName;
487 a->enmSpecKind = CMachineHandlerArg::NAME;
488 return VINF_SUCCESS;
489 }
490
491 /*
492 * Plain word (no dash/es). This must name a machine, though
493 * we have to guess whether it's an id or a name.
494 */
495 case VINF_GETOPT_NOT_OPTION:
496 {
497 const char *pcszNameOrId = Val.psz;
498
499 if (a->pcszSpec != NULL)
500 {
501 errThereCanBeOnlyOne();
502 return VERR_PARSE_ERROR;
503 }
504
505 a->pcszSpec = pcszNameOrId;
506 a->enmSpecKind = CMachineHandlerArg::GUESS;
507 return VINF_SUCCESS;
508 }
509
510 /* might as well do it here */
511 case 'h': /* --help */
512 {
513 printHelp(g_pStdOut);
514 return VINF_CALLBACK_RETURN;
515 }
516 }
517
518 /* let the caller deal with it */
519 return VINF_NOT_SUPPORTED;
520}
521
522
523static HRESULT
524getMachineBySpec(CMachineHandlerArg *a)
525{
526 HRESULT hrc = E_FAIL;
527
528 if (a->pcszSpec == NULL)
529 {
530 RTMsgErrorExit(RTEXITCODE_SYNTAX, CloudMachine::tr("machine not specified"));
531 return E_FAIL;
532 }
533
534 if (a->pcszSpec[0] == '\0')
535 {
536 RTMsgError(CloudMachine::tr("machine name is empty"));
537 return E_FAIL;
538 }
539
540 switch (a->enmSpecKind)
541 {
542 case CMachineHandlerArg::ID:
543 hrc = getMachineById(a);
544 if (FAILED(hrc))
545 {
546 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
547 RTMsgError(CloudMachine::tr("unable to find machine with id %s"), a->pcszSpec);
548 return hrc;
549 }
550 break;
551
552 case CMachineHandlerArg::NAME:
553 hrc = getMachineByName(a);
554 if (FAILED(hrc))
555 {
556 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
557 RTMsgError(CloudMachine::tr("unable to find machine with name %s"), a->pcszSpec);
558 return hrc;
559 }
560 break;
561
562 case CMachineHandlerArg::GUESS:
563 hrc = getMachineByGuess(a);
564 if (FAILED(hrc))
565 {
566 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
567 RTMsgError(CloudMachine::tr("unable to find machine %s"), a->pcszSpec);
568 return hrc;
569 }
570 break;
571 }
572
573 /* switch was exhaustive (and successful) */
574 AssertReturn(SUCCEEDED(hrc), E_FAIL);
575 return S_OK;
576}
577
578
579
580
581/*
582 * cloud machine [--id id | --name name] command ...
583 *
584 * We allow machine to be specified after "machine" but only with an
585 * explicit option for the obvious reason. We will also check for
586 * these options and machine spec as a plain words argument after the
587 * command word, so user can use either of:
588 *
589 * cloud machine --name foo start
590 * cloud machine start --name foo
591 * cloud machine start foo
592 *
593 * This will accept e.g. cloud machine --name foo list ... b/c we
594 * don't yet know that it's "list" that is coming, so commands that
595 * don't take machine argument check that separately when called. One
596 * side effect of this is that specifying several machines or using a
597 * syntactically invalid id will be reported as such, not as an
598 * unknown option, but that's a relatively minor nit.
599 */
600static RTEXITCODE
601handleCloudMachineImpl(CMachineHandlerArg *a, int iFirst)
602{
603 int rc;
604
605 enum
606 {
607 kMachineIota = 1000,
608 kMachine_ConsoleHistory,
609 kMachine_Info,
610 kMachine_List,
611 kMachine_Powerdown,
612 kMachine_Reboot,
613 kMachine_Shutdown,
614 kMachine_Start,
615 kMachine_Terminate,
616 };
617
618 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE);
619 static const RTGETOPTDEF s_aOptions[] =
620 {
621 { "console-history", kMachine_ConsoleHistory, RTGETOPT_REQ_NOTHING },
622 { "consolehistory", kMachine_ConsoleHistory, RTGETOPT_REQ_NOTHING },
623 { "info", kMachine_Info, RTGETOPT_REQ_NOTHING },
624 { "list", kMachine_List, RTGETOPT_REQ_NOTHING },
625 { "powerdown", kMachine_Powerdown, RTGETOPT_REQ_NOTHING },
626 { "reboot", kMachine_Reboot, RTGETOPT_REQ_NOTHING },
627 { "shutdown", kMachine_Shutdown, RTGETOPT_REQ_NOTHING },
628 { "start", kMachine_Start, RTGETOPT_REQ_NOTHING },
629 { "terminate", kMachine_Terminate, RTGETOPT_REQ_NOTHING },
630 CLOUD_MACHINE_RTGETOPTDEF_MACHINE,
631 CLOUD_MACHINE_RTGETOPTDEF_HELP
632 };
633
634 RTGETOPTSTATE OptState;
635 rc = RTGetOptInit(&OptState, a->argc, a->argv,
636 s_aOptions, RT_ELEMENTS(s_aOptions),
637 iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
638 AssertRCReturn(rc, RTMsgErrorExit(RTEXITCODE_INIT,
639 CloudMachine::tr("cloud machine: RTGetOptInit: %Rra"), rc));
640
641 int ch;
642 RTGETOPTUNION Val;
643 while ((ch = RTGetOpt(&OptState, &Val)) != 0)
644 {
645 if (RT_FAILURE(ch))
646 return RTGetOptPrintError(ch, &Val);
647
648 /*
649 * Check for an unknown word first: checkMachineSpecArgument()
650 * would try to interpret that as a machine id/name.
651 */
652 if (ch == VINF_GETOPT_NOT_OPTION)
653 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
654 CloudMachine::tr("Invalid sub-command: %s"), Val.psz);
655
656 /*
657 * Allow --id/--name after "machine", before the command.
658 * Also handles --help.
659 */
660 rc = checkMachineSpecArgument(a, ch, Val);
661 if (rc == VINF_SUCCESS)
662 continue;
663 else if (rc == VINF_CALLBACK_RETURN)
664 return RTEXITCODE_SUCCESS;
665 else if (rc == VERR_PARSE_ERROR)
666 return RTEXITCODE_SYNTAX;
667
668 /*
669 * Dispatch to command implementation ([ab]use getopt to do
670 * string comparisons for us).
671 */
672 switch (ch)
673 {
674 case kMachine_ConsoleHistory:
675 return handleCloudMachineConsoleHistory(a, OptState.iNext);
676
677 case kMachine_Info:
678 return handleCloudMachineInfo(a, OptState.iNext);
679
680 case kMachine_List:
681 return listCloudMachinesImpl(a, OptState.iNext);
682
683 case kMachine_Powerdown:
684 return handleCloudMachinePowerdown(a, OptState.iNext);
685
686 case kMachine_Reboot:
687 return handleCloudMachineReboot(a, OptState.iNext);
688
689 case kMachine_Shutdown:
690 return handleCloudMachineShutdown(a, OptState.iNext);
691
692 case kMachine_Start:
693 return handleCloudMachineStart(a, OptState.iNext);
694
695 case kMachine_Terminate:
696 return handleCloudMachineTerminate(a, OptState.iNext);
697
698 default: /* should never happen */
699 return RTMsgErrorExit(RTEXITCODE_INIT,
700 CloudMachine::tr("cloud machine: internal error: %d"), ch);
701 }
702 }
703
704 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
705 CloudMachine::tr("cloud machine: command required\n"
706 "Try '--help' for more information."));
707}
708
709
710/*
711 * cloud list machines
712 *
713 * The "cloud list" prefix handling is in VBoxManageCloud.cpp, so this
714 * function is not static. See handleCloudMachine() for the
715 * explanation early provider/profile lookup.
716 */
717RTEXITCODE
718listCloudMachines(HandlerArg *a, int iFirst,
719 const char *pcszProviderName,
720 const char *pcszProfileName)
721{
722 CMachineHandlerArg handlerArg(*a);
723 int rc = getCloudClient(handlerArg, pcszProviderName, pcszProfileName);
724 if (RT_FAILURE(rc))
725 return RTEXITCODE_FAILURE;
726
727 return listCloudMachinesImpl(&handlerArg, iFirst);
728}
729
730
731/*
732 * cloud machine list # convenience alias
733 * cloud list machines # see above
734 */
735static RTEXITCODE
736listCloudMachinesImpl(CMachineHandlerArg *a, int iFirst)
737{
738 HRESULT hrc;
739 int rc;
740
741 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_LIST);
742 static const RTGETOPTDEF s_aOptions[] =
743 {
744 { "--long", 'l', RTGETOPT_REQ_NOTHING },
745 { "--sort", 's', RTGETOPT_REQ_NOTHING },
746 CLOUD_MACHINE_RTGETOPTDEF_HELP
747 };
748
749 enum kFormatEnum { kFormat_Short, kFormat_Long };
750 kFormatEnum enmFormat = kFormat_Short;
751
752 enum kSortOrderEnum { kSortOrder_None, kSortOrder_Name, kSortOrder_Id };
753 kSortOrderEnum enmSortOrder = kSortOrder_None;
754
755 if (a->pcszSpec != NULL)
756 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
757 CloudMachine::tr("cloud machine list: unexpected machine argument"));
758
759
760 RTGETOPTSTATE OptState;
761 rc = RTGetOptInit(&OptState, a->argc, a->argv,
762 s_aOptions, RT_ELEMENTS(s_aOptions),
763 iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
764 AssertRCReturn(rc, RTMsgErrorExit(RTEXITCODE_INIT,
765 CloudMachine::tr("cloud machine list: RTGetOptInit: %Rra"), rc));
766
767 int ch;
768 RTGETOPTUNION Val;
769 while ((ch = RTGetOpt(&OptState, &Val)) != 0)
770 {
771 switch (ch)
772 {
773 case 'l':
774 enmFormat = kFormat_Long;
775 break;
776
777 case 's':
778 /** @todo optional argument to select the sort key? */
779 enmSortOrder = kSortOrder_Name;
780 break;
781
782 case 'h': /* --help */
783 printHelp(g_pStdOut);
784 return RTEXITCODE_SUCCESS;
785
786
787 case VINF_GETOPT_NOT_OPTION:
788 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
789 CloudMachine::tr("Invalid sub-command: %s"), Val.psz);
790
791 default:
792 return RTGetOptPrintError(ch, &Val);
793 }
794 }
795
796 com::SafeIfaceArray<ICloudMachine> aMachines;
797 hrc = getMachineList(aMachines, a->pClient);
798 if (FAILED(hrc))
799 return RTEXITCODE_FAILURE;
800
801 const size_t cMachines = aMachines.size();
802 if (cMachines == 0)
803 return RTEXITCODE_SUCCESS;
804
805
806 /*
807 * Get names/ids that we need for the short output and to sort the
808 * list.
809 */
810 std::vector<ComPtr<ICloudMachine> > vMachines(cMachines);
811 std::vector<com::Bstr> vBstrNames(cMachines);
812 std::vector<com::Bstr> vBstrIds(cMachines);
813 for (size_t i = 0; i < cMachines; ++i)
814 {
815 vMachines[i] = aMachines[i];
816
817 CHECK_ERROR2_RET(hrc, vMachines[i],
818 COMGETTER(Name)(vBstrNames[i].asOutParam()),
819 RTEXITCODE_FAILURE);
820
821 CHECK_ERROR2_RET(hrc, vMachines[i],
822 COMGETTER(Id)(vBstrIds[i].asOutParam()),
823 RTEXITCODE_FAILURE);
824 }
825
826
827 /*
828 * Sort the list if necessary. The sort is indirect via an
829 * intermediate array of indexes.
830 */
831 std::vector<size_t> vIndexes(cMachines);
832 for (size_t i = 0; i < cMachines; ++i)
833 vIndexes[i] = i;
834
835 if (enmSortOrder != kSortOrder_None)
836 {
837 struct SortBy {
838 const std::vector<com::Bstr> &ks;
839 SortBy(const std::vector<com::Bstr> &aKeys) : ks(aKeys) {}
840 bool operator() (size_t l, size_t r) { return ks[l] < ks[r]; }
841 };
842
843 std::sort(vIndexes.begin(), vIndexes.end(),
844 SortBy(enmSortOrder == kSortOrder_Name
845 ? vBstrNames : vBstrIds));
846 }
847
848
849 if (enmFormat == kFormat_Short)
850 {
851 for (size_t i = 0; i < cMachines; ++i)
852 {
853 const size_t idx = vIndexes[i];
854 const com::Bstr &bstrId = vBstrIds[idx];
855 const com::Bstr &bstrName = vBstrNames[idx];
856
857 RTPrintf("%ls %ls\n", bstrId.raw(), bstrName.raw());
858 }
859 }
860 else // kFormat_Long
861 {
862 for (size_t i = 0; i < cMachines; ++i)
863 {
864 const size_t idx = vIndexes[i];
865 const ComPtr<ICloudMachine> &pMachine = vMachines[idx];
866
867 if (i != 0)
868 RTPrintf("\n");
869 printMachineInfo(pMachine);
870 }
871 }
872
873 return RTEXITCODE_SUCCESS;
874}
875
876
877/*
878 * cloud showvminfo "id"
879 *
880 * Alias for "cloud machine info" that tries to match the local vm
881 * counterpart.
882 */
883RTEXITCODE
884handleCloudShowVMInfo(HandlerArg *a, int iFirst,
885 const char *pcszProviderName,
886 const char *pcszProfileName)
887{
888 CMachineHandlerArg handlerArg(*a);
889 int rc = getCloudClient(handlerArg, pcszProviderName, pcszProfileName);
890 if (RT_FAILURE(rc))
891 return RTEXITCODE_FAILURE;
892
893 return handleCloudMachineInfo(&handlerArg, iFirst);
894}
895
896
897/*
898 * cloud machine info "id" ...
899 */
900static RTEXITCODE
901handleCloudMachineInfo(CMachineHandlerArg *a, int iFirst)
902{
903 HRESULT hrc;
904 int rc;
905
906 enum
907 {
908 kMachineInfoIota = 1000,
909 kMachineInfo_Details,
910 };
911
912 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_INFO);
913 static const RTGETOPTDEF s_aOptions[] =
914 {
915 { "--details", kMachineInfo_Details, RTGETOPT_REQ_NOTHING },
916 CLOUD_MACHINE_RTGETOPTDEF_MACHINE,
917 CLOUD_MACHINE_RTGETOPTDEF_HELP
918 };
919
920 RTGETOPTSTATE OptState;
921 rc = RTGetOptInit(&OptState, a->argc, a->argv,
922 s_aOptions, RT_ELEMENTS(s_aOptions),
923 iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
924 AssertRCReturn(rc, RTMsgErrorExit(RTEXITCODE_INIT,
925 "RTGetOptInit: %Rra", rc));
926
927 int ch;
928 RTGETOPTUNION Val;
929 while ((ch = RTGetOpt(&OptState, &Val)) != 0)
930 {
931 rc = checkMachineSpecArgument(a, ch, Val);
932 if (rc == VINF_SUCCESS)
933 continue;
934 else if (rc == VINF_CALLBACK_RETURN)
935 return RTEXITCODE_SUCCESS;
936 else if (rc == VERR_PARSE_ERROR)
937 return RTEXITCODE_SYNTAX;
938
939 switch (ch)
940 {
941 case kMachineInfo_Details:
942 /* currently no-op */
943 break;
944
945 default:
946 return RTGetOptPrintError(ch, &Val);
947 }
948 }
949
950 hrc = getMachineBySpec(a);
951 if (FAILED(hrc))
952 return RTEXITCODE_FAILURE;
953
954 /* end of boilerplate */
955
956
957 hrc = printMachineInfo(a->pMachine);
958 if (FAILED(hrc))
959 return RTEXITCODE_FAILURE;
960
961 return RTEXITCODE_SUCCESS;
962}
963
964
965static HRESULT
966printMachineInfo(const ComPtr<ICloudMachine> &pMachine)
967{
968 HRESULT hrc;
969
970 com::Bstr bstrId;
971 CHECK_ERROR2_RET(hrc, pMachine,
972 COMGETTER(Id)(bstrId.asOutParam()),
973 hrc);
974 RTPrintf("UUID: %ls\n", bstrId.raw());
975
976
977 /*
978 * Check if the machine is accessible and print the error
979 * message if not.
980 */
981 BOOL fAccessible = FALSE;
982 CHECK_ERROR2_RET(hrc, pMachine,
983 COMGETTER(Accessible)(&fAccessible), hrc);
984
985 if (!fAccessible)
986 {
987 RTMsgError(CloudMachine::tr("machine is not accessible")); // XXX: Id?
988
989 ComPtr<IVirtualBoxErrorInfo> pErrorInfo;
990 CHECK_ERROR2_RET(hrc, pMachine,
991 COMGETTER(AccessError)(pErrorInfo.asOutParam()), hrc);
992
993 while (!pErrorInfo.isNull())
994 {
995 com::Bstr bstrText;
996 CHECK_ERROR2_RET(hrc, pErrorInfo,
997 COMGETTER(Text)(bstrText.asOutParam()), hrc);
998 RTStrmPrintf(g_pStdErr, "%ls\n", bstrText.raw());
999
1000 CHECK_ERROR2_RET(hrc, pErrorInfo,
1001 COMGETTER(Next)(pErrorInfo.asOutParam()), hrc);
1002 }
1003
1004 return E_FAIL;
1005 }
1006
1007
1008 /*
1009 * The machine seems to be ok, print its details.
1010 */
1011 CloudMachineState_T enmState;
1012 CHECK_ERROR2_RET(hrc, pMachine,
1013 COMGETTER(State)(&enmState),
1014 hrc);
1015 switch (enmState) {
1016 case CloudMachineState_Invalid:
1017 RTPrintf(CloudMachine::tr("State: Invalid (%RU32)\n"), CloudMachineState_Invalid);
1018 break;
1019
1020 case CloudMachineState_Provisioning:
1021 RTPrintf(CloudMachine::tr("State: Provisioning (%RU32)\n"), CloudMachineState_Provisioning);
1022 break;
1023
1024 case CloudMachineState_Running:
1025 RTPrintf(CloudMachine::tr("State: Running (%RU32)\n"), CloudMachineState_Running);
1026 break;
1027
1028 case CloudMachineState_Starting:
1029 RTPrintf(CloudMachine::tr("State: Starting (%RU32)\n"), CloudMachineState_Starting);
1030 break;
1031
1032 case CloudMachineState_Stopping:
1033 RTPrintf(CloudMachine::tr("State: Stopping (%RU32)\n"), CloudMachineState_Stopping);
1034 break;
1035
1036 case CloudMachineState_Stopped:
1037 RTPrintf(CloudMachine::tr("State: Stopped (%RU32)\n"), CloudMachineState_Stopped);
1038 break;
1039
1040 case CloudMachineState_CreatingImage:
1041 RTPrintf(CloudMachine::tr("State: CreatingImage (%RU32)\n"), CloudMachineState_CreatingImage);
1042 break;
1043
1044 case CloudMachineState_Terminating:
1045 RTPrintf(CloudMachine::tr("State: Terminating (%RU32)\n"), CloudMachineState_Terminating);
1046 break;
1047
1048 case CloudMachineState_Terminated:
1049 RTPrintf(CloudMachine::tr("State: Terminated (%RU32)\n"), CloudMachineState_Terminated);
1050 break;
1051
1052 default:
1053 RTPrintf(CloudMachine::tr("State: Unknown state (%RU32)\n"), enmState);
1054 }
1055
1056 ComPtr<IForm> pDetails;
1057 CHECK_ERROR2_RET(hrc, pMachine,
1058 GetDetailsForm(pDetails.asOutParam()), hrc);
1059
1060 if (RT_UNLIKELY(pDetails.isNull()))
1061 {
1062 RTMsgError(CloudMachine::tr("null details")); /* better error message? */
1063 return E_FAIL;
1064 }
1065
1066 com::SafeIfaceArray<IFormValue> aValues;
1067 CHECK_ERROR2_RET(hrc, pDetails,
1068 COMGETTER(Values)(ComSafeArrayAsOutParam(aValues)), hrc);
1069 for (size_t i = 0; i < aValues.size(); ++i)
1070 {
1071 hrc = printFormValue(aValues[i]);
1072 if (FAILED(hrc))
1073 return hrc;
1074 }
1075
1076 return S_OK;
1077}
1078
1079
1080static HRESULT
1081printFormValue(const ComPtr<IFormValue> &pValue)
1082{
1083 HRESULT hrc;
1084
1085 BOOL fVisible = FALSE;
1086 CHECK_ERROR2_RET(hrc, pValue,
1087 COMGETTER(Visible)(&fVisible), hrc);
1088 if (!fVisible)
1089 return S_OK;
1090
1091
1092 com::Bstr bstrLabel;
1093 CHECK_ERROR2_RET(hrc, pValue,
1094 COMGETTER(Label)(bstrLabel.asOutParam()), hrc);
1095
1096 FormValueType_T enmType;
1097 CHECK_ERROR2_RET(hrc, pValue,
1098 COMGETTER(Type)(&enmType), hrc);
1099
1100 switch (enmType)
1101 {
1102 case FormValueType_Boolean:
1103 {
1104 ComPtr<IBooleanFormValue> pBoolValue;
1105 hrc = pValue.queryInterfaceTo(pBoolValue.asOutParam());
1106 if (FAILED(hrc))
1107 {
1108 RTStrmPrintf(g_pStdErr,
1109 CloudMachine::tr("%ls: unable to convert to boolean value\n"),
1110 bstrLabel.raw());
1111 break;
1112 }
1113
1114 BOOL fSelected;
1115 hrc = pBoolValue->GetSelected(&fSelected);
1116 if (FAILED(hrc))
1117 {
1118 RTStrmPrintf(g_pStdOut,
1119 "%ls: %Rhra", bstrLabel.raw(), hrc);
1120 break;
1121 }
1122
1123 RTPrintf("%ls: %RTbool\n",
1124 bstrLabel.raw(), RT_BOOL(fSelected));
1125 break;
1126 }
1127
1128 case FormValueType_String:
1129 {
1130 ComPtr<IStringFormValue> pStrValue;
1131 hrc = pValue.queryInterfaceTo(pStrValue.asOutParam());
1132 if (FAILED(hrc))
1133 {
1134 RTStrmPrintf(g_pStdErr,
1135 CloudMachine::tr("%ls: unable to convert to string value\n"),
1136 bstrLabel.raw());
1137 break;
1138 }
1139
1140 /*
1141 * GUI hack: if clipboard string is set, it contains
1142 * untruncated long value, usually full OCID, so check it
1143 * first. Make this selectable with an option?
1144 */
1145 com::Bstr bstrValue;
1146 hrc = pStrValue->COMGETTER(ClipboardString)(bstrValue.asOutParam());
1147 if (FAILED(hrc))
1148 {
1149 RTStrmPrintf(g_pStdOut,
1150 "%ls: %Rhra", bstrLabel.raw(), hrc);
1151 break;
1152 }
1153
1154 if (bstrValue.isEmpty())
1155 {
1156 hrc = pStrValue->GetString(bstrValue.asOutParam());
1157 if (FAILED(hrc))
1158 {
1159 RTStrmPrintf(g_pStdOut,
1160 "%ls: %Rhra", bstrLabel.raw(), hrc);
1161 break;
1162 }
1163 }
1164
1165 RTPrintf("%ls: %ls\n",
1166 bstrLabel.raw(), bstrValue.raw());
1167 break;
1168 }
1169
1170 case FormValueType_RangedInteger:
1171 {
1172 ComPtr<IRangedIntegerFormValue> pIntValue;
1173 hrc = pValue.queryInterfaceTo(pIntValue.asOutParam());
1174 if (FAILED(hrc))
1175 {
1176 RTStrmPrintf(g_pStdErr,
1177 CloudMachine::tr("%ls: unable to convert to integer value\n"),
1178 bstrLabel.raw());
1179 break;
1180 }
1181
1182 LONG lValue;
1183 hrc = pIntValue->GetInteger(&lValue);
1184 if (FAILED(hrc))
1185 {
1186 RTStrmPrintf(g_pStdOut,
1187 "%ls: %Rhra", bstrLabel.raw(), hrc);
1188 break;
1189 }
1190
1191 RTPrintf("%ls: %RI64\n",
1192 bstrLabel.raw(), (int64_t)lValue);
1193 break;
1194 }
1195
1196 case FormValueType_Choice:
1197 {
1198 ComPtr<IChoiceFormValue> pChoiceValue;
1199 hrc = pValue.queryInterfaceTo(pChoiceValue.asOutParam());
1200 if (FAILED(hrc))
1201 {
1202 RTStrmPrintf(g_pStdErr,
1203 CloudMachine::tr("%ls: unable to convert to choice value\n"),
1204 bstrLabel.raw());
1205 break;
1206 }
1207
1208 com::SafeArray<BSTR> aValues;
1209 hrc = pChoiceValue->COMGETTER(Values)(ComSafeArrayAsOutParam(aValues));
1210 if (FAILED(hrc))
1211 {
1212 RTStrmPrintf(g_pStdOut,
1213 CloudMachine::tr("%ls: values: %Rhra"),
1214 bstrLabel.raw(), hrc);
1215 break;
1216 }
1217
1218 LONG idxSelected = -1;
1219 hrc = pChoiceValue->GetSelectedIndex(&idxSelected);
1220 if (FAILED(hrc))
1221 {
1222 RTStrmPrintf(g_pStdOut,
1223 CloudMachine::tr("%ls: selectedIndex: %Rhra"),
1224 bstrLabel.raw(), hrc);
1225 break;
1226 }
1227
1228 if (idxSelected < 0 || (size_t)idxSelected > aValues.size())
1229 {
1230 RTStrmPrintf(g_pStdOut,
1231 CloudMachine::tr("%ls: selected index %RI64 out of range [0, %zu)\n"),
1232 bstrLabel.raw(), (int64_t)idxSelected, aValues.size());
1233 break;
1234 }
1235
1236 RTPrintf("%ls: %ls\n",
1237 bstrLabel.raw(), aValues[idxSelected]);
1238 break;
1239 }
1240
1241 default:
1242 {
1243 RTStrmPrintf(g_pStdOut, CloudMachine::tr("unknown value type %RU32\n"), enmType);
1244 break;
1245 }
1246 }
1247
1248 return S_OK;
1249}
1250
1251
1252/*
1253 * Boilerplate code to get machine by name/id from the arguments.
1254 * Shared by action subcommands b/c they currently don't have any
1255 * extra options (but we can't use this for e.g. "info" that has
1256 * --details).
1257 */
1258static RTEXITCODE
1259getMachineFromArgs(CMachineHandlerArg *a, int iFirst)
1260{
1261 HRESULT hrc;
1262 int rc;
1263
1264 static const RTGETOPTDEF s_aOptions[] =
1265 {
1266 CLOUD_MACHINE_RTGETOPTDEF_MACHINE,
1267 CLOUD_MACHINE_RTGETOPTDEF_HELP
1268 };
1269
1270 RTGETOPTSTATE OptState;
1271 rc = RTGetOptInit(&OptState, a->argc, a->argv,
1272 s_aOptions, RT_ELEMENTS(s_aOptions),
1273 iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1274 AssertRCStmt(rc,
1275 return RTMsgErrorExit(RTEXITCODE_INIT, /* internal error */
1276 "RTGetOptInit: %Rra", rc));
1277
1278 int ch;
1279 RTGETOPTUNION Val;
1280 while ((ch = RTGetOpt(&OptState, &Val)) != 0)
1281 {
1282 rc = checkMachineSpecArgument(a, ch, Val);
1283 if (rc == VINF_SUCCESS)
1284 continue;
1285 else if (rc == VINF_CALLBACK_RETURN)
1286 return RTEXITCODE_SUCCESS;
1287 else if (rc == VERR_PARSE_ERROR)
1288 return RTEXITCODE_SYNTAX;
1289
1290 switch (ch)
1291 {
1292 /* no other options currently */
1293 default:
1294 return RTGetOptPrintError(ch, &Val);
1295 }
1296 }
1297
1298 hrc = getMachineBySpec(a);
1299 if (FAILED(hrc))
1300 return RTEXITCODE_FAILURE;
1301
1302 return RTEXITCODE_SUCCESS;
1303}
1304
1305
1306/*
1307 * cloud machine start "id"
1308 */
1309static RTEXITCODE
1310handleCloudMachineStart(CMachineHandlerArg *a, int iFirst)
1311{
1312 HRESULT hrc;
1313
1314 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_START);
1315 RTEXITCODE status = getMachineFromArgs(a, iFirst);
1316 if (status != RTEXITCODE_SUCCESS)
1317 return status;
1318
1319
1320 ComPtr<IProgress> pProgress;
1321 CHECK_ERROR2_RET(hrc, a->pMachine,
1322 PowerUp(pProgress.asOutParam()),
1323 RTEXITCODE_FAILURE);
1324
1325 hrc = showProgress(pProgress, SHOW_PROGRESS_NONE);
1326 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1327}
1328
1329
1330/*
1331 * cloud machine reboot "id"
1332 * "Press" ACPI power button, then power the instance back up.
1333 */
1334static RTEXITCODE
1335handleCloudMachineReboot(CMachineHandlerArg *a, int iFirst)
1336{
1337 HRESULT hrc;
1338
1339 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_REBOOT);
1340 RTEXITCODE status = getMachineFromArgs(a, iFirst);
1341 if (status != RTEXITCODE_SUCCESS)
1342 return status;
1343
1344
1345 ComPtr<IProgress> pProgress;
1346 CHECK_ERROR2_RET(hrc, a->pMachine,
1347 Reboot(pProgress.asOutParam()),
1348 RTEXITCODE_FAILURE);
1349
1350 hrc = showProgress(pProgress, SHOW_PROGRESS_NONE);
1351 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1352}
1353
1354
1355/*
1356 * cloud machine shutdown "id"
1357 * "Press" ACPI power button.
1358 */
1359static RTEXITCODE
1360handleCloudMachineShutdown(CMachineHandlerArg *a, int iFirst)
1361{
1362 HRESULT hrc;
1363
1364 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_SHUTDOWN);
1365 RTEXITCODE status = getMachineFromArgs(a, iFirst);
1366 if (status != RTEXITCODE_SUCCESS)
1367 return status;
1368
1369
1370 ComPtr<IProgress> pProgress;
1371 CHECK_ERROR2_RET(hrc, a->pMachine,
1372 Shutdown(pProgress.asOutParam()),
1373 RTEXITCODE_FAILURE);
1374
1375 hrc = showProgress(pProgress, SHOW_PROGRESS_NONE);
1376 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1377}
1378
1379
1380/*
1381 * cloud machine powerdown "id"
1382 * Yank the power cord.
1383 */
1384static RTEXITCODE
1385handleCloudMachinePowerdown(CMachineHandlerArg *a, int iFirst)
1386{
1387 HRESULT hrc;
1388
1389 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_POWERDOWN);
1390 RTEXITCODE status = getMachineFromArgs(a, iFirst);
1391 if (status != RTEXITCODE_SUCCESS)
1392 return status;
1393
1394
1395 ComPtr<IProgress> pProgress;
1396 CHECK_ERROR2_RET(hrc, a->pMachine,
1397 PowerDown(pProgress.asOutParam()),
1398 RTEXITCODE_FAILURE);
1399
1400 hrc = showProgress(pProgress, SHOW_PROGRESS_NONE);
1401 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1402}
1403
1404
1405/*
1406 * cloud machine terminate "id"
1407 * Discard the instance running this machine.
1408 */
1409static RTEXITCODE
1410handleCloudMachineTerminate(CMachineHandlerArg *a, int iFirst)
1411{
1412 HRESULT hrc;
1413
1414 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_TERMINATE);
1415 RTEXITCODE status = getMachineFromArgs(a, iFirst);
1416 if (status != RTEXITCODE_SUCCESS)
1417 return status;
1418
1419
1420 ComPtr<IProgress> pProgress;
1421 CHECK_ERROR2_RET(hrc, a->pMachine,
1422 Terminate(pProgress.asOutParam()),
1423 RTEXITCODE_FAILURE);
1424
1425 hrc = showProgress(pProgress, SHOW_PROGRESS_NONE);
1426 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1427}
1428
1429
1430/*
1431 * cloud machine console-history "id"
1432 */
1433static RTEXITCODE
1434handleCloudMachineConsoleHistory(CMachineHandlerArg *a, int iFirst)
1435{
1436 HRESULT hrc;
1437
1438 // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_CONSOLEHISTORY);
1439 RTEXITCODE status = getMachineFromArgs(a, iFirst);
1440 if (status != RTEXITCODE_SUCCESS)
1441 return status;
1442
1443
1444 ComPtr<IDataStream> pHistoryStream;
1445 ComPtr<IProgress> pHistoryProgress;
1446 CHECK_ERROR2_RET(hrc, a->pMachine,
1447 GetConsoleHistory(pHistoryStream.asOutParam(),
1448 pHistoryProgress.asOutParam()),
1449 RTEXITCODE_FAILURE);
1450
1451 hrc = showProgress(pHistoryProgress, SHOW_PROGRESS_NONE);
1452 if (FAILED(hrc))
1453 return RTEXITCODE_FAILURE;
1454
1455 bool fEOF = false;
1456 while (!fEOF)
1457 {
1458 com::SafeArray<BYTE> aChunk;
1459 CHECK_ERROR2_RET(hrc, pHistoryStream,
1460 Read(64 *_1K, 0, ComSafeArrayAsOutParam(aChunk)),
1461 RTEXITCODE_FAILURE);
1462 if (aChunk.size() == 0)
1463 break;
1464
1465 RTStrmWrite(g_pStdOut, aChunk.raw(), aChunk.size());
1466 }
1467
1468 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1469}
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