]>
Commit | Line | Data |
---|---|---|
7378b84a DM |
1 | Index: new/qapi-schema.json |
2 | =================================================================== | |
3 | --- new.orig/qapi-schema.json 2012-09-04 12:52:21.000000000 +0200 | |
4 | +++ new/qapi-schema.json 2012-09-04 12:53:35.000000000 +0200 | |
5 | @@ -2493,3 +2493,10 @@ | |
6 | # Since: 1.2.0 | |
7 | ## | |
8 | { 'command': 'query-target', 'returns': 'TargetInfo' } | |
9 | + | |
10 | + | |
11 | +{ 'command': 'snapshot-start' 'data': { 'statefile': 'str' } } | |
12 | + | |
13 | +{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } } | |
14 | + | |
15 | +{ 'command': 'snapshot-end' } | |
16 | Index: new/qmp.c | |
17 | =================================================================== | |
18 | --- new.orig/qmp.c 2012-09-04 12:52:21.000000000 +0200 | |
19 | +++ new/qmp.c 2012-09-04 12:53:35.000000000 +0200 | |
20 | @@ -479,3 +479,145 @@ | |
21 | return arch_query_cpu_definitions(errp); | |
22 | } | |
23 | ||
24 | +static struct SnapshotState { | |
25 | + int in_progress; | |
26 | + int saved_vm_running; | |
27 | +} snap_state; | |
28 | + | |
29 | +void qmp_snapshot_start(const char *statefile, Error **errp) | |
30 | +{ | |
31 | + QEMUFile *f; | |
32 | + int ret; | |
33 | + | |
34 | + if (snap_state.in_progress) { | |
35 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
36 | + "VM snapshot already started\n"); | |
37 | + return; | |
38 | + } | |
39 | + snap_state.in_progress = 1; | |
40 | + | |
41 | + snap_state.saved_vm_running = runstate_is_running(); | |
42 | + | |
43 | + vm_stop(RUN_STATE_SAVE_VM); | |
44 | + | |
45 | + f = qemu_fopen(statefile, "wb"); | |
46 | + if (!f) { | |
47 | + error_set(errp, QERR_OPEN_FILE_FAILED, statefile); | |
48 | + return; | |
49 | + } | |
50 | + | |
51 | + /* todo: does that save complete state? */ | |
52 | + ret = qemu_savevm_state_complete(f); | |
53 | + qemu_fclose(f); | |
54 | + if (ret < 0) { | |
55 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
56 | + "Error %d while writing VM state\n", ret); | |
57 | + return; | |
58 | + } | |
59 | +} | |
60 | + | |
61 | +void qmp_snapshot_end(Error **errp) | |
62 | +{ | |
63 | + if (!snap_state.in_progress) { | |
64 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
65 | + "VM snapshot not started\n"); | |
66 | + return; | |
67 | + } | |
68 | + snap_state.in_progress = 0; | |
69 | + | |
70 | + if (snap_state.saved_vm_running) { | |
71 | + vm_start(); | |
72 | + } | |
73 | +} | |
74 | + | |
75 | +/* Fixme: Copied from savevm */ | |
76 | +static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, | |
77 | + const char *name) | |
78 | +{ | |
79 | + QEMUSnapshotInfo *sn_tab, *sn; | |
80 | + int nb_sns, i, ret; | |
81 | + | |
82 | + ret = -ENOENT; | |
83 | + nb_sns = bdrv_snapshot_list(bs, &sn_tab); | |
84 | + if (nb_sns < 0) | |
85 | + return ret; | |
86 | + for(i = 0; i < nb_sns; i++) { | |
87 | + sn = &sn_tab[i]; | |
88 | + if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { | |
89 | + *sn_info = *sn; | |
90 | + ret = 0; | |
91 | + break; | |
92 | + } | |
93 | + } | |
94 | + g_free(sn_tab); | |
95 | + return ret; | |
96 | +} | |
97 | + | |
98 | +void qmp_snapshot_drive(const char *device, const char *name, Error **errp) | |
99 | +{ | |
100 | + BlockDriverState *bs; | |
101 | + QEMUSnapshotInfo sn1, *sn = &sn1; | |
102 | + int ret; | |
103 | +#ifdef _WIN32 | |
104 | + struct _timeb tb; | |
105 | +#else | |
106 | + struct timeval tv; | |
107 | +#endif | |
108 | + | |
109 | + if (!snap_state.in_progress) { | |
110 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
111 | + "VM snapshot not started\n"); | |
112 | + return; | |
113 | + } | |
114 | + | |
115 | + bs = bdrv_find(device); | |
116 | + if (!bs) { | |
117 | + error_set(errp, QERR_DEVICE_NOT_FOUND, device); | |
118 | + return; | |
119 | + } | |
120 | + | |
121 | + if (!bdrv_is_inserted(bs)) { | |
122 | + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); | |
123 | + return; | |
124 | + } | |
125 | + | |
126 | + if (bdrv_is_read_only(bs)) { | |
127 | + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); | |
128 | + return; | |
129 | + } | |
130 | + | |
131 | + if (!bdrv_can_snapshot(bs)) { | |
132 | + error_set(errp, QERR_NOT_SUPPORTED); | |
133 | + return; | |
134 | + } | |
135 | + | |
136 | + if (bdrv_snapshot_find(bs, sn, name) >= 0) { | |
137 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
138 | + "snapshot '%s' already exists", name); | |
139 | + return; | |
140 | + } | |
141 | + | |
142 | + sn = &sn1; | |
143 | + memset(sn, 0, sizeof(*sn)); | |
144 | + | |
145 | +#ifdef _WIN32 | |
146 | + _ftime(&tb); | |
147 | + sn->date_sec = tb.time; | |
148 | + sn->date_nsec = tb.millitm * 1000000; | |
149 | +#else | |
150 | + gettimeofday(&tv, NULL); | |
151 | + sn->date_sec = tv.tv_sec; | |
152 | + sn->date_nsec = tv.tv_usec * 1000; | |
153 | +#endif | |
154 | + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); | |
155 | + | |
156 | + pstrcpy(sn->name, sizeof(sn->name), name); | |
157 | + | |
158 | + sn->vm_state_size = 0; /* do not save state */ | |
159 | + | |
160 | + ret = bdrv_snapshot_create(bs, sn); | |
161 | + if (ret < 0) { | |
162 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error while creating snapshot on '%s'\n", device); | |
163 | + return; | |
164 | + } | |
165 | +} | |
166 | Index: new/qmp-commands.hx | |
167 | =================================================================== | |
168 | --- new.orig/qmp-commands.hx 2012-09-04 12:52:21.000000000 +0200 | |
169 | +++ new/qmp-commands.hx 2012-09-04 12:53:35.000000000 +0200 | |
170 | @@ -2514,3 +2514,21 @@ | |
171 | .args_type = "", | |
172 | .mhandler.cmd_new = qmp_marshal_input_query_target, | |
173 | }, | |
174 | + | |
175 | + { | |
176 | + .name = "snapshot-start", | |
177 | + .args_type = "statefile:s", | |
178 | + .mhandler.cmd_new = qmp_marshal_input_snapshot_start, | |
179 | + }, | |
180 | + | |
181 | + { | |
182 | + .name = "snapshot-drive", | |
183 | + .args_type = "device:s,name:s", | |
184 | + .mhandler.cmd_new = qmp_marshal_input_snapshot_drive, | |
185 | + }, | |
186 | + | |
187 | + { | |
188 | + .name = "snapshot-end", | |
189 | + .args_type = "", | |
190 | + .mhandler.cmd_new = qmp_marshal_input_snapshot_end, | |
191 | + }, | |
192 | Index: new/hmp.c | |
193 | =================================================================== | |
194 | --- new.orig/hmp.c 2012-09-04 12:52:21.000000000 +0200 | |
195 | +++ new/hmp.c 2012-09-04 12:53:35.000000000 +0200 | |
196 | @@ -1102,3 +1102,30 @@ | |
197 | qmp_closefd(fdname, &errp); | |
198 | hmp_handle_error(mon, &errp); | |
199 | } | |
200 | + | |
201 | +void hmp_snapshot_start(Monitor *mon, const QDict *qdict) | |
202 | +{ | |
203 | + Error *errp = NULL; | |
204 | + const char *statefile = qdict_get_str(qdict, "statefile"); | |
205 | + | |
206 | + qmp_snapshot_start(statefile, &errp); | |
207 | + hmp_handle_error(mon, &errp); | |
208 | +} | |
209 | + | |
210 | +void hmp_snapshot_drive(Monitor *mon, const QDict *qdict) | |
211 | +{ | |
212 | + Error *errp = NULL; | |
213 | + const char *name = qdict_get_str(qdict, "name"); | |
214 | + const char *device = qdict_get_str(qdict, "device"); | |
215 | + | |
216 | + qmp_snapshot_drive(device, name, &errp); | |
217 | + hmp_handle_error(mon, &errp); | |
218 | +} | |
219 | + | |
220 | +void hmp_snapshot_end(Monitor *mon, const QDict *qdict) | |
221 | +{ | |
222 | + Error *errp = NULL; | |
223 | + | |
224 | + qmp_snapshot_end(&errp); | |
225 | + hmp_handle_error(mon, &errp); | |
226 | +} | |
227 | Index: new/hmp.h | |
228 | =================================================================== | |
229 | --- new.orig/hmp.h 2012-09-04 12:52:21.000000000 +0200 | |
230 | +++ new/hmp.h 2012-09-04 12:53:35.000000000 +0200 | |
231 | @@ -71,5 +71,8 @@ | |
232 | void hmp_netdev_del(Monitor *mon, const QDict *qdict); | |
233 | void hmp_getfd(Monitor *mon, const QDict *qdict); | |
234 | void hmp_closefd(Monitor *mon, const QDict *qdict); | |
235 | +void hmp_snapshot_start(Monitor *mon, const QDict *qdict); | |
236 | +void hmp_snapshot_drive(Monitor *mon, const QDict *qdict); | |
237 | +void hmp_snapshot_end(Monitor *mon, const QDict *qdict); | |
238 | ||
239 | #endif | |
240 | Index: new/hmp-commands.hx | |
241 | =================================================================== | |
242 | --- new.orig/hmp-commands.hx 2012-09-04 12:52:21.000000000 +0200 | |
243 | +++ new/hmp-commands.hx 2012-09-04 12:53:35.000000000 +0200 | |
244 | @@ -1494,3 +1494,28 @@ | |
245 | STEXI | |
246 | @end table | |
247 | ETEXI | |
248 | + | |
249 | + { | |
250 | + .name = "snapshot-start", | |
251 | + .args_type = "statefile:s", | |
252 | + .params = "statefile", | |
253 | + .help = "Prepare for snapshot and halt VM. Save VM state to statefile.", | |
254 | + .mhandler.cmd = hmp_snapshot_start, | |
255 | + }, | |
256 | + | |
257 | + | |
258 | + { | |
259 | + .name = "snapshot-drive", | |
260 | + .args_type = "device:s,name:s", | |
261 | + .params = "device name", | |
262 | + .help = "Create internal snapshot.", | |
263 | + .mhandler.cmd = hmp_snapshot_drive, | |
264 | + }, | |
265 | + | |
266 | + { | |
267 | + .name = "snapshot-end", | |
268 | + .args_type = "", | |
269 | + .params = "", | |
270 | + .help = "Resume VM after snaphot.", | |
271 | + .mhandler.cmd = hmp_snapshot_end, | |
272 | + }, |