#!/bin/bash
# olpc-configure       Perform one-time configuration

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

OLPC_HOME=/home/olpc
XO_VERSION=0

# control variable so that we only generate the library homepage a maximum of
# 1 time per boot
LIBRARY_INDEX_BUILT=0

source /usr/share/olpc-utils/olpc-utils-functions

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
	amixer -q sset 'Capture' '100%'

	# 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

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

configure_hw_1_5() {
	amixer -q sset Master -- "-10.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'
	amixer -q sset 'Capture' '100%' cap

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

configure_hw_1_75() {

	# open permissions on the OLS 'ceiling' node, so activities
	# that want to get light sensor readings can raise it.
	# of course, after raising it, it would be really nice if
	# activities would then lower it to 0 again when they're done.
	# it's not critical -- mainly it just keeps the storage LED from
	# working properly while the ceiling is raised.
	ols_device=$(grep -l 'OLPC OLS notify' /sys/class/input/input*/name)
	ols_device=${ols_device%/name}
	ceiling=$ols_device/device/ceiling
	chmod a+w $ceiling

}

configure_hw_3() {
	# FIXME - nothing to do yet
	echo XO-3
}

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

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

mount_boot_1_0() {
	local rootdev
	rootdev=$(findmnt -rvn -o SOURCE /) || return 0

	# If using UBIFS root, assume JFFS2 boot
	if [ "$rootdev" = "/dev/ubi0_0" ]; then
		mount -t jffs2 -o noatime mtd1 /bootpart
	fi
}

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

	echo "olpc-configure: configuring hardware..."

	if [ "${XO_VERSION}" == "1.5" ] ; then
		configure_hw_1_5
		mount_boot
        elif  [ "${XO_VERSION}" == "1.75" ] ; then
		configure_hw_1_75
		mount_boot
        elif  [ "${XO_VERSION}" == "3" ] ; then
		configure_hw_3
		mount_boot
	else
		configure_hw_1_0
		mount_boot_1_0
	fi
}

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

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

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

	# test if we can get mfg data reading it
	local KM=`get_xo_mfg_tag KM`
	if [ -n "$KM" ]; then
		XKB_MODEL="$KM"
		XKB_LAYOUT="`get_xo_mfg_tag_stripspaces KL`"
		XKB_VARIANT="`get_xo_mfg_tag KV`"
		SYS_LANG="`get_xo_mfg_tag LO`"
		if [ "$XKB_LAYOUT" = "en" ] ; then
			# Some units have invalid KL 'en' tags (e.g. SKU104, #10711)
			XKB_LAYOUT="us"
		fi
		if [ "$SYS_LANG" = "es_MX.UTF-8" \
                     -a "$XKB_LAYOUT" = "us" \
                     -a "$XKB_VARIANT" = "olpc" ] ; then
			# We have some XO-1 machines in Peru with US(Intl) membrane 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() {
	[ "$LIBRARY_INDEX_BUILT" = "1" ] && return 0
	LIBRARY_INDEX_BUILT=1
	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
		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="`get_xo_mfg_tag SN`"
	UUID="`get_xo_mfg_tag 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

	# version 11: switch from batterymon to batti (#10436)
	rm -f /home/olpc/.config/autostart/batterymon.desktop

	# version 12: create default unencrypted keyring (#10290)
	if [ ! -e /home/olpc/.gnome2/keyrings/default.keyring ]; then
		mkdir -p /home/olpc/.gnome2/keyrings
		echo 'default' > /home/olpc/.gnome2/keyrings/default
		TIME=$(/bin/date +%s)
		cat >/home/olpc/.gnome2/keyrings/default.keyring.tmp <<EOF
[keyring]
display-name=default
ctime=$TIME
mtime=$TIME
lock-on-idle=false
lock-timeout=0
EOF
		mv /home/olpc/.gnome2/keyrings/default.keyring{.tmp,}
		# .gnome2 permissions are fixed up below
	fi

	# version 13: get rid of old ~/Activities/.groups from gg-802-1 (#10582)
	# activity group changed for XO-1, and this is now specified in /etc
	if [ -e /home/olpc/Activities/.groups ]; then
		ACTGROUPS=$(</home/olpc/Activities/.groups)
		if [ "$ACTGROUPS" = "http://wiki.laptop.org/go/Activities/G1G1" ]; then
			rm -f /home/olpc/Activities/.groups
		fi
	fi

	# version 17: version 12 accidently created /home/olpc/.gnome2 with root ownership
	# correct that here for upgrades
	chown -R olpc:olpc /home/olpc/.gnome2
}

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
}

# determine presence of USB-VGA adapter on boot, switch Xorg config
# appropriately
configure_xorg() {
	# TODO: put this all in a hal callout or something similar
	if [ -e /dev/sisusbvga0 ]; then
		ln -s /usr/share/olpc-utils/xorg.conf.d/sisusb.conf \
			/etc/X11/xorg.conf.d/
		rm -f /etc/X11/xorg.conf.d/xo$XO_VERSION.conf
	else
		rm -f /etc/X11/xorg.conf.d/sisusb.conf
		[ -e /etc/X11/xorg.conf.d/xo$XO_VERSION.conf ] || \
			ln -s /usr/share/olpc-utils/xorg.conf.d/xo$XO_VERSION.conf /etc/X11/xorg.conf.d/
	fi
}

resize_system() {
	[ -e "/security/no-resize" ] && return 0

	# Check that we're on a correctly partitioned system
	local bootpart=$(findmnt -rvn -o SOURCE /bootpart) || return 0
	local syspart=$(findmnt -rvn -o SOURCE /) || return 0

	local syspart_base=$(basename $syspart)
	[ "${syspart_base:0:3}" = "mmc" ] || return 0

	echo "Resizing disk"
	resize2fs "$syspart" && touch /security/no-resize
}

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

	# 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

	# version 10: use xorg.conf.d instead of xorg.conf
	rm -f /etc/X11/xorg.conf

	# version 15: set hostname via /etc/hosts and /etc/sysconfig/network
	if [ -e "/sys/class/net/eth0/address" ]; then
		local addr=$(</sys/class/net/eth0/address)
		if [ "${#addr}" == 17 ]; then
			addr=${addr:9}
			local hname=xo-${addr//:/-}
			cat <<EOF >/etc/sysconfig/network
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=$hname.localdomain
NM_IGNORE_HOSTNAME_LOCALHOST=yes
EOF
			hostname ${hname}.localdomain
			sed -c -i -e "s/^127\.0\.0\.1.*/127.0.0.1 ${hname}.localdomain ${hname} localhost/g" /etc/hosts
		fi
	fi

}

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

# unconditionally configure the hardware
configure_hw

# unconditionally configure xorg for USB2VGA
configure_xorg

# if we lack a root version but we already have a home version,
# it means that we have just updated with olpc-update.
if [ -z "$olpc_root_version" -a -n "$olpc_home_version" ]; then
	# trigger the activity upgrade prompt (#7495, #9747)
	touch "$OLPC_HOME/.sugar-update"
fi

# check whether /home configuration is necessary.
if [ "$olpc_home_version" != "$OLPC_CONFIGURE_VERSION" ]; then
	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
	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
