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.
33 liveimage-mount [opts] ISO.iso|LiveOSdevice MOUNTPOINT [COMMAND] [ARGS]
35 where [opts] = [-h|--help
40 and [ARGS] = [arg1[ arg2[ ...]]]\n"""
44 def call(*popenargs
, **kwargs
):
46 Calls subprocess.Popen() with the provided arguments. All stdout and
47 stderr output is sent to print. The return value is the exit
50 p
= subprocess
.Popen(*popenargs
, stdout
=subprocess
.PIPE
,
51 stderr
=subprocess
.STDOUT
, **kwargs
)
54 # Log output using logging module
56 # FIXME choose a more appropriate buffer size
57 buf
= p
.stdout
.read(4096)
65 def rcall(args
, env
=None):
67 environ
= os
.environ
.copy()
72 p
= subprocess
.Popen(args
, stdout
=subprocess
.PIPE
,
73 stderr
=subprocess
.PIPE
, env
=environ
)
74 out
, err
= p
.communicate()
76 raise CreatorError(u
"Failed to execute:\n'%s'\n'%s'" % (args
, e
))
78 raise CreatorError(u
"""Failed to execute:\n'%s'
79 \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
80 (args
, environ
, out
, err
, p
.returncode
))
83 raise CreatorError(u
"""Error in call:\n'%s'\nenviron: '%s'
84 \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
85 (args
, environ
, out
, err
, p
.returncode
))
89 def get_device_mountpoint(path
):
90 """Return the device and mountpoint for a file or device path."""
93 info
[0:5] = [None] * 6
94 if os
.path
.exists(path
):
95 st_mode
= os
.stat(path
).st_mode
96 if stat
.S_ISBLK(st_mode
) or os
.path
.ismount(path
):
97 devinfo
= rcall(['/bin/df', path
]).splitlines()
98 info
= devinfo
[1].split(None)
100 info
.extend(devinfo
[2].split(None))
101 return [info
[0], info
[5]]
105 if os
.geteuid() != 0:
106 print >> sys
.stderr
, """\n Exiting...
107 \r You must run liveimage-mount with root priviledges.\n"""
111 opts
,args
= getopt
.getopt(sys
.argv
[1:], 'h', ['help',
114 except getopt
.GetoptError
, e
:
122 if o
in ('-h', '--help'):
124 elif o
in ('--chroot', ):
126 elif o
in ('--mount-hacks', ):
128 elif o
in ('--persist', ):
129 """Option used to run a command in a spawned process."""
138 if not os
.path
.exists(liveos
):
139 print """\n Exiting...
140 %s is not a file, directory, or device.\n""" % liveos
144 if stat
.S_ISBLK(os
.stat(liveos
).st_mode
):
151 if img_type
is 'blk':
152 liveosmnt
= tempfile
.mkdtemp(prefix
='livemnt-device-')
153 if img_type
is 'iso':
154 liveosmnt
= tempfile
.mkdtemp(prefix
='livemnt-iso-')
156 liveosdir
= os
.path
.join(liveosmnt
, 'LiveOS')
162 verbose
= not command
164 squashmnt
= tempfile
.mkdtemp(prefix
='livemnt-squash-')
167 losetup_args
= ['/sbin/losetup', '-f', '--show']
170 if img_type
is 'blk':
171 call(['/bin/mount', liveos
, liveosmnt
])
172 elif img_type
is 'iso':
173 liveosloop
= rcall(losetup_args
+ [liveos
]).rstrip()
174 call(['/bin/mount', '-o', 'ro', liveosloop
, liveosmnt
])
176 squash_img
= os
.path
.join(liveosdir
, 'squashfs.img')
177 if not os
.path
.exists(squash_img
):
178 ext3_img
= os
.path
.join(liveosdir
, 'ext3fs.img')
179 if not os
.path
.exists(ext3_img
):
181 \r\tNo LiveOS was found on %s\n\t Exiting...\n""" % liveos
185 squashloop
= rcall(losetup_args
+ [squash_img
]).rstrip()
186 call(['/bin/mount', '-o', 'ro', squashloop
, squashmnt
])
187 ext3_img
= os
.path
.join(squashmnt
, 'LiveOS', 'ext3fs.img')
189 if img_type
is 'blk':
190 imgloop
= rcall(losetup_args
+ [ext3_img
]).rstrip()
191 imgsize
= rcall(['/sbin/blockdev', '--getsz', imgloop
]).rstrip()
192 files
= os
.listdir(liveosdir
)
195 if f
.find('overlay-') == 0:
198 overlayloop
= rcall(['/sbin/losetup', '-f']).rstrip()
200 call(['/sbin/losetup', overlayloop
, os
.path
.join(liveosdir
,
204 overlay
= tempfile
.NamedTemporaryFile(dir='/dev/shm')
205 print "\npreparing temporary overlay..."
206 call(['/bin/dd', 'if=/dev/null', 'of=%s' % overlay
.name
,
207 'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])
208 call(['/sbin/losetup', overlayloop
, overlay
.name
])
210 call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
211 'create', dm_cow
, '--table=0 %s snapshot %s %s p 8' %
212 (imgsize
, imgloop
, overlayloop
)])
213 call(['/bin/mount', os
.path
.join('/dev/mapper', dm_cow
), destmnt
])
214 home_path
= os
.path
.join(liveosdir
, 'home.img')
215 if os
.path
.exists(home_path
):
216 homemnt
= os
.path
.join(destmnt
, 'home')
217 homeloop
= rcall(losetup_args
+ [home_path
]).rstrip()
218 call(['/bin/mount', homeloop
, homemnt
])
219 mntlive
= os
.path
.join(destmnt
, 'mnt', 'live')
220 if not os
.path
.exists(mntlive
):
222 call(['/bin/mount', '--bind', liveosmnt
, mntlive
])
223 elif img_type
is 'iso':
224 imgloop
= rcall(losetup_args
+ [ext3_img
]).rstrip()
225 call(['/bin/mount', '-o', 'ro', imgloop
, destmnt
])
228 subprocess
.check_call(['mount', '-t', 'proc', 'proc', os
.path
.join(destmnt
, 'proc')], stderr
=sys
.stderr
)
229 subprocess
.check_call(['mount', '-t', 'tmpfs', 'tmpfs', os
.path
.join(destmnt
, 'var', 'run')], stderr
=sys
.stderr
)
231 if len(command
) > 0 and persist
:
235 if img_type
is 'blk':
237 print """Starting process with this command line:
238 \r%s\n %s is %smounted.""" % (' '.join(command
), liveos
, live
)
239 p
= subprocess
.Popen(args
, close_fds
=True)
240 print "Process id: %s" % p
.pid
242 elif len(command
) > 0:
243 args
= ['chroot', destmnt
]
245 ecode
= subprocess
.call(args
, stdin
=sys
.stdin
, stdout
=sys
.stdout
, stderr
=sys
.stderr
)
247 print "Starting subshell in chroot, press Ctrl-D to exit..."
248 ecode
= subprocess
.call(['chroot', destmnt
], stdin
=sys
.stdin
, stdout
=sys
.stdout
, stderr
=sys
.stderr
)
250 if dm_cow
== 'live-ro':
251 status
= ' with NO LiveOS persistence,'
254 print "Entering subshell,%s press Ctrl-D to exit..." % status
255 ecode
= subprocess
.call([os
.environ
['SHELL']], cwd
=destmnt
, stdin
=sys
.stdin
, stdout
=sys
.stdout
, stderr
=sys
.stderr
)
260 print """Cleaning up...
261 Please wait if large files were written."""
263 subprocess
.call(['umount', os
.path
.join(destmnt
, 'var', 'run')])
264 subprocess
.call(['umount', os
.path
.join(destmnt
, 'proc')])
266 call(['/bin/umount', homemnt
])
267 call(['/sbin/losetup', '-d', homeloop
])
268 if img_type
is 'blk':
270 call(['/bin/umount', mntlive
])
272 if os
.path
.ismount(destmnt
):
273 call(['/bin/umount', destmnt
])
274 if img_type
is 'blk':
276 call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
279 call(['/sbin/losetup', '-d', overlayloop
])
281 call(['/sbin/losetup', '-d', imgloop
])
283 call(['/bin/umount', squashloop
])
284 call(['/sbin/losetup', '-d', squashloop
])
285 call(['/bin/umount', liveosmnt
])
286 if not img_type
is 'blk':
287 call(['/sbin/losetup', '-d', liveosloop
])
289 if not os
.path
.ismount(liveosmnt
):
292 print "Cleanup complete"
296 if __name__
== '__main__':