1#
2# ExtLinuxConf.py - Simple syslinux config parsing
3#
4# Copyright 2010 Citrix Systems Ltd.
5#
6# This software may be freely redistributed under the terms of the GNU
7# general public license.
8#
9# You should have received a copy of the GNU General Public License
10# along with this program; If not, see <http://www.gnu.org/licenses/>.
11#
12
13import sys, re, os
14import logging
15import GrubConf
16
17class ExtLinuxImage(object):
18    def __init__(self, lines, path):
19        self.reset(lines, path)
20
21    def __repr__(self):
22        return ("title: %s\n"
23                "  root: %s\n"
24                "  kernel: %s\n"
25                "  args: %s\n"
26                "  initrd: %s\n" %(self.title, self.root, self.kernel,
27                                   self.args, self.initrd))
28    def reset(self, lines, path):
29        self._initrd = self._kernel = self._readonly = None
30        self._args = ""
31        self.title = ""
32        self.lines = []
33        self.path = path
34        self.root = ""
35        map(self.set_from_line, lines)
36
37    def set_from_line(self, line, replace = None):
38        (com, arg) = GrubConf.grub_exact_split(line, 2)
39        com = com.lower()
40
41        # Special handling for mboot.c32
42        if com.lower() == "append" and self.kernel is not None:
43            (_,kernel) = self.kernel
44            if kernel.endswith("mboot.c32"):
45                kernel = None
46                args = None
47                initrd = None
48                modules = arg.split("---")
49
50                if len(modules) == 3: # Assume Xen + Kernel + Initrd
51                    (_,kernel,initrd) = modules
52                elif len(modules) == 2: # Assume Kernel + Initrd
53                    (kernel,initrd) = modules
54
55                if kernel:
56                    setattr(self, "kernel", kernel.strip())
57                if initrd:
58                    setattr(self, "initrd", initrd.strip())
59
60                # Bypass regular self.commands handling
61                com = None
62            elif "initrd=" in arg:
63                # find initrd image in append line
64                args = arg.strip().split(" ")
65                for a in args:
66                    if a.lower().startswith("initrd="):
67                        setattr(self, "initrd", a.replace("initrd=", ""))
68                        arg = arg.replace(a, "")
69
70        if com is not None and self.commands.has_key(com):
71            if self.commands[com] is not None:
72                setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip()))
73            else:
74                logging.info("Ignored image directive %s" %(com,))
75        elif com is not None:
76            logging.warning("Unknown image directive %s" %(com,))
77
78        # now put the line in the list of lines
79        if replace is None:
80            self.lines.append(line)
81        else:
82            self.lines.pop(replace)
83            self.lines.insert(replace, line)
84
85    def set_kernel(self, val):
86        if val.find(" ") == -1:
87            self._kernel = (None,val)
88            self._args = None
89            return
90        (kernel, args) = val.split(None, 1)
91        self._kernel = (None,kernel)
92        self._args = args
93    def get_kernel(self):
94        return self._kernel
95    def set_args(self, val):
96        self._args = val
97    def get_args(self):
98        return self._args
99    kernel = property(get_kernel, set_kernel)
100    args = property(get_args, set_args)
101
102    def set_initrd(self, val):
103        self._initrd = (None,val)
104    def get_initrd(self):
105        return self._initrd
106    initrd = property(get_initrd, set_initrd)
107
108    def set_readonly(self, val):
109        self._readonly = 1
110    def get_readonly(self):
111        return self._readonly
112    readonly = property(get_readonly, set_readonly)
113
114    # set up command handlers
115    commands = {
116        "label": "title",
117        "kernel": "kernel",
118        "append": "args"}
119
120class ExtLinuxConfigFile(object):
121    def __init__(self, fn = None):
122        self.filename = fn
123        self.images = []
124        self.timeout = -1
125        self._default = 0
126
127        if fn is not None:
128            self.parse()
129
130    def new_image(self, title, lines):
131        # ExtLinuxImage constructor doesn't have title but since path
132        # is being used by get_{kernel|initrd} functions we pass
133        # empty string rather than None (see lines above)
134        return ExtLinuxImage(lines, "")
135
136    def parse(self, buf = None):
137        if buf is None:
138            if self.filename is None:
139                raise ValueError, "No config file defined to parse!"
140
141            f = open(self.filename, 'r')
142            lines = f.readlines()
143            f.close()
144        else:
145            lines = buf.split("\n")
146
147        path = os.path.dirname(self.filename)
148        img = []
149        for l in lines:
150            l = l.strip()
151            # skip blank lines
152            if len(l) == 0:
153                continue
154            # skip comments
155            if l.startswith('#'):
156                continue
157            # new image
158            if l.lower().startswith("label"):
159                if len(img) > 0:
160                    self.add_image(ExtLinuxImage(img, path))
161                img = [l]
162                continue
163
164            if len(img) > 0:
165                img.append(l)
166                continue
167
168            (com, arg) = GrubConf.grub_exact_split(l, 2)
169            com = com.lower()
170            if self.commands.has_key(com):
171                if self.commands[com] is not None:
172                    setattr(self, self.commands[com], arg.strip())
173                else:
174                    logging.info("Ignored directive %s" %(com,))
175            else:
176                logging.warning("Unknown directive %s" %(com,))
177
178        if len(img) > 0:
179            self.add_image(ExtLinuxImage(img, path))
180
181    def hasPassword(self):
182        return False
183
184    def hasPasswordAccess(self):
185        return True
186
187    def add_image(self, image):
188        self.images.append(image)
189
190    def _get_default(self):
191        for i in range(len(self.images)):
192            if self.images[i].title == self._default:
193                return i
194        return 0
195    def _set_default(self, val):
196        self._default = val
197    default = property(_get_default, _set_default)
198
199    commands = { "default": "default",
200                 "timeout": "timeout",
201                 "serial": None,
202                 "prompt": None,
203                 "display": None,
204                 "f1": None,
205                 "f2": None,
206                 }
207
208if __name__ == "__main__":
209    if len(sys.argv) < 2:
210        raise RuntimeError, "Need a configuration file to read"
211    g = ExtLinuxConfigFile(sys.argv[1])
212    for i in g.images:
213        print i
214    print g.default
215