def generateOtherBootEntry(self, boot_menu: BootLoaderMenu, sect) -> bool: """ Generates the boot entry for other systems """ mytype = self.boot_config["{s}/type".format(s=sect)].lower() if mytype in ["dos", "msdos"]: mytype = "dos" elif mytype in ["windows", "windows 2000", "win2000", "windows xp", "winxp"]: mytype = "winxp" elif mytype in ["windows vista", "vista"]: mytype = "vista" elif mytype in ["windows 7", "win7"]: mytype = "win7" elif mytype in ["windows 8", "win8"]: mytype = "win8" elif mytype in ["windows 10", "win10"]: mytype = "win10" elif mytype in ["haiku", "haiku os"]: mytype = "haiku" elif mytype in ["linux16"]: mytype = "linux16" else: self.msgs.append(["fatal", "Unrecognized boot entry type \"{mt}\"".format(mt=mytype)]) return False params = self.boot_config["{s}/params".format(s=sect)].split() myroot = self.resolver.GetParam(params, "root=") mychainloader = self.resolver.GetParam(params, "chainloader=") myname = sect # TODO check for valid root entry boot_menu.lines.append("") boot_menu.lines.append("menuentry \"{mn}\" {{".format(mn=myname)) if mytype in ["linux16"]: k = self.resolver.strip_mount_point(self.boot_config[sect + "/kernel"]) full_k = os.path.join(self.boot_config["boot/path"], k.lstrip("/")) if not os.path.exists(full_k): self.msgs.append(["warn", "Image for section {sect} not found - {full_k}".format(sect=sect, full_k=full_k)]) else: self.bootitems.append(myname) boot_menu.lines.append(" linux16 " + k) else: # TODO: add entry to boot_menu object self.PrepareGRUBForDevice(myroot, boot_menu.lines) self.bootitems.append(myname) self.DeviceGRUB(myroot) if mytype in ["win7", "win8"] or mytype == "win10" and self.uefiboot is False: boot_menu.lines.append(" chainloader " + mychainloader) if mychainloader else boot_menu.lines.append(" chainloader +4") elif mytype in ["vista", "dos", "winxp", "haiku"]: boot_menu.lines.append(" chainloader " + mychainloader) if mychainloader else boot_menu.lines.append(" chainloader +1") elif mytype in ["win10"]: boot_menu.lines.append(" chainloader " + mychainloader) if mychainloader else boot_menu.lines.append(" chainloader /EFI/Microsoft/Boot/bootmgfw.efi") boot_menu.lines.append("}") boot_menu.addBootEntry(BootLoaderEntryType.OTHER, label=myname) return True
def _GenerateOtherSection(self, boot_menu: BootLoaderMenu, sect: str, ofunc: Callable[[BootLoaderMenu, str], bool]) -> bool: """Generate section for non-Linux systems""" ok = ofunc(boot_menu, sect) self._defnames.append(sect) if self._default == sect: if boot_menu.default_position is not None: self.msgs.append(["warn", "multiple matches found for default boot entry \"{name}\" - first match used.".format(name=self._default)]) else: boot_menu.default_position = self._pos self._pos += 1 return ok
def _GenerateOtherSection( self, boot_menu: BootLoaderMenu, sect: str, ofunc: Callable[[BootLoaderMenu, str], bool]) -> bool: """Generate section for non-Linux systems""" ok = ofunc(boot_menu, sect) self._defnames.append(sect) if self._default == sect: if boot_menu.default_position is not None: self.msgs.append([ "warn", "multiple matches found for default boot entry \"{name}\" - first match used." .format(name=self._default) ]) else: boot_menu.default_position = self._pos self._pos += 1 return ok
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 _GenerateLinuxSection( self, boot_menu: BootLoaderMenu, sect: str, sfunc: Callable[[BootLoaderMenu, str, str, str], bool]) -> bool: """Generates section for Linux systems""" ok = True # Process a section, such as "genkernel" section. findlist, skiplist = self.boot_config.flagItemList( "{s}/kernel".format(s=sect)) # findlist == special patterns to match (i.e. kernel[-v]) # skiplist == patterns to skip. findmatch = [] skipmatch = [] scanpaths = self.boot_config.item(sect, "scan").split() for scanpath in scanpaths: scanpath = os.path.join(self.config.root_path, scanpath.lstrip("/")) if self.config.root_path == "/": self.mount_if_necessary(scanpath) if len(skiplist): # find kernels to skip... matches = self.GetMatchingKernels(scanpath, skiplist) skipmatch += matches if len(findlist): # find kernels to match (skipping any kernels we should skip...) matches = self.GetMatchingKernels(scanpath, findlist, skipmatch) findmatch += matches # Generate individual boot entry using extension-supplied function found_multi = False # logic for finding a kernel to boot, based on default setting: # sort by modification time: findmatch = sorted(findmatch, key=lambda x: x[2], reverse=True) if self._default_mode == "autopick: mtime": # pick newest kernel by mtime, which happens to be top-of-list boot_menu.default_position = 0 for kname, kext, mtime in findmatch: self._defnames.append(kname) ok = sfunc(boot_menu, sect, kname, kext) self._pos += 1 else: def_mtime = None for kname, kext, mtime in findmatch: if (self._default == sect) or (self._default == kname) or ( self._default == os.path.basename(kname)): # default match if boot_menu.default_position is not None: found_multi = True if mtime > def_mtime: # this kernel is newer, use it instead boot_menu.default_position = self._pos def_mtime = mtime else: boot_menu.default_position = self._pos def_mtime = os.stat(kname)[8] self._defnames.append(kname) ok = sfunc(boot_menu, sect, kname, kext) if not ok: break self._pos += 1 if found_multi: self.msgs.append([ "warn", "multiple matches found for default \"{name}\" - most recent used." .format(name=self._default) ]) return ok
def generateConfigFile(self, boot_menu: BootLoaderMenu): """ Generate new config file based on config data. Returns a list of all lines of the config file, without trailing newlines. """ return BootLoaderMenu()
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 generateBootEntry(self, boot_menu: BootLoaderMenu, sect: str, k_full_path: str, kext: str) -> bool: """ Generates the boot entry """ mytype = self.boot_config["{s}/type".format(s=sect)] boot_menu.lines.append("") label = self.resolver.GetBootEntryString(sect, k_full_path) boot_menu.lines.append("menuentry \"{l}\" {{".format(l=label)) # TODO: add last-selected, which is different than last-booted. #if self.config["boot/autopick"] == "last-booted": # boot_menu.lines.append(" savedefault") # self.bootitems records all our boot items self.bootitems.append(label) scanpath = os.path.join(self.config.root_path, self.boot_config.item(sect, "scan").lstrip("/")) self.PrepareGRUBForFilesystem(scanpath, boot_menu.lines) # removes ROOT env var and /boot from the kernel path: k_sub_path = self.resolver.strip_mount_point(k_full_path) c = self.boot_config params = [] if c.hasItem("boot/terminal") and c["boot/terminal"] == "serial": params += [ "console=tty0", "console=ttyS%s,%s%s%s" % (c["serial/unit"], c["serial/speed"], c["serial/parity"][0], c["serial/word"]) ] for param in self.boot_config["{s}/params".format(s=sect)].split(): if param not in params: params.append(param) # Logic here to see if we are processing a boot entry that is a kernel we should "attempt" to boot. It gets special parameters added to its boot # entry. It may be tagged by the user on this call to ego boot (user_specified_attempt_identifier) or we may simply have boot_menu.attempt_kname # set due to an attempted kernel having been selected previously: entry = boot_menu.addBootEntry(BootLoaderEntryType.LINUX, label=label, image_path=k_full_path) if BootMenuFlag.ATTEMPT in entry["flags"]: # Add special boot parameters for a kernel we are attempting to boot (usually panic=10 or similar to force a reboot) for param in self.boot_config["{s}/attemptparams".format(s=sect)].split(): if param not in params: params.append(param) # TODO: turn off panic setting after successful boot? (ego boot success?) ok, myroot = self.resolver.calculate_rootfs_for_section(params) if not ok: return False ok, fstype = self.resolver.calculate_filesystem_for_section(params) if not ok: return False initrds = self.boot_config.item(sect, "initrd") initrds = self.resolver.find_initrds(initrds, scanpath, k_full_path, kext) if myroot and ('root=' + myroot) in params and 0 == len(initrds): params.remove('root=' + myroot) params.append('root=' + self.resolver.resolvedev(myroot)) xenpath = None xenparams = None # Populate xen variables if type is xen if mytype == "xen": xenkernel = self.boot_config["{s}/xenkernel".format(s=sect)] # Add leading / if needed if not xenkernel.startswith("/"): xenkernel = "/{xker}".format(xker=xenkernel) xenpath = self.resolver.strip_mount_point(xenkernel) xenparams = self.boot_config["{s}/xenparams".format(s=sect)].split() # Add unique identifier that can be used to determine if kernel booted. params.append("rand_id=%s" % self.resolver.idmapper.get(k_full_path)) # Append kernel lines based on type if mytype == "xen": boot_menu.lines.append(" multiboot {xker} {xparams}".format(xker=xenpath, xparams=" ".join(xenparams))) boot_menu.lines.append(" module {ker} {params}".format(ker=k_sub_path, params=" ".join(params))) for initrd in initrds: boot_menu.lines.append(" module {initrd}".format(initrd=self.resolver.strip_mount_point(initrd))) else: boot_menu.lines.append(" {t} {k} {par}".format(t=mytype, k=k_sub_path, par=" ".join(params))) if initrds: initrds = (self.resolver.strip_mount_point(initrd) for initrd in initrds) boot_menu.lines.append(" initrd {rds}".format(rds=" ".join(initrds))) # Append graphics line if self.boot_config.hasItem("{s}/gfxmode".format(s=sect)): skipgfx = False for p in params: if p.startswith("vga=") or p.startswith("video=uvesafb:"): skipgfx = True break if not skipgfx: boot_menu.lines.append(" set gfxpayload=keep") boot_menu.lines.append("}") return ok
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 generateBootEntry(self, boot_menu: BootLoaderMenu, sect: str, k_full_path: str, kext: str) -> bool: """ Generates the boot entry """ mytype = self.boot_config["{s}/type".format(s=sect)] boot_menu.lines.append("") label = self.resolver.GetBootEntryString(sect, k_full_path) boot_menu.lines.append("menuentry \"{l}\" {{".format(l=label)) # TODO: add last-selected, which is different than last-booted. #if self.config["boot/autopick"] == "last-booted": # boot_menu.lines.append(" savedefault") # self.bootitems records all our boot items self.bootitems.append(label) scanpath = os.path.join( self.config.root_path, self.boot_config.item(sect, "scan").lstrip("/")) self.PrepareGRUBForFilesystem(scanpath, boot_menu.lines) # removes ROOT env var and /boot from the kernel path: k_sub_path = self.resolver.strip_mount_point(k_full_path) c = self.boot_config params = [] if c.hasItem("boot/terminal") and c["boot/terminal"] == "serial": params += [ "console=tty0", "console=ttyS%s,%s%s%s" % (c["serial/unit"], c["serial/speed"], c["serial/parity"][0], c["serial/word"]) ] for param in self.boot_config["{s}/params".format(s=sect)].split(): if param not in params: params.append(param) # Logic here to see if we are processing a boot entry that is a kernel we should "attempt" to boot. It gets special parameters added to its boot # entry. It may be tagged by the user on this call to ego boot (user_specified_attempt_identifier) or we may simply have boot_menu.attempt_kname # set due to an attempted kernel having been selected previously: entry = boot_menu.addBootEntry(BootLoaderEntryType.LINUX, label=label, image_path=k_full_path) if BootMenuFlag.ATTEMPT in entry["flags"]: # Add special boot parameters for a kernel we are attempting to boot (usually panic=10 or similar to force a reboot) for param in self.boot_config["{s}/attemptparams".format( s=sect)].split(): if param not in params: params.append(param) # TODO: turn off panic setting after successful boot? (ego boot success?) ok, myroot = self.resolver.calculate_rootfs_for_section(params) if not ok: return False ok, fstype = self.resolver.calculate_filesystem_for_section(params) if not ok: return False initrds = self.boot_config.item(sect, "initrd") initrds = self.resolver.find_initrds(initrds, scanpath, k_full_path, kext) if myroot and ('root=' + myroot) in params and 0 == len(initrds): params.remove('root=' + myroot) params.append('root=' + self.resolver.resolvedev(myroot)) xenpath = None xenparams = None # Populate xen variables if type is xen if mytype == "xen": xenkernel = self.boot_config["{s}/xenkernel".format(s=sect)] # Add leading / if needed if not xenkernel.startswith("/"): xenkernel = "/{xker}".format(xker=xenkernel) xenpath = self.resolver.strip_mount_point(xenkernel) xenparams = self.boot_config["{s}/xenparams".format( s=sect)].split() # Add unique identifier that can be used to determine if kernel booted. params.append("rand_id=%s" % self.resolver.idmapper.get(k_full_path)) # Append kernel lines based on type if mytype == "xen": boot_menu.lines.append(" multiboot {xker} {xparams}".format( xker=xenpath, xparams=" ".join(xenparams))) boot_menu.lines.append(" module {ker} {params}".format( ker=k_sub_path, params=" ".join(params))) for initrd in initrds: boot_menu.lines.append(" module {initrd}".format( initrd=self.resolver.strip_mount_point(initrd))) else: boot_menu.lines.append(" {t} {k} {par}".format( t=mytype, k=k_sub_path, par=" ".join(params))) if initrds: initrds = (self.resolver.strip_mount_point(initrd) for initrd in initrds) boot_menu.lines.append( " initrd {rds}".format(rds=" ".join(initrds))) # Append graphics line if self.boot_config.hasItem("{s}/gfxmode".format(s=sect)): skipgfx = False for p in params: if p.startswith("vga=") or p.startswith("video=uvesafb:"): skipgfx = True break if not skipgfx: boot_menu.lines.append(" set gfxpayload=keep") boot_menu.lines.append("}") return ok
def generateOtherBootEntry(self, boot_menu: BootLoaderMenu, sect) -> bool: """ Generates the boot entry for other systems """ mytype = self.boot_config["{s}/type".format(s=sect)].lower() if mytype in ["dos", "msdos"]: mytype = "dos" elif mytype in [ "windows", "windows 2000", "win2000", "windows xp", "winxp" ]: mytype = "winxp" elif mytype in ["windows vista", "vista"]: mytype = "vista" elif mytype in ["windows 7", "win7"]: mytype = "win7" elif mytype in ["windows 8", "win8"]: mytype = "win8" elif mytype in ["windows 10", "win10"]: mytype = "win10" elif mytype in ["haiku", "haiku os"]: mytype = "haiku" elif mytype in ["linux16"]: mytype = "linux16" else: self.msgs.append([ "fatal", "Unrecognized boot entry type \"{mt}\"".format(mt=mytype) ]) return False params = self.boot_config["{s}/params".format(s=sect)].split() myroot = self.resolver.GetParam(params, "root=") mychainloader = self.resolver.GetParam(params, "chainloader=") myname = sect # TODO check for valid root entry boot_menu.lines.append("") boot_menu.lines.append("menuentry \"{mn}\" {{".format(mn=myname)) if mytype in ["linux16"]: k = self.resolver.strip_mount_point(self.boot_config[sect + "/kernel"]) full_k = os.path.join(self.boot_config["boot/path"], k.lstrip("/")) if not os.path.exists(full_k): self.msgs.append([ "warn", "Image for section {sect} not found - {full_k}".format( sect=sect, full_k=full_k) ]) else: self.bootitems.append(myname) boot_menu.lines.append(" linux16 " + k) else: # TODO: add entry to boot_menu object self.PrepareGRUBForDevice(myroot, boot_menu.lines) self.bootitems.append(myname) self.DeviceGRUB(myroot) if mytype in ["win7", "win8" ] or mytype == "win10" and self.uefiboot is False: boot_menu.lines.append( " chainloader " + mychainloader ) if mychainloader else boot_menu.lines.append( " chainloader +4") elif mytype in ["vista", "dos", "winxp", "haiku"]: boot_menu.lines.append( " chainloader " + mychainloader ) if mychainloader else boot_menu.lines.append( " chainloader +1") elif mytype in ["win10"]: boot_menu.lines.append( " chainloader " + mychainloader ) if mychainloader else boot_menu.lines.append( " chainloader /EFI/Microsoft/Boot/bootmgfw.efi") boot_menu.lines.append("}") boot_menu.addBootEntry(BootLoaderEntryType.OTHER, label=myname) return True
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 _GenerateLinuxSection(self, boot_menu: BootLoaderMenu, sect: str, sfunc: Callable[[BootLoaderMenu, str, str, str], bool]) -> bool: """Generates section for Linux systems""" ok = True # Process a section, such as "genkernel" section. findlist, skiplist = self.boot_config.flagItemList("{s}/kernel".format(s=sect)) # findlist == special patterns to match (i.e. kernel[-v]) # skiplist == patterns to skip. findmatch = [] skipmatch = [] scanpaths = self.boot_config.item(sect, "scan").split() for scanpath in scanpaths: scanpath = os.path.join(self.config.root_path, scanpath.lstrip("/")) if self.config.root_path == "/": self.mount_if_necessary(scanpath) if len(skiplist): # find kernels to skip... matches = self.GetMatchingKernels(scanpath, skiplist) skipmatch += matches if len(findlist): # find kernels to match (skipping any kernels we should skip...) matches = self.GetMatchingKernels(scanpath, findlist, skipmatch) findmatch += matches # Generate individual boot entry using extension-supplied function found_multi = False # logic for finding a kernel to boot, based on default setting: # sort by modification time: findmatch = sorted(findmatch, key=lambda x: x[2], reverse=True) if self._default_mode == "autopick: mtime": # pick newest kernel by mtime, which happens to be top-of-list boot_menu.default_position = 0 for kname, kext, mtime in findmatch: self._defnames.append(kname) ok = sfunc(boot_menu, sect, kname, kext) self._pos += 1 else: def_mtime = None for kname, kext, mtime in findmatch: if (self._default == sect) or (self._default == kname) or (self._default == os.path.basename(kname)): # default match if boot_menu.default_position is not None: found_multi = True if mtime > def_mtime: # this kernel is newer, use it instead boot_menu.default_position = self._pos def_mtime = mtime else: boot_menu.default_position = self._pos def_mtime = os.stat(kname)[8] self._defnames.append(kname) ok = sfunc(boot_menu, sect, kname, kext) if not ok: break self._pos += 1 if found_multi: self.msgs.append(["warn", "multiple matches found for default \"{name}\" - most recent used.".format(name=self._default)]) return ok