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 <fgrose(a)gmail.com>
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 <bcl(a)redhat.com>
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 <fgrose(a)gmail.com>
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 <fgrose(a)gmail.com>
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