VirtualBox

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

Last change on this file since 76366 was 76366, checked in by vboxsync, 5 years ago

include/VBox/com/Guid.h: Don't include iprt/err.h for no good reason. bugref:9344

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

© 2023 Oracle
ContactPrivacy policyTerms of Use