VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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