drop 'xdriver=vesa' from basic graphics mode parameters (per ajax)
[livecd.git] / tools / liveimage-mount
blob90cfe9005d6bb9dab902420df5f410ee6b508e6e
1 #!/usr/bin/python -tt
3 # liveimage-mount: Mount a LiveOS at the specified point, and log
4 # into a shell.
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.
23 import os
24 import sys
25 import stat
26 import getopt
27 import tempfile
28 import subprocess
30 from imgcreate.errors import *
32 def usage(ecode):
33 print """Usage:
34 liveimage-mount [opts] ISO.iso|LiveOSdevice MOUNTPOINT [COMMAND] [ARGS]
36 where [opts] = [-h|--help
37 [--chroot
38 [--mount-hacks
39 [--persist]]]]]
41 and [ARGS] = [arg1[ arg2[ ...]]]\n"""
42 sys.exit(ecode)
45 def call(*popenargs, **kwargs):
46 '''
47 Calls subprocess.Popen() with the provided arguments. All stdout and
48 stderr output is sent to print. The return value is the exit
49 code of the command.
50 '''
51 p = subprocess.Popen(*popenargs, stdout=subprocess.PIPE,
52 stderr=subprocess.STDOUT, **kwargs)
53 rc = p.wait()
55 # Log output using logging module
56 while True:
57 # FIXME choose a more appropriate buffer size
58 buf = p.stdout.read(4096)
59 if not buf:
60 break
61 print buf
63 return rc
66 def rcall(args, env=None):
67 if env:
68 environ = os.environ.copy()
69 environ.update(env)
70 else:
71 environ = None
72 try:
73 p = subprocess.Popen(args, stdout=subprocess.PIPE,
74 stderr=subprocess.PIPE, env=environ)
75 out, err = p.communicate()
76 except OSError, e:
77 raise CreatorError(u"Failed to execute:\n'%s'\n'%s'" % (args, e))
78 except:
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))
82 else:
83 if p.returncode != 0:
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))
87 return out
90 def get_device_mountpoint(path):
91 """Return the device and mountpoint for a file or device path."""
93 info = list()
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)
100 if len(info) == 1:
101 info.extend(devinfo[2].split(None))
102 return [info[0], info[5]]
105 def main():
106 if os.geteuid() != 0:
107 print >> sys.stderr, """\n Exiting...
108 \r You must run liveimage-mount with root priviledges.\n"""
109 return 1
111 try:
112 opts,args = getopt.getopt(sys.argv[1:], 'h', ['help',
113 'chroot', 'persist',
114 'mount-hacks'])
115 except getopt.GetoptError, e:
116 usage(1)
118 img_type = None
119 chroot = False
120 persist = False
121 mount_hacks = False
122 for o,a in opts:
123 if o in ('-h', '--help'):
124 usage(0)
125 elif o in ('--chroot', ):
126 chroot = True
127 elif o in ('--mount-hacks', ):
128 mount_hacks = True
129 elif o in ('--persist', ):
130 """Option used to run a command in a spawned process."""
131 persist = True
133 if len(args) < 2:
134 usage(1)
136 liveos = args[0]
137 destmnt = args[1]
139 if not os.path.exists(liveos):
140 print """\n Exiting...
141 %s is not a file, directory, or device.\n""" % liveos
142 ecode = 1
143 return
145 if stat.S_ISBLK(os.stat(liveos).st_mode):
146 img_type = 'blk'
147 imgloop = None
148 overlayloop = None
149 else:
150 img_type = 'iso'
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')
158 homemnt = None
159 dm_cow = None
160 mntlive = None
162 command = args[2:]
163 verbose = not command
165 squashmnt = tempfile.mkdtemp(prefix='livemnt-squash-')
166 squashloop = None
167 imgloop = None
168 losetup_args = ['/sbin/losetup', '-f', '--show']
170 try:
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):
181 print """
182 \r\tNo LiveOS was found on %s\n\t Exiting...\n""" % liveos
183 ecode = 1
184 return
185 else:
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)
194 overlay = None
195 for f in files:
196 if f.find('overlay-') == 0:
197 overlay = f
198 break
199 overlayloop = rcall(['/sbin/losetup', '-f']).rstrip()
200 if overlay:
201 call(['/sbin/losetup', overlayloop, os.path.join(liveosdir,
202 overlay)])
203 dm_cow = 'live-rw'
204 else:
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])
210 dm_cow = 'live-ro'
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):
222 os.makedirs(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])
228 if mount_hacks:
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:
233 args = []
234 args.extend(command)
235 live = ''
236 if img_type is 'blk':
237 live = 'live-'
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
242 ecode = p.returncode
243 elif len(command) > 0:
244 args = ['chroot', destmnt]
245 args.extend(command)
246 ecode = subprocess.call(args, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
247 elif chroot:
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)
250 else:
251 if dm_cow == 'live-ro':
252 status = ' with NO LiveOS persistence,'
253 else:
254 status = ''
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)
257 finally:
258 call(['/bin/sync'])
259 if not persist:
260 if verbose:
261 print """Cleaning up...
262 Please wait if large files were written."""
263 if mount_hacks:
264 subprocess.call(['umount', os.path.join(destmnt, 'var', 'run')])
265 subprocess.call(['umount', os.path.join(destmnt, 'proc')])
266 if homemnt:
267 call(['/bin/umount', homemnt])
268 call(['/sbin/losetup', '-d', homeloop])
269 if img_type is 'blk':
270 if mntlive:
271 call(['/bin/umount', mntlive])
272 os.rmdir(mntlive)
273 if os.path.ismount(destmnt):
274 call(['/bin/umount', destmnt])
275 if img_type is 'blk':
276 if dm_cow:
277 call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
278 'remove', dm_cow])
279 if overlayloop:
280 call(['/sbin/losetup', '-d', overlayloop])
281 if imgloop:
282 call(['/sbin/losetup', '-d', imgloop])
283 if squashloop:
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])
289 os.rmdir(squashmnt)
290 if not os.path.ismount(liveosmnt):
291 os.rmdir(liveosmnt)
292 if verbose:
293 print "Cleanup complete"
295 sys.exit(ecode)
297 if __name__ == '__main__':
298 main()