From 81406946bf264a05e2fd840016321fe6793baa13 Mon Sep 17 00:00:00 2001 From: cajus Date: Thu, 24 Nov 2005 13:24:13 +0000 Subject: [PATCH] Added goto-fai package stuff for interested people git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@2046 594d385d-05f5-0310-b6e9-bd551577e9d8 --- contrib/fai/goto-fai/Makefile | 19 + contrib/fai/goto-fai/confdir.DEFAULT.source | 19 + contrib/fai/goto-fai/debian/README.debian | 6 + contrib/fai/goto-fai/debian/changelog | 5 + contrib/fai/goto-fai/debian/control | 12 + contrib/fai/goto-fai/debian/copyright | 8 + contrib/fai/goto-fai/debian/dirs | 2 + contrib/fai/goto-fai/debian/postrm | 11 + contrib/fai/goto-fai/debian/preinst | 11 + contrib/fai/goto-fai/debian/rules | 50 + contrib/fai/goto-fai/diversions/fai | 272 +++++ .../fai/goto-fai/diversions/setup_harddisks | 954 ++++++++++++++++++ contrib/fai/goto-fai/diversions/subroutines | 449 +++++++++ contrib/fai/goto-fai/faimond | 93 ++ contrib/fai/goto-fai/get_fai_dir | 128 +++ contrib/fai/goto-fai/goto-support.lib | 510 ++++++++++ contrib/fai/goto-fai/ldap2fai | 630 ++++++++++++ contrib/fai/goto-fai/secret | 1 + 18 files changed, 3180 insertions(+) create mode 100644 contrib/fai/goto-fai/Makefile create mode 100755 contrib/fai/goto-fai/confdir.DEFAULT.source create mode 100644 contrib/fai/goto-fai/debian/README.debian create mode 100644 contrib/fai/goto-fai/debian/changelog create mode 100644 contrib/fai/goto-fai/debian/control create mode 100644 contrib/fai/goto-fai/debian/copyright create mode 100644 contrib/fai/goto-fai/debian/dirs create mode 100755 contrib/fai/goto-fai/debian/postrm create mode 100755 contrib/fai/goto-fai/debian/preinst create mode 100755 contrib/fai/goto-fai/debian/rules create mode 100755 contrib/fai/goto-fai/diversions/fai create mode 100755 contrib/fai/goto-fai/diversions/setup_harddisks create mode 100755 contrib/fai/goto-fai/diversions/subroutines create mode 100755 contrib/fai/goto-fai/faimond create mode 100755 contrib/fai/goto-fai/get_fai_dir create mode 100644 contrib/fai/goto-fai/goto-support.lib create mode 100755 contrib/fai/goto-fai/ldap2fai create mode 100644 contrib/fai/goto-fai/secret diff --git a/contrib/fai/goto-fai/Makefile b/contrib/fai/goto-fai/Makefile new file mode 100644 index 000000000..85756c2d2 --- /dev/null +++ b/contrib/fai/goto-fai/Makefile @@ -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 index 000000000..afed2a22e --- /dev/null +++ b/contrib/fai/goto-fai/confdir.DEFAULT.source @@ -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 index 000000000..584b1dad6 --- /dev/null +++ b/contrib/fai/goto-fai/debian/README.debian @@ -0,0 +1,6 @@ +goto-fai for Debian +------------------- + +Comments regarding the Package + +Cajus Pollmeier , 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 index 000000000..905c15e7c --- /dev/null +++ b/contrib/fai/goto-fai/debian/changelog @@ -0,0 +1,5 @@ +goto-fai (2.0-1) unstable; urgency=low + + * Initial release. + + -- Cajus Pollmeier 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 index 000000000..1cf61844b --- /dev/null +++ b/contrib/fai/goto-fai/debian/control @@ -0,0 +1,12 @@ +Source: goto-fai +Section: lhm/main +Priority: optional +Maintainer: Cajus Pollmeier +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 index 000000000..e18fe190b --- /dev/null +++ b/contrib/fai/goto-fai/debian/copyright @@ -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 + +Copyright: + + diff --git a/contrib/fai/goto-fai/debian/dirs b/contrib/fai/goto-fai/debian/dirs new file mode 100644 index 000000000..ca882bbb7 --- /dev/null +++ b/contrib/fai/goto-fai/debian/dirs @@ -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 index 000000000..526aaad47 --- /dev/null +++ b/contrib/fai/goto-fai/debian/postrm @@ -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 index 000000000..fa469b9ad --- /dev/null +++ b/contrib/fai/goto-fai/debian/preinst @@ -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 index 000000000..6967ca5c7 --- /dev/null +++ b/contrib/fai/goto-fai/debian/rules @@ -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 index 000000000..eb48bbd9a --- /dev/null +++ b/contrib/fai/goto-fai/diversions/fai @@ -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 + ----------------------------------------------------- +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 index 000000000..de1427c4e --- /dev/null +++ b/contrib/fai/goto-fai/diversions/setup_harddisks @@ -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] default: parse classes +# [-c] 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 +# Last changes: 9.11.2001 by Thomas Lange +# reiserfs patch 8.11.2001 by Diane Trout +# 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 |first|end" +# The disk_config command starts the parsing. +# It has to be the first command. +# 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|- |preserve [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. +# +# "|preserve": +# "": +# 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": +# This is the alternative for the size attribute. +# is the partition number. For example +# preserve3 for the third partition. If the +# 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 per inodes +# (only ext2/3 filesystem) +# -m % : 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 : set reiserfs hash +# -v : 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 ("/" or "swap" or "no" or "extended") +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] default: parse classes + [-c] 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 = ){ + 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 /etc/fstab +#**************************************************** +sub WriteFSTab{ + my ($FileSystemTab, $device, $type, $filename); + $FileSystemTab = << "EOM"; +# /etc/fstab: static file system information. +# +# +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 index 000000000..c2590e8ad --- /dev/null +++ b/contrib/fai/goto-fai/diversions/subroutines @@ -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 $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 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 index 000000000..3ebdd5d12 --- /dev/null +++ b/contrib/fai/goto-fai/faimond @@ -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 = ; + 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 index 000000000..5f246973c --- /dev/null +++ b/contrib/fai/goto-fai/get_fai_dir @@ -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 +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 index 000000000..b608c041a --- /dev/null +++ b/contrib/fai/goto-fai/goto-support.lib @@ -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 index 000000000..50f315d77 --- /dev/null +++ b/contrib/fai/goto-fai/ldap2fai @@ -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 +# (c) 2005, Jens Nitschke +# (c) 2005, Jan-Marek Glogowski +# (c) 2005, Cajus Pollmeier +# +#********************************************************************* + +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=; + 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] + +-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 index 000000000..7f480a8c1 --- /dev/null +++ b/contrib/fai/goto-fai/secret @@ -0,0 +1 @@ +your secret terminal-admin password -- 2.30.2