#!/usr/bin/env python # # medium.py # # (c) Copyright 2009 Michael Towers (larch42 at googlemail dot com) # # This file is part of the larch project. # # larch is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # larch is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with larch; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # #---------------------------------------------------------------------------- # 2010.03.14 import os class Medium: """This class manages writing a built larch system to a boot medium. """ def __init__(self): pass def _bootdir(self, btype, device, partsel, label): command.log("# Preparing boot directory, %s." % btype) # Clean out any old boot stuff supershell("rm -rf %s/{boot,grub,syslinux,isolinux}" % self.medium) # The idea is that a basic boot directory for each boot-loader is # provided in larch at cd-root/{grub0,isolinux0}. Individual files # can be added or substituted by supplying them in the profile at # cd-root/{grub,isolinux}. # It is also possible to completely replace the basic boot directory # by having cd-root/{grub0,isolinux0} in the profile - then the default # larch versions will not be used. if btype: if btype == "boot": # GRUB d0 = "grub" configfile = "grub/menu.lst" else: # syslinux/isolinux d0 = "isolinux" configfile = "isolinux.cfg" source0 = "%s/cd-root/%s0" % (self.profile, d0) if not os.path.isdir(source0): source0 = "%s/cd-root/%s0" % (base_dir, d0) supershell("cp -r %s %s/%s" % (source0, self.medium, d0)) # Copy any additional profile stuff psource = "%s/cd-root/%s" % (self.profile, d0) if os.path.isdir(psource): supershell("cp -rf %s %s" % (psource, self.medium)) # Compose the bootloader config file (insert 'bootlines' file) bootlines = self.profile + "/bootlines" insert = config.working_dir + "/bootlines_" if not os.path.isfile(bootlines): bootlines = base_dir + "/cd-root/bootlines" # Convert and complete the bootlines file # - add boot partition to options if partsel == "uuid": bootp = "uuid=" + supershell("blkid -c /dev/null -o value -s UUID %s" % device).result[0].strip() elif partsel == "label": if not label: config_error(_("Can't boot to label - no label supplied")) return False bootp = "label=" + label elif partsel == "partition": bootp = "root=" + device else: bootp = "" # - convert bootfiles to the correct format, inserting necessary info fhi = open(bootlines) fho = open(insert, "w") i = 0 block = "" title = "" options = "" for line in fhi: line = line.strip() if not line: if title: i += 1 # A block is ready if btype == "boot": # GRUB block += "title %s\n" % title block += "kernel /boot/larch.kernel %s %s\n" % (bootp, options) block += "initrd /boot/larch.img\n" else: # isolinux/syslinux block += "label %02d\n" % i block += "MENU LABEL %s\n" % title block += "kernel larch.kernel\n" block += "append initrd=larch.img %s %s\n" % (bootp, options) if i > 1: fho.write("\n") fho.write(block) block = "" title = "" options = "" elif line.startswith("comment:"): block += "#%s\n" % (line.split(":", 1)[1]) elif line.startswith("title:"): title = line.split(":", 1)[1].lstrip() elif line.startswith("options:"): options = line.split(":", 1)[1].lstrip() fho.close() fhi.close() # - insert the resulting file into the bootloader config file # This doesn't work, maybe one day I'll find out why ... #supershell("sed '/###LARCH/ { r %s\n d }' -i %s/%s/%s" % # (insert, self.medium, d0, configfile)) configtmp = config.working_dir + "/bootconfig_" configpath = "%s/%s/%s" % (self.medium, d0, configfile) fhi = open(configpath) fho = open(configtmp, "w") for line in fhi: if line.startswith("###LARCH"): fhr = open(insert) fho.write(fhr.read()) fhr.close() else: fho.write(line) fho.close() fhi.close() supershell("cp -f %s %s && rm {%s,%s}" % (configtmp, configpath, configtmp, insert)) if btype != "isolinux": # Rename the boot directory supershell("mv %s/%s %s/%s" % (self.medium, d0, self.medium, btype)) if btype == "syslinux": # Rename isolinux.cfg to syslinux.cfg supershell("mv %s/syslinux/isolinux.cfg %s/syslinux/syslinux.cfg" % (self.medium, self.medium)) elif btype == "boot": # Copy the grub boot files to the medium's grub directory supershell("cp %s/* %s/boot/grub" % (config.ipath("usr/lib/grub/i386-pc"), self.medium)) # Copy bootloader independent stuff source0 = "%s/cd-root/boot0" % self.profile if not os.path.isdir(source0): source0 = "%s/cd-root/boot0" % base_dir supershell("cp -r %s/* %s/%s &>/dev/null" % (source0, self.medium, btype)) # Copy any additional profile stuff psource = "%s/cd-root/boot" % self.profile if os.path.isdir(psource): supershell("cp -rf %s/* %s/%s" % (psource, self.medium, btype)) else: # No bootloader btype = "boot" supershell("mkdir %s/%s" % (self.medium, btype)) # Copy the stuff from the larchify stage (kernel, initcpio, etc.) supershell("cp -r %s/tmp/boot/* %s/%s" % (self.larch_dir, self.medium, btype)) return True def make(self, btype, device, label, partsel, format, larchboot): # btype is "boot" (grub), "syslinux", "isolinux" or "" (no bootloader) # For cd/dvd (iso), device is "" # partsel = "uuid", "label", "partition", or "" # For boot iso, # Location for the live medium image self.medium = config.ipath(config.medium_dir) self.larch_dir = config.ipath(config.larch_build_dir) self.profile = config.get("profile") grub = "lin" not in btype if format and device: if command.script("larch-format %s %s %s" % (device, "ext2" if grub else "vfat", label)): run_error(_("Couldn't format %s") % device) return False # The medium's initial "boot" dir, with kernel and initcpio, is # at self.larch_dir + "/tmp/boot" if not self._bootdir(btype, device, partsel, label): return False # Replace any existing larch/copy directory supershell("rm -rf %s/larch/copy" % self.medium) if os.path.isdir(self.profile + "/cd-root/larch/copy"): supershell("cp -r %s/cd-root/larch/copy %s/larch" % (self.profile, self.medium)) # Replace any existing larch/extra directory supershell("rm -rf %s/larch/extra" % self.medium) if os.path.isdir(self.profile + "/cd-root/larch/extra"): supershell("cp -r %s/cd-root/larch/extra %s/larch" % (self.profile, self.medium)) # To boot in 'search' mode the file larch/larchboot must be present # (though at present this is only relevant for partitions, CDs will # be booted even without this file). # To enable session saving the file larch/save must be present # (only relevant if not building an iso). supershell("rm -f %s/larch/{larchboot,save}" % self.medium) if larchboot: lbfile = (r"The presence of the file 'larch/larchboot' enables\n" r"booting the device in 'search' mode.\n") supershell("echo -e '%s' >%s/larch/larchboot" % (lbfile, self.medium)) if device: #TODO: Is this really the best way to handle the 'save' file? if not os.path.isfile(self.profile + "/nosave"): savefile = (r"The presence of the file 'larch/save'" r"enables session saving.\n") supershell("echo -e '%s' >%s/larch/save" % (savefile, self.medium)) self._build_partition(btype, device, grub, label, format) else: # iso if btype == "boot": return _mkiso("-b boot/grub/stage2_eltorito") if getisolinuxbin(self.medium): return _mkiso("-b isolinux/isolinux.bin -c isolinux/isolinux.boot") else: return False def _build_partition(self, btype, device, grub, label, format): if not format: ok, lines = supershell("blkid -c /dev/null -o value -s TYPE %s" % device) if ok and label: fstype = lines[0] if grub: if not fstype.startswith("ext"): config_error(_("GRUB is at present only supported on extN")) return False command.chroot("e2label %s %s" % (device, label), ["dev"]) else: if fstype != "vfat": config_error(_("syslinux is only supported on vfat")) return False command.chroot("mlabel -i %s ::%s" % (device, label), ["dev"]) #reiserfs: reiserfstune -l