from bootstrapvz.base import Task from ..exceptions import TaskError from .. import phases from ..tools import log_check_call from . import filesystem from . import kernel from bootstrapvz.base.fs import partitionmaps import os.path class AddGrubPackage(Task): description = 'Adding grub package' phase = phases.preparation @classmethod def run(cls, info): info.packages.add('grub-pc') class InitGrubConfig(Task): description = 'Initializing grub standard configuration' phase = phases.preparation @classmethod def run(cls, info): # The default values and documentation below was fetched from # https://www.gnu.org/software/grub/manual/html_node/Simple-configuration.html # Some explanations have been shortened info.grub_config = { # The default menu entry. This may be a number, in which case it identifies the Nth entry # in the generated menu counted from zero, or the title of a menu entry, # or the special string `saved'. Using the title may be useful if you want to set a menu entry # as the default even though there may be a variable number of entries before it. # If you set this to `saved', then the default menu entry will be that # saved by `GRUB_SAVEDEFAULT', grub-set-default, or grub-reboot. # The default is `0'. 'GRUB_DEFAULT': 0, # If this option is set to `true', then, when an entry is selected, # save it as a new default entry for use by future runs of GRUB. # This is only useful if `GRUB_DEFAULT=saved'; # it is a separate option because `GRUB_DEFAULT=saved' is useful without this option, # in conjunction with grub-set-default or grub-reboot. Unset by default. 'GRUB_SAVEDEFAULT': None, # Boot the default entry this many seconds after the menu is displayed, unless a key is pressed. # The default is `5'. Set to `0' to boot immediately without displaying the menu, # or to `-1' to wait indefinitely. 'GRUB_TIMEOUT': 5, # Wait this many seconds for a key to be pressed before displaying the menu. # If no key is pressed during that time, display the menu for the number of seconds specified # in GRUB_TIMEOUT before booting the default entry. # We expect that most people who use GRUB_HIDDEN_TIMEOUT will want to have GRUB_TIMEOUT set to `0' # so that the menu is not displayed at all unless a key is pressed. Unset by default. 'GRUB_HIDDEN_TIMEOUT': None, # In conjunction with `GRUB_HIDDEN_TIMEOUT', set this to `true' to suppress the verbose countdown # while waiting for a key to be pressed before displaying the menu. Unset by default. 'GRUB_HIDDEN_TIMEOUT_QUIET': None, # Variants of the corresponding variables without the `_BUTTON' suffix, # used to support vendor-specific power buttons. See Vendor power-on keys. 'GRUB_DEFAULT_BUTTON': None, 'GRUB_TIMEOUT_BUTTON': None, 'GRUB_HIDDEN_TIMEOUT_BUTTON': None, 'GRUB_BUTTON_CMOS_ADDRESS': None, # Set by distributors of GRUB to their identifying name. # This is used to generate more informative menu entry titles. 'GRUB_DISTRIBUTOR': None, # Select the terminal input device. You may select multiple devices here, separated by spaces. # Valid terminal input names depend on the platform, but may include # `console' (PC BIOS and EFI consoles), # `serial' (serial terminal), # `ofconsole' (Open Firmware console), # `at_keyboard' (PC AT keyboard, mainly useful with Coreboot), # or `usb_keyboard' (USB keyboard using the HID Boot Protocol, # for cases where the firmware does not handle this). # The default is to use the platform's native terminal input. 'GRUB_TERMINAL_INPUT': None, # Select the terminal output device. You may select multiple devices here, separated by spaces. # Valid terminal output names depend on the platform, but may include # `console' (PC BIOS and EFI consoles), # `serial' (serial terminal), # `gfxterm' (graphics-mode output), # `ofconsole' (Open Firmware console), # or `vga_text' (VGA text output, mainly useful with Coreboot). # The default is to use the platform's native terminal output. 'GRUB_TERMINAL_OUTPUT': None, # If this option is set, it overrides both `GRUB_TERMINAL_INPUT' and `GRUB_TERMINAL_OUTPUT' # to the same value. 'GRUB_TERMINAL': None, # A command to configure the serial port when using the serial console. # See serial. Defaults to `serial'. 'GRUB_SERIAL_COMMAND': 'serial', # Command-line arguments to add to menu entries for the Linux kernel. 'GRUB_CMDLINE_LINUX': [], # Unless `GRUB_DISABLE_RECOVERY' is set to `true', # two menu entries will be generated for each Linux kernel: # one default entry and one entry for recovery mode. # This option lists command-line arguments to add only to the default menu entry, # after those listed in `GRUB_CMDLINE_LINUX'. 'GRUB_CMDLINE_LINUX_DEFAULT': [], # As `GRUB_CMDLINE_LINUX' and `GRUB_CMDLINE_LINUX_DEFAULT', but for NetBSD. 'GRUB_CMDLINE_NETBSD': [], 'GRUB_CMDLINE_NETBSD_DEFAULT': [], # As `GRUB_CMDLINE_LINUX', but for GNU Mach. 'GRUB_CMDLINE_GNUMACH': [], # The values of these options are appended to the values of # `GRUB_CMDLINE_LINUX' and `GRUB_CMDLINE_LINUX_DEFAULT' # for Linux and Xen menu entries. 'GRUB_CMDLINE_XEN': [], 'GRUB_CMDLINE_XEN_DEFAULT': [], # The values of these options replace the values of # `GRUB_CMDLINE_LINUX' and `GRUB_CMDLINE_LINUX_DEFAULT' for Linux and Xen menu entries. 'GRUB_CMDLINE_LINUX_XEN_REPLACE': [], 'GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT': [], # Normally, grub-mkconfig will generate menu entries that use # universally-unique identifiers (UUIDs) to identify the root filesystem to the Linux kernel, # using a `root=UUID=...' kernel parameter. This is usually more reliable, # but in some cases it may not be appropriate. # To disable the use of UUIDs, set this option to `true'. 'GRUB_DISABLE_LINUX_UUID': None, # If this option is set to `true', disable the generation of recovery mode menu entries. 'GRUB_DISABLE_RECOVERY': None, # If graphical video support is required, either because the `gfxterm' graphical terminal is # in use or because `GRUB_GFXPAYLOAD_LINUX' is set, # then grub-mkconfig will normally load all available GRUB video drivers and # use the one most appropriate for your hardware. # If you need to override this for some reason, then you can set this option. # After grub-install has been run, the available video drivers are listed in /boot/grub/video.lst. 'GRUB_VIDEO_BACKEND': None, # Set the resolution used on the `gfxterm' graphical terminal. # Note that you can only use modes which your graphics card supports # via VESA BIOS Extensions (VBE), so for example native LCD panel resolutions # may not be available. # The default is `auto', which tries to select a preferred resolution. See gfxmode. 'GRUB_GFXMODE': 'auto', # Set a background image for use with the `gfxterm' graphical terminal. # The value of this option must be a file readable by GRUB at boot time, # and it must end with .png, .tga, .jpg, or .jpeg. # The image will be scaled if necessary to fit the screen. 'GRUB_BACKGROUND': None, # Set a theme for use with the `gfxterm' graphical terminal. 'GRUB_THEME': None, # Set to `text' to force the Linux kernel to boot in normal text mode, # `keep' to preserve the graphics mode set using `GRUB_GFXMODE', # `widthxheight'[`xdepth'] to set a particular graphics mode, # or a sequence of these separated by commas or semicolons to try several modes in sequence. # See gfxpayload. # Depending on your kernel, your distribution, your graphics card, and the phase of the moon, # note that using this option may cause GNU/Linux to suffer from various display problems, # particularly during the early part of the boot sequence. # If you have problems, set this option to `text' and GRUB will # tell Linux to boot in normal text mode. 'GRUB_GFXPAYLOAD_LINUX': None, # Normally, grub-mkconfig will try to use the external os-prober program, if installed, # to discover other operating systems installed on the same system and generate appropriate # menu entries for them. Set this option to `true' to disable this. 'GRUB_DISABLE_OS_PROBER': None, # Play a tune on the speaker when GRUB starts. # This is particularly useful for users unable to see the screen. # The value of this option is passed directly to play. 'GRUB_INIT_TUNE': None, # If this option is set, GRUB will issue a badram command to filter out specified regions of RAM. 'GRUB_BADRAM': None, # This option may be set to a list of GRUB module names separated by spaces. # Each module will be loaded as early as possible, at the start of grub.cfg. 'GRUB_PRELOAD_MODULES': [], } class WriteGrubConfig(Task): description = 'Writing grub defaults configuration' phase = phases.system_modification @classmethod def run(cls, info): grub_config_contents = """# This file was created by bootstrap-vz. # See https://github.com/andsens/bootstrap-vz/blob/master/LICENSE for # legal notices and disclaimers. """ for key, value in info.grub_config.items(): if isinstance(value, str): grub_config_contents += '{}="{}"\n'.format(key, value) elif isinstance(value, int): grub_config_contents += '{}={}\n'.format(key, value) elif isinstance(value, bool): grub_config_contents += '{}="{}"\n'.format(key, str(value).lower()) elif isinstance(value, list): if value: args_list = ' '.join(map(str, value)) grub_config_contents += '{}="{}"\n'.format(key, args_list) elif value is not None: raise TaskError('Don\'t know how to handle type {}, ' 'when creating grub config'.format(type(value))) grub_defaults = os.path.join(info.root, 'etc/default/grub') with open(grub_defaults, 'w') as grub_defaults_handle: grub_defaults_handle.write(grub_config_contents) class DisablePNIN(Task): description = 'Disabling Predictable Network Interfaces' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): # See issue #245 for more details info.grub_config['GRUB_CMDLINE_LINUX'].append('net.ifnames=0') info.grub_config['GRUB_CMDLINE_LINUX'].append('biosdevname=0') class DisableConsoleBlanking(Task): description = 'Disabling Console blanking' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): info.grub_config['GRUB_CMDLINE_LINUX'].append('consoleblank=0') class SetIoScheduler(Task): description = 'Set VM optimized IO scheduler' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): info.grub_config['GRUB_CMDLINE_LINUX'].append('elevator=noop') class MakeLinuxFastAgain(Task): description = 'make-linux-fast-again.com' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): info.grub_config['GRUB_CMDLINE_LINUX'].append('noibrs') info.grub_config['GRUB_CMDLINE_LINUX'].append('noibpb') info.grub_config['GRUB_CMDLINE_LINUX'].append('nopti') info.grub_config['GRUB_CMDLINE_LINUX'].append('nospectre_v2') info.grub_config['GRUB_CMDLINE_LINUX'].append('nospectre_v1') info.grub_config['GRUB_CMDLINE_LINUX'].append('l1tf=off') info.grub_config['GRUB_CMDLINE_LINUX'].append('nospec_store_bypass_disable') info.grub_config['GRUB_CMDLINE_LINUX'].append('no_stf_barrier') info.grub_config['GRUB_CMDLINE_LINUX'].append('mds=off') info.grub_config['GRUB_CMDLINE_LINUX'].append('mitigations=off') class SetGrubTerminalToConsole(Task): description = 'Setting the grub terminal to `console\'' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): # See issue #245 for more details info.grub_config['GRUB_TERMINAL'] = 'console' class SetGrubConsolOutputDeviceToSerial(Task): description = 'Setting the grub terminal output device to `ttyS0\'' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): # See issue #245 for more details info.grub_config['GRUB_CMDLINE_LINUX'].append('console=ttyS0') info.grub_config['GRUB_CMDLINE_LINUX'].append('earlyprintk=ttyS0') class RemoveGrubTimeout(Task): description = 'Setting grub menu timeout to 0' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): info.grub_config['GRUB_TIMEOUT'] = 0 info.grub_config['GRUB_HIDDEN_TIMEOUT'] = 0 info.grub_config['GRUB_HIDDEN_TIMEOUT_QUIET'] = True class DisableGrubRecovery(Task): description = 'Disabling the grub recovery menu entry' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): info.grub_config['GRUB_DISABLE_RECOVERY'] = True class EnableSystemd(Task): description = 'Enabling systemd' phase = phases.system_modification successors = [WriteGrubConfig] @classmethod def run(cls, info): info.grub_config['GRUB_CMDLINE_LINUX'].append('init=/bin/systemd') class InstallGrub_1_99(Task): description = 'Installing grub 1.99' phase = phases.system_modification predecessors = [filesystem.FStab, WriteGrubConfig] @classmethod def run(cls, info): p_map = info.volume.partition_map # GRUB screws up when installing in chrooted environments # so we fake a real harddisk with dmsetup. # Guide here: http://ebroder.net/2009/08/04/installing-grub-onto-a-disk-image/ from ..fs import unmounted with unmounted(info.volume): info.volume.link_dm_node() if isinstance(p_map, partitionmaps.none.NoPartitions): p_map.root.device_path = info.volume.device_path try: [device_path] = log_check_call(['readlink', '-f', info.volume.device_path]) device_map_path = os.path.join(info.root, 'boot/grub/device.map') partition_prefix = 'msdos' if isinstance(p_map, partitionmaps.gpt.GPTPartitionMap): partition_prefix = 'gpt' with open(device_map_path, 'w') as device_map: device_map.write('(hd0) {device_path}\n'.format(device_path=device_path)) if not isinstance(p_map, partitionmaps.none.NoPartitions): for idx, partition in enumerate(info.volume.partition_map.partitions): device_map.write('(hd0,{prefix}{idx}) {device_path}\n' .format(device_path=partition.device_path, prefix=partition_prefix, idx=idx + 1)) # Install grub log_check_call(['chroot', info.root, 'grub-install', device_path]) log_check_call(['chroot', info.root, 'update-grub']) finally: with unmounted(info.volume): info.volume.unlink_dm_node() if isinstance(p_map, partitionmaps.none.NoPartitions): p_map.root.device_path = info.volume.device_path class InstallGrub_2(Task): description = 'Installing grub 2' phase = phases.system_modification predecessors = [filesystem.FStab, WriteGrubConfig] # Make sure the kernel image is updated after we have installed the bootloader successors = [kernel.UpdateInitramfs] @classmethod def run(cls, info): log_check_call(['chroot', info.root, 'grub-install', info.volume.device_path]) log_check_call(['chroot', info.root, 'update-grub'])