Code

Added goto-fai package stuff for interested people
authorcajus <cajus@594d385d-05f5-0310-b6e9-bd551577e9d8>
Thu, 24 Nov 2005 13:24:13 +0000 (13:24 +0000)
committercajus <cajus@594d385d-05f5-0310-b6e9-bd551577e9d8>
Thu, 24 Nov 2005 13:24:13 +0000 (13:24 +0000)
git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@2046 594d385d-05f5-0310-b6e9-bd551577e9d8

18 files changed:
contrib/fai/goto-fai/Makefile [new file with mode: 0644]
contrib/fai/goto-fai/confdir.DEFAULT.source [new file with mode: 0755]
contrib/fai/goto-fai/debian/README.debian [new file with mode: 0644]
contrib/fai/goto-fai/debian/changelog [new file with mode: 0644]
contrib/fai/goto-fai/debian/control [new file with mode: 0644]
contrib/fai/goto-fai/debian/copyright [new file with mode: 0644]
contrib/fai/goto-fai/debian/dirs [new file with mode: 0644]
contrib/fai/goto-fai/debian/postrm [new file with mode: 0755]
contrib/fai/goto-fai/debian/preinst [new file with mode: 0755]
contrib/fai/goto-fai/debian/rules [new file with mode: 0755]
contrib/fai/goto-fai/diversions/fai [new file with mode: 0755]
contrib/fai/goto-fai/diversions/setup_harddisks [new file with mode: 0755]
contrib/fai/goto-fai/diversions/subroutines [new file with mode: 0755]
contrib/fai/goto-fai/faimond [new file with mode: 0755]
contrib/fai/goto-fai/get_fai_dir [new file with mode: 0755]
contrib/fai/goto-fai/goto-support.lib [new file with mode: 0644]
contrib/fai/goto-fai/ldap2fai [new file with mode: 0755]
contrib/fai/goto-fai/secret [new file with mode: 0644]

diff --git a/contrib/fai/goto-fai/Makefile b/contrib/fai/goto-fai/Makefile
new file mode 100644 (file)
index 0000000..85756c2
--- /dev/null
@@ -0,0 +1,19 @@
+all:
+       @echo "Nothing to do for all"
+
+install:
+       mkdir -p $(DESTDIR)/usr/sbin
+       mkdir -p $(DESTDIR)/etc/goto
+       mkdir -p $(DESTDIR)/usr/lib/goto
+       mkdir -p $(DESTDIR)/fai/hooks
+       cp secret $(DESTDIR)/etc/goto
+       cp -a get_fai_dir faimond $(DESTDIR)/usr/sbin
+       cp -a goto-support.lib $(DESTDIR)/usr/lib/goto
+       cp -a ldap2fai $(DESTDIR)/usr/sbin
+       cp confdir.DEFAULT.source $(DESTDIR)/fai/hooks
+       chmod go-rwx $(DESTDIR)/etc/goto/secret
+
+       # Install diversions
+       mkdir -p $(DESTDIR)/usr/lib/fai/sbin
+       cp diversions/setup_harddisks $(DESTDIR)/usr/lib/fai/sbin
+
diff --git a/contrib/fai/goto-fai/confdir.DEFAULT.source b/contrib/fai/goto-fai/confdir.DEFAULT.source
new file mode 100755 (executable)
index 0000000..afed2a2
--- /dev/null
@@ -0,0 +1,19 @@
+# undef default shell subroutine get_fai_dir
+# instead the new script get_fai_dir will be used
+
+setterm -cursor off >/dev/tty3
+/usr/sbin/faimond >/dev/tty3 & 
+chvt 3
+unset get_fai_dir
+unset sndmon
+
+sndmon() {
+       # send message to monitor daemon
+       [ "$faimond" -eq 0 ] && return 0
+       if [ "$debug" ];then
+               echo "$sndhostname $*" | nc localhost 4711
+       else
+               echo "$sndhostname $*" | nc localhost 4711 2>/dev/null
+       fi
+       return $?
+}
diff --git a/contrib/fai/goto-fai/debian/README.debian b/contrib/fai/goto-fai/debian/README.debian
new file mode 100644 (file)
index 0000000..584b1da
--- /dev/null
@@ -0,0 +1,6 @@
+goto-fai for Debian
+-------------------
+
+Comments regarding the Package
+
+Cajus Pollmeier <pollmeier@gonicus.de>, Thu, 17 Mar 2005 09:05:17 +0100
diff --git a/contrib/fai/goto-fai/debian/changelog b/contrib/fai/goto-fai/debian/changelog
new file mode 100644 (file)
index 0000000..905c15e
--- /dev/null
@@ -0,0 +1,5 @@
+goto-fai (2.0-1) unstable; urgency=low
+
+  * Initial release.
+
+ -- Cajus Pollmeier <pollmeier@gonicus.de>  Thu, 17 Mar 2005 09:05:17 +0100
diff --git a/contrib/fai/goto-fai/debian/control b/contrib/fai/goto-fai/debian/control
new file mode 100644 (file)
index 0000000..1cf6184
--- /dev/null
@@ -0,0 +1,12 @@
+Source: goto-fai
+Section: lhm/main
+Priority: optional
+Maintainer: Cajus Pollmeier <pollmeier@gonicus.de>
+Standards-Version: 3.6.1
+Build-Depends: debmake
+
+Package: goto-fai
+Architecture: any
+Depends: ${shlibs:Depends}, libnet-ldap-perl, hwdata-knoppix, hwsetup, ddcxinfo-knoppix
+Description: GOto support scripts
+ Support and build scripts for terminal server
diff --git a/contrib/fai/goto-fai/debian/copyright b/contrib/fai/goto-fai/debian/copyright
new file mode 100644 (file)
index 0000000..e18fe19
--- /dev/null
@@ -0,0 +1,8 @@
+This package was debianized by cajus cajus@ots-2.gonicus.local on
+Thu, 17 Mar 2005 09:05:17 +0100.
+
+It was downloaded from <fill in ftp site>
+
+Copyright:
+
+<Must follow here>
diff --git a/contrib/fai/goto-fai/debian/dirs b/contrib/fai/goto-fai/debian/dirs
new file mode 100644 (file)
index 0000000..ca882bb
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/contrib/fai/goto-fai/debian/postrm b/contrib/fai/goto-fai/debian/postrm
new file mode 100755 (executable)
index 0000000..526aaad
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+#DEBHELPER#
+
+if [ "remove" = "$1" ]; then
+       dpkg-divert --package goto-fai --remove --rename \
+               --divert /usr/lib/fai/sbin/setup_harddisks.goto-fai \
+                       /usr/lib/fai/sbin/setup_harddisks
+fi
+
+exit 0
diff --git a/contrib/fai/goto-fai/debian/preinst b/contrib/fai/goto-fai/debian/preinst
new file mode 100755 (executable)
index 0000000..fa469b9
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+#DEBHELPER#
+#
+if [ ! -e /usr/lib/fai/sbin/setup_harddisks.goto-fai ]; then
+       dpkg-divert --package goto-fai --add --rename \
+               --divert /usr/lib/fai/sbin/setup_harddisks.goto-fai \
+               /usr/lib/fai/sbin/setup_harddisks
+fi
+
+exit 0
diff --git a/contrib/fai/goto-fai/debian/rules b/contrib/fai/goto-fai/debian/rules
new file mode 100755 (executable)
index 0000000..6967ca5
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/make -f
+# Made with the aid of debmake, by Christoph Lameter,
+# based on the sample debian/rules file for GNU hello by Ian Jackson.
+
+package=goto
+
+build:
+       $(checkdir)
+       
+       $(MAKE) CFLAGS="-O2 -g -Wall"
+       touch build
+
+clean:
+       $(checkdir)
+       rm -f build
+       -$(MAKE) clean
+       rm -f `find . -name "*~"`
+       rm -rf debian/tmp debian/files* core debian/substvars
+
+binary-indep: checkroot build
+       $(checkdir)
+# There are no architecture-independent files to be uploaded
+# generated by this package.  If there were any they would be
+# made here.
+
+binary-arch: checkroot build
+       $(checkdir)
+       rm -rf debian/tmp
+       install -d debian/tmp
+       cd debian/tmp && install -d `cat ../dirs`
+       $(MAKE) install DESTDIR=`pwd`/debian/tmp
+# Must have debmake installed for this to work. Otherwise please copy
+# /usr/bin/debstd into the debian directory and change debstd to debian/debstd
+       debstd 
+       dpkg-gencontrol -isp
+       chown -R root:root debian/tmp
+       chmod -R go=rX debian/tmp
+       dpkg --build debian/tmp ..
+
+define checkdir
+       test -f debian/rules
+endef
+
+binary: binary-indep binary-arch
+
+checkroot:
+       $(checkdir)
+       test root = "`whoami`"
+
+.PHONY: binary binary-arch binary-indep clean checkroot
diff --git a/contrib/fai/goto-fai/diversions/fai b/contrib/fai/goto-fai/diversions/fai
new file mode 100755 (executable)
index 0000000..eb48bbd
--- /dev/null
@@ -0,0 +1,272 @@
+#!/bin/bash
+# $Id: fai,v 1.9 2005/05/05 21:56:24 lange Exp $
+#*********************************************************************
+#
+# fai -- main installation script executed after booting
+#
+# This script is part of FAI (Fully Automatic Installation)
+# (c) 1999-2005 by Thomas Lange, lange@informatik.uni-koeln.de
+# Universitaet zu Koeln
+#
+#*********************************************************************
+# 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.
+# 
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#*********************************************************************
+
+#set -xv # for full debugging
+
+export PATH=/usr/local/sbin:/usr/local/bin:/usr/lib/fai:/bin:/sbin:/usr/bin:/usr/sbin:
+# some variables
+export FAI_VERSION="FAI 2.8.4, 25 May 2005"
+rundir=/var/run/fai
+stamp=$rundir/FAI_INSTALLATION_IN_PROGRESS
+romountopt="-o async,noatime,nolock,ro,actimeo=1800"
+fstab=fstab  # Solaris uses vfstab
+
+# the type of operating system (linux, sunos)
+oclass=$(uname -s | tr /a-z/ /A-Z/)
+# $classes is now set so we can call hooks before fai-class defines the classes
+classes="DEFAULT $oclass $HOSTNAME LAST"
+faimond=0
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+prcopyleft() {
+
+    cat <<-EOF
+             -----------------------------------------------------
+               Fully Automatic Installation for $osname
+               $FAI_VERSION    Copyright (c) 1999-2005
+
+               Thomas Lange      <lange@informatik.uni-koeln.de>
+             -----------------------------------------------------
+EOF
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+fai_init() {
+
+    local sub osname
+    set -a # now export all variables
+
+    umask 022
+    # read fai.conf
+    # linux dir
+    [ -f /etc/fai/fai.conf ] && . /etc/fai/fai.conf
+    # solaris dir
+    [ -f /tmp/install_config/fai/fai.conf ] && . /tmp/install_config/fai/fai.conf
+
+    if [ -f /boot/RUNNING_FROM_FAICD ]; then   # we are booting from fai cd
+       umount /initrd
+       romountopt=
+       FAI_DEBMIRROR="--bind /media/mirror"
+       MNTPOINT=/media/mirror
+       FAI_LOCATION=
+    fi
+
+    # some variables from are not needed any more
+    unset NFSROOT FAI_CONFIGDIR installserver
+
+    # read subroutine definitions
+    sub=/usr/share/fai/subroutines
+    [ -f $sub ] && . $sub
+    [ -f $sub-$OS_TYPE ] && . $sub-$OS_TYPE
+
+    [ -f "$stamp" ] && {
+       echo "$0 already running, aborting"
+       exit 1
+    }
+
+    # HG: are we called as an init substitute ?
+    DO_INIT_TASKS=0
+    [ "$0" = "/etc/init.d/rcS" ] && DO_INIT_TASKS=1
+    [ $DO_INIT_TASKS -eq 1 ] && renewclass=1 # always renew class list when installing
+
+    # color logo only if initial install
+    COLOR_FAI_LOGO=$DO_INIT_TASKS
+
+    DEBIAN_FRONTEND=noninteractive
+    # local disks are mounted to $FAI_ROOT
+    if [ -z "$FAI_ROOT" ] ; then
+      [ $DO_INIT_TASKS -eq 1 ] && FAI_ROOT=/tmp/target || FAI_ROOT=/
+    fi
+    # executed command in the environment of the new system
+    ROOTCMD="chroot $FAI_ROOT"
+    # no chroot needed
+    [ "$FAI_ROOT" = '/' ] && ROOTCMD=
+
+    # Solaris has already a writable /tmp directory
+    [ "$oclass" = LINUX -a $DO_INIT_TASKS -eq 1 ] && create_ramdisk
+    unset oclass
+
+    # directory where temporary log files are stored
+    # set default value if nothing is set in fai.conf
+    if [ -z "$LOGDIR" -a $DO_INIT_TASKS -eq 1 ]; then
+       LOGDIR=/tmp/fai
+       mkdir -p $LOGDIR
+    fi
+    [ $DO_INIT_TASKS -eq 0 ] && LOGDIR=$(mktemp -t -d fai.XXXXXX)
+    ln -s $LOGDIR $rundir/current_log
+
+    # several log files
+    diskvar=$LOGDIR/disk_var.sh
+    moduleslog=$LOGDIR/modules.log
+    rcslog=$LOGDIR/fai.log
+
+    # variables for cfengine
+    files=$FAI/files
+    target=$FAI_ROOT
+
+    if [ $DO_INIT_TASKS -eq 1 ]; then
+#        trap 'echo "Now rebooting";faireboot' INT QUIT ;
+       trap 'echo "Now rebooting";/bin/bash' INT QUIT ;
+    else
+        trap "echo 'Aborted';rm -f $stamp" INT QUIT ;
+    fi
+
+    # if HOST was specified on the commandline, set hostname to it
+    eval_cmdline
+    if [ -n "$HOST" ]; then
+       HOSTNAME=$HOST
+       hostname $HOST
+       echo "Hostname set to $HOST" | tee -a $rcslog
+       sleep 3
+    fi
+    export HOSTNAME
+
+    if [ X$OS_TYPE = Xlinux ]; then
+       osname='Debian GNU/Linux'
+       if [ $DO_INIT_TASKS -eq 1 ]; then
+           grep -q '[[:space:]]sysfs' /proc/filesystems && mount -t sysfs sysfs /sys
+           ifup lo
+           [ -x /sbin/portmap ] && /sbin/portmap
+           mount -t devpts devpts /dev/pts
+           # add other options for nfs mount of /dev/root to root-path in dhcpd.conf
+           mount -o remount,noatime,ro /dev/root /
+           cat /proc/kmsg >/dev/tty4 &
+       fi
+    fi
+    if [ X$OS_TYPE = Xsunos ]; then
+       osname='Sun Solaris'
+    fi
+
+    # set red color, but not on some archs
+    [ -e /.nocolorlogo ] && COLOR_FAI_LOGO=0
+    case $HOSTTYPE in
+       sparc*|powerpc*) COLOR_FAI_LOGO=0 ;;
+    esac
+
+    [ $COLOR_FAI_LOGO -eq 1 ] && echo -ne "\ec\e[1;31m"
+
+    prcopyleft | tee -a $rcslog
+
+    if [ $COLOR_FAI_LOGO -eq 1 ]; then
+       echo -ne "\e[7;0r"
+       echo -ne "\e[9B\e[1;m"
+    fi
+    save_dmesg
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+usage() {
+    cat <<-EOF
+       fai $FAI_VERSION. Copyright (C) 1999-2005 Thomas Lange
+       Usage: $0 [options] [action]
+       
+       Options:
+          -v|--verbose      display more information during the update
+          -h|--help         display this help message
+          -N|--new          renew list of classes
+EOF
+    exit 0
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+fstart() {
+
+    # these tasks can define variables, that are needed later
+    task confdir
+    task setup
+    task defclass
+    set_disk_info
+    task defvar
+    [ $DO_INIT_TASKS -eq 1 ] && load_keymap_consolechars
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Main routine
+
+export renewclass=0
+# Parse commandline options
+TEMP=$(getopt -o Nhv --long new,help,verbose -n "$0" -- "$@")
+if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
+# Note the quotes around `$TEMP': they are essential!
+eval set -- "$TEMP"
+
+while true ; do
+    case "$1" in
+        -h|--help)
+           shift
+           usage
+            ;;
+       -v|--verbose)
+           shift
+           verbose=1
+           ;;
+       -N|--new)
+           shift
+           renewclass=1
+           ;;
+        --) 
+            shift
+            break
+            ;;
+         *)
+            echo "$0: command line parsing error ! $@"
+            exit 1
+            ;;
+    esac
+done
+
+fai_init
+
+lpipe=$LOGDIR/logfifo
+mkfifo $lpipe
+tee -a $rcslog < $lpipe &
+# in bash &> redirect stdout and stderr to file
+fstart &> $lpipe
+rm $lpipe
+unset lpipe
+sleep 1 # wait for tee to complete. One second should be ok
+
+# old code
+# {
+# # a bash group command with { does not work on sparc
+# task confdir
+# task setup
+# task defclass
+# task defvar
+# load_keymap_consolechars
+# set_disk_info
+# } > >( tee -a $rcslog )  2>&1
+
+# override FAI_ACTION if a command line argument is given
+[ "$1" ] && FAI_ACTION=$1
+
+task action 2>&1 | tee -a $rcslog
+
+# not quiet happy with it
+[ "$FAI_CVSROOT" ] && rm -rf $FAI
+rm -rf $LOGDIR
+
+[ -L "$rundir/current_log" ] && rm -f "$rundir/current_log"
+[ -L "$rundir/current_config" ] && rm -f "$rundir/current_config"
+
+echo "End of $0"
diff --git a/contrib/fai/goto-fai/diversions/setup_harddisks b/contrib/fai/goto-fai/diversions/setup_harddisks
new file mode 100755 (executable)
index 0000000..de1427c
--- /dev/null
@@ -0,0 +1,954 @@
+#!/usr/bin/perl
+
+# $Id: setup_harddisks,v 1.41 2005/04/08 10:08:54 lange Exp $
+#*********************************************************************
+#
+# setup_harddisks -- create partitions and filesystems on harddisk
+#
+# This script is part of FAI (Fully Automatic Installation)
+# Copyright (c) 1999, 2000 by ScALE Workgroup, Universitaet zu Koeln
+# Copyright (c) 2000-2005 by Thomas Lange, Uni Koeln
+#
+#*********************************************************************
+# 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; see the file COPYING. If not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+# MA 02111-1307, USA.
+#*********************************************************************
+#
+# This program first read the configfiles, partitions and formats the harddisks,
+# produces fstab and FAI-variables-file.  It uses sfdisk, mke2fs, mkswap
+#
+# Parameters:
+# [-X]                     no test, your harddisks will be formated
+#                          default: only test, no real formating
+# [-f<config-filename>]    default: parse classes
+# [-c<class-path>]         default: $FAI/disk_config/
+# [-d]                     default: no DOS alignment
+#
+#---------------------------------------------------
+# Last changes:  31.3.2005 by Thomas Lange add sub mapdisk{}
+# Last changes:  8.11.2004 by Thomas Lange add $devdisklist when calling sfdisk
+# Last changes:   3.2.2004 by Thomas Lange typos
+# Last changes: 14.07.2003 by Thomas Lange add xfs filesystem support
+# Last changes: 23.01.2003 by Thomas Lange print info data to stdout
+# Last changes: 03.12.2002 by Thomas Lange remove ida, cciss stuff. Just match everything
+# Last changes: 27.11.2002 by Thomas Lange allow more that 3 primary partitions
+# Last changes: 14.05.2002 by Thomas Lange use strict
+# Last changes: 04.05.2002 by Thomas Lange use strict
+# Last changes: 29.04.2002 by Thomas Lange add swaplist
+# Last changes: 12.01.2002 by Thomas Lange
+# /dev/ida/ patch 12.01.2002 by Marc Martinez <lastxit+fai@technogeeks.org>
+# Last changes: 9.11.2001 by Thomas Lange
+# reiserfs patch 8.11.2001 by Diane Trout <diane@caltech.edu>
+# Last changes: 25.10.2001 by Thomas Lange
+# Last changes: 09.07.2001 by Thomas Lange
+# Last changes: 04.07.2001 by Thomas Lange
+# Last changes: 06.05.2001 by Thomas Lange
+# Last changes: 09.03.2001 by Thomas Lange
+# Last changes: 05.12.2000 by Thomas Lange
+# Last changes: 03.05.2000 by Thomas Lange
+# Last changes: 03.04.2000 by Mattias Gaertner
+#---------------------------------------------------
+#
+# config-file format:
+#   lines beginning with # are comments
+#
+# "disk_config <device>|first|end"
+#   The disk_config command starts the parsing.
+#   It has to be the first command.
+#    <device> is the harddisk to format in short form like "hda" or "sdc".
+#    if first is used, the first of $ENV{disklist} is used
+#    "end"    = end parsing here
+#   Example: "disk_config hdb"
+#   Example: "disk_config first"
+#
+# Defining one partition:
+# "primary|logical mountpoint|swap|- <size in mb>|preserve<No> [fstab-options][;extraordinary options]"
+#    "primary|logical":
+#      "primary": this are the bootable partitions like the
+#         root directory "/" or the DOS "C:" disk.
+#      "logical": this are all other partitions like a linux
+#         "/var" or a swap partition or a DOS disk.
+#
+#    "mountpoint|swap|-":
+#      "mountpoint": 
+#         This is the mount-point for fstab.
+#         For example "/","/var","/usr". There must not
+#         be a space in the mountpoint.
+#      "swap":
+#         swap-partitions
+#      "-":
+#         do not mount this partition.
+#
+#    "<size in mb>|preserve<No>":
+#      "<size in mb>":
+#        The size of the partition in megabyte
+#         Examples:
+#          "30"     = 30 mb
+#          "10-100" = 10 to 100 mb
+#          "20-"    = minimum of 20 mb
+#          "-500"   = 1 to 500 mb
+#          The megabytes will be rounded up to cylinders.
+#      "preserve<No>":
+#         This is the alternative for the size attribute.
+#         <No> is the partition number. For example
+#         preserve3 for the third partition. If the
+#         <device> was hda then this results in hda3.
+#         The partition will be left unchanged. This
+#         is useful if you have partitions that do not
+#         need re-installation or if you want to have
+#         other operation systems on the device together
+#         with Linux. Extended Partitions can not be preserved.
+#         The bootable flag will not be preserved.
+#         Preserved partitions are mounted readonly during
+#         installation.
+#
+#    "fstab-options":
+#         These options are copied to the fstab-file. The
+#         default is "default"
+#
+#   After the semicolon there could be extra options like:
+#     -i <bytes>   : Bytes per inodes
+#                    (only ext2/3 filesystem)
+#     -m <blocks>% : reserved blocks percentage for superuser
+#                    (only ext2/3 filesystem)
+#     -j          : format in ext3
+#     -c           : check for bad blocks
+#     format       : Always format this partition even if preserve
+#     lazyformat   : Do not format if partition has not moved
+#                    (useful for testing the installation)
+#     boot         : make this partition the boot-partition (the
+#                    linux root filesystem is the default)
+#     ext2         : Extended 2 filesystem (this is the default)
+#     swap         : swap partition
+#     dosfat16     : DOS 16bit FAT file system
+#     winfat32     : Win95 FAT32 file system
+#     writable     : mounts a preserved partition writable
+#     xfs          : xfs
+#     reiser       : reiserfs
+#       -h <hash>  : set reiserfs hash
+#       -v <ver>   : set reiserfs version
+#
+use strict;
+# getopts variables:
+our ($opt_X, $opt_f, $opt_c, $opt_d);
+my $test;
+
+$| = 1;                     # flush always
+
+#****************************************************
+# Variables
+#****************************************************
+
+my $Version = "version 0.35fai";
+
+my $megabyte = 1024 * 1024;    # guess
+# $gigabyte = 1024 * $megabyte;
+my $sectorsize = 512;
+
+# used programs
+my $sfdisk_options = "-q $ENV{sfdisk}";     # be quiet
+my $mke2fs_options = "-q";     # be quiet
+my $mkreiserfs_options = "";
+my $mkxfs_options = "-f";
+my $mkswap_options = "";
+
+# FAI input variables
+my $ClassPath = "$ENV{FAI}/disk_config";# this directory contains the classes
+my $ConfigFileName = "";   # alternative classfile, only for tests
+my $DOS_Alignment = "";    # align partitions for tracks
+
+# FAI output variables
+my $BootPartition = "";    # the boot partition like "hda1"
+my $BOOT_DEVICE = "";      # the root device like "hda" or "sdb"
+my $FAIOutputFile = $ENV{diskvar}; # write output variables to this file
+
+# old partition tables
+my %DiskUnits = ();        # unit size of each disk in sectors
+my %DiskSize = ();         # size of every disk in units
+my %SectorsAlignment = ();  # tracksize in sectors
+my %PartOldBoot = ();      # partition was bootable. "yes"=yes
+my %PartOldStart = ();     # old startunit of partition
+my %PartOldEnd = ();       # old endunit of partition
+my %PartOldStartSec = ();  # old startsector of partition
+my %PartOldEndSec = ();    # old endsector of partition
+my %PartOldID = ();        # old ID of partition
+my %OldNotAligned = (); # "yes" if old partition boundaries are not DOS aligned
+
+# mountpoints  ("/<path>" or "swap<No>" or "no<No>" or "extended<disk>")
+my $NofSwapPart = 0;       # number of swap partitions
+my $NofNotMoPart = 0;      # number of not mountet partitions
+my %DiskMountpoints = ();  # mountpoints of every disk. separated by spaces
+my %MountpointPart = ();   # partition of every mountpoint. e.g. "hda2"
+my %PartMountpoint = ();   # mountpoint of every partition.
+my @swaplist;              # list of all swpa devices
+
+# size of partition/mountpoint
+my %MPMinSize = ();        # minimum size of mountpoint in units
+my %MPMaxSize = ();        # maximum size of mountpoint in units
+my %MPPreserve = ();       # preserve partition: "yes"=yes
+my %MPPrimary = ();        # primary partition: "yes"=yes
+my %MPStart = ();          # start of partition in units
+my %MPSize = ();           # size of partition in units
+my %MPID = ();             # id of partition
+
+# options
+my %MPfstaboptions = ();   # fstab options for every mountpoint
+my %MPOptions = ();        # extra options for every mountpoint
+
+# sfdisk partition tables
+my %sfdiskTables = ();     # partition tables for sfdisk
+
+my $verbose = 0;
+$verbose = $ENV{verbose} if $ENV{verbose};
+
+# Parse command line
+
+use Getopt::Std;
+&getopts('Xf:c:d') || die "
+USAGE: [-X]                     no test, your harddisks will be formated
+                                default: only test, no real formating
+       [-f<config-filename>]    default: parse classes
+       [-c<class-path>]         default: \$FAI/disk_config/
+       [-d]                     default: no DOS alignment
+";
+
+print "setup_harddisks $Version\n";
+if (defined $opt_X){
+    $test = 2;
+} else {
+    print "TEST ONLY - no real formating\n\n";
+    $test = 1;
+}
+$ConfigFileName = $opt_f if $opt_f;# alternative config file
+$ClassPath      = $opt_c if $opt_c;# search classes here
+$DOS_Alignment  = "yes" if $opt_d; # track alignment
+
+# main part
+&GetAllDisks;
+&ParseAllConfigFiles;
+&BuildNewPartTables;
+&PartitionPersfdisk;
+&FormatDisks;
+&WriteFSTab;
+&WriteFAIVariables;
+exit 0;
+#****************************************************
+
+#****************************************************
+# get a partition pathname
+#****************************************************
+sub PartName {
+    my ($disk, $partno) = @_;
+    my $ppath;
+    for ($disk) {
+       /^[a-z]+$/ and $ppath = "${disk}${partno}";
+       /\d$/ and $ppath = "${disk}p${partno}";
+    }
+    return $ppath;
+}
+
+#****************************************************
+# Read all partition tables of this machine
+#****************************************************
+sub GetAllDisks{
+    my $line=""; my $disk=""; my $device=""; my $rest; my $result; my $divi;
+    my $devdisklist="";
+
+    foreach my $device(split(/\s/,$ENV{disklist})){
+      $devdisklist = "$devdisklist /dev/$device";
+    }
+    print "Probing disks: $devdisklist\n";
+    print "Disks found:";
+    $result = `sh -c "LC_ALL=C sfdisk -g -q $devdisklist"`;
+    foreach my $line(split(/\n/,$result)){
+       if($line =~ m'^/dev/(.+?):\s+(\d+)\s+cylinders,\s+(\d+)\s+heads,\s+(\d+)\s+sectors'i){
+           $disk = $1;
+           $DiskUnits{$disk} = $3 * $4;# heads * sectors = cylinder size in sectors
+           $DiskSize{$disk} = $2;      # cylinders
+           ($DOS_Alignment eq "yes") ? ($SectorsAlignment{$disk} = $4) : ($SectorsAlignment{$disk} = 1);
+           print " $disk";
+       }
+    }
+    $result = `sh -c "LC_ALL=C sfdisk -d -q $devdisklist"`;
+    foreach my $line(split(/\n/,$result)){
+#      if($line =~ m'# partition table of /dev/(cciss/c\dd\d|ida/c\dd\d|rd/c\dd\d|[a-z]+)'i){
+# now just match all devices
+       if($line =~ m'# partition table of /dev/(\S+)$'i){
+          $disk = $1;
+        }
+       if($line =~ m#^/dev/(.+?)\s*:\s+start=\s*(\d+),\s+size=\s*(\d+),\s+Id=\s*([a-z0-9]+)\b(.*)$#i){
+           $device = $1;
+            # Sectors
+            $PartOldStartSec{$device} = $2;
+            $PartOldEndSec{$device} = $2 + $3 - 1;
+            # DiskUnits
+           $PartOldStart{$device} = int ($2 / $DiskUnits{$disk});
+           $PartOldEnd{$device} = int (($2 + $3 - 1) / $DiskUnits{$disk});
+           $divi = $2 / $SectorsAlignment{$disk};
+           ($divi != int ($divi)) && ($OldNotAligned{$device} = "yes");
+           $divi = $3 / $SectorsAlignment{$disk};
+           ($divi != int ($divi)) && ($OldNotAligned{$device} = "yes");
+           $PartOldID{$device} = $4;
+           $rest = $5;
+           $PartOldBoot{$device} = ($rest =~ /bootable/) ? "yes" : "";
+       }
+    }
+    print "\n\n";
+}
+
+#****************************************************
+# parse config file or all class files
+#****************************************************
+sub ParseAllConfigFiles{
+    my $ConfigFileExists = 0;  # no config file parsed yet
+    if ($ConfigFileName){
+       # Read config filename
+       &ParseConfigFile($ConfigFileName);
+       $ConfigFileExists = 1;
+    } else {
+       # Read classes
+       foreach my $classfile (reverse split(/\s+/,$ENV{"classes"})){
+           my $filename = "$ClassPath/$classfile";
+           if (($classfile) && (-r $filename)) {
+               &ParseConfigFile($filename);
+               $ConfigFileExists = 1;
+            }
+           ($ConfigFileExists) && last;
+       }
+    }
+    ($ConfigFileExists == 0) && die "ERROR: no config file for setup_harddisk found. Please check you classes and files in disk_config.\n";
+}
+
+#****************************************************
+# map "disk_config first" to real disk device
+#****************************************************
+sub mapdisk {
+
+  my ($disk) = @_;
+  my @dlist = split /\s+/,$ENV{disklist};
+
+  if ($disk eq "disk1") {
+    print "Mapping disk name disk1 to $dlist[0]\n";
+    $disk = $dlist[0];
+  }
+  if ($disk eq "disk2") {
+    print "Mapping disk name disk2 to $dlist[1]\n";
+    $disk = $dlist[1];
+  }
+  return $disk;
+}
+
+#****************************************************
+# parse config-file
+#****************************************************
+sub ParseConfigFile{
+    my $size=""; my $mountpoint=""; my $device ="";
+    my $fstaboptions=""; my $options=""; my $disk=""; my $command = "";
+    my $LogPartNo; my $PrimPartNo; my $NoMoreLogicals;
+    my $LastPresPart; my $extmp; my $Min; my $Max;
+    my $filename = shift;
+    open (FILE,"$filename")
+      || die "config file not found: $filename\n";
+    (print "Using config file: $filename\n");
+    $disk = "";
+    my $a = 1, my $paras ="", my $number=0;
+    while (my $line = <FILE>){
+       chomp($line);
+       $a++;
+       next if( $line =~ /^#|^\s*$/ );
+
+       # disk_config - command
+       if ($line =~ /^disk_config(.*)/i){
+           $paras = $1;
+           if ($paras =~ / end/i){
+               $disk = "";
+           } else {
+#              if($paras =~ m# (/dev/)?(cciss/c\dd\d|ida/c\dd\d|rd/c\dd\d|[a-z]+)#i){
+# now match all devives
+               if($paras =~ m# (/dev/)?(\S+)#i){
+                   $disk = mapdisk($2);
+                   ($DiskMountpoints{$disk})
+                     && die "ERROR: there are more than one configuration of disk $disk.\n";
+                   ($DiskSize{$disk}) || die "ERROR: could not read device /dev/$disk\n";
+                   ($test != 1) || (print "config: $disk\n");
+                   $DiskMountpoints{$disk} = "";
+                   $MPPrimary{"extended$disk"} = "";
+                   $LogPartNo = 4;
+                   $PrimPartNo = 0;
+                   $NoMoreLogicals = 0;
+                   $LastPresPart = "";
+                   $extmp = "extended$disk";
+               } else {
+                   die "SYNTAX ERROR: in config file line $a, unknown disk_config parameter $paras\n$line\n";
+               }
+           }
+       }
+
+       if ($disk){
+           # primary|partition - command
+           if($line =~ /^\s*(primary|logical)\s+(.*)$/i){
+               $command = $1;
+               # split variables
+               $paras = $2;
+               $options = "";
+               if($paras =~ /(.*?)\s*;\s*(.*)$/){
+                   $paras = $1;
+                   $options = $2;
+               }
+               $size="";
+               $mountpoint ="";
+               $fstaboptions = "";
+               ($mountpoint,$size,$fstaboptions)=split(/\s+/,$paras);
+               # mountpoint
+               ($mountpoint =~ m#^/.*|^swap$|^-$#i)
+                 || die "SYNTAX ERROR in config file line $a, mountpoint: $mountpoint\n$line\n";
+               ($MountpointPart{$mountpoint})
+                 && die "SYNTAX ERROR in config file line $a. Mountpoint $mountpoint redefined.\n$line\n";
+               if($mountpoint eq "/"){
+                   ($BootPartition) || ($BOOT_DEVICE = $disk);
+               }
+               if($mountpoint eq "-"){
+                   $NofNotMoPart++;
+                   $mountpoint = "no$NofNotMoPart";
+               }
+               if($mountpoint eq "swap"){
+                   $NofSwapPart++;
+                   $mountpoint = "swap$NofSwapPart";
+                   ($options !~ /\bswap\b/i) && ($options .= " swap");
+                   ($fstaboptions) || ($fstaboptions = "sw");
+               }
+               if($mountpoint =~ m#^/#){
+                   ($fstaboptions) || ($fstaboptions = "defaults");
+               }
+               if ($command eq "primary") {
+                   ($MPPrimary{$extmp} eq "yes") && ($NoMoreLogicals = 1);
+                   $MPPrimary{$mountpoint} = "yes";
+                   $PrimPartNo++;
+#                  ($PrimPartNo == 3) && ($disk =~ /^sd/) && ($PrimPartNo++);
+                    ($PrimPartNo >4 ) && die "ERROR: Too much primary partitions (max 4).".
+                                " All logicals together need one primary too.\n";
+                   $MountpointPart{$mountpoint} = PartName($disk,$PrimPartNo);
+                   if($options =~ /\bboot\b/i){
+                       ($BootPartition) && die "ERROR: only one partition can be bootable at a time.";
+                       $BootPartition = $MountpointPart{$mountpoint};
+                       $BOOT_DEVICE = $disk;
+                   }
+               } else {
+                   ($NoMoreLogicals != 0) && die "ERROR: the logical partitions must be together.\n";
+                   $MPPrimary{$mountpoint} = "";
+                   $LogPartNo++;
+                   $MountpointPart{$mountpoint} = PartName($disk,$LogPartNo);
+                   if (!$MPPrimary{$extmp}){
+                       $MPPreserve{$extmp} = "";
+                       $MPPrimary{$extmp} = "yes";
+                       $MPMinSize{$extmp} = 0;
+                       $MPMaxSize{$extmp} = 0;
+                       $MPID{$extmp} = 5;
+                       $PrimPartNo++;
+                       ($PrimPartNo == 3) && ($disk =~ /^sd/) && ($PrimPartNo++);
+                        ($PrimPartNo >4 ) 
+                         && die "ERROR: too much primary partitions (max 4).".
+                               " All logicals together need one primary too.\n";
+                       $MountpointPart{$extmp} = PartName($disk,$PrimPartNo);
+                       $DiskMountpoints{$disk} .= " $extmp";
+                   }
+#                  ($options =~ /\bboot\b/i) && die "ERROR: line $a, only primary partitions can be bootable.\n";
+               }
+               $DiskMountpoints{$disk} .= " $mountpoint";
+               # size
+               ($size =~ /^preserve\d+$|^\d+\-?\d*$|^-\d+$/i)
+                   || die "SYNTAX ERROR in config file line $a, size: $size\n$line\n";
+               if($size =~ /^preserve(\d+)$/i){
+                   my $number = $1;
+                   $device = PartName($disk,$number);
+                   ($OldNotAligned{$device} eq "yes")
+                     && die "ERROR: unable to preserve partition /dev/$device. Partition is not DOS aligned.";
+                   ($command eq "primary") && ($number != $PrimPartNo)
+                       && die "NUMERATION ERROR in line $a, the number of the partition can not be preserved:\n$line\n";
+                   ($command eq "logical") && ($number != $LogPartNo)
+                       && die "NUMERATION ERROR in line $a, the number of the partition can not be preserved:\n$line\n";
+                   if ($PartOldEnd{$device}){
+                       (($PartOldID{$device} == 5) || ($PartOldID{$device} == 85)) &&
+                         die "ERROR in config file line $a.".
+                              " Extended partitions can not be preserved. /dev/$device\n$line\n";
+                       $MPPreserve{$mountpoint}="yes";
+                       $MPMinSize{$mountpoint} = $PartOldEnd{$device}-$PartOldStart{$device}+1;
+                       $MPMaxSize{$mountpoint} = $MPMinSize{$mountpoint}; # Max=Min
+                       $MPStart{$mountpoint} = $PartOldStart{$device};
+                       $MPSize{$mountpoint} = $MPMinSize{$mountpoint};
+                       $MPID{$mountpoint} = $PartOldID{$device};
+                   } else {
+                       die "ERROR: cannot preserve partition $device. partition not found.$PartOldEnd{$device}\n";
+                   }
+                   if ($LastPresPart) {
+                       ($PartOldStart{$device} < $PartOldStart{$LastPresPart}) &&
+                         die "ERROR: misordered partitions: cannot preserve partitions $LastPresPart and $device\n".
+                              "       in this order because of their positions on disk.";
+                   }
+                   $LastPresPart = $device;
+                   ($MPMinSize{$mountpoint} < 1)
+                     && die "ERROR: unable to preserve partitions of size 0.\n$line\n ";
+                 } else {
+                   # If not preserve we must know the filesystemtype
+                   ($options !~ /\b(ext2|ext3|auto|swap|dosfat16|winfat32|reiser|xfs)\b/i ) && ($options .= " auto");
+                 }
+               if($size =~ /^(\d*)(\-?)(\d*)$/){
+                   $Min = $1;
+                   $Min||= 1;
+                   $Max = $3;
+                   $MPMinSize{$mountpoint} = int (($Min * $megabyte - 1) / ($DiskUnits{$disk} * $sectorsize)) + 1;
+                   if ($2 eq "-"){
+                       if($Max =~ /\d+/){
+                           $MPMaxSize{$mountpoint} = int (($Max * $megabyte - 1) / ($DiskUnits{$disk} * $sectorsize)) + 1;
+                       } else {
+                           $MPMaxSize{$mountpoint} = $DiskSize{$disk};
+                       }
+                   } else {
+                       $MPMaxSize{$mountpoint} = $MPMinSize{$mountpoint}; # Max=Min
+                   }
+                   ($MPMinSize{$mountpoint} > $DiskSize{$disk})
+                     && die "ERROR in config file line $a: Minsize larger than disk.\n$line\n";
+                   ($MPMinSize{$mountpoint} > $MPMaxSize{$mountpoint}) 
+                       && die "SYNTAX ERROR in config file line $a, MIN-MAX-size: $MPMinSize{$mountpoint}-$MPMaxSize{$mountpoint}\n$line\n";
+                   ($MPMinSize{$mountpoint} < 1)
+                     && die "SYNTAX ERROR in config file line $a. Minsize must be greater than 1.\n$line\n";
+                   $MPPreserve{$mountpoint} = "";
+               }
+               # fstaboptions
+               $MPfstaboptions{$mountpoint} = $fstaboptions;
+               # extra options
+               ($options =~ /\b(ext[23]|auto)\b/i) && ($MPID{$mountpoint} = 83); # Linux native
+               ($options =~ /\bswap\b/i) && ($MPID{$mountpoint} = 82); # Linux swap
+               ($options =~ /\bdosfat16\b/i) && ($MPID{$mountpoint} = 6); # DOS FAT 16bit (>=32MB, will be changed later)
+               ($options =~ /\bwinfat32\b/i) && ($MPID{$mountpoint} = "b"); # Win 95 FAT 32
+               $MPOptions{$mountpoint} = $options;
+               if($test == 1){
+                   print "$mountpoint,$MPMinSize{$mountpoint}-$MPMaxSize{$mountpoint},";
+                   print "$fstaboptions,$options";
+                   ($MPPreserve{$mountpoint} eq "yes") && (print " Preserve: $MountpointPart{$mountpoint}");
+                   print "\n";
+               }
+           }
+       }
+    }
+    close(FILE);
+}
+
+#****************************************************
+# Build all partition tables
+#****************************************************
+sub BuildNewPartTables{
+    my ($disk, $mountpoint, $part, $PrimaryMP, $LogicalMP);
+    ($test != 1) || (print "\nBuilding partition tables:\n");
+    # Build PartMountpoint array
+    foreach $disk(keys %DiskMountpoints) {
+       $DiskMountpoints{$disk} =~ s/\s(\s)/$1/g;
+       $DiskMountpoints{$disk} =~ s/^\s//;
+       $DiskMountpoints{$disk} =~ s/\s$//;
+       foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+           $PartMountpoint{$MountpointPart{$mountpoint}} = $mountpoint;
+       }
+    }
+    foreach $disk(keys %DiskMountpoints) {
+       &SetPartitionPositions($disk);
+        # change units to sectors
+        foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+            if($MPPreserve{$mountpoint} eq "yes"){
+               $MPStart{$mountpoint} = $PartOldStartSec{$MountpointPart{$mountpoint}};
+               $MPSize{$mountpoint} = $PartOldEndSec{$MountpointPart{$mountpoint}} - $MPStart{$mountpoint} + 1;
+           } else {
+               $MPStart{$mountpoint} *= $DiskUnits{$disk};
+               $MPSize{$mountpoint} *= $DiskUnits{$disk};
+               # align first partition for mbr
+               if($MPStart{$mountpoint} == 0){
+                   $MPStart{$mountpoint} += $SectorsAlignment{$disk};
+                   $MPSize{$mountpoint} -= $SectorsAlignment{$disk};
+               }
+           }
+       }
+       # align all logical partitions
+        foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+            next if ($MPPrimary{$mountpoint} eq "yes");
+           if ($MountpointPart{$mountpoint} eq "${disk}5") {
+               # partition with number 5 is first logical partition and start of extended partition
+               $MPStart{"extended$disk"} = $MPStart{$mountpoint};
+                ($MPPreserve{$mountpoint} eq "yes") && ($MPStart{"extended$disk"} -= $SectorsAlignment{$disk});
+           }
+            if ($MPPreserve{$mountpoint} ne "yes") {
+               $MPStart{$mountpoint} += $SectorsAlignment{$disk};
+               $MPSize{$mountpoint} -= $SectorsAlignment{$disk};
+           }
+       }
+        &CalculateExtPartSize($disk);
+        # sort mountpoints of partition number
+        $PrimaryMP = "";
+        $LogicalMP = "";
+        foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+         ($MPPrimary{$mountpoint} eq "yes") ? ($PrimaryMP .= " $mountpoint") : ($LogicalMP .= " $mountpoint");
+       }
+       $DiskMountpoints{$disk} = "$PrimaryMP$LogicalMP";
+       $DiskMountpoints{$disk} =~ s/^\s//;
+       # print partition table
+        ($test != 1) || (PrintPartitionTable($disk));
+    }
+    if (!$BootPartition){
+        $BootPartition = $MountpointPart{"/"};
+    }
+}
+
+#****************************************************
+# set position for every partition
+#****************************************************
+sub SetPartitionPositions{
+    my $disk = shift;
+    my $mountpoint; my $DynGroup =""; my $StartPos; my $EndPos;
+    # Build groups of unpreserved partitions between
+    # preserved partitions
+    $StartPos = 0;
+    foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+        if ($MPPreserve{$mountpoint} eq "yes") {
+           $EndPos = $PartOldStart{$MountpointPart{$mountpoint}} - 1;
+            &SetGroupPos($DynGroup,$StartPos,$EndPos);
+           $DynGroup = "";
+           $StartPos = $PartOldEnd{$MountpointPart{$mountpoint}} + 1;
+        } else {
+           $DynGroup .= " $mountpoint";
+       }
+    }
+    $EndPos = $DiskSize{$disk} - 1;
+    &SetGroupPos($DynGroup,$StartPos,$EndPos);
+    foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+       ($MPOptions{$mountpoint} =~ /\bdosfat16\b/i)
+           && (($MPSize{$mountpoint} * $DiskUnits{$disk} * $sectorsize) < 32 * $megabyte)
+               && ($MPID{$mountpoint} = 4); # DOS 16-bit FAT <32MB
+    }
+}
+
+#****************************************************
+# set position for a group of unpreserved partitions
+# between start and end
+#****************************************************
+sub SetGroupPos{
+    my ($PartGroup,$Start,$End) = @_;
+    $PartGroup =~ s/^ //;
+    ($PartGroup) || return;
+    my $totalsize = $End - $Start + 1;
+    ($totalsize <= 0) && return;
+    my $mountpoint; my $mintotal = 0; my $maxmintotal = 0; my $rest = 0; my $EndUnit = 0;
+    # compute total of MinSizes and difference to MaxSizes
+    foreach $mountpoint (split(/\s/,$PartGroup)) {
+        $mintotal += $MPMinSize{$mountpoint};
+        $maxmintotal += ($MPMaxSize{$mountpoint} - $MPMinSize{$mountpoint});
+        $MPSize{$mountpoint} = $MPMinSize{$mountpoint};
+    }
+    # Test if partitions fit
+    ($mintotal > $totalsize)
+      && die "ERROR: Mountpoints $PartGroup do not fit.\n";
+    # Maximize partitions
+    $rest = $totalsize - $mintotal;
+    ($rest > $maxmintotal) && ($rest = $maxmintotal);
+    if ($rest > 0) {
+        foreach $mountpoint (split(/\s/,$PartGroup)) {
+            $MPSize{$mountpoint} += int ((($MPMaxSize{$mountpoint} - $MPMinSize{$mountpoint}) * $rest) / $maxmintotal);
+        }
+    }
+    # compute rest
+    $rest = $totalsize;
+    foreach $mountpoint (split(/\s/,$PartGroup)) {
+        $rest -= $MPSize{$mountpoint};
+    }
+    # Minimize rest
+    foreach $mountpoint (split(/\s/,$PartGroup)) {
+        if (($rest >0) && ($MPSize{$mountpoint} < $MPMaxSize{$mountpoint})){
+            $MPSize{$mountpoint}++;
+           $rest--;
+       }
+    }
+    # Set start for every partition
+    foreach $mountpoint (split(/\s/,$PartGroup)) {
+        $MPStart{$mountpoint} = $Start;
+       $Start += $MPSize{$mountpoint};
+       $EndUnit = $MPStart{$mountpoint} + $MPSize{$mountpoint} - 1;
+    }
+}
+
+#****************************************************
+# calculate extended partition size
+#****************************************************
+sub CalculateExtPartSize{
+    my ($disk) = @_;
+    my $extmp = "extended$disk";
+    my $mountpoint; my $ExtEnd; my $NewEnd;
+    ($MPPrimary{$extmp}) || return;
+    $ExtEnd = $MPStart{$extmp};
+    foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+        next if ($MPPrimary{$mountpoint} eq "yes");
+       $NewEnd = $MPStart{$mountpoint} + $MPSize{$mountpoint} - 1;
+       ($NewEnd > $ExtEnd) && ($ExtEnd = $NewEnd);
+    }
+    $MPSize{$extmp} = ($ExtEnd - $MPStart{$extmp} + 1);
+}
+
+#****************************************************
+# Print partition "number - mountpoint" table
+#****************************************************
+sub PrintPartitionTable{
+    my ($disk) = @_;
+    my $part; my $mountpoint; my $mountpointname; my $end;
+    foreach $part (sort %MountpointPart) {
+        next if($part !~ /^$disk/);
+       $mountpoint = $PartMountpoint{$part};
+        if ($mountpoint =~ /^no(.*)/){
+            $mountpointname = "no mountpoint ($1)";
+       } else {
+           $mountpointname = $mountpoint;
+       }
+       $end = $MPStart{$mountpoint} + $MPSize{$mountpoint} - 1;
+       print <<"EOM";
+/dev/$part $mountpointname start=$MPStart{$mountpoint} size=$MPSize{$mountpoint} end=$end id=$MPID{$mountpoint}
+EOM
+      }
+}
+
+#****************************************************
+# build all partition tables for sfdisk
+#****************************************************
+sub PartitionPersfdisk{
+    my ($disk, $mountpoint, $line, $part, $PrimaryNo);
+    my ($command, $result, $filename, $number);
+    print "Creating partition table: ";
+    foreach $disk(keys %DiskMountpoints) {
+        $sfdiskTables{$disk} = "# partition table of device: /dev/$disk\nunit: sectors\n\n";
+       $PrimaryNo = 1;
+        foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
+           $part = $MountpointPart{$mountpoint};
+           $part =~ /(\d+)$/;
+           ($1 < 5) && ($PrimaryNo++);
+           if ( ($1 == 5) && ($PrimaryNo < 5) ){
+               for my $number($PrimaryNo..4) {
+                   $sfdiskTables{$disk} .= BuildsfdiskDumpLine(PartName($disk,$number),0,0,0)."\n";
+               }
+           }
+           $line = BuildsfdiskDumpLine($MountpointPart{$mountpoint},$MPStart{$mountpoint},$MPSize{$mountpoint},$MPID{$mountpoint});
+            ($part eq $BootPartition) && ($line .= ", bootable");
+            $sfdiskTables{$disk} .= "$line\n";
+       }
+#      print $sfdiskTables{$disk};
+       $filename = "$ENV{LOGDIR}/partition." . (($disk=~ m#/#) ? join('_', split('/', $disk)) : $disk);
+       if(($test != 1) && ($filename)){
+           open(FILE, ">$filename") || die "unable to write temporary file $filename\n";
+           print FILE $sfdiskTables{$disk};
+           close(FILE);
+        }
+       $command = "LC_ALL=C sfdisk $sfdisk_options /dev/$disk < $filename";
+       if($test != 1){
+            print "$command\n";
+           $result = `sh -c "$command"`;
+           (($? >> 8) == 0) || (die "\nSFDISK ERROR:\n $result\n");
+       }
+    }
+}
+
+#****************************************************
+# build a sfdisk dump line
+#****************************************************
+sub BuildsfdiskDumpLine{
+
+  sprintf "/dev/%-5s: start=%10s, size=%10s, Id=%3s",@_;
+}
+
+#****************************************************
+# Format all disks
+#****************************************************
+sub FormatDisks{
+    my ($disk, $device, $mountpoint, $mountpointname, $command, $result);
+    print "Creating file systems:\n";
+    foreach $disk(keys %DiskMountpoints) {
+        foreach $mountpoint (split(/\s/,$DiskMountpoints{$disk})) {
+           $device = $MountpointPart{$mountpoint};
+            if ($mountpoint =~ /^no/){
+                $mountpointname = "no mountpoint";
+            } else {
+               $mountpointname = $mountpoint;
+           }
+           # preserved partition
+           if ( ($MPPreserve{$mountpoint} eq "yes") && ($MPOptions{$mountpoint} !~ /\bformat\b/i)){
+               print "Preserve partition $device";
+                if ($mountpoint =~ /^no$1/){
+                    print " with no mountpoint\n";
+                } else {
+                   print " with mountpoint $mountpoint\n";
+               }
+               next;
+           }
+           # lazy format
+           if ( ( $MPOptions{$mountpoint} =~ /\blazyformat\b/i )
+              && ($MPStart{$mountpoint} == $PartOldStartSec{$device})
+              && (($MPStart{$mountpoint} + $MPSize{$mountpoint} - 1) == $PartOldEndSec{$device}) ){
+               print "Lazy format: $device";
+                if ($mountpoint =~ /^no$1/){
+                    print " with no mountpoint";
+                } else {
+                   print " with mountpoint $mountpoint";
+               }
+                print " was neither moved nor formated.\n";
+               next;
+           }
+           # swap
+           if ($mountpoint =~ /^swap/i) {
+#              print "Make swap partition:\n";
+               $command = "mkswap $mkswap_options";
+               ($MPOptions{$mountpoint} =~ /(\-c)\b/i) && ($command .= " $1");
+               push @swaplist, "/dev/$device";
+               $command .= " /dev/$device";
+               print "  $command\n";
+               if($test != 1){
+                   $result = `$command`;
+                   (($? >> 8) == 0) || (die "\nMKSWAP ERROR:\n $result\n");
+               }
+               next;
+           }
+           # Linux Reiser file system
+           if ($MPOptions{$mountpoint} =~ /\breiser\b/i) {
+#              print "Make Reiser Filesystem:\n";
+               $command = "echo y | mkreiserfs $mkreiserfs_options";
+               ($MPOptions{$mountpoint} =~ /(\-h\s*\w+)\b/) && ($command .= " $1");
+               ($MPOptions{$mountpoint} =~ /(\-v\s*\d+)\b/) && ($command .= " $1");
+               $command .= " /dev/$device";
+               print "  $command\n";
+               if ($test != 1){
+                   $result = `$command`;
+                   (($? >> 8) == 0) || die "\nMKREISERFS ERROR:\n $result\n";
+               }
+               next;
+           }
+           # Linux XFS file system
+           if ($MPOptions{$mountpoint} =~ /\bxfs\b/i) {
+#              print "Make XFS Filesystem:\n";
+               $command = "mkfs.xfs $mkxfs_options";
+               $command .= " /dev/$device";
+               print "  $command\n";
+               if ($test != 1){
+                   $result = `$command`;
+                   (($? >> 8) == 0) || die "\nMKFS.XFS ERROR:\n $result\n";
+               }
+               next;
+           }
+           # Linux Extended 2 file system
+           if ($MPOptions{$mountpoint} =~ /\b(ext[23]|auto)\b/i) {
+#              print "Make Extended 2/3 Filesystem:\n";
+               $command = "mke2fs $mke2fs_options";
+               ($MPOptions{$mountpoint} =~ /(\-c)\b/i) && ($command .= " $1");
+               ($MPOptions{$mountpoint} =~ /(\-i\s*\d+)\b/) && ($command .= " $1");
+               ($MPOptions{$mountpoint} =~ /(\-m\s*\d+)\b/) && ($command .= " $1");
+               ($MPOptions{$mountpoint} =~ /(\-j)\b/) && ($command .= " $1");
+               $command .= " /dev/$device";
+               print "  $command\n";
+               if ($test != 1){
+                   $result = `$command`;
+                   (($? >> 8) == 0) || die "\nMKE2FS ERROR:\n $result\n";
+               }
+               next;
+           }
+           # DOS 16bit FAT / Win95 FAT 32
+           if ($MPOptions{$mountpoint} =~ /\b(dosfat16|winfat32)\b/i) {
+               print "Clear first sector for DOS/Windows\n";
+               $command = "dd if=/dev/zero of=/dev/$MountpointPart{$mountpoint} bs=512 count=1";
+               print "  $command\n";
+               if ($test != 1){
+                   $result = `$command`;
+                   (($? >> 8) == 0) || die "\nDD ERROR:\n $result\n";
+               }
+               next;
+           }
+        }
+    }
+}
+
+#****************************************************
+# Build fstab and write it to <root>/etc/fstab
+#****************************************************
+sub WriteFSTab{
+    my ($FileSystemTab, $device, $type, $filename);
+    $FileSystemTab  = << "EOM";
+# /etc/fstab: static file system information.
+#
+#<file sys>          <mount point>     <type>   <options>   <dump>   <pass>
+EOM
+    # 1. /
+    $type = "ext2";
+    ($MPOptions{'/'} =~ /\b(reiser)\b/i) && ($type = "reiserfs");
+    ($MPOptions{'/'} =~ /\b(xfs)\b/i) && ($type = "xfs");
+    ($MPOptions{'/'} =~ /\b(ext3)\b/i) && ($type = "ext3");
+    ($MPOptions{'/'} =~ /\b(ext2)\b/i) && ($type = "ext2");
+    $FileSystemTab .= BuildfstabLine("/dev/$MountpointPart{'/'}","/",$type,$MPfstaboptions{'/'},0,1);
+    # 2. swap partitions
+    foreach my $mountpoint (%PartMountpoint){
+       next if( $mountpoint !~ /^swap/i);
+       $FileSystemTab .= BuildfstabLine("/dev/$MountpointPart{$mountpoint}",
+                           "none","swap",$MPfstaboptions{$mountpoint},0,0);
+    }
+    # 3. /proc
+    $FileSystemTab .= BuildfstabLine("none","/proc","proc","defaults",0,0);
+    # 4. sorted others
+    foreach my $mountpoint (sort %PartMountpoint){
+       next if ( ($mountpoint !~ m#^/#) || ($mountpoint eq "/"));
+       $device = $MountpointPart{$mountpoint};
+       $type = "ext2";
+       ($MPOptions{$mountpoint} =~ /\b(dosfat16|winfat32)\b/i) && ($type = "vfat");
+       ($MPOptions{$mountpoint} =~ /\b(reiser)\b/i) && ($type = "reiserfs");
+       ($MPOptions{$mountpoint} =~ /\b(xfs)\b/i) && ($type = "xfs");
+       ($MPOptions{$mountpoint} =~ /\b(ext3)\b/i) && ($type = "ext3");
+       ($MPOptions{$mountpoint} =~ /\b(ext2)\b/i) && ($type = "ext2");
+       $FileSystemTab .= BuildfstabLine("/dev/$device",$mountpoint,$type,$MPfstaboptions{$mountpoint},0,2);
+    }
+    # write it
+    $filename = "$ENV{LOGDIR}/fstab";
+#    print $FileSystemTab;
+    print "Write fstab to $filename\n" if $verbose;
+    if($test != 1){
+       open(FILE, ">$filename") || die "unable to write fstab $filename\n";
+       print FILE $FileSystemTab;
+       close(FILE);
+    }
+}
+
+#****************************************************
+# Build fstab line
+#****************************************************
+sub BuildfstabLine{
+
+    sprintf "%-10s   %-15s   %-6s  %-8s  %-4s %-4s\n",@_;
+}
+
+#****************************************************
+# Write all FAI variables of this program to file
+#****************************************************
+sub WriteFAIVariables{
+
+  my $swaps;
+
+  print "Write FAI variables to file $FAIOutputFile\n" if $verbose;
+    return if ($test == 1);
+  $swaps = join ' ',@swaplist;
+    open(FILE, ">$FAIOutputFile") || die "Unable to write file $FAIOutputFile\n";
+    print FILE << "EOM";
+BOOT_DEVICE=/dev/$BOOT_DEVICE
+ROOT_PARTITION=/dev/$MountpointPart{'/'}
+BOOT_PARTITION=/dev/$BootPartition
+SWAPLIST="$swaps"
+EOM
+    close(FILE);
+}
diff --git a/contrib/fai/goto-fai/diversions/subroutines b/contrib/fai/goto-fai/diversions/subroutines
new file mode 100755 (executable)
index 0000000..c2590e8
--- /dev/null
@@ -0,0 +1,449 @@
+#! /bin/bash
+
+# $Id: subroutines,v 1.65 2005/04/06 19:03:46 lange Exp $
+#*********************************************************************
+#
+# subroutines -- useful subroutines for FAI
+#
+# This script is part of FAI (Fully Automatic Installation)
+# (c) 2000-2005 by Thomas Lange, lange@informatik.uni-koeln.de
+# Universitaet zu Koeln
+#
+#*********************************************************************
+# 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.
+# 
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#*********************************************************************
+
+# source this file, then you have these function available in the shell
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+die() {
+
+    # echo comment and exit installation
+    task_savelog
+    echo "$@"
+    exec bash
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+defnop() {
+
+    # define given list of subroutine names as dummy function;
+    # this will fake unknown commands
+
+    local name
+    for name in "$@";do
+        eval "$name () { :;}"
+    done
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ifclass() {
+
+    [ "$debug" ] && echo "Test if class $1 is in $classes"
+    # test if a class is defined
+    local cl
+    local ret=1
+
+    for cl in $classes; do
+       [ x$cl = x$1 ] && ret=0 && break
+    done
+    [ "$debug" ] && echo "ifclass returns $ret"
+    return $ret
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+rwmount() {
+
+    # remount partition read/write
+    mount -o rw,remount $1
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+save_dmesg() {
+
+    dmesg > $LOGDIR/dmesg.log
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+wait_for_jobs() {
+
+    # can be an extern script
+    # wait for running (background) jobs to finish (e.g. update-auctex-elisp)
+    local i=0
+    while jobsrunning; do
+       [ $(($i % 3)) -eq 0 ] && echo "Waiting for background jobs to finish."
+       i=$(($i+1))
+       sleep 10
+    done
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task() {
+
+    # hooks are called before a task is called
+    # if a task is skipped, also its hooks are skipped
+    # a hook can set the flag, so the accociated task is skipped
+
+    local taskname=$1
+
+    [ -f $LOGDIR/skip.$taskname ] || call_hook $taskname
+
+    if [ -f $LOGDIR/skip.$taskname ]; then
+        # skip task
+       rm $LOGDIR/skip.$taskname
+       [ "$verbose" ] && echo "Skiping task_$taskname"
+       sndmon "TASKSKIP $taskname"
+    else
+       echo "Calling task_$taskname"
+       sndmon "TASKBEGIN $taskname"
+       terror=0   # task can set this variable to indicate an error
+       task_$taskname
+       sndmon "TASKEND $taskname $terror"
+    fi
+    # since the subroutine is not needed any more, we can undefine it
+    unset task_$taskname
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+call_hook() {
+
+    local hook=$1
+    local cl dflag hfile
+    [ "$debug" ] && dflag="-d"
+
+    for cl in $classes; do
+       hfile=$FAI/hooks/$hook.$cl
+       if [ -x $hfile ]; then
+           echo "Calling hook: $hook.$cl"
+           sndmon "HOOK $hook.$cl"
+           # execute the hook
+           $hfile $dflag
+           check_status $hook.$cl $?
+       fi
+       if [ -x $hfile.source ]; then
+           echo "Source hook: $hook.$cl.source"
+           sndmon "HOOK $hook.$cl.source"
+            # source this hook
+           . $hfile.source $dflag
+           check_status $hook.$cl.source $?
+       fi
+     done
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+skiptask() {
+
+    # mark all given tasks, so they will be skipped
+    local tasklist="$@"
+    local task
+
+    for task in $tasklist; do
+       > $LOGDIR/skip.$task
+    done
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+get_fai_dir() {
+
+    /usr/sbin/get_fai_dir
+    ln -s $FAI $rundir/current_config
+    if [ ! -d $FAI/class ]; then
+       echo "WARNING: directory $FAI/class not found."
+    fi
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+get_fai_cvs() {
+
+    # subroutine which gets $FAI (/fai) configuration directory from
+    # a cvs repository. You can redefine this subroutine if you need
+    # access via ftp, http, or from a database
+
+    if [ "$FAI_CVSROOT" ] ; then
+        local TAG=""
+       [ -n "$FAI_CVSTAG" ] && TAG="-r $FAI_CVSTAG"
+        export FAI_CONFIG_AREA=$FAI_ROOT$FAI
+        export FAI=$(mktemp -t -d fai-config.XXXXXX)
+        
+       [ "$debug" ] && echo "\$FAI now points to $FAI"
+        
+       if [ -d "$FAI_CONFIG_AREA/CVS" -a -z "$FORCE" ] ; then
+          echo "Config found at $FAI_CONFIG_AREA: Copying"
+          cp -a $FAI_CONFIG_AREA/. $FAI
+          echo "Updating CVS"
+          cd $FAI
+           cvs -q -d"$FAI_CVSROOT" up -P $TAG -d -C > $LOGDIR/cvs.log
+       else 
+          echo "Checking out CVS"
+          cd /tmp
+          cvs -q -d"$FAI_CVSROOT" co -P -d $(basename "$FAI") \
+            $TAG $FAI_CVSMODULE > $LOGDIR/cvs.log
+       fi
+    else
+       echo "Warning $0: Neither \$FAI_LOCATION nor \$FAI_CVSROOT are defined."
+    fi
+    cd /
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+define_fai_flags() {
+
+    local flag
+    # FAI_FLAGS are comma separated, define all flags
+    FAI_FLAGS=${FAI_FLAGS//,/ }
+       [ "$verbose" ] && echo "FAI_FLAGS: $FAI_FLAGS"
+    for flag in $FAI_FLAGS; do
+       # define this flag as 1
+       eval "$flag=1"
+    done
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_setup() {
+
+    # source user specific subroutines
+    [ -f $FAI/hooks/subroutines ] && . $FAI/hooks/subroutines
+    if [ $DO_INIT_TASKS -eq 1 ]; then
+        # set the system time and date using rdate or/and ntpdate
+        [ "$TIMESRVS_1" ] && rdate $TIMESRVS_1
+        [ "$NTPSRVS_1" ]  && ntpdate -b -v $NTPSRVS
+    fi
+
+    define_fai_flags
+    DNSDOMAIN=$DOMAIN     # cfengine 1.5.3 can't use $DOMAIN
+
+    if [ $DO_INIT_TASKS -eq 1 ] ; then
+        [ "$createvt" ] && {
+           # create two virtual terminals; acces via alt-F2 and alt-F3
+           echo "Press ctrl-c to interrupt FAI and to get a shell"
+           openvt -c2 /bin/bash ; openvt -c3 /bin/bash
+           trap 'echo "You can reboot with faireboot";bash' INT QUIT
+        }
+    
+        # start secure shell daemon for remote access
+       [ "$sshd" -a -x /usr/sbin/sshd ] && /usr/sbin/sshd
+    fi
+
+    # when did FAI start, using localtime
+    FAI_RUNDATE=$(date +'%Y%m%d_%H%M%S')
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_action() {
+
+    if [ -z "$FAI_ACTION" ]; then
+       echo "No action in \$FAI_ACTION defined."
+       sndmon "TASKERROR action 21"
+       task_faiend
+       exit 
+    fi
+    echo "FAI_ACTION: $FAI_ACTION"
+    case $FAI_ACTION in
+
+       install)
+           if [ $DO_INIT_TASKS -eq 0 ]; then
+               echo "Cowardly refusing to run a FAI installation on a running system."
+               return
+           fi
+           echo Performing FAI installation. All data may be overwritten!
+           echo -ne "\a"; sleep 1
+           echo -ne "\a"; sleep 1
+           echo  -e "\a"; sleep 5
+           task install
+           task faiend
+           ;;
+       softupdate)
+           echo Performing FAI system update. All data may be overwritten!
+           task softupdate
+           ;;
+       sysinfo)
+           echo Showing system information.
+           task sysinfo
+           task_faiend
+           die Now you have a shell.
+           ;;
+       *)
+           if [ -f $FAI/hooks/$FAI_ACTION ]; then
+               echo "Calling user defined action: $FAI_ACTION"
+               $FAI/hooks/$FAI_ACTION
+           else
+               echo "ERROR: User defined action $FAI/hooks/$FAI_ACTION not found."
+               sndmon "TASKERROR action 22"
+               task_faiend
+           fi
+           ;;
+       esac
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_defclass() {
+
+    if [ ! -d $FAI/class ]; then
+       sndmon "TASKERROR defclass 21"
+       echo "Directory $FAI/class not found. Following subdirectories are found:"
+       find $FAI -type d -maxdepth 1 -printf "%p\n"
+       die "Aborting."
+    fi
+
+    # new script for defining classes; variables imported: $LOGDIR, $verbose, $debug
+    if [ $renewclass -eq 1 ]; then
+       # reevaluate new list of classes
+       fai-class -T $FAI/class $LOGDIR/FAI_CLASSES
+       classes=$(< $LOGDIR/FAI_CLASSES)
+    else
+       # use classes defined at installation time
+       if [ ! -f /var/log/fai/FAI_CLASSES ]; then
+           die "Try to read classes from /var/log/fai/FAI_CLASSES. Failed. Aborting."
+       fi
+       classes=$(< /var/log/fai/FAI_CLASSES)
+    fi
+
+    # define classes as: a.b.c.d for cfengine -D
+    # this doesn't work without echo
+    cfclasses=$(echo $classes)
+    cfclasses=${cfclasses// /.}
+    [ "$debug" ] && echo "cfclasses: $cfclasses"
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_defvar() {
+
+    cd $FAI/class
+    for class in $classes ; do
+       if [ -f $class.var ]; then
+           [ "$verbose" ] && echo "Executing $class.var"
+           [ "$debug" ] && set -vx
+           . $class.var </dev/null
+           [ "$debug" ] && set +vx
+       fi
+    done
+
+    # /fai/class/S* scripts or hooks can write variable definitions
+    # to additonal.var. now source these definitions
+    [ "$debug" ] && set -vx
+    [ -f $LOGDIR/additional.var ] && . $LOGDIR/additional.var
+    [ "$debug" ] && set +vx
+    unset class
+    # now all variables are defined. Dump them to variables.sh
+    set | perl -ne 'print if /^\w\w+=/' | egrep -v "^BASH_VERSINFO|^EUID|^PPID|^SHELLOPTS|^UID|^rootpw|^HOME|^PWD" > $LOGDIR/variables.sh
+    cd /
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_mountdisks() {
+
+    [ ! -f $LOGDIR/$fstab ] && die "No $LOGDIR/$fstab created."
+    # mount swap space
+    local sd
+    for sd in $SWAPLIST; do
+       swapon $sd && [ "$verbose" ] && echo "Enable swap device $sd"
+    done
+    mount2dir $FAI_ROOT $LOGDIR/$fstab
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_configure() {
+
+    fai-do-scripts $FAI/scripts
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_savelog() {
+
+    [ -d $target/var/log/fai ] || mkdir -p $target/var/log/fai
+    [ -d $target/var/log/fai ] && fai-savelog -l
+    cd $LOGDIR && cp -p FAI_CLASSES variables.sh $diskvar $target/var/log/fai
+    fai-savelog -r
+    cd /
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_faiend() {
+
+    [ $DO_INIT_TASKS -eq 0 ] && exit 0
+    wait_for_jobs
+    echo "Press <RETURN> to reboot or ctrl-c to execute a shell"
+    # reboot without prompting if FAI_FLAG reboot is set
+    [ -z $reboot ] && read
+    echo "Rebooting $HOSTNAME now"
+    sndmon REBOOT
+    cd /
+    sync
+
+    case $(uname -s) in
+       Linux)
+           killall -q sshd
+           umount $target/proc
+           umount -ar
+           exec reboot -dfi
+           ;;
+    esac
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_backup() {
+
+    die "Task backup not yet used. But you can use the hook backup."
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_install() {
+
+    > $stamp
+
+    save_dmesg
+
+    task partition
+    task mountdisks
+    task extrbase
+    task mirror
+    task debconf
+    task prepareapt
+    task updatebase
+    task instsoft
+    task configure
+    task finish
+    task chboot
+
+    rm -f $stamp
+    # save again, because new messages could be created
+    save_dmesg
+    task savelog
+
+    if [ -f $stamp ]; then
+       echo "Error while executing commands in subshell."
+       echo "$stamp was not removed."
+       sndmon "TASKERROR install 21"
+       die "Please look at the log files in $LOGDIR for errors."
+    fi
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task_softupdate() {
+
+    > $stamp
+
+    save_dmesg
+
+    local start_seconds=$(cut -d . -f 1 /proc/uptime)
+    task mirror
+    task debconf
+    task prepareapt
+    task updatebase
+    task instsoft
+    task configure
+    date
+    echo "The update took $[$(cut -d . -f 1 /proc/uptime)-$start_seconds] seconds."
+
+    rm -f $stamp
+    # save again, because new messages could be created
+    save_dmesg
+    task savelog
+
+    if [ -f $stamp ]; then
+       echo "Error while executing commands in subshell."
+       echo "$stamp was not removed."
+       sndmon "TASKERROR softupdate 21"
+       die "Please look at the log files in $LOGDIR for errors."
+    fi
+    umount $FAI_ROOT/fai
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+catnc() {
+    # cat but no comment lines
+    egrep -v "^#" $@
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/contrib/fai/goto-fai/faimond b/contrib/fai/goto-fai/faimond
new file mode 100755 (executable)
index 0000000..3ebdd5d
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+
+# $Id: faimond,v 1.2 2004/06/27 11:18:55 lange Exp $
+#*********************************************************************
+#
+# faimond -- monitor daemon which collects client status info
+#
+# This script is part of FAI (Fully Automatic Installation)
+# (c) 2003-2004 by Thomas Lange, lange@informatik.uni-koeln.de
+# Universitaet zu Koeln
+#
+#*********************************************************************
+
+#use strict;
+use Socket;
+
+$| = 1;
+my $port = 4711;
+
+@tasklist = qw/confdir defclass defvar partition mountdisks extrbase updatebase instsoft configure finish/;
+
+%tasks = (
+confdir => [' ', "Beziehe System-Einstellungen"],
+defclass => [' ',"Definieren von Klassen"],
+defvar => [' ',"Definieren von Variablen"],
+partition => [' ',"Paritionieren der Festplatten"],
+mountdisks => [' ',"Einbinden der Dateisysteme"],
+extrbase => [' ',"Installieren des Basis-Systems"],
+updatebase => [' ',"Aktualisieren des Basis-Systems"],
+instsoft => [' ',"Installieren der Software"],
+configure => [' ',"Abschließende Konfiguration"],
+finish => [' ',"Abschließen der Installation"]
+);
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub server_init() {
+
+  my $proto = getprotobyname('tcp');
+  socket(SERVER, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
+  setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsock: $!";
+
+  my $paddr = sockaddr_in($port, INADDR_ANY);
+
+  bind(SERVER, $paddr) or die "bind: $!";
+  listen(SERVER, SOMAXCONN) or die "listen: $!";
+#  print "FAI monitoring daemon started on port $port\n";
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub big_loop() {
+
+  # accept a connection, print message received and close
+  my ($client_addr,$inp);
+  while ($client_addr = accept(CLIENT, SERVER)) {
+    $inp = <CLIENT>;
+    close CLIENT;
+    ($host,$begend,$task,$ecode) = split /\s+/,$inp;
+    chomp $ecode;
+    $strecode = sprintf "%-3s",$ecode;
+    $sym = ($begend =~ /TASKEND/) ? "   \\Z2OK\\Zn" : "   ->";
+    $tasks{$task}[0] = $ecode ? " \\Z1E$strecode\\Zn" : $sym;
+    showtab();
+
+    # Stop if we've reached faiend
+    if ( $task =~ /faiend/ ){
+       system("dialog --timeout 60 --msgbox '\nDie Installation wurde abgeschlossen. Drücken Sie die Eingabetaste um das System neu zu starten.' 8 60");
+       break;
+    }
+  }
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub showtab() {
+
+# nach taskbeg soll es blinken, bei taskend, X oder error code
+
+  my $pre = '--colors --title " Aktueller Installationsverlauf "';
+  my $s2 = " --infobox \"\n";
+  # show tabular %tasks
+
+  $str = "$pre $s2";
+  foreach (@tasklist) {
+    $x = sprintf "%5s  $tasks{$_}[1]\n", $tasks{$_}[0];
+    $str .= $x;
+  }
+
+  $str .=  "\" 14 50\n";
+#  print $str;
+  system("dialog $str");
+
+}
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+server_init;
+big_loop;
diff --git a/contrib/fai/goto-fai/get_fai_dir b/contrib/fai/goto-fai/get_fai_dir
new file mode 100755 (executable)
index 0000000..5f24697
--- /dev/null
@@ -0,0 +1,128 @@
+#!/bin/sh
+# FAI script for preparing LDAP objects. It calls ldap2fai to generate
+# the config space after everything is done.
+#
+# (C) 2005 Cajus Pollmeier <pollmeier@gonicus.de>
+echo 0 > /proc/sys/kernel/printk
+trap '' INT
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH
+LANG=C
+
+. /usr/lib/goto/goto-support.lib
+
+#dialog() {
+#      echo $*
+#}
+
+abort() {
+       setterm -cursor off
+       while true; do sleep 60; done
+}
+       
+# Try to figure out which interface is configured, in doubt
+# choose the first one.
+interfaces=$(ifconfig | awk '/^[a-z0-9]/ {print $1}' | grep -v "lo")
+for int in $interfaces; do
+       ip=$(v=`ifconfig $int | awk '/inet addr/ {print $2}'`; echo ${v##*:})
+       mac=$(ifconfig $int | awk '/HWaddr/ {print $5}')
+       [ -n "$ip" ] && break
+done
+
+# Cancel if there's no IP available
+if [ -z "$ip" ]; then
+       dialog --title 'Fehler' --no-shadow --infobox 'Fehler: Das System konnte keine Netzwerk-Adresse ermitteln.\n\nDie Installation kann ohne diese Adresse nicht fortgesetzt werden.' 5 60
+       abort
+fi
+
+# Check if DNS setup is correct and set the hostname
+hostname=$(get_hostname_from_ip $ip)
+if [ "$hostname" == "unknown" ]; then
+       dialog --title 'Fehler' --no-shadow --infobox 'Fehler: Das System konnte keinen Rechner-Namen ermitteln.\n\nDie Installation kann ohne diese Information nicht fortgesetzt werden.' 5 60
+       abort
+fi
+
+echo "* setting hostname: $hostname"
+hostname "$hostname"
+mount -t tmpfs tmpfs /etc/ldap
+
+
+# Look for interesting parameters on kernel commandline
+ldap=""; splash=""
+for v in $(cat /proc/cmdline); do
+   case $v in
+      ldap=*)
+                echo -n "* found LDAP information, adapting configuration: "
+                ldap=$(echo ${v##ldap=}|base64-decode)
+
+               # ldap://hostname:389/basedn
+               LDAP_HOST=$(echo $ldap|sed 's!^[^:][^:]*://\([^:/][^:/]*\).*$!\1!g')
+               LDAP_PORT=$(echo $ldap|sed 's!^[^:]*://[^:][^:]*:\([0-9]*\)/.*$!\1!g')
+               echo -n $ldap_port | grep -q '^[0-9]*$' || LDAP_PORT=389
+               LDAP_BASE=$(echo $ldap|sed 's!^[^:][^:]*://[^/][^/]*/\(.*\)$!\1!g')
+               echo -e "BASE   $LDAP_BASE\nURI     ldap://$LDAP_HOST:$LDAP_PORT\n" > /etc/ldap/ldap.conf
+                echo "ok"
+                ;;
+      splash=*)
+                echo -n "* setting splash mode: "
+                splash=$(echo ${v##splash=})
+                [ $splash == "silent" ] && echo "silent" || echo "normal"
+                ;;
+    esac
+done
+
+[ -z "$ldap" ] && exit 0
+
+# Check if autosetup is needed at this point
+echo -n "* configurator: "
+if ! terminal_has_hardware_profile $mac; then
+    setterm -cursor off
+    echo "not configured yet - please wait, detecting hardware"
+
+    # Switch from bootsplash to normal screen, show dialog
+    [ -f /proc/splash ] && echo "verbose" > /proc/splash
+
+    setterm -blank 60
+    chvt 1
+    dialog --infobox 'Bitte warten, die installierte Hardware wird untersucht...' 3 64
+
+    # Get common config
+    hwsetup
+    terminal_alsa_setup
+    terminal_autofs_setup
+
+    # Save hardware profile
+    terminal_save_hardware_profile $mac
+fi
+
+if ! terminal_activated $mac; then
+    # wait till we get activated
+    setterm -blank 60
+    chvt 1
+    dialog --infobox 'Warte auf Aktivierung durch den Systemadministrator.' 3 60
+
+    while ! terminal_activated $mac; do
+                sleep 2
+    done
+
+    # GOsa writes the GOto entry in three steps. To continue, we check
+    # if XDRIVER is present.
+    dialog --infobox 'System wurde aktiviert. Eintr�e werden nun bernommen.' 3 60
+    while ! terminal_load_hardware_profile $mac &> /dev/null; do
+       cat /etc/sysconfig/GOto | grep -v 'XDRIVER="unknown"' | grep -q 'XDRIVER'
+       sleep 2
+    done
+
+    # Enable splash if it was enabled before
+    [ -f /proc/splash ] && echo "silent" > /proc/splash
+
+    echo -n "* configurator (pass2): "
+    setterm -cursor on
+fi
+
+# Mount configuration space
+[ ! -d /tmp/goto-fai ] && mkdir /tmp/goto-fai
+mount -obind /tmp/goto-fai /fai
+ldap2fai $mac
+
+chvt 3
+exit 0
diff --git a/contrib/fai/goto-fai/goto-support.lib b/contrib/fai/goto-fai/goto-support.lib
new file mode 100644 (file)
index 0000000..b608c04
--- /dev/null
@@ -0,0 +1,510 @@
+#!/bin/sh
+###############################################################################
+#                             GOsa agent library                              #
+###############################################################################
+
+SSH='ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile /dev/null" -o "BatchMode yes" '
+
+get_hostname_from_ip() {
+       v=$(host -i $1); w=${v##*[      ]}
+       echo ${w%%.*} | grep -q 'NX'
+       if [ $? -eq 0  ]; then
+               echo "unknown"
+       else
+               echo "$v" | grep -q ';;'
+               if [ $? -eq 0 ]; then
+                       if [ -n "$HOSTNAME" ]; then
+                               echo "$HOSTNAME"
+                       else
+                               echo "unknown"
+                       fi
+               else
+                       echo ${w%%.*}
+               fi
+       fi
+}
+
+get_hostname_from_display()
+{
+        if [ -n "$DISPLAY" ]; then
+
+                HOST=${DISPLAY%%:*}
+                NUMBER=${DISPLAY##*:}
+
+                # IP addresses are not supported here
+                echo $HOST | grep -q '^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$'
+                if [ $? -ne 0 ]; then
+                        echo ${DISPLAY%%.*}
+                               else
+                                       get_hostname_from_ip $HOST
+                               fi
+
+        else
+                echo "unknown"
+        fi
+}
+
+
+kill_user_processes() {
+       # don't let root do this
+       if [ "$USER" == "root" -o $UID -eq 0 ]; then
+               return
+       fi
+
+       # Preset, or load from file
+       candidates="kdeinit\: soffice.bin mozilla-bin"
+       [ -r /etc/goto/kill-process.conf ] && candidates=$(cat /etc/goto/kill-process.conf)
+
+       # kill old existing user processes
+       for process in $candidates; do
+               ps -fu $USER | grep "$process" | grep -v 'kprogress' | awk ' FS=" " { system("kill "$2) } '
+       done
+
+       # kill old existing user processes that didn't left us with SIGTERM
+       for process in $candidates; do
+               ps -fu $USER | grep "$process" | grep -v 'kprogress' | awk ' FS=" " { system("kill "$2) } '
+       done
+}
+
+fix_ldif() {
+        (cat -; echo "bank") | awk '
+/^[a-zA-Z]/     {
+        if(line!=""){
+                print line
+        }
+
+        line    = $0
+}
+/^ /    {
+        line    = line substr($0,2)
+}
+'
+}
+
+
+ldap_init() {
+       if [ $# -ne 2 ]; then
+               for config in /etc/*ldap/ldap.conf /etc/ldap.conf; do
+
+                       # Not readable? Continue
+                       [ ! -r $config ] && continue
+
+                       # Try to read config
+                       touch /tmp/agent.$$
+                       cat $config | while read line; do
+                               echo $line | grep -q '^BASE'
+                               [ $? -eq 0 ] && echo LDAP_BASE="\"$(echo $line|tr '\t' ' '|cut -d\  -f2-)\"" >>/tmp/agent.$$
+                               echo $line | grep -q '^HOST'
+                               [ $? -eq 0 ] && echo LDAP_HOST="$(echo $line|tr '\t' ' '|cut -d\  -f2-)" >>/tmp/agent.$$
+                               echo $line | grep -q '^URI'
+                               [ $? -eq 0 ] && echo LDAP_HOST="$(v=`echo $line|tr '\t' ' '|cut -d\  -f2-`;echo ${v##*://})" >> /tmp/agent.$$
+                       done
+                       eval $(cat /tmp/agent.$$)
+                       rm /tmp/agent.$$
+
+                       # One successful configuration should be enough
+                       break
+               done
+               if [ -z "$LDAP_HOST" -o -z "$LDAP_BASE" ]; then
+                       echo "Critical: no LDAP configuration found!"
+                       exit
+               fi
+       else
+               LDAP_HOST=$1
+               LDAP_BASE=$2
+       fi
+}
+
+
+ldap_count() {
+       ldapsearch -x -LLL -h "$LDAP_HOST" -b "$LDAP_BASE" "$1" dn | grep '^dn:' | wc -l
+}
+
+
+decode_blob() {
+       base64-decode > /tmp/agent-lib-decode.$$
+       file /tmp/agent-lib-decode.$$ 2>/dev/null| grep -qi 'text'
+       [ $? -eq 0 ] && cat /tmp/agent-lib-decode.$$ | recode 'utf8..latin1'
+       [ -f /tmp/agent-lib-decode.$$ ] && rm /tmp/agent-lib-decode.$$
+}
+
+ldap_import() {
+  for v in $(set grep ldap_import_ | cut -d= -f1); do unset $v; done
+  vname_lastrun=""
+  counter=0
+  > /tmp/agent-lib.$$
+  (ldapsearch -x -LLL -h "$LDAP_HOST" -b "$LDAP_BASE" $2 "$1" $3 2> /dev/null) | fix_ldif | sed 's/^\([^:]*\):\(.*\)$/\1="\2"/' | while read line; do
+               vname=$(echo $line|cut -d= -f1)
+               vvalue=$(echo $line|cut -d= -f2-)
+
+               echo $line | grep -q '=": '
+               if [ $? -eq 0 ]; then
+                       vvalue=`echo $line|sed 's/^[^="]*=": //'|decode_blob`
+                       vvalue="$vvalue\""
+               else
+                       vvalue=`echo $line|sed 's/^[^="]*=" //'`
+               fi
+
+               if [ "$vname_lastrun" == "$vname" ]; then
+                       counter=$(( $counter + 1 ));
+               else
+                       counter=0
+                       vname_lastrun=$vname
+               fi
+               
+               echo "ldap_import_$vname[$counter]=\"$vvalue" >> /tmp/agent-lib.$$
+       done
+
+       eval $(cat /tmp/agent-lib.$$)
+       rm /tmp/agent-lib.$$
+}
+
+ldap_cat() {
+   vname_lastrun=""
+   counter=0
+   > /tmp/agent-lib.$$
+   (ldapsearch -x -LLL -h "$LDAP_HOST" -b "$1" -s base 2> /dev/null) | fix_ldif | sed 's/
+^\([^:]*\):\(.*\)$/\1="\2"/' | while read line; do
+               vname=$(echo $line|cut -d= -f1)
+               vvalue=$(echo $line|cut -d= -f2-)
+
+               echo $line | grep -q '=": '
+               if [ $? -eq 0 ]; then
+                       vvalue=`echo $line|sed 's/^[^="]*=": //'|decode_blob`
+                       vvalue="$vvalue\""
+               else
+                       vvalue=`echo $line|sed 's/^[^="]*=" //'`
+               fi
+
+               if [ "$vname_lastrun" == "$vname" ]; then
+                       counter=$(( $counter + 1 ));
+               else
+                       counter=0
+                       vname_lastrun=$vname
+               fi
+
+               echo "ldap_import_$vname[$counter]=\"$vvalue" >> /tmp/agent-lib.$$
+       done
+
+       eval $(cat /tmp/agent-lib.$$)
+       rm /tmp/agent-lib.$$
+ }
+
+
+
+ldap_get_group_membership_of() {
+       ldapsearch -x -LLL -h "$LDAP_HOST" -b "$LDAP_BASE" "(memberUid=$1)" \
+                               cn 2> /dev/null | fix_ldif | awk '/^cn: / {print $2}'
+}
+
+
+ldap_get_applications_of() {
+       ldapsearch -x -LLL "(memberUid=$1)" gosaMemberApplication | fix_ldif | \
+                               awk '/^gosaMemberApplication:/ {print $2}'| sort | uniq
+}
+
+
+ldap_get_appservers() {
+       ldapsearch -x -LLL "(objectclass=goTerminalServer)" cn | fix_ldif | grep -w cn: |cut -d' ' -f 2
+}
+
+
+translate() {
+       # Look for translation
+       while read line; do
+               string="${line%%=*}"
+               if [ "$string" == "$*" ]; then
+                       echo "${line##*=}"
+                       return
+               fi
+       done < /etc/goto/goto-locales.dat
+       echo $*
+}
+
+
+show_progress() {
+       # No translation available
+       echo $PROGRESS $(translate "$*")
+}
+
+
+create_desktop_link() {
+       echo "$gosaApplicationFlags" | grep -q "D"
+       if [ $? -eq 0 ]; then
+               [ $DEBUG -eq 1 ] && echo "goto_setup: creating desktop link for application $application" 1>&2
+               cat << EOF > ~/Desktop/$cn
+[Desktop Entry]
+Comment=$description
+Encoding=UTF-8
+Exec=$gosaApplicationExecute
+Icon=$HOME/.kde/share/icons/${cn}.png
+Name=$gosaApplicationName
+Type=Application
+EOF
+       fi
+}
+
+
+create_menu_entry() {
+       echo "$gosaApplicationFlags" | grep -q "M"
+       if [ $? -eq 0 ]; then
+               [ $DEBUG -eq 1 ] && echo "goto_setup: creating menu link for application $application" 1>&2
+               cat << EOF > ~/.local/share/applications/$cn.desktop
+[Desktop Entry]
+Type=Application
+Encoding=UTF-8
+Exec=$gosaApplicationExecute
+Name=$gosaApplicationName
+GenericName=
+Comment=$description
+Icon=$HOME/.kde/share/icons/${cn}.png
+Terminal=false
+Categories=$appcat;
+EOF
+       fi
+}
+
+delete_all_applinks() {
+       list=`ldapsearch -x "objectClass=gosaApplication" cn | fix_ldif | awk '/^cn: / {print $2}'`
+       for link in $list; do
+               [ -f $HOME/Desktop/$link ] && rm -f $HOME/Desktop/$link
+               [ -f $HOME/.kde/share/applnk/$link.desktop ] && rm -rf $HOME/.kde/share/applnk/$link.desktop
+       done
+}
+
+
+function terminal_load_hardware_profile() {
+       rm -f $RAM/etc/sysconfig/GOto && touch $RAM/etc/sysconfig/GOto
+       ldapsearch -x -LLL -h $LDAP_HOST -b "$LDAP_BASE" -D "cn=terminal-admin,$LDAP_BASE" -w "$(cat /etc/goto/secret)" "(&(objectClass=gotoWorkstation)(macAddress=$1))" 2> /dev/null | fix_ldif | sed -e 's/^\([^:]*\): \(.*\)$/\U\1\E="\2"/' -e 's/^GOTO//g' >> /etc/sysconfig/GOto
+
+       # Get DN and load all parent defaults from tree
+       current=$(grep "^DN=" /etc/sysconfig/GOto|sed 's/\"//g;s/, /,/g;s/^.*,ou=terminals,ou=systems,//g')
+
+       # Load potential object group entries 
+       ldapsearch -x -LLL -h $LDAP_HOST -b "$LDAP_BASE" -D "cn=terminal-admin,$LDAP_BASE" -w "$(cat /etc/goto/secret)" "(&(objectClass=gosaGroupOfNames)(member=$(echo -n $current|sed 's/^DN=//')))" 2> /dev/null | fix_ldif | sed -e 's/^\([^:]*\): \(.*\)$/\U\1\E="\2"/' -e 's/^GOTO//g' >> /etc/sysconfig/GOto
+
+       # get reverse list of potential default entries - for backward compatibility
+       { while true; do
+               # write out current value
+               echo "ou=terminals,ou=systems,$current"
+
+               # prepare next entry
+               echo $current | grep -q ','
+               [ $? -ne 0 ] && break
+               [ "$LDAP_BASE" == "$current" ] && break
+               current=${current#*,}
+       done } | tac | while read line; do
+
+    # Read potential default entries and append
+    # them to sysconfig/GOto
+       ldapsearch -x -LLL -h $LDAP_HOST -D "cn=terminal-admin,$LDAP_BASE" -w "$(cat /etc/goto/secret)" -b $line "(&(objectClass=gotoWorkstation)(cn=wdefault))" 2> /dev/null | fix_ldif | sed -e 's/^\([^:]*\): \(.*\)$/\U\1\E="\2"/' -e 's/^GOTO//g' >> /etc/sysconfig/GOto
+  done
+
+  # Reverse sysconfig/GOto
+  tac /etc/sysconfig/GOto > /etc/sysconfig/GOto.tmp
+  mv /etc/sysconfig/GOto.tmp /etc/sysconfig/GOto
+}
+
+
+terminal_has_hardware_profile() {
+       # Do we have a configuration?
+       terminal_load_hardware_profile $1
+       grep -v "cn=default," /etc/sysconfig/GOto | grep -q "DN="
+}
+
+
+terminal_activated() {
+       # Do we have a configuration?
+       terminal_load_hardware_profile $1
+       grep -v ',ou=incoming,' /etc/sysconfig/GOto | grep -v 'cn=default,' | grep -q "DN="
+}
+
+
+terminal_dump_hwprofile() {
+       # Save mac address
+       mac=$1
+       name=$(hostname)
+       
+       # Source hardware information detected by hwsetup
+       for module in xserver sound netcard mouse; do
+               [ -f /etc/sysconfig/$module ] && . /etc/sysconfig/$module
+       done
+
+       # Get hardware information directly from /proc
+       cpu=$(cat /proc/cpuinfo | awk 'BEGIN { FS=": "; ORS="" } /^vendor_id/ {print $2" / "} /^model name/{print $2" - "} /^cpu MHz/ {print $2" MHz"}')
+       mem=$(cat /proc/meminfo | awk '/^MemTotal:/ {print $2" KB"}')
+       modlist=$(lsmod | sed -e '/^Module/d;/^snd/d;s/^\(\w*\).*$/\1/g')
+       hsync=$(ddcxinfo-knoppix -hsync|tr -d ' ')
+       vsync=$(ddcxinfo-knoppix -vsync|tr -d ' ')
+
+       # USB support?
+       [ -d /proc/bus/usb ] && usb="true" || usb="false"
+
+       # Add floppy/cdrom
+       grep -q 'floppy' /etc/sysconfig/autofs && FLOPPY='YES' || FLOPPY='NO'
+       grep -q 'cdrom' /etc/sysconfig/autofs && CDROM='YES' || CDROM='NO'
+
+       cat << EOF
+dn: cn=$name,ou=incoming,$LDAP_BASE
+objectClass: gotoWorkstation
+objectClass: goHard
+cn: $name
+macAddress: $mac
+gotoMode: locked
+gotoXDriver: $XMODULE
+gotoXMouseType: $XMOUSETYPE
+gotoXMouseport: $DEVICE
+gotoXHsync: $hsync
+gotoXVsync: $vsync
+ghUsbSupport: $usb
+gotoFloppyEnable: $FLOPPY
+gotoCdromEnable: $CDROM
+gotoSndModule: $SNDMODULE
+EOF
+
+       # Insert IDE-Devices
+       for f in /proc/ide/ide?/hd?/model; do
+               [ -f $f ] && echo "ghIdeDev: "$(cat $f)
+       done
+
+       (cat /proc/scsi/scsi | sed -ne 's/.*Vendor: \([^ ]*\) *Model: \([^ ]*\) *.*$/\1 \2/p') 2> /dev/null|while read line; do
+               echo ghScsiDev: $line
+       done
+
+       # Insert modules
+       for m in $modlist; do
+               echo "gotoModules: $m"
+       done | sort | uniq
+
+       # Add potential swap filesystems
+       [ -f /etc/sysconfig/swap ] && cat /etc/sysconfig/swap | while read line; do
+               echo "gotoFilesystem: $line"
+       done
+
+       # Add autofs devices
+       [ -f /etc/sysconfig/autofs ] && cat /etc/sysconfig/autofs | while read line; do
+               echo "gotoAutoFs: $line"
+       done
+
+       cat << EOF
+ghGfxAdapter: $XDESC
+ghNetNic: `cat /etc/sysconfig/netcard|grep "^FULLNAME"|cut -d= -f2|tr -d "\""`
+ghSoundAdapter: `cat /etc/sysconfig/sound|grep "^FULLNAME"|cut -d= -f2|tr -d "\""`
+ghMemSize: $mem
+ghCpuType: $cpu
+EOF
+}
+
+
+terminal_save_hardware_profile() {
+       # Get hardware ldif and strip out possibly broken entries
+    terminal_dump_hwprofile $1 | grep -v '^[^:]*: *$' &> /tmp/upload.ldif
+
+       # Upload ldif
+       while true; do
+               error=$(ldapadd -x -h "$LDAP_HOST" -D "cn=terminal-admin,$LDAP_BASE" -w "$(cat /etc/goto/secret)" < /tmp/upload.ldif 2>&1)
+               if [ $? -ne 0 ]; then
+                       dialog --msgbox "Das Terminal konnte sich nicht am LDAP anmelden. Bitte prüfen Sie de Einstellungen: $error" 14 60
+               else
+                       break
+               fi
+       done
+}
+
+
+terminal_alsa_setup() {
+       audio=$(lspci -n | awk '/ 0401/ {print $3}' | sed 's/://g' | head -1)
+       KVER=$(uname -r)
+       MODULE=$(cat /lib/modules/$KVER/modules.pcimap | (while read driver vendor device dummy; do
+               if expr $driver : 'snd-.*' > /dev/null; then
+                       printf '%04x%04x %s\n' $vendor $device $driver | grep "^$audio" | cut -d\  -f2
+               fi
+       done))
+       echo "SNDMODULE=\"$MODULE\"" >> /etc/sysconfig/sound
+}
+
+
+terminal_autofs_setup(){
+       wcount=1
+       lcount=1
+
+       # Remove old ones
+       rm -f /etc/sysconfig/autofs /etc/sysconfig/swap
+
+       # Generate autofs entries for removable devices
+       for d in /dev/floppy/?; do
+               [ "$d" == "/dev/floppy/?" ] && break
+               nr=$(echo $d | sed 's/^.*\/\([^/]*$\)/\1/g')
+               echo "floppy$nr -fstype=auto,sync,nodev,nosuid,umask=000,quiet,rw :$d" >> /etc/sysconfig/autofs
+       done
+
+       for d in /dev/cdroms/*; do
+               [ "$d" == "/dev/cdroms/*" ] && break
+               name=`echo $d | sed 's/^.*\/\([^/]*$\)/\1/g'`
+               echo "$name -fstype=iso9660,sync,nodev,nosuid,umask=000,quiet,ro :$d" >> /etc/sysconfig/autofs
+       done
+
+       # Generate autofs entries for fixed drives
+       (sfdisk -qLl | grep "^/" | tr -d '\*') | while read device d1 d2 d3 d4 type d5; do
+        case $type in
+         [4bce])
+               echo "win$wcount -fstype=vfat,sync,nodev,nosuid,umask=000,quiet,rw :$device" >> /etc/sysconfig/autofs
+               wcount=$(( $wcount + 1 ))
+               ;;
+         7)
+               echo "win$wcount -fstype=ntfs,sync,nodev,nosuid,umask=000,quiet,ro :$device" >> /etc/sysconfig/autofs
+               wcount=$(( $wcount + 1 ))
+               ;;
+         83)
+               echo "linux$lcount -fstype=ext3,sync,nodev,nosuid,umask=000,quiet,rw :$device" >> /etc/sysconfig/autofs
+               lcount=$(( $lcount + 1 ))
+               ;;
+      82)
+                echo "$device none swap sw 0 0" >> /etc/sysconfig/swap
+               ;;
+        esac
+       done
+}
+
+
+get_xdmcp_server(){
+       SERVERS=$(ldapsearch -LLL -b "$LDAP_BASE" -H $LDAP_HOST -x '(&(objectclass=goTerminalServer)(goXdmcpIsEnabled=true))'| awk '/^cn/{print $2}' 2> /dev/null)
+
+       # Generate load sorted server list
+       { for s in $SERVERS; do
+               xdmping $s -v -t 1 2> /dev/null | awk '!/contacting/ {print $5"|"$1"|"$2}' | sed 's/[:,]//g'
+       done } | egrep "^[0-9]" | sort -n > /tmp/xservers.tmp
+
+       case $(cat /tmp/xservers.tmp | wc -w | awk '{print $1}') in
+               0)
+                       return
+                       ;;
+               1)
+                       cat /tmp/xservers.tmp | cut -d\| -f2
+                       return
+            ;;
+               *)
+                       AVAILABLE=""
+                       for i in $(cat /tmp/xservers.tmp); do
+                               NEW=$(echo "$i" | awk -F "|" '{if ($1 < 0.5) print $1"|"$2}')
+                               [ -n "$NEW" ] && AVAILABLE="$NEW\n$AVAILABLE"
+                       done
+                       if [ -n "$AVAILABLE" ]; then
+                               echo -e "$AVAILABLE" > /tmp/xservers.tmp
+                               NUM=$(cat /tmp/xservers.tmp | wc -l | awk '{print $1 - 1}')
+                               ROW=$(echo $NUM | awk '{print rand() * $1 + 1 ;}' | cut -d . -f1)
+                               cat /tmp/xservers.tmp | sed -n "${ROW}p" | cut -d\| -f2
+                       else
+                               cat /tmp/xservers.tmp|egrep "^[0-9]"|tr "." ","|sort -n|head -1|cut -d\| -f2
+                       fi
+                       ;;
+       esac
+}
+
+
+get_fontpath() {
+       ldapsearch -x -LLL -h $LDAP_HOST -b "$LDAP_BASE" "(&(objectClass=goTerminalServer)(cn=$1))" |
+               grep "^goFontPath" | cut -d\  -f2- | sed 's!\/!\/!g'
+}
+
diff --git a/contrib/fai/goto-fai/ldap2fai b/contrib/fai/goto-fai/ldap2fai
new file mode 100755 (executable)
index 0000000..50f315d
--- /dev/null
@@ -0,0 +1,630 @@
+#!/usr/bin/perl
+# $Id$
+#*********************************************************************
+#
+# ldap2fai -- read FAI config from LDAP and create config space
+#
+# This script is part of FAI (Fully Automatic Installation)
+# (c) 2005, Thomas Lange <lange@informatik.uni-koeln.de>
+# (c) 2005, Jens Nitschke <jens.nitschke@2int.de>
+# (c) 2005, Jan-Marek Glogowski <glogow@fbihome.de>
+# (c) 2005, Cajus Pollmeier <pollmeier@gonicus.de>
+#
+#*********************************************************************
+
+use strict;
+use Net::LDAP;
+use MIME::Base64;
+use Getopt::Std;
+use File::Path;
+use File::Copy;
+use vars qw/ %opt /;
+
+my $base;
+my $ldapuri;
+my $ldapdir = "/etc/ldap/ldap.conf";
+my $outdir = "/fai";
+my $verbose = 0;
+my $opt_string = 'c:d:hv';
+my $hostname;
+
+getopts( "$opt_string", \%opt ) or usage("Hello");
+usage("Help") if $opt{h};
+
+$verbose = $opt{v} ? 1 : 0;
+$outdir  = $opt{d} ? $opt{d} : $outdir;
+$ldapdir = $opt{c} ? $opt{c} : $ldapdir;
+
+# Get MAC from cmdline
+my $mac = shift @ARGV;
+$mac eq '' && usage("MAC address not specified.");
+
+# Is outdir a directory
+-d "$outdir" || usage("'$outdir' is not a directory.\n");
+
+my @classes=(); # the classes a host belongs to
+
+# initialize ldap
+setup();
+my $ldap = Net::LDAP->new("$ldapuri") or die "$@";
+my $mesg = $ldap->bind;
+
+# create class hooks debconf disk_config package_config scripts files
+my @dirs= qw/class hooks debconf disk_config package_config scripts files/;
+foreach (@dirs) {
+  -d "$outdir/$_" || mkpath "$outdir/$_" 
+    || warn "WARNING: Can't create subdir $outdir/$_ $!\n";
+}
+
+@classes= get_classes($mac);
+prt_scripts();
+prt_package_list();
+prt_debconf();
+prt_templates();
+prt_var();
+prt_hooks();
+prt_disk_config();
+
+# create sources list
+if (!$hostname) {
+  -d "${outdir}/files/etc/apt/sources.list" 
+    || mkpath "${outdir}/files/etc/apt/sources.list";
+  copy ("${outdir}/tmp/apt-sources.list",
+    "${outdir}/files/etc/apt/sources.list/$hostname") ;
+}
+
+$mesg = $ldap->unbind;   # take down session
+exit 0;
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub setup
+{
+  # Read LDAP
+  open (LDAPCONF,"${ldapdir}") 
+    || usage("Can't open LDAP configuration$!\n");
+  my @content=<LDAPCONF>;
+  close(LDAPCONF);
+
+  # Scan LDAP config
+  foreach my $line (@content) {
+    $line =~ /^\s*(#|$)/ && next;
+    chomp($line);
+
+    if ($line =~ /^BASE\s+(.*)$/) {
+      $base= $1;
+      next;
+    }
+    if ($line =~ m#^URI\s+ldaps?://([^/:]+).*$#) {
+      $ldapuri= $1;
+      next;
+    }
+  }
+}
+
+sub usage
+{
+  (@_) && print STDERR "\n@_\n\n";
+
+  print STDERR << "EOF";
+usage: $0 [-hv] [-c config] [-d outdir] <MAC>
+
+-h        : this (help) message
+-c        : LDAP config file (default: ${ldapdir})
+-d        : output dir (default: ${outdir})
+-v        : be verbose
+EOF
+       exit -1;
+}
+#-----------------------------------------------------------------------------------
+
+sub write_file {
+
+       my @opts = @_;
+       my $len = scalar @_;
+       ($len < 2) && return;
+
+       my $filename = shift;
+       my $data = shift;
+
+       open (SCRIPT,">${filename}") || warn "Can't create ${filename}. $!\n";
+       print SCRIPT $data;
+       close(SCRIPT);
+
+  ($opts[2] ne "") && chmod oct($opts[2]),${filename};
+       ($opts[3] ne "") && chown_files(${filename}, $opts[3]);
+}
+
+#-----------------------------------------------------------------------------------
+
+sub chown_files
+{
+  my @owner = split('.',@_[1]);
+  my $filename = @_[0];
+  my ($uid,$gid);
+  $uid = getpwnam(@owner[0]);
+  $gid = getgrnam(@owner[1]);
+  
+  chown $uid, $gid, $filename;
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_classes {
+
+       # return list of FAI classes defined for host
+       my $mac = shift;
+       my (@classes,$mesg,$entry);
+
+  $mesg = $ldap->search(
+    base => "ou=systems,$base",
+    filter => "(&(macAddress=$mac)(objectClass=gotoWorkstation))",
+    attrs => [ 'FAIclass', 'cn']);
+  $mesg->code && die $mesg->error;
+  # normally, only one value should be returned
+  if ($mesg->count != 1) {
+      die "LDAP search for client failed. ".$mesg->count." entries have been returned\n";
+    }
+  
+   # this assigns the last value to @classes     
+   $entry= ($mesg->entries)[0];
+   @classes= split /\s+/,$entry->get_value('FAIclass');
+
+  # get hostname
+       my $hname= $entry->get_value('cn');
+  my $dn= $entry->dn;
+  $hostname= $hname;
+
+  # Search for object groups containing this client
+  $mesg = $ldap->search(
+    base => "ou=groups,$base",
+    filter => "(&(objectClass=gosaGroupOfNames)(objectClass=FAIobject)(member=$dn))",
+    attrs => [ 'FAIclass' ]);
+  $mesg->code && die $mesg->error;
+       foreach my $m ($mesg->entries) {
+    push @classes, split /\s+/,$m->get_value('FAIclass');
+  }
+
+       # print all classes to the file with hostname
+       open (FAICLASS,">$outdir/class/$hname") || warn "Can't create $outdir/class/$hname. $!\n";
+  my @newclasses;
+       foreach my $class (@classes) {
+
+    # We need to walk through the list of classes and watch out for
+    # a profile which is named like the class. Replace the profile
+    # name by the names of the included classes.
+    $mesg = $ldap->search(
+      base => "ou=systems,$base",
+      filter => "(&(objectClass=FAIprofile)(cn=$class))",
+      attrs => [ 'FAIclass' ]);
+    $mesg->code && die $mesg->error;
+
+    if ($mesg->count > 0){
+      foreach my $m ($mesg->entries) {
+        foreach my $tc (split /\s+/,$m->get_value('FAIclass')){
+          print FAICLASS "$tc\n";
+          push @newclasses, $tc;
+        }
+      }
+    } else {
+      print FAICLASS "$class\n";
+      push @newclasses, $class;
+    }
+  }
+       close(FAICLASS);
+       print "Host $hname belongs to FAI classes: ",join ' ',@newclasses,"\n" if $verbose;
+       return @newclasses;
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_variables {
+       # gets all variables defined for a class
+       # returns a list of lines in bourne shell syntax
+
+   my $class = shift; 
+        my ($mesg,$var_base,$entry,$line,@vars);
+
+        $mesg = $ldap->search(
+                      base => "$base",
+                      filter => "(&(cn=$class)(objectClass=FAIvariable))",
+                      attrs => [ 'cn']);
+        return if ($mesg->count() == 0); # skip if no such object exists              
+        $mesg->code && die $mesg->error;
+
+        $entry=($mesg->entries)[0];
+        $var_base=$entry->dn;
+
+        $mesg = $ldap->search(
+                        base => "$var_base",
+                        filter => "(objectClass=FAIvariableEntry)",
+                        attrs => ['cn', 'FAIvariableContent']);
+        return if ($mesg->count() == 0); # skip if no such object exists
+        $mesg->code && die $mesg->error;
+                        
+
+        foreach $entry ($mesg->entries) {
+                $line= sprintf "%s=\'%s\'\n", $entry->get_value('cn'), 
+                        $entry->get_value('FAIvariableContent');
+                push @vars,$line;
+        }
+        return @vars;
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_var {
+
+       my (@lines, $hname);
+
+       foreach my $class (@classes) {
+               @lines = get_variables($class);
+               next until @lines; # do not create .var file if no variables are defined
+               open (FAIVAR,">$outdir/class/${class}.var") 
+      || warn "Can't create $outdir/class/$hname.var.$!\n";
+               print FAIVAR @lines;
+               close(FAIVAR);
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_disk_config {
+
+       my $class = shift;
+  my ($mesg,$entry,$line,@diskconfig,$partition_base,$dn,%diskline,$xxmesg);
+
+       # Search for partition schema for the specified class
+       $mesg = $ldap->search(
+                     base => "$base",
+                     filter => "(&(cn=$class)(objectClass=FAIpartitionTable))" );
+
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       $entry=($mesg->entries)[0];
+       $partition_base= $entry->dn;
+       
+       # Search for disks
+       $mesg = $ldap->search(
+                     base => "$partition_base",
+                     filter => "(objectClass=FAIpartitionDisk)" );
+
+       return if ($mesg->code == 32); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       foreach $entry ($mesg->entries) {
+    my $logic_count= 4;
+    my $primary_count= 0;
+               my $dn=$entry->dn;
+               my $disk=$entry->get_value('cn');
+    my $part;
+               undef %diskline;
+               $diskline{0} = "disk_config $disk\n";
+               $xxmesg = $ldap->search(
+                       base => "$dn",
+                       filter => "objectClass=FAIpartitionEntry" );
+               $xxmesg->code && die $xxmesg->error;
+               foreach my $dl ($xxmesg->entries) {
+      if ($dl->get_value('FAIpartitionType') eq 'primary'){
+        $primary_count++;
+      } else {
+        $logic_count++;
+      }
+                       if ($dl->get_value('FAIpartitionFlags') eq 'preserve'){
+        if ($dl->get_value('FAIpartitionType') eq 'primary'){
+          $part= 'preserve'.$primary_count;
+        } else {
+          $part= 'preserve'.$logic_count;
+        }
+                               $line= sprintf "%-7s %-12s %-12s %-10s ; %s\n",
+                                       $dl->get_value('FAIpartitionType'),
+                                       $dl->get_value('FAImountPoint'),
+                                       $part,
+                                       $dl->get_value('FAImountOptions') eq '' 
+                                               ? 'rw' : $dl->get_value('FAImountOptions'),
+                                       $dl->get_value('FAIfsOptions');
+                       }         
+                       elsif ($dl->get_value('FAIfsType') eq 'swap'){
+                               $line= sprintf "%-7s %-12s %-12s %-10s\n",
+                               $dl->get_value('FAIpartitionType'),
+                               $dl->get_value('FAImountPoint'),
+                               $dl->get_value('FAIpartitionSize'),
+                               $dl->get_value('FAImountOptions') eq '' 
+                                       ? 'rw' : $dl->get_value('FAImountOptions');
+                       } 
+                       else {
+                               $line= sprintf "%-7s %-12s %-12s %-10s ; %s %s\n",
+                               $dl->get_value('FAIpartitionType'),
+                               $dl->get_value('FAImountPoint'),
+                               $dl->get_value('FAIpartitionSize'),
+                               $dl->get_value('FAImountOptions') eq '' 
+                                       ? 'rw' : $dl->get_value('FAImountOptions'),
+                               $dl->get_value('FAIfsOptions'),
+                               $dl->get_value('FAIfsType');
+                       }
+
+                       $diskline{$dl->get_value('FAIpartitionNr')}=$line;
+               }
+               foreach my $l (sort {$a <=> $b} keys %diskline) {
+                       push @diskconfig, $diskline{$l};
+               }
+       }
+       return @diskconfig;
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_disk_config {
+
+       # create one disk_config file
+
+       my ($class,@lines);
+
+       foreach $class (reverse @classes) {
+               @lines=get_disk_config($class);
+               next until @lines; # skip if nothing is defined for this class
+
+    print "Generating partition layout for class '${class}'\n." if $verbose;
+               open (FAIVAR,">${outdir}/disk_config/${class}") 
+      || warn "Can't create $outdir/disk_config/$class. $!\n";
+               print FAIVAR join '',@lines;
+               close(FAIVAR);
+               last; # finish when one config file is created
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_packages {
+
+       # gets list of packages defined for a class
+
+       my $class = shift;
+       my ($mesg,$entry,$line,$method,%packlist);
+
+  -d "${outdir}/tmp" || mkpath "${outdir}/tmp"
+    || warn "Can't create ${outdir}/tmp. $!\n";
+  print "Generate sources.list for install\n" if $verbose;
+       open (SOURCES,">>${outdir}/tmp/apt-sources.list") 
+    || warn "Can't create ${outdir}/tmp/apt-sources.list. $!\n";
+
+       $mesg = $ldap->search(
+                       base => "$base",
+                       filter => "(&(cn=$class)(objectClass=FAIpackageList))" ,
+                       attrs => [ 'FAIpackage', 'FAIinstallMethod', 
+                 'FAIdebianMirror', 'FAIdebianRelease', 'FAIdebianSection']);
+
+       $mesg->code && die $mesg->error;
+       # should also return only one value
+
+       undef %packlist;
+       foreach $entry ($mesg->entries) {
+               $method=$entry->get_value('FAIinstallMethod');
+               push @{$packlist{$method}}, $entry->get_value('FAIpackage');
+
+               print SOURCES "deb ".$entry->get_value('FAIdebianMirror')." ".$entry->get_value('FAIdebianRelease')." ";
+    my $section;
+    foreach $section ($entry->get_value('FAIdebianSection')){
+      print SOURCES "$section ";
+    }
+    print SOURCES "\n";
+       }
+
+  close (SOURCES);
+
+       # return a ref to the hash of arrays (key of the hash is the method),
+       # the value is the array of package names for this method
+       return \%packlist;
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_package_list {
+
+       my (@lines,$plist,$method,$value);
+
+       foreach my $class (@classes) {
+               $plist=get_packages($class);
+               # test if hash contains any keys or values
+               unless (keys %{$plist}) {
+                       next;
+               }
+
+    print "Generate package list for class '$class'.\n" if $verbose;
+               open (PACKAGES,">$outdir/package_config/$class") 
+      || warn "Can't create $outdir/package_config/$class. $!\n";
+               while (($method, $value) = each %{$plist}) {
+                       print PACKAGES "PACKAGES $method\n";
+                       print PACKAGES join "\n",@{$value};
+                       print PACKAGES "\n";
+               }
+               close(PACKAGES);
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_templates {
+
+       # get list of template-files defined for a class
+       my $class = shift;
+       my ($mesg,$entry,$str,$pfad,$name,$owner,$mode,$template_base,@template);
+
+       $mesg = $ldap->search(
+                       base => "$base",
+                       filter => "(&(cn=$class)(objectClass=FAItemplate))",
+                       attrs => ['cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       $entry=($mesg->entries)[0];
+       $template_base=$entry->dn;
+
+       $mesg = $ldap->search(
+                       base => "$template_base",
+                       filter => "(objectClass=FAItemplateEntry)",
+                       attrs => ['FAItemplateFile', 'FAItemplatePath', 'FAIowner', 'FAImode' ,'cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       foreach $entry ($mesg->entries) {
+               $name = $entry->get_value('cn');
+               $owner = $entry->get_value('FAIowner');
+               $owner = $entry->get_value('FAImode');
+               $pfad = $entry->get_value('FAItemplatePath');
+               chomp($pfad);
+               -d "${outdir}/files/${pfad}" || mkpath "${outdir}/files/${pfad}"
+      || warn "WARNING: Can't create subdir ${outdir}/files/${pfad} !$\n";
+    print "Generate template '$pfad' ($name) for class '$class'.\n" if $verbose;
+               write_file( "${outdir}/files/${pfad}/${class}", 
+                       $entry->get_value('FAItemplateFile'),$entry->get_value('FAImode'),$entry->get_value('FAIowner'));
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_templates {
+       my ($class);
+
+       foreach $class (reverse @classes) {
+               get_templates($class);
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_debconf {
+
+       # gets list of packages defined for a class
+
+       my $class = shift; 
+       my ($mesg,$entry,$str,$debconf_base,@debconf);
+
+       $mesg = $ldap->search(
+                       base => "$base",
+                       filter => "(&(cn=$class)(objectClass=FAIpackageList))",
+                       attrs => ['cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       $entry=($mesg->entries)[0];
+       $debconf_base=$entry->dn;
+
+       $mesg = $ldap->search(
+                       base => "$debconf_base",
+                       filter => "(objectClass=FAIdebconfInfo)" ,
+                       attrs => [ 'FAIpackage', 'FAIvariable', 
+                               'FAIvariableType','FAIvariableContent']);
+       $mesg->code && die $mesg->error;
+
+       # undef @debconf;
+       foreach $entry ($mesg->entries) {
+               $str = sprintf "%s %s %s %s\n",
+               $entry->get_value('FAIpackage'),
+               $entry->get_value('FAIvariable'),
+               $entry->get_value('FAIvariableType'),
+               $entry->get_value('FAIvariableContent');
+               push @debconf, $str;
+       }
+       return @debconf;
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_debconf {
+
+       my @lines;
+       my $class;
+
+       foreach $class (@classes) {
+               @lines = get_debconf($class);
+               next until @lines;
+    print "Generate DebConf for class '$class'.\n" if $verbose;
+               open (DEBCONF,">${outdir}/debconf/${class}") || warn "Can't create $outdir/debconf/$class. $!\n";
+               print DEBCONF @lines;
+               close(DEBCONF);
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_scripts {
+       my ($class,@lines);
+
+       foreach $class (@classes) {
+               get_scripts($class);
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_scripts {
+
+       # gets list of packages defined for a class
+
+       my $class = shift;
+       my ($mesg,$entry,$str,$script_base,$prio,$name,$script);
+
+       $mesg = $ldap->search(
+                       base => "$base",
+                       filter => "(&(cn=$class)(objectClass=FAIscript))",
+                       attrs => ['cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       $entry=($mesg->entries)[0];
+       $script_base= $entry->dn;
+
+       $mesg = $ldap->search(
+                       base => "$script_base",
+                       filter => "(objectClass=FAIscriptEntry)",
+                       attrs => ['FAIpriority', 'FAIscript', 'cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+       
+       foreach $entry ($mesg->entries) {
+               $name  = $entry->get_value('cn');
+               $prio  = $entry->get_value('FAIpriority');
+               $script= sprintf('%02d-%s', $prio, $name);
+
+    -d "$outdir/scripts/$class" || mkpath "$outdir/scripts/$class" ||
+       warn "WARNING: Can't create subdir $outdir/scripts/$class !$\n";
+
+               write_file("${outdir}/scripts/${class}/${script}",
+                       $entry->get_value('FAIscript'), "0700");
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub prt_hooks {
+       my ($class,@lines);
+
+       foreach $class (reverse @classes) {
+               get_hooks($class);
+       }
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_hooks {
+
+       # gets list of packages defined for a class
+
+       my $class = shift;
+       my ($mesg,$entry,$str,$hook_base,$prio,$task,$hook,$name);
+
+       $mesg = $ldap->search(
+                       base => "$base",
+                       filter => "(&(cn=$class)(objectClass=FAIhook))",
+                       attrs => ['cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+
+       $entry=($mesg->entries)[0];
+       $hook_base= $entry->dn;
+
+       $mesg = $ldap->search(
+                       base => "$hook_base",
+                       filter => "(objectClass=FAIhookEntry)",
+                       attrs => ['FAItask', 'FAIscript', 'cn']);
+       return if ($mesg->count() == 0); # skip if no such object exists
+       $mesg->code && die $mesg->error;
+       
+       foreach $entry ($mesg->entries) {
+               $name = $entry->get_value('cn');
+               $task = $entry->get_value('FAItask');
+               $prio = $entry->get_value('FAIpriority');
+               $hook = sprintf('%s.%s', ${task}, ${class});
+
+               write_file("${outdir}/hooks/${hook}", 
+                       $entry->get_value('FAIscript'), "0700");
+       }
+}
+
+# vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste
diff --git a/contrib/fai/goto-fai/secret b/contrib/fai/goto-fai/secret
new file mode 100644 (file)
index 0000000..7f480a8
--- /dev/null
@@ -0,0 +1 @@
+your secret terminal-admin password