#!/usr/bin/perl
#
# Template -- for translation general intentions to exact initrd layout
#   Copyright (C) 2005  Erik van Konijnenburg
#
#   This program 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.
#
#   This program 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 this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# These templates define the translation from general intentions in the
# plan to exact lists of files and script fragments to be included 
# on the initrd image.  Templates are indexed by an action name, and include:
# 	- files to be copied from current system to initrd;
# 	  often executables.
# 	- directories to be created
# 	- fragments to be appended to scripts on the image,
# 	  these are in a hash from script name to script content.
#
# Everything is processed by HTML::Template before being added to the
# image: script fragments, but also file names and script names.
# Leading spaces are dropped from template lines.
#
# If you want to implement busybox support, this is the place to do it.
#
# Obviously, the template names and the attributes used in the template
# should match what is provided by the planner.
#
#

use strict;
package Template;


my $templates = {
	prologue => {
		# In principle, we don't want device files on the initrd
		# image: device numbers can change between kernels.
		# Instead, create device files at boot time based on info
		# in sysfs.  However, /dev/null and /dev/console are needed
		# to mount sysfs.
		files => [
			'/dev/null',
			'/dev/console',
			'/bin/ash',
			'/bin/cat',
			'/bin/mkdir',
			'/bin/mount',
			'/bin/umount',
			'/bin/mknod',
			'/bin/sleep',
			],
		directories => [
			'/mnt', '/proc', '/sys', '/dev', '/etc', '/var',
			],
		trees => [],
		scripts => {
			'/init' => <<'//',
				#!/bin/ash

				# Generator version: yaird <TMPL_VAR NAME=appVersion>.
				#
				set -x
				#
				#  Utility functions
				#
				mksymdev () {
					devfile="$1"
					sysfile="$2"
					cb="$3"
					devpair=$(/bin/cat "$sysfile")
					for delay in 1 2 4 8 16
					do
						if [ "$devpair" = "" ]
						then
							echo "Waiting $delay seconds for $sysfile to show up"
							sleep $delay
						fi
						devpair=$(/bin/cat "$sysfile")
					done

					if [ "$devpair" = "" ]
					then
						echo "Device $sysfile seems to be down."
						echo "Debugging opportunity, type ^D to continue."
						/bin/dash
					fi

					maj=${devpair%:*}
					min=${devpair#*:}
					/bin/mknod "$devfile" $cb $maj $min
				}
				mkcdev () {
					mksymdev "$1" "/sys/class/$2/dev" c
				}
				mkbdev () {
					mksymdev "$1" "/sys/block/$2/dev" b
				}


				#
				# Setting up mounts
				#
				mount -nt sysfs sysfs /sys
				mount -nt proc proc /proc
                                # In Fedora FC3, tmpfs *must* have the name
                                # 'none', otherwise start_udev, called from
                                # rc.sysinit will mount a new tmpfs over it,
                                # and you loose access to nodes such as
                                # /dev/vg0/root, expected to be created by
				# LVM in initramfs.
                                mount -nt tmpfs none /dev

                                #
				#
				# Command line processing
				# init - first proc to start on next root
				# root - to be done: how should it relate
				#	to file system selection?
				# ro,rw - mount root read-only or read-write.
				# 	This is like a mount -r; it overrules
				# 	a -o rw.
				# noresume, resume - to be done
				# ide - options for module ide_core.
				# 	need a way to append these to proper
				# 	module.  do a check on module name
				# 	in insmod template?
				#
				ro=-r
				init=/sbin/init
				for i in $(cat /proc/cmdline)
				do
					case "$i" in
					init=*)
						init=${i#init=}
						;;
					ro)
						ro=-r
						;;
					rw)
						ro=
					esac
				done
//
		},
	},

	insmod => {
		files => [
			'<TMPL_VAR NAME=target>',
			'/sbin/insmod',
			],
		directories => [],
		trees => [],
		scripts => {
			'/init' => <<'//',
				/sbin/insmod '<TMPL_VAR NAME=target>'
//
		},

	},

	mkbdev => {
		files => [],
		directories => [],
		trees => [],
		scripts => {
			'/init' => <<'//',
				mkbdev '<TMPL_VAR NAME=target>' '<TMPL_VAR NAME=sysname>'
//
		},
	},

	vgchange => {
		#
		# Use static version.  Note that fedora starts with
		# empty /dev, so we must make device nodes explicitly.
		#
		files => [
			'/sbin/lvm',
			],
		directories => [],
		trees => [],
		scripts => {
			# We cannot make mapper/control any earlier,
			# since dm-mod needs to be loaded to be able
			# to determine devno.
			'/init' => <<'//',
				if [ ! -c /dev/mapper/control ]
				then
					/bin/mkdir /dev/mapper
					mkcdev /dev/mapper/control misc/device-mapper
				fi
				/bin/mount -nt tmpfs tmpfs /etc
				/bin/mount -nt tmpfs tmpfs /var
				/sbin/lvm vgchange -a y '<TMPL_VAR NAME=target>'
				/sbin/lvm vgmknodes
				/bin/umount -n /var
				/bin/umount -n /etc
//
		},
	},

	mdadm => {
		files => [
			'/sbin/mdadm',
			],
		directories => [],
		trees => [],
		scripts => {
			#
			# NOTE: mdadm can operate without knowledge
			# of device numbers; it will assign an unused
			# one to the new device.  Thus you could have
			# device name /dev/md/boot that is stable,
			# regardless of which other raid devices are
			# operational.  However, debian installer
			# consistently uses device names like /dev/md0,
			# where the name depends on earlier minors
			# that happen to have been assigned.
			# To work around this problem, we forego
			# the --auto option, and mknod the device
			# before putting an md under it.  Better hope
			# the major number is not going to change ...
			#
			'/init' => <<'//',
				mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor>
				mdadm --assemble <TMPL_VAR NAME=target> --uuid <TMPL_VAR NAME=uuid> \
					<TMPL_LOOP NAME=components> <TMPL_VAR NAME=dev></TMPL_LOOP>
//
		}
	},

	mount => {
		files => [],
		directories => [],
		trees => [],
		scripts => {
			#
			# NOTE: honouring the kernel cmdline option ro,rw
			# is very nice, but...  If you have an ext3 in a
			# file loopback-mounted from vfat, it's unlikely
			# that a remount rw of root will also make the
			# underlying vfat read-write.  Underlying filesystems
			# should ignore the kernel ro/rw option; we
			# have an attribute isRoot that's defined iff
			# this is the real root.
			#
			# always -n, since we dont have writable /etc/mtab.
			#
			'/init' => <<'//',
				mount -n \
					<TMPL_IF NAME=isRoot>$ro</TMPL_IF> \
					-t <TMPL_VAR NAME=fsType> \
					<TMPL_VAR NAME=options> \
					<TMPL_VAR NAME=device> \
					<TMPL_VAR NAME=target>
//
		}
	},

	postlude => {
		files => [
			'<TMPL_VAR NAME=auxDir>/run_init',
			],
		directories => [],
		trees => [],
		scripts => {
			#
			# fedora does not come with populated /dev 
			# on root and needs at least /dev/console.
			# Pass them via tmpfs.
			#
			# Do *not* do a pivot_root here.  We're
			# working from rootfs; a pivotroot would
			# make that end up in /initrd, which gets
			# unmounted by rc.sysinit, and without rootfs,
			# too much is broken to even do a panic.
			#
			# Instead use run_init from klibc, which uses
			# a magical combination of mount --move /mnt /,
			# chdir and chroot.
			# 

			'/init' => <<'//',
				echo "Debugging opportunity, ^D to continue."
				/bin/ash

				echo "Switching root ..."
				cd /mnt
				mkcdev /dev/null mem/null
				mkcdev /dev/zero mem/zero
				mkcdev /dev/console tty/console
				mkdir /dev/pts
				mkdir /dev/shm
				mount -n -t tmpfs --bind /dev /mnt/dev
				/bin/umount -n /sys
				/bin/umount -n /proc
				exec <TMPL_VAR NAME=auxDir>/run_init \
					/mnt $init "$@"
//
		}
	},
};

sub template ($) { return $templates->{$_[0]}; }


1;
