summaryrefslogtreecommitdiff
path: root/copy_qxl_to_image
blob: 89b651a95bbd18432607d9ddba1c273889cb72af (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/usr/bin/python

import argparse
import sys
import os
import subprocess
import zipfile
import guestfs
import tempfile
import tarfile

parser = argparse.ArgumentParser()
parser.add_argument('--image', dest='image', default=None)
parser.add_argument('--root', dest='root', default=None)
parser.add_argument('--partition', dest='partition', default=None)
parser.add_argument('--system32', dest='system32', choices=['Windows/System32', 'WINDOWS/system32'])
parser.add_argument('--erase_old_driver_files', dest='erase', default=False, action='store_true')
parser.add_argument('--allow-old', action='store_true', default=False)
parser.add_argument('--brew')
parser.add_argument('--arch', choices=['x86', 'x64'])
parser.add_argument('--winver', choices=['w7', 'xp'])
args, rest = parser.parse_known_args(sys.argv[1:])

if os.system('which guestfish'):
    print "missing guestfish - yum install guestfish (or something)"
    sys.exit(1)

def no_flags_usage(rest):
    if len(rest) != 4:
        parser.print_usage()
        sys.exit(1)

# do this via argparse??
if not args.image:
    no_flags_usage(rest)
    args.image = rest[0]
if not args.root and not args.brew:
    no_flags_usage(rest)
    args.root = rest[1]
if not args.partition and not args.winver:
    no_flags_usage(rest)
    args.partition = rest[2]
if not args.system32:
    args.system32 = 'WINDOWS/system32' if (len(rest) != 4) else rest[3]
if args.winver:
    # override system32 logic before
    args.system32 = 'Windows/System32' if args.winver == 'w7' else 'WINDOWS/system32'
    if not args.partition:
        args.partition = 2 if args.winver == 'w7' else 1

HOME = os.environ['HOME']
BREW_CACHE='%s/.brew_qxl_win_cache' % HOME

class chdir:
    """ usage:
    with chdir('/tmp'):
        print os.getcwd() # prints '/tmp'
    """
    def __init__(self, d):
        self.d = d
        if not os.path.exists(d):
            raise Exception('chdir: no directory %r' % d)

    def __enter__(self, *args, **kw):
        self.org = os.getcwd()
        os.chdir(self.d)

    def __exit__(self, *args, **kw):
        os.chdir(self.org)

# curl invocation notes:
#  -R/--remote-time
#      When used, this will make libcurl attempt to figure out the timestamp of the remote file, and if that is available make the local file get that same timestamp.

def curl(url):
    print "downloading %s" % url
    os.system('curl -R -O %s' % url)

def fill_brew_driver_cache(ver, winver, arch, debug=False):
    # instead of using the brew utility. can't grok download-latest
    assert(winver in ['w7', 'xp'])
    assert((winver == 'xp' and arch == 'x86') or arch in ['x86', 'x64'])
    driver_path = os.path.join(BREW_CACHE, os.path.join(*ver.split('-')))
    if not os.path.exists(driver_path):
        os.makedirs(driver_path)
    print "ver = %r" % ver
    base_url = 'http://download.devel.redhat.com/brewroot/packages/qxl-win/%s/%s/win/' % tuple(ver.split('-'))
    saved_wd = os.getcwd()
    with chdir(driver_path):
        for mid in ['w7_x86', 'w7_x64', 'xp_x86']:
            for post in ['', '_debug']:
                filename = 'qxl_%s%s.zip' % (mid, post)
                target = os.path.join(driver_path, filename)
                if not os.path.exists(target):
                    curl(base_url + filename)
                if not os.path.exists(target):
                    print "curl failed?"
                    sys.exit(1)
    return driver_path + '/qxl_%s_%s%s.zip' % (winver, arch, '' if not debug else '_debug')

def install_brew_driver(args):
    """
    get a driver from brew and install in an image
    args.brew - version (0.1-8)
    args.winver - windows version. TODO - extract from image.
    args.arch - architecture. TODO - extract from image

    Unpacks the driver files under the brew cache directory.
    TODO - also grab the pdbs?
    """
    if not args.winver or not args.arch:
        print "missing --arch and --winver"
        sys.exit(1)
    winver = args.winver
    arch = args.arch
    zip_filename = fill_brew_driver_cache(ver=args.brew, winver=winver, arch=arch)
    zip_dirname = os.path.dirname(zip_filename)
    args.root = os.path.join(zip_dirname, args.winver, args.arch)
    if not os.path.exists(args.root):
        print "unpacking %s" % zip_filename
        with chdir(zip_dirname):
            zipfile.ZipFile(zip_filename).extractall()
    install_driver(args)

def install_driver(args):
    base=os.path.dirname(args.image)
    qxldd_path = os.path.realpath(os.path.join(args.root, 'qxldd.dll'))
    if not os.path.exists(qxldd_path):
        print "missing qxldd.dll"
        sys.exit(1)
    qxlsys_path = os.path.realpath(os.path.join(args.root, 'qxl.sys'))
    if not os.path.exists(qxlsys_path):
        print "missing qxl.sys"
        sys.exit(1)
    if not os.path.exists(args.image):
        print "provided image does not exist"
        sys.exit(1)
    image_user = os.popen("lsof -t %s" % args.image).read().strip()
    if len(image_user) != 0:
        print "image is being used by %s" % image_user
        print "will not update used image"
        sys.exit(1)
    not_too_old(qxldd_path, not args.allow_old)
    not_too_old(qxlsys_path, not args.allow_old)

    print "copying qxl driver %s to %s" % (args.root, args.image)
    g = guestfs.GuestFS()
    g.set_trace(1)
    g.add_drive(args.image)
    g.launch()
    g.mount("/dev/vda%s" % args.partition, "/")
    for local_fname, target_fname in [
        (qxldd_path, '/' + args.system32 + '/qxldd.dll'),
        (qxlsys_path, '/' + args.system32 + '/qxlsys.dll')]:
        g.upload(local_fname, target_fname)
        g.checksum('md5', target_fname)
    temp_dir = tempfile.mkdtemp()
    tar_name = os.path.join(temp_dir, 'qxl.tar')
    with chdir(args.root):
        tar = tarfile.open(tar_name, 'w')
        tar.add('.')
        tar.close()
    if not g.exists('/qxl'):
        g.mkdir('/qxl')
    g.tar_in(tar_name, '/qxl/')
    os.system("md5sum %s" % qxldd_path)
    os.system("md5sum %s" % qxlsys_path)

def not_too_old(p, do_exit):
    if os.system("peversion %s" % p):
        print "%s is too old" % p
        if do_exit:
            sys.exit(1)

def guestfish(commands):
    gf = subprocess.Popen('guestfish', stdin=subprocess.PIPE)
    gf.stdin.write(commands)
    gf.stdin.close()
    return gf.wait()

if __name__ == '__main__':
    # get driver from brew
    if args.brew:
        args.allow_old = True
        install_brew_driver(args)
    else:
        install_driver(args)