aboutsummaryrefslogtreecommitdiffstats
path: root/tools/python/xen/xend/XendCheckpoint.py
blob: 654fb022c59d10a4a20d5e7608d0d2fa6cd32eed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>

# This file is subject to the terms and conditions of the GNU General
# Public License.  See the file "COPYING" in the main directory of
# this archive for more details.

import errno
import os
import select
import sxp
from string import join
from struct import pack, unpack, calcsize
from xen.util.xpopen import xPopen3
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()

from XendError import XendError
from XendLogging import log

SIGNATURE = "LinuxGuestRecord"
PAGE_SIZE = 4096
PATH_XC_SAVE = "/usr/libexec/xen/xc_save"
PATH_XC_RESTORE = "/usr/libexec/xen/xc_restore"

sizeof_int = calcsize("i")
sizeof_unsigned_long = calcsize("L")

def write_exact(fd, buf, errmsg):
    if os.write(fd, buf) != len(buf):
        raise XendError(errmsg)

def read_exact(fd, size, errmsg):
    buf = os.read(fd, size)
    if len(buf) != size:
        raise XendError(errmsg)
    return buf

def save(xd, fd, dominfo):
    write_exact(fd, SIGNATURE, "could not write guest state file: signature")

    config = sxp.to_string(dominfo.sxpr())
    write_exact(fd, pack("!i", len(config)),
                "could not write guest state file: config len")
    write_exact(fd, config, "could not write guest state file: config")

    cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
           str(dominfo.id)]
    log.info("[xc_save] " + join(cmd))
    child = xPopen3(cmd, True, -1, [fd, xc.handle()])
    
    lasterr = ""
    p = select.poll()
    p.register(child.fromchild.fileno())
    p.register(child.childerr.fileno())
    while True:
        r = p.poll()
        for (fd, event) in r:
            if not event & select.POLLIN:
                continue
            if fd == child.childerr.fileno():
                l = child.childerr.readline()
                log.error(l.rstrip())
                lasterr = l.rstrip()
            if fd == child.fromchild.fileno():
                l = child.fromchild.readline()
                if l.rstrip() == "suspend":
                    log.info("suspending %d" % dominfo.id)
                    xd.domain_shutdown(dominfo.id, reason='suspend')
                    dominfo.state_wait("suspended")
                    log.info("suspend %d done" % dominfo.id)
                    child.tochild.write("done\n")
                    child.tochild.flush()
        if filter(lambda (fd, event): event & select.POLLHUP, r):
            break

    if child.wait() >> 8 == 127:
        lasterr = "popen %s failed" % PATH_XC_SAVE
    if child.wait() != 0:
        raise XendError("xc_save failed: %s" % lasterr)

    xd.domain_destroy(dominfo.id)
    return None

def restore(xd, fd):
    signature = read_exact(fd, len(SIGNATURE),
        "not a valid guest state file: signature read")
    if signature != SIGNATURE:
        raise XendError("not a valid guest state file: found '%s'" %
                        signature)

    l = read_exact(fd, sizeof_int,
                   "not a valid guest state file: config size read")
    vmconfig_size = unpack("!i", l)[0]
    vmconfig_buf = read_exact(fd, vmconfig_size,
        "not a valid guest state file: config read")

    p = sxp.Parser()
    p.input(vmconfig_buf)
    if not p.ready:
        raise XendError("not a valid guest state file: config parse")

    vmconfig = p.get_val()
    dominfo = xd.domain_configure(vmconfig)

    l = read_exact(fd, sizeof_unsigned_long,
                   "not a valid guest state file: pfn count read")
    nr_pfns = unpack("=L", l)[0]   # XXX endianess
    if nr_pfns > 1024*1024:     # XXX
        raise XendError(
            "not a valid guest state file: pfn count out of range")

    cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd),
           str(dominfo.id), str(nr_pfns)]
    log.info("[xc_restore] " + join(cmd))
    child = xPopen3(cmd, True, -1, [fd, xc.handle()])
    child.tochild.close()

    lasterr = ""
    p = select.poll()
    p.register(child.fromchild.fileno())
    p.register(child.childerr.fileno())
    while True:
        r = p.poll()
        for (fd, event) in r:
            if not event & select.POLLIN:
                continue
            if fd == child.childerr.fileno():
                l = child.childerr.readline()
                log.error(l.rstrip())
                lasterr = l.rstrip()
            if fd == child.fromchild.fileno():
                l = child.fromchild.readline()
                log.info(l.rstrip())
        if filter(lambda (fd, event): event & select.POLLHUP, r):
            break

    if child.wait() >> 8 == 127:
        lasterr = "popen %s failed" % PATH_XC_RESTORE
    if child.wait() != 0:
        raise XendError("xc_restore failed: %s" % lasterr)

    return dominfo