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