VirtualBox

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

Last change on this file since 98298 was 98298, checked in by vboxsync, 16 months ago

VBoxManage: rc -> vrc/hrc. Make scm check. bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use