mkinitrd.py (5897B) - raw
1 #!/usr/bin/env python
2
3 import os
4 import sys
5 import tempfile
6 import shutil
7 import re
8 import subprocess
9 from glob import glob
10
11 def check_output(*args, **kwargs):
12 p = subprocess.Popen(*args, stdout=subprocess.PIPE, **kwargs)
13 out = p.communicate()[0]
14 if p.returncode != 0:
15 raise RuntimeError('Command %r exited with return code %r'
16 % (args[0], p.returncode))
17 return out
18
19 install_set = dict()
20
21 class Dir(object):
22 def install_to(self, dest):
23 os.mkdir(dest)
24
25 class File(object):
26 def __init__(self, path):
27 self.path = path
28 def install_to(self, dest):
29 shutil.copy2(self.path, dest)
30
31 class Symlink(object):
32 def __init__(self, target):
33 self.target = target
34 def install_to(self, dest):
35 os.symlink(self.target, dest)
36
37 def install_dir(path):
38 install_set[path] = Dir()
39
40 def install_config(path):
41 install_set[path] = File(path)
42
43 def install_binary(path):
44 install_set[path] = File(path)
45 for line in check_output(['ldd', path]).splitlines():
46 if re.match(r'\s*linux-vdso\.so', line):
47 continue
48 m = re.match(r'\s*(\S+) => (\S+)\s+\(0x', line)
49 if m:
50 lib = m.group(2)
51 else:
52 m = re.match(r'\s*(\S+)\s+\(0x', line)
53 lib = m.group(1)
54 install_set[os.path.realpath(lib)] = File(lib)
55 if os.path.islink(lib):
56 install_set[os.path.join(os.path.realpath(os.path.dirname(lib)), os.path.basename(lib))] \
57 = Symlink(os.readlink(lib))
58
59 def install_symlink(path, target):
60 install_set[path] = Symlink(target)
61
62 def install_tree(path):
63 for dirpath, dirnames, filenames in os.walk(path):
64 install_set[dirpath] = Dir()
65 for filename in filenames:
66 path = os.path.join(dirpath, filename)
67 install_set[path] = File(path)
68
69 def main():
70 install_dir('/newroot')
71 install_dir('/sys')
72 install_dir('/proc')
73 install_dir('/dev')
74 install_dir('/run')
75 install_symlink('/lib', 'lib64')
76 install_binary('/bin/bash')
77 install_binary('/sbin/udevd')
78 install_binary('/sbin/udevadm')
79 install_config('/etc/mdadm.conf')
80 install_binary('/sbin/mdadm')
81 for binary in glob('/sbin/fsck*'):
82 install_binary(binary)
83 install_binary('/sbin/dmsetup')
84 install_binary('/sbin/blkid')
85 install_binary('/sbin/lvm')
86 for program in ['lvchange', 'lvconvert', 'lvcreate', 'lvdisplay',
87 'lvextend', 'lvmchange', 'lvmdiskscan', 'lvmsadc', 'lvmsar', 'lvreduce',
88 'lvremove', 'lvrename', 'lvresize', 'lvs', 'lvscan', 'pvchange', 'pvck',
89 'pvcreate', 'pvdisplay', 'pvmove', 'pvremove', 'pvresize', 'pvs', 'pvscan',
90 'vgcfgbackup', 'vgcfgrestore', 'vgchange', 'vgck', 'vgconvert', 'vgcreate',
91 'vgdisplay', 'vgexport', 'vgextend', 'vgimport', 'vgmerge', 'vgmknodes',
92 'vgreduce', 'vgremove', 'vgrename', 'vgs', 'vgscan', 'vgsplit']:
93 install_symlink('/sbin/%s' % program, 'lvm')
94 install_binary('/usr/bin/cat')
95 install_binary('/usr/bin/ls')
96 install_binary('/usr/bin/ln')
97 install_binary('/usr/bin/ed')
98 install_binary('/usr/bin/less')
99 install_binary('/usr/bin/mkdir')
100 install_binary('/bin/pidof')
101 install_binary('/bin/mount')
102 install_binary('/bin/umount')
103 install_binary('/sbin/switch_root')
104 install_tree('/lib64/udev')
105 install_tree('/etc/udev')
106
107 tmpdir = tempfile.mkdtemp(prefix='mkinitrd')
108 print 'Building initrd in %s ...' % tmpdir
109 for path, obj in sorted(install_set.items(),
110 key=lambda (path, obj): (isinstance(obj, Symlink), path)):
111 print path
112 dest = tmpdir + path
113 if not os.path.exists(os.path.dirname(dest)):
114 os.makedirs(os.path.dirname(dest))
115 obj.install_to(dest)
116 open(tmpdir + '/init', 'w').write('''#!/bin/bash
117
118 export PATH=/sbin:/usr/bin:/bin
119 export EDITOR=ed
120
121 function edo() {
122 [ -e /dev/kmsg ] && echo "initrd: $*" >/dev/kmsg
123 $* 2>&1 >/dev/kmsg || ( echo "Bailing..." ; exec /bin/bash )
124 }
125
126 # mount important stuff
127 edo mount -n -t devtmpfs -o mode=0755 udev /dev
128 edo mkdir /dev/shm /dev/pts
129 edo mkdir -p -m 0755 /dev/.udev/rules.d
130 edo mount -n -t devpts -o gid=5,mode=620 devpts /dev/pts
131 edo mount -n -t tmpfs tmpfs /dev/shm
132 edo mount -n -t sysfs none /sys
133 edo mount -n -t proc none /proc
134 cmdline=$(cat /proc/cmdline)
135 edo mount -n -t tmpfs tmpfs /run
136
137 # let udev do its thing
138 edo udevd --daemon --resolve-names=never
139 edo udevadm settle
140
141 # set up some nice block devices to mount
142 edo mdadm --quiet --assemble --scan
143 edo vgchange -a y
144
145 # pass fixme in kernel args to get a shell for fixing things
146 for arg in $cmdline ; do
147 if [[ "$arg" == fixme ]] ; then
148 ( export PS1='fixme$ ' ; bash )
149 break
150 fi
151 done
152
153 # the important bit: mount root, and /usr if defined
154 root_mounted=""
155 for arg in $cmdline ; do
156 if [[ "$arg" == root=* ]] ; then
157 edo fsck -a "${arg:5}"
158 edo mount -n -r "${arg:5}" /newroot
159 root_mounted="true"
160 break
161 fi
162 done
163 edo [ $root_mounted ]
164 ( while read -r dev mountpoint type opts rest ; do
165 if [[ "$dev" != \#* ]] && [[ "$mountpoint" == /usr ]] ; then
166 edo fsck -a "$dev"
167 edo mount -n -r -t "$type" -o "$opts" "$dev" /newroot/usr
168 break
169 fi
170 done ) </newroot/etc/fstab
171
172 # clean up
173 edo udevadm control --exit
174 edo umount -n /dev/pts
175 edo umount -n /dev/shm
176 edo mount --move /run /newroot/run
177
178 # sanity check
179 edo [ -x /newroot/sbin/init ]
180
181 # switch to the new root
182 edo exec switch_root /newroot /sbin/init $cmdline
183 ''')
184 os.chmod(tmpdir + '/init', 0755)
185
186 image = '/boot/initramfs.img'
187 print 'Writing image to %s' % image
188 subprocess.check_call('find . | cpio --quiet -o -H newc | gzip -9',
189 stdout=open(image, 'w'), shell=True, cwd=tmpdir)
190
191 shutil.rmtree(tmpdir)
192
193 if __name__ == '__main__':
194 main()