#!/bin/sh ################################################################################ # This file is part of OpenELEC - http://www.openelec.tv # Copyright (C) 2009-2014 Stephan Raue (stephan@openelec.tv) #      Copyright (C) 2010-2011 Roman Weber (roman@openelec.tv) # Copyright (C) 2012 Yann Cézard (eesprit@free.fr) # # OpenELEC 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 2 of the License, or # (at your option) any later version. # # OpenELEC 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 OpenELEC. If not, see . ################################################################################ # create directories /bin/busybox mkdir -p /dev /bin/busybox mkdir -p /proc /bin/busybox mkdir -p /sys /bin/busybox mkdir -p /flash /bin/busybox mkdir -p /sysroot /bin/busybox mkdir -p /storage # mount all needed special filesystems /bin/busybox mount -t devtmpfs devtmpfs /dev /bin/busybox mount -t proc proc /proc /bin/busybox mount -t sysfs sysfs /sys # set needed variables MODULE_DIR=/lib/modules UPDATE_DIR=/storage/.update UPDATE_KERNEL="KERNEL" UPDATE_SYSTEM="SYSTEM" IMAGE_KERNEL="KERNEL" IMAGE_SYSTEM="SYSTEM" BOOT_STEP="start" REBOOT="0" MD5_FAILED="0" MD5_NOCHECK="0" SIZE_FAILED="0" NBD_DEVS="0" FLASH_FREE_MIN="5" INSTALLED_MEMORY=`cat /proc/meminfo | grep 'MemTotal:' | awk '{print $2}'` SYSTEM_TORAM_LIMIT=1024000 # load any configuration if [ -f "/etc/initramfs.conf" ]; then . /etc/initramfs.conf fi # hide kernel log messages on console echo '1 4 1 7' > /proc/sys/kernel/printk # clear screen and hide cursor clear echo 0 > /sys/devices/virtual/graphics/fbcon/cursor_blink # parse command line arguments for arg in $(cat /proc/cmdline); do case $arg in BOOT_IMAGE=*) IMAGE_KERNEL="${arg#*=}" ;; SYSTEM_IMAGE=*) IMAGE_SYSTEM="${arg#*=}" ;; boot=*) boot="${arg#*=}" case $boot in CIFS=*|SMB=*|ISCSI=*|NBD=*|NFS=*) UPDATE_DISABLED=yes ;; esac ;; disk=*) disk="${arg#*=}" case $disk in CIFS=*|SMB=*|ISCSI=*|NBD=*|NFS=*) STORAGE_NETBOOT=yes ;; esac ;; textmode) INIT_ARGS="$INIT_ARGS --unit=textmode.target" ;; installer) INIT_ARGS="$INIT_ARGS --unit=installer.target" ;; debugging) DEBUG=yes ;; ssh) SSH=yes ;; progress) PROGRESS=yes INIT_ARGS="$INIT_ARGS --show-status=1" ;; nosplash) SPLASH=no ;; noram) SYSTEM_TORAM=no ;; overlay) OVERLAY=yes ;; break=*) BREAK="${arg#*=}" ;; esac done if test "$DEBUG" = "yes"; then exec 3>&1 else exec 3>/dev/null fi SILENT_OUT=3 progress() { if test "$PROGRESS" = "yes"; then echo "### $1 ###" fi } debug_shell() { echo "### Starting debugging shell... type exit to quit ###" # show cursor echo 0 > /sys/devices/virtual/graphics/fbcon/cursor_blink sh /dev/tty1 2>&1 } error() { # Display fatal error message # $1:action which caused error, $2:message echo "*** Error in $BOOT_STEP: $1: $2 ***" debug_shell } break_after() { # Start debug shell after boot step $1 case $BREAK in all|*$1*) debug_shell ;; esac } # Mount handlers # All handlers take the following parameters: # $1:target, $2:mountpoint, $3:mount options, [$4:fs type] mount_common() { # Common mount handler, handles block devices and filesystem images MOUNT_OPTIONS="-o $3" [ -n "$4" ] && MOUNT_OPTIONS="-t $4 $MOUNT_OPTIONS" for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do ERR_ENV=1 mount $MOUNT_OPTIONS $1 $2 >&$SILENT_OUT 2>&1 [ "$?" -eq "0" ] && ERR_ENV=0 && break usleep 1000000 done [ "$ERR_ENV" -ne "0" ] && error "mount_common" "Could not mount $1" } mount_cifs() { # Mount CIFS (Samba) share CIFS_SHARE="${1%%,*}" CIFS_OPTIONS="${1#*,}" [ "$CIFS_OPTIONS" = "$1" ] && CIFS_OPTIONS= mount_common "$CIFS_SHARE" "$2" "$3,$CIFS_OPTIONS" "cifs" } get_iscsistart_options() { # Convert kernel commandline ISCSI= options to iscsistart options IFS_SAVE="$IFS" IFS=, for arg in $1; do val="${arg#*=}" case "$arg" in iscsi_initiator=*) option="-i" ;; iscsi_target_name=*) option="-t" ;; iscsi_target_ip=*) option="-a" ;; iscsi_target_port=*) option="-p" ;; iscsi_target_group=*) option="-g" ;; iscsi_username=*) option="-u" ;; iscsi_password=*) option="-w" ;; iscsi_in_username=*) option="-U" ;; iscsi_in_password=*) option="-W" ;; esac echo "$option $val" done IFS="$IFS_SAVE" } mount_iscsi() { # Mount iSCSI target ISCSI_DEV="${1##*,}" ISCSI_OPTIONS="${1%,*}" if [ ! -f "/sbin/iscsistart" ]; then error "iscsistart" "iSCSI support not available" fi if [ "$ISCSI_OPTIONS" = "auto" ]; then progress "Network configuration based on iBFT" /sbin/iscsistart -N >&$SILENT_OUT 2>&1 || \ error "iscsistart" "Unable to configure network" progress "iSCSI auto connect based on iBFT" /sbin/iscsistart -b >&$SILENT_OUT 2>&1 || \ error "iscsistart" "Unable to auto connect" else /sbin/iscsistart $(get_iscsistart_options "$ISCSI_OPTIONS") >&$SILENT_OUT 2>&1 || \ error "iscsistart" "Unable to connect to ISCSI target" fi mount_common "$ISCSI_DEV" "$2" "$3" "$4" } mount_nbd() { # Mount NBD device NBD_SERVER="${1%%:*}" NBD_PORT="${1#*:}" NBD_DEV="/dev/nbd$NBD_DEVS" nbd-client $NBD_SERVER $NBD_PORT $NBD_DEV >&$SILENT_OUT 2>&1 || \ error "nbd-client" "Could not connect to NBD server $1" mount_common "$NBD_DEV" "$2" "$3" "$4" NBD_DEVS=$(( ${NBD_DEVS} + 1 )) } mount_nfs() { # Mount NFS export NFS_EXPORT="${1%%,*}" NFS_OPTIONS="${1#*,}" [ "$NFS_OPTIONS" = "$1" ] && NFS_OPTIONS= mount_common "$NFS_EXPORT" "$2" "$3,nolock,soft,timeo=3,retrans=2,rsize=32768,wsize=32768,$NFS_OPTIONS" "nfs" } mount_part() { # Mount a local or network filesystem # $1:[TYPE=]target, $2:mountpoint, $3:mount options, [$4:fs type] progress "mount filesystem $1 ..." MOUNT_TARGET="${1#*=}" case $1 in LABEL=*|UUID=*|/*) MOUNT_CMD="mount_common" MOUNT_TARGET="$1" ;; CIFS=*|SMB=*) MOUNT_CMD="mount_cifs" ;; ISCSI=*) MOUNT_CMD="mount_iscsi" ;; NBD=*) MOUNT_CMD="mount_nbd" ;; NFS=*) MOUNT_CMD="mount_nfs" ;; *) error "mount_part" "Unknown filesystem $1" ;; esac $MOUNT_CMD "$MOUNT_TARGET" "$2" "$3" "$4" } update() { if [ -f "$UPDATE_DIR/$2" -a -f "$3" ]; then echo "updating $1..." mount -o remount,rw /flash mv $UPDATE_DIR/$2 $3 2>/dev/null # loopback file needs writable /flash all the time if [ "${disk%%=*}" != "FILE" ]; then mount -o remount,ro /flash fi sync fi } update_bootloader() { export BOOT_ROOT="/flash" export SYSTEM_ROOT="/sysroot" mount_part "/flash/$IMAGE_SYSTEM" "/sysroot" "ro,loop" if [ -f $SYSTEM_ROOT/usr/share/bootloader/update.sh ]; then echo "updating Bootloader..." sh $SYSTEM_ROOT/usr/share/bootloader/update.sh sync fi umount /sysroot } hfsdiskprep() { for DEVICE in $(/bin/busybox blkid /dev/sd* | sed -e "s,\",,g"| grep $boot); do for device in $(/bin/busybox blkid $DEVICE); do case $device in TYPE=*) FS_TYPE=${device#TYPE=} ;; esac done if [ "$FS_TYPE" = "\"hfs\"" -o "$FS_TYPE" = "\"hfsplus\"" ]; then progress "check filesystem $DEVICE [$FS_TYPE]..." /bin/fsck_hfs -r -y $DEVICE >&$SILENT_OUT 2>&1 fi done } load_modules() { progress "Loading kernel modules" [ ! -f "/etc/modules" ] && return for module in $(cat /etc/modules); do progress "Loading kernel module $module" insmod "$MODULE_DIR/$module.ko" || \ progress "... Failed to load kernel module $module, skipping" done } load_splash() { if [ ! "$SPLASH" = "no" ]; then progress "Loading bootsplash" SPLASHIMAGE="/splash/splash-full.png" # load uvesafb module if needed if [ -f "$MODULE_DIR/uvesafb.ko" -a ! -e /dev/fb0 ]; then progress "Loading kernel module uvesafb.ko" insmod "$MODULE_DIR/uvesafb.ko" || \ progress "... Failed to load kernel module uvesafb, skipping" # set framebuffer to a default resolution (1024x768-32) if [ ! "$SWITCH_FRAMEBUFFER" = "no" ]; then fbset -g 1024 768 1024 768 32 SPLASHIMAGE="/splash/splash-1024.png" fi fi if [ -e /dev/fb0 ]; then # load splash if [ -f /flash/oemsplash.png ]; then SPLASHIMAGE="/flash/oemsplash.png" elif [ -f /splash/splash.conf ]; then . /splash/splash.conf fi ply-image $SPLASHIMAGE > /dev/null 2>&1 fi fi } check_disks() { progress "Checking disks" if [ -x /bin/fsck_hfs ]; then # deal with hfs partitions hfsdiskprep fi } mount_flash() { progress "Mounting flash" mount_part "$boot" "/flash" "ro,noatime" } mount_storage() { progress "Mounting storage" if [ -n "$disk" ]; then if [ -n "$OVERLAY" ]; then OVERLAY_DIR=`cat /sys/class/net/eth0/address | tr -d :` mount_part "$disk" "/storage" "rw,noatime" mkdir -p /storage/$OVERLAY_DIR umount /storage # split $disk into $target,$options so we can append $OVERLAY_DIR options="${disk#*,}" target="${disk%%,*}" if [ "$options" = "$disk" ]; then disk="$target/$OVERLAY_DIR" else disk="$target/$OVERLAY_DIR,$options" fi fi mount_part "$disk" "/storage" "rw,noatime" fi } check_update() { progress "Checking for updates" # check for ATV1 mach-o-kernel if [ -f "/flash/com.apple.Boot.plist" -a -f "/flash/boot.efi" ]; then UPDATE_KERNEL="MACH_KERNEL" fi UPDATE_TAR=`ls -1 "$UPDATE_DIR"/*.tar 2>/dev/null | head -n 1` if [ -f "$UPDATE_DIR/$UPDATE_KERNEL" -a -f "$UPDATE_DIR/$UPDATE_SYSTEM" -o -f "$UPDATE_TAR" ] ; then if [ "$UPDATE_DISABLED" = "yes" ] ; then rm -rf $UPDATE_DIR/[0-9a-zA-Z]* &>/dev/null echo "Updating not supported on netboot. normal startup in 10s..." sync usleep 10000000 return 0 fi # check for .tar if [ -f "$UPDATE_TAR" ] ; then echo "Found new .tar archive. extracting..." mkdir -p $UPDATE_DIR/.tmp &>/dev/null tar -xf "$UPDATE_TAR" -C $UPDATE_DIR/.tmp &>/dev/null mv $UPDATE_DIR/.tmp/*/target/* $UPDATE_DIR &>/dev/null rm -f "$UPDATE_TAR" &>/dev/null rm -rf $UPDATE_DIR/.tmp &>/dev/null sync if [ ! -f "$UPDATE_DIR/$UPDATE_KERNEL" -o ! -f "$UPDATE_DIR/$UPDATE_SYSTEM" ] ; then echo "missing ${UPDATE_KERNEL} or ${UPDATE_SYSTEM}... normal startup in 10s" sync usleep 10000000 return 0 fi fi if [ -f "$UPDATE_DIR/.nocheck" ] ; then MD5_NOCHECK="1" fi # check md5 sums if .nocheck doesn't exist if [ "$MD5_NOCHECK" -eq "0" ] ; then if [ -f "$UPDATE_DIR/${UPDATE_KERNEL}.md5" -a -f "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" ] ; then # *.md5 size-check if [ ! -s "$UPDATE_DIR/${UPDATE_KERNEL}.md5" -o ! -s "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" ] ; then echo "zero-sized .md5 file..." MD5_FAILED="1" else sed -i 's#target#/storage/.update#g' "$UPDATE_DIR/${UPDATE_KERNEL}.md5" sed -i 's#target#/storage/.update#g' "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" echo "Checking ${UPDATE_KERNEL}.md5..." md5sum -c "$UPDATE_DIR/${UPDATE_KERNEL}.md5" || MD5_FAILED="1" echo "Checking ${UPDATE_SYSTEM}.md5..." md5sum -c "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" || MD5_FAILED="1" fi else echo "missing ${UPDATE_KERNEL}.md5 or ${UPDATE_SYSTEM}.md5..." MD5_FAILED="1" fi fi # get sizes FLASH_FREE=$(df /flash/ | awk '/[0-9]%/{print $4}') FLASH_FREE=$(( $FLASH_FREE * 1024 )) OLD_KERNEL=$(stat -t "/flash/$IMAGE_KERNEL" | awk '{print $2}') OLD_SYSTEM=$(stat -t "/flash/$IMAGE_SYSTEM" | awk '{print $2}') NEW_KERNEL=$(stat -t "$UPDATE_DIR/$UPDATE_KERNEL" | awk '{print $2}') NEW_SYSTEM=$(stat -t "$UPDATE_DIR/$UPDATE_SYSTEM" | awk '{print $2}') # old KERNEL+SYSTEM+free space - new KERNEL+SYSTEM must be higher then 5MB # at least 5MB free after update TMP_SIZE=$(($OLD_KERNEL+$OLD_SYSTEM+$FLASH_FREE-$NEW_KERNEL-$NEW_SYSTEM)) FLASH_FREE_MIN=$(($FLASH_FREE_MIN*1024*1024)) if [ $TMP_SIZE -ge $FLASH_FREE_MIN ]; then echo "Checking size: OK" else echo "Checking size: FAILED" SIZE_FAILED="1" fi # update if size check is ok if [ "$SIZE_FAILED" -eq "0" ] ; then # update if md5 check is ok or .nocheck exists if [ "$MD5_FAILED" -eq "0" -o "$MD5_NOCHECK" -eq "1" ] ; then update "Kernel" "$UPDATE_KERNEL" "/flash/$IMAGE_KERNEL" update "System" "$UPDATE_SYSTEM" "/flash/$IMAGE_SYSTEM" update_bootloader REBOOT="1" else rm -rf $UPDATE_DIR/[0-9a-zA-Z]* &>/dev/null echo "md5 check failed. normal startup in 30s..." sync usleep 30000000 fi rm -rf $UPDATE_DIR/[0-9a-zA-Z]* &>/dev/null else rm -rf $UPDATE_DIR/[0-9a-zA-Z]* &>/dev/null echo "size check failed. normal startup in 30s..." sync usleep 30000000 fi fi if test "$REBOOT" -eq "1"; then echo "System reboots now..." usleep 2000000 reboot fi } prepare_sysroot() { progress "Preparing system" if [ "$SYSTEM_TORAM" = "no" -o "$INSTALLED_MEMORY" -lt "$SYSTEM_TORAM_LIMIT" ]; then mount_part "/flash/$IMAGE_SYSTEM" "/sysroot" "ro,loop" else cp /flash/$IMAGE_SYSTEM /dev/$IMAGE_SYSTEM mount_part "/dev/$IMAGE_SYSTEM" "/sysroot" "ro,loop" fi mount --move /flash /sysroot/flash if [ -n "$disk" ]; then mount --move /storage /sysroot/storage fi [ -f "/sysroot/usr/lib/systemd/systemd" ] || error "final_check" "Could not find system." } if [ "${boot%%=*}" = "FILE" ]; then error "check arguments" "boot argument can't be FILE type..." fi # main boot sequence for BOOT_STEP in \ load_modules \ check_disks \ mount_flash \ load_splash \ mount_storage \ check_update \ prepare_sysroot; do $BOOT_STEP [ -n "$DEBUG" ] && break_after $BOOT_STEP done BOOT_STEP=final # no suspend when booted from nonpersistent storage STORAGE=$(cat /proc/mounts | grep " /sysroot/storage " | awk '{print $1}' | awk -F '/' '{print $3}') if [ -n "$STORAGE" ] ; then removable="/sys/class/block/*/$STORAGE/../removable" if [ -e $removable ] ; then if [ "$(cat $removable 2>/dev/null)" = "1" ] ; then SUSPEND_DISABLED=yes fi fi fi FLASH=$(cat /proc/mounts | grep " /sysroot/flash " | awk '{print $1}' | awk -F '/' '{print $3}') if [ -n "$FLASH" ] ; then removable="/sys/class/block/*/$FLASH/../removable" if [ -e $removable ] ; then if [ "$(cat $removable 2>/dev/null)" = "1" ] ; then SUSPEND_DISABLED=yes fi fi fi # move some special filesystems /bin/busybox mount --move /dev /sysroot/dev /bin/busybox mount --move /proc /sysroot/proc /bin/busybox mount --move /sys /sysroot/sys # tell OE settings addon to disable updates if [ "$UPDATE_DISABLED" = "yes" ] ; then echo "" > /sysroot/dev/.update_disabled fi # swap can not be used over nfs.(see scripts/mount-swap) if [ "$STORAGE_NETBOOT" = "yes" ] ; then echo "" > /sysroot/dev/.storage_netboot fi # no suspend when booted from nonpersistent storage if [ "$SUSPEND_DISABLED" = "yes" ] ; then echo "" > /sysroot/dev/.suspend_disabled fi # switch to new sysroot and start real init exec /bin/busybox switch_root /sysroot /usr/lib/systemd/systemd $INIT_ARGS error "switch_root" "Error in initramfs. Could not switch to new root"