]> git.ipfire.org Git - ipfire-3.x.git/blob - src/mkinitramfs/functions
lararcyrheb-sun16 is also our default font in mkinitramfs.
[ipfire-3.x.git] / src / mkinitramfs / functions
1 #!/bin/bash
2 HEADER="\
3 ###############################################################################
4 # #
5 # IPFire.org - A linux based firewall #
6 # Copyright (C) 2009 Michael Tremer & Christian Schmidt #
7 # #
8 # This program is free software: you can redistribute it and/or modify #
9 # it under the terms of the GNU General Public License as published by #
10 # the Free Software Foundation, either version 3 of the License, or #
11 # (at your option) any later version. #
12 # #
13 # This program is distributed in the hope that it will be useful, #
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
16 # GNU General Public License for more details. #
17 # #
18 # You should have received a copy of the GNU General Public License #
19 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 # #
21 ###############################################################################
22 "
23
24 ## GLOBAL VARIABLES ARE UPPERCASE
25 ## LOCAL VARIABLES ARE LOWERCASE
26
27 VERBOSE=
28 FORCE=
29 TARGET=
30 KERNEL=$(uname -r)
31 MODULES=
32
33 TMPDIR=$(mktemp -d)
34
35 # Check if we are root
36 if [ $UID != 0 ]; then
37 error "$0 must be run as root."
38 exit 1
39 fi
40
41 # Usage
42 function usage() {
43 echo "$0 [--help] [-f] [-v] <out-initrd-image> <kernel-version>"
44 echo " [--with=<module>]"
45 echo
46 echo "example: $0 /boot/myinitramfs.img \`uname -r\`"
47 }
48
49 # Setting verbose mode
50 function set_verbose() {
51 case "$1" in
52 yes|y|on|1)
53 VERBOSE=-v
54 ;;
55 no|n|off|0)
56 VERBOSE=
57 ;;
58 esac
59 }
60
61 # Returns if verbose is on or not
62 function is_verbose() {
63 [ -n "$VERBOSE" ] && return 0
64 return 1
65 }
66
67 # Like is_verbose but prints
68 # the content of $VERBOSE
69 function get_verbose() {
70 echo "$VERBOSE"
71 is_verbose
72 }
73
74 # Prints text if verbose is on
75 function vecho() {
76 is_verbose && echo "$@"
77 }
78
79 # Prints error if verbose is on
80 function error() {
81 vecho "$@" >&2
82 }
83
84 function compress() {
85 cd - >/dev/null
86 IMAGE=$(mktemp)
87 vecho "[TASK] Compressing image $TARGET..."
88 (cd $TMPDIR && find . | cpio -H newc --quiet -o >| $IMAGE) || exit 1
89 gzip -c9 $IMAGE > $TARGET
90 }
91
92 function findone() {
93 find "$@" | awk '{ print $1; exit; }'
94 }
95
96 function findall() {
97 find "$@"
98 }
99
100 qpushd() {
101 pushd "$1" >/dev/null 2>&1
102 }
103
104 qpopd() {
105 popd >/dev/null 2>&1
106 }
107
108 function read_link() {
109 READLINK=$(readlink $1)
110 if grep -q "^/" <<< $READLINK; then
111 echo $READLINK
112 else
113 echo "$(dirname $1)/$READLINK"
114 fi
115 }
116
117 function get_dso_deps() {
118 local bin="$1"
119 shift
120
121 declare -a FILES
122 declare -a NAMES
123
124 local LDSO="/lib/ld-linux.so.2"
125
126 declare -i n=0
127 while read NAME I0 FILE ADDR I1 ; do
128 [ "$FILE" == "not" ] && FILE="$FILE $ADDR"
129 [ "$NAME" == "not" ] && NAME="$NAME $I0"
130 NAMES[$n]="$NAME"
131 FILES[$n]="$FILE"
132 let n++
133 done << EOF
134 $(LD_TRACE_PRELINKING=1 LD_WARN= LD_TRACE_LOADED_OBJECTS=1 \
135 $LDSO $bin 2>/dev/null)
136 EOF
137
138 [ ${#FILES[*]} -eq 0 ] && return 1
139
140 # we don't want the name of the binary in the list
141 if [ "${FILES[0]}" == "$bin" ]; then
142 FILES[0]=""
143 NAMES[0]=""
144 [ ${#FILES[*]} -eq 1 ] && return 1
145 fi
146
147 declare -i n=0
148 while [ $n -lt ${#FILES[*]} ]; do
149 local FILE="${FILES[$n]}"
150 local NAME="${NAMES[$n]}"
151 if [ "$FILE" == "not found" -o "$NAME" == "not found" ]; then
152 cat 1>&2 <<EOF
153 There are missing files on your system. The dynamic object $bin
154 requires ${NAMES[$n]} n order to properly function. mkinitramfs cannot continue.
155 EOF
156 return 1
157 fi
158 case "$FILE" in
159 /lib*)
160 TLIBDIR=`echo "$FILE" | sed 's,\(/lib[^/]*\)/.*$,\1,'`
161 BASE=`basename "$FILE"`
162 if [ -f "$TLIBDIR/$BASE" ]; then
163 FILE="$TLIBDIR/$BASE"
164 fi
165 FILES[$n]="$FILE"
166 ;;
167 esac
168 let n++
169 done
170 echo "${FILES[@]}"
171 }
172
173 function install() {
174 local file dest
175 for file in $@; do
176 local msg="[FILE]"
177
178 ## Check if this is an absolute path
179 if [ "$(basename $file)" = "$file" ]; then
180 file=$(which $file)
181 fi
182
183 # Destination file
184 dest="$TMPDIR$file"
185 [ -e "$dest" ] && return 0
186
187 mkdir -p "$(dirname $dest)" 2>/dev/null
188
189 local old_indent=$INDENT
190 INDENT=" $INDENT"
191
192 [ "${file%%.ko}" != "${file}" ] && msg="[KMOD]"
193 [ -L "$file" ] && msg="[SYML]"
194 #vecho "$msg$INDENT$file -> $dest"
195 vecho "$msg$INDENT$file"
196
197 # Check if $file is a symlink
198 if [ -L "$file" ]; then
199 install $(read_link $file)
200 fi
201
202 if [ "${file%%.ko}" != "${file}" ]; then
203 for i in $(moduledep $file); do
204 [ "$(locatemodule $i)" = "$file" ] && continue
205 installmodule $i
206 done
207 fi
208
209 cp -ld $file $dest
210
211 # Check if file is a script file
212 if [ "$(dd if=$file bs=2 count=1 2>/dev/null)" = "#!" ]; then
213 install $(head -n 1 $file | cut -c3-)
214 # Check if file is a kernel module
215 elif [ "${file%%.ko}" != "${file}" ]; then
216 local firmware
217 for firmware in $(modinfo -F firmware $file 2>/dev/null); do
218 firmware="/lib/firmware/$firmware"
219 [ -e "$firmware" ] && install $firmware
220 done
221 else
222 for dep in $(get_dso_deps "$file"); do
223 install $dep
224 done
225 fi
226
227 INDENT=$old_indent
228 done
229 }
230
231 # find module dependencies
232 function moduledep() {
233 local deps mpargs
234 if [ "$1" == "--ignore-install" ]; then
235 mpargs="$mpargs --ignore-install"
236 shift
237 fi
238 local module=$(basename ${1%%.ko})
239 modprobe $mpargs --set-version $KERNEL --show-depends $module 2>/dev/null | \
240 awk '/^insmod / { print gensub(".*/","","g",$2) }' | \
241 while read foo; do \
242 [ "${foo%%.ko}" != "$1" ] && \
243 echo -n "${foo%%.ko} "; \
244 done
245 }
246
247 # XXX May be, we can drop this...
248 # This loops to make sure it resolves dependencies of dependencies of...
249 function resolvemoduledeps () {
250 local dep
251 local deps
252 local module
253 local newmodules
254 local modules=$@
255
256 before=0; after=1
257 while [ $before != $after ]; do
258 newmodules=
259 before=$(wc -c <<< $modules)
260 for module in $modules; do
261 deps=$(moduledep $module)
262 is_verbose && echo "Module $module depends on: $deps"
263 newmodules="$newmodules $module $deps"
264 done
265 modules=$(for i in $newmodules; do echo $i; done | sort -u)
266 after=$(wc -c <<< $modules)
267 done
268 echo $modules
269 }
270
271 function locatemodule() {
272 local mpargs=""
273 if [ "$1" == "--ignore-install" ]; then
274 mpargs="$mpargs --ignore-install"
275 shift
276 fi
277 local path=$(modprobe $mpargs --set-version $KERNEL --show-depends $1 2>/dev/null | \
278 awk '/^insmod / { print $2; }' | tail -n 1)
279 [ -n "$path" -a -f "$path" ] && echo $path
280 }
281
282 function installmodule() {
283 local module
284 for module in $@; do
285 module=$(locatemodule $module)
286 [ -z "$module" ] && continue
287
288 install $module
289 done
290 }
291
292 resolve_device_name() {
293 if [[ "$1" =~ ^/dev ]]; then
294 echo $1
295 else
296 findfs $1
297 fi
298 }
299
300 function finalize() {
301 qpopd
302
303 # Adding modules
304 installmodule $MODULES
305
306 # Build module deps file so we can use modprobe
307 vecho "[TASK] Running depmod..."
308 depmod -a -b "$TMPDIR" $KERNEL
309
310 # ldconfig
311 install /etc/ld.so.conf
312 [ -d "/etc/ld.so.conf.d" ] && \
313 for i in $(find /etc/ld.so.conf.d -type f); do
314 install $i
315 done
316 vecho "[TASK] Running ldconfig..."
317 ldconfig -r $TMPDIR
318
319 # Compressing
320 compress
321 rm -rf $TMPDIR 2>/dev/null
322 }
323
324 # resolve a device node to its major:minor numbers in decimal or hex
325 function get_numeric_dev() {
326 ( fmt="%d:%d"
327 if [ "$1" == "hex" ]; then
328 fmt="%x:%x"
329 fi
330 ls -lH "$2" | awk '{ sub(/,/, "", $5); printf("'"$fmt"'", $5, $6); }'
331 ) 2>/dev/null
332 }
333
334 function resolve_device_name() {
335 if [[ "$1" =~ ^/dev ]]; then
336 echo $1
337 else
338 findfs $1
339 fi
340 }
341
342 function finddevnoinsys() {
343 majmin="$1"
344 if [ -n "$majmin" ]; then
345 dev=$(for x in /sys/block/* ; do find $x/ -name dev ; done | while read device ; do \
346 echo "$majmin" | cmp -s $device && echo $device ; done)
347 if [ -n "$dev" ]; then
348 dev=${dev%%/dev}
349 dev=${dev%%/}
350 echo "$dev"
351 return 0
352 fi
353 fi
354 return 1
355 }
356
357 findblockdevinsys() {
358 devname=$(resolve_device_name "$1")
359 if [[ "$devname" =~ ^/sys/block/ ]]; then
360 echo "$devname"
361 fi
362 majmin=$(get_numeric_dev dec $devname)
363 finddevnoinsys "$majmin"
364 }
365
366 findstoragedriverinsys () {
367 while [ ! -L device ]; do
368 for slave in $(ls -d slaves/* 2>/dev/null) ; do
369 slavename=${slave##*/}
370 case " $slavestried " in
371 *" $slavename "*)
372 continue
373 ;;
374 *)
375 slavestried="$slavestried $slavename"
376 qpushd $slave
377 findstoragedriverinsys
378 qpopd
379 ;;
380 esac
381 done
382 [ "$PWD" = "/sys" ] && return
383 cd ..
384 done
385 cd $(read_link ./device)
386 if echo $PWD | grep -q /virtio-pci/ ; then
387 installmodule virtio_pci
388 fi
389 while [ "$PWD" != "/sys/devices" ]; do
390 deps=
391 if [ -f modalias ]; then
392 installmodule $(cat modalias)
393 fi
394
395 [ -z "$deps" -a -L driver/module ] && \
396 deps=$(basename $(read_link driver/module))
397 installmodule $deps
398 cd ..
399 done
400 }
401
402 function findstoragedriver() {
403 for device in $@; do
404 case " $handleddevices " in
405 *" $device "*)
406 continue ;;
407 *) handleddevices="$handleddevices $device" ;;
408 esac
409 vecho "[INFO] Looking for driver for device $device"
410 if [[ "$device" =~ ^/sys ]]; then
411 device=${device##*/}
412 fi
413 sysfs=""
414 device=$(echo "$device" | sed 's,/,!,g')
415 if [ -d /sys/block/$device/ ]; then
416 sysfs="/sys/block/$device"
417 else
418 sysfs=$(for x in /sys/block/*; do findone $x/ -type d -name $device; done)
419 fi
420 [ -z "$sysfs" ] && return
421 qpushd $sysfs
422 findstoragedriverinsys
423 qpopd
424 done
425 }
426
427 # Main
428 while [ $# -gt 0 ] ; do
429 case $1 in
430 --help)
431 usage
432 exit 0
433 ;;
434 -f|--force)
435 FORCE=yes
436 ;;
437 -v|--verbose)
438 set_verbose on
439 ;;
440 --with=*)
441 MODULES="$MODULES ${1#--with=*}"
442 ;;
443 *)
444 if [ -z "$target" ] ; then
445 target=$1
446 elif [ -z "$kernel" ] ; then
447 kernel=$1
448 else
449 echo "Unknown option or parameter \"$1\""
450 usage
451 exit 1
452 fi
453 ;;
454 esac
455
456 shift
457 done
458
459 TARGET=${target-$TARGET}
460 KERNEL=${kernel-$KERNEL}
461
462 if [ -z "$TARGET" ]; then
463 usage
464 exit 1
465 fi
466
467 [[ "$TARGET" =~ "^/" ]] && TARGET="$PWD/$TARGET"
468
469 if [ -z "$FORCE" ] && [ -e "$TARGET" ]; then
470 echo "Image $TARGET already exists. Use -f to overwrite"
471 exit 1
472 fi
473
474 # Changing to our dir, where we do our actions in
475 qpushd $TMPDIR || exit 1
476
477 # Make directory structure
478 mkdir -p bin sbin dev sys proc sysroot \
479 etc/udev/rules.d lib/udev/rules.d
480
481 # Install some essential binaries
482 install bash chmod cat cut dd dmesg env grep kbd_mode kill killall5 ln ls lsmod mkdir \
483 mknod modprobe mount mountpoint openvt pidof rm sed setfont sh sleep switchroot \
484 udevadm udevd umount /lib/udev/console_init /lib/udev/vol_id
485
486 # Install /etc/fstab
487 install /etc/fstab
488
489 # Copy modprobe.conf and friends over
490 [ -e /etc/modprobe.conf ] && install /etc/modprobe.conf
491 for f in $(find /etc/modprobe.d -type f); do
492 install $f
493 done
494
495 # terminfo bits make things work better if you fall into interactive mode
496 [ -d "/usr/lib/terminfo" ] && \
497 for f in $(find /usr/lib/terminfo -type f); do
498 install $f
499 done
500
501 # Add localization
502 install /etc/sysconfig/console /etc/inittab
503 . /etc/sysconfig/console
504 [ -z "$FONT" ] && FONT="latarcyrheb-sun16"
505 for i in /lib/kbd/consolefonts/$FONT.*; do
506 mkdir -p $TMPDIR/$(dirname $i) 2>/dev/null || true
507 cp -f $i $TMPDIR/$i
508 case "$i" in
509 *.gz)
510 gzip -fd $TMPDIR/$i
511 ;;
512 *.bz2)
513 bzip2 -fd $TMPDIR/$i
514 ;;
515 esac
516 done
517
518 cat > init <<'EOF'
519 #!/bin/sh
520
521 # Mounting directories
522 mount -t proc proc /proc
523 mount -t sysfs /sys /sys
524 mount -t tmpfs -o mode=0755 udev /dev
525
526 # Silencing kernel
527 echo > /proc/sys/kernel/printk "1 4 1 7"
528
529 # Adding important dev nodes
530 mknod /dev/console c 5 1
531 mknod /dev/null c 1 3
532 mknod /dev/kmsg c 1 11
533 mknod /dev/ptmx c 5 2
534 mknod /dev/fb c 29 0
535 mknod /dev/systty c 4 0
536
537 # XXX really we need to openvt too, in case someting changes the
538 # color palette and then changes vts on fbcon before gettys start.
539 # (yay, fbcon bugs!)
540 for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 ; do
541 mknod /dev/tty$i c 4 $i
542 done
543
544 for i in 0 1 2 3 ; do
545 mknod /dev/ttyS$i c 4 $(($i + 64))
546 done
547
548 mkdir -m 1777 /dev/shm
549 ln -s /proc/self/fd /dev/fd
550 ln -s fd/0 /dev/stdin
551 ln -s fd/1 /dev/stdout
552 ln -s fd/2 /dev/stderr
553
554 mkdir /dev/pts
555 mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts
556
557 exec sbin/real-init
558 EOF
559 chmod 755 init
560
561 # Write out real-init
562 touch sbin/real-init; chmod 755 sbin/real-init
563 cat > sbin/real-init <<'EOF'
564 #!/bin/bash
565 $HEADER
566
567 export PATH=/sbin:/bin:/usr/sbin:/usr/bin
568 export TERM=linux
569
570 INIT="/sbin/init"
571 VERBOSE=-v
572
573 READONLY=0
574 SHELL=0
575 ESHELL=0
576
577 function emergency_shell()
578 {
579 echo "Bug in initramfs /init detected. Dropping to a shell. Good luck!"
580 echo
581 bash
582 }
583 trap "emergency_shell" 0 2
584
585 # exit immediately if a command fails
586 set -e
587
588 # Setting verbose mode
589 function set_verbose() {
590 case "$1" in
591 yes|y|on|1)
592 VERBOSE=-v
593 ;;
594 no|n|off|0)
595 VERBOSE=
596 ;;
597 esac
598 }
599
600 # Returns if verbose is on or not
601 function is_verbose() {
602 [ -n "$VERBOSE" ] && return 0
603 return 1
604 }
605
606 # Like is_verbose but prints
607 # the content of $VERBOSE
608 function get_verbose() {
609 echo "$VERBOSE"
610 is_verbose
611 }
612
613 # Prints text if verbose is on
614 function vecho() {
615 if is_verbose; then echo "$@"; fi
616 }
617
618 # Prints error if verbose is on
619 function error() {
620 vecho "$@" >&2
621 }
622
623 /lib/udev/console_init tty0
624
625 # Disable hotplugging
626 echo "" > /proc/sys/kernel/hotplug
627
628 # Parse kernel commandline options
629 for o in $(cat /proc/cmdline) ; do
630 case $o in
631 init=*)
632 INIT=${o#init=}
633 ;;
634 ro)
635 READONLY=1
636 ;;
637 rw)
638 READONLY=0
639 ;;
640 quiet)
641 set_verbose no
642 ;;
643 shell)
644 SHELL=1
645 ;;
646 eshell)
647 ESHELL=1
648 ;;
649 blacklist=*)
650 echo "blacklist ${o#blacklist=}" >> /etc/modprobe.conf
651 ;;
652 *)
653 m=$(echo $o |cut -s -d . -f 1)
654 opt=$(echo $o |cut -s -d . -f 2-)
655 if [ -z "$m" -o -z "$opt" ]; then
656 continue
657 fi
658 p=$(echo $opt |cut -s -d = -f 1)
659 v=$(echo $opt |cut -s -d = -f 2-)
660 if [ -z "$p" -o -z "$v" ]; then
661 continue
662 fi
663 echo "options $m $p=$v" >> /etc/modprobe.conf
664 ;;
665 esac
666 done
667
668 vecho "kernel commandline: $(cat /proc/cmdline)"
669 EOF