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