#!/bin/bash
#
# olpc-configure       Perform one-time configuration
#
# chkconfig: 345 01 01
# description: olpc-configure performs one-time configuration
# processname: olpc-configure

# Bump this version each time compatibility gets broken in ways that require
# regenerating the configuration files
OLPC_CONFIGURE_VERSION=9

OLPC_HOME=/home/olpc
MFG_DATA=/ofw/mfg-data
XO_VERSION=0

get_xo_version() {
	# This requires an OFW that runs setup-smbios at boot.
	if [ -e "/sys/class/dmi/id/product_name" ]; then
		local name=$(</sys/class/dmi/id/product_name)
		local version=$(</sys/class/dmi/id/product_version)
		if [ "${name}" == "XO" ]; then
			if [ "${version}" == "1" ]; then
				XO_VERSION="1"
			elif [ "${version}" == "1.5" ]; then
				XO_VERSION="1.5"
			fi
		fi
	# This fallback detection method is necessary even if we ship an updated
	# OpenFirmware which enables SMBIOS within the signed OS image because
	# a laptop may use the previous firmware until it is plugged to AC
	# during boot, thus misconfiguring the keyboard.
	elif [ -e "/ofw/banner-name" ]; then
		case "$(</ofw/banner-name)" in
			OLPC\ B*|OLPC\ C*) XO_VERSION="1" ;;
		esac
	fi
}

# based on code from OLPC's NM-0.6
set_hostname() {
	[ -e "/sys/class/net/eth0/address" ] || return
	local addr=$(</sys/class/net/eth0/address)
	[ "${#addr}" == 17 ] || return
	addr=${addr:9}
	local hname=xo-${addr//:/-}
	hostname ${hname}.localdomain

	# check if we have to rewrite hosts
	grep --silent "127\.0\.0\.1.*${hname}" /etc/hosts && return
	sed -i "1i\
127.0.0.1 ${hname}.localdomain ${hname} localhost" /etc/hosts
}

initialize_ptmode()
{
	# the hgpk driver for the "old" ALPS touchpads has a
	# means of switching between capacitive "finger" mode and
	# stylus-only tablet mode.  make it possible for the user
	# to switch it back and forth.
	PTMODE=/sys/devices/platform/i8042/serio1/ptmode 
	test -e $PTMODE && chmod a+rw $PTMODE
}

configure_hw_1_0() {
	# load alsa configuration here since doing it at module load time wasn't working
	/usr/sbin/alsactl restore

	# workaround activities lowering PCM level
	amixer -q sset PCM "90%" unmute

	# Libertas Marvell LED configuration
	# map LED1 to GPIO1, LED2 to GPIO12, LED3 to GPIO16
	/sbin/iwpriv eth0 ledgpio 1 1 2 12 3 16 > /dev/null

	initialize_ptmode
}

configure_hw_1_5() {
	amixer -q sset Master -- "-13.0dB"
	amixer -q sset PCM "100%"
	amixer -q sset "DC Input Bias" Off
	amixer -q sset "DC Mode Enable" mute
	amixer -q sset 'Analog Mic Boost' '30dB'

	# force camera driver to load (#9795)
	modprobe via-camera &>/dev/null &

	if [ -x /sbin/rfkill -a -e $OLPC_HOME/.rfkill_block_wifi ]
	then
	    /sbin/rfkill block wifi
	fi

	initialize_ptmode
}

# Dynamically determine the boot device and mount it
mount_boot() {
	local bootpath=$(</ofw/chosen/bootpath)
	local bootdev
	local tmp

	case $bootpath in
	/pci/sd@c/disk@?:*) # XO-1.5 SD
		tmp=${bootpath#/pci/sd@c/disk@}
		tmp=${tmp%%:*}
		((tmp--))
		bootdev="/dev/disk/mmc/mmc${tmp}p1"
		;;
	esac
	[[ -n "$bootdev" && -d "/bootpart" && -e "$bootdev" ]] || return
	mount -o noatime "$bootdev" /bootpart
}

configure_hw() {
	[ "${XO_VERSION}" == "0" ] && return

	echo "olpc-configure: configuring hardware..."
	if [ "${XO_VERSION}" == "1.5" ] ; then
		configure_hw_1_5
	else
		configure_hw_1_0
	fi

	mount_boot
}

# Read a manufacturing data tag and strip the leading NUL
mfgtag () {
	cat $MFG_DATA/$1 | tr -d '\000' 2>/dev/null
}

read_nvram() {
	dd if=/dev/nvram bs=1 skip=$(($1)) count=$2 2>/dev/null
}

# Work around for typo in Ethiopian mfg-data 
# http://dev.laptop.org/ticket/6945#comment:16
mfgtag_stripspaces() {
	cat $MFG_DATA/$1 | tr -d [:space:] | tr -d '\000' 2>/dev/null
} 

read_config() {
	# Some defaults
	XKB_MODEL="olpc"
	XKB_LAYOUT="us"
	SYS_LANG="en_US.UTF-8"

	[ "${XO_VERSION}" == "0" ] && return

	if [ -f $MFG_DATA/KL ]; then
		XKB_MODEL="`mfgtag KM`"
		XKB_LAYOUT="`mfgtag_stripspaces KL`"
		XKB_VARIANT="`mfgtag KV`"
		SYS_LANG="`mfgtag LO`"
		if [ "$SYS_LANG" = "es_MX.UTF-8" ] && [ "$XKB_LAYOUT" = "us" ] ; then
			# We have some machines in Peru with US(Intl) keyboards and
			# they need some modifications to the standard layout
			# See http://dev.laptop.org/ticket/9126
			XKB_LAYOUT="es"
			XKB_VARIANT="olpc2"
		fi
	elif [ "${XO_VERSION}" == "1" ] ; then
		KB_HEADER=`read_nvram 0x44 2`
		if [ "x$KB_HEADER" = "xKB" ] ; then
			KB_CODE=`read_nvram 0x46 10`
			if [ "x$KB_CODE" = "xus_INTL" ] ; then
				XKB_LAYOUT="us"
			elif [ "x$KB_CODE" = "xes" ] ; then
				XKB_LAYOUT="es"
				SYS_LANG="es_AR.UTF-8"
			elif [ "x$KB_CODE" = "xpt_BR" ] ; then
				XKB_LAYOUT="br"
				SYS_LANG="pt_BR.UTF-8"
			elif [ "x$KB_CODE" = "xara" ] ; then
				XKB_LAYOUT="us(olpc2),ara(olpc)"
				SYS_LANG="ar_LY.UTF-8"
			elif [ "x$KB_CODE" = "xth" ] ; then
				XKB_LAYOUT="us(olpc2),th(olpc)"
				SYS_LANG="th_TH.UTF-8"
			elif [ "x$KB_CODE" = "xng" ] ; then
				XKB_LAYOUT="ng(olpc)"
			elif [ "x$KB_CODE" = "xpk" ] ; then
				XKB_LAYOUT="us(olpc2),ur(olpc)"
				SYS_LANG="ur_PK.UTF-8"
			elif [ "x$KB_CODE" = "xet" ] ; then
				XKB_LAYOUT="us(olpc2),et"
				SYS_LANG="am_ET.UTF-8"
			fi
		fi
	fi
	if ! locale -a | grep -qx "${SYS_LANG/UTF-8/utf8}"; then
	    echo "olpc-configure: Locale $SYS_LANG is not supported;"
	    SYS_LANG="en_US.UTF-8"  # better fallback than "C"
	    echo "olpc-configure: Falling back to locale $SYS_LANG"
	fi
}

rebuild_library_index() {
	if [ -x /usr/share/library-common/make_index.py ]; then
		echo '* olpc-configure: Rebuilding the library index.'
		rm -rf $OLPC_HOME/.library_pages
		[ -n "$SYS_LANG" ] || read_config
		/usr/bin/env - LANG=$SYS_LANG /usr/share/library-common/make_index.py
		chown -R olpc:olpc $OLPC_HOME/.library_pages
	fi
}

# see dev.laptop.org trac #6432 for use case and spec.
install_customization_packages () {
	# we'd like to look on USB/SD here, but udev/sugar hasn't mounted
	# them yet =(
	for pkgdir in $OLPC_HOME/.custom/rpms ; do # other paths?
		if [ ! -d "$pkgdir" ]; then continue; fi
		pkgs=$(find "$pkgdir" -name '*.rpm' -a ! -name '*.src.rpm' )
		if [ -n "$pkgs" ]; then
			echo '* olpc-configure: Installing customization packages:'
			echo "$pkgs"
			rpm -Uvh $pkgs
		fi
		unset pkgs
	done
	unset pkgdir
}

# configurations which happen in /home
# these don't need to be repeated when we upgrade.
configure_home() {
	echo "olpc-configure: reconfiguring home..."

	rm -f $OLPC_HOME/.i18n
	cat >$OLPC_HOME/.i18n <<__EOF__
LANG="$SYS_LANG"
__EOF__
	chown olpc:olpc $OLPC_HOME/.i18n

	# can't live in $OLPC_HOME because activities can't read this dir.
	SERNUM="`mfgtag SN`"
	UUID="`mfgtag U#`"
	if [ -e /etc/olpc-configure/devkey.html ];then
	    sed -e "s/REPLACE_WITH_SERIALNUMBER/$SERNUM/" \
		-e "s/REPLACE_WITH_UUID/$UUID/" > /home/.devkey.html.tmp < /etc/olpc-configure/devkey.html
	else
	    sed -e "s/REPLACE_WITH_SERIALNUMBER/$SERNUM/" \
		-e "s/REPLACE_WITH_UUID/$UUID/" > /home/.devkey.html.tmp <<__EOF__
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Developer key request</title>
<style type="text/css" media="all">@import "https://activation.laptop.org/devkey/stylesheet/";</style>
</head>
<body>
<iframe src="https://activation.laptop.org/devkey/content/"
        width="100%" height="450" >
<p>Use the button below to request a developer key for your
        machine.</p>
</iframe>
<form action="https://activation.laptop.org/devkey/post/"
        method="post" id="devkeyform" >
<input type="hidden" name="serialnum" value="REPLACE_WITH_SERIALNUMBER" />
<input type="hidden" name="uuid" value="REPLACE_WITH_UUID" />
<input type="submit" />
</form>
</body>
</html>
__EOF__
	fi
	chmod a+r,a-w /home/.devkey.html.tmp
	mv /home/.devkey.html.tmp /home/.devkey.html

	rebuild_library_index

	# ensure /home/root exists (#6700)
	mkdir -p /home/root
	chmod 750 /home/root
}

update_home_permissions_to_v6() {
	echo "olpc-configure: fixing permissions on home (dlo#4928, dlo#5626)..."

	# Our first attempt at fixing dlo#4928 assumed that home was totally
	# private and public data would be explicitly made available.
	# Since it's inpractical, we relax this requirement and undo the
	# changes made by earlier versions of this script.
	# However, we must be careful not to mess with the rainbow isolation
	# scheme which is intentionally 770.
	find $OLPC_HOME -type d -perm 770 -name isolation -prune -o -print0 | xargs -0 chmod a+rX,u+w,go-w
}

set_home_permissions() {
	# Home must be world aXessible so that activities installed
	# in ~/Activities can reach their bundles
	# We make the home also listable because we expect that confidential
	# data will be explicitly isolated in nested subdirectories.
	chmod 755 $OLPC_HOME

	# SSH would bitch if permissions on .ssh happens to be too lax
	if [ -d "$OLPC_HOME/.ssh" ]; then
		find $OLPC_HOME/.ssh -print0 | xargs -0 chmod u+rwX,go-rwx
	fi

	# .sugar contains plenty of confidential data
	chmod 700 $OLPC_HOME/.sugar 2>/dev/null
}

clean_previews() {
	echo "olpc-configure: cleaning the previews directory..."

	/usr/bin/olpc-clean-previews
}

# xorg needs to be configured for each boot
# to setup xinerama for ext monitor / projector
configure_xorg() {
	# TODO: put this all in a hal callout or something similar
	XORG_CONF=xorg-emu.conf
	if [ "${XO_VERSION}" == "1.5" ]; then
	    if [ -e /dev/sisusbvga0 ]; then
	        XORG_CONF='xorg-xo1.5-dcon-extmon.conf'
	    else
	        XORG_CONF=xorg-xo1.5-dcon.conf
	    fi
	elif [ "${XO_VERSION}" == "1" ]; then
	    XORG_CONF=xorg-dcon.conf
	fi

	#  Only fixup when needed
	CURLINK=`readlink -f /etc/X11/xorg.conf | xargs basename`
	if [ "$CURLINK" != "$XORG_CONF" ];then
	    ln -sf -T "$XORG_CONF" /etc/X11/xorg.conf
	fi
}

# configurations which happen in the root fs 
# these need to be repeated when we upgrade.
configure_root() {
	echo "olpc-configure: reconfiguring root..."

	# First X layout used also as kernel keytable
	KERN_KEYTABLE=`echo "$XKB_LAYOUT" |  cut -d , -f 1`

	# Some keyboards come with a special OLPC variant for the console
	if [ -f /lib/kbd/keymaps/i386/olpc/$KERN_KEYTABLE-olpc.map.gz ]; then
		KERN_KEYTABLE=$KERN_KEYTABLE-olpc
	fi

	# We keep keyboard settings here to avoid users from messing around
	# with them in their home and maybe shut themselves out
	cat >/etc/sysconfig/keyboard <<__EOF__
KEYTABLE="$KERN_KEYTABLE"
XKB_MODEL="$XKB_MODEL"
XKB_LAYOUT="$XKB_LAYOUT"
XKB_VARIANT="$XKB_VARIANT"
__EOF__
	# Keyboard is being loaded early in rc.sysint, reset it now
	loadkeys "$KERN_KEYTABLE"

	# set olpc-dm as desktop manager
	echo "DISPLAYMANAGER=/usr/sbin/olpc-dm" > /etc/sysconfig/desktop

	# Update library index
	rebuild_library_index

	if [ -x /usr/sbin/rainbow-replay-spool ]; then
		echo "olpc-configure: replaying rainbow spool..."
		/usr/sbin/rainbow-replay-spool
	fi

	# developer customizations.
	if /usr/bin/olpc-test-devkey -q ; then
		install_customization_packages
	fi
}

#
# Main entry point
#
# This is organized to minimize boot time by bailing out early
# in the common case of an already configured system
#
case "$1" in
  start|restart)
	get_xo_version
	olpc_home_version="`cat $OLPC_HOME/.olpc-configured 2>/dev/null`"
	olpc_root_version="`cat /.olpc-configured 2>/dev/null`"

	set_hostname

	# unconditionally configure the hardware
	configure_hw

	# unconditionally configure xorg
	# for USB2VGA
        configure_xorg
	
	# check whether /home configuration is necessary.
	if [ "$olpc_home_version" != "$OLPC_CONFIGURE_VERSION" ]; then
	    if [ -n "$olpc_home_version" ]; then
		###
		### upgrades only
		###
	        ## trigger activity upgrade (dlo trac #7495 #9747)
		touch "$OLPC_HOME/.sugar-update"
		if [ "0$olpc_home_version" -le "6" ]; then
		    # run it *before* set_home_permissions
		    update_home_permissions_to_v6
		fi
		if [ "0$olpc_home_version" -le "7" ]; then
		    clean_previews
		fi
	    fi
	    set_home_permissions
	    read_config
	    configure_home
	    echo "$OLPC_CONFIGURE_VERSION" >$OLPC_HOME/.olpc-configured
	fi


	# check whether / configuration is necessary.
	if [ "$olpc_root_version" != "$OLPC_CONFIGURE_VERSION" ]; then
		[ -n "$XKB_MODEL" ] || read_config
		configure_root
		echo "$OLPC_CONFIGURE_VERSION" >/.olpc-configured
	fi

	# check whether we need to reconfigure in response to usb-driven customizations
	if [ -f "$OLPC_HOME/.usb-customizations" ]; then
		olpc_usb_version="`cat $OLPC_HOME/.usb-customizations 2>/dev/null`"
		if [ -n "$olpc_usb_version" ] && [ "$olpc_usb_version" -ge "1" ]; then
			rebuild_library_index
		fi

		# we've done whatever we can; remove the customization flag
		# XXX: This choice means that whichever image is booted after usb-based
		# customizations gets to decide what customizations it wants to do. <MS>
		rm -rf "$OLPC_HOME/.usb-customizations"
	fi
	;;
  stop|status)
  	;;
  *)
        echo $"Usage: $0 {start|stop|restart|status}"
  	;;
esac
