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 """ detect the architecture of an ELF file """
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 """ calculate all bind mount entries for global usage """
76 # bindmounts should be a string like '/dev:/dev'
77 # FIXME: refine the bindmounts from string to dict
78 global chroot_bindmounts
81 """ convert string contained ':' to a tuple """
83 src, dst = string.split(':', 1)
88 return (src or None, dst or None)
91 return chroot_bindmounts
93 chroot_bindmounts = []
94 bindmounts = bindmounts or ""
97 for mount in bindmounts.split(";"):
101 (src, dst) = totuple(mount)
103 if src in BIND_MOUNTS or src == '/':
106 if not os.path.exists(src):
109 if dst and os.path.isdir("%s/%s" % (chrootdir, dst)):
110 msger.warning("%s existed in %s , skip it." % (dst, chrootdir))
113 mountlist.append(totuple(mount))
115 for mntpoint in BIND_MOUNTS:
116 if os.path.isdir(mntpoint):
117 mountlist.append(tuple((mntpoint, None)))
119 for pair in mountlist:
120 bmount = fs_related.BindChrootMount(pair[0], chrootdir, pair[1])
121 chroot_bindmounts.append(bmount)
123 return chroot_bindmounts
125 #####################################################################
126 ### SETUP CHROOT ENVIRONMENT
127 #####################################################################
129 def bind_mount(chrootmounts):
130 """ perform bind mounting """
131 for mnt in chrootmounts:
132 msger.verbose("bind_mount: %s -> %s" % (mnt.src, mnt.dest))
135 def setup_resolv(chrootdir):
136 """ resolve network """
138 shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
139 except (OSError, IOError):
142 def setup_mtab(chrootdir):
143 """ adjust mount table """
145 dstmtab = chrootdir + mtab
146 if not os.path.islink(dstmtab):
147 shutil.copyfile(mtab, dstmtab)
149 def setup_chrootenv(chrootdir, bindmounts = None):
150 """ setup chroot environment """
155 lockpath = os.path.join(chrootdir, '.chroot.lock')
156 chroot_lock = lock.SimpleLockfile(lockpath)
157 chroot_lock.acquire()
159 bind_mount(get_bindmounts(chrootdir, bindmounts))
161 setup_resolv(chrootdir)
163 setup_mtab(chrootdir)
166 chroot_lock = os.path.join(chrootdir, ".chroot.lock")
167 chroot_lockfd = open(chroot_lock, "w")
171 ######################################################################
172 ### CLEANUP CHROOT ENVIRONMENT
173 ######################################################################
175 def bind_unmount(chrootmounts):
176 """ perform bind unmounting """
177 for mnt in reversed(chrootmounts):
178 msger.verbose("bind_unmount: %s -> %s" % (mnt.src, mnt.dest))
181 def cleanup_resolv(chrootdir):
182 """ clear resolv.conf """
184 fdes = open(chrootdir + "/etc/resolv.conf", "w")
187 except (OSError, IOError):
190 def kill_proc_inchroot(chrootdir):
191 """ kill all processes running inside chrootdir """
193 for fpath in glob.glob("/proc/*/root"):
195 if os.readlink(fpath) == chrootdir:
196 pid = int(fpath.split("/")[2])
198 except (OSError, ValueError):
201 def cleanup_mtab(chrootdir):
202 """ remove mtab file """
203 if os.path.exists(chrootdir + "/etc/mtab"):
204 os.unlink(chrootdir + "/etc/mtab")
206 def cleanup_mounts(chrootdir):
207 """ clean up all mount entries owned by chrootdir """
208 umountcmd = misc.find_binary_path("umount")
209 mounts = open('/proc/mounts').readlines()
210 for line in reversed(mounts):
211 if chrootdir not in line:
214 point = line.split()[1]
216 # '/' to avoid common name prefix
217 if chrootdir == point or point.startswith(chrootdir + '/'):
218 args = [ umountcmd, "-l", point ]
219 ret = runner.quiet(args)
221 msger.warning("failed to unmount %s" % point)
222 if os.path.isdir(point) and len(os.listdir(point)) == 0:
225 msger.warning("%s is not directory or is not empty" % point)
227 def cleanup_chrootenv(chrootdir, bindmounts=None, globalmounts=()):
228 """ clean up chroot environment """
230 kill_proc_inchroot(chrootdir)
232 cleanup_mtab(chrootdir)
234 cleanup_resolv(chrootdir)
236 bind_unmount(get_bindmounts(chrootdir, bindmounts))
238 cleanup_mounts(chrootdir)
240 chroot_lock.release()
244 #####################################################################
246 #####################################################################
248 def savefs_before_chroot(chrootdir, saveto = None):
249 """ backup chrootdir to another directory before chrooting in """
250 if configmgr.chroot['saveto']:
252 saveto = configmgr.chroot['saveto']
253 wrnmsg = "Can't save chroot fs for dir %s exists" % saveto
254 if saveto == chrootdir:
256 wrnmsg = "Dir %s is being used to chroot" % saveto
257 elif os.path.exists(saveto):
258 if msger.ask("Dir %s already exists, cleanup and continue?" %
260 shutil.rmtree(saveto, ignore_errors = True)
266 msger.info("Saving image to directory %s" % saveto)
267 fs_related.makedirs(os.path.dirname(os.path.abspath(saveto)))
268 runner.quiet("cp -af %s %s" % (chrootdir, saveto))
274 ignlst = [os.path.join(saveto, x) for x in devs]
275 map(os.unlink, filter(os.path.exists, ignlst))
277 msger.warning(wrnmsg)
279 def cleanup_after_chroot(targettype, imgmount, tmpdir, tmpmnt):
280 """ clean up all temporary directories after chrooting """
281 if imgmount and targettype == "img":
285 shutil.rmtree(tmpdir, ignore_errors = True)
288 shutil.rmtree(tmpmnt, ignore_errors = True)
290 def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"):
291 """ chroot the chrootdir and execute the command """
293 """ pre-execute function """
297 arch = ELF_arch(chrootdir)
299 qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm")
303 savefs_before_chroot(chrootdir, None)
306 msger.info("Launching shell. Exit to continue.\n"
307 "----------------------------------")
308 globalmounts = setup_chrootenv(chrootdir, bindmounts)
309 subprocess.call(execute, preexec_fn = mychroot, shell=True)
312 raise errors.CreatorError("chroot err: %s" % str(err))
315 cleanup_chrootenv(chrootdir, bindmounts, globalmounts)
317 os.unlink(chrootdir + qemu_emulator)