#! /bin/bash
# vim: syntax=sh ts=4
# $Id: lilo.new 1023 2007-09-11 09:56:43Z olh $
#
# a simple lilo to store the boot loader and the kernel images 
# in bash2 ... Think different [tm]
#
# olh@suse.de
# jplack@suse.de
#

# interpret a file like the following:


## Modified by YaST2. Last modification on Sun Mar  7 16:27:46 2004
##
##
## default = linux
## timeout = 100
## boot = /boot/suse_linux_bootfile
## boot = B
##
## image = /boot/vmlinux
##     ###Don't change this comment - YaST2 identifier: Original name: linux###
##     label = Linux
##     root = /dev/iseries/vda3
##     append = "hwinfo=-cdrom 3 desktop"
##

## requires    fdisk   parted  stat tr


# Language kills all parsing effort
unset LANG
unset LC_CTYPE
# parted uses readline, which sends control chars if TERM=xterm
# this breaks parted --version
export TERM=dumb

CONFIG_LILO_CONF=/etc/lilo.conf
SHOW_OF_PATH="/bin/show_of_path.sh --quiet"
DEFAULT_BOOTFOLDER=suseboot
TEMP="${TMPDIR:-/tmp}/ppc_lilo"
MACHINE=
_sysfs_mounted=
_proc_mounted=
boot_once=

shopt -s extglob

# OPTION_BOOT is an array to contains the bootloader partition and/or targets
declare -a OPTION_BOOT
# OPTION_BOOT_COUNT is a counter to the array above
declare -i OPTION_BOOT_COUNT
# OPTION_BOOT_DISK_DEVICE_NAME is the kernel device name of the whole bootdisk
declare -a OPTION_BOOT_DISK_DEVICE_NAME
# OPTION_BOOT_DISK_DEVICE_NODE is the device node of a whole bootdisk
declare -a OPTION_BOOT_DISK_DEVICE_NODE
# OPTION_BOOT_PARTITION_NUMBER is the partition number
declare -a OPTION_BOOT_PARTITION_NUMBER
# OPTION_CLONE contains a space separated list of additional boot partitions
OPTION_CLONE=
# OPTION_BOOTFOLDER contains the MacOS folder with the bootstuff
OPTION_BOOTFOLDER=
# OPTION_ACTIVATE is a flag whether or not the boot partition must be set active in OF
OPTION_ACTIVATE=
# OPTION_USE_OS_CHOOSER is a flag whether or not the Forth script used as bootfile will open the screen
OPTION_USE_OS_CHOOSER=yes
# OPTION_FORCEFAT is a flag wether the bootloader on chrp should be on a 0xc FAT partition
OPTION_FORCEFAT=no
# OPTION_TIMEOUT contains the timeout variable in seconds
declare -i OPTION_TIMEOUT
# OPTION_MACOSTIMEOUT contains the timeout between linux/macos in seconds
declare -i OPTION_MACOSTIMEOUT=5
# OPTION_DEFAULT contains the default label
OPTION_DEFAULT=
# OPTION_ROOT contains the global or local root= device
OPTION_ROOT=
# OPTION_APPEND contains the global or local append= strings
OPTION_APPEND=
# OPTION_INITRD containes the global or local initrd filename
OPTION_INITRD=
OPTION_INITRD_DISK_DEVICE=
OPTION_INITRD_PARTITION_NUMBER=
OPTION_INITRD_PATH_ON_FILESYSTEM=
# CONFIG_PARSE_HASOTHER is a flag for os-chooser creation
CONFIG_PARSE_HASOTHER=
# CONFIG_IMAGE_FILE array contains the kernel image for a section
declare -a CONFIG_IMAGE_FILE
declare -a CONFIG_IMAGE_FILE_DISK_DEVICE
declare -a CONFIG_IMAGE_FILE_PARTITION_NUMBER
declare -a CONFIG_IMAGE_FILE_PATH_ON_FILESYSTEM
declare -a CONFIG_IMAGE_FILE_YABOOT_CONF_PATH
# CONFIG_IMAGE_OTHER array contains the device of MacOS
declare -a CONFIG_IMAGE_OTHER
declare -a CONFIG_IMAGE_OTHER_DISK_DEVICE
declare -a CONFIG_IMAGE_OTHER_PARTITION_NUMBER
# CONFIG_IMAGE_LABEL array contains the name of the section
declare -a CONFIG_IMAGE_LABEL
# CONFIG_IMAGE_INITRD array contains the (optional) initrd file
declare -a CONFIG_IMAGE_INITRD
declare -a CONFIG_IMAGE_INITRD_DISK_DEVICE
declare -a CONFIG_IMAGE_INITRD_PARTITION_NUMBER
declare -a CONFIG_IMAGE_INITRD_PATH_ON_FILESYSTEM
declare -a CONFIG_IMAGE_INITRD_YABOOT_CONF_PATH
# CONFIG_IMAGE_APPEND array contains the (optional) kernel commandline options
declare -a CONFIG_IMAGE_APPEND
# CONFIG_IMAGE_COPY array contains copy flag for images on macs
declare -a CONFIG_IMAGE_COPY
# CONFIG_IMAGE_OPTIONAL array contains flag about image/initrd availibility
declare -a CONFIG_IMAGE_OPTIONAL
# CONFIG_IMAGE_COUNT is a simple counter of image sections
declare -i CONFIG_IMAGE_COUNT
# used by can_yaboot_read function
declare -a _filesystem

err_var1=
FDISK=/sbin/fdisk
PARTED="/usr/sbin/parted -s"
# the parted kids keep the userinterface stable:
#-
# GNU Parted 1.6.25.1
#-
# GNU Parted 1.7.1
#-
# parted (GNU parted) 1.8.6
# Copyright (C) 2007 Free Software Foundation, Inc.
# more useless blurb
#-
PVERSION=$($PARTED --version | sed -n '/^GNU Parted /s@GNU Parted @@p;/^parted (GNU parted) /s@parted (GNU parted) @@p')
read a b c d e f  <<< "${PVERSION//./ }"
PVERSION=$(( a*1000000 + b*1000 + c ))	# e.g. 1006022
STAT_CMD=/usr/bin/stat


# trap EXIT function
function clean_environment () {
    cd /
    [[ $(</proc/mounts) == *$TEMP/boot\ * ]] && umount $TEMP/boot
    rm -rf $TEMP
    [ $_sysfs_mounted ] && umount /sys
    [ $_proc_mounted ] && umount /proc
} #end function clean_environment


function Usage() {
       cat  <<EOHELP
lilo for PowerPC 10.1.22
configures the Linux Loader on a few different PowerPC board types,
based on the configuration file /etc/lilo.conf.
This includes IBM RS/6000 and pSeries models, IBM legacy iSeries,
Apple PowerMacs (OldWorld and NewWorld) and older PReP based boards.

Known options are:
    --quiet         sets an internal variable ;-)
    --debug | -d    sets another internal variable ;-)
    --get-arch      returns the board type lilo is running on
                    known values are:
		    iseries chrp pmac_new pmac_old pegasos
    -R "label opts" set default command line for next reboot (boot once)
    --version | -v  displays the version number
    --help          displays this help text
EOHELP
}


function error() {
    _severity="ERROR"
    _error "$@"
    exit 1
}

function warning() {
    _severity="Warning"
    _error "$@"
}


function _error() {
    # helper function to print _all_ error messages in a unified form
    # argument list consists of a list of msg numbers and custom strings
    until  [ "$#" = 0 ] ; do
	case "$1" in
	    1)
	        echo '************************************************************'
	       	echo '* You must create a PPC PReP Boot partition (type 0x41) or *'
	       	echo '* a FAT partition for the CHRP bootloader to be installed. *'
	       	echo '************************************************************'
		;;
	    2)
	       	echo '************************************************************'
	       	echo '* There is more than one PPC PReP Boot (type 0x41) or FAT  *'
	       	echo '* on this system. Please specify the boot partition NUMBER *'
	       	printf '* in boot= %-30s                  *\n' $err_var1
	       	echo '************************************************************'
		;;
	    3)
	        echo '************************************************************'
		echo '* You have to create a PPC PReP Boot partition (type 0x41) *'
		echo '* for the kernel binary to be installed.                   *'
		echo '************************************************************'
		;;
	    4)
       	    	echo '************************************************************'
	    	echo '* You have to create a PPC PReP Boot partition (type 0x41) *'
	    	echo '* for the CHRP bootloader to be installed.                 *'
	    	echo '************************************************************'
		;;
	    5)
	       	echo '************************************************************'
	       	echo '* There is more than one PPC PReP Boot partition (type     *'
	       	echo '* 0x41) on this system. Please specify the boot partition  *'
	        printf '* NUMBER in boot = %-30s          *\n' $err_var1
	       	echo '************************************************************'
		;;
	    6)
	        echo "${_severity}: in config file, no label in section image/other = $err_var1"
		;;
	    7)
	        echo "${_severity}: in config file, $option option must not be in an image/other section!"
		;;
	    8)
		echo "${_severity}: in config file, $option option has to be in an image/other section!"
		;;
	    9)
	        echo "${_severity}: in config file, boot = $err_var1 is not 41 PReP"
		;;
	    10)
	        echo "${_severity}: in config file, guessing of boot partition failed"
		;;
	    11)
		echo "${_severity}: parted complains:"
		cat $TEMP/parted.log
		;;
	
	    *)  # plain custom string
	        echo "${_severity}: $1"
		;;
	esac >&2
	shift
    done   
}


function get_of_path() {
    local errnum=$?

    $SHOW_OF_PATH $1
    errnum=$?

    #if (( errnum != 0 )); then
    #	error "show_of_path.sh returned error $errnum while analysing path $1"
    #fi
}


function is_on_primary() {
    local of_path="$1"

    # check for invalid of path
    [ "$of_path" ] || return 1;

    # check whether file is on a primary partition [1234]
    if [[ "${of_path%,/*}" != *:[1234] ]]; then return 1; fi

    return 0
}

function is_on_iscsi() {
	local path

	path=`get_of_path "$1"`
	path="${path#*:}"
	case "$path" in
		iscsi,*)
		return 0
		;;
	esac
	return 1
}

# convert a number of 512 byte sectors in a format that parted accepts and the
# other way `round. Behaviour changed in subminor version. Hurray :-(
if (( PVERSION > 1006022 )); then

    function sect2parted() {
	local s=$1
	echo ${s}s
    }
    function parted2sect() {
	local s=$1
	if [ ${s:0-1} == "s" ]; then
	    echo  ${s/%s}
	else
	    echo 0
	fi
    }
    function parted_print(){
	local device=$1
	$PARTED "$device" unit s print | sed '/^[[:blank:]]\+[0-9]/s@^[[:blank:]]\+@@'
    }

else

    function sect2parted() {
	local s=$1
	s=$(( (s + 1) * 1000 / 2 / 1024 ))
	while (( ${#s} < 4 )); do s="0$s"; done
	echo ${s%???}.${s:$[${#s}-3]}
    }
    function parted2sect() {
	local s=$1
	s=${s/.}
	s=${s##*(0)}
	echo $(( s * 1024 * 2 / 1000 ))
    }
    function parted_print() {
	local device=$1
	$PARTED "$device" print
    }

fi

function parted_call() {
    # call parted binary
    $PARTED "$@"
    local ret=$?

    # wait for pending triggered udev events.
    # until we know how to do better, just wait for _all_ events to finish
    local loop=300	# wait for a maximum of 30sec
    while [ -d /dev/.udev/queue ] && (( loop-- > 0 )); do
	sleep 0.1
    done

    return $ret
}


# check whether a file lies on a file system that is
# readable by yaboot
function can_yaboot_read () {
    local path="$1"

    # check for invalid path
    [ -e "$path" ] || return 1;

    # initialize _filesystem array when needed
    if [ "${_filesystem[0]}" != "invalid" ]; then
        # check all mounted local partitions and remember file systems
	local device mount type opts

	while read device mount type opts; do
	    if [ "${device:0:1}" = "/" ]; then
		local -i dn=$(( `stat -L --format="0x%D" $mount` ))
		(( dn == 0 )) && continue;
		_filesystem[$dn]="$type";
	    fi
	done < /proc/mounts
	_filesystem[0]="invalid"
    fi

    local fs=${_filesystem[$(( ` stat -L --format="0x%D" $path` ))]}

    # check for a file system yaboot can handle
    [[ "$fs" == @(ext2|ext3|msdos|vfat|reiserfs|iso9660|xfs) ]] || return 1;

    # return true finally
    return 0
}

# some cases may not work
function get_relative_path_on_filesystem() {
	local file
	local file_relative_path
	local file_majorminor
	local file_dirname

	file=$1
	file_majorminor=`$STAT_CMD -L --format="0x%D" "$file"`
	file_dirname=$file
	file_relative_path="${file##*/}"

	until test -z "$file_dirname"
	do
		file_dirname="${file_dirname%/*}"
		if test "`$STAT_CMD -L --format="0x%D" ${file_dirname%/*}/`" = "$file_majorminor"
		then
			file_relative_path="${file_dirname##*/}/$file_relative_path"
		else
			break
		fi
	done
	case "$file_relative_path" in
		/*) ;;
		*) file_relative_path="/$file_relative_path" ;;
	esac
	echo "$file_relative_path"
}

function collect_path_info() {
	local querytype
	local direntry
	local direntry_majorminor
	local direntry_major direntry_minor
	local dev
	local direntry_sysfspath
	local direntry_kernel_disk_name
	local direntry_kernel_partition_name
	local direntry_kernel_partition_number

	querytype=$1
	direntry=$2

	# iSeries slots
	[[ "$direntry" == [ABCD] ]] && return

	# get major:minor pair
	if test -c "$direntry"
	then
		echo >&2 "'$direntry' is a char device"
		return 1
	elif test -b "$direntry"
	then
		direntry_majorminor=`$STAT_CMD -L --format="0x%t 0x%T" "$direntry"`
		direntry_majorminor=`printf '%d:%d' $direntry_majorminor`
	else
		direntry_majorminor=`$STAT_CMD -L --format="%d" "$direntry"`
		direntry_major="$[direntry_majorminor >> 8]"
		direntry_minor="$[direntry_majorminor & 255]"
		direntry_majorminor=$direntry_major:$direntry_minor
	fi
	# find kernel name for major:minor pair
	: direntry_majorminor $direntry_majorminor
	for dev in /sys/block/*/*/dev /sys/block/*/dev
	do
		: looking at $dev
		if test "$(< $dev)" = "$direntry_majorminor"
		then
			direntry_sysfspath=$dev
			break
		fi
	done
	if test -z "$direntry_sysfspath"
	then
		echo >&2 "'$direntry' has no sysfs entry for major:minor $direntry_majorminor"
		return 1
	fi
	# collect names and numbers
	if test -f "${direntry_sysfspath%/dev}/range"
	then
		direntry_kernel_disk_name="${direntry_sysfspath%/dev}"
		direntry_kernel_disk_name="${direntry_kernel_disk_name##*/}"
		direntry_kernel_partition_name=
		direntry_kernel_partition_number=
	elif test -f "${direntry_sysfspath%/dev}/../range"
	then
		direntry_kernel_partition_name="${direntry_sysfspath%/dev}"
		direntry_kernel_partition_name="${direntry_kernel_partition_name##*/}"
		direntry_kernel_disk_name="${direntry_sysfspath%/dev}"
		direntry_kernel_disk_name="${direntry_kernel_disk_name%/*}"
		direntry_kernel_disk_name="${direntry_kernel_disk_name##*/}"
		direntry_kernel_partition_number="${direntry_kernel_partition_name%%+([0-9])}"
		direntry_kernel_partition_number="${direntry_kernel_partition_name#$direntry_kernel_partition_number}"
	else
		echo >&2 "can not find sysfs info for '$direntry' due to changed interfaces"
		return 1
	fi
	# return info
	case "$querytype" in
		partition_number)
		echo "$direntry_kernel_partition_number"
		return 0
		;;
		partition_name)
		echo "$direntry_kernel_partition_name"
		return 0
		;;
		disk_name)
		echo "$direntry_kernel_disk_name"
		return 0
		;;
	esac
	echo >&2 "'$querytype' not handled for '$direntry'"
	return 1
}

function fsize() { # get file size in blocks
    local s r
    if [ -z "$1" ]; then
	echo 0
    else
    	# can not use ls -s because the zimage is a sparse file
	read s r < <( $STAT_CMD  -L -c %s "$1" )
	s=$(( ( $s / 512 ) + 1 ))
	echo $s
    fi
}

function find_interrupt_controller_property() {
	local f
	# to be called with "/proc/device-tree" as entry directory
	for f in "$1"/*
	do
		if test -L "$f"
		then
			continue
		fi
		if test -d "$f"
		then
			find_interrupt_controller_property "$f"
			if test "$?" = "1"
			then
				return 1
			fi
		fi
		if test -f "$f"
		then
			if test "${f##*/}" = "interrupt-controller"
			then
				return 1
			fi
		fi
	done
	return 0
}

function check_arch () {
	# check for the current ppc subarch
	# this function set the global MACHINE to either
	# * iseries
	# * pmac_new
	# * pmac_old
	# * chrp
	# * pegasos
	# * prep
	# * ps3
	# or nothing at all if the boardtype is not recognized
	# 
	local prop
	local board_type

	#
	if test -d /proc/iSeries
	then
		board_type="iseries"
	fi
	#
	if test -z "$board_type"
	then
		if test -f /proc/device-tree/compatible
		then
			prop="`tr '\0' '\n' < /proc/device-tree/compatible`"
		else
			read prop < /proc/device-tree/model
		fi
		: prop $prop
		case "$prop" in
			*MacRISC*)
				board_type=pmac
				;;
			*Power\ Macintosh*)
				board_type=pmac
				;;
			IBM,*)
				board_type=chrp
				;;
			Momentum,Maple-D)
				board_type=chrp
				;;
			Momentum,Maple-L)
				board_type=chrp
				;;
			Momentum,Maple)
				board_type=chrp
				;;
			Pegasos2)
				board_type=pegasos
				;;
			PS3PF|sony,ps3|SonyPS3)
				board_type=ps3
				;;
		esac
		if test "$board_type" = "pmac"
		then
			# PowerMacs with an "interrupt-controller" property are all NewWorld
			if find_interrupt_controller_property /proc/device-tree
			then
				board_type=pmac_old
			else
				board_type=pmac_new
			fi
		fi
	fi

	if [ -z "$board_type" ]; then
	    case $(</proc/cpuinfo) in
		*PReP*)
		    board_type=prep
		    ;;
		*CHRP*)
		    board_type=chrp
		    ;;
	    esac
	fi
	if [ -z "$board_type" ]; then
	    case $(</proc/device-tree/device_type) in
		chrp)
		    board_type=chrp
		    ;;
	    esac
	fi
		
	MACHINE="$board_type"

} #end function check_arch


#
#   main script
#

#  parse options
#
while [ "$1" ]; do case "$1" in
    --quiet|-q)
       quietmode=1
       ;;
    --debug|-d)
       debug=1
       ;;
    --get-arch)
       get_arch=1
       ;;
    -R)
       boot_once=$2
       shift
       ;;
    --version|-v)
       echo "lilo for PowerPC 10.1.22"
       exit 0
       ;;
    --help)
       Usage
       exit 0
       ;;

    *)
        echo 1>&2 "lilo: Option \"$1\" not supported"
	Usage;
        exit 1
	;;
esac; shift; done



function running_on_iseries () {
    source /lib/lilo/lilo-iseries.lib
    running_on_iseries;
}


function running_on_chrp () {
    source /lib/lilo/lilo-chrp.lib
    running_on_chrp;
}


function running_on_prep () {
    source /lib/lilo/lilo-chrp.lib
    running_on_prep;
}

function running_on_pegasos () {
    source /lib/lilo/lilo-chrp.lib
    running_on_pegasos;
}

function running_on_pmac_old () {
    source /lib/lilo/lilo-pmac.lib
    running_on_pmac_old;
}


function running_on_pmac_new () {
    source /lib/lilo/lilo-pmac.lib
    running_on_pmac_new;
}


function parse_config_file () {
    # parse the lilo.conf and place it in CONFIG_IMAGE_FILE[]
    declare option
    local separator value
    # CONFIG_PARSE_HASIMAGE is a flag if an image/other section exists
    local CONFIG_PARSE_HASIMAGE

    if [ ! -f $CONFIG_LILO_CONF ]; then
	error "Config file $CONFIG_LILO_CONF not found"
    fi

    CONFIG_IMAGE_COUNT=0
    OPTION_BOOT_COUNT=0
    while read; do
	# strip comments, heading and trailing whitespace and empty lines
	REPLY=${REPLY%%#*}
	REPLY=${REPLY%%+([ 	])}
	REPLY=${REPLY##+([ 	])}
	if [ -z "$REPLY" ]; then continue; fi
	REPLY=${REPLY/=/ = }
	REPLY=${REPLY/\"\"/}	# replace quoted empty string by "itself"
 
	read option separator value <<< "$REPLY"
	#	echo option "$option"
	#	echo separator "$separator"
	#	echo value $value

	if [ -n "$separator" ] && [ "$separator" != "=" ]; then
	    echo "Illegal separator '$separator', line ignored"
	    continue
	fi

	case "$option" in
	    boot)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		if [ -L "$value" ]; then
		    OPTION_BOOT[$OPTION_BOOT_COUNT]=$(readlink -f $value)
		else
		    OPTION_BOOT[$OPTION_BOOT_COUNT]=$value
		fi
		let OPTION_BOOT_COUNT++
		;;
	    clone)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		# this is valid for chrp only ...
		if [ -L "$value" ]; then
		    OPTION_CLONE=$(readlink -f $value)
		else
		    OPTION_CLONE=$value
		fi
		;;
	    activate)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_ACTIVATE="yes"
		;;
	    no_os_chooser)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_USE_OS_CHOOSER="no"
		;;
	    force_fat)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_FORCEFAT="yes"
		;;
	    bootfolder)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_BOOTFOLDER=":${value}"
		;;
  	    timeout)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_TIMEOUT=$value
		;;
  	    macos_timeout)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_MACOSTIMEOUT=$value
		;;
	    default)
		[ "$CONFIG_PARSE_HASIMAGE" ] && error 7
		OPTION_DEFAULT=$value
		;;
	    image)
		# check if previous image section has a label
		if [ "$CONFIG_PARSE_HASIMAGE" ] ; then
		    err_var1="${CONFIG_IMAGE_FILE[$CONFIG_IMAGE_COUNT]:-${CONFIG_IMAGE_OTHER[$CONFIG_IMAGE_COUNT]}}"
		    if [ -z "$err_var1" ]; then	error 6; fi
		fi
		CONFIG_PARSE_HASIMAGE=true
		let CONFIG_IMAGE_COUNT++
		CONFIG_IMAGE_FILE[$CONFIG_IMAGE_COUNT]=$value
		CONFIG_IMAGE_OPTIONAL[$CONFIG_IMAGE_COUNT]="no"
		;;
	    other)
		# check if previous image section has a label
		if [ "$CONFIG_PARSE_HASIMAGE" ] ; then
		    err_var1="${CONFIG_IMAGE_FILE[$CONFIG_IMAGE_COUNT]:-${CONFIG_IMAGE_OTHER[$CONFIG_IMAGE_COUNT]}}"
		    if [ -z "$err_var1" ]; then	error 6; fi
		fi
		CONFIG_PARSE_HASIMAGE=true
		CONFIG_PARSE_HASOTHER=true
		let CONFIG_IMAGE_COUNT++
		CONFIG_IMAGE_OTHER[$CONFIG_IMAGE_COUNT]=$value
		CONFIG_IMAGE_OTHER_DISK_DEVICE[$CONFIG_IMAGE_COUNT]="`collect_path_info disk_name $value`"
		CONFIG_IMAGE_OTHER_PARTITION_NUMBER[$CONFIG_IMAGE_COUNT]="`collect_path_info partition_number $value`"
		;;
	    root)
		# FIXME: fix some common typos like missing quotes of spaces
		# like that? "'${a//*( )=*( )/=}'"
		if [ -z "$CONFIG_PARSE_HASIMAGE" ] ; then
		    OPTION_ROOT=$value
		else
		    CONFIG_IMAGE_ROOT[$CONFIG_IMAGE_COUNT]=$value
		fi
		;;
	    copy)
		[ "$CONFIG_PARSE_HASIMAGE" ] || error 8
		CONFIG_IMAGE_COPY[$CONFIG_IMAGE_COUNT]="true"
		;;
	    optional)
		[ "$CONFIG_PARSE_HASIMAGE" ] || error 8
		CONFIG_IMAGE_OPTIONAL[$CONFIG_IMAGE_COUNT]="yes"
		;;
	    label)
		[ "$CONFIG_PARSE_HASIMAGE" ] || error 8
		CONFIG_IMAGE_LABEL[$CONFIG_IMAGE_COUNT]=$value
		;;
	    append)
		# FIXME: fix some common typos like missing quotes of spaces
		# like that? "'${a//*( )=*( )/=}'"
		if [ -z "$CONFIG_PARSE_HASIMAGE" ] ; then
		    # use eval to strip ""
		    eval OPTION_APPEND=$value
		else
		    # use eval to strip ""
		    eval CONFIG_IMAGE_APPEND[$CONFIG_IMAGE_COUNT]=$value
		fi
		;;
	    sysmap)
		echo "sysmap= is not required anymore, remove it from your lilo.conf file"
		;;
	    initrd)
		if [ -z "$CONFIG_PARSE_HASIMAGE" ] ; then
		    if [ -f "$value" ] ; then
			OPTION_INITRD=$value
			OPTION_INITRD_DISK_DEVICE="`collect_path_info disk_name $value`"
			OPTION_INITRD_PARTITION_NUMBER="`collect_path_info partition_number $value`"
			OPTION_INITRD_PATH_ON_FILESYSTEM="`get_relative_path_on_filesystem $value`"
		    else
			error "global initrd $value: No such file or directory"
		    fi
		else
		    CONFIG_IMAGE_INITRD[$CONFIG_IMAGE_COUNT]=$value
		fi
		;;		
	    *)
	        error "!!!!!!!!!! unkown option $option !!!!!!!!!!!!!"
		;;
	esac
    done < $CONFIG_LILO_CONF
    
} #end function parse_config_file

function update_option_boot () {
	local i=$1
	# this will fail if the kernel device name contains a '!'
	OPTION_BOOT_DISK_DEVICE_NAME[$i]="`collect_path_info disk_name ${OPTION_BOOT[$i]}`"
	OPTION_BOOT_DISK_DEVICE_NODE[$i]="/dev/${OPTION_BOOT_DISK_DEVICE_NAME[$i]}"
	OPTION_BOOT_PARTITION_NUMBER[$i]="`collect_path_info partition_number ${OPTION_BOOT[$i]}`"
}

function check_config_file () {
    local i
    if [ -z $OPTION_BOOT ]; then
	# only pegasos and PS3 do not need boot=
	if [[ "$MACHINE" != @(pegasos|ps3) ]]; then
	    error "boot=<partition> is not specified!"
	fi
    else
	if [ "$MACHINE" != "iseries" -a "$MACHINE"  != "pegasos" ]; then
	    if [ ! -b $OPTION_BOOT ]; then
		error "boot = $OPTION_BOOT is not a valid block device"
	    fi
	    if [ $OPTION_BOOT_COUNT -ne 1 ]; then
		error "only one boot= line allowed here!"
	    fi
	fi
    fi
    if [ "$CONFIG_IMAGE_COUNT" = 0 ] ; then
	error "no image section is specified"
    fi
    
    for (( i=1; i < CONFIG_IMAGE_COUNT; i++ )); do
	if [ -f "${CONFIG_IMAGE_FILE[$i]}" ] ; then
	    CONFIG_IMAGE_FILE_DISK_DEVICE[$i]="`collect_path_info disk_name ${CONFIG_IMAGE_FILE[$i]}`"
	    CONFIG_IMAGE_FILE_PARTITION_NUMBER[$i]="`collect_path_info partition_number ${CONFIG_IMAGE_FILE[$i]}`"
	    CONFIG_IMAGE_FILE_PATH_ON_FILESYSTEM[$i]="`get_relative_path_on_filesystem ${CONFIG_IMAGE_FILE[$i]}`"
	else
	    if [ "${CONFIG_IMAGE_OPTIONAL[$i]}" = "no" -o "${CONFIG_IMAGE_LABEL[$i]}" = "$OPTION_DEFAULT" ] ; then
		error   "${CONFIG_IMAGE_LABEL[$i]}: image = ${CONFIG_IMAGE_FILE[$i]}: No such file or directory"
	    else
		warning "${CONFIG_IMAGE_LABEL[$i]}: image = ${CONFIG_IMAGE_FILE[$i]}: No such file or directory"
		CONFIG_IMAGE_OPTIONAL[$i]="skip"
	    fi
	fi
	if [ ! -z "${CONFIG_IMAGE_INITRD[$i]}" ] ; then
		if [ -f "${CONFIG_IMAGE_INITRD[$i]}" ] ; then
		    CONFIG_IMAGE_INITRD_DISK_DEVICE[$i]="`collect_path_info disk_name ${CONFIG_IMAGE_INITRD[$i]}`"
		    CONFIG_IMAGE_INITRD_PARTITION_NUMBER[$i]="`collect_path_info partition_number ${CONFIG_IMAGE_INITRD[$i]}`"
		    CONFIG_IMAGE_INITRD_PATH_ON_FILESYSTEM[$i]="`get_relative_path_on_filesystem ${CONFIG_IMAGE_INITRD[$i]}`"
		else
		    if [ "${CONFIG_IMAGE_OPTIONAL[$i]}" = "no" -o "${CONFIG_IMAGE_LABEL[$i]}" = "$OPTION_DEFAULT" ] ; then
				error   "${CONFIG_IMAGE_LABEL[$i]}: initrd = ${CONFIG_IMAGE_INITRD[$i]}: No such file or directory"
		    else
				warning "${CONFIG_IMAGE_LABEL[$i]}: initrd = ${CONFIG_IMAGE_INITRD[$i]}: No such file or directory"
				CONFIG_IMAGE_OPTIONAL[$i]="skip"
		    fi
		fi
	fi
    done
    
    #
    #  loop for multiple OPTION_BOOT entries
    #
    for (( i=0; i<OPTION_BOOT_COUNT; i++ )); do
    	update_option_boot $i
    done
} #end function check_config_file

function setup_boot_once () {
	local blah
	local boot_once
	local -i driver_count_offset=16
	local -i driverlist_offset=18
	local -i driverlist_entry_size=8
	local -i driver_count
	local -i writing_to_disk_from_firmware_is_broken

	boot_once=$1
	if test -z "$boot_once"
	then
		return
	fi
	pushd "/sys/block/${OPTION_BOOT_DISK_DEVICE_NAME[0]}/device" > /dev/null
	cd `pwd -P`
	until test "$PWD" = "/"
	do
		if test -f devspec
		then
			blah="`cat devspec`"
			echo "devspec '$blah' in $PWD"
			blah="`cat /proc/device-tree/$blah/device_type`"
			echo "device_type is '$blah'"
			break
		fi
		cd ..
	done
	popd > /dev/null
	case "$blah" in
		pci-ide|pci-ata)
			# pci-ide is the CMD646 onboard controller in B&W (untested)
			# and early G4 (tested) models. Writing to disk from yaboot
			# will corrupt the first 32k with random data
			echo "The firmware can not write reliable to ${OPTION_BOOT_DISK_DEVICE_NAME[0]}."
			writing_to_disk_from_firmware_is_broken=1
		;;
		ata)
			# ata is used on pmac (tested) and QS22
			writing_to_disk_from_firmware_is_broken=0
		;;
		ide)
			# ide is used on first gen iMac (untested) and JS20
			writing_to_disk_from_firmware_is_broken=2
		;;
		*)
			# everything else is untested and will cause data integrity issues
			writing_to_disk_from_firmware_is_broken=2
		;;
	esac
	if test $writing_to_disk_from_firmware_is_broken = 0
	then
		echo "The dubios lilo-once feature will most likely work with your firmware."
		echo "But better fix YaST to not force reboot after first stage."
		echo "Its pointless. Its wrong. It does not fix anything."
	elif test $writing_to_disk_from_firmware_is_broken = 1
	then
		echo "The firmware disk driver will corrupt your disk when writing to the first disk block"
		echo "$0 -R is disabled"
		return
	else
		echo "The firmware disk driver may corrupt your disk when writing to the first disk block"
		echo "$0 -R is disabled because this system is untested."
		return
	fi
	return
	if test "`dd if=${OPTION_BOOT_DISK_DEVICE_NODE[0]} count=2 bs=1 2>/dev/null`" != "ER"
	then
		echo "Block zero does not start with 'ER', no mac label on ${OPTION_BOOT_DISK_DEVICE_NODE[0]}"
		return
	fi
	if test "`dd if=${OPTION_BOOT_DISK_DEVICE_NODE[0]} count=2 bs=1 skip=512 2>/dev/null`" != "PM"
	then
		echo "Block one does not start with 'PM', no (or empty) mac label on ${OPTION_BOOT_DISK_DEVICE_NODE[0]}"
		return
	fi
	echo "reading driver_count from ${OPTION_BOOT_DISK_DEVICE_NODE[0]}"
	read blah driver_count < <( dd if="${OPTION_BOOT_DISK_DEVICE_NODE[0]}" bs=1 skip=$driver_count_offset count=2 2>/dev/null | od --read-bytes=2 --width=2 -t x2 )
	echo "driver_count is $driver_count"
	offset_into_driverlist=$(( ($driver_count * $driverlist_entry_size) + $driverlist_offset))
	echo "offset into driverlist is $offset_into_driverlist"
	if test "$offset_into_driverlist" -lt 512
	then
		echo -n $boot_once | dd of=${OPTION_BOOT_DISK_DEVICE_NODE[0]} bs=1 seek=$(( $offset_into_driverlist )) count=$(( 512 - $offset_into_driverlist  ))
	fi
}

# check for requirements:
#   /proc
#   /sys

# assert that /proc is mounted, else try to mount, on fail complain
if test -d /proc/1; then
    :
elif mount -t proc proc /proc; then
    _proc_mounted=1
else
    error "proc not mounted and attempt to mount /proc failed"
fi


# assert that /sys is mounted, else try to mount, on fail complain
if test -d /sys/block/; then
    :
elif mount -t sysfs sysfs /sys; then
    _sysfs_mounted=1
else
    error "sysfs not mounted on /sys and attempt to mount failed" "may be no kernel 2.6.x?"
fi


#
# prepare environment:
#
rm -rf $TEMP
mkdir -p $TEMP
trap "clean_environment" EXIT INT

#
# here we go
#

check_arch
if [ "$get_arch" ]; then
    echo "$MACHINE"
    exit 0
fi
parse_config_file
check_config_file

if [ "$boot_once" ]; then
	setup_boot_once "$boot_once"
	exit 0
fi
function running_on_ps3() {
local device
local kernel_filename
local initrd_filename 
local root_mount boot_mount
local file_majorminor file_major file_minor
local file_sysfs_path
	
	if [ -f /etc/kboot.conf ]; then
		return
	fi

	root_mount=`$STAT_CMD -L -c %D /`
	boot_mount=`$STAT_CMD -L -c %D /boot/vmlinux`

	if [ "$root_mount" = "$boot_mount" ]; then
		kernel_filename=/boot/vmlinux
		initrd_filename=/boot/initrd
	else
		kernel_filename=/vmlinux
		initrd_filename=/initrd
	fi

    	file_majorminor=$($STAT_CMD -L --format="%d" "/boot/vmlinux")
    	file_major="$[file_majorminor >> 8]"
    	file_minor="$[file_majorminor & 255]"

    	file_majorminor=$file_major:$file_minor


	for i in /sys/block/*/*/dev /sys/block/*/dev
	do
    		: looking at $i
    		if [ "$(< $i)" = "$file_majorminor" ] ; then file_sysfs_path=$i ; break ; fi
	done

	if [ -z "$file_sysfs_path" ] ; then
    		error "can not find major:minor $file_majorminor for $file"
	fi
	file_sysfs_path="${file_sysfs_path%/dev}"
	file_sysfs_path="${file_sysfs_path##*/}"
	device=/dev/$file_sysfs_path

	cat > /etc/kboot.conf <<EOF
default=openSuSE
timeout=20
openSuSE="$device:$kernel_filename initrd=$device:$initrd_filename quiet panic=42 sysrq=1"
EOF

}

case "$MACHINE" in
    pmac_new)  running_on_pmac_new ;;
    pmac_old)  running_on_pmac_old ;;
    chrp)      running_on_chrp     ;;
    prep)      running_on_prep     ;;
    iseries)   running_on_iseries  ;;
    pegasos)   running_on_pegasos  ;;
    ps3)       running_on_ps3      ;;
esac

#
#
# Local variables:
#     mode: sh
#     mode: font-lock
#     mode: auto-fill
#     sh-indent: 4
#     sh-multiline-offset: 2
#     sh-if-re: "\\s *\\b\\(if\\)\\b[^=]"
#     fill-column: 78
# End:
#
