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
28 #####################################################################
30 #####################################################################
32 chroot_bindmounts = None
37 "/proc/sys/fs/binfmt_misc",
47 #####################################################################
49 #####################################################################
51 def ELF_arch(chrootdir):
52 #FIXME: if chkfiles are symlink, it will be complex
53 chkfiles = ('/bin/bash', '/sbin/init')
54 # regular expression to arch mapping
56 r"Intel 80[0-9]86": "i686",
62 cpath = os.path.join(chrootdir, path.lstrip('/'))
63 if not os.path.exists(cpath):
66 outs = runner.outs(['file', cpath])
67 for ptn in mapping.keys():
68 if re.search(ptn, outs):
71 raise errors.CreatorError("Failed to detect architecture of chroot: %s" %
74 def get_bindmounts(chrootdir, bindmounts = None):
75 # bindmounts should be a string like '/dev:/dev'
76 # FIXME: refine the bindmounts from string to dict
77 global chroot_bindmounts
81 src, dst = string.split(':', 1)
86 return (src or None, dst or None)
89 return chroot_bindmounts
91 chroot_bindmounts = []
92 bindmounts = bindmounts or ""
95 for mount in bindmounts.split(";"):
99 (src, dst) = totuple(mount)
101 if src in BIND_MOUNTS or src == '/':
104 if not os.path.exists(src):
107 if dst and os.path.isdir("%s/%s" % (chrootdir, dst)):
108 msger.warning("%s existed in %s , skip it." % (dst, chrootdir))
111 mountlist.append(totuple(mount))
113 for mntpoint in BIND_MOUNTS:
114 if os.path.isdir(mntpoint):
115 mountlist.append(tuple((mntpoint, None)))
117 for pair in mountlist:
118 bmount = fs_related.BindChrootMount(pair[0], chrootdir, pair[1])
119 chroot_bindmounts.append(bmount)
121 return chroot_bindmounts
123 #####################################################################
124 ### SETUP CHROOT ENVIRONMENT
125 #####################################################################
127 def bind_mount(chrootmounts):
128 for b in chrootmounts:
129 msger.verbose("bind_mount: %s -> %s" % (b.src, b.dest))
132 def setup_resolv(chrootdir):
134 shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
138 def setup_mtab(chrootdir):
140 dstmtab = chrootdir + mtab
141 if not os.path.islink(dstmtab):
142 shutil.copyfile(mtab, dstmtab)
144 def setup_chrootenv(chrootdir, bindmounts = None):
146 bind_mount(get_bindmounts(chrootdir, bindmounts))
148 setup_resolv(chrootdir)
150 setup_mtab(chrootdir)
153 chroot_lock = os.path.join(chrootdir, ".chroot.lock")
154 chroot_lockfd = open(chroot_lock, "w")
158 ######################################################################
159 ### CLEANUP CHROOT ENVIRONMENT
160 ######################################################################
162 def bind_unmount(chrootmounts):
163 for b in reversed(chrootmounts):
164 msger.verbose("bind_unmount: %s -> %s" % (b.src, b.dest))
167 def cleanup_resolv(chrootdir):
169 fd = open(chrootdir + "/etc/resolv.conf", "w")
175 def kill_processes(chrootdir):
177 for fp in glob.glob("/proc/*/root"):
179 if os.readlink(fp) == chrootdir:
180 pid = int(fp.split("/")[2])
185 def cleanup_mtab(chrootdir):
186 if os.path.exists(chrootdir + "/etc/mtab"):
187 os.unlink(chrootdir + "/etc/mtab")
189 def cleanup_mounts(chrootdir):
190 umountcmd = misc.find_binary_path("umount")
191 mounts = open('/proc/mounts').readlines()
192 for line in reversed(mounts):
193 if chrootdir not in line:
196 point = line.split()[1]
198 # '/' to avoid common name prefix
199 if chrootdir == point or point.startswith(chrootdir + '/'):
200 args = [ umountcmd, "-l", point ]
201 ret = runner.quiet(args)
203 msger.warning("failed to unmount %s" % point)
204 if os.path.isdir(point) and len(os.listdir(point)) == 0:
207 msger.warning("%s is not directory or is not empty" % point)
209 def cleanup_chrootenv(chrootdir, bindmounts=None, globalmounts=()):
211 chroot_lockfd.close()
213 kill_processes(chrootdir)
215 cleanup_mtab(chrootdir)
217 cleanup_resolv(chrootdir)
219 bind_unmount(get_bindmounts(chrootdir, bindmounts))
221 cleanup_mounts(chrootdir)
225 #####################################################################
227 #####################################################################
229 def savefs_before_chroot(chrootdir, saveto = None):
230 if configmgr.chroot['saveto']:
232 saveto = configmgr.chroot['saveto']
233 wrnmsg = "Can't save chroot fs for dir %s exists" % saveto
234 if saveto == chrootdir:
236 wrnmsg = "Dir %s is being used to chroot" % saveto
237 elif os.path.exists(saveto):
238 if msger.ask("Dir %s already exists, cleanup and continue?" %
240 shutil.rmtree(saveto, ignore_errors = True)
246 msger.info("Saving image to directory %s" % saveto)
247 fs_related.makedirs(os.path.dirname(os.path.abspath(saveto)))
248 runner.quiet("cp -af %s %s" % (chrootdir, saveto))
254 ignlst = [os.path.join(saveto, x) for x in devs]
255 map(os.unlink, filter(os.path.exists, ignlst))
257 msger.warning(wrnmsg)
259 def cleanup_after_chroot(targettype, imgmount, tmpdir, tmpmnt):
260 if imgmount and targettype == "img":
264 shutil.rmtree(tmpdir, ignore_errors = True)
267 shutil.rmtree(tmpmnt, ignore_errors = True)
269 def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"):
274 arch = ELF_arch(chrootdir)
276 qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm")
280 savefs_before_chroot(chrootdir, None)
283 msger.info("Launching shell. Exit to continue.\n"
284 "----------------------------------")
285 globalmounts = setup_chrootenv(chrootdir, bindmounts)
286 subprocess.call(execute, preexec_fn = mychroot, shell=True)
289 raise errors.CreatorError("chroot err: %s" % str(err))
292 cleanup_chrootenv(chrootdir, bindmounts, globalmounts)
294 os.unlink(chrootdir + qemu_emulator)