def GenerateSections( self, boot_menu: BootLoaderMenu, sfunc: Callable[[BootLoaderMenu, str, str], bool], ofunc: Callable[[BootLoaderMenu, str], bool] = None) -> BootLoaderMenu: """Generates sections using passed in extension-supplied functions""" try: timeout = int(self.boot_config["boot/timeout"]) except ValueError: ok = False self.msgs.append(["fatal", "Invalid value for boot/timeout."]) return boot_menu if timeout == 0: self.msgs.append([ "warn", "boot/timeout value is zero - boot menu will not appear!" ]) elif timeout < 3: self.msgs.append( ["norm", "boot/timeout value is below 3 seconds."]) # Remove builtins from list of sections sections = self.boot_config.getSections() for sect in sections[:]: if sect in self.boot_config.builtins: sections.remove(sect) # If we have no boot entries, throw an error - force user to be # explicit. if len(sections) == 0: self.msgs.append( ["fatal", "No boot entries are defined in /etc/boot.conf."]) boot_menu.success = False return boot_menu # Warn if there are no linux entries has_linux = False for sect in sections: if self.boot_config["{s}/{t}".format(s=sect, t="type")] == "linux": has_linux = True break if has_linux is False: self.msgs.append([ "warn", "No Linux boot entries are defined. You may not be able to re-enter Linux." ]) # Generate sections for sect in sections: if self.boot_config["{s}/type".format(s=sect)] in ["linux", "xen"]: ok = self._GenerateLinuxSection(boot_menu, sect, sfunc) elif ofunc: ok = self._GenerateOtherSection(boot_menu, sect, ofunc) if self._pos == 0: # this means we processed no kernels -- so we have nothing to boot! self.msgs.append([ "fatal", "No matching kernels or boot entries found in /etc/boot.conf." ]) boot_menu.success = False return boot_menu elif boot_menu.default_position is None: # this means we didn't pick a default kernel to boot! self.msgs.append([ "warn", "Had difficulty finding a default kernel -- using first one." ]) # If we didn't find a specified default, use the first one boot_menu.default_position = 0 else: self.msgs.append([ "note", "Default kernel selected via: %s." % self._default_mode ]) # Tag the boot menu as being default for display: boot_menu.boot_entries[boot_menu.default_position]["flags"].append( BootMenuFlag.DEFAULT) if self._default_mode == "autopick: mtime" and self.boot_config.item( "boot", "autopick") == "last-booted": self.msgs.append([ "warn", "Falling back to last modification time booting due to lack of last-booted info." ]) return boot_menu
def generateConfigFile(self, boot_menu: BootLoaderMenu): if self.uefiboot: self.msgs.append( ["note", "Detected UEFI boot. Configuring for UEFI booting."]) else: self.msgs.append([ "note", "Detected MBR boot. Configuring for Legacy MBR booting." ]) boot_menu.lines.append( self.boot_config.condFormatSubItem("boot/timeout", "set timeout={s}")) # pass our boot entry generator function to GenerateSections, # and everything is taken care of for our boot entries boot_menu.lines += [ "", "if [ -s $prefix/grubenv ]; then", " load_env", "fi", "", "function savedefault {", " if [ -z \"{boot_once}\" ]; then", " saved_entry=\"${chosen}\"", " save_env saved_entry", " fi", "}" ] if self.boot_config.hasItem("boot/terminal") and self.boot_config[ "boot/terminal"] == "serial": self.msgs.append(["warn", "Configured for SERIAL input/output."]) boot_menu.lines += [ "serial --unit=%s --speed=%s --word=%s --parity=%s --stop=%s" % (self.boot_config["serial/unit"], self.boot_config["serial/speed"], self.boot_config["serial/word"], self.boot_config["serial/parity"], self.boot_config["serial/stop"]), "terminal_input serial", "terminal_output serial" ] elif self.boot_config.hasItem("display/gfxmode"): boot_menu.lines.append("") self.PrepareGRUBForFilesystem( os.path.join(self.config.root_path, self.boot_config["boot/path"].lstrip('/')), boot_menu.lines) if self.boot_config.hasItem("display/font"): font = self.boot_config["display/font"] else: font = None dst_font = None if font is None: fonts = ["unicode.pf2", "unifont.pf2"] else: fonts = [font] for fontpath in [self.grubpath, self.grubpath + "/fonts"]: if dst_font is not None: break for font in fonts: path_to_font = fontpath + "/" + font full_path_to_font = os.path.join(self.config.root_path, path_to_font.lstrip("/")) if os.path.exists(full_path_to_font): dst_font = path_to_font break if dst_font is None: # font does not exist at destination... so we will need to find it somewhere and copy into /boot/grub for fontpath in self.boot_config["grub/font_src"].split(): if dst_font is not None: break for font in fonts: path_to_font = fontpath + "/" + font if os.path.exists(path_to_font): src_font = path_to_font dst_font = self.grubpath + '/fonts/' + font dst_font = os.path.join(self.config.root_path, dst_font.lstrip("/")) if not os.path.exists(dst_font): import shutil shutil.copy(src_font, dst_font) break if dst_font is None: if font: self.msgs.append([ "fatal", "specified font \"{ft}\" not found at {dst}; aborting." .format(ft=font, dst=dst_font) ]) else: self.msgs.append([ "fatal", "Could not find one of %s to copy into boot directory; aborting." % ",".join(fonts) ]) boot_menu.success = False boot_menu.lines += [ "if loadfont {dst}; then".format( dst=self.resolver.RelativePathTo( dst_font, self.boot_config["boot/path"])), " set gfxmode={gfx}".format(gfx=self.sanitizeDisplayMode( self.boot_config["display/gfxmode"])), " insmod all_video", " terminal_output gfxterm" ] bg = self.boot_config.item("display", "background").split() if len(bg): bgimg = None bgext = None if len(bg) == 1: # get extension from file: bgimg = bg[0] bgext = bg[0].rsplit(".")[-1].lower() elif len(bg) == 2: # extension specified as second argument: bgimg, bgext = bg else: self.msgs.append([ "warn", "Unexpected number of arguments for background image - skipping." ]) if bgimg is not None: if bgext == "jpg": bgext = "jpeg" if bgext in ["jpeg", "png", "tga"]: rel_cfgpath = "{path}/{img}".format( path=self.boot_config["boot/path"], img=bgimg) # first, look for absolute path, because our relative path # can eval to "/boot/boot/foo.png" which # due to the /boot/boot symlink will "exist". if bgimg[0] == "/" and os.path.exists(bgimg): # user specified absolute path to file on disk: boot_menu.lines += [ " insmod {bg}".format(bg=bgext), " background_image {img}".format( img=self.resolver.RelativePathTo( bgimg, self.boot_config["boot/path"])) ] elif os.path.exists(rel_cfgpath): # user specified path relative to /boot: boot_menu.lines += [ " insmod {ext}".format(ext=bgext), " background_image {img}".format( img=self.resolver.RelativePathTo( rel_cfgpath, self.boot_config["boot/path"])) ] else: self.msgs.append([ "warn", "background image \"{img}\" does not exist - skipping." .format(img=bgimg) ]) else: self.msgs.append([ "warn", "background image \"{img}\" (format \"{ext}\") not recognized - skipping." .format(img=bgimg, ext=bgext) ]) boot_menu.lines += [ "fi", "", self.boot_config.condFormatSubItem( "color/normal", "set menu_color_normal={s}"), self.boot_config.condFormatSubItem( "color/highlight", "set menu_color_highlight={s}"), ] else: if self.boot_config.hasItem("display/background"): self.msgs.append([ "warn", "display/gfxmode not provided - display/background \"{bg}\" will not be displayed." .format(bg=self.boot_config["display/background"]) ]) self.resolver.GenerateSections(boot_menu, self.generateBootEntry, self.generateOtherBootEntry) if boot_menu.user_specified_attempt_identifier: if boot_menu.attempt_kname is not None and boot_menu.attempt_position is not None: boot_menu.lines += [] # This condition indicates that we successfully found the boot entry in the boot menu: # Record entry on-disk as a kernel to promote to default if we succeed booting... self.boot_config.idmapper.update_promote_kname( boot_menu._attempt_kname) self._attempt_kernel(boot_menu) else: self.msgs.append([ "error", "Unable to find a matching boot entry for attempted kernel you specified." ]) else: # make sure the *default* kernel is attempted, to wipe out any existing attempt settings. self._attempt_kernel(boot_menu, set_default=True) # The following lines load the GRUB env data. Then we see if "$next_entry" is set, which specifies a to-be-attempted kernel. # If so, it becomes the default (for one boot.) Otherwise, we look and see if "$saved_entry" is set, which is the grub env # variable set by "grub-set-default". If this specifies a default kernel, we'll use it. Otherwise, fall back to a hard- # coded value from boot-update config itself, pointing to the default kernel. boot_menu.lines += [ "", "if [ ! \"${next_entry}\" = \"\" ] ; then", " set default=\"${next_entry}\"", " set next_entry=", " save_env next_entry", " set boot_once=true", "elif [ ! \"${saved_entry}\" = \"\" ]; then", " set default=\"${saved_entry}\"", "else", " set default={pos}".format(pos=boot_menu.default_position), "fi" ]
def generateConfigFile(self, boot_menu: BootLoaderMenu): if self.uefiboot: self.msgs.append(["note", "Detected UEFI boot. Configuring for UEFI booting."]) else: self.msgs.append(["note", "Detected MBR boot. Configuring for Legacy MBR booting."]) boot_menu.lines.append(self.boot_config.condFormatSubItem("boot/timeout", "set timeout={s}")) # pass our boot entry generator function to GenerateSections, # and everything is taken care of for our boot entries boot_menu.lines += [ "", "if [ -s $prefix/grubenv ]; then", " load_env", "fi", "", "function savedefault {", " if [ -z \"{boot_once}\" ]; then", " saved_entry=\"${chosen}\"", " save_env saved_entry", " fi", "}" ] if self.boot_config.hasItem("boot/terminal") and self.boot_config["boot/terminal"] == "serial": self.msgs.append(["warn", "Configured for SERIAL input/output."]) boot_menu.lines += [ "serial --unit=%s --speed=%s --word=%s --parity=%s --stop=%s" % ( self.boot_config["serial/unit"], self.boot_config["serial/speed"], self.boot_config["serial/word"], self.boot_config["serial/parity"], self.boot_config["serial/stop"]), "terminal_input serial", "terminal_output serial" ] elif self.boot_config.hasItem("display/gfxmode"): boot_menu.lines.append("") self.PrepareGRUBForFilesystem(os.path.join(self.config.root_path, self.boot_config["boot/path"].lstrip('/')), boot_menu.lines) if self.boot_config.hasItem("display/font"): font = self.boot_config["display/font"] else: font = None dst_font = None if font is None: fonts = ["unicode.pf2", "unifont.pf2"] else: fonts = [font] for fontpath in [self.grubpath, self.grubpath + "/fonts"]: if dst_font is not None: break for font in fonts: path_to_font = fontpath + "/" + font full_path_to_font = os.path.join(self.config.root_path, path_to_font.lstrip("/")) if os.path.exists(full_path_to_font): dst_font = path_to_font break if dst_font is None: # font does not exist at destination... so we will need to find it somewhere and copy into /boot/grub for fontpath in self.boot_config["grub/font_src"].split(): if dst_font is not None: break for font in fonts: path_to_font = fontpath + "/" + font if os.path.exists(path_to_font): src_font = path_to_font dst_font = self.grubpath + '/fonts/' + font dst_font = os.path.join(self.config.root_path, dst_font.lstrip("/")) if not os.path.exists(dst_font): import shutil shutil.copy(src_font, dst_font) break if dst_font is None: if font: self.msgs.append(["fatal", "specified font \"{ft}\" not found at {dst}; aborting.".format(ft=font, dst=dst_font)]) else: self.msgs.append(["fatal", "Could not find one of %s to copy into boot directory; aborting." % ",".join(fonts)]) boot_menu.success = False boot_menu.lines += ["if loadfont {dst}; then".format(dst=self.resolver.RelativePathTo(dst_font, self.boot_config["boot/path"])), " set gfxmode={gfx}".format(gfx=self.sanitizeDisplayMode(self.boot_config["display/gfxmode"])), " insmod all_video", " terminal_output gfxterm"] bg = self.boot_config.item("display", "background").split() if len(bg): bgimg = None bgext = None if len(bg) == 1: # get extension from file: bgimg = bg[0] bgext = bg[0].rsplit(".")[-1].lower() elif len(bg) == 2: # extension specified as second argument: bgimg, bgext = bg else: self.msgs.append(["warn", "Unexpected number of arguments for background image - skipping."]) if bgimg is not None: if bgext == "jpg": bgext = "jpeg" if bgext in ["jpeg", "png", "tga"]: rel_cfgpath = "{path}/{img}".format(path=self.boot_config["boot/path"], img=bgimg) # first, look for absolute path, because our relative path # can eval to "/boot/boot/foo.png" which # due to the /boot/boot symlink will "exist". if bgimg[0] == "/" and os.path.exists(bgimg): # user specified absolute path to file on disk: boot_menu.lines += [ " insmod {bg}".format(bg=bgext), " background_image {img}".format(img=self.resolver.RelativePathTo(bgimg, self.boot_config["boot/path"])) ] elif os.path.exists(rel_cfgpath): # user specified path relative to /boot: boot_menu.lines += [ " insmod {ext}".format(ext=bgext), " background_image {img}".format(img=self.resolver.RelativePathTo(rel_cfgpath, self.boot_config["boot/path"])) ] else: self.msgs.append(["warn", "background image \"{img}\" does not exist - skipping.".format(img=bgimg)]) else: self.msgs.append(["warn", "background image \"{img}\" (format \"{ext}\") not recognized - skipping.".format(img=bgimg, ext=bgext)]) boot_menu.lines += ["fi", "", self.boot_config.condFormatSubItem("color/normal", "set menu_color_normal={s}"), self.boot_config.condFormatSubItem("color/highlight", "set menu_color_highlight={s}"), ] else: if self.boot_config.hasItem("display/background"): self.msgs.append(["warn", "display/gfxmode not provided - display/background \"{bg}\" will not be displayed.".format(bg=self.boot_config["display/background"])]) self.resolver.GenerateSections(boot_menu, self.generateBootEntry, self.generateOtherBootEntry) if boot_menu.user_specified_attempt_identifier: if boot_menu.attempt_kname is not None and boot_menu.attempt_position is not None: boot_menu.lines += [ ] # This condition indicates that we successfully found the boot entry in the boot menu: # Record entry on-disk as a kernel to promote to default if we succeed booting... self.boot_config.idmapper.update_promote_kname(boot_menu._attempt_kname) self._attempt_kernel(boot_menu) else: self.msgs.append(["error", "Unable to find a matching boot entry for attempted kernel you specified."]) else: # make sure the *default* kernel is attempted, to wipe out any existing attempt settings. self._attempt_kernel(boot_menu, set_default=True) # The following lines load the GRUB env data. Then we see if "$next_entry" is set, which specifies a to-be-attempted kernel. # If so, it becomes the default (for one boot.) Otherwise, we look and see if "$saved_entry" is set, which is the grub env # variable set by "grub-set-default". If this specifies a default kernel, we'll use it. Otherwise, fall back to a hard- # coded value from boot-update config itself, pointing to the default kernel. boot_menu.lines += [ "", "if [ ! \"${next_entry}\" = \"\" ] ; then", " set default=\"${next_entry}\"", " set next_entry=", " save_env next_entry", " set boot_once=true", "elif [ ! \"${saved_entry}\" = \"\" ]; then", " set default=\"${saved_entry}\"", "else", " set default={pos}".format(pos=boot_menu.default_position), "fi" ]
def GenerateSections(self, boot_menu: BootLoaderMenu, sfunc: Callable[[BootLoaderMenu, str, str], bool], ofunc: Callable[[BootLoaderMenu, str], bool] = None) -> BootLoaderMenu: """Generates sections using passed in extension-supplied functions""" try: timeout = int(self.boot_config["boot/timeout"]) except ValueError: ok = False self.msgs.append(["fatal", "Invalid value for boot/timeout."]) return boot_menu if timeout == 0: self.msgs.append(["warn", "boot/timeout value is zero - boot menu will not appear!"]) elif timeout < 3: self.msgs.append(["norm", "boot/timeout value is below 3 seconds."]) # Remove builtins from list of sections sections = self.boot_config.getSections() for sect in sections[:]: if sect in self.boot_config.builtins: sections.remove(sect) # If we have no boot entries, throw an error - force user to be # explicit. if len(sections) == 0: self.msgs.append(["fatal", "No boot entries are defined in /etc/boot.conf."]) boot_menu.success = False return boot_menu # Warn if there are no linux entries has_linux = False for sect in sections: if self.boot_config["{s}/{t}".format(s=sect, t="type")] == "linux": has_linux = True break if has_linux is False: self.msgs.append(["warn", "No Linux boot entries are defined. You may not be able to re-enter Linux."]) # Generate sections for sect in sections: if self.boot_config["{s}/type".format(s=sect)] in ["linux", "xen"]: ok = self._GenerateLinuxSection(boot_menu, sect, sfunc) elif ofunc: ok = self._GenerateOtherSection(boot_menu, sect, ofunc) if self._pos == 0: # this means we processed no kernels -- so we have nothing to boot! self.msgs.append(["fatal", "No matching kernels or boot entries found in /etc/boot.conf."]) boot_menu.success = False return boot_menu elif boot_menu.default_position is None: # this means we didn't pick a default kernel to boot! self.msgs.append(["warn", "Had difficulty finding a default kernel -- using first one."]) # If we didn't find a specified default, use the first one boot_menu.default_position = 0 else: self.msgs.append(["note", "Default kernel selected via: %s." % self._default_mode]) # Tag the boot menu as being default for display: boot_menu.boot_entries[boot_menu.default_position]["flags"].append(BootMenuFlag.DEFAULT) if self._default_mode == "autopick: mtime" and self.boot_config.item("boot", "autopick") == "last-booted": self.msgs.append(["warn", "Falling back to last modification time booting due to lack of last-booted info."]) return boot_menu