tools/livecd-iso-to-disk.sh | 14 +- tools/liveimage-mount | 256 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 233 insertions(+), 37 deletions(-)
New commits: commit 731ba56081dcc580b314d3214d0758a932a96118 Author: Frederick Grose <[email protected]> Date: Mon Mar 21 14:38:18 2011 -0700 liveimage-mount installed LiveOS with overlay Enable standalone live-mounting of an installed LiveOS image with access to its overlay. Detect the presence of an overlay on a device with an installed LiveOS, and use loop mounts and the device-mapper service to create a 'live-mounted' block device for the attached LiveOS-bearing partition. Provide an option to --persist the mounting for chaining to another script. The script continues to be a standalone tool with limited error checking. The script requires 3 to 4 free loop devices, so is not yet suitable for use within a booted LiveOS image. diff --git a/tools/liveimage-mount b/tools/liveimage-mount index 76602a7..80d67d1 100755 --- a/tools/liveimage-mount +++ b/tools/liveimage-mount @@ -1,9 +1,11 @@ #!/usr/bin/python -tt # -# livecd-mount: Mount a live CD at the specified point, and log +# liveimage-mount: Mount a LiveOS at the specified point, and log # into a shell. # -# Copyright 2010, Red Hat Inc. +# Copyright 2011, Red Hat Inc. +# Code for Live mounting an attached LiveOS device added by Frederick Grose, +# <fgrose at sugarlabs.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,22 +22,101 @@ import os import sys +import stat import getopt import tempfile import subprocess + def usage(ecode): - print "Usage: %s ISO.iso MOUNTPOINT [COMMAND] [ARGS]" + print """Usage: + liveimage-mount [opts] ISO.iso|LiveOSdevice MOUNTPOINT [COMMAND] [ARGS] + + where [opts] = [-h|--help + [--chroot + [--mount-hacks + [--persist]]]]] + + and [ARGS] = [arg1[ arg2[ ...]]]\n""" sys.exit(ecode) + +def call(*popenargs, **kwargs): + ''' + Calls subprocess.Popen() with the provided arguments. All stdout and + stderr output is sent to print. The return value is the exit + code of the command. + ''' + p = subprocess.Popen(*popenargs, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, **kwargs) + rc = p.wait() + + # Log output using logging module + while True: + # FIXME choose a more appropriate buffer size + buf = p.stdout.read(4096) + if not buf: + break + print buf + + return rc + + +def rcall(args, env=None): + if env: + environ = os.environ.copy() + environ.update(env) + else: + environ = None + try: + p = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, env=environ) + out, err = p.communicate() + except OSError, e: + raise CreatorError(u"Failed to execute:\n'%s'\n'%s'" % (args, e)) + except: + raise CreatorError(u"""Failed to execute:\n'%s' + \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" % + (args, environ, out, err, p.returncode)) + else: + if p.returncode != 0: + raise CreatorError(u"""Error in call:\n'%s'\nenviron: '%s' + \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" % + (args, environ, out, err, p.returncode)) + return out + + +def get_device_mountpoint(path): + """Return the device and mountpoint for a file or device path.""" + + info = list() + info[0:5] = [None] * 6 + if os.path.exists(path): + st_mode = os.stat(path).st_mode + if stat.S_ISBLK(st_mode) or os.path.ismount(path): + devinfo = rcall(['/bin/df', path]).splitlines() + info = devinfo[1].split(None) + if len(info) == 1: + info.extend(devinfo[2].split(None)) + return [info[0], info[5]] + + def main(): + if os.geteuid() != 0: + print >> sys.stderr, """\n Exiting... + \r You must run liveimage-mount with root priviledges.\n""" + return 1 + try: - opts,args = getopt.getopt(sys.argv[1:], 'h', ['help', 'chroot', 'mount-hacks']) + opts,args = getopt.getopt(sys.argv[1:], 'h', ['help', + 'chroot', 'persist', + 'mount-hacks']) except getopt.GetoptError, e: usage(1) - rw = False + img_type = None chroot = False + persist = False mount_hacks = False for o,a in opts: if o in ('-h', '--help'): @@ -44,34 +125,121 @@ def main(): chroot = True elif o in ('--mount-hacks', ): mount_hacks = True + elif o in ('--persist', ): + """Option used to run a command in a spawned process.""" + persist = True if len(args) < 2: usage(1) - isopath = args[0] + liveos = args[0] destmnt = args[1] + if not os.path.exists(liveos): + print """\n Exiting... + %s is not a file, directory, or device.\n""" % liveos + ecode = 1 + return + + if stat.S_ISBLK(os.stat(liveos).st_mode): + img_type = 'blk' + imgloop = None + overlayloop = None + else: + img_type = 'iso' + + if img_type is 'blk': + liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-') + if img_type is 'iso': + liveosmnt = tempfile.mkdtemp(prefix='livemnt-iso-') + + liveosdir = os.path.join(liveosmnt, 'LiveOS') + homemnt = None + dm_cow = None + mntlive = None + command = args[2:] verbose = not command - isomnt = tempfile.mkdtemp(prefix='livemnt-iso') - squashmnt = tempfile.mkdtemp(prefix='livemnt-squash') - - mountflags = ['loop', 'ro'] - mountflags_str = ','.join(mountflags) + squashmnt = tempfile.mkdtemp(prefix='livemnt-squash-') + squashloop = None + imgloop = None + losetup_args = ['/sbin/losetup', '-f', '--show'] try: - subprocess.check_call(['mount', '-o', mountflags_str, isopath, isomnt], stderr=sys.stderr) - squash_img_path = os.path.join(isomnt, 'LiveOS', 'squashfs.img') - subprocess.check_call(['mount', '-o', mountflags_str, squash_img_path, squashmnt], stderr=sys.stderr) - ext3_img_path = os.path.join(squashmnt, 'LiveOS', 'ext3fs.img') - subprocess.check_call(['mount', '-o', mountflags_str, ext3_img_path, destmnt], stderr=sys.stderr) + if img_type is 'blk': + call(['/bin/mount', liveos, liveosmnt]) + elif img_type is 'iso': + liveosloop = rcall(losetup_args + [liveos]).rstrip() + call(['/bin/mount', '-o', 'ro', liveosloop, liveosmnt]) + + squash_img = os.path.join(liveosdir, 'squashfs.img') + if not os.path.exists(squash_img): + ext3_img = os.path.join(liveosdir, 'ext3fs.img') + if not os.path.exists(ext3_img): + print """ + \r\tNo LiveOS was found on %s\n\t Exiting...\n""" % liveos + ecode = 1 + return + else: + squashloop = rcall(losetup_args + [squash_img]).rstrip() + call(['/bin/mount', '-o', 'ro', squashloop, squashmnt]) + ext3_img = os.path.join(squashmnt, 'LiveOS', 'ext3fs.img') + + if img_type is 'blk': + imgloop = rcall(losetup_args + [ext3_img]).rstrip() + imgsize = rcall(['/sbin/blockdev', '--getsz', imgloop]).rstrip() + files = os.listdir(liveosdir) + overlay = None + for f in files: + if f.find('overlay-') == 0: + overlay = f + break + overlayloop = rcall(['/sbin/losetup', '-f']).rstrip() + if overlay: + call(['/sbin/losetup', overlayloop, os.path.join(liveosdir, + overlay)]) + dm_cow = 'live-rw' + else: + overlay = tempfile.NamedTemporaryFile(dir='/dev/shm') + print "\npreparing temporary overlay..." + call(['/bin/dd', 'if=/dev/null', 'of=%s' % overlay.name, + 'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)]) + call(['/sbin/losetup', overlayloop, overlay.name]) + dm_cow = 'live-ro' + call(['/sbin/dmsetup', '--noudevrules', '--noudevsync', + 'create', dm_cow, '--table=0 %s snapshot %s %s p 8' % + (imgsize, imgloop, overlayloop)]) + call(['/bin/mount', os.path.join('/dev/mapper', dm_cow), destmnt]) + home_path = os.path.join(liveosdir, 'home.img') + if os.path.exists(home_path): + homemnt = os.path.join(destmnt, 'home') + homeloop = rcall(losetup_args + [home_path]).rstrip() + call(['/bin/mount', homeloop, homemnt]) + mntlive = os.path.join(destmnt, 'mnt', 'live') + if not os.path.exists(mntlive): + os.makedirs(mntlive) + call(['/bin/mount', '--bind', liveosmnt, mntlive]) + elif img_type is 'iso': + imgloop = rcall(losetup_args + [ext3_img]).rstrip() + call(['/bin/mount', '-o', 'ro', imgloop, destmnt]) if mount_hacks: subprocess.check_call(['mount', '-t', 'proc', 'proc', os.path.join(destmnt, 'proc')], stderr=sys.stderr) subprocess.check_call(['mount', '-t', 'tmpfs', 'tmpfs', os.path.join(destmnt, 'var', 'run')], stderr=sys.stderr) - if len(command) > 0: + if len(command) > 0 and persist: + args = [] + args.extend(command) + live = '' + if img_type is 'blk': + live = 'live-' + print """Starting process with this command line: + \r%s\n %s is %smounted.""" % (' '.join(command), liveos, live) + p = subprocess.Popen(args, close_fds=True) + print "Process id: %s" % p.pid + ecode = p.returncode + elif len(command) > 0: args = ['chroot', destmnt] args.extend(command) ecode = subprocess.call(args, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) @@ -79,21 +247,49 @@ def main(): print "Starting subshell in chroot, press Ctrl-D to exit..." ecode = subprocess.call(['chroot', destmnt], stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) else: - print "Starting subshell, press Ctrl-D to exit..." + if dm_cow == 'live-ro': + status = ' with NO LiveOS persistence,' + else: + status = '' + print "Entering subshell,%s press Ctrl-D to exit..." % status ecode = subprocess.call([os.environ['SHELL']], cwd=destmnt, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) finally: - if verbose: - print "Cleaning up..." - if mount_hacks: - subprocess.call(['umount', os.path.join(destmnt, 'var', 'run')]) - subprocess.call(['umount', os.path.join(destmnt, 'proc')]) - subprocess.call(['umount', destmnt]) - subprocess.call(['umount', squashmnt]) - os.rmdir(squashmnt) - subprocess.call(['umount', isomnt]) - os.rmdir(isomnt) - if verbose: - print "Cleanup complete" + call(['/bin/sync']) + if not persist: + if verbose: + print """Cleaning up... + Please wait if large files were written.""" + if mount_hacks: + subprocess.call(['umount', os.path.join(destmnt, 'var', 'run')]) + subprocess.call(['umount', os.path.join(destmnt, 'proc')]) + if homemnt: + call(['/bin/umount', homemnt]) + call(['/sbin/losetup', '-d', homeloop]) + if img_type is 'blk': + if mntlive: + call(['/bin/umount', mntlive]) + os.rmdir(mntlive) + if os.path.ismount(destmnt): + call(['/bin/umount', destmnt]) + if img_type is 'blk': + m_cow: + call(['/sbin/dmsetup', '--noudevrules', '--noudevsync', + 'remove', dm_cow]) + if overlayloop: + call(['/sbin/losetup', '-d', overlayloop]) + if imgloop: + call(['/sbin/losetup', '-d', imgloop]) + if squashloop: + call(['/bin/umount', squashloop]) + call(['/sbin/losetup', '-d', squashloop]) + call(['/bin/umount', liveosmnt]) + if not img_type is 'blk': + call(['/sbin/losetup', '-d', liveosloop]) + os.rmdir(squashmnt) + if not os.path.ismount(liveosmnt): + os.rmdir(liveosmnt) + if verbose: + print "Cleanup complete" sys.exit(ecode) commit aea37a23dafe3d540a3fd2e463d69e36fa9ba650 Author: Brian C. Lane <[email protected]> Date: Mon Mar 21 14:24:48 2011 -0700 Fix overzealous boot->BOOT change diff --git a/tools/livecd-iso-to-disk.sh b/tools/livecd-iso-to-disk.sh index 3e160e3..8ea2081 100755 --- a/tools/livecd-iso-to-disk.sh +++ b/tools/livecd-iso-to-disk.sh @@ -890,7 +890,7 @@ fi if [[ live == $srctype ]]; then targets="$TGTMNT/$SYSLINUXPATH" [[ -n $efi ]] && targets+=" $TGTMNT$EFI_BOOT" - [[ -n $xo ]] && targets+=" $TGTMNT/BOOT/olpc.fth" + [[ -n $xo ]] && targets+=" $TGTMNT/boot/olpc.fth" duTable=($(du -c -B 1M $targets 2> /dev/null || :)) tbd=$((tbd + ${duTable[*]: -2:1})) fi @@ -922,7 +922,7 @@ if [[ live == $srctype ]]; then [[ -z $skipcompress ]] && sources+=" $SRCMNT/LiveOS/squashfs.img" sources+=" $SRCMNT/isolinux $SRCMNT/syslinux" [[ -n $efi ]] && sources+=" $SRCMNT$EFI_BOOT" - [[ -n $xo ]] && sources+=" $SRCMNT/BOOT/olpc.fth" + [[ -n $xo ]] && sources+=" $SRCMNT/boot/olpc.fth" duTable=($(du -c -B 1M "$thisScriptpath" $sources 2> /dev/null || :)) livesize=$((livesize + ${duTable[*]: -2:1})) fi commit a00fc723389cc9c531d61e5f2c70025da8d67ab4 Author: Frederick Grose <[email protected]> Date: Mon Mar 21 14:22:52 2011 -0700 Fix return code failure (#689360) A runtime zero-valued arithmetic expression returns "FAILURE", so with the new set -e autoexit setting, the code logic or command must be adjusted to accommodate. diff --git a/tools/livecd-iso-to-disk.sh b/tools/livecd-iso-to-disk.sh index 70e80ba..3e160e3 100755 --- a/tools/livecd-iso-to-disk.sh +++ b/tools/livecd-iso-to-disk.sh @@ -881,7 +881,7 @@ if [[ -d $TGTMNT/$LIVEOS ]]; then tbd=($(du -B 1M $TGTMNT/$LIVEOS)) if [[ -s $TGTMNT/$LIVEOS/$HOMEFILE ]] && [[ -n $keephome ]]; then homesize=($(du -B 1M $TGTMNT/$LIVEOS/$HOMEFILE)) - ((tbd -= homesize)) + tbd=$((tbd - homesize)) fi else tbd=0 @@ -892,7 +892,7 @@ if [[ live == $srctype ]]; then [[ -n $efi ]] && targets+=" $TGTMNT$EFI_BOOT" [[ -n $xo ]] && targets+=" $TGTMNT/BOOT/olpc.fth" duTable=($(du -c -B 1M $targets 2> /dev/null || :)) - ((tbd += ${duTable[*]: -2:1})) + tbd=$((tbd + ${duTable[*]: -2:1})) fi if [[ -n $skipcompress ]] && [[ -s $SRCMNT/LiveOS/squashfs.img ]]; then @@ -924,7 +924,7 @@ if [[ live == $srctype ]]; then [[ -n $efi ]] && sources+=" $SRCMNT$EFI_BOOT" [[ -n $xo ]] && sources+=" $SRCMNT/BOOT/olpc.fth" duTable=($(du -c -B 1M "$thisScriptpath" $sources 2> /dev/null || :)) - ((livesize += ${duTable[*]: -2:1})) + livesize=$((livesize + ${duTable[*]: -2:1})) fi freespace=($(df -B 1M --total $TGTDEV)) commit ab97a013c88441a7d6f1e0ac85342f9025e9f6ae Author: Frederick Grose <[email protected]> Date: Mon Mar 21 14:09:49 2011 -0700 Fix pipefailure in checkSyslinuxVersion (#689329) Use a list operation to bypass the erroneous return value. Alter the test to something that could be used to check the syslinux version, if desired. diff --git a/tools/livecd-iso-to-disk.sh b/tools/livecd-iso-to-disk.sh index deb5473..70e80ba 100755 --- a/tools/livecd-iso-to-disk.sh +++ b/tools/livecd-iso-to-disk.sh @@ -559,7 +559,8 @@ checkSyslinuxVersion() { echo "You need to have syslinux installed to run this script" exit 1 fi - if ! syslinux 2>&1 | grep -qe -d; then + check=($(syslinux --version 2>&1)) || : + if [[ 'syslinux' == $check ]]; then SYSLINUXPATH="" elif [ -n "$multi" ]; then SYSLINUXPATH="$LIVEOS/syslinux" @@ -1006,7 +1007,6 @@ fi [ -n "$efi" -a ! -d $TGTMNT$EFI_BOOT ] && mkdir -p $TGTMNT$EFI_BOOT # Live image copy -set -o pipefail if [ "$srctype" = "live" -a -z "$skipcopy" ]; then echo "Copying live image to target device." [ ! -d $TGTMNT/$LIVEOS ] && mkdir $TGTMNT/$LIVEOS -- livecd mailing list [email protected] https://admin.fedoraproject.org/mailman/listinfo/livecd
