VirtualBox

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

Last change on this file since 93318 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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