# -*- coding: utf-8 -*- # Copyright (c) 2007 Jimmy Rönnholm # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import os from bootconfig import utils class Grub: """Represent the Grub configuration.""" def __init__(self): """Set the variables""" self.config_file = '/etc/default/grub' self.header_config_file = '/etc/grub.d/00_header' self.readonly_config_file = '/boot/grub/grub.cfg' self.update_grub_command = '/usr/sbin/update-grub' self.grub_install_command = '/usr/sbin/grub-install' self.is_changed = False # Check that grub commands exist for path in [self.update_grub_command, self.grub_install_command, self.config_file, self.header_config_file]: if not os.path.isfile(path): raise SystemExit('Fatal error. Can not find %s' % path) # Make sure config files are up to date os.system(self.update_grub_command) def __get_linux_options(self): """Return a list of the options passed to the Linux kernel.""" filename = self.config_file identifier = 'GRUB_CMDLINE_LINUX=' lines = utils.read_lines_from_file(filename) line_number = utils.get_line_number(filename, identifier) line = lines[line_number] first = line.find('"') + 1 last = line.rfind('"') return line[first:last].split(' ') def __set_linux_options(self, options): """Set the options passed to the Linux kernel from list options.""" filename = self.config_file identifier = 'GRUB_CMDLINE_LINUX=' options = set(options) new_line = 'GRUB_CMDLINE_LINUX="' for option in options: new_line += option + ' ' new_line = new_line[:-1] + '"\n' # Remove the last space lines = utils.read_lines_from_file(filename) line_number = utils.get_line_number(filename, identifier) lines[line_number] = new_line utils.write_lines_to_file(filename, lines) self.is_changed = True def __change_config(self, filename, identifier, value): """Change the configuration files based on the passed value.""" line_number = utils.get_line_number(filename, identifier) lines = utils.read_lines_from_file(filename) if line_number == -1: lines.append('\n') line_number = len(lines) - 1 new_line = identifier + str(value) + '\n' lines[line_number] = new_line utils.write_lines_to_file(filename, lines) self.is_changed = True def get_timeout(self): """Return the timeout in seconds used for Grub menu.""" line = utils.get_and_trim_line(self.config_file, 'GRUB_TIMEOUT=') timeout = utils.extract_number(line) if timeout == -1: timeout = 5 return timeout def set_timeout(self, timeout): """Set the timeout in seconds used in the Grub menu. timeout = number of seconds """ self.__change_config(self.config_file, 'GRUB_TIMEOUT=', timeout) def get_titles(self): """Return names of the boot options, as a list.""" identifier = 'menuentry' lines = utils.read_lines_from_file(self.readonly_config_file) entries = [] for line in lines: if line[:len(identifier)] == identifier: first = line.find('"') + 1 last = line.rfind('"') entries.append(line[first:last]) return entries def get_default_boot(self): """Return the name of the default booted option.""" line = utils.get_and_trim_line(self.config_file, 'GRUB_DEFAULT=') default_boot = utils.extract_number(line) if default_boot == -1: default_boot = 0 entries = self.get_titles() if default_boot < len(entries): return entries[default_boot] return entries[0] def set_default_boot(self, name): """Set the the default booted option to name. Return 1 if failed. """ entries = self.get_titles() try: default = entries.index(name) except: return 1 self.__change_config(self.config_file, 'GRUB_DEFAULT=', default) def get_vga_code(self): """Return the Grub vga code used, as integer. If no code is specified in the config, 769 is returned. Grub vga codes: Colours 640x400 640x480 800x600 1024x768 1152x864 1280x1024 1600x1200 --------+-------------------------------------------------------------- 4 bits | ? ? 770 ? ? ? ? 8 bits | 768 769 771 773 353 775 796 15 bits | ? 784 787 790 354 793 797 16 bits | ? 758 788 791 355 794 798 24 bits | ? 786 789 792 ? 795 799 32 bits | ? ? ? ? 356 ? ? """ vga_code = 769 options = self.__get_linux_options() for option in options: if option[:4] == 'vga=': value = option[4:] if len(value) == 5: # Convert from hexadecimal format vga_code = int(value, 16) else: vga_code = int(value) return vga_code def set_vga_code(self, vga): """Set the resolution based on Grub vga code. vga = grub vga code For vga codes, see get_vga_code """ options = self.__get_linux_options() for i, option in enumerate(options): if option[:4] == 'vga=': del options[i] options.append('vga=' + str(vga)) self.__set_linux_options(options) def get_boot_text_visible(self): """Return whether a verbose boot is enabled.""" return not 'quiet' in self.__get_linux_options() def set_boot_text_visible(self, active): """Set whether a verbose boot is enabled.""" options = self.__get_linux_options() if not active: options.append('quiet') elif 'quiet' in options: options.remove('quiet') self.__set_linux_options(options) def get_splash_active(self): """Return whether a boot splash is shown.""" return 'splash' in self.__get_linux_options() def set_splash_active(self, active): """Set whether a boot splash is shown.""" options = self.__get_linux_options() if active: options.append('splash') elif 'splash' in options: options.remove('splash') self.__set_linux_options(options) def get_gfxmode(self): """Return the gfxmode resolution used in Grub menu. Return -1 if no gfxmode can be read. """ return utils.get_and_trim_line(self.header_config_file, 'set gfxmode=') def set_gfxmode(self, resolution): """Set the gfxmode resolution for Grub menu. resolution ex. '1024x768' Return 1 if the value can not be set. """ filename = self.header_config_file identifier = 'set gfxmode=' line_number = utils.get_line_number(filename, identifier) if line_number == -1: # We do not know where to insert the value return 1 self.__change_config(filename, identifier, resolution) def format_floppy(self): """Format a floppy. Return 0 if it was successful. Return 1 if a floppy was not found. Return 3 if there was an OSError. Return a code < 0 or > 3 for other errors. """ return utils.format_floppy() def make_floppy(self): """Write a Grub boot floppy. Return 0 if it was successful. Return 1 if there was an OSError. Return a code < 0 or > 1 for other errors. """ return utils.make_floppy(self.grub_install_command, self.config_file) def do_shutdown_tasks(self): """Do any post-config tasks necessary.""" if self.is_changed: os.system(self.update_grub_command)