RHEL6 livecd-creator bug fix

Problem

Running livecd-creator (livecd-tools-13.4.5-1.el6.x86_64 + python-imgcreate-13.4.5-1.el6.x86_64.rpm) leaves loopback mounts around after execution.

Environment

RedHat Enterprise Linux 6.5 running under Virtualbox 4.3

# lsof | grep loop
# 
# time livecd-creator -d -v --logfile=/tmp/livecd.debug.out -c /a/infoboot.ks -f "INFO"
# ...
#Inserting fragment md5sums into iso image...
#fragmd5 = a1223c226ed416222a53519717949fd69cfef83f3bf48e8fe5f3d46d5686
#frags = 20
#Setting supported flag to 0
# real    5m54.977s
# user    5m14.839s
# sys    1m32.820s

# lsof | grep loop
  loop0     10927      root  cwd       DIR                8,3     4096          2 /
  loop0     10927      root  rtd       DIR                8,3     4096          2 /
  loop0     10927      root  txt   unknown                                        /proc/10927/exe

loopback turds are left around as the unmounts fail and lazy unmounts are tried, which never resolve themselves.

This leaves a situation where only so many livecd-creators can be run before failing with ‘can’t get loop device’

I coded a workaround as follows.

/usr/lib/python2.6/site-packages/imgcreate/fs.py

The entire modified fs.py file from livecd-tools-13.4.5-1.el6.x86_64.rpm lives here.

Diff of orig version and patched version

$ diff -c fs.py fs.py.ORIG
*** fs.py    2014-03-07 09:41:35.000000000 -0700
--- fs.py.ORIG    2014-03-05 15:22:30.000000000 -0700
***************
*** 27,33 ****
  import logging
  import tempfile
  import time
- import re

  from imgcreate.errors import *

--- 27,32 ----
***************
*** 112,147 ****
      def unmount(self):
          if not self.mounted:
              return
-         self.mounted = False
-
-         rc = subprocess.call(["/bin/umount", self.dest])
-         if rc == 0:
-             logging.debug("umount %s succeeded", self.dest )
-             return
-
-         logging.info("Second attempt Unmounting directory %s" % self.dest)
-         logging.info("Calling fuser -ck on %s" % self.dest)
-
-         if re.match( ".*yum.*", self.dest) == None: # We don't want to nuke everything associated with yum
-             rc = subprocess.call(["/sbin/fuser", '-ck', self.dest])
-             logging.info("fuser -ck returned  %s" % rc )
-
-         time.sleep(2)

          rc = subprocess.call(["/bin/umount", self.dest])
-         if rc == 0:
-             logging.info("Second umount succeeded")
-             return
-
-         logging.debug("Unable to unmount %s normally, using lazy unmount" % self.dest)
-         rc = subprocess.call(["/bin/umount", "-l", self.dest])
          if rc != 0:
!             raise MountError("Unable to unmount fs at %s" % self.dest)
!         else:
!             logging.debug("lazy umount succeeded on %s" % self.dest)
!             print >> sys.stdout, "lazy umount succeeded on %s" % self.dest

!

  class LoopbackMount:
      """LoopbackMount  compatibility layer for old API"""
--- 111,128 ----
      def unmount(self):
          if not self.mounted:
              return

          rc = subprocess.call(["/bin/umount", self.dest])
          if rc != 0:
!             logging.debug("Unable to unmount %s normally, using lazy unmount" % self.dest)
!             rc = subprocess.call(["/bin/umount", "-l", self.dest])
!             if rc != 0:
!                 raise MountError("Unable to unmount fs at %s" % self.dest)
!             else:
!                 logging.debug("lazy umount succeeded on %s" % self.dest)
!                 print >> sys.stdout, "lazy umount succeeded on %s" % self.dest

!         self.mounted = False

  class LoopbackMount:
      """LoopbackMount  compatibility layer for old API"""
***************
*** 156,184 ****
          self.diskmount.unmount()

      def lounsetup(self):
!         if not self.losetup:
!             return
!
!         rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
!         if rc == 0:
              self.losetup = False
              self.loopdev = None
-             logging.info( "First call to losetup remove Succeeded " )
-             return
-
-         logging.info("Calling fuser -ck on %s" % self.loopdev)
-         rc = subprocess.call(["/sbin/fuser", '-ck', self.loopdev])
-         logging.info("fuser -ck returned  %s" % rc )
-
-         self.loopdev = None
-         self.losetup = False
-
-         rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
-         if rc != 0:
-             logging.info("Second call to losetup -d Failed for %s", self.loopdev)
-             return
-         logging.info("Second call to losetup -d Succeeded for %s ", self.loopdev)
-

      def loopsetup(self):
          if self.losetup:
--- 137,146 ----
          self.diskmount.unmount()

      def lounsetup(self):
!         if self.losetup:
!             rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
              self.losetup = False
              self.loopdev = None

      def loopsetup(self):
          if self.losetup:
***************
*** 404,430 ****
              if rc == 0:
                  self.mounted = False
              else:
!                 logging.info("Second attempt Unmounting directory %s" % self.mountdir)
!                 logging.info("Calling fuser -ck on %s" % self.mountdir)
!                 rc = subprocess.call(["/sbin/fuser", '-ck', self.mountdir])
!                 logging.info("fuser -ck returned  %s" % rc )
!
!                 time.sleep(2)
!
!                 rc = subprocess.call(["/bin/umount", self.mountdir])
!                 if rc == 0:
!                     logging.info("Second umount succeeded")
                      self.mounted = False
-                 else:
-                     logging.debug("Second Unmounting directory %s failed, using lazy umount" % self.mountdir)
-                     print >> sys.stdout, "Second Unmounting directory %s failed, using lazy umount" %self.mountdir
-                     rc = subprocess.call(["/bin/umount", "-l", self.mountdir])
-                     if rc != 0:
-                         raise MountError("Unable to unmount filesystem at %s" % self.mountdir)
-                     else:
-                         logging.debug("lazy umount succeeded on %s" % self.mountdir)
-                         print >> sys.stdout, "lazy umount succeeded on %s" % self.mountdir
-                         self.mounted = False

          if self.rmdir and not self.mounted:
              try:
--- 366,380 ----
              if rc == 0:
                  self.mounted = False
              else:
!                 logging.debug("Unmounting directory %s failed, using lazy umount" % self.mountdir)
!                 print >> sys.stdout, "Unmounting directory %s failed, using lazy umount" %self.mountdir
!                 rc = subprocess.call(["/bin/umount", "-l", self.mountdir])
!                 if rc != 0:
!                     raise MountError("Unable to unmount filesystem at %s" % self.mountdir)
!                 else:
!                     logging.debug("lazy umount succeeded on %s" % self.mountdir)
!                     print >> sys.stdout, "lazy umount succeeded on %s" % self.mountdir
                      self.mounted = False

          if self.rmdir and not self.mounted:
              try:

The entire modified fs.py file from livecd-tools-13.4.5-1.el6.x86_64.rpm lives here.

 

This entry was posted in Nerd, Uncategorized. Bookmark the permalink.