From: Michael Tremer Date: Mon, 5 Apr 2010 14:00:41 +0000 (+0200) Subject: naoki: Experimental code to create an iso image. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c72719949eb1693e89fedbad2898d7ecbc60a76b;p=ipfire-3.x.git naoki: Experimental code to create an iso image. --- diff --git a/naoki/__init__.py b/naoki/__init__.py index bdb12de8e..35b64d468 100644 --- a/naoki/__init__.py +++ b/naoki/__init__.py @@ -41,6 +41,7 @@ class Naoki(object): "source" : self.call_source, "shell" : self.call_shell, "repository" : self.call_repository, + "generate" : self.call_generate, } return actionmap[args.action.name](args.action) @@ -123,6 +124,8 @@ class Naoki(object): self.log.error("You need to build or download a toolchain first.") continue + environ.init() + if args.shell: return environ.shell([]) @@ -308,3 +311,10 @@ Release : %(release)s for name in args.names: repo = backend.BinaryRepository(name, naoki=self) repo.build() + + def call_generate(self, args): + if not args.type in ("iso",): + return + + gen = chroot.Generator(self, arches.current, args.type) + return gen.run() diff --git a/naoki/chroot.py b/naoki/chroot.py index 0d5391c86..1941d5881 100644 --- a/naoki/chroot.py +++ b/naoki/chroot.py @@ -5,6 +5,7 @@ import grp import logging import os import random +import shutil import stat import time @@ -48,6 +49,7 @@ class Environment(object): dirs = ( CACHEDIR, CCACHEDIR, + IMAGESDIR, PACKAGESDIR, self.chrootPath("bin"), self.chrootPath("etc"), @@ -59,6 +61,7 @@ class Environment(object): self.chrootPath("tools_%s" % self.arch["name"]), self.chrootPath("usr/src/cache"), self.chrootPath("usr/src/ccache"), + self.chrootPath("usr/src/images"), self.chrootPath("usr/src/packages"), self.chrootPath("usr/src/pkgs"), self.chrootPath("usr/src/src"), @@ -145,8 +148,6 @@ class Environment(object): return env def doChroot(self, command, shell=True, *args, **kwargs): - self.init() - ret = None try: env = self.environ @@ -286,8 +287,10 @@ class Environment(object): "umount -n %s" % self.chrootPath("sys"), "umount -n %s" % self.chrootPath("usr", "src", "cache"), "umount -n %s" % self.chrootPath("usr", "src", "ccache"), + "umount -n %s" % self.chrootPath("usr", "src", "images"), "umount -n %s" % self.chrootPath("usr", "src", "packages"), "umount -n %s" % self.chrootPath("usr", "src", "pkgs"), + "umount -n %s" % self.chrootPath("usr", "src", "src"), "umount -n %s" % self.chrootPath("usr", "src", "tools"), "umount -n %s" % self.chrootPath("dev", "pts"), "umount -n %s" % self.chrootPath("dev", "shm") @@ -302,8 +305,10 @@ class Environment(object): "mount -n -t sysfs naoki_chroot_sysfs %s" % self.chrootPath("sys"), "mount -n --bind %s %s" % (CACHEDIR, self.chrootPath("usr", "src", "cache")), "mount -n --bind %s %s" % (CCACHEDIR, self.chrootPath("usr", "src", "ccache")), + "mount -n --bind %s %s" % (IMAGESDIR, self.chrootPath("usr", "src", "images")), "mount -n --bind %s %s" % (PACKAGESDIR, self.chrootPath("usr", "src", "packages")), "mount -n --bind %s %s" % (PKGSDIR, self.chrootPath("usr", "src", "pkgs")), + "mount -n --bind %s %s" % (os.path.join(BASEDIR, "src"), self.chrootPath("usr", "src", "src")), "mount -n --bind %s %s" % (TOOLSDIR, self.chrootPath("usr", "src", "tools")), ] @@ -483,3 +488,53 @@ class Toolchain(object): os.symlink(destination, link) + + +class Generator(Environment): + def __init__(self, naoki, arch, type): + self.type = type + Environment.__init__(self, naoki, arch) + + def chrootPath(self, *args): + return os.path.join(BUILDDIR, "generators", self.type, self.arch["name"], *args) + + def run(self): + self.init() + + # Extracting installer packages + util.mkdir(self.chrootPath("installer")) + + for package in self.get_packages("installer"): + package.extract(self.chrootPath("installer")) + + all_package_files = [] + for package in self.get_packages("all"): + all_package_files.extend(package.package_files) + + self.doChroot("/usr/src/tools/generator %s" % self.type, + env={"ALL_PACKAGES" : " ".join(all_package_files)}) + + def get_packages(self, type): + _packages = { + "all" : backend.get_package_names(), + "build" : [ "arping", "bash", "coreutils", "cpio", "curl", + "dhcp", "findutils", "grep", "iproute2", "iputils", "kbd", + "less", "module-init-tools", "procps", "sed", "sysvinit", + "udev", "util-linux-ng", "which", "dvdrtools", "kernel", + "squashfs-tools", "syslinux", "zlib",], + "installer" : ["kernel", "pomona", "upstart",], + } + _package_infos = backend.parse_package_info(_packages[type]) + + packages = [] + for package in backend.depsolve(_package_infos, recursive=True): + package = package.getPackage(self.naoki) + if not package in packages: + packages.append(package) + + return packages + + def extractAll(self): + # Extract required tools + for package in self.get_packages("build"): + package.extract(self.chrootPath()) diff --git a/naoki/constants.py b/naoki/constants.py index 85cd728e4..02af67d45 100644 --- a/naoki/constants.py +++ b/naoki/constants.py @@ -11,6 +11,7 @@ CACHEDIR = os.path.join(BASEDIR, "cache") CCACHEDIR = os.path.join(BASEDIR, "ccache") CONFIGDIR = os.path.join(BASEDIR, "config") DOCDIR = os.path.join(BASEDIR, "doc") +IMAGESDIR = os.path.join(BUILDDIR, "images") LOGDIR = os.path.join(BASEDIR, "logs") PKGSDIR = os.path.join(BASEDIR, "pkgs") PACKAGESDIR = os.path.join(BUILDDIR, "packages") diff --git a/naoki/terminal.py b/naoki/terminal.py index 5337962e2..b2adee03c 100644 --- a/naoki/terminal.py +++ b/naoki/terminal.py @@ -377,6 +377,13 @@ class Commandline(object): List("names", help="List of repositories"), ]), ]), + + # Generator + Parser("generate", + help="Generator command", + arguments=[ + Argument("type", help="Type of image to generate"), + ]), ]) self.parser = parser diff --git a/src/bootloader/installer.conf b/src/bootloader/installer.conf index 02e8b5c8c..210224fbd 100644 --- a/src/bootloader/installer.conf +++ b/src/bootloader/installer.conf @@ -23,11 +23,11 @@ LABEL install MENU label ^Install a new @NAME@ system MENU default KERNEL @SNAME@0 - APPEND initrd=initrd0 root=CDLABEL=@NAME@_@VERSION@ rootfstype=iso9660 vga=791 splash mode=install quiet ro + APPEND initrd=initrd0 root=CDLABEL=@NAME@_@VERSION@ rootfstype=iso9660 mode=install quiet ro label rescue MENU label ^Rescue installed @NAME@ system KERNEL @SNAME@0 - APPEND initrd=initrd0 root=CDLABEL=@NAME@_@VERSION@ rootfstype=iso9660 vga=791 splash mode=rescue quiet ro + APPEND initrd=initrd0 root=CDLABEL=@NAME@_@VERSION@ rootfstype=iso9660 mode=rescue quiet ro label local MENU label Boot from ^local drive LOCALBOOT 0xffff diff --git a/tools/generator b/tools/generator new file mode 100755 index 000000000..14ec56cba --- /dev/null +++ b/tools/generator @@ -0,0 +1,115 @@ +#!/bin/bash -x + +BOOTLOADER_DIR=/usr/src/src/bootloader +IMAGES_DIR=/usr/src/images + +MKLIVERAMFS=$(dirname ${0})/mkliveramfs + +ISO_FILENAME=${DISTRO_SNAME}-${DISTRO_VERSION}-${TARGET}.iso + +install_config() { + local src=${1} + local dest=${2} + + sed \ + -e "s/@NAME@/${DISTRO_NAME}/g" \ + -e "s/@SNAME@/${DISTRO_SNAME}/g" \ + -e "s/@VERSION@/${DISTRO_VERSION}/g" \ + -e "s/@SLOGAN@/${DISTRO_SLOGAN}/g" \ + < ${src} > ${dest} +} + +install_isolinux() { + local dest=${1} + + mkdir -p ${dest} 2>/dev/null + + local i + for i in isolinux.bin vesamenu.c32; do + cp -f /usr/share/syslinux/${i} ${dest}/ + done + + install_config ${BOOTLOADER_DIR}/installer.conf ${dest}/isolinux.cfg + cp -f ${BOOTLOADER_DIR}/{*.msg,splash.*} ${dest}/ + + install_kernel ${dest} +} + +install_kernel() { + local dest=${1} + + mkdir -p ${dest} 2>/dev/null + + local release + local releases + for release in /lib/modules/*; do + [ ! -d "${release}" ] && continue + releases="${releases} $(basename ${release})" + done + releases=$(echo ${releases}) + + # Grab first release + release=$(awk '{ print $1 }' <<<${releases}) + + if [ -z "${release}" ]; then + echo "No kernel image installed" >&2 + return 1 + fi + + cp -f /boot/${DISTRO_SNAME}kernel-${release} ${dest}/${DISTRO_SNAME}0 + + # Create liveramfs + ${MKLIVERAMFS} -v --with-net -f ${dest}/initrd0 ${release} +} + +copy_packages() { + local dest=${1} + + mkdir -p ${dest} 2>/dev/null + + for i in ${ALL_PACKAGES}; do + cp -f /usr/src/packages/${i} ${dest}/ + done +} + +make_installer() { + local dest=${1} + + mkdir -p ${dest} 2>/dev/null + + pushd /installer + + # Copy installer overlay + cp -prf /usr/src/src/install/* . + + rm -rf /installer/usr/include + + mksquashfs * ${dest}/installer.img -no-progress + + popd +} + +## main ## + +type=${1} + +case "${type}" in + iso) + mkdir /iso + cd /iso + + copy_packages packages + + make_installer /iso + install_isolinux /iso/isolinux + + cd /iso && \ + mkisofs -J -r -V "${DISTRO_NAME}_${DISTRO_VERSION}" \ + -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \ + -boot-load-size 4 -boot-info-table . > ${IMAGES_DIR}/${ISO_FILENAME} + ;; + + "") + exit 1 + ;; +esac diff --git a/tools/mkliveramfs b/tools/mkliveramfs new file mode 100755 index 000000000..4a1a80aa3 --- /dev/null +++ b/tools/mkliveramfs @@ -0,0 +1,1213 @@ +#!/bin/bash +HEADER="\ +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2009 Michael Tremer & Christian Schmidt # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### +" + +## GLOBAL VARIABLES ARE UPPERCASE +## LOCAL VARIABLES ARE LOWERCASE + +VERBOSE= +FORCE= +TARGET= +KERNEL=$(uname -r) +MODULES= + +TMPDIR=$(mktemp -d) + +# Check if we are root +if [ $UID != 0 ]; then + error "$0 must be run as root." + exit 1 +fi + +# Usage +function usage() { + echo "$0 [--help] [-f] [-v] " + echo " [--with=]" + echo + echo "example: $0 /boot/myinitramfs.img \`uname -r\`" +} + +# Setting verbose mode +function set_verbose() { + case "$1" in + yes|y|on|1) + VERBOSE=-v + ;; + no|n|off|0) + VERBOSE= + ;; + esac +} + +# Returns if verbose is on or not +function is_verbose() { + [ -n "$VERBOSE" ] && return 0 + return 1 +} + +# Like is_verbose but prints +# the content of $VERBOSE +function get_verbose() { + echo "$VERBOSE" + is_verbose +} + +# Prints text if verbose is on +function vecho() { + is_verbose && echo "$@" +} + +# Prints error if verbose is on +function error() { + vecho "$@" >&2 +} + +function compress() { + cd - >/dev/null + IMAGE=$(mktemp) + vecho "[TASK] Compressing image $TARGET..." + (cd $TMPDIR && find . | cpio -H newc --quiet -o >| $IMAGE) || exit 1 + gzip -c9 $IMAGE > $TARGET +} + +function findone() { + find "$@" | awk '{ print $1; exit; }' +} + +function findall() { + find "$@" +} + +qpushd() { + pushd "$1" >/dev/null 2>&1 +} + +qpopd() { + popd >/dev/null 2>&1 +} + +function read_link() { + READLINK=$(readlink $1) + if grep -q "^/" <<< $READLINK; then + echo $READLINK + else + echo "$(dirname $1)/$READLINK" + fi +} + +function get_dso_deps() { + local bin="$1" + shift + + declare -a FILES + declare -a NAMES + + local LDSO="/lib/ld-linux.so.2" + + declare -i n=0 + while read NAME I0 FILE ADDR I1 ; do + [ "$FILE" == "not" ] && FILE="$FILE $ADDR" + [ "$NAME" == "not" ] && NAME="$NAME $I0" + NAMES[$n]="$NAME" + FILES[$n]="$FILE" + let n++ + done << EOF + $(LD_TRACE_PRELINKING=1 LD_WARN= LD_TRACE_LOADED_OBJECTS=1 \ + $LDSO $bin 2>/dev/null) +EOF + + [ ${#FILES[*]} -eq 0 ] && return 1 + + # we don't want the name of the binary in the list + if [ "${FILES[0]}" == "$bin" ]; then + FILES[0]="" + NAMES[0]="" + [ ${#FILES[*]} -eq 1 ] && return 1 + fi + + declare -i n=0 + while [ $n -lt ${#FILES[*]} ]; do + local FILE="${FILES[$n]}" + local NAME="${NAMES[$n]}" + if [ "$FILE" == "not found" -o "$NAME" == "not found" ]; then + cat 1>&2 </dev/null + + local old_indent=$INDENT + INDENT=" $INDENT" + + [ "${file%%.ko}" != "${file}" ] && msg="[KMOD]" + [ -L "$file" ] && msg="[SYML]" + #vecho "$msg$INDENT$file -> $dest" + vecho "$msg$INDENT$file" + + # Check if $file is a symlink + if [ -L "$file" ]; then + install $(read_link $file) + fi + + if [ "${file%%.ko}" != "${file}" ]; then + for i in $(moduledep $file); do + [ "$(locatemodule $i)" = "$file" ] && continue + installmodule $i + done + fi + + cp -ld $file $dest + + # Check if file is a script file + if [ "$(dd if=$file bs=2 count=1 2>/dev/null)" = "#!" ]; then + install $(head -n 1 $file | cut -c3-) + # Check if file is a kernel module + elif [ "${file%%.ko}" != "${file}" ]; then + local firmware + for firmware in $(modinfo -F firmware $file 2>/dev/null); do + firmware="/lib/firmware/$firmware" + [ -e "$firmware" ] && install $firmware + done + else + for dep in $(get_dso_deps "$file"); do + install $dep + done + fi + + INDENT=$old_indent + done +} + +# find module dependencies +function moduledep() { + local deps mpargs + if [ "$1" == "--ignore-install" ]; then + mpargs="$mpargs --ignore-install" + shift + fi + local module=$(basename ${1%%.ko}) + modprobe $mpargs --set-version $KERNEL --show-depends $module 2>/dev/null | \ + awk '/^insmod / { print gensub(".*/","","g",$2) }' | \ + while read foo; do \ + [ "${foo%%.ko}" != "$1" ] && \ + echo -n "${foo%%.ko} "; \ + done +} + +# XXX May be, we can drop this... +# This loops to make sure it resolves dependencies of dependencies of... +function resolvemoduledeps () { + local dep + local deps + local module + local newmodules + local modules=$@ + + before=0; after=1 + while [ $before != $after ]; do + newmodules= + before=$(wc -c <<< $modules) + for module in $modules; do + deps=$(moduledep $module) + is_verbose && echo "Module $module depends on: $deps" + newmodules="$newmodules $module $deps" + done + modules=$(for i in $newmodules; do echo $i; done | sort -u) + after=$(wc -c <<< $modules) + done + echo $modules +} + +function locatemodule() { + local mpargs="" + if [ "$1" == "--ignore-install" ]; then + mpargs="$mpargs --ignore-install" + shift + fi + local path=$(modprobe $mpargs --set-version $KERNEL --show-depends $1 2>/dev/null | \ + awk '/^insmod / { print $2; }' | tail -n 1) + [ -n "$path" -a -f "$path" ] && echo $path +} + +function installmodule() { + local module + local load + for module in $@; do + [ "$module" = "--load" ] && load=1 + module=$(locatemodule $module) + [ -z "$module" ] && continue + + install $module + done + if [ "$load" = "1" ]; then + for module in $@; do + [ "$module" = "--load" ] && continue + cat >> $TMPDIR/sbin/real-init </dev/null +} + +# resolve a device node to its major:minor numbers in decimal or hex +function get_numeric_dev() { + ( fmt="%d:%d" + if [ "$1" == "hex" ]; then + fmt="%x:%x" + fi + ls -lH "$2" | awk '{ sub(/,/, "", $5); printf("'"$fmt"'", $5, $6); }' + ) 2>/dev/null +} + +function resolve_device_name() { + if [[ "$1" =~ ^/dev ]]; then + echo $1 + else + findfs $1 + fi +} + +function finddevnoinsys() { + majmin="$1" + if [ -n "$majmin" ]; then + dev=$(for x in /sys/block/* ; do find $x/ -name dev ; done | while read device ; do \ + echo "$majmin" | cmp -s $device && echo $device ; done) + if [ -n "$dev" ]; then + dev=${dev%%/dev} + dev=${dev%%/} + echo "$dev" + return 0 + fi + fi + return 1 +} + +findblockdevinsys() { + devname=$(resolve_device_name "$1") + if [[ "$devname" =~ ^/sys/block/ ]]; then + echo "$devname" + fi + majmin=$(get_numeric_dev dec $devname) + finddevnoinsys "$majmin" +} + +findstoragedriverinsys () { + while [ ! -L device ]; do + for slave in $(ls -d slaves/* 2>/dev/null) ; do + slavename=${slave##*/} + case " $slavestried " in + *" $slavename "*) + continue + ;; + *) + slavestried="$slavestried $slavename" + qpushd $slave + findstoragedriverinsys + qpopd + ;; + esac + done + [ "$PWD" = "/sys" ] && return + cd .. + done + cd $(read_link ./device) + if echo $PWD | grep -q /virtio-pci/ ; then + installmodule --load virtio_pci + fi + while [ "$PWD" != "/sys/devices" ]; do + deps= + if [ -f modalias ]; then + installmodule --load $(cat modalias) + fi + + [ -z "$deps" -a -L driver/module ] && \ + deps=$(basename $(read_link driver/module)) + installmodule --load $deps + cd .. + done +} + +function findstoragedriver() { + for device in $@; do + case " $handleddevices " in + *" $device "*) + continue ;; + *) handleddevices="$handleddevices $device" ;; + esac + vecho "[INFO] Looking for driver for device $device" + if [[ "$device" =~ ^/sys ]]; then + device=${device##*/} + fi + sysfs="" + device=$(echo "$device" | sed 's,/,!,g') + if [ -d /sys/block/$device/ ]; then + sysfs="/sys/block/$device" + else + sysfs=$(for x in /sys/block/*; do findone $x/ -type d -name $device; done) + fi + [ -z "$sysfs" ] && return + qpushd $sysfs + findstoragedriverinsys + qpopd + done +} + +# Main +while [ $# -gt 0 ] ; do + case $1 in + --help) + usage + exit 0 + ;; + -f|--force) + FORCE=yes + ;; + -v|--verbose) + set_verbose on + ;; + --with=*) + MODULES="$MODULES ${1#--with=*}" + ;; + --with-net) + WITH_NET=1 + ;; + *) + if [ -z "$target" ] ; then + target=$1 + elif [ -z "$kernel" ] ; then + kernel=$1 + else + echo "Unknown option or parameter \"$1\"" + usage + exit 1 + fi + ;; + esac + + shift +done + +TARGET=${target-$TARGET} +KERNEL=${kernel-$KERNEL} + +if [ -z "$TARGET" ]; then + usage + exit 1 +fi + +[[ "$TARGET" =~ "^/" ]] && TARGET="$PWD/$TARGET" + +if [ -z "$FORCE" ] && [ -e "$TARGET" ]; then + echo "Image $TARGET already exists. Use -f to overwrite" + exit 1 +fi + +# Changing to our dir, where we do our actions in +qpushd $TMPDIR || exit 1 + +# Make directory structure +mkdir -p bin sbin dev sys proc sysroot \ + etc/udev/rules.d lib/udev/rules.d + +# Install some essential binaries +install bash blkid chmod cat cut dmesg env find grep head ip kbd_mode \ + kill killall5 less ln \ + ls lsmod mkdir mknod modprobe mount mountpoint openvt pidof ps rm sed \ + setfont sh sleep switch_root udevadm udevd umount \ + /lib/udev/console_init + +# Copy modprobe.conf and friends over +[ -e /etc/modprobe.conf ] && install /etc/modprobe.conf +for f in $(find /etc/modprobe.d -type f); do + install $f +done + +# Install an empty fstab +touch $TMPDIR/etc/fstab + +# terminfo bits make things work better if you fall into interactive mode +[ -d "/usr/lib/terminfo" ] && \ + for f in $(find /usr/lib/terminfo -type f); do + install $f + done + +# Add localization +if [ -e "/etc/sysconfig/console" ]; then + install /etc/sysconfig/console + . /etc/sysconfig/console +fi +[ -z "$FONT" ] && FONT="LatArCyrHeb-16" +for i in /lib/kbd/consolefonts/$FONT.*; do + mkdir -p $TMPDIR/$(dirname $i) 2>/dev/null || true + cp -f $i $TMPDIR/$i + case "$i" in + *.gz) + gzip -fd $TMPDIR/$i + ;; + *.bz2) + bzip2 -fd $TMPDIR/$i + ;; + esac +done + +cat > init <<'EOF' +#!/bin/sh + +# Mounting directories +mount -t proc proc /proc +mount -t sysfs /sys /sys +mount -t tmpfs -o mode=0755 udev /dev + +# Silencing kernel +echo > /proc/sys/kernel/printk "1 4 1 7" + +# Adding important dev nodes +mknod /dev/console c 5 1 +mknod /dev/null c 1 3 +mknod /dev/kmsg c 1 11 +mknod /dev/ptmx c 5 2 +mknod /dev/fb c 29 0 +mknod /dev/systty c 4 0 + +# XXX really we need to openvt too, in case someting changes the +# color palette and then changes vts on fbcon before gettys start. +# (yay, fbcon bugs!) +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 ; do + mknod /dev/tty$i c 4 $i +done + +for i in 0 1 2 3 ; do + mknod /dev/ttyS$i c 4 $(($i + 64)) +done + +mkdir -m 1777 /dev/shm +ln -s /proc/self/fd /dev/fd +ln -s fd/0 /dev/stdin +ln -s fd/1 /dev/stdout +ln -s fd/2 /dev/stderr + +mkdir /dev/pts +mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts + +exec sbin/real-init +EOF +chmod 755 init + +# Write out real-init +touch sbin/real-init; chmod 755 sbin/real-init +cat > sbin/real-init <<'EOF' +#!/bin/bash +$HEADER + +export PATH=/sbin:/bin:/usr/sbin:/usr/bin +export TERM=linux + +init="/sbin/init" +VERBOSE=-v + +READONLY=0 +SHELL=0 +ESHELL=0 + +function emergency_shell() +{ + echo "Bug in initramfs /init detected. Dropping to a shell. Good luck!" + echo + bash +} +trap "emergency_shell" 0 2 + +# exit immediately if a command fails +set -e + +# Setting verbose mode +function set_verbose() { + case "$1" in + yes|y|on|1) + VERBOSE=-v + ;; + no|n|off|0) + VERBOSE= + ;; + esac +} + +# Returns if verbose is on or not +function is_verbose() { + [ -n "$VERBOSE" ] && return 0 + return 1 +} + +# Like is_verbose but prints +# the content of $VERBOSE +function get_verbose() { + echo "$VERBOSE" + is_verbose +} + +# Prints text if verbose is on +function vecho() { + if is_verbose; then echo "$@"; fi +} + +# Prints error if verbose is on +function error() { + vecho "$@" >&2 +} + +/lib/udev/console_init tty0 + +# Disable hotplugging +echo "" > /proc/sys/kernel/hotplug + +# Parse kernel commandline options +for o in $(cat /proc/cmdline) ; do + case $o in + init=*) + init=${o#init=} + ;; + ro) + READONLY=1 + ;; + rw) + READONLY=0 + ;; + quiet) + set_verbose no + ;; + shell) + SHELL=1 + ;; + eshell) + ESHELL=1 + ;; + blacklist=*) + echo "blacklist ${o#blacklist=}" >> /etc/modprobe.conf + ;; + *) + m=$(echo $o |cut -s -d . -f 1) + opt=$(echo $o |cut -s -d . -f 2-) + if [ -z "$m" -o -z "$opt" ]; then + continue + fi + p=$(echo $opt |cut -s -d = -f 1) + v=$(echo $opt |cut -s -d = -f 2-) + if [ -z "$p" -o -z "$v" ]; then + continue + fi + echo "options $m $p=$v" >> /etc/modprobe.conf + ;; + esac +done + +vecho "kernel commandline: $(cat /proc/cmdline)" +EOF + +if [ "${WITH_NET}" = "1" ]; then + MODULES="$MODULES af_packet" + for module in /lib/modules/$KERNEL/kernel/drivers/net/{,*/}*; do + MODULES="$MODULES $(basename ${module/.ko})" + done + + install arping curl dhclient ip ping sha1sum /etc/nsswitch.conf \ + /usr/lib/libnsl.so /usr/lib/libnss_{dns,files}.so + touch etc/hosts + mkdir -p $TMPDIR/var/lib/dhclient 2>/dev/null + + cat > sbin/dhclient-script <<'EOF' +#!/bin/sh + +PATH=/usr/sbin:/sbin:/usr/bin:/bin + +function ip_encode() { + IFS=. + + local int=0 + for field in $1; do + int=$(( $(( $int << 8 )) | $field )) + done + + echo $int + unset IFS +} + +function mask_to_cidr() { + local mask + mask=$(ip_encode $1) + local cidr + cidr=0 + local x + x=$(( 128 << 24 )) # 0x80000000 + + while [ $(( $x & $mask )) -ne 0 ]; do + [ $mask -eq $x ] && mask=0 || mask=$(( $mask << 1 )) + cidr=$(($cidr + 1)) + done + + if [ $(( $mask & 2147483647 )) -ne 0 ]; then # 2147483647 = 0x7fffffff + echo "Invalid net mask: $1" >&2 + else + echo $cidr + fi +} + +function ip_in_subnet() { + local netmask + netmask=$(_netmask $2) + [ $(( $(ip_encode $1) & $netmask)) = $(( $(ip_encode ${2%/*}) & $netmask )) ] +} + +function _netmask() { + local vlsm + vlsm=${1#*/} + [ $vlsm -eq 0 ] && echo 0 || echo $(( -1 << $(( 32 - $vlsm )) )) +} + +dhconfig() { + if [ -n "${old_ip_address}" ] && + [ ! "${old_ip_address}" = "${new_ip_address}" ]; then + # IP address changed. Bringing down the interface will delete all + # routes, and clear the ARP cache. + ip -family inet addr flush dev ${interface} >/dev/null 2>&1 + ip -family inet link set dev ${interface} down + fi + + if [ "${reason}" = "BOUND" ] || [ "${reason}" = "REBOOT" ] || + [ ! "${old_ip_address}" = "${new_ip_address}" ] || + [ ! "${old_subnet_mask}" = "${new_subnet_mask}" ] || + [ ! "${old_network_number}" = "${new_network_number}" ] || + [ ! "${old_broadcast_address}" = "${new_broadcast_address}" ] || + [ ! "${old_routers}" = "${new_routers}" ] || + [ ! "${old_interface_mtu}" = "${new_interface_mtu}" ]; then + ip -family inet addr add ${new_ip_address}/${new_prefix} \ + broadcast ${new_broadcast_address} dev ${interface} + + if [ -n "${new_interface_mtu}" ]; then + ip link set ${interface} mtu ${new_interface_mtu} + fi + + for router in ${new_routers}; do + if ! ip_in_subnet ${router} ${new_ip_address}/${new_prefix}; then + continue + fi + ip route replace default via ${router} + break + done + fi + + if [ "${reason}" = "RENEW" ] && \ + [ "${new_domain_name}" = "${old_domain_name}" ] && \ + [ "${new_domain_name_servers}" = "${old_domain_name_servers}" ]; then + return + fi + + if [ -n "${new_domain_name}" ] || [ -n "${new_domain_name_servers}" ] || \ + [ -n "${new_domain_search}" ]; then + + echo "; generated by $0" > /etc/resolv.conf + + if [ -n "${new_domain_search}" ]; then + echo "search ${new_domain_search//\\032/ }" >> /etc/resolv.conf + elif [ -n "${new_domain_name}" ]; then + echo "search ${new_domain_name//\\032/ }" >> /etc/resolv.conf + fi + + for nameserver in ${new_domain_name_servers} ; do + echo "nameserver ${nameserver}" >> /etc/resolv.conf + done + fi + + if [ -n "${new_host_name}" ]; then + hostname ${new_host_name} + fi +} + +new_prefix=$(mask_to_cidr ${new_subnet_mask}) + +case "${reason}" in + MEDIUM) + exit 0 + ;; + + PREINIT) + ip -family inet addr flush dev ${interface} >/dev/null 2>&1 + ip -family inet link set ${interface} up + exit 0 + ;; + + ARPCHECK|ARPSEND) + if [ -z "${new_ip_address}" ] || [ -z "${interface}" ] || \ + arping -q -f -c 2 -w 3 -D -I ${interface} ${new_ip_address}; then + exit 0 + else + exit 1 + fi + ;; + + BOUND|RENEW|REBIND|REBOOT) + dhconfig + exit 0 + ;; + + EXPIRE|FAIL|RELEASE|STOP) + if [ -n "${old_ip_address}" ]; then + # Shut down interface, which will delete routes and clear arp cache. + ip -family inet addr flush dev ${interface} >/dev/null 2>&1 + ip -family inet link set ${interface} down + fi + exit 0 + ;; + + *) + echo "Unhandled state: ${reason}" >&2 + exit 1 + ;; +esac + +EOF + chmod 755 sbin/dhclient-script +fi + +# Modules needed by the live system +MODULES="$MODULES aufs squashfs loop vfat ehci-hcd ohci-hcd uhci-hcd usb-storage" + +# Add all storage modules +for module in /lib/modules/$KERNEL/kernel/drivers/{ata,message/fusion,pcmcia,scsi{,/*}}/*; do + MODULES="$MODULES $(basename ${module/.ko})" +done + +# Creating folders +mkdir -p mnt/{source,tmpfs,overlayfs,squashfs} + +cat >> sbin/real-init <<'EOF' + +netdevice=eth0 + +# Users can override rootfs target on the kernel commandline +for o in $(cat /proc/cmdline); do + case $o in + root=*) + root=${o#root=} + ;; + rootflags=*) + rootflags=${o#rootflags=} + ;; + rootfstype=*) + rootfstype=${o#rootfstype=} + ;; + net=*) + net=${o#net=} + ;; + netdevice=*) + netdevice=${o#netdevice=} + ;; + gateway=*) + gateway=${o#gateway=} + ;; + dns=*) + dns="$dns ${o#dns=}" + ;; + esac +done + +# generate udev rules to generate /dev/root symlink +if [ -z $root ] ; then + root=/dev/something +else + case $root in + /dev/disk/by-label/*) + type="block" + LABEL=${root#/dev/disk/by-label/} + echo "SUBSYSTEM==\"block\", PROGRAM=\"/sbin/blkid -s LABEL -o value %N\", RESULT==\"$LABEL\", SYMLINK+=\"root\"" > /etc/udev/rules.d/00-label.rules + if is_verbose; then + echo "Added udev rule 00-label.rules:" + cat /etc/udev/rules.d/00-label.rules + fi + type="block" + ;; + CDLABEL=*) + CDLABEL=${root#CDLABEL=} + echo "KERNEL==\"hd[a-z]\", BUS==\"ide\", SYSFS{removable}==\"1\", ATTRS{media}==\"cdrom\", PROGRAM=\"/sbin/blkid -s LABEL -o value %N\", RESULT==\"$CDLABEL\", SYMLINK+=\"root\"" > /etc/udev/rules.d/00-cdlabel.rules + echo "KERNEL==\"sr[0-9]\", PROGRAM=\"/sbin/blkid -s LABEL -o value %N\", RESULT==\"$CDLABEL\", SYMLINK+=\"root\"" >> /etc/udev/rules.d/00-cdlabel.rules + echo "KERNEL==\"scd[0-9]\", PROGRAM=\"/sbin/blkid -s LABEL -o value %N\", RESULT==\"$CDLABEL\", SYMLINK+=\"root\"" >> /etc/udev/rules.d/00-cdlabel.rules + echo "KERNEL==\"pcd[0-9]\", PROGRAM=\"/sbin/blkid -s LABEL -o value %N\", RESULT==\"$CDLABEL\", SYMLINK+=\"root\"" >> /etc/udev/rules.d/00-cdlabel.rules + if is_verbose; then + echo "Added udev rule 00-cdlabel.rules:" + cat /etc/udev/rules.d/00-cdlabel.rules + fi + type="block" + ;; + LABEL=*) + LABEL=${root#LABEL=} + echo "SUBSYSTEM==\"block\", PROGRAM=\"/sbin/blkid -s LABEL -o value %N\", RESULT==\"$LABEL\", SYMLINK+=\"root\"" > /etc/udev/rules.d/00-label.rules + if is_verbose; then + echo "Added udev rule 00-label.rules:" + cat /etc/udev/rules.d/00-label.rules + fi + type="block" + ;; + /dev/disk/by-id/*) + UUID=${root#/dev/disk/by-id/} + echo "SUBSYSTEM==\"block\", PROGRAM=\"/sbin/blkid -s UUID -o value %N\", RESULT==\"$UUID\", SYMLINK+=\"root\"" > /etc/udev/rules.d/01-uuid.rules + if is_verbose; then + echo "Added udev rule 01-uuid.rules:" + cat /etc/udev/rules.d/01-uuid.rules + fi + type="block" + ;; + UUID=*) + UUID=${root#UUID=} + echo "SUBSYSTEM==\"block\", PROGRAM=\"/sbin/blkid -s UUID -o value %N\", RESULT==\"$UUID\", SYMLINK+=\"root\"" > /etc/udev/rules.d/01-uuid.rules + if is_verbose; then + echo "Added udev rule 01-uuid.rules:" + cat /etc/udev/rules.d/01-uuid.rules + fi + type="block" + ;; + http://*|https://*|ftp*://*|ftps://*|scp://*|sftp://*) + thingtofetch="${root}" + type="download" + + # When doing http(s) transfers, compress the data + if [ "${thingtofetch:0:4}" = "http" ]; then + curloptions="--compressed" + fi + ;; + /dev/*) + ln -s $root /dev/root + type="block" + ;; + *) + thingtomount=$root + ;; + esac +fi + +if [ "$type" = "block" ]; then + thingtomount=/dev/root + +elif [ "$type" = "download" ]; then + rootflags="loop" + thingtomount=/${thingtofetch##*/} + + # If no network configuration was given, + # we'll try dhcp + if [ -z "${net}" ]; then + net="dhcp" + fi +fi + +echo "udev_log=\"error\"" >> /etc/udev/udev.conf + +# rules for loading modules +echo -n "ACTION==\"add\", SUBSYSTEM==\"?*\", ENV{MODALIAS}==\"?*\", RUN+=\"/sbin/modprobe $" >> /etc/udev/rules.d/10-modprobe.rules +echo "env{MODALIAS}\"" >> /etc/udev/rules.d/10-modprobe.rules +echo "ACTION==\"add\", SUBSYSTEM==\"scsi_device\" RUN+=\"/sbin/modprobe sg\"" >> /etc/udev/rules.d/10-modprobe.rules +echo "ACTION==\"add\", SUBSYSTEM==\"scsi_device\", SYSFS{type}==\"0|7|14\", RUN+=\"/sbin/modprobe sd_mod\"" >> /etc/udev/rules.d/10-modprobe.rules +echo "ACTION==\"add\", SUBSYSTEM==\"scsi_device\", SYSFS{type}==\"[45]\", RUN+=\"/sbin/modprobe sr_mod\"" >> /etc/udev/rules.d/10-modprobe.rules +echo "SUBSYSTEM==\"mmc\", RUN+=\"/sbin/modprobe mmc_block\"" >> /etc/udev/rules.d/10-modprobe.rules + +if [ "${type}" = "block" ]; then + # FIXME: hack since sr_mod seems to fail to get loaded sometimes (#239657) + modprobe sr_mod +fi + +modprobe loop max_loop=16 + +vecho "Starting udevd..." +udevd --daemon + +vecho "Creating devices..." +udevadm trigger + +if [ "${type}" = "download" ]; then + # Wait 30 seconds for network to appear. + COUNTDOWN=30 + while [ "x$COUNTDOWN" != "x0" ] ; do + is_verbose && echo -n "." + + if ip link show $netdevice &>/dev/null; then + COUNTDOWN=0 + continue + fi + + COUNTDOWN=$(($COUNTDOWN - 1)) + sleep 1 + done + vecho # Blank line + + ip link set $netdevice up + if [ "$net" = "dhcp" ]; then + vecho "Getting an IP address by DHCP..." + dhclient $(get_verbose) $netdevice + else + vecho "Setting IP address $net..." + ip addr add $net dev $netdevice + fi + if [ -n "$gateway" ]; then + vecho "Setting default gateway $gateway..." + ip route add default via $gateway + fi + if [ -n "$dns" ]; then + vecho "Setting up dns server $dns..." + echo "nameserver $dns" > /etc/resolv.conf + fi + if [ -n "$gateway" ]; then + vecho "Pinging gateway $gateway..." + if ! ping -c3 -w10 $gateway &>/dev/null; then + echo "This box does not seem to have a connection" + echo "to the default gateway $gateway." + echo "You may fix this now and continue then:" + echo + bash + fi + fi +fi + +if [ "$type" = "download" ]; then + curloptions="${curloptions} --connect-timeout 30 --output ${thingtomount}" + #curloptions="${curloptions} --write-out=\"Fetched %{url_effective} in %{time_total}s (%{speed_download} bytes/s).\"" + + vecho "Running donwload..." + vecho " $thingtofetch" + + curl ${curloptions} ${thingtofetch} + + if [ "$?" != "0" ]; then + echo "An error occured when fetching ${thingtomount}:" + echo " Command: ${curl} ${thingtofetch}" + echo + echo "You now get a shell to download the file manually." + echo "Exit to continue booting." + echo + bash + fi +fi + +if [ "$SHELL" == "1" ] ; then + echo "Shell requested on kernel commandline. Exit to continue booting." + echo + bash +fi + +if [ "${type}" = "block" ]; then + # Wait 90 seconds for $thingtomount to appear. + COUNTDOWN=90 + while [ "x$COUNTDOWN" != "x0" ] ; do + is_verbose && echo -n "." + + if [ -e $thingtomount ]; then + COUNTDOWN=0 + continue + fi + # this is kind of lame, but we could have had a situation + # where we were unable to read the volume id. so trigger + # another run through the block devs + if [ "x$COUNTDOWN" = "x30" ]; then + udevadm trigger --subsystem-match=block + fi + + COUNTDOWN=$(($COUNTDOWN - 1)) + sleep 1 + done + vecho # Blank line +fi + +if [ ! -e $thingtomount ] ; then + echo + echo "--------------------------------------" + echo "WARNING: Cannot find root file system!" + echo "--------------------------------------" + echo + echo "Create symlink $thingtomount and then exit this shell to continue" + echo "the boot sequence." + echo + bash +fi + +if is_verbose; then + vecho "Mounting $thingtomount..." + ls -lh $thingtomount +fi + +if [ "x$READONLY" == "x1" ] ; then + rootflags="$rootflags,ro" +else + rootflags="$rootflags,rw" +fi + +if [ -n $rootflags ]; then + mountoptions=" -o$rootflags" +fi + +[ -n "$rootfstype" ] && rootfstype="-t $rootfstype" + +mount -n $rootfstype $mountoptions $thingtomount /mnt/source + +if [ "$?" != "0" ]; then + echo "---------------------------------" + echo "WARNING: Cannot mount rootfs!" + echo "---------------------------------" + echo + echo "Dropping to a shell. " + echo "Mount /mnt/source and exit shell to continue. Good luck!" + echo + bash +fi + +mount -n -t tmpfs none /mnt/tmpfs +aufsmountoptions="br:/mnt/tmpfs=rw" + +count=0 +for overlay in $(find /mnt/source -name "*.overlay" 2>/dev/null); do + vecho "Setting up overlay ${overlay}..." + mkdir -p /mnt/overlay${count} 2>/dev/null + mount -n -t squashfs -o loop,ro ${overlay} /mnt/overlay${count} + aufsmountoptions="$aufsmountoptions:/mnt/overlay${count}=rr" + count=$((${count} + 1)) +done + +count=0 +for sfs in $(find /mnt/source -name "*.sfs" 2>/dev/null); do + vecho "Setting up squashed fs ${sfs}..." + mount -n -t squashfs -o loop,ro ${sfs} /mnt/squashfs + aufsmountoptions="$aufsmountoptions:/mnt/squashfs=rr" + break +done + +mount -t aufs -o $aufsmountoptions none /sysroot + +for i in $(cd /mnt; ls); do + mountpoint /mnt/$i >/dev/null || continue + mkdir -p /sysroot/mnt/$i || : + mount --move /mnt/$i /sysroot/mnt/$i +done + +# Shutting down network +if [ "${type}" = "download" ]; then + if [ "${net}" = "dhcp" ]; then + kill $(pidof dhclient) + else + ip address flush dev ${netdevice} + fi + ip link set ${netdevice} down + ip route flush table main +fi + +if [ "$ESHELL" == "1" ]; then + echo "Shell requested on kernel commandline." + echo "Rootfs is mounted ro on /sysroot. Exit to continue booting." + echo + bash +fi + +if [ -x /sysroot$init ]; then + # Leave initramfs and transition to rootfs + kill $(pidof udevd) + vecho "Transfering control to $init" + + exec switch_root /sysroot ${init} + echo "---------------------------------" + echo "WARNING: Error switching to real rootfs!" + echo "---------------------------------" + echo + echo "Dropping to a shell. Good luck!" + echo + bash +else + echo "---------------------------------------------------------" + echo "WARNING: Requested $init binary does not exist on rootfs." + echo "---------------------------------------------------------" + echo + echo "Dropping to a shell. Good luck!" + echo + bash +fi + +EOF + +finalize