summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic/chroot.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/mic/chroot.py')
-rw-r--r--scripts/lib/mic/chroot.py343
1 files changed, 343 insertions, 0 deletions
diff --git a/scripts/lib/mic/chroot.py b/scripts/lib/mic/chroot.py
new file mode 100644
index 0000000000..99fb9a2c17
--- /dev/null
+++ b/scripts/lib/mic/chroot.py
@@ -0,0 +1,343 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2009, 2010, 2011 Intel, Inc.
4#
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
8#
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
12# for more details.
13#
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.
17
18from __future__ import with_statement
19import os
20import shutil
21import subprocess
22
23from mic import msger
24from mic.conf import configmgr
25from mic.utils import misc, errors, runner, fs_related
26
27chroot_lockfd = -1
28chroot_lock = ""
29BIND_MOUNTS = (
30 "/proc",
31 "/proc/sys/fs/binfmt_misc",
32 "/sys",
33 "/dev",
34 "/dev/pts",
35 "/dev/shm",
36 "/var/lib/dbus",
37 "/var/run/dbus",
38 "/var/lock",
39 )
40
41def cleanup_after_chroot(targettype,imgmount,tmpdir,tmpmnt):
42 if imgmount and targettype == "img":
43 imgmount.cleanup()
44
45 if tmpdir:
46 shutil.rmtree(tmpdir, ignore_errors = True)
47
48 if tmpmnt:
49 shutil.rmtree(tmpmnt, ignore_errors = True)
50
51def check_bind_mounts(chrootdir, bindmounts):
52 chrootmounts = []
53 for mount in bindmounts.split(";"):
54 if not mount:
55 continue
56
57 srcdst = mount.split(":")
58 if len(srcdst) == 1:
59 srcdst.append("none")
60
61 if not os.path.isdir(srcdst[0]):
62 return False
63
64 if srcdst[1] == "" or srcdst[1] == "none":
65 srcdst[1] = None
66
67 if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/':
68 continue
69
70 if chrootdir:
71 if not srcdst[1]:
72 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[0]))
73 else:
74 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
75
76 tmpdir = chrootdir + "/" + srcdst[1]
77 if os.path.isdir(tmpdir):
78 msger.warning("Warning: dir %s has existed." % tmpdir)
79
80 return True
81
82def cleanup_mounts(chrootdir):
83 umountcmd = misc.find_binary_path("umount")
84 abs_chrootdir = os.path.abspath(chrootdir)
85 mounts = open('/proc/mounts').readlines()
86 for line in reversed(mounts):
87 if abs_chrootdir not in line:
88 continue
89
90 point = line.split()[1]
91
92 # '/' to avoid common name prefix
93 if abs_chrootdir == point or point.startswith(abs_chrootdir + '/'):
94 args = [ umountcmd, "-l", point ]
95 ret = runner.quiet(args)
96 if ret != 0:
97 msger.warning("failed to unmount %s" % point)
98
99 return 0
100
101def setup_chrootenv(chrootdir, bindmounts = None, mountparent = True):
102 global chroot_lockfd, chroot_lock
103
104 def get_bind_mounts(chrootdir, bindmounts, mountparent = True):
105 chrootmounts = []
106 if bindmounts in ("", None):
107 bindmounts = ""
108
109 for mount in bindmounts.split(";"):
110 if not mount:
111 continue
112
113 srcdst = mount.split(":")
114 srcdst[0] = os.path.abspath(os.path.expanduser(srcdst[0]))
115 if len(srcdst) == 1:
116 srcdst.append("none")
117
118 # if some bindmount is not existed, but it's created inside
119 # chroot, this is not expected
120 if not os.path.exists(srcdst[0]):
121 os.makedirs(srcdst[0])
122
123 if not os.path.isdir(srcdst[0]):
124 continue
125
126 if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/':
127 msger.verbose("%s will be mounted by default." % srcdst[0])
128 continue
129
130 if srcdst[1] == "" or srcdst[1] == "none":
131 srcdst[1] = None
132 else:
133 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
134 if os.path.isdir(chrootdir + "/" + srcdst[1]):
135 msger.warning("%s has existed in %s , skip it."\
136 % (srcdst[1], chrootdir))
137 continue
138
139 chrootmounts.append(fs_related.BindChrootMount(srcdst[0],
140 chrootdir,
141 srcdst[1]))
142
143 """Default bind mounts"""
144 for pt in BIND_MOUNTS:
145 if not os.path.exists(pt):
146 continue
147 chrootmounts.append(fs_related.BindChrootMount(pt,
148 chrootdir,
149 None))
150
151 if mountparent:
152 chrootmounts.append(fs_related.BindChrootMount("/",
153 chrootdir,
154 "/parentroot",
155 "ro"))
156
157 for kernel in os.listdir("/lib/modules"):
158 chrootmounts.append(fs_related.BindChrootMount(
159 "/lib/modules/"+kernel,
160 chrootdir,
161 None,
162 "ro"))
163
164 return chrootmounts
165
166 def bind_mount(chrootmounts):
167 for b in chrootmounts:
168 msger.verbose("bind_mount: %s -> %s" % (b.src, b.dest))
169 b.mount()
170
171 def setup_resolv(chrootdir):
172 try:
173 shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
174 except:
175 pass
176
177 globalmounts = get_bind_mounts(chrootdir, bindmounts, mountparent)
178 bind_mount(globalmounts)
179
180 setup_resolv(chrootdir)
181
182 mtab = "/etc/mtab"
183 dstmtab = chrootdir + mtab
184 if not os.path.islink(dstmtab):
185 shutil.copyfile(mtab, dstmtab)
186
187 chroot_lock = os.path.join(chrootdir, ".chroot.lock")
188 chroot_lockfd = open(chroot_lock, "w")
189
190 return globalmounts
191
192def cleanup_chrootenv(chrootdir, bindmounts=None, globalmounts=()):
193 global chroot_lockfd, chroot_lock
194
195 def bind_unmount(chrootmounts):
196 for b in reversed(chrootmounts):
197 msger.verbose("bind_unmount: %s -> %s" % (b.src, b.dest))
198 b.unmount()
199
200 def cleanup_resolv(chrootdir):
201 try:
202 fd = open(chrootdir + "/etc/resolv.conf", "w")
203 fd.truncate(0)
204 fd.close()
205 except:
206 pass
207
208 def kill_processes(chrootdir):
209 import glob
210 for fp in glob.glob("/proc/*/root"):
211 try:
212 if os.readlink(fp) == chrootdir:
213 pid = int(fp.split("/")[2])
214 os.kill(pid, 9)
215 except:
216 pass
217
218 def cleanup_mountdir(chrootdir, bindmounts):
219 if bindmounts == "" or bindmounts == None:
220 return
221 chrootmounts = []
222 for mount in bindmounts.split(";"):
223 if not mount:
224 continue
225
226 srcdst = mount.split(":")
227
228 if len(srcdst) == 1:
229 srcdst.append("none")
230
231 if srcdst[0] == "/":
232 continue
233
234 if srcdst[1] == "" or srcdst[1] == "none":
235 srcdst[1] = srcdst[0]
236
237 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
238 tmpdir = chrootdir + "/" + srcdst[1]
239 if os.path.isdir(tmpdir):
240 if len(os.listdir(tmpdir)) == 0:
241 shutil.rmtree(tmpdir, ignore_errors = True)
242 else:
243 msger.warning("Warning: dir %s isn't empty." % tmpdir)
244
245 chroot_lockfd.close()
246 bind_unmount(globalmounts)
247
248 if not fs_related.my_fuser(chroot_lock):
249 tmpdir = chrootdir + "/parentroot"
250 if os.path.exists(tmpdir) and len(os.listdir(tmpdir)) == 0:
251 shutil.rmtree(tmpdir, ignore_errors = True)
252
253 cleanup_resolv(chrootdir)
254
255 if os.path.exists(chrootdir + "/etc/mtab"):
256 os.unlink(chrootdir + "/etc/mtab")
257
258 kill_processes(chrootdir)
259
260 cleanup_mountdir(chrootdir, bindmounts)
261
262def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"):
263 def mychroot():
264 os.chroot(chrootdir)
265 os.chdir("/")
266
267 if configmgr.chroot['saveto']:
268 savefs = True
269 saveto = configmgr.chroot['saveto']
270 wrnmsg = "Can't save chroot fs for dir %s exists" % saveto
271 if saveto == chrootdir:
272 savefs = False
273 wrnmsg = "Dir %s is being used to chroot" % saveto
274 elif os.path.exists(saveto):
275 if msger.ask("Dir %s already exists, cleanup and continue?" %
276 saveto):
277 shutil.rmtree(saveto, ignore_errors = True)
278 savefs = True
279 else:
280 savefs = False
281
282 if savefs:
283 msger.info("Saving image to directory %s" % saveto)
284 fs_related.makedirs(os.path.dirname(os.path.abspath(saveto)))
285 runner.quiet("cp -af %s %s" % (chrootdir, saveto))
286 devs = ['dev/fd',
287 'dev/stdin',
288 'dev/stdout',
289 'dev/stderr',
290 'etc/mtab']
291 ignlst = [os.path.join(saveto, x) for x in devs]
292 map(os.unlink, filter(os.path.exists, ignlst))
293 else:
294 msger.warning(wrnmsg)
295
296 dev_null = os.open("/dev/null", os.O_WRONLY)
297 files_to_check = ["/bin/bash", "/sbin/init"]
298
299 architecture_found = False
300
301 """ Register statically-linked qemu-arm if it is an ARM fs """
302 qemu_emulator = None
303
304 for ftc in files_to_check:
305 ftc = "%s/%s" % (chrootdir,ftc)
306
307 # Return code of 'file' is "almost always" 0 based on some man pages
308 # so we need to check the file existance first.
309 if not os.path.exists(ftc):
310 continue
311
312 for line in runner.outs(['file', ftc]).splitlines():
313 if 'ARM' in line:
314 qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm")
315 architecture_found = True
316 break
317
318 if 'Intel' in line:
319 architecture_found = True
320 break
321
322 if architecture_found:
323 break
324
325 os.close(dev_null)
326 if not architecture_found:
327 raise errors.CreatorError("Failed to get architecture from any of the "
328 "following files %s from chroot." \
329 % files_to_check)
330
331 try:
332 msger.info("Launching shell. Exit to continue.\n"
333 "----------------------------------")
334 globalmounts = setup_chrootenv(chrootdir, bindmounts)
335 subprocess.call(execute, preexec_fn = mychroot, shell=True)
336
337 except OSError, err:
338 raise errors.CreatorError("chroot err: %s" % str(err))
339
340 finally:
341 cleanup_chrootenv(chrootdir, bindmounts, globalmounts)
342 if qemu_emulator:
343 os.unlink(chrootdir + qemu_emulator)