|
1 /* |
|
2 * QEMU System Emulator |
|
3 * |
|
4 * Copyright (c) 2003-2008 Fabrice Bellard |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 #include "qemu-common.h" |
|
25 #include "hw/hw.h" |
|
26 #include "net.h" |
|
27 #include "console.h" |
|
28 #include "sysemu.h" |
|
29 #include "qemu-timer.h" |
|
30 #include "qemu-char.h" |
|
31 #include "block.h" |
|
32 #include "audio/audio.h" |
|
33 #include "migration.h" |
|
34 #include "qemu_socket.h" |
|
35 |
|
36 #include <unistd.h> |
|
37 #include <fcntl.h> |
|
38 #include <signal.h> |
|
39 #include <time.h> |
|
40 #include <errno.h> |
|
41 #include <sys/time.h> |
|
42 #include <zlib.h> |
|
43 |
|
44 #ifndef _WIN32 |
|
45 #include <sys/times.h> |
|
46 #include <sys/wait.h> |
|
47 #include <termios.h> |
|
48 #include <sys/mman.h> |
|
49 #include <sys/ioctl.h> |
|
50 #include <sys/resource.h> |
|
51 #include <sys/socket.h> |
|
52 #include <netinet/in.h> |
|
53 #include <net/if.h> |
|
54 #if defined(__NetBSD__) |
|
55 #include <net/if_tap.h> |
|
56 #endif |
|
57 #ifdef __linux__ |
|
58 #include <linux/if_tun.h> |
|
59 #endif |
|
60 #include <arpa/inet.h> |
|
61 #include <dirent.h> |
|
62 #include <netdb.h> |
|
63 #include <sys/select.h> |
|
64 #ifdef _BSD |
|
65 #include <sys/stat.h> |
|
66 #ifdef __FreeBSD__ |
|
67 #include <libutil.h> |
|
68 #else |
|
69 #include <util.h> |
|
70 #endif |
|
71 #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__) |
|
72 #include <freebsd/stdlib.h> |
|
73 #else |
|
74 #ifdef __linux__ |
|
75 #include <pty.h> |
|
76 #include <malloc.h> |
|
77 #include <linux/rtc.h> |
|
78 #endif |
|
79 #endif |
|
80 #endif |
|
81 |
|
82 #ifdef _WIN32 |
|
83 #include <malloc.h> |
|
84 #include <sys/timeb.h> |
|
85 #include <mmsystem.h> |
|
86 #define getopt_long_only getopt_long |
|
87 #define memalign(align, size) malloc(size) |
|
88 #endif |
|
89 |
|
90 /* point to the block driver where the snapshots are managed */ |
|
91 static BlockDriverState *bs_snapshots; |
|
92 |
|
93 #define SELF_ANNOUNCE_ROUNDS 5 |
|
94 #define ETH_P_EXPERIMENTAL 0x01F1 /* just a number */ |
|
95 //#define ETH_P_EXPERIMENTAL 0x0012 /* make it the size of the packet */ |
|
96 #define EXPERIMENTAL_MAGIC 0xf1f23f4f |
|
97 |
|
98 static int announce_self_create(uint8_t *buf, |
|
99 uint8_t *mac_addr) |
|
100 { |
|
101 uint32_t magic = EXPERIMENTAL_MAGIC; |
|
102 uint16_t proto = htons(ETH_P_EXPERIMENTAL); |
|
103 |
|
104 /* FIXME: should we send a different packet (arp/rarp/ping)? */ |
|
105 |
|
106 memset(buf, 0xff, 6); /* h_dst */ |
|
107 memcpy(buf + 6, mac_addr, 6); /* h_src */ |
|
108 memcpy(buf + 12, &proto, 2); /* h_proto */ |
|
109 memcpy(buf + 14, &magic, 4); /* magic */ |
|
110 |
|
111 return 18; /* len */ |
|
112 } |
|
113 |
|
114 void qemu_announce_self(void) |
|
115 { |
|
116 int i, j, len; |
|
117 VLANState *vlan; |
|
118 VLANClientState *vc; |
|
119 uint8_t buf[256]; |
|
120 |
|
121 for (i = 0; i < nb_nics; i++) { |
|
122 len = announce_self_create(buf, nd_table[i].macaddr); |
|
123 vlan = nd_table[i].vlan; |
|
124 for(vc = vlan->first_client; vc != NULL; vc = vc->next) { |
|
125 for (j=0; j < SELF_ANNOUNCE_ROUNDS; j++) |
|
126 vc->fd_read(vc->opaque, buf, len); |
|
127 } |
|
128 } |
|
129 } |
|
130 |
|
131 /***********************************************************/ |
|
132 /* savevm/loadvm support */ |
|
133 |
|
134 #define IO_BUF_SIZE 32768 |
|
135 |
|
136 struct QEMUFile { |
|
137 QEMUFilePutBufferFunc *put_buffer; |
|
138 QEMUFileGetBufferFunc *get_buffer; |
|
139 QEMUFileCloseFunc *close; |
|
140 QEMUFileRateLimit *rate_limit; |
|
141 void *opaque; |
|
142 int is_write; |
|
143 |
|
144 int64_t buf_offset; /* start of buffer when writing, end of buffer |
|
145 when reading */ |
|
146 int buf_index; |
|
147 int buf_size; /* 0 when writing */ |
|
148 uint8_t buf[IO_BUF_SIZE]; |
|
149 |
|
150 int has_error; |
|
151 }; |
|
152 |
|
153 typedef struct QEMUFilePopen |
|
154 { |
|
155 FILE *popen_file; |
|
156 QEMUFile *file; |
|
157 } QEMUFilePopen; |
|
158 |
|
159 typedef struct QEMUFileSocket |
|
160 { |
|
161 int fd; |
|
162 QEMUFile *file; |
|
163 } QEMUFileSocket; |
|
164 |
|
165 static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) |
|
166 { |
|
167 QEMUFileSocket *s = opaque; |
|
168 ssize_t len; |
|
169 |
|
170 do { |
|
171 len = recv(s->fd, buf, size, 0); |
|
172 } while (len == -1 && socket_error() == EINTR); |
|
173 |
|
174 if (len == -1) |
|
175 len = -socket_error(); |
|
176 |
|
177 return len; |
|
178 } |
|
179 |
|
180 static int socket_close(void *opaque) |
|
181 { |
|
182 QEMUFileSocket *s = opaque; |
|
183 qemu_free(s); |
|
184 return 0; |
|
185 } |
|
186 |
|
187 static int popen_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) |
|
188 { |
|
189 QEMUFilePopen *s = opaque; |
|
190 return fwrite(buf, 1, size, s->popen_file); |
|
191 } |
|
192 |
|
193 static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) |
|
194 { |
|
195 QEMUFilePopen *s = opaque; |
|
196 return fread(buf, 1, size, s->popen_file); |
|
197 } |
|
198 |
|
199 static int popen_close(void *opaque) |
|
200 { |
|
201 QEMUFilePopen *s = opaque; |
|
202 pclose(s->popen_file); |
|
203 qemu_free(s); |
|
204 return 0; |
|
205 } |
|
206 |
|
207 QEMUFile *qemu_popen(FILE *popen_file, const char *mode) |
|
208 { |
|
209 QEMUFilePopen *s; |
|
210 |
|
211 if (popen_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { |
|
212 fprintf(stderr, "qemu_popen: Argument validity check failed\n"); |
|
213 return NULL; |
|
214 } |
|
215 |
|
216 s = qemu_mallocz(sizeof(QEMUFilePopen)); |
|
217 if (!s) { |
|
218 fprintf(stderr, "qemu_popen: malloc failed\n"); |
|
219 return NULL; |
|
220 } |
|
221 |
|
222 s->popen_file = popen_file; |
|
223 |
|
224 if(mode[0] == 'r') { |
|
225 s->file = qemu_fopen_ops(s, NULL, popen_get_buffer, popen_close, NULL); |
|
226 } else { |
|
227 s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL); |
|
228 } |
|
229 fprintf(stderr, "qemu_popen: returning result of qemu_fopen_ops\n"); |
|
230 return s->file; |
|
231 } |
|
232 |
|
233 QEMUFile *qemu_popen_cmd(const char *command, const char *mode) |
|
234 { |
|
235 FILE *popen_file; |
|
236 |
|
237 popen_file = popen(command, mode); |
|
238 if(popen_file == NULL) { |
|
239 return NULL; |
|
240 } |
|
241 |
|
242 return qemu_popen(popen_file, mode); |
|
243 } |
|
244 |
|
245 QEMUFile *qemu_fopen_socket(int fd) |
|
246 { |
|
247 QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); |
|
248 |
|
249 if (s == NULL) |
|
250 return NULL; |
|
251 |
|
252 s->fd = fd; |
|
253 s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, NULL); |
|
254 return s->file; |
|
255 } |
|
256 |
|
257 typedef struct QEMUFileStdio |
|
258 { |
|
259 FILE *outfile; |
|
260 } QEMUFileStdio; |
|
261 |
|
262 static int file_put_buffer(void *opaque, const uint8_t *buf, |
|
263 int64_t pos, int size) |
|
264 { |
|
265 QEMUFileStdio *s = opaque; |
|
266 fseek(s->outfile, pos, SEEK_SET); |
|
267 fwrite(buf, 1, size, s->outfile); |
|
268 return size; |
|
269 } |
|
270 |
|
271 static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) |
|
272 { |
|
273 QEMUFileStdio *s = opaque; |
|
274 fseek(s->outfile, pos, SEEK_SET); |
|
275 return fread(buf, 1, size, s->outfile); |
|
276 } |
|
277 |
|
278 static int file_close(void *opaque) |
|
279 { |
|
280 QEMUFileStdio *s = opaque; |
|
281 fclose(s->outfile); |
|
282 qemu_free(s); |
|
283 return 0; |
|
284 } |
|
285 |
|
286 QEMUFile *qemu_fopen(const char *filename, const char *mode) |
|
287 { |
|
288 QEMUFileStdio *s; |
|
289 |
|
290 s = qemu_mallocz(sizeof(QEMUFileStdio)); |
|
291 if (!s) |
|
292 return NULL; |
|
293 |
|
294 s->outfile = fopen(filename, mode); |
|
295 if (!s->outfile) |
|
296 goto fail; |
|
297 |
|
298 if (!strcmp(mode, "wb")) |
|
299 return qemu_fopen_ops(s, file_put_buffer, NULL, file_close, NULL); |
|
300 else if (!strcmp(mode, "rb")) |
|
301 return qemu_fopen_ops(s, NULL, file_get_buffer, file_close, NULL); |
|
302 |
|
303 fail: |
|
304 if (s->outfile) |
|
305 fclose(s->outfile); |
|
306 qemu_free(s); |
|
307 return NULL; |
|
308 } |
|
309 |
|
310 typedef struct QEMUFileBdrv |
|
311 { |
|
312 BlockDriverState *bs; |
|
313 int64_t base_offset; |
|
314 } QEMUFileBdrv; |
|
315 |
|
316 static int bdrv_put_buffer(void *opaque, const uint8_t *buf, |
|
317 int64_t pos, int size) |
|
318 { |
|
319 QEMUFileBdrv *s = opaque; |
|
320 bdrv_pwrite(s->bs, s->base_offset + pos, buf, size); |
|
321 return size; |
|
322 } |
|
323 |
|
324 static int bdrv_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) |
|
325 { |
|
326 QEMUFileBdrv *s = opaque; |
|
327 return bdrv_pread(s->bs, s->base_offset + pos, buf, size); |
|
328 } |
|
329 |
|
330 static int bdrv_fclose(void *opaque) |
|
331 { |
|
332 QEMUFileBdrv *s = opaque; |
|
333 qemu_free(s); |
|
334 return 0; |
|
335 } |
|
336 |
|
337 static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) |
|
338 { |
|
339 QEMUFileBdrv *s; |
|
340 |
|
341 s = qemu_mallocz(sizeof(QEMUFileBdrv)); |
|
342 if (!s) |
|
343 return NULL; |
|
344 |
|
345 s->bs = bs; |
|
346 s->base_offset = offset; |
|
347 |
|
348 if (is_writable) |
|
349 return qemu_fopen_ops(s, bdrv_put_buffer, NULL, bdrv_fclose, NULL); |
|
350 |
|
351 return qemu_fopen_ops(s, NULL, bdrv_get_buffer, bdrv_fclose, NULL); |
|
352 } |
|
353 |
|
354 QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, |
|
355 QEMUFileGetBufferFunc *get_buffer, |
|
356 QEMUFileCloseFunc *close, |
|
357 QEMUFileRateLimit *rate_limit) |
|
358 { |
|
359 QEMUFile *f; |
|
360 |
|
361 f = qemu_mallocz(sizeof(QEMUFile)); |
|
362 if (!f) |
|
363 return NULL; |
|
364 |
|
365 f->opaque = opaque; |
|
366 f->put_buffer = put_buffer; |
|
367 f->get_buffer = get_buffer; |
|
368 f->close = close; |
|
369 f->rate_limit = rate_limit; |
|
370 f->is_write = 0; |
|
371 |
|
372 return f; |
|
373 } |
|
374 |
|
375 int qemu_file_has_error(QEMUFile *f) |
|
376 { |
|
377 return f->has_error; |
|
378 } |
|
379 |
|
380 void qemu_fflush(QEMUFile *f) |
|
381 { |
|
382 if (!f->put_buffer) |
|
383 return; |
|
384 |
|
385 if (f->is_write && f->buf_index > 0) { |
|
386 int len; |
|
387 |
|
388 len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index); |
|
389 if (len > 0) |
|
390 f->buf_offset += f->buf_index; |
|
391 else |
|
392 f->has_error = 1; |
|
393 f->buf_index = 0; |
|
394 } |
|
395 } |
|
396 |
|
397 static void qemu_fill_buffer(QEMUFile *f) |
|
398 { |
|
399 int len; |
|
400 |
|
401 if (!f->get_buffer) |
|
402 return; |
|
403 |
|
404 if (f->is_write) |
|
405 abort(); |
|
406 |
|
407 len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE); |
|
408 if (len > 0) { |
|
409 f->buf_index = 0; |
|
410 f->buf_size = len; |
|
411 f->buf_offset += len; |
|
412 } else if (len != -EAGAIN) |
|
413 f->has_error = 1; |
|
414 } |
|
415 |
|
416 int qemu_fclose(QEMUFile *f) |
|
417 { |
|
418 int ret = 0; |
|
419 qemu_fflush(f); |
|
420 if (f->close) |
|
421 ret = f->close(f->opaque); |
|
422 qemu_free(f); |
|
423 return ret; |
|
424 } |
|
425 |
|
426 void qemu_file_put_notify(QEMUFile *f) |
|
427 { |
|
428 f->put_buffer(f->opaque, NULL, 0, 0); |
|
429 } |
|
430 |
|
431 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) |
|
432 { |
|
433 int l; |
|
434 |
|
435 if (!f->has_error && f->is_write == 0 && f->buf_index > 0) { |
|
436 fprintf(stderr, |
|
437 "Attempted to write to buffer while read buffer is not empty\n"); |
|
438 abort(); |
|
439 } |
|
440 |
|
441 while (!f->has_error && size > 0) { |
|
442 l = IO_BUF_SIZE - f->buf_index; |
|
443 if (l > size) |
|
444 l = size; |
|
445 memcpy(f->buf + f->buf_index, buf, l); |
|
446 f->is_write = 1; |
|
447 f->buf_index += l; |
|
448 buf += l; |
|
449 size -= l; |
|
450 if (f->buf_index >= IO_BUF_SIZE) |
|
451 qemu_fflush(f); |
|
452 } |
|
453 } |
|
454 |
|
455 void qemu_put_byte(QEMUFile *f, int v) |
|
456 { |
|
457 if (!f->has_error && f->is_write == 0 && f->buf_index > 0) { |
|
458 fprintf(stderr, |
|
459 "Attempted to write to buffer while read buffer is not empty\n"); |
|
460 abort(); |
|
461 } |
|
462 |
|
463 f->buf[f->buf_index++] = v; |
|
464 f->is_write = 1; |
|
465 if (f->buf_index >= IO_BUF_SIZE) |
|
466 qemu_fflush(f); |
|
467 } |
|
468 |
|
469 int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1) |
|
470 { |
|
471 int size, l; |
|
472 |
|
473 if (f->is_write) |
|
474 abort(); |
|
475 |
|
476 size = size1; |
|
477 while (size > 0) { |
|
478 l = f->buf_size - f->buf_index; |
|
479 if (l == 0) { |
|
480 qemu_fill_buffer(f); |
|
481 l = f->buf_size - f->buf_index; |
|
482 if (l == 0) |
|
483 break; |
|
484 } |
|
485 if (l > size) |
|
486 l = size; |
|
487 memcpy(buf, f->buf + f->buf_index, l); |
|
488 f->buf_index += l; |
|
489 buf += l; |
|
490 size -= l; |
|
491 } |
|
492 return size1 - size; |
|
493 } |
|
494 |
|
495 int qemu_get_byte(QEMUFile *f) |
|
496 { |
|
497 if (f->is_write) |
|
498 abort(); |
|
499 |
|
500 if (f->buf_index >= f->buf_size) { |
|
501 qemu_fill_buffer(f); |
|
502 if (f->buf_index >= f->buf_size) |
|
503 return 0; |
|
504 } |
|
505 return f->buf[f->buf_index++]; |
|
506 } |
|
507 |
|
508 int64_t qemu_ftell(QEMUFile *f) |
|
509 { |
|
510 return f->buf_offset - f->buf_size + f->buf_index; |
|
511 } |
|
512 |
|
513 int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) |
|
514 { |
|
515 if (whence == SEEK_SET) { |
|
516 /* nothing to do */ |
|
517 } else if (whence == SEEK_CUR) { |
|
518 pos += qemu_ftell(f); |
|
519 } else { |
|
520 /* SEEK_END not supported */ |
|
521 return -1; |
|
522 } |
|
523 if (f->put_buffer) { |
|
524 qemu_fflush(f); |
|
525 f->buf_offset = pos; |
|
526 } else { |
|
527 f->buf_offset = pos; |
|
528 f->buf_index = 0; |
|
529 f->buf_size = 0; |
|
530 } |
|
531 return pos; |
|
532 } |
|
533 |
|
534 int qemu_file_rate_limit(QEMUFile *f) |
|
535 { |
|
536 if (f->rate_limit) |
|
537 return f->rate_limit(f->opaque); |
|
538 |
|
539 return 0; |
|
540 } |
|
541 |
|
542 void qemu_put_be16(QEMUFile *f, unsigned int v) |
|
543 { |
|
544 qemu_put_byte(f, v >> 8); |
|
545 qemu_put_byte(f, v); |
|
546 } |
|
547 |
|
548 void qemu_put_be32(QEMUFile *f, unsigned int v) |
|
549 { |
|
550 qemu_put_byte(f, v >> 24); |
|
551 qemu_put_byte(f, v >> 16); |
|
552 qemu_put_byte(f, v >> 8); |
|
553 qemu_put_byte(f, v); |
|
554 } |
|
555 |
|
556 void qemu_put_be64(QEMUFile *f, uint64_t v) |
|
557 { |
|
558 qemu_put_be32(f, v >> 32); |
|
559 qemu_put_be32(f, v); |
|
560 } |
|
561 |
|
562 unsigned int qemu_get_be16(QEMUFile *f) |
|
563 { |
|
564 unsigned int v; |
|
565 v = qemu_get_byte(f) << 8; |
|
566 v |= qemu_get_byte(f); |
|
567 return v; |
|
568 } |
|
569 |
|
570 unsigned int qemu_get_be32(QEMUFile *f) |
|
571 { |
|
572 unsigned int v; |
|
573 v = qemu_get_byte(f) << 24; |
|
574 v |= qemu_get_byte(f) << 16; |
|
575 v |= qemu_get_byte(f) << 8; |
|
576 v |= qemu_get_byte(f); |
|
577 return v; |
|
578 } |
|
579 |
|
580 uint64_t qemu_get_be64(QEMUFile *f) |
|
581 { |
|
582 uint64_t v; |
|
583 v = (uint64_t)qemu_get_be32(f) << 32; |
|
584 v |= qemu_get_be32(f); |
|
585 return v; |
|
586 } |
|
587 |
|
588 typedef struct SaveStateEntry { |
|
589 char idstr[256]; |
|
590 int instance_id; |
|
591 int version_id; |
|
592 int section_id; |
|
593 SaveLiveStateHandler *save_live_state; |
|
594 SaveStateHandler *save_state; |
|
595 LoadStateHandler *load_state; |
|
596 void *opaque; |
|
597 struct SaveStateEntry *next; |
|
598 } SaveStateEntry; |
|
599 |
|
600 static SaveStateEntry *first_se; |
|
601 |
|
602 /* TODO: Individual devices generally have very little idea about the rest |
|
603 of the system, so instance_id should be removed/replaced. |
|
604 Meanwhile pass -1 as instance_id if you do not already have a clearly |
|
605 distinguishing id for all instances of your device class. */ |
|
606 int register_savevm_live(const char *idstr, |
|
607 int instance_id, |
|
608 int version_id, |
|
609 SaveLiveStateHandler *save_live_state, |
|
610 SaveStateHandler *save_state, |
|
611 LoadStateHandler *load_state, |
|
612 void *opaque) |
|
613 { |
|
614 SaveStateEntry *se, **pse; |
|
615 static int global_section_id; |
|
616 |
|
617 se = qemu_malloc(sizeof(SaveStateEntry)); |
|
618 if (!se) |
|
619 return -1; |
|
620 pstrcpy(se->idstr, sizeof(se->idstr), idstr); |
|
621 se->instance_id = (instance_id == -1) ? 0 : instance_id; |
|
622 se->version_id = version_id; |
|
623 se->section_id = global_section_id++; |
|
624 se->save_live_state = save_live_state; |
|
625 se->save_state = save_state; |
|
626 se->load_state = load_state; |
|
627 se->opaque = opaque; |
|
628 se->next = NULL; |
|
629 |
|
630 /* add at the end of list */ |
|
631 pse = &first_se; |
|
632 while (*pse != NULL) { |
|
633 if (instance_id == -1 |
|
634 && strcmp(se->idstr, (*pse)->idstr) == 0 |
|
635 && se->instance_id <= (*pse)->instance_id) |
|
636 se->instance_id = (*pse)->instance_id + 1; |
|
637 pse = &(*pse)->next; |
|
638 } |
|
639 *pse = se; |
|
640 return 0; |
|
641 } |
|
642 |
|
643 int register_savevm(const char *idstr, |
|
644 int instance_id, |
|
645 int version_id, |
|
646 SaveStateHandler *save_state, |
|
647 LoadStateHandler *load_state, |
|
648 void *opaque) |
|
649 { |
|
650 return register_savevm_live(idstr, instance_id, version_id, |
|
651 NULL, save_state, load_state, opaque); |
|
652 } |
|
653 |
|
654 #define QEMU_VM_FILE_MAGIC 0x5145564d |
|
655 #define QEMU_VM_FILE_VERSION_COMPAT 0x00000002 |
|
656 #define QEMU_VM_FILE_VERSION 0x00000003 |
|
657 |
|
658 #define QEMU_VM_EOF 0x00 |
|
659 #define QEMU_VM_SECTION_START 0x01 |
|
660 #define QEMU_VM_SECTION_PART 0x02 |
|
661 #define QEMU_VM_SECTION_END 0x03 |
|
662 #define QEMU_VM_SECTION_FULL 0x04 |
|
663 |
|
664 int qemu_savevm_state_begin(QEMUFile *f) |
|
665 { |
|
666 SaveStateEntry *se; |
|
667 |
|
668 qemu_put_be32(f, QEMU_VM_FILE_MAGIC); |
|
669 qemu_put_be32(f, QEMU_VM_FILE_VERSION); |
|
670 |
|
671 for (se = first_se; se != NULL; se = se->next) { |
|
672 int len; |
|
673 |
|
674 if (se->save_live_state == NULL) |
|
675 continue; |
|
676 |
|
677 /* Section type */ |
|
678 qemu_put_byte(f, QEMU_VM_SECTION_START); |
|
679 qemu_put_be32(f, se->section_id); |
|
680 |
|
681 /* ID string */ |
|
682 len = strlen(se->idstr); |
|
683 qemu_put_byte(f, len); |
|
684 qemu_put_buffer(f, (uint8_t *)se->idstr, len); |
|
685 |
|
686 qemu_put_be32(f, se->instance_id); |
|
687 qemu_put_be32(f, se->version_id); |
|
688 |
|
689 se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque); |
|
690 } |
|
691 |
|
692 if (qemu_file_has_error(f)) |
|
693 return -EIO; |
|
694 |
|
695 return 0; |
|
696 } |
|
697 |
|
698 int qemu_savevm_state_iterate(QEMUFile *f) |
|
699 { |
|
700 SaveStateEntry *se; |
|
701 int ret = 1; |
|
702 |
|
703 for (se = first_se; se != NULL; se = se->next) { |
|
704 if (se->save_live_state == NULL) |
|
705 continue; |
|
706 |
|
707 /* Section type */ |
|
708 qemu_put_byte(f, QEMU_VM_SECTION_PART); |
|
709 qemu_put_be32(f, se->section_id); |
|
710 |
|
711 ret &= !!se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque); |
|
712 } |
|
713 |
|
714 if (ret) |
|
715 return 1; |
|
716 |
|
717 if (qemu_file_has_error(f)) |
|
718 return -EIO; |
|
719 |
|
720 return 0; |
|
721 } |
|
722 |
|
723 int qemu_savevm_state_complete(QEMUFile *f) |
|
724 { |
|
725 SaveStateEntry *se; |
|
726 |
|
727 for (se = first_se; se != NULL; se = se->next) { |
|
728 if (se->save_live_state == NULL) |
|
729 continue; |
|
730 |
|
731 /* Section type */ |
|
732 qemu_put_byte(f, QEMU_VM_SECTION_END); |
|
733 qemu_put_be32(f, se->section_id); |
|
734 |
|
735 se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque); |
|
736 } |
|
737 |
|
738 for(se = first_se; se != NULL; se = se->next) { |
|
739 int len; |
|
740 |
|
741 if (se->save_state == NULL) |
|
742 continue; |
|
743 |
|
744 /* Section type */ |
|
745 qemu_put_byte(f, QEMU_VM_SECTION_FULL); |
|
746 qemu_put_be32(f, se->section_id); |
|
747 |
|
748 /* ID string */ |
|
749 len = strlen(se->idstr); |
|
750 qemu_put_byte(f, len); |
|
751 qemu_put_buffer(f, (uint8_t *)se->idstr, len); |
|
752 |
|
753 qemu_put_be32(f, se->instance_id); |
|
754 qemu_put_be32(f, se->version_id); |
|
755 |
|
756 se->save_state(f, se->opaque); |
|
757 } |
|
758 |
|
759 qemu_put_byte(f, QEMU_VM_EOF); |
|
760 |
|
761 if (qemu_file_has_error(f)) |
|
762 return -EIO; |
|
763 |
|
764 return 0; |
|
765 } |
|
766 |
|
767 int qemu_savevm_state(QEMUFile *f) |
|
768 { |
|
769 int saved_vm_running; |
|
770 int ret; |
|
771 |
|
772 saved_vm_running = vm_running; |
|
773 vm_stop(0); |
|
774 |
|
775 bdrv_flush_all(); |
|
776 |
|
777 ret = qemu_savevm_state_begin(f); |
|
778 if (ret < 0) |
|
779 goto out; |
|
780 |
|
781 do { |
|
782 ret = qemu_savevm_state_iterate(f); |
|
783 if (ret < 0) |
|
784 goto out; |
|
785 } while (ret == 0); |
|
786 |
|
787 ret = qemu_savevm_state_complete(f); |
|
788 |
|
789 out: |
|
790 if (qemu_file_has_error(f)) |
|
791 ret = -EIO; |
|
792 |
|
793 if (!ret && saved_vm_running) |
|
794 vm_start(); |
|
795 |
|
796 return ret; |
|
797 } |
|
798 |
|
799 static SaveStateEntry *find_se(const char *idstr, int instance_id) |
|
800 { |
|
801 SaveStateEntry *se; |
|
802 |
|
803 for(se = first_se; se != NULL; se = se->next) { |
|
804 if (!strcmp(se->idstr, idstr) && |
|
805 instance_id == se->instance_id) |
|
806 return se; |
|
807 } |
|
808 return NULL; |
|
809 } |
|
810 |
|
811 typedef struct LoadStateEntry { |
|
812 SaveStateEntry *se; |
|
813 int section_id; |
|
814 int version_id; |
|
815 struct LoadStateEntry *next; |
|
816 } LoadStateEntry; |
|
817 |
|
818 static int qemu_loadvm_state_v2(QEMUFile *f) |
|
819 { |
|
820 SaveStateEntry *se; |
|
821 int len, ret, instance_id, record_len, version_id; |
|
822 int64_t total_len, end_pos, cur_pos; |
|
823 char idstr[256]; |
|
824 |
|
825 total_len = qemu_get_be64(f); |
|
826 end_pos = total_len + qemu_ftell(f); |
|
827 for(;;) { |
|
828 if (qemu_ftell(f) >= end_pos) |
|
829 break; |
|
830 len = qemu_get_byte(f); |
|
831 qemu_get_buffer(f, (uint8_t *)idstr, len); |
|
832 idstr[len] = '\0'; |
|
833 instance_id = qemu_get_be32(f); |
|
834 version_id = qemu_get_be32(f); |
|
835 record_len = qemu_get_be32(f); |
|
836 cur_pos = qemu_ftell(f); |
|
837 se = find_se(idstr, instance_id); |
|
838 if (!se) { |
|
839 fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", |
|
840 instance_id, idstr); |
|
841 } else { |
|
842 ret = se->load_state(f, se->opaque, version_id); |
|
843 if (ret < 0) { |
|
844 fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", |
|
845 instance_id, idstr); |
|
846 } |
|
847 } |
|
848 /* always seek to exact end of record */ |
|
849 qemu_fseek(f, cur_pos + record_len, SEEK_SET); |
|
850 } |
|
851 |
|
852 if (qemu_file_has_error(f)) |
|
853 return -EIO; |
|
854 |
|
855 return 0; |
|
856 } |
|
857 |
|
858 int qemu_loadvm_state(QEMUFile *f) |
|
859 { |
|
860 LoadStateEntry *first_le = NULL; |
|
861 uint8_t section_type; |
|
862 unsigned int v; |
|
863 int ret; |
|
864 |
|
865 v = qemu_get_be32(f); |
|
866 if (v != QEMU_VM_FILE_MAGIC) |
|
867 return -EINVAL; |
|
868 |
|
869 v = qemu_get_be32(f); |
|
870 if (v == QEMU_VM_FILE_VERSION_COMPAT) |
|
871 return qemu_loadvm_state_v2(f); |
|
872 if (v != QEMU_VM_FILE_VERSION) |
|
873 return -ENOTSUP; |
|
874 |
|
875 while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { |
|
876 uint32_t instance_id, version_id, section_id; |
|
877 LoadStateEntry *le; |
|
878 SaveStateEntry *se; |
|
879 char idstr[257]; |
|
880 int len; |
|
881 |
|
882 switch (section_type) { |
|
883 case QEMU_VM_SECTION_START: |
|
884 case QEMU_VM_SECTION_FULL: |
|
885 /* Read section start */ |
|
886 section_id = qemu_get_be32(f); |
|
887 len = qemu_get_byte(f); |
|
888 qemu_get_buffer(f, (uint8_t *)idstr, len); |
|
889 idstr[len] = 0; |
|
890 instance_id = qemu_get_be32(f); |
|
891 version_id = qemu_get_be32(f); |
|
892 |
|
893 /* Find savevm section */ |
|
894 se = find_se(idstr, instance_id); |
|
895 if (se == NULL) { |
|
896 fprintf(stderr, "Unknown savevm section or instance '%s' %d\n", idstr, instance_id); |
|
897 ret = -EINVAL; |
|
898 goto out; |
|
899 } |
|
900 |
|
901 /* Validate version */ |
|
902 if (version_id > se->version_id) { |
|
903 fprintf(stderr, "savevm: unsupported version %d for '%s' v%d\n", |
|
904 version_id, idstr, se->version_id); |
|
905 ret = -EINVAL; |
|
906 goto out; |
|
907 } |
|
908 |
|
909 /* Add entry */ |
|
910 le = qemu_mallocz(sizeof(*le)); |
|
911 if (le == NULL) { |
|
912 ret = -ENOMEM; |
|
913 goto out; |
|
914 } |
|
915 |
|
916 le->se = se; |
|
917 le->section_id = section_id; |
|
918 le->version_id = version_id; |
|
919 le->next = first_le; |
|
920 first_le = le; |
|
921 |
|
922 le->se->load_state(f, le->se->opaque, le->version_id); |
|
923 break; |
|
924 case QEMU_VM_SECTION_PART: |
|
925 case QEMU_VM_SECTION_END: |
|
926 section_id = qemu_get_be32(f); |
|
927 |
|
928 for (le = first_le; le && le->section_id != section_id; le = le->next); |
|
929 if (le == NULL) { |
|
930 fprintf(stderr, "Unknown savevm section %d\n", section_id); |
|
931 ret = -EINVAL; |
|
932 goto out; |
|
933 } |
|
934 |
|
935 le->se->load_state(f, le->se->opaque, le->version_id); |
|
936 break; |
|
937 default: |
|
938 fprintf(stderr, "Unknown savevm section type %d\n", section_type); |
|
939 ret = -EINVAL; |
|
940 goto out; |
|
941 } |
|
942 } |
|
943 |
|
944 ret = 0; |
|
945 |
|
946 out: |
|
947 while (first_le) { |
|
948 LoadStateEntry *le = first_le; |
|
949 first_le = first_le->next; |
|
950 qemu_free(le); |
|
951 } |
|
952 |
|
953 if (qemu_file_has_error(f)) |
|
954 ret = -EIO; |
|
955 |
|
956 return ret; |
|
957 } |
|
958 |
|
959 /* device can contain snapshots */ |
|
960 static int bdrv_can_snapshot(BlockDriverState *bs) |
|
961 { |
|
962 return (bs && |
|
963 !bdrv_is_removable(bs) && |
|
964 !bdrv_is_read_only(bs)); |
|
965 } |
|
966 |
|
967 /* device must be snapshots in order to have a reliable snapshot */ |
|
968 static int bdrv_has_snapshot(BlockDriverState *bs) |
|
969 { |
|
970 return (bs && |
|
971 !bdrv_is_removable(bs) && |
|
972 !bdrv_is_read_only(bs)); |
|
973 } |
|
974 |
|
975 static BlockDriverState *get_bs_snapshots(void) |
|
976 { |
|
977 BlockDriverState *bs; |
|
978 int i; |
|
979 |
|
980 if (bs_snapshots) |
|
981 return bs_snapshots; |
|
982 for(i = 0; i <= nb_drives; i++) { |
|
983 bs = drives_table[i].bdrv; |
|
984 if (bdrv_can_snapshot(bs)) |
|
985 goto ok; |
|
986 } |
|
987 return NULL; |
|
988 ok: |
|
989 bs_snapshots = bs; |
|
990 return bs; |
|
991 } |
|
992 |
|
993 static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, |
|
994 const char *name) |
|
995 { |
|
996 QEMUSnapshotInfo *sn_tab, *sn; |
|
997 int nb_sns, i, ret; |
|
998 |
|
999 ret = -ENOENT; |
|
1000 nb_sns = bdrv_snapshot_list(bs, &sn_tab); |
|
1001 if (nb_sns < 0) |
|
1002 return ret; |
|
1003 for(i = 0; i < nb_sns; i++) { |
|
1004 sn = &sn_tab[i]; |
|
1005 if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { |
|
1006 *sn_info = *sn; |
|
1007 ret = 0; |
|
1008 break; |
|
1009 } |
|
1010 } |
|
1011 qemu_free(sn_tab); |
|
1012 return ret; |
|
1013 } |
|
1014 |
|
1015 static void do_savevm_file(const char *name) |
|
1016 { |
|
1017 QEMUFile *f; |
|
1018 int saved_vm_running; |
|
1019 |
|
1020 qemu_aio_flush(); |
|
1021 saved_vm_running = vm_running; |
|
1022 vm_stop(0); |
|
1023 |
|
1024 f = qemu_fopen(name, "wb"); |
|
1025 if (!f) { |
|
1026 term_printf("Unable to create snapshot\n"); |
|
1027 return; |
|
1028 } |
|
1029 |
|
1030 qemu_savevm_state(f); |
|
1031 qemu_fclose(f); |
|
1032 |
|
1033 if (saved_vm_running) |
|
1034 vm_start(); |
|
1035 } |
|
1036 |
|
1037 void do_savevm(const char *name) |
|
1038 { |
|
1039 BlockDriverState *bs, *bs1; |
|
1040 QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; |
|
1041 int must_delete, ret, i; |
|
1042 BlockDriverInfo bdi1, *bdi = &bdi1; |
|
1043 QEMUFile *f; |
|
1044 int saved_vm_running; |
|
1045 uint32_t vm_state_size; |
|
1046 #ifdef _WIN32 |
|
1047 struct _timeb tb; |
|
1048 #else |
|
1049 struct timeval tv; |
|
1050 #endif |
|
1051 |
|
1052 if (strncmp(name, "file:", 5) == 0) { |
|
1053 do_savevm_file(name + 5); |
|
1054 return; |
|
1055 } |
|
1056 |
|
1057 bs = get_bs_snapshots(); |
|
1058 if (!bs) { |
|
1059 term_printf("No block device can accept snapshots\n"); |
|
1060 return; |
|
1061 } |
|
1062 |
|
1063 /* ??? Should this occur after vm_stop? */ |
|
1064 qemu_aio_flush(); |
|
1065 |
|
1066 saved_vm_running = vm_running; |
|
1067 vm_stop(0); |
|
1068 |
|
1069 must_delete = 0; |
|
1070 if (name) { |
|
1071 ret = bdrv_snapshot_find(bs, old_sn, name); |
|
1072 if (ret >= 0) { |
|
1073 must_delete = 1; |
|
1074 } |
|
1075 } |
|
1076 memset(sn, 0, sizeof(*sn)); |
|
1077 if (must_delete) { |
|
1078 pstrcpy(sn->name, sizeof(sn->name), old_sn->name); |
|
1079 pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str); |
|
1080 } else { |
|
1081 if (name) |
|
1082 pstrcpy(sn->name, sizeof(sn->name), name); |
|
1083 } |
|
1084 |
|
1085 /* fill auxiliary fields */ |
|
1086 #ifdef _WIN32 |
|
1087 _ftime(&tb); |
|
1088 sn->date_sec = tb.time; |
|
1089 sn->date_nsec = tb.millitm * 1000000; |
|
1090 #else |
|
1091 gettimeofday(&tv, NULL); |
|
1092 sn->date_sec = tv.tv_sec; |
|
1093 sn->date_nsec = tv.tv_usec * 1000; |
|
1094 #endif |
|
1095 sn->vm_clock_nsec = qemu_get_clock(vm_clock); |
|
1096 |
|
1097 if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { |
|
1098 term_printf("Device %s does not support VM state snapshots\n", |
|
1099 bdrv_get_device_name(bs)); |
|
1100 goto the_end; |
|
1101 } |
|
1102 |
|
1103 /* save the VM state */ |
|
1104 f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1); |
|
1105 if (!f) { |
|
1106 term_printf("Could not open VM state file\n"); |
|
1107 goto the_end; |
|
1108 } |
|
1109 ret = qemu_savevm_state(f); |
|
1110 vm_state_size = qemu_ftell(f); |
|
1111 qemu_fclose(f); |
|
1112 if (ret < 0) { |
|
1113 term_printf("Error %d while writing VM\n", ret); |
|
1114 goto the_end; |
|
1115 } |
|
1116 |
|
1117 /* create the snapshots */ |
|
1118 |
|
1119 for(i = 0; i < nb_drives; i++) { |
|
1120 bs1 = drives_table[i].bdrv; |
|
1121 if (bdrv_has_snapshot(bs1)) { |
|
1122 if (must_delete) { |
|
1123 ret = bdrv_snapshot_delete(bs1, old_sn->id_str); |
|
1124 if (ret < 0) { |
|
1125 term_printf("Error while deleting snapshot on '%s'\n", |
|
1126 bdrv_get_device_name(bs1)); |
|
1127 } |
|
1128 } |
|
1129 /* Write VM state size only to the image that contains the state */ |
|
1130 sn->vm_state_size = (bs == bs1 ? vm_state_size : 0); |
|
1131 ret = bdrv_snapshot_create(bs1, sn); |
|
1132 if (ret < 0) { |
|
1133 term_printf("Error while creating snapshot on '%s'\n", |
|
1134 bdrv_get_device_name(bs1)); |
|
1135 } |
|
1136 } |
|
1137 } |
|
1138 |
|
1139 the_end: |
|
1140 if (saved_vm_running) |
|
1141 vm_start(); |
|
1142 } |
|
1143 |
|
1144 static void do_loadvm_file(const char *name) |
|
1145 { |
|
1146 QEMUFile *f; |
|
1147 int saved_vm_running; |
|
1148 int ret; |
|
1149 |
|
1150 qemu_aio_flush(); |
|
1151 |
|
1152 saved_vm_running = vm_running; |
|
1153 vm_stop(0); |
|
1154 |
|
1155 f = qemu_fopen(name, "rb"); |
|
1156 if (!f) { |
|
1157 term_printf("Unable to open snapshot\n"); |
|
1158 return; |
|
1159 } |
|
1160 |
|
1161 ret = qemu_loadvm_state(f); |
|
1162 qemu_fclose(f); |
|
1163 if (ret < 0) { |
|
1164 term_printf("Error %d while loading VM state\n", ret); |
|
1165 } |
|
1166 |
|
1167 if (saved_vm_running) |
|
1168 vm_start(); |
|
1169 } |
|
1170 |
|
1171 void do_loadvm(const char *name) |
|
1172 { |
|
1173 BlockDriverState *bs, *bs1; |
|
1174 BlockDriverInfo bdi1, *bdi = &bdi1; |
|
1175 QEMUSnapshotInfo sn; |
|
1176 QEMUFile *f; |
|
1177 int i, ret; |
|
1178 int saved_vm_running; |
|
1179 |
|
1180 if (strncmp(name, "file:", 5) == 0) { |
|
1181 do_loadvm_file(name + 5); |
|
1182 return; |
|
1183 } |
|
1184 bs = get_bs_snapshots(); |
|
1185 if (!bs) { |
|
1186 term_printf("No block device supports snapshots\n"); |
|
1187 return; |
|
1188 } |
|
1189 |
|
1190 /* Flush all IO requests so they don't interfere with the new state. */ |
|
1191 qemu_aio_flush(); |
|
1192 |
|
1193 saved_vm_running = vm_running; |
|
1194 vm_stop(0); |
|
1195 |
|
1196 for(i = 0; i <= nb_drives; i++) { |
|
1197 bs1 = drives_table[i].bdrv; |
|
1198 if (bdrv_has_snapshot(bs1)) { |
|
1199 ret = bdrv_snapshot_goto(bs1, name); |
|
1200 if (ret < 0) { |
|
1201 if (bs != bs1) |
|
1202 term_printf("Warning: "); |
|
1203 switch(ret) { |
|
1204 case -ENOTSUP: |
|
1205 term_printf("Snapshots not supported on device '%s'\n", |
|
1206 bdrv_get_device_name(bs1)); |
|
1207 break; |
|
1208 case -ENOENT: |
|
1209 term_printf("Could not find snapshot '%s' on device '%s'\n", |
|
1210 name, bdrv_get_device_name(bs1)); |
|
1211 break; |
|
1212 default: |
|
1213 term_printf("Error %d while activating snapshot on '%s'\n", |
|
1214 ret, bdrv_get_device_name(bs1)); |
|
1215 break; |
|
1216 } |
|
1217 /* fatal on snapshot block device */ |
|
1218 if (bs == bs1) |
|
1219 goto the_end; |
|
1220 } |
|
1221 } |
|
1222 } |
|
1223 |
|
1224 if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { |
|
1225 term_printf("Device %s does not support VM state snapshots\n", |
|
1226 bdrv_get_device_name(bs)); |
|
1227 return; |
|
1228 } |
|
1229 |
|
1230 /* Don't even try to load empty VM states */ |
|
1231 ret = bdrv_snapshot_find(bs, &sn, name); |
|
1232 if ((ret >= 0) && (sn.vm_state_size == 0)) |
|
1233 goto the_end; |
|
1234 |
|
1235 /* restore the VM state */ |
|
1236 f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0); |
|
1237 if (!f) { |
|
1238 term_printf("Could not open VM state file\n"); |
|
1239 goto the_end; |
|
1240 } |
|
1241 ret = qemu_loadvm_state(f); |
|
1242 qemu_fclose(f); |
|
1243 if (ret < 0) { |
|
1244 term_printf("Error %d while loading VM state\n", ret); |
|
1245 } |
|
1246 the_end: |
|
1247 if (saved_vm_running) |
|
1248 vm_start(); |
|
1249 } |
|
1250 |
|
1251 void do_delvm(const char *name) |
|
1252 { |
|
1253 BlockDriverState *bs, *bs1; |
|
1254 int i, ret; |
|
1255 |
|
1256 bs = get_bs_snapshots(); |
|
1257 if (!bs) { |
|
1258 term_printf("No block device supports snapshots\n"); |
|
1259 return; |
|
1260 } |
|
1261 |
|
1262 for(i = 0; i <= nb_drives; i++) { |
|
1263 bs1 = drives_table[i].bdrv; |
|
1264 if (bdrv_has_snapshot(bs1)) { |
|
1265 ret = bdrv_snapshot_delete(bs1, name); |
|
1266 if (ret < 0) { |
|
1267 if (ret == -ENOTSUP) |
|
1268 term_printf("Snapshots not supported on device '%s'\n", |
|
1269 bdrv_get_device_name(bs1)); |
|
1270 else |
|
1271 term_printf("Error %d while deleting snapshot on '%s'\n", |
|
1272 ret, bdrv_get_device_name(bs1)); |
|
1273 } |
|
1274 } |
|
1275 } |
|
1276 } |
|
1277 |
|
1278 void do_info_snapshots(void) |
|
1279 { |
|
1280 BlockDriverState *bs, *bs1; |
|
1281 QEMUSnapshotInfo *sn_tab, *sn; |
|
1282 int nb_sns, i; |
|
1283 char buf[256]; |
|
1284 |
|
1285 bs = get_bs_snapshots(); |
|
1286 if (!bs) { |
|
1287 term_printf("No available block device supports snapshots\n"); |
|
1288 return; |
|
1289 } |
|
1290 term_printf("Snapshot devices:"); |
|
1291 for(i = 0; i <= nb_drives; i++) { |
|
1292 bs1 = drives_table[i].bdrv; |
|
1293 if (bdrv_has_snapshot(bs1)) { |
|
1294 if (bs == bs1) |
|
1295 term_printf(" %s", bdrv_get_device_name(bs1)); |
|
1296 } |
|
1297 } |
|
1298 term_printf("\n"); |
|
1299 |
|
1300 nb_sns = bdrv_snapshot_list(bs, &sn_tab); |
|
1301 if (nb_sns < 0) { |
|
1302 term_printf("bdrv_snapshot_list: error %d\n", nb_sns); |
|
1303 return; |
|
1304 } |
|
1305 term_printf("Snapshot list (from %s):\n", bdrv_get_device_name(bs)); |
|
1306 term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); |
|
1307 for(i = 0; i < nb_sns; i++) { |
|
1308 sn = &sn_tab[i]; |
|
1309 term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); |
|
1310 } |
|
1311 qemu_free(sn_tab); |
|
1312 } |