This note is to inform the community about work in progress.

As part of the the Live USB customization project work, I've filed several
patches to this ticket,
https://bugzilla.redhat.com/show_bug.cgi?id=448030, RFE: create a bootable
Live USB stick from the running livecd.

I'm currently working on a new version of the new, edit-livecd tool,
http://git.fedorahosted.org/git/?p=livecd;a=blob_plain;f=tools/edit-livecd;hb=HEAD,
I've renamed to edit-liveos.

The goal is to support cloning of an existing image along with resparsing
the operating system so that the overlay can be reset  and a livecd.iso file
could be generated, if desired.

The attached edit-liveos and .diff files will show the approach and current
state of development.

Due to bugs in the shutil.copytree() function, I've used rsync to copy the
filesystem.  This seems fast and allows greater customization, if desired,
by use of per directory .rsync-filter files.

This is my first python project, so right now I'm learning how to work with
the various filesystem image & mount objects and methods.

I've also attached the edit-liveos and .diff files to ticket # 448030.

Comments or suggestions are welcome.              --Fred
diff --git a/tools/edit-livecd b/tools/edit-livecd
old mode 100755
new mode 100644
index 809b015..be6baed
--- a/tools/edit-livecd
+++ b/tools/edit-livecd
@@ -1,6 +1,6 @@
 #!/usr/bin/python -tt
 #
-# edit livecd: Edit a livecd to insert files
+# edit-liveos: Edit a LiveOS to insert files
 #
 # Copyright 2009, Red Hat  Inc.
 # Written by Perry Myers <pmyers at redhat.com> & David Huff <dhuff at redhat.com>
@@ -21,6 +21,7 @@
 
 import os
 import sys
+import stat
 import tempfile
 import shutil
 import subprocess
@@ -41,11 +42,11 @@ class ExistingSparseLoopbackDisk(SparseLoopbackDisk):
         LoopbackDisk.create(self)
 
 class LiveImageEditor(LiveImageCreator):
-    """class for editing LiveCD images.
+    """class for editing LiveOS images.
 
     We need an instance of LiveImageCreator however we do not have a kickstart
-    file nor do we need to create a new image. We just want to reuse some of
-    LiveImageCreators methods on an existing livecd image.
+    file and we may not need to create a new image. We just want to reuse some of
+    LiveImageCreators methods on an existing LiveOS image.
 
     """
 
@@ -61,6 +62,15 @@ class LiveImageEditor(LiveImageCreator):
         self.tmpdir = "/var/tmp"
         """The directory in which all temporary files will be created."""
 
+        self.clone = False
+        """Signals when to copy an attached LiveOS image as base."""
+
+        self._include = None
+        """A string of file or directory paths to include in __copy_img_root."""
+
+        self._builder = "someone"
+        """The name of the Remix builder for _branding."""
+
         self.skip_compression = False
         """Controls whether to use squashfs to compress the image."""
 
@@ -97,8 +107,14 @@ class LiveImageEditor(LiveImageCreator):
     def __get_image(self):
         if self._LoopImageCreator__imagedir is None:
             self.__ensure_builddir()
-            self._LoopImageCreator__imagedir = tempfile.mkdtemp(dir = os.path.abspath(self.tmpdir), prefix = self.name + "-")
-        return self._LoopImageCreator__imagedir + "/ext3fs.img"
+            self._LoopImageCreator__imagedir = \
+                tempfile.mkdtemp(dir = os.path.abspath(self.tmpdir),
+                                                       prefix = self.name + "-")
+        if self.clone:
+            rtn = self._LoopImageCreator__imagedir
+        else:
+            rtn = self._LoopImageCreator__imagedir + "/ext3fs.img"
+        return rtn
     _image = property(__get_image)
     """The location of the image file"""
 
@@ -117,14 +133,14 @@ class LiveImageEditor(LiveImageCreator):
             raise CreatorError("Failed to determine fsimage TYPE: %s" % e )
 
 
-    def _get_fslable(self):
+    def _get_fslabel(self):
         dev_null = os.open("/dev/null", os.O_WRONLY)
         try:
             out = subprocess.Popen(["/sbin/e2label", self._image],
                                    stdout = subprocess.PIPE,
                                    stderr = dev_null).communicate()[0]
 
-            self._LoopImageCreator__fslable = out.strip()
+            self._LoopImageCreator__fslabel = out.strip()
 
         except IOError, e:
             raise CreatorError("Failed to determine fsimage TYPE: %s" % e )
@@ -136,7 +152,7 @@ class LiveImageEditor(LiveImageCreator):
 
         try:
             self._ImageCreator__builddir = tempfile.mkdtemp(dir =  os.path.abspath(self.tmpdir),
-                                               prefix = "edit-livecd-")
+                                               prefix = "edit-liveos-")
         except OSError, (err, msg):
             raise CreatorError("Failed create build directory in %s: %s" %
                                (self.tmpdir, msg))
@@ -162,21 +178,25 @@ class LiveImageEditor(LiveImageCreator):
         finally:
             os.unlink(path)
 
-
     def mount(self, base_on, cachedir = None):
         """mount existing file system.
 
         we have to override mount b/c we are not creating an new install root
-        nor do we need to setup the file system, ie makedirs(/etc/, /boot, ...),
+        nor do we need to setup the file system, i.e., makedirs(/etc/, /boot, ...),
         nor do we want to overwrite fstab, or create selinuxfs
 
         We also need to get some info about the image before we
         can mount it.
 
+        base_on --  the <LIVEIMG.src> a LiveOS.iso file or attached LiveOS device,
+                    such as, /dev/live for a currently running image.
+
+        cachedir -- a directory in which to store a Yum cache; Not used in edit-liveos.
+
         """
 
         if not base_on:
-            raise CreatorError("No base livecd image specified")
+            raise CreatorError("No base LiveOS image specified.")
 
         self.__ensure_builddir()
 
@@ -188,19 +208,33 @@ class LiveImageEditor(LiveImageCreator):
         makedirs(self._LoopImageCreator__imagedir)
         makedirs(self._ImageCreator_outdir)
 
-        LiveImageCreator._base_on(self, base_on)
+        if self.clone:
+            self._base_on(base_on)
+        else:
+            LiveImageCreator._base_on(self, base_on)
 
         self._LoopImageCreator__image_size = os.stat(self._image)[stat.ST_SIZE]
+
         self._get_fstype()
-        self._get_fslable()
-        self.fslabel = self._LoopImageCreator__fslable
-
-        self._LoopImageCreator__instloop = ExtDiskMount(ExistingSparseLoopbackDisk(self._image,
-                                                                                   self._LoopImageCreator__image_size),
-                                                        self._ImageCreator_instroot,
-                                                        self._fstype,
-                                                        self._LoopImageCreator__blocksize,
-                                                        self.fslabel)
+        self._get_fslabel()
+        self.fslabel = self._LoopImageCreator__fslabel
+
+        if self.clone:
+            self._LoopImageCreator__instloop = \
+                ExtDiskMount(self._image,
+                             self._LoopImageCreator__image_size,
+                             self._ImageCreator_instroot,
+                             self._fstype,
+                             self._LoopImageCreator__blocksize,
+                             self.fslabel)
+        else:
+            self._LoopImageCreator__instloop = \
+                ExtDiskMount(ExistingSparseLoopbackDisk(self._image,
+                                            self._LoopImageCreator__image_size),
+                             self._ImageCreator_instroot,
+                             self._fstype,
+                             self._LoopImageCreator__blocksize,
+                             self.fslabel)
         try:
             self._LoopImageCreator__instloop.mount()
         except MountError, e:
@@ -219,46 +253,174 @@ class LiveImageEditor(LiveImageCreator):
 
         os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
 
-        self.__copy_cd_root(base_on)
+        self.__copy_img_root(base_on)
+
+    def _base_on(self, base_on):
+        """clone the running LiveOS image as the basis for the new image."""
+
+#        imgmnt = DiskMount(RawDisk(base_on, 0), self._mkdtemp())
+#                              , self._LoopImageCreator__imagedir)
+
+#        try:
+#            imgmnt.mount()
+
+#        except MountError, e:
+#            raise CreatorError("Failed to mount '%s' : %s" %
+#                           (base_on, e))
+
+        subprocess.call(["rsync", "-ptgorlHASx", "--specials", "--progress", 
+                         "--exclude", "dev",
+                         "--exclude", "proc",
+                         "--exclude", "media",
+                         "--exclude", "mnt",
+                         "--exclude", "sys",
+                         "--exclude", ".liveimg*",
+                         "--exclude", "tmp",
+                         "/", self._LoopImageCreator__imagedir])
+
+    def _mount_instroot(self, base_on = None):
+        self.__imgdir = self._mkdtemp()
+
+        self.__instloop = ExtDiskMount(self._image,
+                                       self._instroot,
+                                       self.__fstype,
+                                       self.__blocksize,
+                                       self.fslabel)
+
+        try:
+            self.__instloop.mount()
+        except MountError, e:
+            raise CreatorError("Failed to mount '%s' : %s" %
+                               (self._image, e))
 
+    def __copy_img_root(self, base_on):
+        """helper function to copy root content of the base LiveIMG to ISOdir"""
 
-    def __copy_cd_root(self, base_on):
-        """helper function to root content of the base liveCD to ISOdir"""
+        ignore_list = ['squashfs.img', 'osmin.img', 'home.img', 'overlay-*']
+
+        if self.clone:
+            imgmnt = DiskMount(base_on, self._mkdtemp())
+            ignore_list.remove('home.img')
+        else:
+            imgmnt = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
+            print imgmnt.disk
+            print imgmnt.mountdir
+            print imgmnt.fstype
+            print imgmnt.rmmountdir
+            exit()
 
-        isoloop = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
         self._LiveImageCreatorBase__isodir = self._ImageCreator__builddir + "/iso"
 
         try:
-            isoloop.mount()
+            imgmnt.mount()
             # legacy LiveOS filesystem layout support, remove for F9 or F10
-            if os.path.exists(isoloop.mountdir + "/squashfs.img"):
-                squashimg = isoloop.mountdir + "/squashfs.img"
+            if os.path.exists(imgmnt.mountdir + "/squashfs.img"):
+                squashimg = imgmnt.mountdir + "/squashfs.img"
             else:
-                squashimg = isoloop.mountdir + "/LiveOS/squashfs.img"
-
-            #copy over everything but squashimg
-            shutil.copytree(isoloop.mountdir,
-                            self._LiveImageCreatorBase__isodir,
-                            ignore=shutil.ignore_patterns("squashfs.img", "osmin.img"))
+                squashimg = imgmnt.mountdir + "/LiveOS/squashfs.img"
+
+            # include specified files or directories
+            if self._include:
+                dst = self._LiveImageCreatorBase__isodir
+                print self._include
+                for fd in self._include:
+                    if os.path.isfile(fd):
+                        shutil.copy2(fd, os.path.join(dst, name))
+                    elif os.path.isdir(fd):
+                        shutil.copytree(fd,
+                                        self._LiveImageCreatorBase__isodir)
+            else:
+                #copy over everything but squashimg
+                shutil.copytree(imgmnt.mountdir,
+                                self._LiveImageCreatorBase__isodir,
+                                ignore=shutil.ignore_patterns(ignore_list)
         except MountError, e:
-            raise CreatorError("Failed to loopback mount '%s' : %s" %
+            raise CreatorError("Failed to mount '%s' : %s" %
                                (base_on, e))
 
         finally:
-            isoloop.cleanup()
+            imgmnt.cleanup()
+
+    def _brand (self, _LiveImageCreatorBase__isodir, _instroot):
+
+        # Get build name from boot configuration file.
+        try:
+            cfgf = open(_LiveImageCreatorBase__isodir
+                        + "/syslinux/syslinux.cfg", "r")
+                        
+        except IOError:
+            cfgf = open(_LiveImageCreatorBase__isodir
+                + "/syslinux/extlinux.conf", "r")
+        for line in cfgf:
+            i = line.find("Welcome to ")
+            if i > -1:
+                self.name = line[i+11:-2]
+                break
+        cfgf.close()
+
+        # Update fedora-release message with Remix details.
+        try:
+            text = open(_instroot + "/etc/fedora-release", "r").read()
+        except IOError:
+            raise CreatorError("Failed to open '%s' : %s" %
+                               (cfgf, e))
+
+        dt = datetime.strptime(datetime.now(), "%d-%b-%Y")
+        text = text + "  Remix of %(name)s by %(_builder)s, packaged on %(dt)s"
+        open(self._instroot + "/etc/fedora-release", "w").write(text)
+
+    def __get_basic_syslinux_config(self, **args):
+        dt = datetime.strptime(datetime.now(), "%d-%b-%Y")
+        return """
+default %(menu)s
+timeout %(timeout)d
+
+%(background)s
+menu title Welcome to %(name)s %(dt)s Remix!
+menu color border 0 #ffffffff #00000000
+menu color sel 7 #ffffffff #ff000000
+menu color title 0 #ffffffff #00000000
+menu color tabmsg 0 #ffffffff #00000000
+menu color unsel 0 #ffffffff #00000000
+menu color hotsel 0 #ff000000 #ffffffff
+menu color hotkey 7 #ffffffff #ff000000
+menu color timeout_msg 0 #ffffffff #00000000
+menu color timeout 0 #ffffffff #00000000
+menu color cmdline 0 #ffffffff #00000000
+menu hidden
+menu hiddenrow 5
+""" % args
 
 
 def parse_options(args):
-    parser = optparse.OptionParser(usage = "%prog [-s=<script.sh>] <LIVECD.iso>")
+    parser = optparse.OptionParser(usage = "%prog [-n=<name>] \
+                           \n\r                   [-o=<output>] \
+                           \n\r                   [-s=<script.sh>] \
+                           \n\r                   [-t=<tmpdir>] \
+                           \n\r                   [-i=<includes>] \
+                           \n\r                   [--builder] \
+                           \n\r                   [--clone] \
+                           \n\r                   [--skip-compression] \
+                           \n\r                   [--skip-minimize] \
+                           \n\r                   <LIVEIMG.src>")
 
     parser.add_option("-n", "--name", type="string", dest="name",
-                      help="name of new livecd (don't include .iso will be added)")
+                      help="name of new LiveOS (don't include .iso, it will be added)")
 
     parser.add_option("-o", "--output", type="string", dest="output",
                       help="specify the output dir")
 
     parser.add_option("-s", "--script", type="string", dest="script",
-                      help="specify script to run chrooted in the livecd fsimage")
+                      help="specify script to run chrooted in the LiveOS fsimage")
+
+    parser.add_option("", "--clone", action="store_true", dest="clone",
+                      help="Specify that source image is LiveOS block device.")
+
+    parser.add_option("-i", "--include", type="string", dest="include",
+                      help="Specify directory or file patterns to be included in copy_img_root.")
+
+    parser.add_option("", "--builder", type="string", dest="builder",
+                      help="Specify the builder of a Remix.")
 
     parser.add_option("-t", "--tmpdir", type="string",
                       dest="tmpdir", default="/var/tmp",
@@ -276,6 +438,9 @@ def parse_options(args):
         parser.print_usage()
         sys.exit(1)
 
+    if stat.S_ISBLK(os.stat(args[0]).st_mode):
+        options.clone = True
+
     return (args[0], options)
 
 def rebuild_iso_symlinks(isodir):
@@ -285,37 +450,42 @@ def rebuild_iso_symlinks(isodir):
     efi_initrd = "%s/EFI/boot/initrd0.img" % isodir
     isolinux_initrd = "%s/isolinux/initrd0.img" % isodir
 
-    os.remove(efi_vmlinuz)
-    os.remove(efi_initrd)
-    os.symlink(isolinux_vmlinuz,efi_vmlinuz)
-    os.symlink(isolinux_initrd,efi_initrd)
+    if os.path.exists(efi_vmlinuz):
+        os.remove(efi_vmlinuz)
+        os.remove(efi_initrd)
+        os.symlink(isolinux_vmlinuz,efi_vmlinuz)
+        os.symlink(isolinux_initrd,efi_initrd)
 
 
 def main():
-    (livecd, options) = parse_options(sys.argv[1:])
+    (LiveOS, options) = parse_options(sys.argv[1:])
 
     if os.geteuid () != 0:
-        print >> sys.stderr, "You must run edit-livecd as root"
+        print >> sys.stderr, "You must run edit-liveos as root"
         return 1
 
-    if options.name:
+    if options.name and options.name != os.path.basename(LiveOS):
         name = options.name
     else:
-        name = os.path.basename(livecd) + ".edited"
+        name = os.path.basename(LiveOS) + ".edited"
 
     if options.output:
         output = options.output
     else:
-        output = os.path.dirname(livecd)
-
+        output = os.path.dirname(LiveOS)
 
     editor = LiveImageEditor(name)
+    editor._include = options.include
+    editor.clone = options.clone
     editor.tmpdir = os.path.abspath(options.tmpdir)
+    editor._builder = options.builder
     editor.skip_compression = options.skip_compression
     editor.skip_minimize = options.skip_minimize
 
     try:
-        editor.mount(livecd, cachedir = None)
+        editor.mount(LiveOS, cachedir = None)
+        if editor.clone:
+            editor._brand(editor._LiveImageCreatorBase__isodir, self.__instloop)
         if options.script:
             print "Running edit script '%s'" % options.script
             editor._run_script(options.script)
@@ -327,7 +497,7 @@ def main():
         editor.unmount()
         editor.package(output)
     except CreatorError, e:
-        logging.error(u"Error editing Live CD : %s" % e)
+        logging.error(u"Error editing LiveOS : %s" % e)
         return 1
     finally:
         editor.cleanup()
@@ -338,7 +508,6 @@ def main():
 if __name__ == "__main__":
     sys.exit(main())
 
-
 arch = rpmUtils.arch.getBaseArch()
 if arch in ("i386", "x86_64"):
     LiveImageCreator = x86LiveImageCreator

Attachment: edit-liveos
Description: Binary data

_______________________________________________
SoaS mailing list
SoaS@lists.sugarlabs.org
http://lists.sugarlabs.org/listinfo/soas

Reply via email to