VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

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