VirtualBox

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

Last change on this file since 106842 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • 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 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxManage - The 'snapshot' command.
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
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