VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp@ 100240

Last change on this file since 100240 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.1 KB
Line 
1/* $Id: VBoxManageSnapshot.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * VBoxManage - The 'snapshot' command.
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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <VBox/com/com.h>
33#include <VBox/com/string.h>
34#include <VBox/com/array.h>
35#include <VBox/com/ErrorInfo.h>
36#include <VBox/com/errorprint.h>
37
38#include <VBox/com/VirtualBox.h>
39
40#include <iprt/getopt.h>
41#include <iprt/stream.h>
42#include <iprt/time.h>
43
44#include "VBoxManage.h"
45using namespace com;
46
47DECLARE_TRANSLATION_CONTEXT(Snapshot);
48
49/**
50 * Helper function used with "VBoxManage snapshot ... dump". Gets called to find the
51 * snapshot in the machine's snapshot tree that uses a particular diff image child of
52 * a medium.
53 * Horribly inefficient since we keep re-querying the snapshots tree for each image,
54 * but this is for quick debugging only.
55 * @param pMedium
56 * @param pThisSnapshot
57 * @param pCurrentSnapshot
58 * @param uMediumLevel
59 * @param uSnapshotLevel
60 * @return
61 */
62static bool FindAndPrintSnapshotUsingMedium(ComPtr<IMedium> &pMedium,
63 ComPtr<ISnapshot> &pThisSnapshot,
64 ComPtr<ISnapshot> &pCurrentSnapshot,
65 uint32_t uMediumLevel,
66 uint32_t uSnapshotLevel)
67{
68 HRESULT hrc;
69
70 do
71 {
72 // get snapshot machine so we can figure out which diff image this created
73 ComPtr<IMachine> pSnapshotMachine;
74 CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Machine)(pSnapshotMachine.asOutParam()));
75
76 // get media attachments
77 SafeIfaceArray<IMediumAttachment> aAttachments;
78 CHECK_ERROR_BREAK(pSnapshotMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
79
80 for (uint32_t i = 0;
81 i < aAttachments.size();
82 ++i)
83 {
84 ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
85 DeviceType_T type;
86 CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
87 if (type == DeviceType_HardDisk)
88 {
89 ComPtr<IMedium> pMediumInSnapshot;
90 CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pMediumInSnapshot.asOutParam()));
91
92 if (pMediumInSnapshot == pMedium)
93 {
94 // get snapshot name
95 Bstr bstrSnapshotName;
96 CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Name)(bstrSnapshotName.asOutParam()));
97
98 RTPrintf("%*s \"%ls\"%s\n",
99 50 + uSnapshotLevel * 2, "", // indent
100 bstrSnapshotName.raw(),
101 (pThisSnapshot == pCurrentSnapshot) ? " (CURSNAP)" : "");
102 return true; // found
103 }
104 }
105 }
106
107 // not found: then recurse into child snapshots
108 SafeIfaceArray<ISnapshot> aSnapshots;
109 CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Children)(ComSafeArrayAsOutParam(aSnapshots)));
110
111 for (uint32_t i = 0;
112 i < aSnapshots.size();
113 ++i)
114 {
115 ComPtr<ISnapshot> pChild(aSnapshots[i]);
116 if (FindAndPrintSnapshotUsingMedium(pMedium,
117 pChild,
118 pCurrentSnapshot,
119 uMediumLevel,
120 uSnapshotLevel + 1))
121 // found:
122 break;
123 }
124 } while (0);
125
126 return false;
127}
128
129/**
130 * Helper function used with "VBoxManage snapshot ... dump". Called from DumpSnapshot()
131 * for each hard disk attachment found in a virtual machine. This then writes out the
132 * root (base) medium for that hard disk attachment and recurses into the children
133 * tree of that medium, correlating it with the snapshots of the machine.
134 * @param pCurrentStateMedium constant, the medium listed in the current machine data (latest diff image).
135 * @param pMedium variant, initially the base medium, then a child of the base medium when recursing.
136 * @param pRootSnapshot constant, the root snapshot of the machine, if any; this then looks into the child snapshots.
137 * @param pCurrentSnapshot constant, the machine's current snapshot (so we can mark it in the output).
138 * @param uLevel variant, the recursion level for output indentation.
139 */
140static void DumpMediumWithChildren(ComPtr<IMedium> &pCurrentStateMedium,
141 ComPtr<IMedium> &pMedium,
142 ComPtr<ISnapshot> &pRootSnapshot,
143 ComPtr<ISnapshot> &pCurrentSnapshot,
144 uint32_t uLevel)
145{
146 HRESULT hrc;
147 do
148 {
149 // print this medium
150 Bstr bstrMediumName;
151 CHECK_ERROR_BREAK(pMedium, COMGETTER(Name)(bstrMediumName.asOutParam()));
152 RTPrintf("%*s \"%ls\"%s\n",
153 uLevel * 2, "", // indent
154 bstrMediumName.raw(),
155 (pCurrentStateMedium == pMedium) ? " (CURSTATE)" : "");
156
157 // find and print the snapshot that uses this particular medium (diff image)
158 FindAndPrintSnapshotUsingMedium(pMedium, pRootSnapshot, pCurrentSnapshot, uLevel, 0);
159
160 // recurse into children
161 SafeIfaceArray<IMedium> aChildren;
162 CHECK_ERROR_BREAK(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(aChildren)));
163 for (uint32_t i = 0;
164 i < aChildren.size();
165 ++i)
166 {
167 ComPtr<IMedium> pChild(aChildren[i]);
168 DumpMediumWithChildren(pCurrentStateMedium, pChild, pRootSnapshot, pCurrentSnapshot, uLevel + 1);
169 }
170 } while (0);
171}
172
173
174/**
175 * Handles the 'snapshot myvm list' sub-command.
176 * @returns Exit code.
177 * @param pArgs The handler argument package.
178 * @param pMachine Reference to the VM (locked) we're operating on.
179 */
180static RTEXITCODE handleSnapshotList(HandlerArg *pArgs, ComPtr<IMachine> &pMachine)
181{
182 static const RTGETOPTDEF g_aOptions[] =
183 {
184 { "--details", 'D', RTGETOPT_REQ_NOTHING },
185 { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
186 };
187
188 VMINFO_DETAILS enmDetails = VMINFO_STANDARD;
189
190 int c;
191 RTGETOPTUNION ValueUnion;
192 RTGETOPTSTATE GetState;
193 RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, g_aOptions, RT_ELEMENTS(g_aOptions), 2 /*iArg*/, 0 /*fFlags*/);
194 while ((c = RTGetOpt(&GetState, &ValueUnion)))
195 {
196 switch (c)
197 {
198 case 'D': enmDetails = VMINFO_FULL; break;
199 case 'M': enmDetails = VMINFO_MACHINEREADABLE; break;
200 default: return errorGetOpt(c, &ValueUnion);
201 }
202 }
203
204 ComPtr<ISnapshot> pSnapshot;
205 HRESULT hrc = pMachine->FindSnapshot(Bstr().raw(), pSnapshot.asOutParam());
206 if (FAILED(hrc))
207 {
208 RTPrintf(Snapshot::tr("This machine does not have any snapshots\n"));
209 return RTEXITCODE_FAILURE;
210 }
211 if (pSnapshot)
212 {
213 ComPtr<ISnapshot> pCurrentSnapshot;
214 CHECK_ERROR2I_RET(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()), RTEXITCODE_FAILURE);
215 hrc = showSnapshots(pSnapshot, pCurrentSnapshot, enmDetails);
216 if (FAILED(hrc))
217 return RTEXITCODE_FAILURE;
218 }
219 return RTEXITCODE_SUCCESS;
220}
221
222/**
223 * Implementation for "VBoxManage snapshot ... dump". This goes thru the machine's
224 * medium attachments and calls DumpMediumWithChildren() for each hard disk medium found,
225 * which then dumps the parent/child tree of that medium together with the corresponding
226 * snapshots.
227 * @param pMachine Machine to dump snapshots for.
228 */
229static void DumpSnapshot(ComPtr<IMachine> &pMachine)
230{
231 HRESULT hrc;
232
233 do
234 {
235 // get root snapshot
236 ComPtr<ISnapshot> pSnapshot;
237 CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr("").raw(), pSnapshot.asOutParam()));
238
239 // get current snapshot
240 ComPtr<ISnapshot> pCurrentSnapshot;
241 CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
242
243 // get media attachments
244 SafeIfaceArray<IMediumAttachment> aAttachments;
245 CHECK_ERROR_BREAK(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
246 for (uint32_t i = 0;
247 i < aAttachments.size();
248 ++i)
249 {
250 ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
251 DeviceType_T type;
252 CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
253 if (type == DeviceType_HardDisk)
254 {
255 ComPtr<IMedium> pCurrentStateMedium;
256 CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pCurrentStateMedium.asOutParam()));
257
258 ComPtr<IMedium> pBaseMedium;
259 CHECK_ERROR_BREAK(pCurrentStateMedium, COMGETTER(Base)(pBaseMedium.asOutParam()));
260
261 Bstr bstrBaseMediumName;
262 CHECK_ERROR_BREAK(pBaseMedium, COMGETTER(Name)(bstrBaseMediumName.asOutParam()));
263
264 RTPrintf(Snapshot::tr("[%RI32] Images and snapshots for medium \"%ls\"\n"), i, bstrBaseMediumName.raw());
265
266 DumpMediumWithChildren(pCurrentStateMedium,
267 pBaseMedium,
268 pSnapshot,
269 pCurrentSnapshot,
270 0);
271 }
272 }
273 } while (0);
274}
275
276typedef enum SnapshotUniqueFlags
277{
278 SnapshotUniqueFlags_Null = 0,
279 SnapshotUniqueFlags_Number = RT_BIT(1),
280 SnapshotUniqueFlags_Timestamp = RT_BIT(2),
281 SnapshotUniqueFlags_Space = RT_BIT(16),
282 SnapshotUniqueFlags_Force = RT_BIT(30)
283} SnapshotUniqueFlags;
284
285static int parseSnapshotUniqueFlags(const char *psz, SnapshotUniqueFlags *pUnique)
286{
287 int vrc = VINF_SUCCESS;
288 unsigned uUnique = 0;
289 while (psz && *psz && RT_SUCCESS(vrc))
290 {
291 size_t len;
292 const char *pszComma = strchr(psz, ',');
293 if (pszComma)
294 len = pszComma - psz;
295 else
296 len = strlen(psz);
297 if (len > 0)
298 {
299 if (!RTStrNICmp(psz, "number", len))
300 uUnique |= SnapshotUniqueFlags_Number;
301 else if (!RTStrNICmp(psz, "timestamp", len))
302 uUnique |= SnapshotUniqueFlags_Timestamp;
303 else if (!RTStrNICmp(psz, "space", len))
304 uUnique |= SnapshotUniqueFlags_Space;
305 else if (!RTStrNICmp(psz, "force", len))
306 uUnique |= SnapshotUniqueFlags_Force;
307 else
308 vrc = VERR_PARSE_ERROR;
309 }
310 if (pszComma)
311 psz += len + 1;
312 else
313 psz += len;
314 }
315
316 if (RT_SUCCESS(vrc))
317 *pUnique = (SnapshotUniqueFlags)uUnique;
318 return vrc;
319}
320
321/**
322 * Implementation for all VBoxManage snapshot ... subcommands.
323 * @param a
324 * @return
325 */
326RTEXITCODE handleSnapshot(HandlerArg *a)
327{
328 HRESULT hrc;
329
330/** @todo r=bird: sub-standard command line parsing here!
331 *
332 * 'VBoxManage snapshot empty take --help' takes a snapshot rather than display
333 * help as you would expect.
334 *
335 */
336
337 /* we need at least a VM and a command */
338 if (a->argc < 2)
339 return errorSyntax(Snapshot::tr("Not enough parameters"));
340
341 /* the first argument must be the VM */
342 Bstr bstrMachine(a->argv[0]);
343 ComPtr<IMachine> pMachine;
344 CHECK_ERROR(a->virtualBox, FindMachine(bstrMachine.raw(),
345 pMachine.asOutParam()));
346 if (!pMachine)
347 return RTEXITCODE_FAILURE;
348
349 /* we have to open a session for this task (new or shared) */
350 CHECK_ERROR_RET(pMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
351 do
352 {
353 /* replace the (read-only) IMachine object by a writable one */
354 ComPtr<IMachine> sessionMachine;
355 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
356
357 /* switch based on the command */
358 bool fDelete = false,
359 fRestore = false,
360 fRestoreCurrent = false;
361
362 if (!strcmp(a->argv[1], "take"))
363 {
364 setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_TAKE);
365
366 /* there must be a name */
367 if (a->argc < 3)
368 {
369 errorSyntax(Snapshot::tr("Missing snapshot name"));
370 hrc = E_FAIL;
371 break;
372 }
373 Bstr name(a->argv[2]);
374
375 /* parse the optional arguments */
376 Bstr desc;
377 bool fPause = true; /* default is NO live snapshot */
378 SnapshotUniqueFlags enmUnique = SnapshotUniqueFlags_Null;
379 static const RTGETOPTDEF s_aTakeOptions[] =
380 {
381 { "--description", 'd', RTGETOPT_REQ_STRING },
382 { "-description", 'd', RTGETOPT_REQ_STRING },
383 { "-desc", 'd', RTGETOPT_REQ_STRING },
384 { "--pause", 'p', RTGETOPT_REQ_NOTHING },
385 { "--live", 'l', RTGETOPT_REQ_NOTHING },
386 { "--uniquename", 'u', RTGETOPT_REQ_STRING }
387 };
388 RTGETOPTSTATE GetOptState;
389 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions),
390 3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
391 int ch;
392 RTGETOPTUNION Value;
393 int vrc;
394 while ( SUCCEEDED(hrc)
395 && (ch = RTGetOpt(&GetOptState, &Value)))
396 {
397 switch (ch)
398 {
399 case 'p':
400 fPause = true;
401 break;
402
403 case 'l':
404 fPause = false;
405 break;
406
407 case 'd':
408 desc = Value.psz;
409 break;
410
411 case 'u':
412 vrc = parseSnapshotUniqueFlags(Value.psz, &enmUnique);
413 if (RT_FAILURE(vrc))
414 return errorArgument(Snapshot::tr("Invalid unique name description '%s'"), Value.psz);
415 break;
416
417 default:
418 errorGetOpt(ch, &Value);
419 hrc = E_FAIL;
420 break;
421 }
422 }
423 if (FAILED(hrc))
424 break;
425
426 if (enmUnique & (SnapshotUniqueFlags_Number | SnapshotUniqueFlags_Timestamp))
427 {
428 ComPtr<ISnapshot> pSnapshot;
429 hrc = sessionMachine->FindSnapshot(name.raw(),
430 pSnapshot.asOutParam());
431 if (SUCCEEDED(hrc) || (enmUnique & SnapshotUniqueFlags_Force))
432 {
433 /* there is a duplicate, need to create a unique name */
434 uint32_t count = 0;
435 RTTIMESPEC now;
436
437 if (enmUnique & SnapshotUniqueFlags_Number)
438 {
439 if (enmUnique & SnapshotUniqueFlags_Force)
440 count = 1;
441 else
442 count = 2;
443 RTTimeSpecSetNano(&now, 0); /* Shut up MSC */
444 }
445 else
446 RTTimeNow(&now);
447
448 while (count < 500)
449 {
450 Utf8Str suffix;
451 if (enmUnique & SnapshotUniqueFlags_Number)
452 suffix = Utf8StrFmt("%u", count);
453 else
454 {
455 RTTIMESPEC nowplus = now;
456 RTTimeSpecAddSeconds(&nowplus, count);
457 RTTIME stamp;
458 RTTimeExplode(&stamp, &nowplus);
459 suffix = Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ", stamp.i32Year, stamp.u8Month, stamp.u8MonthDay, stamp.u8Hour, stamp.u8Minute, stamp.u8Second);
460 }
461 Bstr tryName = name;
462 if (enmUnique & SnapshotUniqueFlags_Space)
463 tryName = BstrFmt("%ls %s", name.raw(), suffix.c_str());
464 else
465 tryName = BstrFmt("%ls%s", name.raw(), suffix.c_str());
466 count++;
467 hrc = sessionMachine->FindSnapshot(tryName.raw(),
468 pSnapshot.asOutParam());
469 if (FAILED(hrc))
470 {
471 name = tryName;
472 break;
473 }
474 }
475 if (SUCCEEDED(hrc))
476 {
477 errorArgument(Snapshot::tr("Failed to generate a unique snapshot name"));
478 hrc = E_FAIL;
479 break;
480 }
481 }
482 hrc = S_OK;
483 }
484
485 ComPtr<IProgress> progress;
486 Bstr snapId;
487 CHECK_ERROR_BREAK(sessionMachine, TakeSnapshot(name.raw(), desc.raw(),
488 fPause, snapId.asOutParam(),
489 progress.asOutParam()));
490
491 hrc = showProgress(progress);
492 if (SUCCEEDED(hrc))
493 RTPrintf(Snapshot::tr("Snapshot taken. UUID: %ls\n"), snapId.raw());
494 else
495 CHECK_PROGRESS_ERROR(progress, (Snapshot::tr("Failed to take snapshot")));
496 }
497 else if ( (fDelete = !strcmp(a->argv[1], "delete"))
498 || (fRestore = !strcmp(a->argv[1], "restore"))
499 || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent"))
500 )
501 {
502 setCurrentSubcommand(fDelete ? HELP_SCOPE_SNAPSHOT_DELETE
503 : fRestore ? HELP_SCOPE_SNAPSHOT_RESTORE
504 : HELP_SCOPE_SNAPSHOT_RESTORECURRENT);
505
506 if (fRestoreCurrent)
507 {
508 if (a->argc > 2)
509 {
510 errorSyntax(Snapshot::tr("Too many arguments"));
511 hrc = E_FAIL;
512 break;
513 }
514 }
515 /* exactly one parameter: snapshot name */
516 else if (a->argc != 3)
517 {
518 errorSyntax(Snapshot::tr("Expecting snapshot name only"));
519 hrc = E_FAIL;
520 break;
521 }
522
523 ComPtr<ISnapshot> pSnapshot;
524
525 if (fRestoreCurrent)
526 {
527 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
528 if (pSnapshot.isNull())
529 {
530 RTPrintf(Snapshot::tr("This machine does not have any snapshots\n"));
531 return RTEXITCODE_FAILURE;
532 }
533 }
534 else
535 {
536 // restore or delete snapshot: then resolve cmd line argument to snapshot instance
537 CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
538 pSnapshot.asOutParam()));
539 }
540
541 Bstr bstrSnapGuid;
542 CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));
543
544 Bstr bstrSnapName;
545 CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Name)(bstrSnapName.asOutParam()));
546
547 ComPtr<IProgress> pProgress;
548
549 RTPrintf(Snapshot::tr("%s snapshot '%ls' (%ls)\n"),
550 fDelete ? Snapshot::tr("Deleting") : Snapshot::tr("Restoring"), bstrSnapName.raw(), bstrSnapGuid.raw());
551
552 if (fDelete)
553 {
554 CHECK_ERROR_BREAK(sessionMachine, DeleteSnapshot(bstrSnapGuid.raw(),
555 pProgress.asOutParam()));
556 }
557 else
558 {
559 // restore or restore current
560 CHECK_ERROR_BREAK(sessionMachine, RestoreSnapshot(pSnapshot, pProgress.asOutParam()));
561 }
562
563 hrc = showProgress(pProgress);
564 CHECK_PROGRESS_ERROR(pProgress, (Snapshot::tr("Snapshot operation failed")));
565 }
566 else if (!strcmp(a->argv[1], "edit"))
567 {
568 setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_EDIT);
569 if (a->argc < 3)
570 {
571 errorSyntax(Snapshot::tr("Missing snapshot name"));
572 hrc = E_FAIL;
573 break;
574 }
575
576 /* Parse the optional arguments, allowing more freedom than the
577 * synopsis explains. Can rename multiple snapshots and so on. */
578 ComPtr<ISnapshot> pSnapshot;
579 static const RTGETOPTDEF s_aEditOptions[] =
580 {
581 { "--current", 'c', RTGETOPT_REQ_NOTHING },
582 { "-current", 'c', RTGETOPT_REQ_NOTHING },
583 { "--name", 'n', RTGETOPT_REQ_STRING },
584 { "-name", 'n', RTGETOPT_REQ_STRING },
585 { "-newname", 'n', RTGETOPT_REQ_STRING },
586 { "--description", 'd', RTGETOPT_REQ_STRING },
587 { "-description", 'd', RTGETOPT_REQ_STRING },
588 { "-desc", 'd', RTGETOPT_REQ_STRING }
589 };
590 RTGETOPTSTATE GetOptState;
591 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aEditOptions, RT_ELEMENTS(s_aEditOptions),
592 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
593 int ch;
594 RTGETOPTUNION Value;
595 while ( SUCCEEDED(hrc)
596 && (ch = RTGetOpt(&GetOptState, &Value)))
597 {
598 switch (ch)
599 {
600 case 'c':
601 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
602 if (pSnapshot.isNull())
603 {
604 RTPrintf(Snapshot::tr("This machine does not have any snapshots\n"));
605 return RTEXITCODE_FAILURE;
606 }
607 break;
608
609 case 'n':
610 CHECK_ERROR_BREAK(pSnapshot, COMSETTER(Name)(Bstr(Value.psz).raw()));
611 break;
612
613 case 'd':
614 CHECK_ERROR_BREAK(pSnapshot, COMSETTER(Description)(Bstr(Value.psz).raw()));
615 break;
616
617 case VINF_GETOPT_NOT_OPTION:
618 CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(Value.psz).raw(), pSnapshot.asOutParam()));
619 break;
620
621 default:
622 errorGetOpt(ch, &Value);
623 hrc = E_FAIL;
624 break;
625 }
626 }
627
628 if (FAILED(hrc))
629 break;
630 }
631 else if (!strcmp(a->argv[1], "showvminfo"))
632 {
633 setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_SHOWVMINFO);
634
635 /* exactly one parameter: snapshot name */
636 if (a->argc != 3)
637 {
638 errorSyntax(Snapshot::tr("Expecting snapshot name only"));
639 hrc = E_FAIL;
640 break;
641 }
642
643 ComPtr<ISnapshot> pSnapshot;
644
645 CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
646 pSnapshot.asOutParam()));
647
648 /* get the machine of the given snapshot */
649 ComPtr<IMachine> pMachine2;
650 pSnapshot->COMGETTER(Machine)(pMachine2.asOutParam());
651 showVMInfo(a->virtualBox, pMachine2, NULL, VMINFO_NONE);
652 }
653 else if (!strcmp(a->argv[1], "list"))
654 {
655 setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_LIST);
656 hrc = handleSnapshotList(a, sessionMachine) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
657 }
658 else if (!strcmp(a->argv[1], "dump")) // undocumented parameter to debug snapshot info
659 DumpSnapshot(sessionMachine);
660 else
661 {
662 errorSyntax(Snapshot::tr("Invalid parameter '%s'"), a->argv[1]);
663 hrc = E_FAIL;
664 }
665 } while (0);
666
667 a->session->UnlockMachine();
668
669 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
670}
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