summaryrefslogtreecommitdiffstats
path: root/build_tools/l7/larch0/cli/media_common.py
diff options
context:
space:
mode:
Diffstat (limited to 'build_tools/l7/larch0/cli/media_common.py')
-rw-r--r--build_tools/l7/larch0/cli/media_common.py450
1 files changed, 450 insertions, 0 deletions
diff --git a/build_tools/l7/larch0/cli/media_common.py b/build_tools/l7/larch0/cli/media_common.py
new file mode 100644
index 0000000..b39e67d
--- /dev/null
+++ b/build_tools/l7/larch0/cli/media_common.py
@@ -0,0 +1,450 @@
+#!/usr/bin/env python
+#
+# media_common.py
+#
+# (c) Copyright 2009 - 2010 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.07.14
+
+"""This code is used by both iso generator and partition creator modules.
+The larch medium creation stage first uses the Medium class to prepare the
+bootloader directories. build_iso and build_partition can also be used to
+rebuild media starting from existing larch media, e.g. for conversions from
+CD to usb-stick and vice-versa. Even a switch of bootloader is possible as
+the bootloader configs for both GRUB and syslinux/isolinux are normally
+included on the medium.
+If not using a larch installation directory, the bootloader and/or mkisofs
+must be supported by the host (according to medium and bootloader).
+"""
+
+import os
+from config import *
+from backend import *
+
+class Medium:
+ """This class represents a boot medium image.
+ It checks the options for conflicts.
+ If necessary (no -S option provided on the command line) it
+ converts a larchified system to a boot medium image, by preparing
+ the bootloader directories and adding customization stuff
+ from the profile, but it does not write to any medium.
+ """
+ def __init__(self, options):
+ self.options = options
+ if options.setdir:
+ try:
+ wd = os.path.realpath(options.setdir)
+ os.chdir(wd)
+ except:
+ errout(_("Couldn't switch directory to '%s'") % wd)
+ self.iso = False
+ self.installation_dir = get_installation_dir()
+ self.installation0 = (self.installation_dir
+ if self.installation_dir != '/' else '')
+
+ # Check options
+ if options.chroot and options.nochroot:
+ errout(_("Option -C conflicts with -c"))
+
+ if options.source:
+ # Using a specified source image
+ if options.profile:
+ errout(_("Option -S conflicts with -p"))
+
+ # Use chroot?
+ self.chrootpath = self.installation0 if options.chroot else ''
+
+ else:
+ # Using the larch build system
+
+ # Use chroot?
+ self.chrootpath = '' if options.nochroot else self.installation0
+
+ # Mount point for source, if necessary
+ self.mps = self.chrootpath + SOURCEMOUNT
+ runcmd('rm -rf %s' % self.mps)
+ runcmd('mkdir -p %s' % self.mps)
+
+ # Create a temporary work area
+ self.build = self.chrootpath + BUILD0
+ runcmd('rm -rf %s' % self.build)
+ runcmd('mkdir -p %s' % self.build)
+
+ if options.source:
+ # Location of medium
+ self.medium_dir = self._get_source(options.source,
+ options.syslinux, self.chrootpath, test=options.testmedium)
+
+ else:
+ # Location for the live medium image
+ self.medium_dir = self.installation0 + CHROOT_DIR_MEDIUM
+ self._check_larchimage(self.medium_dir)
+ self.profile_dir = get_profile()
+
+ self._bootdir(options.syslinux)
+ self._customizelarchdir()
+
+ # The boot directory needs to be outside of the medium directory
+ # for some of the next steps
+ runcmd('cp -a %s/boot %s' % (self.medium_dir, self.build))
+
+
+ def unmount(self):
+ if self.medium_dir == self.mps:
+ unmount(self.mps)
+
+ if self.iso:
+ if self.chrootpath:
+ unmount(self.chrootpath + ISOBUILDMNT)
+ # Change owner of iso to match current directory
+ cwdstat = os.stat(os.getcwd())
+ os.lchown(self.iso, cwdstat.st_uid, cwdstat.st_gid)
+
+
+ def get_device_label(self, device):
+ l = runcmd('blkid -c /dev/null -o value -s LABEL %s'
+ % device)[1][0]
+ if self.options.detection not in detection_methods.split('|'):
+ errout(_("Invalid detection method (-d option)"))
+ return l
+
+
+ def chroot_medium_dir(self):
+ """Get the medium path relative within the (possible) chroot.
+ """
+ return self.medium_dir[len(self.chrootpath):]
+
+
+ def iso_path(self):
+ """If building an iso within a chroot the build directory must
+ be bind-mounted into the accessible area.
+ """
+ self.iso = self.options.isofile.replace('/', '_')
+ basedir = os.getcwd()
+ if self.chrootpath:
+ runcmd('mkdir -p %s' % (self.chrootpath + ISOBUILDMNT))
+ if not mount(basedir, self.chrootpath + ISOBUILDMNT, '--bind'):
+ errout(_("Couldn't bind-mount current directory"))
+ basedir = ISOBUILDMNT
+ return os.path.join(basedir, self.iso)
+
+
+ def _check_larchimage(self, mediumdir):
+ testfile = mediumdir + '/larch/system.sqf'
+ if not os.path.isfile(testfile):
+ errout(_("File '%s' doesn't exist, '%s' is not a larch medium")
+ % (testfile, mediumdir))
+
+
+ def _get_source(self, source, syslinux, ipath='', test=False):
+ source = os.path.realpath(source)
+ testcode = 64 if test else 0 # For various tests, below
+
+ # Is source an iso?
+ if os.path.isfile(source):
+ # Mount it
+ if mount(source, self.mps, '-o loop'):
+ source = self.mps
+ else:
+ errout(_("Couldn't mount '%s'. Not an iso?") % source)
+
+ # Is source a device?
+ elif source.startswith('/dev/'):
+ mp = _get_mountpoint(source)
+ if mp:
+ source = mp
+ elif mount(source, self.mps):
+ source = self.mps
+ else:
+ errout(_("Couldn't mount '%s'") % source)
+
+ # Simple check that it is really a larch image
+ self._check_larchimage(source)
+
+ # Check bootloader support
+ if not (os.path.isdir(source + '/boot/isolinux')
+ or os.path.isdir(source + '/boot/syslinux')):
+ if test or syslinux:
+ errout(_("Source doesn't support syslinux"), 0)
+ testcode += 8
+ if not os.path.isdir(source + '/boot/grub'):
+ if test or not syslinux:
+ errout(_("Source doesn't support GRUB"), 0)
+ testcode += 16
+
+ if testcode:
+ if os.path.isfile(source + 'larch/nosave'):
+ testcode += 2
+
+ unmount()
+ sys.exit(testcode)
+ return None
+
+ # Is source within the chroot directory?
+ if ipath and not source.startswith(ipath + '/'):
+ if mount(source, self.mps, '--bind'):
+ source = self.mps
+ else:
+ errout(_("Couldn't bind-mount '%s'") % source)
+
+ # Copy boot files
+ runcmd('cp -a %s/boot %s' % (source, self.build))
+
+ # Remove old config files
+ runcmd('rm -f %s/boot/syslinux/syslinux.cfg' % self.build)
+ runcmd('rm -f %s/boot/isolinux/isolinux.cfg' % self.build)
+ runcmd('rm -f %s/boot/grub/menu.lst' % self.build)
+ runcmd('rm -f %s/boot/isolinux/isolinux.bin' % self.build)
+ runcmd('rm -f %s/boot/isolinux/isolinux.boot' % self.build)
+
+ # Rename the syslinux boot directory if necessary
+ if os.path.isdir(self.build + '/boot/syslinux'):
+ runcmd('mv %s/boot/syslinux %s/boot/isolinux'
+ % (self.build, self.build))
+
+ return source
+
+
+ def _bootdir(self, syslinux):
+ """Prepare boot directories for the various bootloaders. The
+ bootloader configuration files are not generated yet, as these
+ depend on the medium.
+ """
+ comment("Fetch kernel and initramfs")
+ if not runcmd('cp -r %s/boot %s' % (self.medium_dir, self.build))[0]:
+ errout(_("No kernel and/or initramfs"))
+
+ comment("Preparing bootloader directories")
+ # Only include the directories if the corresponding bootloader
+ # package ('grub' and/or 'syslinux') is installed
+ grub = os.path.isfile(self.installation0 + GRUBDIR
+ + '/stage2_eltorito')
+ isolinux = os.path.isfile(self.installation0 + SYSLINUXDIR
+ + '/isolinux.bin')
+ # Check bootloader support
+ if syslinux:
+ if not isolinux:
+ errout(_("Installation doesn't support syslinux"))
+ elif not grub:
+ errout(_("Installation doesn't support GRUB"))
+
+ # Bootloader independent files are provided in larch at
+ # cd-root/boot0. The contents of this directory are placed in the
+ # medium's 'boot' directory.
+ # Individual files can be added or substituted by
+ # supplying them in the profile at cd-root/boot.
+ # It is also possible to completely replace the basic boot directory
+ # by having cd-root/boot0 in the profile - then the default
+ # larch version will not be used.
+ source0 = '%s/cd-root/boot0' % self.profile_dir
+ if not os.path.isdir(source0):
+ source0 = '%s/cd-root/boot0' % base_dir
+ runcmd('bash -c "cp -r %s/* %s/boot"' % (source0, self.build))
+ # Copy any additional profile stuff
+ psource = '%s/cd-root/boot' % self.profile_dir
+ if os.path.isdir(psource):
+ runcmd('cp -rf %s/* %s' % (psource, self.build))
+ # Get the boot options file
+ bootlines = self.profile_dir + '/bootlines'
+ if not os.path.isfile(bootlines):
+ bootlines = base_dir + '/data/bootlines'
+ runcmd('cp -f %s %s/boot' % (bootlines, self.build))
+
+ # The idea is that a basic boot directory for each boot-loader is
+ # provided in larch at cd-root/{grub0,isolinux0}. These are copied
+ # to the medium as 'boot/grub' and 'boot/isolinux'. 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.
+ for ok, boot_dir in ((grub, 'grub'), (isolinux, 'isolinux')):
+ if ok:
+ # Copy bootloader specific stuff
+ source0 = '%s/cd-root/%s0' % (self.profile_dir, boot_dir)
+ if not os.path.isdir(source0):
+ source0 = '%s/cd-root/%s0' % (base_dir, boot_dir)
+ runcmd('cp -rf %s %s/boot/%s'
+ % (source0, self.build, boot_dir))
+
+ # Copy any additional profile stuff
+ psource = '%s/cd-root/%s' % (self.profile_dir, boot_dir)
+ if os.path.isdir(psource):
+ runcmd('cp -rf %s %s/boot'
+ % (psource, self.build))
+
+ # Copy the grub boot files to the medium's grub directory
+ # and rename base config file
+ if grub:
+ runcmd('bash -c "cp %s/* %s/boot/grub"' %
+ (self.installation0 + GRUBDIR, self.build))
+ runcmd('mv %s/boot/grub/menu.lst %s/boot/grub/menu.lst_0'
+ % (self.build, self.build))
+
+ # Copy mbr.bin, vesamenu.c32 and isolinux.bin (renamed)
+ # to the boot directory and rename base config file
+ if isolinux:
+ runcmd('cp %s/mbr.bin %s/boot/isolinux' %
+ (self.installation0 + SYSLINUXDIR, self.build))
+ runcmd('cp %s/vesamenu.c32 %s/boot/isolinux' %
+ (self.installation0 + SYSLINUXDIR, self.build))
+ runcmd('cp %s/isolinux.bin %s/boot/isolinux/isolinux.bin0' %
+ (self.installation0 + SYSLINUXDIR, self.build))
+ runcmd(('mv %s/boot/isolinux/isolinux.cfg'
+ ' %s/boot/isolinux/isolinux.cfg_0')
+ % (self.build, self.build))
+
+
+ def _customizelarchdir(self):
+ # Replace any existing larch/copy directory
+ runcmd('rm -rf %s/larch/copy' % self.medium_dir)
+ if os.path.isdir(self.profile_dir + '/cd-root/larch/copy'):
+ runcmd('cp -r %s/cd-root/larch/copy %s/larch' %
+ (self.profile_dir, self.medium_dir))
+
+ # Replace any existing larch/extra directory
+ runcmd('rm -rf %s/larch/extra' % self.medium_dir)
+ if os.path.isdir(self.profile_dir + '/cd-root/larch/extra'):
+ runcmd('cp -r %s/cd-root/larch/extra %s/larch' %
+ (self.profile_dir, self.medium_dir))
+
+ # Handle existence of larch/nosave
+ runcmd('rm -f %s/larch/nosave' % self.medium_dir)
+ if os.path.isfile(self.profile_dir + '/nosave'):
+ runcmd('cp %s/nosave %s/larch' %
+ (self.profile_dir, self.medium_dir))
+
+
+
+def _get_mountpoint(path):
+ ok, lines = runcmd('cat /etc/mtab')
+ for l in lines:
+ ll = l.split()
+ if (len(ll) > 4) and (ll[1] == path):
+ return ll[0]
+ return ''
+
+
+def check_label(l, n):
+ if isinstance(l, unicode):
+ l = l.encode('utf8')
+ if len(l) > n:
+ if query_yn(_("The volume label is too long. Use the default (%s)?")
+ % LABEL):
+ return LABEL
+ else:
+ errout(_("Cancelled"))
+ return l
+
+
+def add_larchboot(idir):
+ writefile("The presence of the file 'larch/larchboot' enables\n"
+ "booting the device in 'search' mode.\n", idir + '/larch/larchboot')
+
+
+def bootconfig(medium, syslinux, label='', device='', detection=''):
+ """Convert and complete the bootlines file.
+ """
+ # - add boot partition to options
+ if detection == 'uuid':
+ bootp = ('uuid=' +
+ runcmd('blkid -c /dev/null -o value -s UUID %s'
+ % device)[1][0].strip())
+ elif detection == 'label':
+ if not label:
+ errout(_("Can't boot to label - device has no label"))
+ bootp = 'label=' + label
+ elif detection == 'partition':
+ bootp = 'root=' + device
+ else:
+ bootp = ''
+
+ # - convert bootfiles to the correct format,
+ # inserting necessary info
+ bootconf = medium + '/boot/bootlines'
+ if not os.path.isfile(bootconf):
+ errout(_("Boot configuration file '%s' not found") % bootconf)
+ fhi = open(bootconf)
+ insert = ''
+ i = 0
+ block = ''
+ title = ''
+ opts = ''
+ for line in fhi:
+ line = line.strip()
+ if not line:
+ if title:
+ i += 1
+ # A block is ready
+
+ if syslinux:
+ # isolinux/syslinux
+ block += 'label %02d\n' % i
+ block += 'MENU LABEL %s\n' % title
+ block += 'kernel /boot/larch.kernel\n'
+ block += ('append initrd=/boot/larch.img %s %s\n'
+ % (bootp, opts))
+
+ else:
+ # GRUB
+ block += 'title %s\n' % title
+ block += ('kernel /boot/larch.kernel %s %s\n'
+ % (bootp, opts))
+ block += 'initrd /boot/larch.img\n'
+
+ if i > 1:
+ insert += '\n'
+ insert += block
+ block = ''
+ title = ''
+ opts = ''
+
+ 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:'):
+ opts = line.split(':', 1)[1].lstrip()
+ fhi.close()
+
+ # - insert the resulting string into the bootloader config file
+ if syslinux:
+ boot_dir = ('isolinux' if os.path.isdir(medium + '/boot/isolinux')
+ else 'syslinux')
+ configfile0 = 'isolinux.cfg_0'
+ configfile = boot_dir + '.cfg'
+ else:
+ boot_dir = 'grub'
+ configfile0 = 'menu.lst_0'
+ configfile = 'menu.lst'
+ configpath = '%s/boot/%s/%s' % (medium, boot_dir, configfile0)
+ if not os.path.isfile(configpath):
+ errout(_("Base configuration file (%s) not found") % configpath)
+ fhi = open(configpath)
+ fho = open('%s/boot/%s/%s' % (medium, boot_dir, configfile), 'w')
+ for line in fhi:
+ if line.startswith("###LARCH"):
+ fho.write(insert)
+ else:
+ fho.write(line)
+ fhi.close()
+ fho.close()