VirtualBox

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

Last change on this file since 30608 was 28870, checked in by vboxsync, 15 years ago

VBoxManage/snapshot: fixed selection of snapshots by name (regression from UUID => Bstr)

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