3 # liveimage-mount: Mount a LiveOS at the specified point, and log
6 # Copyright 2011, Red Hat Inc.
7 # Code for Live mounting an attached LiveOS device added by Frederick Grose,
8 # <fgrose at sugarlabs.org>
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.
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.
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.
30 from imgcreate
.errors
import *
34 liveimage-mount [opts] ISO.iso|LiveOSdevice MOUNTPOINT [COMMAND] [ARGS]
36 where [opts] = [-h|--help
41 and [ARGS] = [arg1[ arg2[ ...]]]\n"""
45 def call(*popenargs
, **kwargs
):
47 Calls subprocess.Popen() with the provided arguments. All stdout and
48 stderr output is sent to print. The return value is the exit
51 p
= subprocess
.Popen(*popenargs
, stdout
=subprocess
.PIPE
,
52 stderr
=subprocess
.STDOUT
, **kwargs
)
55 # Log output using logging module
57 # FIXME choose a more appropriate buffer size
58 buf
= p
.stdout
.read(4096)
66 def rcall(args
, env
=None):
68 environ
= os
.environ
.copy()
73 p
= subprocess
.Popen(args
, stdout
=subprocess
.PIPE
,
74 stderr
=subprocess
.PIPE
, env
=environ
)
75 out
, err
= p
.communicate()
77 raise CreatorError(u
"Failed to execute:\n'%s'\n'%s'" % (args
, e
))
79 raise CreatorError(u
"""Failed to execute:\n'%s'
80 \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
81 (args
, environ
, out
, err
, p
.returncode
))
84 raise CreatorError(u
"""Error in call:\n'%s'\nenviron: '%s'
85 \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
86 (args
, environ
, out
, err
, p
.returncode
))
90 def get_device_mountpoint(path
):
91 """Return the device and mountpoint for a file or device path."""
94 info
[0:5] = [None] * 6
95 if os
.path
.exists(path
):
96 st_mode
= os
.stat(path
).st_mode
97 if stat
.S_ISBLK(st_mode
) or os
.path
.ismount(path
):
98 devinfo
= rcall(['/bin/df', path
]).splitlines()
99 info
= devinfo
[1].split(None)
101 info
.extend(devinfo
[2].split(None))
102 return [info
[0], info
[5]]
106 if os
.geteuid() != 0:
107 print >> sys
.stderr
, """\n Exiting...
108 \r You must run liveimage-mount with root priviledges.\n"""
112 opts
,args
= getopt
.getopt(sys
.argv
[1:], 'h', ['help',
115 except getopt
.GetoptError
, e
:
123 if o
in ('-h', '--help'):
125 elif o
in ('--chroot', ):
127 elif o
in ('--mount-hacks', ):
129 elif o
in ('--persist', ):
130 """Option used to run a command in a spawned process."""
139 if not os
.path
.exists(liveos
):
140 print """\n Exiting...
141 %s is not a file, directory, or device.\n""" % liveos
145 if stat
.S_ISBLK(os
.stat(liveos
).st_mode
):
152 if img_type
is 'blk':
153 liveosmnt
= tempfile
.mkdtemp(prefix
='livemnt-device-')
154 if img_type
is 'iso':
155 liveosmnt
= tempfile
.mkdtemp(prefix
='livemnt-iso-')
157 liveosdir
= os
.path
.join(liveosmnt
, 'LiveOS')
163 verbose
= not command
165 squashmnt
= tempfile
.mkdtemp(prefix
='livemnt-squash-')
168 losetup_args
= ['/sbin/losetup', '-f', '--show']
171 if img_type
is 'blk':
172 call(['/bin/mount', liveos
, liveosmnt
])
173 elif img_type
is 'iso':
174 liveosloop
= rcall(losetup_args
+ [liveos
]).rstrip()
175 call(['/bin/mount', '-o', 'ro', liveosloop
, liveosmnt
])
177 squash_img
= os
.path
.join(liveosdir
, 'squashfs.img')
178 if not os
.path
.exists(squash_img
):
179 ext3_img
= os
.path
.join(liveosdir
, 'ext3fs.img')
180 if not os
.path
.exists(ext3_img
):
182 \r\tNo LiveOS was found on %s\n\t Exiting...\n""" % liveos
186 squashloop
= rcall(losetup_args
+ [squash_img
]).rstrip()
187 call(['/bin/mount', '-o', 'ro', squashloop
, squashmnt
])
188 ext3_img
= os
.path
.join(squashmnt
, 'LiveOS', 'ext3fs.img')
190 if img_type
is 'blk':
191 imgloop
= rcall(losetup_args
+ [ext3_img
]).rstrip()
192 imgsize
= rcall(['/sbin/blockdev', '--getsz', imgloop
]).rstrip()
193 files
= os
.listdir(liveosdir
)
196 if f
.find('overlay-') == 0:
199 overlayloop
= rcall(['/sbin/losetup', '-f']).rstrip()
201 call(['/sbin/losetup', overlayloop
, os
.path
.join(liveosdir
,
205 overlay
= tempfile
.NamedTemporaryFile(dir='/dev/shm')
206 print "\npreparing temporary overlay..."
207 call(['/bin/dd', 'if=/dev/null', 'of=%s' % overlay
.name
,
208 'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])
209 call(['/sbin/losetup', overlayloop
, overlay
.name
])
211 call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
212 'create', dm_cow
, '--table=0 %s snapshot %s %s p 8' %
213 (imgsize
, imgloop
, overlayloop
)])
214 call(['/bin/mount', os
.path
.join('/dev/mapper', dm_cow
), destmnt
])
215 home_path
= os
.path
.join(liveosdir
, 'home.img')
216 if os
.path
.exists(home_path
):
217 homemnt
= os
.path
.join(destmnt
, 'home')
218 homeloop
= rcall(losetup_args
+ [home_path
]).rstrip()
219 call(['/bin/mount', homeloop
, homemnt
])
220 mntlive
= os
.path
.join(destmnt
, 'mnt', 'live')
221 if not os
.path
.exists(mntlive
):
223 call(['/bin/mount', '--bind', liveosmnt
, mntlive
])
224 elif img_type
is 'iso':
225 imgloop
= rcall(losetup_args
+ [ext3_img
]).rstrip()
226 call(['/bin/mount', '-o', 'ro', imgloop
, destmnt
])
229 subprocess
.check_call(['mount', '-t', 'proc', 'proc', os
.path
.join(destmnt
, 'proc')], stderr
=sys
.stderr
)
230 subprocess
.check_call(['mount', '-t', 'tmpfs', 'tmpfs', os
.path
.join(destmnt
, 'var', 'run')], stderr
=sys
.stderr
)
232 if len(command
) > 0 and persist
:
236 if img_type
is 'blk':
238 print """Starting process with this command line:
239 \r%s\n %s is %smounted.""" % (' '.join(command
), liveos
, live
)
240 p
= subprocess
.Popen(args
, close_fds
=True)
241 print "Process id: %s" % p
.pid
243 elif len(command
) > 0:
244 args
= ['chroot', destmnt
]
246 ecode
= subprocess
.call(args
, stdin
=sys
.stdin
, stdout
=sys
.stdout
, stderr
=sys
.stderr
)
248 print "Starting subshell in chroot, press Ctrl-D to exit..."
249 ecode
= subprocess
.call(['chroot', destmnt
], stdin
=sys
.stdin
, stdout
=sys
.stdout
, stderr
=sys
.stderr
)
251 if dm_cow
== 'live-ro':
252 status
= ' with NO LiveOS persistence,'
255 print "Entering subshell,%s press Ctrl-D to exit..." % status
256 ecode
= subprocess
.call([os
.environ
['SHELL']], cwd
=destmnt
, stdin
=sys
.stdin
, stdout
=sys
.stdout
, stderr
=sys
.stderr
)
261 print """Cleaning up...
262 Please wait if large files were written."""
264 subprocess
.call(['umount', os
.path
.join(destmnt
, 'var', 'run')])
265 subprocess
.call(['umount', os
.path
.join(destmnt
, 'proc')])
267 call(['/bin/umount', homemnt
])
268 call(['/sbin/losetup', '-d', homeloop
])
269 if img_type
is 'blk':
271 call(['/bin/umount', mntlive
])
273 if os
.path
.ismount(destmnt
):
274 call(['/bin/umount', destmnt
])
275 if img_type
is 'blk':
277 call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
280 call(['/sbin/losetup', '-d', overlayloop
])
282 call(['/sbin/losetup', '-d', imgloop
])
284 call(['/bin/umount', squashloop
])
285 call(['/sbin/losetup', '-d', squashloop
])
286 call(['/bin/umount', liveosmnt
])
287 if not img_type
is 'blk':
288 call(['/sbin/losetup', '-d', liveosloop
])
290 if not os
.path
.ismount(liveosmnt
):
293 print "Cleanup complete"
297 if __name__
== '__main__':