1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4""" Verify a v2 format migration stream """
5
6from __future__ import print_function
7
8import sys
9import struct
10import os, os.path
11import syslog
12import traceback
13
14from xen.util import open_file_or_fd as open_file_or_fd
15from xen.migration.verify import StreamError, RecordError
16from xen.migration.libxc import VerifyLibxc
17from xen.migration.libxl import VerifyLibxl
18
19fin = None             # Input file/fd
20log_to_syslog = False  # Boolean - Log to syslog instead of stdout/err?
21verbose = False        # Boolean - Summarise stream contents
22quiet = False          # Boolean - Suppress error printing
23
24def info(msg):
25    """Info message, routed to appropriate destination"""
26    if not quiet and verbose:
27        if log_to_syslog:
28            for line in msg.split("\n"):
29                syslog.syslog(syslog.LOG_INFO, line)
30        else:
31            print(msg)
32
33def err(msg):
34    """Error message, routed to appropriate destination"""
35    if not quiet:
36        if log_to_syslog:
37            for line in msg.split("\n"):
38                syslog.syslog(syslog.LOG_ERR, line)
39        print(msg, file = sys.stderr)
40
41def stream_read(_ = None):
42    """Read from input"""
43    return fin.read(_)
44
45def rdexact(nr_bytes):
46    """Read exactly nr_bytes from fin"""
47    _ = stream_read(nr_bytes)
48    if len(_) != nr_bytes:
49        raise IOError("Stream truncated")
50    return _
51
52def unpack_exact(fmt):
53    """Unpack a format from fin"""
54    sz = struct.calcsize(fmt)
55    return struct.unpack(fmt, rdexact(sz))
56
57
58def skip_xl_header():
59    """Skip over an xl header in the stream"""
60
61    hdr = rdexact(32)
62    if hdr != b"Xen saved domain, xl format\n \0 \r":
63        raise StreamError("No xl header")
64
65    _, mflags, _, optlen = unpack_exact("=IIII")
66    _ = rdexact(optlen)
67
68    info("Processed xl header")
69
70    if mflags & 2: # XL_MANDATORY_FLAG_STREAMv2
71        return "libxl"
72    else:
73        return "libxc"
74
75def read_stream(fmt):
76    """ Read an entire stream """
77
78    try:
79        if fmt == "xl":
80            fmt = skip_xl_header()
81
82        if fmt == "libxc":
83            VerifyLibxc(info, stream_read).verify()
84        else:
85            VerifyLibxl(info, stream_read).verify()
86
87    except (IOError, StreamError, RecordError):
88        err("Stream Error:")
89        err(traceback.format_exc())
90        return 1
91
92    except Exception:
93        err("Script Error:")
94        err(traceback.format_exc())
95        err("Please fix me")
96        return 2
97
98    return 0
99
100
101def main():
102    """ main """
103    from optparse import OptionParser
104    global fin, quiet, verbose
105
106    # Change stdout to be line-buffered.
107    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
108
109    parser = OptionParser(usage = "%prog [options]",
110                          description =
111                          "Verify a stream according to the v2 (or later) spec")
112
113    # Optional options
114    parser.add_option("-i", "--in", dest = "fin", metavar = "<FD or FILE>",
115                      default = "0",
116                      help = "Stream to verify (defaults to stdin)")
117    parser.add_option("-v", "--verbose", action = "store_true", default = False,
118                      help = "Summarise stream contents")
119    parser.add_option("-q", "--quiet", action = "store_true", default = False,
120                      help = "Suppress all logging/errors")
121    parser.add_option("-f", "--format", dest = "format",
122                      metavar = "<libxc|libxl|xl>", default = "libxc",
123                      choices = ["libxc", "libxl", "xl"],
124                      help = "Format of the incoming stream (defaults to libxc)")
125    parser.add_option("--syslog", action = "store_true", default = False,
126                      help = "Log to syslog instead of stdout")
127
128    opts, _ = parser.parse_args()
129
130    if opts.syslog:
131        global log_to_syslog
132
133        syslog.openlog("verify-stream-v2", syslog.LOG_PID)
134        log_to_syslog = True
135
136    verbose = opts.verbose
137    quiet = opts.quiet
138    fin = open_file_or_fd(opts.fin, "rb", 0)
139
140    return read_stream(opts.format)
141
142if __name__ == "__main__":
143    try:
144        sys.exit(main())
145    except SystemExit as e:
146        sys.exit(e.code)
147    except KeyboardInterrupt:
148        sys.exit(2)
149