3 # Copyright (c) 2009, 2010, 2011 Intel, Inc.
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation; version 2 of the License
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 from __future__ import with_statement
25 from mic.conf import configmgr
26 from mic.utils import misc, errors, runner, fs_related, lock
28 #####################################################################
30 #####################################################################
32 chroot_bindmounts = None
36 "/proc/sys/fs/binfmt_misc",
46 #####################################################################
48 #####################################################################
50 def ELF_arch(chrootdir):
51 #FIXME: if chkfiles are symlink, it will be complex
52 chkfiles = ('/bin/bash', '/sbin/init')
53 # regular expression to arch mapping
55 r"Intel 80[0-9]86": "i686",
61 cpath = os.path.join(chrootdir, path.lstrip('/'))
62 if not os.path.exists(cpath):
65 outs = runner.outs(['file', cpath])
66 for ptn in mapping.keys():
67 if re.search(ptn, outs):
70 raise errors.CreatorError("Failed to detect architecture of chroot: %s" %
73 def get_bindmounts(chrootdir, bindmounts = None):
74 # bindmounts should be a string like '/dev:/dev'
75 # FIXME: refine the bindmounts from string to dict
76 global chroot_bindmounts
80 src, dst = string.split(':', 1)
85 return (src or None, dst or None)
88 return chroot_bindmounts
90 chroot_bindmounts = []
91 bindmounts = bindmounts or ""
94 for mount in bindmounts.split(";"):
98 (src, dst) = totuple(mount)
100 if src in BIND_MOUNTS or src == '/':
103 if not os.path.exists(src):
106 if dst and os.path.isdir("%s/%s" % (chrootdir, dst)):
107 msger.warning("%s existed in %s , skip it." % (dst, chrootdir))
110 mountlist.append(totuple(mount))
112 for mntpoint in BIND_MOUNTS:
113 if os.path.isdir(mntpoint):
114 mountlist.append(tuple((mntpoint, None)))
116 for pair in mountlist:
117 bmount = fs_related.BindChrootMount(pair[0], chrootdir, pair[1])
118 chroot_bindmounts.append(bmount)
120 return chroot_bindmounts
122 #####################################################################
123 ### SETUP CHROOT ENVIRONMENT
124 #####################################################################
126 def bind_mount(chrootmounts):
127 for b in chrootmounts:
128 msger.verbose("bind_mount: %s -> %s" % (b.src, b.dest))
131 def setup_resolv(chrootdir):
133 shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
137 def setup_mtab(chrootdir):
139 dstmtab = chrootdir + mtab
140 if not os.path.islink(dstmtab):
141 shutil.copyfile(mtab, dstmtab)
143 def setup_chrootenv(chrootdir, bindmounts = None):
148 lockpath = os.path.join(chrootdir, '.chroot.lock')
149 chroot_lock = lock.SimpleLockfile(lockpath)
150 chroot_lock.acquire()
152 bind_mount(get_bindmounts(chrootdir, bindmounts))
154 setup_resolv(chrootdir)
156 setup_mtab(chrootdir)
159 chroot_lock = os.path.join(chrootdir, ".chroot.lock")
160 chroot_lockfd = open(chroot_lock, "w")
164 ######################################################################
165 ### CLEANUP CHROOT ENVIRONMENT
166 ######################################################################
168 def bind_unmount(chrootmounts):
169 for b in reversed(chrootmounts):
170 msger.verbose("bind_unmount: %s -> %s" % (b.src, b.dest))
173 def cleanup_resolv(chrootdir):
175 fd = open(chrootdir + "/etc/resolv.conf", "w")
181 def kill_proc_inchroot(chrootdir):
183 for fp in glob.glob("/proc/*/root"):
185 if os.readlink(fp) == chrootdir:
186 pid = int(fp.split("/")[2])
191 def cleanup_mtab(chrootdir):
192 if os.path.exists(chrootdir + "/etc/mtab"):
193 os.unlink(chrootdir + "/etc/mtab")
195 def cleanup_mounts(chrootdir):
196 umountcmd = misc.find_binary_path("umount")
197 mounts = open('/proc/mounts').readlines()
198 for line in reversed(mounts):
199 if chrootdir not in line:
202 point = line.split()[1]
204 # '/' to avoid common name prefix
205 if chrootdir == point or point.startswith(chrootdir + '/'):
206 args = [ umountcmd, "-l", point ]
207 ret = runner.quiet(args)
209 msger.warning("failed to unmount %s" % point)
210 if os.path.isdir(point) and len(os.listdir(point)) == 0:
213 msger.warning("%s is not directory or is not empty" % point)
215 def cleanup_chrootenv(chrootdir, bindmounts=None, globalmounts=()):
217 kill_proc_inchroot(chrootdir)
219 cleanup_mtab(chrootdir)
221 cleanup_resolv(chrootdir)
223 bind_unmount(get_bindmounts(chrootdir, bindmounts))
225 cleanup_mounts(chrootdir)
227 chroot_lock.release()
231 #####################################################################
233 #####################################################################
235 def savefs_before_chroot(chrootdir, saveto = None):
236 if configmgr.chroot['saveto']:
238 saveto = configmgr.chroot['saveto']
239 wrnmsg = "Can't save chroot fs for dir %s exists" % saveto
240 if saveto == chrootdir:
242 wrnmsg = "Dir %s is being used to chroot" % saveto
243 elif os.path.exists(saveto):
244 if msger.ask("Dir %s already exists, cleanup and continue?" %
246 shutil.rmtree(saveto, ignore_errors = True)
252 msger.info("Saving image to directory %s" % saveto)
253 fs_related.makedirs(os.path.dirname(os.path.abspath(saveto)))
254 runner.quiet("cp -af %s %s" % (chrootdir, saveto))
260 ignlst = [os.path.join(saveto, x) for x in devs]
261 map(os.unlink, filter(os.path.exists, ignlst))
263 msger.warning(wrnmsg)
265 def cleanup_after_chroot(targettype, imgmount, tmpdir, tmpmnt):
266 if imgmount and targettype == "img":
270 shutil.rmtree(tmpdir, ignore_errors = True)
273 shutil.rmtree(tmpmnt, ignore_errors = True)
275 def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"):
280 arch = ELF_arch(chrootdir)
282 qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm")
286 savefs_before_chroot(chrootdir, None)
289 msger.info("Launching shell. Exit to continue.\n"
290 "----------------------------------")
291 globalmounts = setup_chrootenv(chrootdir, bindmounts)
292 subprocess.call(execute, preexec_fn = mychroot, shell=True)
295 raise errors.CreatorError("chroot err: %s" % str(err))
298 cleanup_chrootenv(chrootdir, bindmounts, globalmounts)
300 os.unlink(chrootdir + qemu_emulator)