VirtualBox

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

Last change on this file since 47934 was 47544, checked in by vboxsync, 11 years ago

VBoxManage: when taking snapshots, --pause is default and --live must be explicitely specified

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.0 KB
Line 
1/* $Id: VBoxManageSnapshot.cpp 47544 2013-08-05 13:45:55Z vboxsync $ */
2/** @file
3 * VBoxManage - The 'snapshot' command.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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* Header Files *
20*******************************************************************************/
21#include <VBox/com/com.h>
22#include <VBox/com/string.h>
23#include <VBox/com/array.h>
24#include <VBox/com/ErrorInfo.h>
25#include <VBox/com/errorprint.h>
26
27#include <VBox/com/VirtualBox.h>
28
29#include <iprt/stream.h>
30#include <iprt/getopt.h>
31
32#include "VBoxManage.h"
33using namespace com;
34
35/**
36 * Helper function used with "VBoxManage snapshot ... dump". Gets called to find the
37 * snapshot in the machine's snapshot tree that uses a particular diff image child of
38 * a medium.
39 * Horribly inefficient since we keep re-querying the snapshots tree for each image,
40 * but this is for quick debugging only.
41 * @param pMedium
42 * @param pThisSnapshot
43 * @param pCurrentSnapshot
44 * @param uMediumLevel
45 * @param uSnapshotLevel
46 * @return
47 */
48bool FindAndPrintSnapshotUsingMedium(ComPtr<IMedium> &pMedium,
49 ComPtr<ISnapshot> &pThisSnapshot,
50 ComPtr<ISnapshot> &pCurrentSnapshot,
51 uint32_t uMediumLevel,
52 uint32_t uSnapshotLevel)
53{
54 HRESULT rc;
55
56 do
57 {
58 // get snapshot machine so we can figure out which diff image this created
59 ComPtr<IMachine> pSnapshotMachine;
60 CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Machine)(pSnapshotMachine.asOutParam()));
61
62 // get media attachments
63 SafeIfaceArray<IMediumAttachment> aAttachments;
64 CHECK_ERROR_BREAK(pSnapshotMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
65
66 for (uint32_t i = 0;
67 i < aAttachments.size();
68 ++i)
69 {
70 ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
71 DeviceType_T type;
72 CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
73 if (type == DeviceType_HardDisk)
74 {
75 ComPtr<IMedium> pMediumInSnapshot;
76 CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pMediumInSnapshot.asOutParam()));
77
78 if (pMediumInSnapshot == pMedium)
79 {
80 // get snapshot name
81 Bstr bstrSnapshotName;
82 CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Name)(bstrSnapshotName.asOutParam()));
83
84 RTPrintf("%*s \"%ls\"%s\n",
85 50 + uSnapshotLevel * 2, "", // indent
86 bstrSnapshotName.raw(),
87 (pThisSnapshot == pCurrentSnapshot) ? " (CURSNAP)" : "");
88 return true; // found
89 }
90 }
91 }
92
93 // not found: then recurse into child snapshots
94 SafeIfaceArray<ISnapshot> aSnapshots;
95 CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Children)(ComSafeArrayAsOutParam(aSnapshots)));
96
97 for (uint32_t i = 0;
98 i < aSnapshots.size();
99 ++i)
100 {
101 ComPtr<ISnapshot> pChild(aSnapshots[i]);
102 if (FindAndPrintSnapshotUsingMedium(pMedium,
103 pChild,
104 pCurrentSnapshot,
105 uMediumLevel,
106 uSnapshotLevel + 1))
107 // found:
108 break;
109 }
110 } while (0);
111
112 return false;
113}
114
115/**
116 * Helper function used with "VBoxManage snapshot ... dump". Called from DumpSnapshot()
117 * for each hard disk attachment found in a virtual machine. This then writes out the
118 * root (base) medium for that hard disk attachment and recurses into the children
119 * tree of that medium, correlating it with the snapshots of the machine.
120 * @param pCurrentStateMedium constant, the medium listed in the current machine data (latest diff image).
121 * @param pMedium variant, initially the base medium, then a child of the base medium when recursing.
122 * @param pRootSnapshot constant, the root snapshot of the machine, if any; this then looks into the child snapshots.
123 * @param pCurrentSnapshot constant, the machine's current snapshot (so we can mark it in the output).
124 * @param uLevel variant, the recursion level for output indentation.
125 */
126void DumpMediumWithChildren(ComPtr<IMedium> &pCurrentStateMedium,
127 ComPtr<IMedium> &pMedium,
128 ComPtr<ISnapshot> &pRootSnapshot,
129 ComPtr<ISnapshot> &pCurrentSnapshot,
130 uint32_t uLevel)
131{
132 HRESULT rc;
133 do
134 {
135 // print this medium
136 Bstr bstrMediumName;
137 CHECK_ERROR_BREAK(pMedium, COMGETTER(Name)(bstrMediumName.asOutParam()));
138 RTPrintf("%*s \"%ls\"%s\n",
139 uLevel * 2, "", // indent
140 bstrMediumName.raw(),
141 (pCurrentStateMedium == pMedium) ? " (CURSTATE)" : "");
142
143 // find and print the snapshot that uses this particular medium (diff image)
144 FindAndPrintSnapshotUsingMedium(pMedium, pRootSnapshot, pCurrentSnapshot, uLevel, 0);
145
146 // recurse into children
147 SafeIfaceArray<IMedium> aChildren;
148 CHECK_ERROR_BREAK(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(aChildren)));
149 for (uint32_t i = 0;
150 i < aChildren.size();
151 ++i)
152 {
153 ComPtr<IMedium> pChild(aChildren[i]);
154 DumpMediumWithChildren(pCurrentStateMedium, pChild, pRootSnapshot, pCurrentSnapshot, uLevel + 1);
155 }
156 } while (0);
157}
158
159
160/**
161 * Handles the 'snapshot myvm list' sub-command.
162 * @returns Exit code.
163 * @param pArgs The handler argument package.
164 * @param rptrMachine Reference to the VM (locked) we're operating on.
165 */
166static RTEXITCODE handleSnapshotList(HandlerArg *pArgs, ComPtr<IMachine> &rptrMachine)
167{
168 static const RTGETOPTDEF g_aOptions[] =
169 {
170 { "--details", 'D', RTGETOPT_REQ_NOTHING },
171 { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
172 };
173
174 VMINFO_DETAILS enmDetails = VMINFO_STANDARD;
175
176 int c;
177 RTGETOPTUNION ValueUnion;
178 RTGETOPTSTATE GetState;
179 RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, g_aOptions, RT_ELEMENTS(g_aOptions), 2 /*iArg*/, 0 /*fFlags*/);
180 while ((c = RTGetOpt(&GetState, &ValueUnion)))
181 {
182 switch (c)
183 {
184 case 'D': enmDetails = VMINFO_FULL; break;
185 case 'M': enmDetails = VMINFO_MACHINEREADABLE; break;
186 default: return errorGetOpt(USAGE_SNAPSHOT, c, &ValueUnion);
187 }
188 }
189
190 /* See showVMInfo. */
191 ComPtr<ISnapshot> ptrSnapshot;
192 HRESULT hrc = rptrMachine->FindSnapshot(Bstr().raw(), ptrSnapshot.asOutParam());
193 if (FAILED(hrc))
194 {
195 RTPrintf("This machine does not have any snapshots\n");
196 return RTEXITCODE_FAILURE;
197 }
198 if (ptrSnapshot)
199 {
200 ComPtr<ISnapshot> ptrCurrentSnapshot;
201 CHECK_ERROR2_RET(rptrMachine,COMGETTER(CurrentSnapshot)(ptrCurrentSnapshot.asOutParam()), RTEXITCODE_FAILURE);
202 hrc = showSnapshots(ptrSnapshot, ptrCurrentSnapshot, enmDetails);
203 if (FAILED(hrc))
204 return RTEXITCODE_FAILURE;
205 }
206 return RTEXITCODE_SUCCESS;
207}
208
209/**
210 * Implementation for "VBoxManage snapshot ... dump". This goes thru the machine's
211 * medium attachments and calls DumpMediumWithChildren() for each hard disk medium found,
212 * which then dumps the parent/child tree of that medium together with the corresponding
213 * snapshots.
214 * @param pMachine Machine to dump snapshots for.
215 */
216void DumpSnapshot(ComPtr<IMachine> &pMachine)
217{
218 HRESULT rc;
219
220 do
221 {
222 // get root snapshot
223 ComPtr<ISnapshot> pSnapshot;
224 CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr("").raw(), pSnapshot.asOutParam()));
225
226 // get current snapshot
227 ComPtr<ISnapshot> pCurrentSnapshot;
228 CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
229
230 // get media attachments
231 SafeIfaceArray<IMediumAttachment> aAttachments;
232 CHECK_ERROR_BREAK(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
233 for (uint32_t i = 0;
234 i < aAttachments.size();
235 ++i)
236 {
237 ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
238 DeviceType_T type;
239 CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
240 if (type == DeviceType_HardDisk)
241 {
242 ComPtr<IMedium> pCurrentStateMedium;
243 CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pCurrentStateMedium.asOutParam()));
244
245 ComPtr<IMedium> pBaseMedium;
246 CHECK_ERROR_BREAK(pCurrentStateMedium, COMGETTER(Base)(pBaseMedium.asOutParam()));
247
248 Bstr bstrBaseMediumName;
249 CHECK_ERROR_BREAK(pBaseMedium, COMGETTER(Name)(bstrBaseMediumName.asOutParam()));
250
251 RTPrintf("[%RI32] Images and snapshots for medium \"%ls\"\n", i, bstrBaseMediumName.raw());
252
253 DumpMediumWithChildren(pCurrentStateMedium,
254 pBaseMedium,
255 pSnapshot,
256 pCurrentSnapshot,
257 0);
258 }
259 }
260 } while (0);
261}
262
263/**
264 * Implementation for all VBoxManage snapshot ... subcommands.
265 * @param a
266 * @return
267 */
268int handleSnapshot(HandlerArg *a)
269{
270 HRESULT rc;
271
272 /* we need at least a VM and a command */
273 if (a->argc < 2)
274 return errorSyntax(USAGE_SNAPSHOT, "Not enough parameters");
275
276 /* the first argument must be the VM */
277 Bstr bstrMachine(a->argv[0]);
278 ComPtr<IMachine> ptrMachine;
279 CHECK_ERROR(a->virtualBox, FindMachine(bstrMachine.raw(),
280 ptrMachine.asOutParam()));
281 if (!ptrMachine)
282 return 1;
283
284 do
285 {
286 /* we have to open a session for this task (new or shared) */
287 rc = ptrMachine->LockMachine(a->session, LockType_Shared);
288 ComPtr<IConsole> console;
289 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
290
291 /* switch based on the command */
292 bool fDelete = false,
293 fRestore = false,
294 fRestoreCurrent = false;
295
296 if (!strcmp(a->argv[1], "take"))
297 {
298 /* there must be a name */
299 if (a->argc < 3)
300 {
301 errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
302 rc = E_FAIL;
303 break;
304 }
305 Bstr name(a->argv[2]);
306
307 /* parse the optional arguments */
308 Bstr desc;
309 bool fPause = true; /* default is NO live snapshot */
310 static const RTGETOPTDEF s_aTakeOptions[] =
311 {
312 { "--description", 'd', RTGETOPT_REQ_STRING },
313 { "-description", 'd', RTGETOPT_REQ_STRING },
314 { "-desc", 'd', RTGETOPT_REQ_STRING },
315 { "--pause", 'p', RTGETOPT_REQ_NOTHING },
316 { "--live", 'l', RTGETOPT_REQ_NOTHING }
317 };
318 RTGETOPTSTATE GetOptState;
319 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions),
320 3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
321 int ch;
322 RTGETOPTUNION Value;
323 while ( SUCCEEDED(rc)
324 && (ch = RTGetOpt(&GetOptState, &Value)))
325 {
326 switch (ch)
327 {
328 case 'p':
329 fPause = true;
330 break;
331
332 case 'l':
333 fPause = false;
334 break;
335
336 case 'd':
337 desc = Value.psz;
338 break;
339
340 default:
341 errorGetOpt(USAGE_SNAPSHOT, ch, &Value);
342 rc = E_FAIL;
343 break;
344 }
345 }
346 if (FAILED(rc))
347 break;
348
349 if (fPause)
350 {
351 MachineState_T machineState;
352 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
353 if (machineState == MachineState_Running)
354 CHECK_ERROR_BREAK(console, Pause());
355 else
356 fPause = false;
357 }
358
359 ComPtr<IProgress> progress;
360 CHECK_ERROR_BREAK(console, TakeSnapshot(name.raw(), desc.raw(),
361 progress.asOutParam()));
362
363 rc = showProgress(progress);
364 CHECK_PROGRESS_ERROR(progress, ("Failed to take snapshot"));
365
366 if (fPause)
367 {
368 MachineState_T machineState;
369 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
370 if (machineState == MachineState_Paused)
371 {
372 if (SUCCEEDED(rc))
373 CHECK_ERROR_BREAK(console, Resume());
374 else
375 console->Resume();
376 }
377 }
378 }
379 else if ( (fDelete = !strcmp(a->argv[1], "delete"))
380 || (fRestore = !strcmp(a->argv[1], "restore"))
381 || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent"))
382 )
383 {
384 if (fRestoreCurrent)
385 {
386 if (a->argc > 2)
387 {
388 errorSyntax(USAGE_SNAPSHOT, "Too many arguments");
389 rc = E_FAIL;
390 break;
391 }
392 }
393 /* exactly one parameter: snapshot name */
394 else if (a->argc != 3)
395 {
396 errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
397 rc = E_FAIL;
398 break;
399 }
400
401 ComPtr<ISnapshot> pSnapshot;
402 ComPtr<IProgress> pProgress;
403 Bstr bstrSnapGuid;
404
405 if (fRestoreCurrent)
406 {
407 CHECK_ERROR_BREAK(ptrMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
408 }
409 else
410 {
411 // restore or delete snapshot: then resolve cmd line argument to snapshot instance
412 CHECK_ERROR_BREAK(ptrMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
413 pSnapshot.asOutParam()));
414 }
415
416 CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));
417
418 if (fDelete)
419 {
420 CHECK_ERROR_BREAK(console, DeleteSnapshot(bstrSnapGuid.raw(),
421 pProgress.asOutParam()));
422 }
423 else
424 {
425 // restore or restore current
426 RTPrintf("Restoring snapshot %ls\n", bstrSnapGuid.raw());
427 CHECK_ERROR_BREAK(console, RestoreSnapshot(pSnapshot, pProgress.asOutParam()));
428 }
429
430 rc = showProgress(pProgress);
431 CHECK_PROGRESS_ERROR(pProgress, ("Snapshot operation failed"));
432 }
433 else if (!strcmp(a->argv[1], "edit"))
434 {
435 if (a->argc < 3)
436 {
437 errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
438 rc = E_FAIL;
439 break;
440 }
441
442 ComPtr<ISnapshot> snapshot;
443
444 if ( !strcmp(a->argv[2], "--current")
445 || !strcmp(a->argv[2], "-current"))
446 {
447 CHECK_ERROR_BREAK(ptrMachine, COMGETTER(CurrentSnapshot)(snapshot.asOutParam()));
448 }
449 else
450 {
451 CHECK_ERROR_BREAK(ptrMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
452 snapshot.asOutParam()));
453 }
454
455 /* parse options */
456 for (int i = 3; i < a->argc; i++)
457 {
458 if ( !strcmp(a->argv[i], "--name")
459 || !strcmp(a->argv[i], "-name")
460 || !strcmp(a->argv[i], "-newname"))
461 {
462 if (a->argc <= i + 1)
463 {
464 errorArgument("Missing argument to '%s'", a->argv[i]);
465 rc = E_FAIL;
466 break;
467 }
468 i++;
469 snapshot->COMSETTER(Name)(Bstr(a->argv[i]).raw());
470 }
471 else if ( !strcmp(a->argv[i], "--description")
472 || !strcmp(a->argv[i], "-description")
473 || !strcmp(a->argv[i], "-newdesc"))
474 {
475 if (a->argc <= i + 1)
476 {
477 errorArgument("Missing argument to '%s'", a->argv[i]);
478 rc = E_FAIL;
479 break;
480 }
481 i++;
482 snapshot->COMSETTER(Description)(Bstr(a->argv[i]).raw());
483 }
484 else
485 {
486 errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
487 rc = E_FAIL;
488 break;
489 }
490 }
491
492 }
493 else if (!strcmp(a->argv[1], "showvminfo"))
494 {
495 /* exactly one parameter: snapshot name */
496 if (a->argc != 3)
497 {
498 errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
499 rc = E_FAIL;
500 break;
501 }
502
503 ComPtr<ISnapshot> snapshot;
504
505 CHECK_ERROR_BREAK(ptrMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
506 snapshot.asOutParam()));
507
508 /* get the machine of the given snapshot */
509 ComPtr<IMachine> ptrMachine2;
510 snapshot->COMGETTER(Machine)(ptrMachine2.asOutParam());
511 showVMInfo(a->virtualBox, ptrMachine2, VMINFO_NONE, console);
512 }
513 else if (!strcmp(a->argv[1], "list"))
514 rc = handleSnapshotList(a, ptrMachine) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
515 else if (!strcmp(a->argv[1], "dump")) // undocumented parameter to debug snapshot info
516 DumpSnapshot(ptrMachine);
517 else
518 {
519 errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[1]).c_str());
520 rc = E_FAIL;
521 }
522 } while (0);
523
524 a->session->UnlockMachine();
525
526 return SUCCEEDED(rc) ? 0 : 1;
527}
528
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