liveimage-mount 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/python -tt
  2. #
  3. # liveimage-mount: Mount a LiveOS at the specified point, and log
  4. # into a shell.
  5. #
  6. # Copyright 2011, Red Hat Inc.
  7. # Code for Live mounting an attached LiveOS device added by Frederick Grose,
  8. # <fgrose at sugarlabs.org>
  9. #
  10. # This program is free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation; version 2 of the License.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU Library General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License
  20. # along with this program; if not, write to the Free Software
  21. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. import os
  23. import sys
  24. import stat
  25. import getopt
  26. import tempfile
  27. import subprocess
  28. from imgcreate.errors import *
  29. def usage(ecode):
  30. print """Usage:
  31. liveimage-mount [opts] ISO.iso|LiveOSdevice MOUNTPOINT [COMMAND] [ARGS]
  32. where [opts] = [-h|--help
  33. [--chroot
  34. [--mount-hacks
  35. [--persist]]]]]
  36. and [ARGS] = [arg1[ arg2[ ...]]]\n"""
  37. sys.exit(ecode)
  38. def call(*popenargs, **kwargs):
  39. '''
  40. Calls subprocess.Popen() with the provided arguments. All stdout and
  41. stderr output is sent to print. The return value is the exit
  42. code of the command.
  43. '''
  44. p = subprocess.Popen(*popenargs, stdout=subprocess.PIPE,
  45. stderr=subprocess.STDOUT, **kwargs)
  46. rc = p.wait()
  47. # Log output using logging module
  48. while True:
  49. # FIXME choose a more appropriate buffer size
  50. buf = p.stdout.read(4096)
  51. if not buf:
  52. break
  53. print buf
  54. return rc
  55. def rcall(args, env=None):
  56. if env:
  57. environ = os.environ.copy()
  58. environ.update(env)
  59. else:
  60. environ = None
  61. try:
  62. p = subprocess.Popen(args, stdout=subprocess.PIPE,
  63. stderr=subprocess.PIPE, env=environ)
  64. out, err = p.communicate()
  65. except OSError, e:
  66. raise CreatorError(u"Failed to execute:\n'%s'\n'%s'" % (args, e))
  67. except:
  68. raise CreatorError(u"""Failed to execute:\n'%s'
  69. \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
  70. (args, environ, out, err, p.returncode))
  71. else:
  72. if p.returncode != 0:
  73. raise CreatorError(u"""Error in call:\n'%s'\nenviron: '%s'
  74. \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
  75. (args, environ, out, err, p.returncode))
  76. return out
  77. def get_device_mountpoint(path):
  78. """Return the device and mountpoint for a file or device path."""
  79. info = list()
  80. info[0:5] = [None] * 6
  81. if os.path.exists(path):
  82. st_mode = os.stat(path).st_mode
  83. if stat.S_ISBLK(st_mode) or os.path.ismount(path):
  84. devinfo = rcall(['/bin/df', path]).splitlines()
  85. info = devinfo[1].split(None)
  86. if len(info) == 1:
  87. info.extend(devinfo[2].split(None))
  88. return [info[0], info[5]]
  89. def main():
  90. if os.geteuid() != 0:
  91. print >> sys.stderr, """\n Exiting...
  92. \r You must run liveimage-mount with root priviledges.\n"""
  93. return 1
  94. try:
  95. opts,args = getopt.getopt(sys.argv[1:], 'h', ['help',
  96. 'chroot', 'persist',
  97. 'mount-hacks'])
  98. except getopt.GetoptError, e:
  99. usage(1)
  100. img_type = None
  101. chroot = False
  102. persist = False
  103. mount_hacks = False
  104. for o,a in opts:
  105. if o in ('-h', '--help'):
  106. usage(0)
  107. elif o in ('--chroot', ):
  108. chroot = True
  109. elif o in ('--mount-hacks', ):
  110. mount_hacks = True
  111. elif o in ('--persist', ):
  112. """Option used to run a command in a spawned process."""
  113. persist = True
  114. if len(args) < 2:
  115. usage(1)
  116. liveos = args[0]
  117. destmnt = args[1]
  118. if not os.path.exists(liveos):
  119. print """\n Exiting...
  120. %s is not a file, directory, or device.\n""" % liveos
  121. ecode = 1
  122. return
  123. if stat.S_ISBLK(os.stat(liveos).st_mode):
  124. img_type = 'blk'
  125. imgloop = None
  126. overlayloop = None
  127. else:
  128. img_type = 'iso'
  129. if img_type is 'blk':
  130. liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')
  131. if img_type is 'iso':
  132. liveosmnt = tempfile.mkdtemp(prefix='livemnt-iso-')
  133. liveosdir = os.path.join(liveosmnt, 'LiveOS')
  134. homemnt = None
  135. dm_cow = None
  136. mntlive = None
  137. command = args[2:]
  138. verbose = not command
  139. squashmnt = tempfile.mkdtemp(prefix='livemnt-squash-')
  140. squashloop = None
  141. imgloop = None
  142. losetup_args = ['/sbin/losetup', '-f', '--show']
  143. try:
  144. if img_type is 'blk':
  145. call(['/bin/mount', liveos, liveosmnt])
  146. elif img_type is 'iso':
  147. liveosloop = rcall(losetup_args + [liveos]).rstrip()
  148. call(['/bin/mount', '-o', 'ro', liveosloop, liveosmnt])
  149. squash_img = os.path.join(liveosdir, 'squashfs.img')
  150. if not os.path.exists(squash_img):
  151. ext3_img = os.path.join(liveosdir, 'ext3fs.img')
  152. if not os.path.exists(ext3_img):
  153. print """
  154. \r\tNo LiveOS was found on %s\n\t Exiting...\n""" % liveos
  155. ecode = 1
  156. return
  157. else:
  158. squashloop = rcall(losetup_args + [squash_img]).rstrip()
  159. call(['/bin/mount', '-o', 'ro', squashloop, squashmnt])
  160. ext3_img = os.path.join(squashmnt, 'LiveOS', 'ext3fs.img')
  161. if img_type is 'blk':
  162. imgloop = rcall(losetup_args + [ext3_img]).rstrip()
  163. imgsize = rcall(['/sbin/blockdev', '--getsz', imgloop]).rstrip()
  164. files = os.listdir(liveosdir)
  165. overlay = None
  166. for f in files:
  167. if f.find('overlay-') == 0:
  168. overlay = f
  169. break
  170. overlayloop = rcall(['/sbin/losetup', '-f']).rstrip()
  171. if overlay:
  172. call(['/sbin/losetup', overlayloop, os.path.join(liveosdir,
  173. overlay)])
  174. dm_cow = 'live-rw'
  175. else:
  176. overlay = tempfile.NamedTemporaryFile(dir='/dev/shm')
  177. print "\npreparing temporary overlay..."
  178. call(['/bin/dd', 'if=/dev/null', 'of=%s' % overlay.name,
  179. 'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])
  180. call(['/sbin/losetup', overlayloop, overlay.name])
  181. dm_cow = 'live-ro'
  182. call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
  183. 'create', dm_cow, '--table=0 %s snapshot %s %s p 8' %
  184. (imgsize, imgloop, overlayloop)])
  185. call(['/bin/mount', os.path.join('/dev/mapper', dm_cow), destmnt])
  186. home_path = os.path.join(liveosdir, 'home.img')
  187. if os.path.exists(home_path):
  188. homemnt = os.path.join(destmnt, 'home')
  189. homeloop = rcall(losetup_args + [home_path]).rstrip()
  190. call(['/bin/mount', homeloop, homemnt])
  191. mntlive = os.path.join(destmnt, 'mnt', 'live')
  192. if not os.path.exists(mntlive):
  193. os.makedirs(mntlive)
  194. call(['/bin/mount', '--bind', liveosmnt, mntlive])
  195. elif img_type is 'iso':
  196. imgloop = rcall(losetup_args + [ext3_img]).rstrip()
  197. call(['/bin/mount', '-o', 'ro', imgloop, destmnt])
  198. if mount_hacks:
  199. subprocess.check_call(['mount', '-t', 'proc', 'proc', os.path.join(destmnt, 'proc')], stderr=sys.stderr)
  200. subprocess.check_call(['mount', '-t', 'tmpfs', 'tmpfs', os.path.join(destmnt, 'var', 'run')], stderr=sys.stderr)
  201. if len(command) > 0 and persist:
  202. args = []
  203. args.extend(command)
  204. live = ''
  205. if img_type is 'blk':
  206. live = 'live-'
  207. print """Starting process with this command line:
  208. \r%s\n %s is %smounted.""" % (' '.join(command), liveos, live)
  209. p = subprocess.Popen(args, close_fds=True)
  210. print "Process id: %s" % p.pid
  211. ecode = p.returncode
  212. elif len(command) > 0:
  213. args = ['chroot', destmnt]
  214. args.extend(command)
  215. ecode = subprocess.call(args, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
  216. elif chroot:
  217. print "Starting subshell in chroot, press Ctrl-D to exit..."
  218. ecode = subprocess.call(['chroot', destmnt], stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
  219. else:
  220. if dm_cow == 'live-ro':
  221. status = ' with NO LiveOS persistence,'
  222. else:
  223. status = ''
  224. print "Entering subshell,%s press Ctrl-D to exit..." % status
  225. ecode = subprocess.call([os.environ['SHELL']], cwd=destmnt, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
  226. finally:
  227. call(['/bin/sync'])
  228. if not persist:
  229. if verbose:
  230. print """Cleaning up...
  231. Please wait if large files were written."""
  232. if mount_hacks:
  233. subprocess.call(['umount', os.path.join(destmnt, 'var', 'run')])
  234. subprocess.call(['umount', os.path.join(destmnt, 'proc')])
  235. if homemnt:
  236. call(['/bin/umount', homemnt])
  237. call(['/sbin/losetup', '-d', homeloop])
  238. if img_type is 'blk':
  239. if mntlive:
  240. call(['/bin/umount', mntlive])
  241. os.rmdir(mntlive)
  242. if os.path.ismount(destmnt):
  243. call(['/bin/umount', destmnt])
  244. if img_type is 'blk':
  245. if dm_cow:
  246. call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
  247. 'remove', dm_cow])
  248. if overlayloop:
  249. call(['/sbin/losetup', '-d', overlayloop])
  250. if imgloop:
  251. call(['/sbin/losetup', '-d', imgloop])
  252. if squashloop:
  253. call(['/bin/umount', squashloop])
  254. call(['/sbin/losetup', '-d', squashloop])
  255. call(['/bin/umount', liveosmnt])
  256. if not img_type is 'blk':
  257. call(['/sbin/losetup', '-d', liveosloop])
  258. os.rmdir(squashmnt)
  259. if not os.path.ismount(liveosmnt):
  260. os.rmdir(liveosmnt)
  261. if verbose:
  262. print "Cleanup complete"
  263. sys.exit(ecode)
  264. if __name__ == '__main__':
  265. main()