mkinitrd

initramfs generation script, supporting lvm2 and md
git clone https://code.djc.id.au/git/mkinitrd/

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()