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