1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4""" 5Convert a legacy migration stream to a v2 stream. 6""" 7 8from __future__ import print_function 9 10import sys 11import os, os.path 12import syslog 13import traceback 14 15from struct import calcsize, unpack, pack 16 17from xen.util import open_file_or_fd as open_file_or_fd 18from xen.migration import legacy, public, libxc, libxl, xl 19 20__version__ = 1 21 22fin = None # Input file/fd 23fout = None # Output file/fd 24twidth = 0 # Legacy toolstack bitness (32 or 64) 25pv = None # Boolean (pv or hvm) 26qemu = True # Boolean - process qemu record? 27log_to_syslog = False # Boolean - Log to syslog instead of stdout/err? 28verbose = False # Boolean - Summarise stream contents 29 30def stream_read(_ = None): 31 """Read from the input""" 32 return fin.read(_) 33 34def stream_write(_): 35 """Write to the output""" 36 return fout.write(_) 37 38def info(msg): 39 """Info message, routed to appropriate destination""" 40 if verbose: 41 if log_to_syslog: 42 for line in msg.split("\n"): 43 syslog.syslog(syslog.LOG_INFO, line) 44 else: 45 print(msg) 46 47def err(msg): 48 """Error message, routed to appropriate destination""" 49 if log_to_syslog: 50 for line in msg.split("\n"): 51 syslog.syslog(syslog.LOG_ERR, line) 52 print(msg, file = sys.stderr) 53 54class StreamError(Exception): 55 """Error with the incoming migration stream""" 56 pass 57 58class VM(object): 59 """Container of VM parameters""" 60 61 def __init__(self, fmt): 62 # Common 63 self.p2m_size = 0 64 65 # PV 66 self.max_vcpu_id = 0 67 self.online_vcpu_map = [] 68 self.width = 0 69 self.levels = 0 70 self.basic_len = 0 71 self.extd = False 72 self.xsave_len = 0 73 74 # libxl 75 self.libxl = fmt == "libxl" 76 self.emu_xenstore = b"" # NUL terminated key&val pairs from "toolstack" records 77 78def write_libxc_ihdr(): 79 stream_write(pack(libxc.IHDR_FORMAT, 80 libxc.IHDR_MARKER, # Marker 81 libxc.IHDR_IDENT, # Ident 82 3, # Version 83 libxc.IHDR_OPT_LE, # Options 84 0, 0)) # Reserved 85 86def write_libxc_dhdr(): 87 if pv: 88 dtype = libxc.DHDR_TYPE_x86_pv 89 else: 90 dtype = libxc.DHDR_TYPE_x86_hvm 91 92 stream_write(pack(libxc.DHDR_FORMAT, 93 dtype, # Type 94 12, # Page size 95 0, # Reserved 96 0, # Xen major (converted) 97 __version__)) # Xen minor (converted) 98 99def write_libxl_hdr(): 100 stream_write(pack(libxl.HDR_FORMAT, 101 libxl.HDR_IDENT, # Ident 102 libxl.HDR_VERSION, # Version 2 103 libxl.HDR_OPT_LE | # Options 104 libxl.HDR_OPT_LEGACY # Little Endian and Legacy 105 )) 106 107def write_record(rt, *argl): 108 alldata = b''.join(argl) 109 length = len(alldata) 110 111 record = pack(libxc.RH_FORMAT, rt, length) + alldata 112 plen = (8 - (length & 7)) & 7 113 record += b'\x00' * plen 114 115 stream_write(record) 116 117def write_libxc_pv_info(vm): 118 write_record(libxc.REC_TYPE_x86_pv_info, 119 pack(libxc.X86_PV_INFO_FORMAT, 120 vm.width, vm.levels, 0, 0)) 121 122def write_libxc_pv_p2m_frames(vm, pfns): 123 write_record(libxc.REC_TYPE_x86_pv_p2m_frames, 124 pack(libxc.X86_PV_P2M_FRAMES_FORMAT, 125 0, vm.p2m_size - 1), 126 pack("Q" * len(pfns), *pfns)) 127 128def write_libxc_pv_vcpu_basic(vcpu_id, data): 129 write_record(libxc.REC_TYPE_x86_pv_vcpu_basic, 130 pack(libxc.X86_PV_VCPU_HDR_FORMAT, vcpu_id, 0), data) 131 132def write_libxc_pv_vcpu_extd(vcpu_id, data): 133 write_record(libxc.REC_TYPE_x86_pv_vcpu_extended, 134 pack(libxc.X86_PV_VCPU_HDR_FORMAT, vcpu_id, 0), data) 135 136def write_libxc_pv_vcpu_xsave(vcpu_id, data): 137 write_record(libxc.REC_TYPE_x86_pv_vcpu_xsave, 138 pack(libxc.X86_PV_VCPU_HDR_FORMAT, vcpu_id, 0), data) 139 140def write_page_data(pfns, pages): 141 if fout is None: # Save copying 1M buffers around for no reason 142 return 143 144 new_pfns = [(((x & 0xf0000000) << 32) | (x & 0x0fffffff)) for x in pfns] 145 146 # Optimise the needless buffer copying in write_record() 147 stream_write(pack(libxc.RH_FORMAT, 148 libxc.REC_TYPE_page_data, 149 8 + (len(new_pfns) * 8) + len(pages))) 150 stream_write(pack(libxc.PAGE_DATA_FORMAT, len(new_pfns), 0)) 151 stream_write(pack("Q" * len(new_pfns), *new_pfns)) 152 stream_write(pages) 153 154def write_libxc_tsc_info(mode, khz, nsec, incarn): 155 write_record(libxc.REC_TYPE_tsc_info, 156 pack(libxc.X86_TSC_INFO_FORMAT, 157 mode, khz, nsec, incarn, 0)) 158 159def write_libxc_hvm_params(params): 160 if pv: 161 raise StreamError("HVM-only param in PV stream") 162 elif len(params) % 2: 163 raise RuntimeError("Expected even length list of hvm parameters") 164 165 write_record(libxc.REC_TYPE_hvm_params, 166 pack(libxc.HVM_PARAMS_FORMAT, len(params) / 2, 0), 167 pack("Q" * len(params), *params)) 168 169def write_libxc_static_data_end(): 170 write_record(libxc.REC_TYPE_static_data_end) 171 172def write_libxl_end(): 173 write_record(libxl.REC_TYPE_end) 174 175def write_libxl_libxc_context(): 176 write_record(libxl.REC_TYPE_libxc_context) 177 178def write_libxl_emulator_xenstore_data(data): 179 write_record(libxl.REC_TYPE_emulator_xenstore_data, 180 pack(libxl.EMULATOR_HEADER_FORMAT, 181 libxl.EMULATOR_ID_unknown, 0) + data) 182 183def write_libxl_emulator_context(blob): 184 write_record(libxl.REC_TYPE_emulator_context, 185 pack(libxl.EMULATOR_HEADER_FORMAT, 186 libxl.EMULATOR_ID_unknown, 0) + blob) 187 188def rdexact(nr_bytes): 189 """Read exactly nr_bytes from fin""" 190 _ = stream_read(nr_bytes) 191 if len(_) != nr_bytes: 192 raise IOError("Stream truncated") 193 return _ 194 195def unpack_exact(fmt): 196 """Unpack a format from fin""" 197 sz = calcsize(fmt) 198 return unpack(fmt, rdexact(sz)) 199 200def unpack_ulongs(nr_ulongs): 201 if twidth == 32: 202 return unpack_exact("I" * nr_ulongs) 203 else: 204 return unpack_exact("Q" * nr_ulongs) 205 206def read_pv_extended_info(vm): 207 208 marker, = unpack_ulongs(1) 209 210 if twidth == 32: 211 expected = 0xffffffff 212 else: 213 expected = 0xffffffffffffffff 214 215 if marker != expected: 216 raise StreamError("Unexpected extended info marker 0x%x" % (marker, )) 217 218 total_length, = unpack_exact("I") 219 so_far = 0 220 221 info("Extended Info: length 0x%x" % (total_length, )) 222 223 while so_far < total_length: 224 225 blkid, datasz = unpack_exact("=4sI") 226 so_far += 8 227 228 info(" Record type: %s, size 0x%x" % (blkid, datasz)) 229 230 data = rdexact(datasz) 231 so_far += datasz 232 233 # Eww, but this is how it is done :( 234 if blkid == b"vcpu": 235 236 vm.basic_len = datasz 237 238 if datasz == 0x1430: 239 vm.width = 8 240 vm.levels = 4 241 info(" 64bit domain, 4 levels") 242 elif datasz == 0xaf0: 243 vm.width = 4 244 vm.levels = 3 245 info(" 32bit domain, 3 levels") 246 else: 247 raise StreamError("Unable to determine guest width/level") 248 249 write_libxc_pv_info(vm) 250 251 elif blkid == b"extv": 252 vm.extd = True 253 254 elif blkid == b"xcnt": 255 vm.xsave_len, = unpack("I", data[:4]) 256 info("xcnt sz 0x%x" % (vm.xsave_len, )) 257 258 else: 259 raise StreamError("Unrecognised extended block") 260 261 262 if so_far != total_length: 263 raise StreamError("Overshot Extended Info size by %d bytes" % 264 (so_far - total_length, )) 265 266def read_pv_p2m_frames(vm): 267 fpp = 4096 / vm.width 268 p2m_frame_len = (vm.p2m_size - 1) / fpp + 1 269 270 info("P2M frames: fpp %d, p2m_frame_len %d" % (fpp, p2m_frame_len)) 271 write_libxc_pv_p2m_frames(vm, unpack_ulongs(p2m_frame_len)) 272 273def read_pv_tail(vm): 274 275 nr_unmapped_pfns, = unpack_exact("I") 276 277 if nr_unmapped_pfns != 0: 278 # "Unmapped" pfns are bogus 279 _ = unpack_ulongs(nr_unmapped_pfns) 280 info("discarding %d bogus 'unmapped pfns'" % (nr_unmapped_pfns, )) 281 282 for vcpu_id in vm.online_vcpu_map: 283 284 basic = rdexact(vm.basic_len) 285 info("Got VCPU basic (size 0x%x)" % (vm.basic_len, )) 286 write_libxc_pv_vcpu_basic(vcpu_id, basic) 287 288 if vm.extd: 289 extd = rdexact(128) 290 info("Got VCPU extd (size 0x%x)" % (128, )) 291 write_libxc_pv_vcpu_extd(vcpu_id, extd) 292 293 if vm.xsave_len: 294 mask, size = unpack_exact("QQ") 295 assert vm.xsave_len - 16 == size 296 297 xsave = rdexact(size) 298 info("Got VCPU xsave (mask 0x%x, size 0x%x)" % (mask, size)) 299 write_libxc_pv_vcpu_xsave(vcpu_id, xsave) 300 301 shinfo = rdexact(4096) 302 info("Got shinfo") 303 304 write_record(libxc.REC_TYPE_shared_info, shinfo) 305 write_record(libxc.REC_TYPE_end) 306 307 308def read_libxl_toolstack(vm, data): 309 310 if len(data) < 8: 311 raise StreamError("Overly short libxl toolstack data") 312 313 ver, count = unpack("=II", data[:8]) 314 data = data[8:] 315 316 if ver != 1: 317 raise StreamError("Cannot decode libxl toolstack version %u" % (ver, )) 318 info(" Version %u, count %u" % (ver, count)) 319 320 for x in range(count): 321 322 if len(data) < 28: 323 raise StreamError("Remaining data too short for physmap header") 324 325 phys, start, size, namelen = unpack("=QQQI", data[:28]) 326 data = data[28:] 327 328 if namelen == 0: 329 raise StreamError("No physmap info name") 330 331 # 64bit leaked 4 bytes of padding onto the end of name 332 if twidth == 64: 333 namelen += 4 334 335 if len(data) < namelen: 336 raise StreamError("Remaining data too short for physmap name") 337 338 name = data[:namelen] 339 data = data[namelen:] 340 341 # Strip padding off the end of name 342 if twidth == 64: 343 name = name[:-4] 344 345 if name[-1] != b'\x00': 346 raise StreamError("physmap name not NUL terminated") 347 348 root = b"physmap/%x" % (phys, ) 349 kv = [root + b"/start_addr", b"%x" % (start, ), 350 root + b"/size", b"%x" % (size, ), 351 root + b"/name", name[:-1]] 352 353 for key, val in zip(kv[0::2], kv[1::2]): 354 info(" '%s' = '%s'" % (key.decode(), val.decode())) 355 356 vm.emu_xenstore += b'\x00'.join(kv) + b'\x00' 357 358 359def read_chunks(vm): 360 361 hvm_params = [] 362 363 while True: 364 365 marker, = unpack_exact("=i") 366 if marker <= 0: 367 info("Chunk: %d - %s" % 368 (marker, legacy.chunk_type_to_str.get(marker, "unknown"))) 369 370 if marker == legacy.CHUNK_end: 371 info(" End") 372 373 if hvm_params: 374 write_libxc_hvm_params(hvm_params) 375 376 return 377 378 elif marker > 0: 379 380 if marker > legacy.MAX_BATCH: 381 raise StreamError("Page batch (%d) exceeded MAX_BATCH (%d)" % 382 (marker, legacy.MAX_BATCH)) 383 pfns = unpack_ulongs(marker) 384 385 # xc_domain_save() leaves many XEN_DOMCTL_PFINFO_XTAB records for 386 # sequences of pfns it cant map. Drop these. 387 pfns = [ x for x in pfns if x != 0xf0000000 ] 388 389 if len(set(pfns)) != len(pfns): 390 raise StreamError("Duplicate pfns in batch") 391 392 nr_pages = len([x for x in pfns if (x & 0xf0000000) < 0xd0000000]) 393 pages = rdexact(nr_pages * 4096) 394 395 write_page_data(pfns, pages) 396 397 elif marker == legacy.CHUNK_enable_verify_mode: 398 info("This is a debug stream") 399 400 elif marker == legacy.CHUNK_vcpu_info: 401 max_id, = unpack_exact("i") 402 403 if max_id > legacy.MAX_VCPU_ID: 404 raise StreamError("Vcpu max_id out of range: %d > %d" % 405 (max_id, legacy.MAX_VCPU_ID)) 406 407 vm.max_vcpu_id = max_id 408 bitmap = unpack_exact("Q" * ((max_id/64) + 1)) 409 410 for idx, word in enumerate(bitmap): 411 bit_idx = 0 412 413 while word > 0: 414 if word & 1: 415 vm.online_vcpu_map.append((idx * 64) + bit_idx) 416 417 bit_idx += 1 418 word >>= 1 419 420 info(" Vcpu info: max_id %d, online map %s" % 421 (vm.max_vcpu_id, vm.online_vcpu_map)) 422 423 elif marker == legacy.CHUNK_hvm_ident_pt: 424 _, ident_pt = unpack_exact("=IQ") 425 info(" EPT Identity Pagetable: 0x%x" % (ident_pt, )) 426 hvm_params.extend([public.HVM_PARAM_IDENT_PT, ident_pt]) 427 428 elif marker == legacy.CHUNK_hvm_vm86_tss: 429 _, vm86_tss = unpack_exact("=IQ") 430 info(" VM86 TSS: 0x%x" % (vm86_tss, )) 431 hvm_params.extend([public.HVM_PARAM_VM86_TSS, vm86_tss]) 432 433 elif marker == legacy.CHUNK_tmem: 434 raise RuntimeError("todo") 435 436 elif marker == legacy.CHUNK_tmem_extra: 437 raise RuntimeError("todo") 438 439 elif marker == legacy.CHUNK_tsc_info: 440 mode, nsec, khz, incarn = unpack_exact("=IQII") 441 info(" X86_TSC_INFO: mode %s, %d ns, %d khz, %d incarn" 442 % (mode, nsec, khz, incarn)) 443 write_libxc_tsc_info(mode, khz, nsec, incarn) 444 445 elif marker == legacy.CHUNK_hvm_console_pfn: 446 _, console_pfn = unpack_exact("=IQ") 447 info(" Console pfn: 0x%x" % (console_pfn, )) 448 hvm_params.extend([public.HVM_PARAM_CONSOLE_PFN, console_pfn]) 449 450 elif marker == legacy.CHUNK_last_checkpoint: 451 info(" Last Checkpoint") 452 # Nothing to do 453 454 elif marker == legacy.CHUNK_hvm_acpi_ioports_location: 455 _, loc = unpack_exact("=IQ") 456 info(" ACPI ioport location: 0x%x" % (loc, )) 457 hvm_params.extend([public.HVM_PARAM_ACPI_IOPORTS_LOCATION, loc]) 458 459 elif marker == legacy.CHUNK_hvm_viridian: 460 _, loc = unpack_exact("=IQ") 461 info(" Viridian location: 0x%x" % (loc, )) 462 hvm_params.extend([public.HVM_PARAM_VIRIDIAN, loc]) 463 464 elif marker == legacy.CHUNK_compressed_data: 465 sz, = unpack_exact("I") 466 data = rdexact(sz) 467 info(" Compressed Data: sz 0x%x" % (sz, )) 468 raise RuntimeError("todo") 469 470 elif marker == legacy.CHUNK_enable_compression: 471 raise RuntimeError("todo") 472 473 elif marker == legacy.CHUNK_hvm_generation_id_addr: 474 _, genid_loc = unpack_exact("=IQ") 475 info(" Generation ID Address: 0x%x" % (genid_loc, )) 476 hvm_params.extend( 477 [public.HVM_PARAM_VM_GENERATION_ID_ADDR, genid_loc]) 478 479 elif marker == legacy.CHUNK_hvm_paging_ring_pfn: 480 _, pfn = unpack_exact("=IQ") 481 info(" Paging ring pfn: 0x%x" % (pfn, )) 482 hvm_params.extend([public.HVM_PARAM_PAGING_RING_PFN, pfn]) 483 484 elif marker == legacy.CHUNK_hvm_monitor_ring_pfn: 485 _, pfn = unpack_exact("=IQ") 486 info(" Monitor ring pfn: 0x%x" % (pfn, )) 487 hvm_params.extend([public.HVM_PARAM_MONITOR_RING_PFN, pfn]) 488 489 elif marker == legacy.CHUNK_hvm_sharing_ring_pfn: 490 _, pfn = unpack_exact("=IQ") 491 info(" Sharing ring pfn: 0x%x" % (pfn, )) 492 hvm_params.extend([public.HVM_PARAM_SHARING_RING_PFN, pfn]) 493 494 elif marker == legacy.CHUNK_toolstack: 495 sz, = unpack_exact("I") 496 497 if sz: 498 data = rdexact(sz) 499 info(" Toolstack Data: sz 0x%x" % (sz, )) 500 501 if vm.libxl: 502 read_libxl_toolstack(vm, data) 503 else: 504 info(" Discarding") 505 506 elif marker == legacy.CHUNK_hvm_ioreq_server_pfn: 507 _, pfn = unpack_exact("=IQ") 508 info(" IOREQ server pfn: 0x%x" % (pfn, )) 509 hvm_params.extend([public.HVM_PARAM_IOREQ_SERVER_PFN, pfn]) 510 511 elif marker == legacy.CHUNK_hvm_nr_ioreq_server_pages: 512 _, nr_pages = unpack_exact("=IQ") 513 info(" IOREQ server pages: %d" % (nr_pages, )) 514 hvm_params.extend( 515 [public.HVM_PARAM_NR_IOREQ_SERVER_PAGES, nr_pages]) 516 517 else: 518 raise StreamError("Unrecognised chunk %d" % (marker, )) 519 520def read_hvm_tail(vm): 521 522 io, bufio, store = unpack_exact("QQQ") 523 info("Magic pfns: 0x%x 0x%x 0x%x" % (io, bufio, store)) 524 write_libxc_hvm_params([public.HVM_PARAM_IOREQ_PFN, io, 525 public.HVM_PARAM_BUFIOREQ_PFN, bufio, 526 public.HVM_PARAM_STORE_PFN, store]) 527 528 blobsz, = unpack_exact("I") 529 info("Got HVM Context (0x%x bytes)" % (blobsz, )) 530 blob = rdexact(blobsz) 531 532 write_record(libxc.REC_TYPE_hvm_context, blob) 533 write_record(libxc.REC_TYPE_end) 534 535 536 537def read_qemu(vm): 538 539 rawsig = rdexact(21) 540 sig, = unpack("21s", rawsig) 541 info("Qemu signature: %s" % (sig, )) 542 543 if sig == b"DeviceModelRecord0002": 544 rawsz = rdexact(4) 545 sz, = unpack("I", rawsz) 546 qdata = rdexact(sz) 547 548 if vm.libxl: 549 write_libxl_emulator_context(qdata) 550 else: 551 stream_write(rawsig) 552 stream_write(rawsz) 553 stream_write(qdata) 554 555 else: 556 raise RuntimeError("Unrecognised Qemu sig '%s'" % (sig, )) 557 558 559def skip_xl_header(fmt): 560 """Skip over an xl header in the stream""" 561 562 hdr = rdexact(len(xl.MAGIC)) 563 if hdr != xl.MAGIC: 564 raise StreamError("No xl header") 565 566 byteorder, mflags, oflags, optlen = unpack_exact(xl.HEADER_FORMAT) 567 568 if fmt == "libxl": 569 mflags |= xl.MANDATORY_FLAG_STREAMV2 570 571 opts = pack(xl.HEADER_FORMAT, byteorder, mflags, oflags, optlen) 572 573 optdata = rdexact(optlen) 574 575 info("Processed xl header") 576 577 stream_write(hdr) 578 stream_write(opts) 579 stream_write(optdata) 580 581def read_legacy_stream(vm): 582 583 try: 584 vm.p2m_size, = unpack_ulongs(1) 585 info("P2M Size: 0x%x" % (vm.p2m_size, )) 586 587 if vm.libxl: 588 write_libxl_hdr() 589 write_libxl_libxc_context() 590 591 write_libxc_ihdr() 592 write_libxc_dhdr() 593 594 if pv: 595 read_pv_extended_info(vm) 596 597 write_libxc_static_data_end() 598 599 if pv: 600 read_pv_p2m_frames(vm) 601 602 read_chunks(vm) 603 604 if pv: 605 read_pv_tail(vm) 606 else: 607 read_hvm_tail(vm) 608 609 if vm.libxl and len(vm.emu_xenstore): 610 write_libxl_emulator_xenstore_data(vm.emu_xenstore) 611 612 if not pv and (vm.libxl or qemu): 613 read_qemu(vm) 614 615 if vm.libxl: 616 write_libxl_end() 617 618 except (IOError, StreamError): 619 err("Stream Error:") 620 err(traceback.format_exc()) 621 return 1 622 623 except RuntimeError: 624 err("Script Error:") 625 err(traceback.format_exc()) 626 err("Please fix me") 627 return 2 628 return 0 629 630 631def main(): 632 from optparse import OptionParser 633 global fin, fout, twidth, pv, qemu, verbose 634 635 # Change stdout to be line-buffered. 636 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) 637 638 parser = OptionParser(version = __version__, 639 usage = ("%prog [options] -i INPUT -o OUTPUT" 640 " -w WIDTH -g GUEST"), 641 description = 642 "Convert a legacy stream to a v2 (or later) stream") 643 644 # Required options 645 parser.add_option("-i", "--in", dest = "fin", metavar = "<FD or FILE>", 646 help = "Legacy input to convert") 647 parser.add_option("-o", "--out", dest = "fout", metavar = "<FD or FILE>", 648 help = "v2 (or later) format output") 649 parser.add_option("-w", "--width", dest = "twidth", 650 metavar = "<32/64>", choices = ["32", "64"], 651 help = "Legacy toolstack bitness") 652 parser.add_option("-g", "--guest-type", dest = "gtype", 653 metavar = "<pv/hvm>", choices = ["pv", "hvm"], 654 help = "Type of guest in stream") 655 656 # Optional options 657 parser.add_option("-f", "--format", dest = "format", 658 metavar = "<libxc|libxl>", default = "libxc", 659 choices = ["libxc", "libxl"], 660 help = "Desired format of the outgoing stream " \ 661 "(defaults to libxc)") 662 parser.add_option("-v", "--verbose", action = "store_true", default = False, 663 help = "Summarise stream contents") 664 parser.add_option("-x", "--xl", action = "store_true", default = False, 665 help = ("Is an `xl` header present in the stream?" 666 " (default no)")) 667 parser.add_option("--skip-qemu", action = "store_true", default = False, 668 help = ("Skip processing of the qemu tail?" 669 " (default no)")) 670 parser.add_option("--syslog", action = "store_true", default = False, 671 help = "Log to syslog instead of stdout") 672 673 opts, _ = parser.parse_args() 674 675 if (opts.fin is None or opts.fout is None or 676 opts.twidth is None or opts.gtype is None): 677 678 parser.print_help(sys.stderr) 679 raise SystemExit(1) 680 681 if opts.syslog: 682 global log_to_syslog 683 684 syslog.openlog("convert-legacy-stream", syslog.LOG_PID) 685 log_to_syslog = True 686 687 fin = open_file_or_fd(opts.fin, "rb") 688 fout = open_file_or_fd(opts.fout, "wb") 689 twidth = int(opts.twidth) 690 pv = opts.gtype == "pv" 691 verbose = opts.verbose 692 if opts.skip_qemu: 693 qemu = False 694 695 if opts.xl: 696 skip_xl_header(opts.format) 697 698 rc = read_legacy_stream(VM(opts.format)) 699 fout.close() 700 701 return rc 702 703if __name__ == "__main__": 704 try: 705 sys.exit(main()) 706 except SystemExit as e: 707 sys.exit(e.code) 708 except KeyboardInterrupt: 709 sys.exit(1) 710