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