VirtualBox

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

Last change on this file since 27087 was 26517, checked in by vboxsync, 15 years ago

*: RTGetOpt cleanup related to --help and --version (now standard option). Use RTGetOptPrintError.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: VBoxManageSnapshot.cpp 26517 2010-02-14 21:39:00Z vboxsync $ */
2/** @file
3 * VBoxManage - The 'snapshot' command.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#include <VBox/com/com.h>
26#include <VBox/com/string.h>
27#include <VBox/com/array.h>
28#include <VBox/com/ErrorInfo.h>
29#include <VBox/com/errorprint.h>
30
31#include <VBox/com/VirtualBox.h>
32
33#include <iprt/stream.h>
34#include <iprt/getopt.h>
35
36#include "VBoxManage.h"
37using namespace com;
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 * Implementation for "VBoxManage snapshot ... dump". This goes thru the machine's
165 * medium attachments and calls DumpMediumWithChildren() for each hard disk medium found,
166 * which then dumps the parent/child tree of that medium together with the corresponding
167 * snapshots.
168 * @param pMachine Machine to dump snapshots for.
169 */
170void DumpSnapshot(ComPtr<IMachine> &pMachine)
171{
172 HRESULT rc;
173
174 do
175 {
176 // get root snapshot
177 ComPtr<ISnapshot> pSnapshot;
178 CHECK_ERROR_BREAK(pMachine, GetSnapshot(Bstr(""), pSnapshot.asOutParam()));
179
180 // get current snapshot
181 ComPtr<ISnapshot> pCurrentSnapshot;
182 CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
183
184 // get media attachments
185 SafeIfaceArray<IMediumAttachment> aAttachments;
186 CHECK_ERROR_BREAK(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
187 for (uint32_t i = 0;
188 i < aAttachments.size();
189 ++i)
190 {
191 ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
192 DeviceType_T type;
193 CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
194 if (type == DeviceType_HardDisk)
195 {
196 ComPtr<IMedium> pCurrentStateMedium;
197 CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pCurrentStateMedium.asOutParam()));
198
199 ComPtr<IMedium> pBaseMedium;
200 CHECK_ERROR_BREAK(pCurrentStateMedium, COMGETTER(Base)(pBaseMedium.asOutParam()));
201
202 Bstr bstrBaseMediumName;
203 CHECK_ERROR_BREAK(pBaseMedium, COMGETTER(Name)(bstrBaseMediumName.asOutParam()));
204
205 RTPrintf("[%RI32] Images and snapshots for medium \"%ls\"\n", i, bstrBaseMediumName.raw());
206
207 DumpMediumWithChildren(pCurrentStateMedium,
208 pBaseMedium,
209 pSnapshot,
210 pCurrentSnapshot,
211 0);
212 }
213 }
214 } while (0);
215}
216
217/**
218 * Implementation for all VBoxManage snapshot ... subcommands.
219 * @param a
220 * @return
221 */
222int handleSnapshot(HandlerArg *a)
223{
224 HRESULT rc;
225
226 /* we need at least a VM and a command */
227 if (a->argc < 2)
228 return errorSyntax(USAGE_SNAPSHOT, "Not enough parameters");
229
230 /* the first argument must be the VM */
231 Bstr bstrMachine(a->argv[0]);
232 ComPtr<IMachine> pMachine;
233 /* assume it's a UUID */
234 rc = a->virtualBox->GetMachine(bstrMachine, pMachine.asOutParam());
235 if (FAILED(rc) || !pMachine)
236 {
237 /* must be a name */
238 CHECK_ERROR(a->virtualBox, FindMachine(bstrMachine, pMachine.asOutParam()));
239 }
240 if (!pMachine)
241 return 1;
242 Bstr guidMachine;
243 pMachine->COMGETTER(Id)(guidMachine.asOutParam());
244
245 do
246 {
247 /* we have to open a session for this task. First try an existing session */
248 rc = a->virtualBox->OpenExistingSession(a->session, guidMachine);
249 if (FAILED(rc))
250 CHECK_ERROR_BREAK(a->virtualBox, OpenSession(a->session, guidMachine));
251 ComPtr<IConsole> console;
252 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
253
254 /* switch based on the command */
255 bool fDelete = false,
256 fRestore = false,
257 fRestoreCurrent = false;
258
259 if (!strcmp(a->argv[1], "take"))
260 {
261 /* there must be a name */
262 if (a->argc < 3)
263 {
264 errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
265 rc = E_FAIL;
266 break;
267 }
268 Bstr name(a->argv[2]);
269
270 /* parse the optional arguments */
271 Bstr desc;
272 bool fPause = false;
273 static const RTGETOPTDEF s_aTakeOptions[] =
274 {
275 { "--description", 'd', RTGETOPT_REQ_STRING },
276 { "-description", 'd', RTGETOPT_REQ_STRING },
277 { "-desc", 'd', RTGETOPT_REQ_STRING },
278 { "--pause", 'p', RTGETOPT_REQ_NOTHING }
279 };
280 RTGETOPTSTATE GetOptState;
281 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions),
282 3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
283 int ch;
284 RTGETOPTUNION Value;
285 while ( SUCCEEDED(rc)
286 && (ch = RTGetOpt(&GetOptState, &Value)))
287 {
288 switch (ch)
289 {
290 case 'p':
291 fPause = true;
292 break;
293
294 case 'd':
295 desc = Value.psz;
296 break;
297
298 default:
299 errorGetOpt(USAGE_SNAPSHOT, ch, &Value);
300 rc = E_FAIL;
301 break;
302 }
303 }
304 if (FAILED(rc))
305 break;
306
307 if (fPause)
308 {
309 MachineState_T machineState;
310 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
311 if (machineState == MachineState_Running)
312 CHECK_ERROR_BREAK(console, Pause());
313 else
314 fPause = false;
315 }
316
317 ComPtr<IProgress> progress;
318 CHECK_ERROR_BREAK(console, TakeSnapshot(name, desc, progress.asOutParam()));
319
320 rc = showProgress(progress);
321 if (FAILED(rc))
322 {
323 com::ProgressErrorInfo info(progress);
324 if (info.isBasicAvailable())
325 RTPrintf("Error: failed to take snapshot. Error message: %lS\n", info.getText().raw());
326 else
327 RTPrintf("Error: failed to take snapshot. No error message available!\n");
328 }
329
330 if (fPause)
331 {
332 MachineState_T machineState;
333 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
334 if (machineState == MachineState_Paused)
335 {
336 if (SUCCEEDED(rc))
337 CHECK_ERROR_BREAK(console, Resume());
338 else
339 console->Resume();
340 }
341 }
342 }
343 else if ( (fDelete = !strcmp(a->argv[1], "delete"))
344 || (fRestore = !strcmp(a->argv[1], "restore"))
345 || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent"))
346 )
347 {
348 if (fRestoreCurrent)
349 {
350 if (a->argc > 2)
351 {
352 errorSyntax(USAGE_SNAPSHOT, "Too many arguments");
353 rc = E_FAIL;
354 break;
355 }
356 }
357 /* exactly one parameter: snapshot name */
358 else if (a->argc != 3)
359 {
360 errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
361 rc = E_FAIL;
362 break;
363 }
364
365 ComPtr<ISnapshot> pSnapshot;
366 ComPtr<IProgress> pProgress;
367 Bstr bstrSnapGuid;
368
369 if (fRestoreCurrent)
370 {
371 CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
372 CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));
373 }
374 else
375 {
376 // restore or delete snapshot: then resolve cmd line argument to snapshot instance
377 // assume it's a UUID
378 bstrSnapGuid = a->argv[2];
379 if (FAILED(pMachine->GetSnapshot(bstrSnapGuid, pSnapshot.asOutParam())))
380 {
381 // then it must be a name
382 CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr(a->argv[2]), pSnapshot.asOutParam()));
383 CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));
384 }
385 }
386
387 if (fDelete)
388 {
389 CHECK_ERROR_BREAK(console, DeleteSnapshot(bstrSnapGuid, pProgress.asOutParam()));
390 }
391 else
392 {
393 // restore or restore current
394 RTPrintf("Restoring snapshot %ls\n", bstrSnapGuid.raw());
395 CHECK_ERROR_BREAK(console, RestoreSnapshot(pSnapshot, pProgress.asOutParam()));
396 }
397
398 rc = showProgress(pProgress);
399 if (FAILED(rc))
400 {
401 com::ProgressErrorInfo info(pProgress);
402 if (info.isBasicAvailable())
403 RTPrintf("Error: snapshot operation failed. Error message: %lS\n", info.getText().raw());
404 else
405 RTPrintf("Error: snapshot operation failed. No error message available!\n");
406 }
407 }
408 else if (!strcmp(a->argv[1], "edit"))
409 {
410 if (a->argc < 3)
411 {
412 errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
413 rc = E_FAIL;
414 break;
415 }
416
417 ComPtr<ISnapshot> snapshot;
418
419 if ( !strcmp(a->argv[2], "--current")
420 || !strcmp(a->argv[2], "-current"))
421 {
422 CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(snapshot.asOutParam()));
423 }
424 else
425 {
426 /* assume it's a UUID */
427 Bstr guidSnap(a->argv[2]);
428 if (!guidSnap.isEmpty())
429 {
430 CHECK_ERROR_BREAK(pMachine, GetSnapshot(guidSnap, snapshot.asOutParam()));
431 }
432 else
433 {
434 /* then it must be a name */
435 CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr(a->argv[2]), snapshot.asOutParam()));
436 }
437 }
438
439 /* parse options */
440 for (int i = 3; i < a->argc; i++)
441 {
442 if ( !strcmp(a->argv[i], "--name")
443 || !strcmp(a->argv[i], "-name")
444 || !strcmp(a->argv[i], "-newname"))
445 {
446 if (a->argc <= i + 1)
447 {
448 errorArgument("Missing argument to '%s'", a->argv[i]);
449 rc = E_FAIL;
450 break;
451 }
452 i++;
453 snapshot->COMSETTER(Name)(Bstr(a->argv[i]));
454 }
455 else if ( !strcmp(a->argv[i], "--description")
456 || !strcmp(a->argv[i], "-description")
457 || !strcmp(a->argv[i], "-newdesc"))
458 {
459 if (a->argc <= i + 1)
460 {
461 errorArgument("Missing argument to '%s'", a->argv[i]);
462 rc = E_FAIL;
463 break;
464 }
465 i++;
466 snapshot->COMSETTER(Description)(Bstr(a->argv[i]));
467 }
468 else
469 {
470 errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[i]).raw());
471 rc = E_FAIL;
472 break;
473 }
474 }
475
476 }
477 else if (!strcmp(a->argv[1], "showvminfo"))
478 {
479 /* exactly one parameter: snapshot name */
480 if (a->argc != 3)
481 {
482 errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
483 rc = E_FAIL;
484 break;
485 }
486
487 ComPtr<ISnapshot> snapshot;
488
489 /* assume it's a UUID */
490 Bstr guidSnap(a->argv[2]);
491 if (!guidSnap.isEmpty())
492 {
493 CHECK_ERROR_BREAK(pMachine, GetSnapshot(guidSnap, snapshot.asOutParam()));
494 }
495 else
496 {
497 /* then it must be a name */
498 CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr(a->argv[2]), snapshot.asOutParam()));
499 }
500
501 /* get the machine of the given snapshot */
502 ComPtr<IMachine> pMachine2;
503 snapshot->COMGETTER(Machine)(pMachine2.asOutParam());
504 showVMInfo(a->virtualBox, pMachine2, VMINFO_NONE, console);
505 }
506 else if (!strcmp(a->argv[1], "dump")) // undocumented parameter to debug snapshot info
507 {
508 DumpSnapshot(pMachine);
509 }
510 else
511 {
512 errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[1]).raw());
513 rc = E_FAIL;
514 }
515 } while (0);
516
517 a->session->Close();
518
519 return SUCCEEDED(rc) ? 0 : 1;
520}
521
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