diff --git a/Makefile b/Makefile index aaa553e..5ce1962 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ sysconfdir ?= /etc/mkinitfs datarootdir ?= /usr/share datadir ?= $(datarootdir)/mkinitfs -SBIN_FILES := mkinitfs bootchartd +SBIN_FILES := mkinitfs bootchartd nlplug-findfs SHARE_FILES := initramfs-init fstab passwd group CONF_FILES := mkinitfs.conf \ features.d/ata.modules \ @@ -25,13 +25,13 @@ CONF_FILES := mkinitfs.conf \ features.d/gfs2.modules \ features.d/jfs.modules \ features.d/keymap.files \ - features.d/kms.files \ features.d/kms.modules \ features.d/lvm.files \ features.d/lvm.modules \ features.d/network.files \ features.d/network.modules \ features.d/ocfs2.modules \ + features.d/raid.files\ features.d/raid.modules \ features.d/reiserfs.modules \ features.d/scsi.modules \ @@ -41,7 +41,7 @@ CONF_FILES := mkinitfs.conf \ features.d/virtio.modules \ features.d/xfs.modules -SCRIPTS := $(SBIN_FILES) initramfs-init +SCRIPTS := mkinitfs bootchartd initramfs-init IN_FILES := $(addsuffix .in,$(SCRIPTS)) GIT_REV := $(shell test -d .git && git describe || echo exported) @@ -62,7 +62,8 @@ SED_REPLACE := -e 's:@VERSION@:$(FULL_VERSION):g' \ -e 's:@datadir@:$(datadir):g' -all: $(SCRIPTS) + +all: $(SBIN_FILES) $(SCRIPTS) clean: rm -f $(SCRIPTS) @@ -71,6 +72,26 @@ help: @echo mkinitfs $(VERSION) @echo "usage: make install [DESTDIR=]" +CFLAGS ?= -Wall -Werror -g +CFLAGS += -D_GNU_SOURCE -DDEBUG + +PKGCONF ?= pkg-config +BLKID_CFLAGS := $(shell $(PKGCONF) --cflags blkid) +BLKID_LIBS := $(shell $(PKGCONF) --libs blkid) +LIBKMOD_CFLAGS := $(shell $(PKGCONF) --cflags libkmod) +LIBKMOD_LIBS := $(shell $(PKGCONF) --libs libkmod) +CRYPTSETUP_CFLAGS := $(shell $(PKGCONF) --cflags libcryptsetup) +CRYPTSETUP_LIBS := $(shell $(PKGCONF) --libs libcryptsetup) + +CFLAGS += $(BLKID_CFLAGS) $(LIBKMOD_CFLAGS) $(CRYPTSETUP_CFLAGS) +LIBS = $(BLKID_LIBS) $(LIBKMOD_LIBS) $(CRYPTSETUP_LIBS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +nlplug-findfs: nlplug-findfs.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + .SUFFIXES: .in .in: ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ diff --git a/arg.h b/arg.h new file mode 100644 index 0000000..92a313f --- /dev/null +++ b/arg.h @@ -0,0 +1,39 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef __ARG_H__ +#define __ARG_H__ + +#define USED(x) ((void)(x)) + +#define ARGBEGIN for (argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char _argc;\ + char **_argv;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (argv[0]++, _argv = argv; argv[0][0];\ + argv[0]++) {\ + if (_argv != argv)\ + break;\ + _argc = argv[0][0];\ + switch (_argc) + +#define ARGEND }\ + USED(_argc);\ + }\ + USED(argv);\ + USED(argc); + +#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ + (argc--, argv++, argv[0])) + +#endif + diff --git a/bootchartd.in b/bootchartd.in index c4daa35..55a0883 100755 --- a/bootchartd.in +++ b/bootchartd.in @@ -46,7 +46,7 @@ do_logging() # Enable process accounting if configured if [ "$PROCESS_ACCOUNTING" = "yes" ]; then [ -e kernel_pacct ] || : > kernel_pacct - accton kernel_pacct + accton kernel_pacct > /dev/null fi # open file descriptors @@ -98,7 +98,7 @@ do_logging() i=$(($i + 1)) done - [ -e kernel_pacct ] && accton + [ -e kernel_pacct ] && accton off > /dev/null } # Stop the boot logger. The lock file is removed to force the loggers in @@ -151,19 +151,18 @@ finalize() # Package log files tar -zcf "$BOOTLOG_DEST" header $pacct *.log - rm "$LOGDIR"/* - rmdir "$LOGDIR" + rm -rf "$LOGDIR" } case "$1" in start-initfs) NEWROOT="$2" + mkdir -p "$LOGDIR" ( cleanup=true trap "not_stop_logging=false" USR1 trap "cleanup=false; not_stop_logging=false" USR2 - mkdir "$LOGDIR" cd "$LOGDIR" do_logging if $cleanup; then @@ -171,18 +170,16 @@ start-initfs) finalize fi ) & - echo $! > $LOGDIR/bootchart.pid + echo $! > "$LOGDIR"/bootchart.pid ;; stop-initfs) NEWROOT="$2" - - cd "$LOGDIR" - mkdir "$NEWROOT$LOGDIR" - cp /sbin/bootchartd $NEWROOT/sbin - PID=`cat bootchart.pid` + [ -x "$NEWROOT"/sbin/bootchartd ] || cp -a /sbin/bootchartd "$NEWROOT"/sbin + rm -rf "$NEWROOT/$LOGDIR" + PID=$(cat "$LOGDIR"/bootchart.pid) kill -USR2 $PID wait $PID - mv * "$NEWROOT$LOGDIR" + mv "$LOGDIR" "$NEWROOT" ;; start-rootfs) ( diff --git a/features.d/base.files b/features.d/base.files index 7dd36e5..23718cb 100644 --- a/features.d/base.files +++ b/features.d/base.files @@ -4,3 +4,4 @@ /sbin/apk /etc/modprobe.d/*.conf /etc/mdev.conf +/sbin/nlplug-findfs diff --git a/features.d/bootchart.files b/features.d/bootchart.files index 8adf4df..6f28294 100644 --- a/features.d/bootchart.files +++ b/features.d/bootchart.files @@ -1,3 +1,4 @@ +/sbin/bootchartd /usr/bin/ac /usr/bin/last /usr/bin/lastcomm @@ -5,4 +6,3 @@ /usr/sbin/dump-acct /usr/sbin/accton /usr/sbin/sa - diff --git a/features.d/btrfs.modules b/features.d/btrfs.modules index b5a271f..a114111 100644 --- a/features.d/btrfs.modules +++ b/features.d/btrfs.modules @@ -1,2 +1,5 @@ kernel/crypto/crc32c* +kernel/arch/*/crypto/crc32* +kernel/arch/*/crypto/crc32* +kernel/crypto/crc32* kernel/fs/btrfs diff --git a/features.d/cryptsetup.modules b/features.d/cryptsetup.modules index 9b63cd3..1469be7 100644 --- a/features.d/cryptsetup.modules +++ b/features.d/cryptsetup.modules @@ -1,2 +1,3 @@ kernel/crypto/* +kernel/arch/*/crypto/* kernel/drivers/md/dm-crypt.ko diff --git a/features.d/kms.files b/features.d/kms.files deleted file mode 100644 index 9d198b2..0000000 --- a/features.d/kms.files +++ /dev/null @@ -1 +0,0 @@ -lib/firmware/radeon/*.bin diff --git a/features.d/lvm.modules b/features.d/lvm.modules index 92f0721..3af8ef9 100644 --- a/features.d/lvm.modules +++ b/features.d/lvm.modules @@ -1 +1,2 @@ kernel/drivers/md/dm-mod.ko +kernel/drivers/md/dm-snapshot.ko diff --git a/features.d/raid.files b/features.d/raid.files new file mode 100644 index 0000000..c393dc4 --- /dev/null +++ b/features.d/raid.files @@ -0,0 +1,2 @@ +/etc/mdadm.conf +/sbin/mdadm diff --git a/features.d/virtio.modules b/features.d/virtio.modules index fa74c10..b0e6c22 100644 --- a/features.d/virtio.modules +++ b/features.d/virtio.modules @@ -1,2 +1,3 @@ kernel/drivers/block/virtio* kernel/drivers/virtio +kernel/drivers/net/virtio_net* diff --git a/features.d/xfs.modules b/features.d/xfs.modules index f577bf3..2b7fd53 100644 --- a/features.d/xfs.modules +++ b/features.d/xfs.modules @@ -1 +1,4 @@ +kernel/arch/*/crypto/crc32* +kernel/arch/*/crypto/crc32* +kernel/crypto/crc32* kernel/fs/xfs diff --git a/initramfs-init.in b/initramfs-init.in index 934fe92..cdf50b9 100755 --- a/initramfs-init.in +++ b/initramfs-init.in @@ -7,7 +7,7 @@ sysroot=/sysroot splashfile=/.splash.ctrl /bin/busybox mkdir -p /usr/bin /usr/sbin /proc /sys /dev $sysroot \ - /media/cdrom /media/usb /tmp + /media/cdrom /media/usb /tmp /run /bin/busybox --install -s # basic environment @@ -42,30 +42,6 @@ eend() { fi } -scan_drivers() { - if [ "$KOPT_autodetect" != no ] ; then - find /sys -name modalias | xargs sort -u | xargs modprobe -a 2> /dev/null - fi -} - -find_ovl() { - local mnt="$1" - - if [ -n "$APKOVL" ]; then - [ -f "$mnt/$APKOVL" ] && echo "$mnt/$APKOVL" - return - fi - - # look for apkovl's on mounted media - set -- "$mnt"/*.apkovl.tar.gz* - - if [ $# -gt 1 ] ; then - echo "ERROR: More than one apkovl file was found on $(basename $mnt). None will be read." >&2 - return 1 - fi - echo "$1" -} - retry_mount() { # usb might need some time to settle so we retry a few times for i in $(seq 0 9); do @@ -87,7 +63,6 @@ unpack_apkovl() { fi # we need openssl. let apk handle deps - find_boot_repositories > /tmp/repositories apk add --quiet --initdb --repositories-file /tmp/repositories openssl\ || return 1 @@ -110,92 +85,35 @@ unpack_apkovl() { # find mount dir for given device in an fstab # returns global MNTOPTS find_mnt() { - local search_dev="$1" fstab="$2" - MNTOPTS= - [ -r "$fstab" ] || return 1 - local dev mnt fs chk + local search_dev="$1" + local fstab="$2" case "$search_dev" in - UUID=*|LABEL=*|/dev/*);; - *) search_dev=/dev/$search_dev;; + UUID*|LABEL*) search_dev=$(findfs "$search_dev");; esac - local search_real_dev=$(resolve_dev $search_dev) + MNTOPTS= + [ -r "$fstab" ] || return 1 + local search_maj_min=$(stat -L -c '%t,%T' $search_dev) while read dev mnt fs MNTOPTS chk; do - local real_dev=$(resolve_dev $dev) - local i j - for i in "$search_dev" "$search_real_dev"; do - [ -z "$i" ] && continue - for j in "$dev" "$real_dev"; do - [ -z "$j" ] && continue - if [ "$i" = "$j" ]; then - echo "$mnt" - return - fi - done - done + case "$dev" in + UUID*|LABEL*) dev=$(findfs "$dev");; + esac + if [ -b "$dev" ]; then + local maj_min=$(stat -L -c '%t,%T' $dev) + if [ "$maj_min" = "$search_maj_min" ]; then + echo "$mnt" + return + fi + fi done < $fstab MNTOPTS= } -# Wait for usb to settle -wait_usb() { - if [ -n "$USB_DONE" ] || ! dmesg | grep '^usb-storage: waiting' >/dev/null; then - return 0 - fi - ebegin "Waiting for USB device to settle" - while ! dmesg | grep 'usb-storage: device scan complete' >/dev/null; do - sleep 1 - done - USB_DONE=yes - eend 0 -} - # add a boot service to $sysroot rc_add() { mkdir -p $sysroot/etc/runlevels/$2 ln -sf /etc/init.d/$1 $sysroot/etc/runlevels/$2/$1 } -find_ovl_blkdev() { - local fsmoddir=/lib/modules/$(uname -r)/kernel/fs - blkid | while read line; do - mod= - UUID= - TYPE= - dev=${line%%: *} - eval ${line#$dev:} - for i in $fsmoddir/$TYPE.ko $fsmoddir/*/$TYPE.ko; do - [ -f $fsmoddir/*/$TYPE.ko ] && mod=$i && break - done - [ -n "$mod" ] || continue - mnt=/media/${UUID:-${dev##*/}} - mkdir -p "$mnt" - mount -o ro -t "$TYPE" "$dev" "$mnt" 2>/dev/null || continue - ovl=$(find_ovl "$mnt") - if [ -f "$ovl" ]; then - echo "$ovl" - break - fi - umount "$mnt" - done -} - -# we have issues with some slow usb 1 hosts so we add 1 second delay -# with possibility to increase delay at boot prompt with usbdelay= -find_ovl_delayed_usb() { - local n i - # look for apkovl - for n in $(seq 0 ${KOPT_usbdelay:-1}); do - # wait for usb to settle if needed - wait_usb - ovl=$(find_ovl_blkdev) - if [ -f "$ovl" ]; then - ovl_unmount="${ovl%/*}" - return - fi - sleep 1 - done -} - setup_inittab_console(){ while [ $# -gt 0 ]; do local tty=${1%,*} @@ -225,44 +143,6 @@ setup_inittab_console(){ done } -start_raid() { - local n= i= - case "$KOPT_root" in - /dev/md*) n=${KOPT_root#/dev/md} ;; - esac - case "$KOPT_autoraid" in - [0-9]*) n="$n $(echo $KOPT_autoraid | tr ',' ' ')" ;; - esac - # if kernel can autostart the raid he will - for i in $n; do - mknod /dev/md$i b 9 $i - raidautorun /dev/md$i - done - # kernel cannot autostart newer versions of mdadm metadata - # so we also check if mdadm binary is there - if [ -x /sbin/mdadm ]; then - mdadm --assemble --scan - fi -} - -# start cryptsetup if exists -start_cryptsetup() { - [ -x /sbin/cryptsetup ] || return - modprobe dm-crypt - if [ -n "$KOPT_cryptroot" ]; then - modprobe dm-crypt - cryptsetup luksOpen $(resolve_dev "$KOPT_cryptroot") "$KOPT_cryptdm" - fi -} - -# start lvm if exists -start_lvm() { - [ -x /sbin/lvm ] || return - modprobe dm-mod - lvm vgscan --mknodes --ignorelockingfailure >/dev/null 2>&1 - lvm vgchange --ignorelockingfailure -a y >/dev/null 2>&1 -} - # determine the default interface to use if ip=dhcp is set # uses the first "eth" interface. ip_choose_if() { @@ -332,10 +212,6 @@ configure_ip() { ip_set "$device" "$client_ip" "$netmask" "$gw_ip" eend $? fi - MAC_ADDRESS=$(cat /sys/class/net/$device/address) - MACHINE_UUID=$(cat /sys/class/dmi/id/product_uuid) - OVL_DEV="${OVL_DEV/{MAC\}/$MAC_ADDRESS}" - OVL_DEV="${OVL_DEV/{UUID\}/$MACHINE_UUID}" } # resolve an uuid or symlink to the real device @@ -346,37 +222,27 @@ resolve_dev() { esac } -# relocate ALPINE_MNT according given fstab -relocate_alpine_mnt() { - local fstab="$1" - local mnt=$(find_mnt $ALPINE_DEV $fstab) - if [ -n "$mnt" ] && [ "$ALPINE_MNT" != "$mnt" ]; then - mkdir -p "$mnt" - mount -o move $ALPINE_MNT $mnt - ALPINE_MNT=$mnt +# relocate mountpoint according given fstab +relocate_mount() { + local dir="${1}" + local fstab="$2" + local dev=$(df -P "$dir" | tail -1 | awk '{print $1}') + local mnt=$(find_mnt $dev $fstab) + if [ -n "$mnt" ]; then + local oldmnt=$(awk -v d=$dev '$1==d {print $2}' /proc/mounts) + if [ "$oldmnt" != "$mnt" ]; then + mkdir -p "$mnt" + mount -o move "$oldmnt" "$mnt" + fi fi } -# detect filesystem type on given device/UUID -find_fs_type() { - local dev=$(findfs $1) - local i= - for i in $(blkid $dev); do - case $i in - TYPE=*) eval "$i" - echo $TYPE - return - ;; - esac - done -} - # find the dirs under ALPINE_MNT that are boot repositories find_boot_repositories() { if [ -n "$ALPINE_REPO" ]; then echo "$ALPINE_REPO" else - find $ALPINE_MNT -name .boot_repository -type f -maxdepth 3 \ + find /media/* -name .boot_repository -type f -maxdepth 3 \ | sed 's:/.boot_repository$::' fi } @@ -438,63 +304,14 @@ if [ "$KOPT_dma" = no ]; then modprobe libata dma=0 fi -ALPINE_DEV=${KOPT_alpine_dev%%:*} -ALPINE_DEV_FS=${KOPT_alpine_dev##*:} -if [ "$ALPINE_DEV_FS" = "$ALPINE_DEV" ]; then - unset ALPINE_DEV_FS -fi - -# /dev/blah:ext3 -if [ -n "$KOPT_ovl_dev" ] ; then - OVL_DEV=${KOPT_ovl_dev%%:*} - OVL_DEV_FS=${KOPT_ovl_dev##*:} - if [ "$OVL_DEV_FS" = "$OVL_DEV" ]; then - unset OVL_DEV_FS - fi -fi - -# http://.../blah.apkovl.tar.gz -case "$KOPT_apkovl" in - http://*|https://|ftp://*) - OVL_DEV="$KOPT_apkovl";; - *:*:*) # apkovl=sda1:ext4:/subdir/host.apkovl.tar.gz - OVL_DEV="${KOPT_apkovl%%:*}" - OVL_DEV_FS="${KOPT_apkovl%:*}" - OVL_DEV_FS="${OVL_DEV_FS#*:}" - APKOVL="${KOPT_apkovl##*:}" - ;; - *:*) # apkovl=sda1:/subdir/host.apkovl.tar.gz - OVL_DEV=${KOPT_apkovl%%:*} - APKOVL=${KOPT_apkovl##*:} - ;; - *) # apkovl=subdir/host.apkovl.tar.gz - APKOVL="${KOPT_apkovl}" - ;; -esac - -case "$ALPINE_DEV" in - UUID=*|LABEL=*) ;; - nfs) - # nfs:IP:EXPORT - ALPINE_DEV_FS="$ALPINE_DEV" - ALPINE_DEV="${KOPT_alpine_dev:4}" - ;; - *) ALPINE_DEV=/dev/$ALPINE_DEV ;; -esac - # The following values are supported: # alpine_repo=auto -- default, search for .boot_repository # alpine_repo=http://... -- network repository ALPINE_REPO=${KOPT_alpine_repo} [ "$ALPINE_REPO" = "auto" ] && ALPINE_REPO= -# look for standard mountpoint locations -ALPINE_MNT=$(find_mnt $ALPINE_DEV /etc/fstab) -[ -z "$ALPINE_MNT" ] && [ "$ALPINE_DEV_FS" = nfs ] && ALPINE_MNT=/media/alpine -[ -z "$ALPINE_MNT" ] && ALPINE_MNT=/media/${ALPINE_DEV##*/} - # hide kernel messages -[ "$KOPT_quiet" = yes ] && dmesg -n 1 +#[ "$KOPT_quiet" = yes ] && dmesg -n 1 # optional blacklist for i in ${KOPT_blacklist/,/ }; do @@ -502,12 +319,8 @@ for i in ${KOPT_blacklist/,/ }; do done # setup /dev -ebegin "Starting mdev" mount -t devtmpfs -o exec,nosuid,mode=0755,size=2M devtmpfs /dev 2>/dev/null \ || mount -t tmpfs -o exec,nosuid,mode=0755,size=2M tmpfs /dev -echo "/sbin/mdev" > /proc/sys/kernel/hotplug -mdev -s -RC=$? [ -d /dev/pts ] || mkdir -m 755 /dev/pts [ -c /dev/ptmx ] || mknod -m 666 /dev/ptmx c 5 2 # make sure /dev/null is setup correctly @@ -516,7 +329,6 @@ RC=$? mount -t devpts -o gid=5,mode=0620,noexec,nosuid devpts /dev/pts [ -d /dev/shm ] || mkdir /dev/shm mount -t tmpfs -o nodev,nosuid,noexec shm /dev/shm -eend $RC # load available drivers to get access to modloop media ebegin "Loading boot drivers" @@ -528,34 +340,36 @@ if [ -f /etc/modules ] ; then modprobe -q $module $args done fi -scan_drivers -scan_drivers eend 0 +if [ -n "$KOPT_cryptroot" ]; then + cryptopts="-c ${KOPT_cryptroot}" + if [ -n "$KOPT_cryptdm" ]; then + cryptopts="$cryptopts -m ${KOPT_cryptdm}" + fi +fi + # check if root=... was set if [ -n "$KOPT_root" ]; then if [ "$SINGLEMODE" = "yes" ]; then echo "Entering single mode. Type 'exit' to continue booting." sh fi - # let usb settle in case we boot from usb disks - [ -n "$KOPT_usbdelay" ] && sleep "$KOPT_usbdelay" - wait_usb - start=${KOPT_alpine_start:-raid,cryptsetup,lvm} - for i in ${start//,/ }; do - start_$i - done + ebegin "Mounting root" if [ "$KOPT_overlaytmpfs" = "yes" ]; then - mkdir -p /media/root-ro /media/root-rw $sysroot/media/root-ro $sysroot/media/root-rw - retry_mount -o ro $KOPT_root /media/root-ro 2>/dev/null + mkdir -p /media/root-ro /media/root-rw $sysroot/media/root-ro \ + $sysroot/media/root-rw + nlplug-findfs $cryptopts -p /sbin/mdev $KOPT_root \ + && mount -o ro $KOPT_root /media/root-ro mount -t tmpfs root-tmpfs /media/root-rw mkdir -p /media/root-rw/work /media/root-rw/root mount -t overlay -o lowerdir=/media/root-ro,upperdir=/media/root-rw/root,workdir=/media/root-rw/work overlayfs $sysroot else - retry_mount ${KOPT_rootfstype:+-t} ${KOPT_rootfstype} \ + nlplug-findfs $cryptopts -p /sbin/mdev $KOPT_root + mount ${KOPT_rootfstype:+-t} ${KOPT_rootfstype} \ -o ${KOPT_rootflags:-ro} \ - $KOPT_root $sysroot 2>/dev/null + $KOPT_root $sysroot fi eend $? @@ -571,35 +385,11 @@ if [ -n "$KOPT_root" ]; then exec /bin/busybox sh fi -# we only want to wait for usb if really needed at this point -if [ -z "${ALPINE_DEV##*usb*}" ]; then - wait_usb -fi - -# IP. This shouldn't be needed if root= is set. -configure_ip - -# incase we have alpine_dev on raid device... -start=${KOPT_alpine_start:-raid,cryptsetup,lvm} -for i in ${start//,/ }; do - start_$i -done - # locate boot media and mount it ebegin "Mounting boot media" -mkdir -p $ALPINE_MNT - -# try detect the filesystem -if [ -z "$ALPINE_DEV_FS" ]; then - ALPINE_DEV_FS=$(find_fs_type $ALPINE_DEV) -fi - -if [ -n "$ALPINE_DEV_FS" ]; then - mount_opts="-t $ALPINE_DEV_FS" - [ "$ALPINE_DEV_FS" = "nfs" ] && mount_opts="$mount_opts -o nolock" -fi - -retry_mount -o ro $mount_opts $ALPINE_DEV $ALPINE_MNT >/dev/null 2>&1 +nlplug-findfs $cryptopts -p /sbin/mdev ${KOPT_debug_init:+-d} \ + ${KOPT_usbdelay:+-t $(( $KOPT_usbdelay * 1000 ))} \ + -b /tmp/repositories -a /tmp/apkovls eend $? # early console? @@ -620,27 +410,24 @@ fi mount -t tmpfs -o $rootflags tmpfs $sysroot -case "$OVL_DEV" in +case "$KOPT_apkovl" in '') - ovl=$(find_ovl $ALPINE_MNT) - if ! [ -f "$ovl" ]; then - find_ovl_delayed_usb + if [ -e /tmp/apkovls ]; then + ovl=$(head -n 1 /tmp/apkovls) fi ;; http://*|https://*|ftp://*) - ovl=/tmp/boot.apkovl.tar.gz - wget -O "$ovl" "$OVL_DEV" || ovl= + configure_ip + + MAC_ADDRESS=$(cat /sys/class/net/$device/address) + MACHINE_UUID=$(cat /sys/class/dmi/id/product_uuid) + url="${KOPT_apkovl/{MAC\}/$MAC_ADDRESS}" + url="${url/{UUID\}/$MACHINE_UUID}" + ovl=/tmp/${url##*/} + wget -O "$ovl" "$url" || ovl= ;; *) - mkdir -p /media/$OVL_DEV - unset mount_opts - if [ -n "$OVL_DEV_FS" ]; then - mount_opts="-t $OVL_DEV_FS" - fi - - retry_mount -o ro $mount_opts /dev/$OVL_DEV /media/$OVL_DEV \ - >/dev/null 2>&1 - ovl=$(find_ovl /media/$OVL_DEV) + ovl="$KOPT_apkovl" ;; esac @@ -660,26 +447,16 @@ if [ -f "$ovl" ]; then eend $? $errstr || ovlfiles= # hack, incase /root/.ssh was included in apkovl [ -d "$sysroot/root" ] && chmod 700 "$sysroot/root" - pkgs="$pkgs $(sed 's/\#.*//' $sysroot/etc/lbu/packages.list 2>/dev/null)" - pkgs="$pkgs $(cat $sysroot/var/lib/apk/world \ - $sysroot/etc/apk/world 2>/dev/null)" - # clean up after upgrade - rm -f $sysroot/etc/lbu/packages.list \ - $sysroot/var/lib/apk/world - - # fix up inittab from pre openrc times (alpine v1.8) - if [ -f "$sysroot"/etc/inittab ]; then - sed -i -e 's|:/etc/init.d/rcS|:/sbin/rc sysinit|' \ - -e 's|:/etc/init.d/rcL|:/sbin/rc default|' \ - -e 's|:/etc/init.d/rcK|:/sbin/rc shutdown|' \ - "$sysroot"/etc/inittab - fi + pkgs="$pkgs $(cat $sysroot/etc/apk/world 2>/dev/null)" fi + if [ -f "$sysroot/etc/.default_boot_services" -o ! -f "$ovl" ]; then # add some boot services by default rc_add devfs sysinit rc_add dmesg sysinit rc_add mdev sysinit + rc_add hwdrivers sysinit + rc_add modloop sysinit rc_add hwclock boot rc_add modules boot @@ -695,17 +472,24 @@ if [ -f "$sysroot/etc/.default_boot_services" -o ! -f "$ovl" ]; then rm -f "$sysroot/etc/.default_boot_services" fi -if [ "$KOPT_splash" != "no" -a -e $ALPINE_MNT/fbsplash.ppm ]; then +if [ "$KOPT_splash" != "no" ]; then + for fbsplash in /media/*/fbsplash.ppm; do + if [ -e "$fbsplash" ]; then + break; + fi + done +fi + +if [ -n "$fbsplash" ] && [ -e "$fbsplash" ]; then local config - ebegin "Starting bootsplash (from $ALPINE_MNT)" + ebegin "Starting bootsplash" mkfifo $sysroot/$splashfile - if [ -e $ALPINE_MNT/fbsplash.cfg ]; then - config=$ALPINE_MNT/fbsplash.cfg - else + config="${fbsplash%.*}.cfg" + if ! [ -e "$config" ]; then config=/tmp/fbsplash.cfg echo "IMAGE_ALIGN=CM" > $config fi - setsid fbsplash -T 16 -s $ALPINE_MNT/fbsplash.ppm -i $config -f $sysroot/$splashfile & + setsid fbsplash -T 16 -s "$fbsplash" -i $config -f $sysroot/$splashfile & eend 0 else KOPT_splash="no" @@ -723,13 +507,13 @@ if [ -f $sysroot/etc/fstab ]; then # move the ALPINE_MNT if ALPINE_DEV is specified in users fstab # this is so a generated /etc/apk/repositories will use correct # mount dir - relocate_alpine_mnt "$sysroot"/etc/fstab -fi - -# in case we upgrade we might need those: -rc_add hwdrivers sysinit -rc_add modloop sysinit + if [ -e /tmp/repositores ]; then + while read dir; do + relocate_mount "$dir" "$sysroot"/etc/fstab + done < /tmp/repositories + fi +fi # hack so we get openrc pkgs="$pkgs alpine-base" @@ -776,11 +560,16 @@ if [ "$KOPT_keep_apk_new" != yes ]; then apkflags="$apkflags --clean-protected" [ -n "$ovlfiles" ] && apkflags="$apkflags --overlay-from-stdin" fi +mkdir -p $sysroot/sys $sysroot/proc $sysroot/dev +mount -o bind /sys $sysroot/sys +mount -o bind /proc $sysroot/proc +mount -o bind /dev $sysroot/dev if [ -n "$ovlfiles" ]; then apk add --root $sysroot $repo_opt $apkflags $pkgs <$ovlfiles else apk add --root $sysroot $repo_opt $apkflags $pkgs fi +umount $sysroot/sys $sysroot/proc $sysroot/dev eend $? # unmount ovl mount if needed @@ -788,9 +577,11 @@ if [ -n "$ovl_unmount" ]; then umount $ovl_unmount 2>/dev/null fi -# remount ALPINE_MNT according default fstab from package -if [ -z "$has_fstab" ] && [ -f "$sysroot"/etc/fstab ]; then - relocate_alpine_mnt "$sysroot"/etc/fstab +# remount according default fstab from package +if [ -z "$has_fstab" ] && [ -f "$sysroot"/etc/fstab ] && [ -f /tmp/repositories ]; then + while read dir; do + relocate_mount "$dir" "$sysroot"/etc/fstab + done < /tmp/repositories fi # generate repositories if none exists. this needs to be done after relocation @@ -810,10 +601,10 @@ fi setup_inittab_console $CONSOLE # copy alpine release info -if ! [ -f "$sysroot"/etc/alpine-release ] && [ -f $ALPINE_MNT/.alpine-release ]; then - cp $ALPINE_MNT/.alpine-release $sysroot/ - ln -sf /.alpine-release $sysroot/etc/alpine-release -fi +#if ! [ -f "$sysroot"/etc/alpine-release ] && [ -f $ALPINE_MNT/.alpine-release ]; then +# cp $ALPINE_MNT/.alpine-release $sysroot/ +# ln -sf /.alpine-release $sysroot/etc/alpine-release +#fi ! [ -f "$sysroot"/etc/resolv.conf ] && [ -f /etc/resolv.conf ] && \ cp /etc/resolv.conf "$sysroot"/etc diff --git a/mkinitfs.conf b/mkinitfs.conf index 35cc96a..67a5444 100644 --- a/mkinitfs.conf +++ b/mkinitfs.conf @@ -1,2 +1,2 @@ # run mkinitfs -L for a list of available features -features="ata base bootchart cdrom cramfs ext2 ext3 ext4 floppy keymap kms raid scsi usb virtio" +features="ata base cdrom ext2 ext3 ext4 keymap kms mmc raid scsi usb virtio" diff --git a/mkinitfs.in b/mkinitfs.in index 04dc99c..c5f6434 100755 --- a/mkinitfs.in +++ b/mkinitfs.in @@ -36,7 +36,7 @@ feature_files() { initfs_base() { local i= dirs= glob= file= - for i in dev proc sys sbin bin .modloop lib/modules media/cdrom \ + for i in dev proc sys sbin bin run .modloop lib/modules media/cdrom \ etc/apk media/floppy media/usb newroot; do dirs="$dirs $tmpdir/$i" done @@ -115,10 +115,24 @@ initfs_kmods() { for file in $(find_kmods); do echo "${file#/}" done | sort -u | cpio -pdm "$tmpdir" || return 1 + for file in modules.order modules.builtin; do + if [ -f "$kerneldir"/$file ]; then + cp "$kerneldir"/$file "$tmpdir"/lib/modules/$kernel/ + fi + done depmod $kernel -b "$tmpdir" cd "$oldpwd" } +initfs_firmware() { + rm -rf "$tmpdir"/lib/firmware + mkdir -p "$tmpdir"/lib/firmware + find "$tmpdir"/lib/modules -type f -name "*.ko" | xargs modinfo -F firmware | sort -u | while read FW; do + [ -e "${basedir}/lib/firmware/${FW}" ] && install -pD "${basedir}/lib/firmware/${FW}" "$tmpdir"/lib/firmware/$FW + done + return 0 +} + initfs_apk_keys() { mkdir -p "$tmpdir"/etc/apk/keys cp "${basedir}etc/apk/keys/"* "$tmpdir"/etc/apk/keys/ @@ -179,7 +193,7 @@ done shift $(( $OPTIND - 1 )) . $(readlink -f "$config") -features_dir=${features_dir:-"$sysconfdir/features.d"} +features_dir=${features_dir:-"${basedir%/:-}/${sysconfdir#/}/features.d"} [ -n "$myfeatures" ] && features="$myfeatures" if [ -n "$list_features" ]; then @@ -232,6 +246,7 @@ fi initfs_base \ && initfs_kmods \ + && initfs_firmware \ && initfs_apk_keys \ && initfs_cpio rc=$? diff --git a/nlplug-findfs.c b/nlplug-findfs.c new file mode 100644 index 0000000..7d1511e --- /dev/null +++ b/nlplug-findfs.c @@ -0,0 +1,1021 @@ + +/* + * Copy me if you can. + * by 20h + * + * Copyright (c) 2015 Natanael Copa + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "arg.h" + +#define DEFAULT_EVENT_TIMEOUT 250 +/* usb mass storage needs 1 sec to settle */ +#define USB_STORAGE_TIMEOUT 1000 + +#define FOUND_DEVICE 0x1 +#define FOUND_BOOTREPO 0x2 +#define FOUND_APKOVL 0x4 + +#define TRIGGER_THREAD 0x1 +#define CRYPTSETUP_THREAD 0x2 + +#define LVM_PATH "/sbin/lvm" +#define MDADM_PATH "/sbin/mdadm" + +static int dodebug; +static char *default_envp[2]; +char *argv0; +static int use_mdadm, use_lvm; + +#if defined(DEBUG) +#include +static void dbg(const char *fmt, ...) +{ + va_list fmtargs; + if (!dodebug) + return; + + fprintf(stderr, "%s: ", argv0); + va_start(fmtargs, fmt); + vfprintf(stderr, fmt, fmtargs); + va_end(fmtargs); + fprintf(stderr, "\n"); +} +#else +#define dbg(...) +#endif + +#define envcmp(env, key) (strncmp(env, key "=", strlen(key "=")) == 0) + + +static char **clone_array(char *const *const a) +{ + size_t i, s; + char **c, *p; + + if (!a) return 0; + + s = sizeof(char*); + for (i = 0; a[i]; i++) + s += sizeof(char*) + strlen(a[i]) + 1; + c = malloc(s); + p = (char*)(c + i + 1); + for (i = 0; a[i]; i++) { + c[i] = p; + p += sprintf(p, "%s", a[i]) + 1; + } + c[i] = 0; + return c; +} + +struct spawn_task { + struct spawn_task *next; + char **argv, **envp; +}; +struct spawn_manager { + int num_running; + int max_running; + struct spawn_task *first, *last; +}; + +static struct spawn_manager spawnmgr; + +static void spawn_execute(struct spawn_manager *mgr, char **argv, char **envp) +{ + pid_t pid; + + dbg("[%d/%d] running %s", mgr->num_running+1, mgr->max_running, argv[0]); + if (!(pid = fork())) { + if (execve(argv[0], argv, envp ? envp : default_envp) < 0) + err(1, argv[0]); + exit(0); + } + if (pid < 0) + err(1,"fork"); + + mgr->num_running++; +} + +static void spawn_queue(struct spawn_manager *mgr, char **argv, char **envp) +{ + struct spawn_task *task; + + task = malloc(sizeof *task); + if (!task) return; + *task = (struct spawn_task) { + .next = NULL, + .argv = clone_array(argv), + .envp = clone_array(envp), + }; + if (mgr->last) { + mgr->last->next = task; + mgr->last = task; + } else { + mgr->first = mgr->last = task; + } +} + +static void spawn_command(struct spawn_manager *mgr, char **argv, char **envp) +{ + if (!mgr->max_running) + mgr->max_running = sysconf(_SC_NPROCESSORS_ONLN); + if (mgr->num_running < mgr->max_running) + spawn_execute(mgr, argv, envp); + else + spawn_queue(mgr, argv, envp); +} + +static void spawn_reap(struct spawn_manager *mgr, pid_t pid) +{ + mgr->num_running--; + if (mgr->first && mgr->num_running < mgr->max_running) { + struct spawn_task *task = mgr->first; + if (task->next) + mgr->first = task->next; + else + mgr->first = mgr->last = NULL; + spawn_execute(mgr, task->argv, task->envp); + free(task->argv); + free(task->envp); + free(task); + } +} + +static int spawn_active(struct spawn_manager *mgr) +{ + return mgr->num_running || mgr->first; +} + +struct uevent { + char *buf; + size_t bufsize; + char *message; + char *subsystem; + char *action; + char *modalias; + char *devname; + char *major; + char *minor; + char devnode[256]; + char *envp[64]; +}; + +struct ueventconf { + char **program_argv; + char *search_device; + char *crypt_device; + char *crypt_name; + char crypt_devnode[256]; + char *subsystem_filter; + int modalias_count; + int fork_count; + char *bootrepos; + char *apkovls; + int timeout; + int efd; + unsigned running_threads; + pthread_t cryptsetup_tid; + pthread_mutex_t cryptsetup_mutex; +}; + + +static void sighandler(int sig) +{ + switch (sig) { + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGABRT: + case SIGTERM: + exit(0); + default: + break; + } +} + +static void initsignals(void) +{ + signal(SIGHUP, sighandler); + signal(SIGINT, sighandler); + signal(SIGQUIT, sighandler); + signal(SIGABRT, sighandler); + signal(SIGTERM, sighandler); + signal(SIGCHLD, sighandler); + signal(SIGPIPE, SIG_IGN); +} + +static int init_netlink_socket(void) +{ + struct sockaddr_nl nls; + int fd, slen; + + memset(&nls, 0, sizeof(nls)); + nls.nl_family = AF_NETLINK; + nls.nl_pid = getpid(); + nls.nl_groups = -1; + + fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, + NETLINK_KOBJECT_UEVENT); + if (fd < 0) + err(1, "socket"); + + /* kernel will not create events bigger than 16kb, but we need + buffer up all events during coldplug */ + slen = 512*1024; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &slen, + sizeof(slen)) < 0) { + err(1, "setsockopt"); + } + slen = 1; + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &slen, + sizeof(slen)) < 0) { + err(1, "setsockopt"); + } + + if (bind(fd, (void *)&nls, sizeof(nls))) + err(1, "bind"); + + return fd; +} + +static int load_kmod(const char *modalias, char *driver, size_t len) +{ + static struct kmod_ctx *ctx = NULL; + struct kmod_list *list = NULL; + struct kmod_list *node; + int r, count=0; + + if (ctx == NULL) { + dbg("initializing kmod"); + ctx = kmod_new(NULL, NULL); + if (ctx == NULL) + return -1; + kmod_set_log_fn(ctx, NULL, NULL); + r = kmod_load_resources(ctx); + } + + r = kmod_module_new_from_lookup(ctx, modalias, &list); + if (r < 0) { + dbg("alias '%s' lookup failure", modalias); + return r; + } + + kmod_list_foreach(node, list) { + struct kmod_module *mod = kmod_module_get_module(node); + const char *fmt; + r = kmod_module_probe_insert_module(mod, + KMOD_PROBE_APPLY_BLACKLIST, + NULL, NULL, NULL, NULL); + if (r == 0) { + fmt = "module '%s' inserted"; + count++; + } else if (r == KMOD_PROBE_APPLY_BLACKLIST) { + fmt = "module '%s' is blacklisted"; + } else { + fmt = "module '%s' failed"; + } + dbg(fmt, kmod_module_get_name(mod)); + if (driver) + strncpy(driver, kmod_module_get_name(mod), len); + kmod_module_unref(mod); + } + kmod_module_unref_list(list); + return count; +} + +static void start_mdadm(char *devnode) +{ + char *mdadm_argv[] = { + MDADM_PATH, + "--incremental", + "--quiet", + devnode, + NULL + }; + if (use_mdadm) + spawn_command(&spawnmgr, mdadm_argv, 0); +} + +static void start_lvm2(char *devnode) +{ + char *lvm2_argv[] = { + LVM_PATH, "vgchange", + "--activate" , "ay", "--noudevsync", "--sysinit", "-q", "-q", + NULL + }; + if (use_lvm) + spawn_command(&spawnmgr, lvm2_argv, 0); +} + + +static int read_pass(char *pass, size_t pass_size) +{ + struct termios old_flags, new_flags; + int r; + + tcgetattr(STDIN_FILENO, &old_flags); + new_flags = old_flags; + new_flags.c_lflag &= ~ECHO; + new_flags.c_lflag |= ECHONL; + + r = tcsetattr(STDIN_FILENO, TCSANOW, &new_flags); + if (r < 0) { + warn("tcsetattr"); + return r; + } + + if (fgets(pass, pass_size, stdin) == NULL) { + warn("fgets"); + return -1; + } + pass[strlen(pass) - 1] = '\0'; + + if (tcsetattr(STDIN_FILENO, TCSANOW, &old_flags) < 0) { + warn("tcsetattr"); + return r; + } + + return 0; +} + +static void *cryptsetup_thread(void *data) +{ + struct ueventconf *c = (struct ueventconf *)data; + uint64_t ok = CRYPTSETUP_THREAD; + struct crypt_device *cd; + int r, passwd_tries = 5; + + r = crypt_init(&cd, c->crypt_devnode); + if (r < 0) { + warnx("crypt_init(%s)", c->crypt_devnode); + goto notify_out; + } + + r = crypt_load(cd , CRYPT_LUKS1, NULL); + if (r < 0) { + warnx("crypt_load(%s)", c->crypt_devnode); + goto free_out; + } + + while (passwd_tries > 0) { + char pass[1024]; + + printf("Enter passphrase for %s: ", c->crypt_devnode); + fflush(stdout); + + if (read_pass(pass, sizeof(pass)) < 0) + goto free_out; + passwd_tries--; + + pthread_mutex_lock(&c->cryptsetup_mutex); + r = crypt_activate_by_passphrase(cd, c->crypt_name, + CRYPT_ANY_SLOT, + pass, strlen(pass), 0); + pthread_mutex_unlock(&c->cryptsetup_mutex); + + if (r == 0) + break; + printf("No key available with this passphrase.\n"); + } + +free_out: + crypt_free(cd); +notify_out: + write(c->efd, &ok, sizeof(ok)); + return NULL; +} + +static void start_cryptsetup(struct ueventconf *conf) +{ + dbg("starting cryptsetup %s -> %s", conf->crypt_devnode, conf->crypt_name); + load_kmod("dm-crypt", NULL, 0); + pthread_create(&conf->cryptsetup_tid, NULL, cryptsetup_thread, conf); + conf->running_threads |= CRYPTSETUP_THREAD; +} + +static int is_mounted(const char *devnode) { + char line[PATH_MAX]; + FILE *f = fopen("/proc/mounts", "r"); + int r = 0; + if (f == NULL) + return 0; + while (fgets(line, sizeof(line), f) != NULL) { + strtok(line, " "); + if (strcmp(devnode, line) == 0) { + r = 1; + break; + } + } + fclose(f); + return r; +} + +struct recurse_opts { + const char *searchname; + void (*callback)(const char *, const void *); + void *userdata; +}; + +/* pathbuf needs hold PATH_MAX chars */ +static void recurse_dir(char *pathbuf, struct recurse_opts *opts) +{ + DIR *d = opendir(pathbuf); + struct dirent *entry; + + if (d == NULL) + return; + + while ((entry = readdir(d)) != NULL) { + size_t pathlen = strlen(pathbuf); + size_t namelen = strlen(entry->d_name); + int is_dir; + + /* d_type is not supported by all filesystems so we need + lstat */ + if (pathlen + 2 + namelen > PATH_MAX) { + dbg("path length overflow"); + continue; + } + + pathbuf[pathlen] = '/'; + strcpy(&pathbuf[pathlen+1], entry->d_name); + + if (entry->d_type == DT_UNKNOWN) { + /* some filesystems like iso9660 does not support + the d_type so we use lstat */ + struct stat st; + if (lstat(pathbuf, &st) < 0) { + dbg("%s: %s", pathbuf, strerror(errno)); + goto next; + } + is_dir = S_ISDIR(st.st_mode); + } else + is_dir = entry->d_type & DT_DIR; + + if (is_dir) { + if (entry->d_name[0] == '.') + goto next; + } else if (opts->searchname + && strcmp(entry->d_name, opts->searchname) != 0) { + goto next; + } + + if (is_dir) + recurse_dir(pathbuf, opts); + else + opts->callback(pathbuf, opts->userdata); +next: + pathbuf[pathlen] = '\0'; + } + closedir(d); +} + +struct bootrepos { + char *outfile; + int count; +}; + +static void bootrepo_cb(const char *path, const void *data) +{ + struct bootrepos *repos = (struct bootrepos *)data; + int fd = open(repos->outfile, O_WRONLY | O_CREAT | O_APPEND); + if (fd == -1) + err(1, "%s", repos->outfile); + + write(fd, path, strlen(path) - strlen("/.boot_repository")); + write(fd, "\n", 1); + close(fd); + dbg("added boot repository %s to %s\n", path, repos->outfile); + repos->count++; +} + +static int find_apkovl(const char *dir, const char *outfile) +{ + char pattern[PATH_MAX]; + glob_t gl; + int r, fd; + + if (outfile == NULL) + return 0; + + snprintf(pattern, sizeof(pattern), "%s/*.apkovl.tar.gz*", dir); + + r = glob(pattern, 0, NULL, &gl); + if (r != 0) + return 0; + + fd = open(outfile, O_WRONLY | O_CREAT | O_APPEND); + if (fd == -1) + err(1, "%s", outfile); + + for (r = 0; r < gl.gl_pathc; r++) { + dbg("Found apkovl: %s", gl.gl_pathv[r]); + write(fd, gl.gl_pathv[r], strlen(gl.gl_pathv[r])); + write(fd, "\n", 1); + } + close(fd); + globfree(&gl); + return FOUND_APKOVL; +} + +static int find_bootrepos(const char *devnode, const char *type, + char *bootrepos, const char *apkovls) +{ + char mountdir[PATH_MAX] = ""; + char *devname; + int r, rc = 0; + struct bootrepos repos = { + .outfile = bootrepos, + .count = 0, + }; + struct recurse_opts opts = { + .searchname = ".boot_repository", + .callback = bootrepo_cb, + .userdata = &repos, + }; + + + /* skip already mounted devices */ + if (is_mounted(devnode)) { + dbg("%s is mounted (%s). skipping", devnode, type); + return 0; + } + devname = strrchr(devnode, '/'); + + if (devname) + snprintf(mountdir, sizeof(mountdir), "/media%s", devname); + + dbg("mounting %s on %s. (%s)", devnode, mountdir, type); + mkdir(mountdir, 0755); + + r = mount(devnode, mountdir, type, MS_RDONLY, NULL); + if (r < 0) { + dbg("Failed to mount %s on %s: %s", + devnode, mountdir, strerror(errno)); + return 0; + } + + recurse_dir(mountdir, &opts); + if (repos.count > 0) + rc |= FOUND_BOOTREPO; + + if (find_apkovl(mountdir, apkovls)) + rc |= FOUND_APKOVL; + + if (rc == 0) + umount(mountdir); + + return rc; +} + +static int is_same_device(const struct uevent *ev, const char *nodepath) +{ + struct stat st; + unsigned int maj, min; + if (stat(nodepath, &st) < 0) + return 0; + + if (ev->major == NULL || ev->minor == NULL) + return 0; + + maj = atoi(ev->major); + min = atoi(ev->minor); + return S_ISBLK(st.st_mode) && makedev(maj, min) == st.st_rdev; +} + + +static int searchdev(struct uevent *ev, const char *searchdev, char *bootrepos, + const char *apkovls) +{ + static blkid_cache cache = NULL; + char *type = NULL, *label = NULL, *uuid = NULL; + int rc = 0; + + if (searchdev == NULL && bootrepos == NULL && apkovls == NULL) + return 0; + + if (searchdev && (strcmp(ev->devname, searchdev) == 0 + || strcmp(ev->devnode, searchdev) == 0 + || is_same_device(ev, searchdev))) { + return FOUND_DEVICE; + } + + if (cache == NULL) + blkid_get_cache(&cache, NULL); + + type = blkid_get_tag_value(cache, "TYPE", ev->devnode); + + if (searchdev != NULL) { + if (strncmp("LABEL=", searchdev, 6) == 0) { + label = blkid_get_tag_value(cache, "LABEL", ev->devnode); + if (label && strcmp(label, searchdev+6) == 0) + rc = FOUND_DEVICE; + } else if (strncmp("UUID=", searchdev, 5) == 0) { + uuid = blkid_get_tag_value(cache, "UUID", ev->devnode); + if (uuid && strcmp(uuid, searchdev+5) == 0) + rc = FOUND_DEVICE; + } + } + + if (type || label || uuid) { + dbg("%s:\n" + "\ttype='%s'\n" + "\tlabel='%s'\n" + "\tuuid='%s'\n", ev->devnode, + type ? type : NULL, + label ? label : NULL, + uuid ? uuid : NULL); + } + + if (!rc && type) { + if (strcmp("linux_raid_member", type) == 0) { + start_mdadm(ev->devnode); + } else if (strcmp("LVM2_member", type) == 0) { + start_lvm2(ev->devnode); + } else if (bootrepos) { + rc = find_bootrepos(ev->devnode, type, bootrepos, apkovls); + } + } + + if (type) + free(type); + if (label) + free(label); + if (uuid) + free(uuid); + + return rc; +} + +static int dispatch_uevent(struct uevent *ev, struct ueventconf *conf) +{ + static int timeout_increment = USB_STORAGE_TIMEOUT; + + if (conf->subsystem_filter && ev->subsystem + && strcmp(ev->subsystem, conf->subsystem_filter) != 0) { + dbg("subsystem '%s' filtered out (by '%s').", + ev->subsystem, conf->subsystem_filter); + return 0; + } + + if (ev->action == NULL) + return 0; + + if (ev->modalias != NULL && strcmp(ev->action, "add") == 0) { + char buf[128]; + memset(buf, 0, sizeof(buf)); + load_kmod(ev->modalias, buf, sizeof(buf)-1); + conf->modalias_count++; + + /* increase timeout so usb drives gets time to settle */ + if (strcmp(buf, "usb_storage") == 0) { + conf->timeout += timeout_increment; + timeout_increment = 0; + } + + } else if (ev->devname != NULL) { + if (conf->program_argv[0] != NULL) { + spawn_command(&spawnmgr, conf->program_argv, ev->envp); + conf->fork_count++; + } + + if (ev->subsystem && strcmp(ev->subsystem, "block") == 0 + && strcmp(ev->action, "add") == 0) { + int rc; + + snprintf(ev->devnode, sizeof(ev->devnode), "/dev/%s", + ev->devname); + pthread_mutex_lock(&conf->cryptsetup_mutex); + rc = searchdev(ev, conf->search_device, + conf->bootrepos, conf->apkovls); + pthread_mutex_unlock(&conf->cryptsetup_mutex); + if (rc) + return rc; + + if (searchdev(ev, conf->crypt_device, NULL, NULL)) { + strncpy(conf->crypt_devnode, + conf->crypt_device[0] == '/' ? conf->crypt_device : ev->devnode, + sizeof(conf->crypt_devnode)); + start_cryptsetup(conf); + } + } + } + return 0; +} + +static int process_uevent(char *buf, const size_t len, struct ueventconf *conf) +{ + struct uevent ev; + + int i, nenvp, slen = 0; + char *key, *value; + + memset(&ev, 0, sizeof(ev)); + ev.buf = buf; + ev.bufsize = len; + + nenvp = sizeof(default_envp) / sizeof(default_envp[0]) - 1; + memcpy(&ev.envp, default_envp, nenvp * sizeof(default_envp[0])); + + for (i = 0; i < len; i += slen + 1) { + key = buf + i; + value = strchr(key, '='); + slen = strlen(buf+i); + + if (i == 0 && slen != 0) { + /* first line, the message */ + ev.message = key; + continue; + } + + if (!slen || !value) + continue; + + value++; + if (envcmp(key, "MODALIAS")) { + ev.modalias = value; + } else if (envcmp(key, "ACTION")) { + ev.action = value; + } else if (envcmp(key, "SUBSYSTEM")) { + ev.subsystem = value; + } else if (envcmp(key, "DEVNAME")) { + ev.devname = value; + } else if (envcmp(key, "MAJOR")) { + ev.major = value; + } else if (envcmp(key, "MINOR")) { + ev.minor = value; + } + + if (!envcmp(key, "PATH")) + ev.envp[nenvp++]= key; + } + ev.envp[nenvp++] = 0; + + return dispatch_uevent(&ev, conf); +} + +static void trigger_uevent_cb(const char *path, const void *data) +{ + int fd = open(path, O_WRONLY); + write(fd, "add", 3); + close(fd); +} + +static void *trigger_thread(void *data) +{ + int fd = *(int *)data; + uint64_t ok = TRIGGER_THREAD; + struct recurse_opts opts = { + .searchname = "uevent", + .callback = trigger_uevent_cb, + .userdata = NULL, + }; + char path[PATH_MAX] = "/sys/bus"; + + recurse_dir(path, &opts); + strcpy(path, "/sys/devices"); + recurse_dir(path, &opts); + write(fd, &ok, sizeof(ok)); + return NULL; +} + +static void usage(int rc) +{ + printf("coldplug system til given device is found\n" + "usage: %s [options] DEVICE\n" + "\n" + "options:\n" + " -a OUTFILE add paths to found apkovls to OUTFILE\n" + " -b OUTFILE add found boot repositories to OUTFILE\n" + " -c CRYPTDEVICE run cryptsetup luksOpen when CRYPTDEVICE is found\n" + " -h show this help\n" + " -m CRYPTNAME use CRYPTNAME name for crypto device mapping\n" + " -d enable debugging ouput\n" + " -f SUBSYSTEM filter subsystem\n" + " -p PROGRAM use PROGRAM as handler for every event with DEVNAME\n" + " -t TIMEOUT timeout after TIMEOUT milliseconds without uevents\n" + "\n", argv0); + + exit(rc); +} + +int main(int argc, char *argv[]) +{ + struct pollfd fds[3]; + int numfds = 3; + int r; + struct ueventconf conf; + int event_count = 0; + size_t total_bytes = 0; + int found = 0; + char *program_argv[2] = {0,0}; + pthread_t tid; + sigset_t sigchldmask; + + for (r = 0; environ[r]; r++) { + if (envcmp(environ[r], "PATH")) + default_envp[0] = environ[r]; + } + + memset(&conf, 0, sizeof(conf)); + conf.program_argv = program_argv; + conf.timeout = DEFAULT_EVENT_TIMEOUT; + use_lvm = access(LVM_PATH, X_OK) == 0; + use_mdadm = access(MDADM_PATH, X_OK) == 0; + + argv0 = strrchr(argv[0], '/'); + if (argv0++ == NULL) + argv0 = argv[0]; + + ARGBEGIN { + case 'a': + conf.apkovls = EARGF(usage(1));; + break; + case 'b': + conf.bootrepos = EARGF(usage(1)); + break; + case 'c': + conf.crypt_device = EARGF(usage(1)); + break; + case 'h': + usage(0); + break; + case 'm': + conf.crypt_name = EARGF(usage(1)); + break; + case 'd': + dodebug = 1; + break; + case 'f': + conf.subsystem_filter = EARGF(usage(1)); + break; + case 'p': + conf.program_argv[0] = EARGF(usage(1)); + break; + case 't': + conf.timeout = atoi(EARGF(usage(1))); + break; + default: + usage(1); + } ARGEND; + + if (argc > 0) + conf.search_device = argv[0]; + + r = pthread_mutex_init(&conf.cryptsetup_mutex, NULL); + if (r < 0) + err(1, "pthread_mutex_init"); + + initsignals(); + sigemptyset(&sigchldmask); + sigaddset(&sigchldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigchldmask, NULL); + + fds[0].fd = init_netlink_socket(); + fds[0].events = POLLIN; + + fds[1].fd = signalfd(-1, &sigchldmask, SFD_NONBLOCK|SFD_CLOEXEC); + fds[1].events = POLLIN; + + fds[2].fd = eventfd(0, EFD_CLOEXEC); + fds[2].events = POLLIN; + conf.efd = fds[2].fd; + pthread_create(&tid, NULL, trigger_thread, &fds[2].fd); + conf.running_threads |= TRIGGER_THREAD; + + while (1) { + r = poll(fds, numfds, (spawn_active(&spawnmgr) || conf.running_threads) ? -1 : conf.timeout); + if (r == -1) { + if (errno == EINTR || errno == ERESTART) + continue; + err(1, "poll"); + } + if (r == 0) { + dbg("exit due to timeout (%i)", conf.timeout); + break; + } + + if (fds[0].revents & POLLIN) { + size_t len; + struct iovec iov; + char cbuf[CMSG_SPACE(sizeof(struct ucred))]; + char buf[16384]; + struct cmsghdr *chdr; + struct ucred *cred; + struct msghdr hdr; + struct sockaddr_nl cnls; + + iov.iov_base = &buf; + iov.iov_len = sizeof(buf); + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = cbuf; + hdr.msg_controllen = sizeof(cbuf); + hdr.msg_name = &cnls; + hdr.msg_namelen = sizeof(cnls); + + len = recvmsg(fds[0].fd, &hdr, 0); + if (len < 0) { + if (errno == EINTR) + continue; + err(1, "recvmsg"); + } + if (len < 32 || len >= sizeof(buf)) + continue; + + total_bytes += len; + chdr = CMSG_FIRSTHDR(&hdr); + if (chdr == NULL || chdr->cmsg_type != SCM_CREDENTIALS) + continue; + + /* filter out messages that are not from root or kernel */ + cred = (struct ucred *)CMSG_DATA(chdr); + if (cred->uid != 0 || cnls.nl_pid > 0) + continue; + + event_count++; + found |= process_uevent(buf, len, &conf); + + if ((found & FOUND_DEVICE) + || ((found & FOUND_BOOTREPO) && + (found & FOUND_APKOVL))) { + if (conf.timeout) + dbg("FOUND! setting timeout to 0"); + conf.timeout = 0; + } + } + + if (fds[0].revents & POLLHUP) { + dbg("parent hung up\n"); + break; + } + + if (fds[1].revents & POLLIN) { + struct signalfd_siginfo fdsi; + pid_t pid; + int status; + + while (read(fds[1].fd, &fdsi, sizeof fdsi) > 0) + ; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + spawn_reap(&spawnmgr, pid); + } + + if (fds[2].revents & POLLIN) { + uint64_t tmask = 0; + if (read(fds[2].fd, &tmask, sizeof(tmask)) < 0) + warn("eventfd"); + if (tmask & TRIGGER_THREAD) { + dbg("terminating trigger thread"); + pthread_join(tid, NULL); + } + if (tmask & CRYPTSETUP_THREAD) { + dbg("terminating cryptsetup thread"); + pthread_join(conf.cryptsetup_tid, NULL); + } + conf.running_threads &= ~tmask; + } + } + close(fds[2].fd); + pthread_mutex_destroy(&conf.cryptsetup_mutex); + + dbg("modaliases: %i, forks: %i, events: %i, total bufsize: %zu", + conf.modalias_count, + conf.fork_count, + event_count, total_bytes); + + return found ? 0 : 1; +}