source: trunk/debirf/src/debirf @ 1153

Last change on this file since 1153 was 1153, checked in by jrollins, 5 years ago

merge commit of new-source-layout branch

  • Property svn:executable set to *
File size: 16.3 KB
Line 
1#!/bin/bash -e
2
3# debirf: script to build debirf system.
4#
5# The debirf scripts were written by
6# Jameson Rollins <jrollins@fifthhorseman.net>
7# and
8# Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net>.
9#
10# They are Copyright 2007, and are all released under the GPL,
11# version 3 or later.
12
13###############################################################
14### VARIABLES
15
16CMD=$(basename $0)
17
18DEBIRF_COMMON=${DEBIRF_COMMON:-/usr/share/debirf/common}
19source "$DEBIRF_COMMON"
20
21# default label
22DEBIRF_LABEL=${DEBIRF_LABEL:-debirf}
23
24# default debirf boot method
25DEBIRF_METHOD=${DEBIRF_METHOD:-nested}
26
27# default build type
28export ROOT_BUILD=false
29
30# stages to run by default
31STAGE_ROOT=${STAGE_ROOT:-true}
32STAGE_MODULES=${STAGE_MODULES:-true}
33STAGE_INITRD=${STAGE_INITRD:-true}
34
35# warn if running as root
36ROOT_WARNING=true
37
38# location of devices.tar.gz file
39export DEVICE_ARCHIVE=${DEVICE_ARCHIVE:-/usr/share/debirf/devices.tar.gz}
40
41# list of packages to include/exclude from debootstrap
42export INCLUDE=less
43export EXCLUDE=apt-utils,bsdmainutils,cron,ed,info,logrotate,man-db,manpages,tasksel,tasksel-data,tcpd,traceroute
44
45###############################################################
46### FUNCTIONS
47
48usage() {
49    cat <<EOF
50Usage: $CMD <subcommand> [options] [args]
51Debirf system tool.
52
53subcommands:
54  make [options] PROFILE    build debirf profile (make kernel and initramfs)
55    -c|--check-vars           check variables before make
56    -n|--new                  create new root, even if old one exists
57    -o|--overwrite            debootstrap on top of old root if it exists
58    -s|--skip                 skip debootstrap step if old root exists
59    -r|--root-build           use real chroot to build instead of fakechroot
60                              (requires superuser privileges or CAP_SYS_CHROOT)
61    -w|--no-warning           skip superuser warning
62    -d|--no-initrd            do not make initramfs
63    -i|--initrd-only          just remake initramfs from existing root
64    -k|--kernel=KERNEL        install KERNEL .deb, instead of default kernel
65  enter PROFILE [CMDS]      enter shell in debirf profile root changes
66                            (optional execute CMDS in root and exit)
67  makeiso PROFILE           create a bootable ISO using the given profile
68                            (requires GRUB)
69  help                      this help
70
71EOF
72}
73
74usage_profile() {
75    cat <<EOF
76It looks like your profile is not correctly formed.  Please refer to the
77README file included with this package on how to setup a profile.
78EOF
79}
80
81failure() {
82    echo "$1" >&2
83    exit ${2:-'1'}
84}
85
86create_debootstrap() {
87    local OPTS="--include=$INCLUDE --exclude=$EXCLUDE $DEBIRF_SUITE $DEBIRF_ROOT $DEBIRF_MIRROR"
88
89    if [ "$DEBIRF_KEYRING" ] ; then
90        OPTS="--keyring='$DEBIRF_KEYRING' $OPTS"
91    fi
92
93    mkdir -p "$DEBIRF_ROOT"
94
95    if [ "$ROOT_BUILD" = 'true' ] ; then
96        eval "/usr/sbin/debootstrap $OPTS"
97    else
98        eval "fakeroot_if_needed fakechroot /usr/sbin/debootstrap --variant=fakechroot $OPTS"
99    fi
100
101    fakeroot_if_needed mv "$DEBIRF_ROOT"/var/log/bootstrap.log "$DEBIRF_BUILDD"/.bootstrap.log
102}
103
104# fix the device tree in the debirf root if fakechroot variant was
105# used with debootstrap (default non-privileged behavior)
106fix_dev() {
107    if [ -L "$DEBIRF_ROOT"/dev -o ! -d "$DEBIRF_ROOT"/dev ] ; then
108        msg "fixing debirf root dev tree..."
109
110        # remove old dev
111        fakeroot_if_needed rm -f "$DEBIRF_ROOT"/dev
112
113        # create new dev from devices archive
114        fakeroot_if_needed sh -c "cd $DEBIRF_ROOT; tar -xzf $DEVICE_ARCHIVE"
115
116        # create /dev/console
117        fakeroot_if_needed sh -c "mknod $DEBIRF_ROOT/dev/console c 5 1; chmod 0600 $DEBIRF_ROOT/dev/console"
118    fi
119}
120
121# run modules in modules directory
122run_modules() {
123    fakeroot_if_needed run-parts --verbose --exit-on-error "$DEBIRF_MODULES"
124}
125
126# pack the rootfs archive
127# takes one input argument as name of output archive file
128pack_rootfs() {
129    # need to pack archive in fakechroot/chroot so that symlinks correctly
130    # point within debirf root, instead of to host path
131    fakeroot_if_needed fakechroot chroot "$DEBIRF_ROOT" sh -c "find * | cpio --create -H newc" | gzip > "$1"
132}
133export -f pack_rootfs
134
135
136## create_initrd functions
137# stupid simple method
138create_initrd_stupid_simple() {
139    fakeroot_if_needed ln -sf /sbin/init "$DEBIRF_ROOT/init"
140    pack_rootfs "$DEBIRF_INITRD"
141}
142
143# nested cpio archives
144create_initrd_nested() {
145    local util lib
146    local NEST_ROOT="$DEBIRF_BUILDD"/nest
147
148    # make the nested root
149    rm -rf "$NEST_ROOT"
150    mkdir -p "$NEST_ROOT"/{bin,lib}
151
152    # copy needed executables into nest
153    cp -f /bin/{busybox,cpio} "$NEST_ROOT"/bin/
154    for util in awk free grep gunzip ls mkdir mount sh umount ; do
155        ln "$NEST_ROOT"/bin/busybox "$NEST_ROOT"/bin/"$util"
156    done
157    cp -f /usr/lib/klibc/bin/run-init "$NEST_ROOT"/bin/
158
159    # copy in needed libraries
160    for lib in $(ldd "$NEST_ROOT"/bin/* | egrep '*.so.[[:digit:]]+ \(0x[[:xdigit:]]{8}\)$' | sed -r 's|.*[[:space:]](/[^[[:space:]]*)[[:space:]]\(0x[[:xdigit:]]{8}\)$|\1|' | sort -u) ; do
161        # pull libraries from most basic place libraries can live
162        # (avoid arch change between build env and debirf)
163        lib=/lib/$(basename "$lib")
164        echo -e "$lib\n$(readlink -f $lib)" | cpio --pass-through --make-directories "$NEST_ROOT"/
165    done
166    cp -f /lib/klibc-* "$NEST_ROOT"/lib/
167
168    # create nest init
169    cat > "$NEST_ROOT"/init <<EOF
170#!/bin/sh
171mkdir /proc
172mount -t proc proc /proc
173if (grep -q break=top /proc/cmdline); then
174  echo "honoring break=top kernel arg"
175  /bin/sh
176fi
177mkdir /newroot
178MEMSIZE=\$(free | grep 'Mem:' | awk '{ print \$2 }')
179mount -t tmpfs -o size=\${MEMSIZE}k tmpfs /newroot
180if (grep -q break=preunpack /proc/cmdline); then
181  echo "honoring break=preunpack kernel arg"
182  /bin/sh
183fi
184cd /newroot
185echo unpacking rootfs...
186# specify /bin/cpio so that it gets used instead of the busybox builtin
187# busybox cpio returns "need to fix this" when unpacking hard links
188gunzip - < /rootfs.cgz | /bin/cpio -i
189if (grep -q break=bottom /proc/cmdline); then
190  echo "honoring break=bottom kernel arg"
191  /bin/sh
192fi
193umount /proc
194echo running /sbin/init...
195exec /bin/run-init . /sbin/init < ./dev/console > ./dev/console
196EOF
197    chmod a+x "$NEST_ROOT"/init
198
199    msg "creating rootfs.cgz..."
200    fakeroot_if_needed ln -sf /sbin/init "$DEBIRF_ROOT/init"
201    pack_rootfs "$NEST_ROOT"/rootfs.cgz
202
203    msg "creating wrapper cgz..."
204    fakeroot_if_needed sh -c "cd $NEST_ROOT && find * | cpio --create -H newc" | gzip > "$DEBIRF_INITRD"
205}
206
207# determine what the host system distro is, and set debirf build defaults
208# accordingly
209set_distro() {
210    RUN_DISTRO=$(head -1 /etc/issue | awk '{ print $1 }' | tr "[:upper:]" "[:lower:]")
211    case "$RUN_DISTRO" in
212        ubuntu)
213            DEBIRF_SUITE=${DEBIRF_SUITE:-"hoary"}
214            ;;
215        *)
216            DEBIRF_SUITE=${DEBIRF_SUITE:-"lenny"}
217            ;;
218    esac
219
220    case "$DEBIRF_SUITE" in
221        breezy|dapper|edgy|feisty|gutsy|hardy|hoary)
222            DEBIRF_DISTRO=${DEBIRF_DISTRO:-"ubuntu"}
223            ;;
224        *)
225            DEBIRF_DISTRO=${DEBIRF_DISTRO:-"debian"}
226            ;;
227    esac
228
229    DEBIRF_MIRROR=${DEBIRF_MIRROR:-"http://mirrors.kernel.org/${DEBIRF_DISTRO}"}
230    DEBIRF_KEYRING=${DEBIRF_KEYRING:-"/usr/share/keyrings/${DEBIRF_DISTRO}-archive-keyring.gpg"}
231
232    if ! [ -f "$DEBIRF_KEYRING" -a -r "$DEBIRF_KEYRING" ] ; then
233        failure "Cannot read keyring '$DEBIRF_KEYRING' for debootstrap verification."
234    fi
235}
236
237# setup profile environment
238setup_environment() {
239    # check profile
240    if [ -d "$DEBIRF_PROFILE" ] ; then
241        echo "Loading profile '$DEBIRF_PROFILE'..."
242        DEBIRF_CONF="$DEBIRF_PROFILE/debirf.conf"
243        DEBIRF_MODULES="$DEBIRF_PROFILE/modules"
244    else
245        failure "Profile '$DEBIRF_PROFILE' not found."
246    fi
247   
248    # source profile debirf.conf
249    if [ -f "$DEBIRF_CONF" ] ; then
250        source "$DEBIRF_CONF"
251    else
252        echo "Configuration file '$DEBIRF_CONF' not found."
253        usage_profile
254        exit 1
255    fi
256
257    # check modules directory
258    if [ ! -d "$DEBIRF_MODULES" ] || [ -z "$(ls "$DEBIRF_MODULES")" ] ; then
259        echo "Modules directoy '$DEBIRF_MODULES' does not exist or is empty."
260        usage_profile
261        exit 1
262    fi
263    for MODULE in $(find "$DEBIRF_MODULES") ; do
264        if [ ! -s "$MODULE" ] ; then
265            failure "Module '$MODULE' is a broken link or empty file."
266        fi
267    done
268   
269    # set/check buildd
270    DEBIRF_BUILDD=${DEBIRF_BUILDD:-"$DEBIRF_PROFILE"}
271    if [ ! -d "$DEBIRF_BUILDD" ] ; then
272        failure "Could not find build directory '$DEBIRF_BUILDD'."
273    fi
274
275    # set root directory
276    DEBIRF_ROOT="$DEBIRF_BUILDD/root"
277
278    # set fakechroot save file
279    DEBIRF_FAKEROOT_STATE="$DEBIRF_BUILDD/.fakeroot-state.${DEBIRF_LABEL}"
280
281    # set the debirf distro variables based on host distro
282    set_distro
283
284    # export all the DEBIRF_* environment variables:
285    for var in ${!DEBIRF_*}; do
286        if [ $var ] ; then
287            export $var
288        else
289            failure "Variable '$var' not properly set."
290        fi
291    done
292   
293    # check variables
294    if [ "$CHECK_VARS" ] ; then
295        echo "Debirf variables:"
296        env | /bin/grep "^DEBIRF_"
297        read -p "enter to continue: " OK
298    fi
299}
300
301# make profile
302make() {
303    # option parsing
304    TEMP=$(getopt --options -hcnosrwdik: --longoptions help,check-vars,new,overwrite,skip,root-build,no-warning,no-initrd,initrd-only,kernel: -n "$CMD" -- "$@")
305
306    if [ $? != 0 ] ; then
307        echo "Invalid options." >&2
308        usage
309        exit 1
310    fi
311   
312    # Note the quotes around `$TEMP': they are essential!
313    eval set -- "$TEMP"
314
315    while true ; do
316        case "$1" in
317            -c|--check-vars)
318                CHECK_VARS=true
319                shift 1
320                ;;
321            -n|--new)
322                WRITE_MODE=rewrite
323                shift 1
324                ;;
325            -o|--overwrite)
326                WRITE_MODE=overwrite
327                shift 1
328                ;;
329            -s|--skip)
330                WRITE_MODE=skip
331                shift 1
332                ;;
333            -r|--root-build)
334                ROOT_BUILD=true
335                shift 1
336                ;;
337            -w|--no-warning)
338                ROOT_WARNING=false
339                shift 1
340                ;;
341            -d|--no-initrd)
342                STAGE_INITRD=false
343                shift 1
344                ;;
345            -i|--initrd-only)
346                STAGE_ROOT=false
347                STAGE_MODULES=false
348                shift 1
349                ;;
350            -k|--kernel)
351                DEBIRF_KERNEL_PACKAGE="$2"
352                shift 2
353                ;;
354            --)
355                shift
356                ;;
357            *)
358                if (( $# < 1 )) ; then
359                    echo "Improper number of input arguments."
360                    usage
361                    exit 1
362                fi
363                DEBIRF_PROFILE=$(dirname "$1"/x)
364                break
365                ;;
366        esac
367    done
368   
369    if [ $(id -u) = '0' ] ; then
370        cat <<EOF
371Warning: You are running debirf as root.  There is a potential
372for improperly written modules to damage your system.
373EOF
374        if [ "$ROOT_WARNING" = 'true' ] ; then
375            read -p "Are you sure you wish to continue? [y|N]: " OK; OK=${OK:=N}
376            if [ "${OK/y/Y}" != 'Y' ] ; then
377                failure "aborting."
378            fi
379        fi
380    fi
381
382    setup_environment
383
384    if [ "$DEBIRF_KERNEL_PACKAGE" ] ; then
385        if [ -f "$DEBIRF_KERNEL_PACKAGE" ] ; then
386            echo "Using kernel package '$DEBIRF_KERNEL_PACKAGE'."
387        else
388            failure "Kernel package '$DEBIRF_KERNEL_PACKAGE' not found."
389        fi
390    fi
391   
392    ### BUILD ROOT
393    if [ "$STAGE_ROOT" = 'true' ] ; then
394        # determine write mode
395        if [ -d "$DEBIRF_ROOT" ] ; then
396            if [ -z "$WRITE_MODE" ] ; then
397                echo "Debirf root already exists.  Select one of the following:"
398                CASE1='new: delete the old root and create a new one'
399                CASE2='overwrite: leave the old root and debootstrap on top of it'
400                CASE3='skip: skip building the root and go right to installing modules'
401                CASE4='exit'
402                select CASE in "$CASE1" "$CASE2" "$CASE3" "$CASE4" ; do
403                    case "$REPLY" in
404                        1)
405                            WRITE_MODE=rewrite
406                            ;;
407                        2)
408                            WRITE_MODE=overwrite
409                            ;;
410                        3)
411                            WRITE_MODE=skip
412                            ;;
413                        *)
414                            failure "aborting."
415                            ;;
416                    esac
417                    break
418                done
419            fi
420        else
421            WRITE_MODE=new
422        fi
423        case "$WRITE_MODE" in
424            'new')
425                msg "creating debirf root..."
426                > "$DEBIRF_FAKEROOT_STATE"
427                create_debootstrap
428                ;;
429            'rewrite')
430                msg "clearing old debirf root..."
431                rm -rf "$DEBIRF_ROOT"
432                msg "creating debirf root..."
433                > "$DEBIRF_FAKEROOT_STATE"
434                create_debootstrap
435                ;;
436            'overwrite')
437                msg "overwriting old debirf root..."
438                create_debootstrap
439                ;;
440            'skip')
441                msg "skipping debootstrap..."
442                ;;
443            *)
444                failure "aborting."
445                ;;
446        esac
447       
448        # fix the dev tree if running as non-priv user (fakechroot debootstrap)
449        fix_dev
450       
451    else
452        echo "Not building root."
453    fi
454    ### END BUILD ROOT
455   
456    ### RUN MODULES
457    if [ "$STAGE_MODULES" = 'true' ] ; then
458        msg "executing modules..."
459        run_modules
460        msg "modules complete."
461    else
462        echo "Not running modules."
463    fi
464    ### END RUN MODULES
465
466    ### BUILD INITRD
467    if [ "$STAGE_INITRD" = 'true' ] ; then
468        if [ ! -d "$DEBIRF_ROOT" ] ; then
469            failure "Debirf root '$DEBIRF_ROOT' not found."
470        fi
471        # determine initrd name
472        KERNEL_VERS=$(ls -1 "$DEBIRF_ROOT/lib/modules" | head -n1)
473        DEBIRF_INITRD="${DEBIRF_BUILDD}/${DEBIRF_LABEL}_${DEBIRF_SUITE}_${KERNEL_VERS}.cgz"
474       
475        msg "creating debirf initrd ('$DEBIRF_METHOD')..."
476        create_initrd_${DEBIRF_METHOD} "$DEBIRF_INITRD"
477       
478        # final output
479        DEBIRF_KERNEL=$(ls "$DEBIRF_BUILDD" | grep "vmlinu" | grep "$KERNEL_VERS$")
480        msg "debirf initrd created."
481        if [ "$DEBIRF_BUILDD/$DEBIRF_KERNEL" ] ; then
482            msg "kernel: $DEBIRF_BUILDD/$DEBIRF_KERNEL"
483        fi
484        msg "initrd: $DEBIRF_INITRD"
485    else
486        echo "Not creating initramfs."
487    fi
488    ### END BUILD INITRD
489}
490
491# enter profile root
492enter() {
493    DEBIRF_PROFILE="$1"
494    shift
495
496    setup_environment
497
498    if [ "$1" ] ; then
499        fakeroot_if_needed debirf_exec "$@"
500    else
501        fakeroot_if_needed debirf_exec bash -i
502    fi
503}
504
505# create an ISO from the given kernel and initramfs (requires GRUB,
506# see:
507# http://www.gnu.org/software/grub/manual/html_node/Making-a-GRUB-bootable-CD-ROM.html)
508makeiso() {
509    DEBIRF_PROFILE="$1"
510    shift
511
512    setup_environment
513
514    (which genisoimage > /dev/null) || failure "genisoimage is not in your path.  Maybe you need to install it?"
515
516    [ -d "$DEBIRF_PROFILE" ] || failure "'$DEBIRF_PROFILE' does not seem to be a directory"
517
518    cd "$DEBIRF_PROFILE" || failure "Could not enter profile directory '$DEBIRF_PROFILE'."
519    local KERNEL=${KERNEL:-$(echo "vmlinu"*)}
520    local INITRAMFS=${INITRAMFS:-$(echo "debirf-"*.cgz)}
521
522    # FIXME: it would be best to pull the name of the architecture
523    # exactly instead of using shell globbing to guess at it:
524    local GRUB_ELTORITO=${GRUB_ELTORITO:-$(echo "/usr/lib/grub/"*"-pc/stage2_eltorito")}
525
526    case $(ls -l $KERNEL 2> /dev/null | wc -l) in
527        0)
528            failure "Failed to find a kernel in '$DEBIRF_PROFILE'.  Maybe you need to run 'debirf make $DEBIRF_PROFILE' first?"
529            ;;
530        1)
531            true
532            ;;
533        *)
534            failure "Multiple kernels found in '$DEBIRF_PROFILE'.  Please clear out all but the desired kernel."
535            ;;
536    esac
537    case $(ls -l $INITRAMFS 2> /dev/null | wc -l) in
538        0)
539            failure "Failed to find a single initramfs in '$DEBIRF_PROFILE'.  Maybe you need to run 'debirf make $DEBIRF_PROFILE' first?"
540            ;;
541        1)
542            true
543            ;;
544        *)
545            failure "Multiple initramfs found in '$DEBIRF_PROFILE'.  Please clear out all but the desired initramfs."
546            ;;
547    esac
548    [ -r "$GRUB_ELTORITO" ] || failure "Failed to find a grub El Torito stage2 loader at '$GRUB_ELTORITO'.  Try specifying it explicitly with the GRUB_ELTORITO environment variable"
549
550    # get rid of the "iso" directory if it exists.
551    rm -rf iso
552    mkdir -p iso/boot/grub
553
554    # use hard links to avoid massive copying time and genisoimage's
555    # warning about -f (we're almost certainly on the same filesystem):
556    ln "$KERNEL" iso/ || failure "Failed to link kernel into iso"
557    ln "$INITRAMFS" iso/ || failure "Failed to link initramfs into iso"
558
559    # this file is very likely not on the same filesystem (and it's
560    # likely to be small) so copying it makes more sense than linking:
561    cp "$GRUB_ELTORITO" iso/boot/grub/
562    cat >iso/boot/grub/menu.lst <<EOF
563serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
564terminal --timeout=10 serial console
565
566title        Debirf ($DEBIRF_LABEL) (created $(date -R))
567kernel       /$KERNEL
568initrd       /$INITRAMFS
569EOF
570
571    # determine iso name
572    KERNEL_VERS=$(ls -1 "$DEBIRF_ROOT/lib/modules" | head -n1)
573    DEBIRF_ISO="${DEBIRF_LABEL}_${DEBIRF_SUITE}_${KERNEL_VERS}.iso"
574
575    genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -o "$DEBIRF_ISO" iso
576    # do we need to clean up the iso/ directory so that this can be run again?   
577}
578
579
580###############################################################
581### MAIN
582
583COMMAND="$1"
584[ "$COMMAND" ] || failure "Type '$CMD help' for usage."
585shift
586
587case $COMMAND in
588    'make'|'m')
589        make "$@"
590        ;;
591    'makeiso'|'i')
592        makeiso "$@"
593        ;;
594    'enter'|'e')
595        enter "$@"
596        ;;
597    'help'|'h'|'?')
598        usage
599        ;;
600    *)
601        failure "Unknown command: '$COMMAND'
602Type '$CMD help' for usage."
603        ;;
604esac
Note: See TracBrowser for help on using the repository browser.