#! /bin/bash
# vim:set sw=4 ts=4:
#! /bin/bash -x
#
##############################################################################
#
# $Id: make_alice_repository,v 1.11 2005/04/11 14:14:34 fabian Exp $
#
#############################################################################
#
# ALICE
# Automatic Linux Installation and Configuration Environment
#
# Copyright (c) 2000-2002 SuSE Linux Solutions AG, Eschborn, Germany
#               2002-2004 SuSE Linux AG, Eschborn, Germany
#               2005           SUSE GmbH, Nuernberg, Germany
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#############################################################################
#
# Author: 
#
##############################################################################
#
# NAME
#	make_alice_repository - Script to create a CVS repository for an ALICE configuration.
#
# SYNTAX
#	make_alice_repository [ -A <path> ] [ -d <path>[:Owner[:OwnerGroup]] ] [ -fhin ]
#		[ -m <module name> ] [ -p CVSuser[:UserAccount[:UserGroup[:CVSpasswd]]] ]
#		[ -v <level> ]...
#
# DESCRIPTION
#	The script "make_alice_repository" is used to create a CVS repository for an ALICE
#	configuration.  In case a CVS repository does not already exists, a CVS repository
#	is created.
#
#	After the creation of the CVS repository (if needed), an initial ALICE configura-
#	tion taken from the ALICE distro is imported into the CVS repository.
#
# OPTIONS
#	-A <path>
#		Overwrite the default path where to find the ALICE sample files for import
#		into the CVS repository.
#		The default path is either '/usr/share/doc/packages/alice2/', or if not
#		available the path '/usr/lib/alice2/'.
#		Under the specified path the directory "samples" is expected.  It is an
#		error, if this is not the case.
#
#	-d <path>[:Owner[:OwnerGroup]]
#		Set the path of the CVS repository to <path> as well as the CVS repository
#		owner and group name.
#		Default path is "/home/CVS_DEPOT".
#		Path must be absolute.  A non absolute path is converted into an absolute
#		path relative to current working directory.
#
#		If the CVS repository already exists, the default values of the CVS
#		repository owner and group account names (<Owner> and <OwnerGroup>) are
#		those of the 'CVSROOT' directory found under the repository path <path>.
#
#		If the CVS repository does not exists, the default values of the CVS
#		repository owner and group account names (<Owner> and <OwnerGroup>) are,
#		when running as 'root':
#				/home/CVS_DEPOT:cvs:cvs
#
#		Use '-' (dash) to use the default value in any of the parameters.
#
#		It is not allowed to use the group name 'root' as <OwnerGroup> -- nor any
#		other group name that results in an id value of 0 (zero).
#
#		If the account and/or the group does not exists, and if running as 'root',
#		the missing account/group is created.
#		If the account is created, the account is created with a locked password,
#		i.e. the account cannot be used for logon, until the password for the
#		account has been set separately.
#
#		When running as non 'root', the following limitations applies:
#
#		0. The <path> must be to a directory where the executing user is allowed
#		   to write to.
#
#		1. The <Owner> is force set to the name of the executing user, and cannot
#		   be changed.
#
#		2. The <OwnerGroup> must be equal to a group name that the executing user
#		   is a member of.
#		   If the executing user is member of the 'cvs' group, this is the default
#		   value, otherwise the name of the master group to which the exuecting
#		   user belongs is the default group name.
#
#		Taken the above limitations into consideration, non 'root' users than
#		have these default values, if executing user is member of the 'cvs' group:
#				CVS_DEPOT:<user name>:cvs
#		otherwise
#				CVS_DEPOT:<user name>:<master group>
#
#		where the string '<user name>' is the user account name associated with
#		the executing user, and the string '<master group>' is the name of the
#		master group to which the executing user belongs to.
#		Use '-' (dash) to use the default value in any of the parameters.
#
#	-f	Force the setting of the owner- and groupship to the CVS owner user and
#		group account names (see option '-d') on an already existing CVS
#		repository.
#		CAUTION; Be aware that not only will the usage of this function create
#		the CVS repository owner and group account names (see option '-d'), but
#		it will also change the file owner-, groupship, and permissions on files
#		under the existing 'CVSROOT' directory in the CVS repository (see option
#		'-d' for that path to this)!
#
#	-h	Print out a short help menu.
#
#	-i	Activate the CVS pserver (see option '-p') using the "inetd" daemon,
#		instead of the default "xinetd" daemon.
#		In case neither the "xinetd" nor the "inetd" has been installed, no
#		attempt is made to configure the starting of the CVS pserver.
#		This option only takes effect, if the option '-p' has been used, too.
#		Must be running as 'root' for this option to be effective, otherwise no
#		configuration of the INET daemon will take place.
#
#	-m <module name>
#		Set module name under which the ALICE configuration files are placed in
#		the CVS repository to <module name>.
#		Default module name is "ALICE".
#
#	-n	Do not (re)start nor activate the "xinetd/inetd" daemon after configura-
#		tion.
#		This option only takes effect, if the option '-p' has been used, too.
#		The option only has an effect if running as 'root', it is otherwise
#		ignored.
#
#	-p CVSuser[:UserAccount[:UserGroup[:CVSpasswd]]]
#		Configure the CVS repository such that it is possible to make use of the
#		CVS pserver facility by creating the necessary CVS anonymous user accounts
#		and group, as well as configuring the selected INET daemon (see option
#		'-i') and (re)start this daemon now as well as after system reboots.
#
#		Use the CVS user account name <CVSuser> as user account for the CVS
#		pserver using the encrypted password <CVSpasswd> as password for this
#		account.
#		Associate the CVS user account name <CVSuser> with the user account named
#		<UserAccount>, which master group name is <UserGroup>.
#
#		The default values used are:	anoncvs:cvsALICE:cvsALICE:8KtQmlqS9phIc
#
#		where the string '8KtQmlqS9phIc' is the encrypted empty password.
#		Use '-' (dash) to use the default value in any of the parameters.
#		The created CVS user account will have read-only access to the CVS
#		repository.
#
#		It is not allowed to use the name 'root' as <CVSuser>, <UserAccount> or
#		<UserGroup> -- nor any other name that results in an id value of 0 (zero).
#
#		In order to create the associated CVS user account and group name, as
#		well as configuring the INET daemon, the script must be executed by the
#		'root' user.
#
#		When running as non 'root', the following limitations applies to the
#		parameters:
#
#		0. The <UserAccount> and <UserGroup> must already exist.
#
#		1. The <UserAccount> must be a member of the <UserGroup> as well as of
#		   the <OwnerGroup> (see option '-d').
#
#		2. The CVS pserver must already have been defined for the CVS repository
#		   path (see option '-d') in order for the defined CVS pserver facility
#		   in the CVS repository to work proper.
#		   No configuration nor starting attempts are being made with regards to
#		   the INET daemons -- i.e. option '-i', should it have been used, is
#		   completely ignored.
#
#	-v <level>
#		Set the verbosity to <level>, where <level> is a binary number indicating
#		the level of verbosity as well as what kind of verbosity that should be
#		displayed.
#		Default level is 1, i.e. only very limited verbosity -- Use 0 for no
#		verbosity.
#
#		This option is accumulative.
#
#		<level>	Information displayed
#		0	No information is displayed at all.
#		1	Minimum user information about the progress of the command.
#		2	Additional user information regarding the progress.
#		4	Internal debug information.
#		
#		The <level> values can be added in a binary fashion to get various
#		information levels and combinations.
#
#		For example will '-v 5' give "Minimum user information about the progress
#		of the command" (=1) as well as "Internal debug information" (=4).
#
# ARGUMENTS
#	None required nor accepted.
#
# FILES
#	None.
#
# SEE ALSO
#	bash(1).
#	The ALICE documentation.
#
# AUTHOR
#	Created on 2004-Jun-16 by Dennis Olsson, SUSE LINUX AG <DOlsson@SUSE.com>
#	Modfied on 2004-Jul-12 by Fabian Herschel, SUSE LINUX AG <Fabian@SUSE.com>
#
# RELEASED
#	make_alice_repository - Script to create a CVS repository for an ALICE configuration
#
#	This program is released under the GPL Version 2 (or later), and is
#	Copyright (c) 2004 Dennis Olsson, SUSE LINUX AG <DOlsson@SUSE.com>
#	All rights reserved.
#
#	This program is free software; you can redistribute it and/or
#	modify it under the terms of the GNU General Public License
#	as published by the Free Software Foundation; either version 2
#	of the License, or (at your option) any later version.
#
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#	The creation of this script was sponsored in 2004 by
#	T-Mobile Deutschland GmbH, Bonn, Germany, EU.
#
# REVISION HISTORY
#	$Id: make_alice_repository,v 1.11 2005/04/11 14:14:34 fabian Exp $
#
#	Use 'cvs log' to view the history log for this script.
# -------------------------------------------------------------------------------------------------

# -------------------------------------------------------------------------------------------------
# Initialization
 
set -o privileged		# Do NOT inherit functions.
set -o nounset			# Raise error on unset variable.
set -o errexit			# Exit on first error.
set +o noglob			# Expand wildcards.
set +o noclobber		# Overwrite files.
set +o allexport		# Do NOT Export all variables.
shopt -s expand_aliases		# Expand aliases, even if non interactive.
# set -o verbose		# List commands as they are read.
# set -o xtrace			# List commands after parameter expansion.
 
alias echo="echo -e"		# Allow the usage of escape sequences.

# Inhibit the "cd" command to print the path changed to
unset CDPATH

# Set our name:)
typeset -r PROG="${0##*/}"

# Default exit value
typeset -i _exit=0

# Running as 'root'?
if [[ $(id -u) -eq 0 ]]
then
    typeset -r IsRoot=true  IsNotRoot=false
else
    typeset -r IsRoot=false IsNotRoot=true
fi

# Where to create temporary working files
typeset -r TMP="/var/tmp/ALICE-$$"


# -------------------------------------------------------------------------------------------------
# Default values for option changable values

# Default is to use the "xinetd" daemon configuration, if option '-i' has not been used
Inetd="xinetd"

# Default level for option '-v' (verbose level)
typeset -ri INFOlevelDefault=1

# Default path to where to place the CVS repository -- Path *must* be absolute!
CVSdepot="/home/CVS_DEPOT"

# Default CVS repository user account, group, and encrypted password
CVSdepotAcct="cvs"
    # In case of non 'root', the default owner is the executing user
    ${IsNotRoot} && CVSdepotAcct=$(id -un)
CVSdepotGrp="cvs"
    # In case of non 'root', the 'cvs' is only default, when user is member of this
    # group, otherwise the default group is the master group of the user
    ${IsNotRoot} && [[ -z $(id -Gn | egrep "${CVSdepotGrp}") ]] && CVSdepotGrp=$(id -gn)

# Default vendor tag name for the import of the ALICE configuration files into the CVS repository.
ALICEmodule="ALICE"

# Default CVS user account and user account settings
CVSuser="cvsanon"
CVSuserAcct="cvsALICE"
CVSuserGrp="cvsALICE"
    # The encrypted string '8KtQmlqS9phIc' is the
    #   empty password coded with salt '8K'
CVSuserPwd="8KtQmlqS9phIc"

# Vendor release tag used for the import of the ALICE configurations file into the CVS repository.
set +o errexit			# Suspend: Exit on first error.
    ALICEversion=$(rpm -q alice 2>/dev/null)
    [[ $? -ne 0 ]] && ALICEversion="alice-Not-Installed"
    ALICEversion=$(echo "${ALICEversion}" | tr "." "_")
    typeset -r ALICEversion
set -o errexit			# Restore: Exit on first error.


# -------------------------------------------------------------------------------------------------
# Function defintions

# Function to extract and print a nice self documenting help menu
function Usage()
{
    sed -e 's/^# \?//' -e '/^SYNTAX/,/^DESCRIPTION/!d' -e '/^DESCRIPTION/q' "${0}" | sed -e '$d'
    sed -e 's/^# \?//' -e '/^OPTIONS/,/^FILES/!d' -e '/^FILES/q' "${0}" | sed -e '$d'
    _exit 1
    # NEVER REACHED!
}

# Functions used to exit from script
function _exit()
{
    [[ -n "${1:-}" ]] && _exit=${1}
    # Ignore catching of script ending
    trap EXIT

    # Clean up after our selfs, if need be
    [[ -e "${TMP}" ]] && rm -rf "${TMP}"*

    # Exit with set error code
    exit ${_exit}
    # NEVER REACHED!
}

trap _exit EXIT 1 2 3 15

# Function to write info messages in accordance with the value of ${INFOlevel}
#
#	info <level> [-c] [-p] [ <text> ]...
# where
#	<level> is the level value at which the <text> is written
#	-c	Do not output a NewLine after having written the last text line
#	-p	Do not output the "${PROG}: " text sequence on the first line
#	<text>	One or more text strings to write.  Each text argument is written
#		on its own separate line
function info()
{
    set +o errexit			# Suspend: Exit on first error.
    local -i level=${1-0}
    local Ptext="${PROG}: "
    shift
    if [[ $(( ${level} & ${INFOlevel} )) -ne 0 ]]
    then
	local c=""
	if [[ $# -gt 1 && "${1}" == "-c" ]]
	then
	    c="\c"
	    shift
	fi
	if [[ $# -gt 1 && "${1}" == "-p" ]]
	then
	    Ptext=""
	    shift
	fi
	if [[ $# -eq 0 ]]
	then
	    echo ${c}
	else
	    local -i i=1
	    while [[ ${i} -le $# ]]
	    do
		eval text=\"\${${i}}\"
		[[ -n "${c}" && ${i} -eq $# ]] && text="${text}${c}"
		echo "${Ptext}${text}"
		(( i += 1))
		Ptext="${PROG}: "
	    done
	fi
    fi
    set -o errexit			# Restore: Exit on first error.
}

function error()
{
    set +o errexit			# Suspend: Exit on first error.
    _exit=${1}
    shift
    if [[ "${1}" == "-C" ]]
    then
	continue=true
	shift
    fi
    for text in "${@}"
    do
	echo "${PROG}: ${text}"
    done

    if [[ -z "${continue:-}" ]]
    then
	_exit
	# NEVER REACHED!
    fi
}

# Function to set an integer value to a variable
#
#	setInteger <var> <value> [ <default value, if not integer> ]
function setInteger()
{
    declare var="${1}" value="${2}"
    declare -i defval="${3:-0}" result=0
    set +o errexit			# Suspend: Exit on first error.

    sign="${value:0:1}"
    if [[ "${sign}" == "+" || "${sign}" == "-" ]]
    then
	value="${value:1}"
    else
	sign=""
    fi
    integer=$(expr match "${value}" '0*\([0-9]\+\)$')
    result=$?
    [[ ${result} -eq 1 && "${integer}" == "0" ]] && result=0
    [[ -z "${integer}" ]] && integer="${defval}"
    eval "${var}"="${sign}${integer}"

    set -o errexit			# Restore: Exit on first error.
    return ${result}
}

# Function to set a variable equal to a parsed valued, provided the parsed value is not
# equal to a predetermined value.

# Function to set a variable <var to set> equal the extracted parsed value <str> from
# a string to be parsed <var to parse> using the string separator <separation char>,
# provided that the extracted parsed value <str> is not the default value indicator
# <defval1>.
# In case the assigned value is empty, and the empty value specificator <DefVal2> has
# not been set, the variable <var to set> is not changed, otherwise <var to set> is set
# to the value of <DefVal2>.
#
# <value> ::= first of string of <var to parse> that does not contain <separation char>
# if (<value> != <value to test for>) <var to set> ::= <value>
# <var to parse> ::= String from <var to set> with "<value><separation char>" removed
#
#	setIfNotEqual <var to set> <var to parse> <separation char> <DefVal1> [ <DefVal2> ]
function setIfNotEqual()
{
    local -r VarReturn="${1}" VarValue="${2}" Sep="${3}" DefVal1="${4}" DefVal2="${5:-}"
    eval local -r String="\${${VarValue}}"
    local -r str="${String%%${Sep}*}"
    set +o errexit			# Suspend: Exit on first error.

    if [[ "${str}" != "${DefVal1}" ]]
    then
	if [[ -z "${str}" ]]
	then
	    [[ -n "${DefVal2}" ]] && eval ${VarReturn}="${DefVal2}"
	else
	    eval ${VarReturn}="${str}"
	fi
    fi
    eval ${VarValue}="${String#*${Sep}}"
    eval local tmp=\"\${${VarValue}}\"
    [[ "${tmp}" == "${String}" ]] && eval ${VarValue}=""

    set -o errexit			# Restore: Exit on first error.
}

# Function to verify that passed name is not "root", nor that its id is = 0 (zero).
#
#	verifyAcctName <name> <id file>
# where
#	<name> is name to verify
#	<id file> is either "group" (for group names) or "passwd" (for user names)
#
# Return values:
#	0 (true)	<name> is OK
#	2 (false)	<name> is not OK, i.e. either "root" or has id = 0!
function verifyAcctName()
{
    local -r name="${1}" file="${2}"
    local id=$(awk -F : '/^'"${name}"':/ {print $3}' "/etc/${file}")
    [[ "${name}" == "root" || ( -n "${id}" && "${id}" -eq 0 ) ]] && return 2
    return 0
}

# Function handling the creation of the CVS pserver entry in the config file for the
# "xinetd" daemon.
function createXinetdEntry()
{
    # In case the "xinetd" has been installed and is in use on the system,
    # configure the "/etc/inetd.conf" file to handle the CVS pserver.
    local etc_xinetd="/etc/xinetd.d"
    local etc_xinetdConf="${etc_inetd%.*}.conf" redirect="&>/dev/null"
    if [[ ! -d "${etc_xinetd}" ]]
    then
	mkdir "${etc_xinetd}"
	if ! egrep -q "^[[:space:]]*includedir[[:space:]]+${etc_xinetd}" "${etc_xinetdConf}"
	then
	    echo "\nincludedir ${file}" >>"${etc_xinetdConf}"
	fi
    fi
    if [[ -d "${etc_xinetd}" ]]
    then
	local cvs="${etc_xinetd}/cvs"
	info 1 "Activating the CVS pserver in '${cvs}'."
	if [[ -e "${cvs}" ]]
	then
	    local cvsOld="${cvs}.$(date '+%Y-%m-%d_%H%S%M')"

	    set +o errexit			# Suspend: Exit on first error.
		egrep -q "disable.*=.*no" "${cvs}"
		local -i enabled=$?
		egrep -q "server_args.*--allow-root=${CVSdepot}[[:space:]]+" "${cvs}"
		local -i cvspserver=$?
	    set -o errexit			# Restore: Exit on first error.
	    if [[ ${enabled} -eq 0 && ${cvspserver} -eq 0 ]]
	    then
		info 2 "The CVS pserver was already activated for CVS repository '${CVSdepot}'."
	    else
		info 2 "Using already available configuration file '${cvs}' to"
		cp "${cvs}" "${TMP}"
		chmod 400 "${TMP}"
		if [[ ${enabled} -ne 0 ]]
		then
		    info 2 "Activate the CVS pserver services via the 'xinetd' daemon."
		    touch "${TMP}sed"
		    chmod 600 "${TMP}sed"
		    sed -e 's/^\([[:space:]]*disable.*=\).*/\1 no/' "${TMP}" >"${TMP}sed"
		    mv -f "${TMP}sed" "${TMP}"
		    chmod 400 "${TMP}"
		fi
		if [[ ${cvspserver} -ne 0 ]]
		then
		    info 2 "Add the parameter '--allow-root=${CVSdepot}' to the CVS pserver."
		    touch "${TMP}sed"
		    chmod 600 "${TMP}sed"
		    local -r OldString="^\([[:space:]]*server_args.*=.*-f\)\(.*\)"
		    local -r NewString="\1 --allow-root=${CVSdepot}\2"
		    sed -e "s${OldString}${NewString}" "${TMP}" >"${TMP}sed"
		    mv -f "${TMP}sed" "${TMP}"
		    chmod 400 "${TMP}"
		fi
		mv -f "${cvs}" "${cvsOld}"
		mv -f "${TMP}" "${cvs}"
		chmod a-w "${cvs}" "${cvsOld}"
	    fi
	else
	    info 2 "Creating a new configuration file for activation of the CVS pserver."
	    cat >"${cvs}" <<EOF
# CVS pserver (remote access to your CVS repositories)
# Please read the section on security and passwords in the CVS manual,
# before you enable this.
# default: off

service cvspserver
{
	disable		= no
	socket_type	= stream
	protocol	= tcp
	wait		= no
	user		= root
	server		= /usr/bin/cvs
	server_args	= -f --allow-root=${CVSdepot} pserver
}
EOF
	    chmod 444 "${cvs}"
	fi

	# Make sure that the "xinetd" daemon will be started after boot and is running now
	if ! ${opt_n}
	then
	    info 2 "(Re)Starting the 'xinetd' daemon to activate the newest configuration."
	    chkconfig --set xinetd on
	    [[ ${INFOlevel} -gt 0 ]] && local redirect=""
	    eval rcxinetd restart ${redirect}
	fi
    else
	error 20 "${etc_inetd} -- No such directory -- Cannot activate xinetd for CVS pserver!"
	# NEVER REACHED!
    fi
}

# Function handling the creation of the CVS pserver entry in the config file for the
# "inetd" daemon.
function createInetdEntry()
{
    # In case the "inetd" has been installed and is in use on the system,
    # configure the "/etc/inetd.conf" file to handle the CVS pserver.
    local etc_inetd="/etc/inetd.conf" redirect="&>/dev/null"
    if [[ -f "${etc_inetd}" ]]
    then
	local etc_inetdOld="${etc_inetd}.$(date '+%Y-%m-%d_%H%S%M')"
	info 1 "Activating the CVS pserver in '${etc_inetd}'."
	if grep -q "^cvspserver" "${etc_inetd}"
	then
	    if egrep -q "^cvspserver.*[[:space:]]+--allow-root=${CVSdepot}[[:space:]]+" "${etc_inetd}"
	    then
		info 2 "The CVS pserver was already activated for CVS repository '${CVSdepot}'."
	    else
		info 2 "Found an already active CVS pserver entry in '${etc_inetd}'."
		info 2 "Adding the parameter '--allow-root=${CVSdepot}' to that line."
		sed -e "/^cvspserver/scvs -f& --allow-root=${CVSdepot}" "${etc_inetd}" >"${TMP}"
		mv -f "${etc_inetd}" "${etc_inetdOld}"
		mv "${TMP}" "${etc_inetd}"
		chmod a-w "${etc_inetd}" "${etc_inetdOld}"
	    fi
	else
	    info 2 "Creating an inetd entry for the CVS pserver."
	    local entry=$(echo "cvspserver\tstream\ttcp\tnowait\troot\t/usr/sbin/tcpd\c")
	    entry="${entry}$(echo "\t/usr/bin/cvs -f --allow-root=${CVSdepot} pserver")"
	    sed -e "/^#[[:space:]]*cvspserver/a \\${entry}" "${etc_inetd}" >"${TMP}"
	    mv -f "${etc_inetd}" "${etc_inetdOld}"
	    mv "${TMP}" "${etc_inetd}"
	    chmod a-w "${etc_inetd}" "${etc_inetdOld}"
	fi

	# Make sure that the "xinetd" daemon will be started after boot and is running now
	if ! ${opt_n}
	then
	    info 2 "(Re)Starting the 'inetd' daemon to activate the newest configuration."
	    chkconfig --set inetd on
	    [[ ${INFOlevel} -gt 0 ]] && redirect=""
	    eval rcinetd restart ${redirect}
	fi
    else
	error 20 "${etc_inetd} -- No such file -- Cannot activate inetd for CVS pserver!"
	# NEVER REACHED!
    fi
}

# -------------------------------------------------------------------------------------------------
# Recognized options -- make use of "silent error" reporting from "getopts" (the first ":")!!
typeset -r OPTIONS=":A:d:fhim:np:v:"
opt_A=false
opt_d=false
    opt_dAcct=false
    opt_dGrp=false
opt_f=false
opt_i=false
    typeset -i INFOlevel=0
opt_m=false
opt_n=false
opt_p=false
opt_v=false

# Decode the options
ArgMissing=""
while getopts "${OPTIONS}" opt
do
    case "${opt}" in
	A)	# Change the setting of the path to the ALICE samples
	    opt_A=true
	    AlicePath="${OPTARG}"
	;;

	d)	# Change the path to the CVS repository
	    opt_d=true
	    STR="${OPTARG}"
	    # For 'root':
	    #	<path>[:Owner[:OwnerGroup]]
	    # For others:
	    #	<path>[:<user name>[:((cvs | master group) | member group)]]
	    setIfNotEqual CVSdepot STR ":" "-"
	    if [[ -n "${STR}" ]]
	    then
		opt_dAcct=true
		tmp="${CVSdepotAcct}"
		setIfNotEqual CVSdepotAcct STR ":" "-"
		if ${IsNotRoot} && [[ "${CVSdepotAcct}" != $(id -un) ]]
		then
		    error 2 -C "Option -${opt}: Not allowed to change owner to '${CVSdepotAcct}'."
		fi
	    fi
	    if [[ -n "${STR}" ]]
	    then
		opt_dGrp=true
		setIfNotEqual CVSdepotGrp STR ":" "-"
		if ${IsNotRoot} && [[ -z $(id -Gn | egrep "${CVSdepotGrp}") ]]
		then
		    error 2 -C "Option -${opt}: User '$(id -un)' is not member of group '${CVSdepotGrp}'."
		fi
	    fi
	    [[ -n "${STR}" ]] &&
		error 2 -C "Option -${opt}: Too many fields passed: ${STR}"
	;;

	f)	# Force change the user ownership and permissions on an already existing CVS depot
	    opt_f=true
	;;

	h)	# Print out a short help menu
	    Usage
	    # NEVER REACHED!
	;;

	i)	# Use "inetd" daemon configuration instead of the default
	    opt_i=true
	    Inetd="inetd"
	;;

	m)	# Change the module name
	    opt_m=true
	    ALICEmodule="${OPTARG}"
	;;

	n)	# Do not (re)start nor activate the INET daemon
	    opt_n=true
	;;

	p)	# Create CVS pserver entries (server and user)
	    opt_p=true
	    STR="${OPTARG}"
	    # CVSuser[:UserAccount[:UserGroup[:CVSpasswd]]]
	    setIfNotEqual CVSuser STR ":" "-"
	    [[ -n "${STR}" ]] && setIfNotEqual CVSuserAcct STR ":" "-"
	    [[ -n "${STR}" ]] && setIfNotEqual CVSuserGrp  STR ":" "-"
	    [[ -n "${STR}" ]] && setIfNotEqual CVSuserPwd  STR ":" "-"
	    [[ -n "${STR}" ]] &&
		error 2 -C "Option -${opt}: Too many fields passed: ${STR}"
	;;

	v)	# Change the verbosity
	    opt_v=true
	    if setInteger tmp "${OPTARG}" "${INFOlevel}"
	    then
		let INFOlevel+=${tmp}
	    else
		error 2 -C "Option -${opt}: Passed value '${OPTARG}' is not an integer."
	    fi
	;;

	:)	# Option requiring argument is missing its argument
	    echo "${PROG}: Option '${OPTARG}' requires an argument!"
	    ArgMissing=true
	;;

	?)	# Unknown option found
	    echo "${PROG}: Option '${OPTARG}' is unknown -- Ignored!"
	;;
    esac
done
# Shift any options off the argument stack
shift $((${OPTIND} - 1))

# Evaluate options
( ${opt_n} && ! ${opt_p} ) && info 3 "Option '-n' ignored as option '-p' was not used."

# Complain, if an option is missing its parameter
[[ -n "${ArgMissing}" ]] && Usage

# Set the default ${INFOlevel}, if not already set
typeset -r INFOlevel=${INFOlevel:-${INFOlevelDefault}}

# Convert CVSdepot to an absolute path, if need be
[[ "${CVSdepot:0:1}" == "." ]] && CVSdepot="$(pwd)/${CVSdepot:1}"
[[ "${CVSdepot:0:1}" != "/" ]] && CVSdepot="$(pwd)/${CVSdepot}"

# Note where the CVS repository <CVSROOT> directory is
typeset -r CVSroot="${CVSdepot}/CVSROOT"

# In case the CVS repository already exists, and the CVS repository owner/group
# defaults have not been overwritten, set the default values to the current
# owner/group of the ${CVSroot} directory.
if [[ -d "${CVSroot}" ]]
then
    read -a owner < <(ls -ld "${CVSroot}")
    if ! ${opt_dAcct}
    then
	CVSdepotAcct="${owner[2]}"
	info 2 "Using user account '${CVSdepotAcct}' from already existing CVS repository."
    fi
    if ! ${opt_dGrp}
    then
	CVSdepotGrp="${owner[3]}"
	info 2 "Using group name '${CVSdepotGrp}' from already existing CVS repository."
    fi
    if ! ${opt_f}
    then
	[[ "${CVSdepotAcct}" != "${owner[2]}" ]] &&
	    error 3 -C \
		"The CVS repository owner '${CVSdepotAcct}' differ from actual owner '${owner[2]}'."
	[[ "${CVSdepotGrp}" != "${owner[3]}" ]] &&
	    error 3 -C \
		"The CVS repository group '${CVSdepotGrp}' differ from actual group '${owner[3]}'."
	[[ ${_exit} -eq 3 ]] && error 3 -C "Use option '-f' to force set the specified value."
    fi
    unset -v owner
fi

# Verify that the CVS repository user/group name is valid
verifyAcctName "${CVSdepotGrp}" "group" ||
    error 2 -C \
	"CVS repository group name '${CVSdepotGrp}' cannot be 'root' nor" \
	"any other group name that has an id of 0 (zero)." \
	""

verifyAcctName "${CVSuser}" "passwd" ||
    error 2 -C \
	"The CVS repository account name '${CVSuser}' cannot be 'root' nor" \
	"any other user name that has an id of 0 (zero)." \
	""

verifyAcctName "${CVSuserAcct}" "passwd" ||
    error 2 -C \
	"The CVS repository account '${CVSuser}' cannot map to user" \
	"account '${CVSuserAcct}', while 'root' or any other user name" \
	"mapping to an id of 0 (zero) is not allowed." \
	""

verifyAcctName "${CVSuserGrp}" "group" ||
    error 2 -C \
	"The CVS repository account '${CVSuser}' cannot map to user" \
	"group '${CVSuserGrp}', while 'root' or any other group name" \
	"mapping to an id of 0 (zero) is not allowed." \
	""

[[ ${_exit} -eq 2 ]] &&
    error 2 -C \
	"For security reasons this is not allowed." \
	"Use either option '-d' or '-p' (which ever is appropriate)" \
	"to specify a non 'root' account/group name."

# Clean up as well as write protect the fetched options and their arguments
unset -v opt_dAcct opt_dGrp
typeset -r opt_A opt_d opt_f opt_i Inetd opt_m opt_n opt_p opt_v
typeset -r CVSdepot CVSdepotAcct CVSdepotGrp
typeset -r CVSuser CVSuserAcct CVSuserGrp CVSuserPwd


# -------------------------------------------------------------------------------------------------
# Cannot continue, if an error was detected!
[[ ${_exit} -ne 0 ]] && exit


# -------------------------------------------------------------------------------------------------
# Set the default path to the ALICE directory
if ! ${opt_A}
then
    # The last argument ("") *MUST* be present -- it is the "emergency stopper" :-)
    for AlicePath in "/usr/share/doc/packages/alice2/" "/usr/lib/alice2/" ""
    do
	[[ -d "${AlicePath}" ]] && break
    done
fi
typeset -r AlicePath

# Verify that AlicePath is "reasonable".
if [[ -z "${AlicePath}" || ! -e "${AlicePath}" ]]
then
    error 2 \
	"No path found to the ALICE sample files needed for the CVS repository import." \
	"Use option '-A <path>' to specify where to find the ALICE sample files."
    # NEVER REACHED!

elif [[ ! -d "${AlicePath}" ]]
then
    error 2 "${AlicePath} - is not a directory!"
    # NEVER REACHED!

elif [[ ! -r "${AlicePath}" ]]
then
    error 2 "${AlicePath} - is not readable!"
    # NEVER REACHED!
fi

####################################
# Verify that we have access to the ALICE sample files
ALICEsamples="${AlicePath}/samples"
[[ "${ALICEsamples%%/*}" == "." ]] && ALICEsamples="$(pwd)${ALICEsamples#.}"
if [[ ! -d "${ALICEsamples}" ]]
then
    if ${opt_A}
    then
	text="Path in option -A"
    else
	text="Default path '${AlicePath}'"
    fi
    error 5 "${ALICEsamples} - No such directory." "${text} not found -- Giving up!"
    # NEVER REACHED!
fi
typeset -r ALICEsamples


# -------------------------------------------------------------------------------------------------
# Decode arguments...

# None to decode & none expected!
[[ $# -gt 0 ]] && Usage


# -------------------------------------------------------------------------------------------------
# Verify the existence of a CVS repository, and create one if non available
CVSdepotCreated=false
if [[ ! -d "${CVSdepot}" ]]
then
    ####################################
    # Create a new initial CVS repository
    info 1 "Creating new CVS repository in '${CVSdepot}'."
    mkdir -p "${CVSdepot%/*}"
    set +o errexit			# Suspend: Exit on first error.
	cvs -d "${CVSdepot}" init
	[[ $? -ne 0 ]] && error 3 "Cannot create the CVS repository -- Giving up!!"
    set -o errexit			# Restore: Exit on first error.
    CVSdepotCreated=true

elif [[ -d "${CVSdepot}/${ALICEmodule}" ]]
then
    error 4 "CVS module \"${ALICEmodule}\" exists already -- Giving up!"
    # NEVER REACHED!

else
    info 1 "Using existing CVS repository in '${CVSdepot}'."
fi
typeset -r CVSdepotExists

####################################
# Harded the security on the CVS repository
if ${CVSdepotCreated} || ${opt_f}
then
    ####################################
    # In case the CVS repository was created from scratch,
    # or if forced settings have been requested, prepare the CVS repository group and
    # user account, as well as harded the file permissions on the <CVSROOT> files.

    ####################################
    # Add the CVSdepotGrp, if not already existing
    if grep -q "^${CVSdepotGrp}:" /etc/group
    then
	${IsRoot} && info 1 "Using already existing CVS depot group '${CVSdepotGrp}'."
    else
	info 1 "Creating CVS depot group '${CVSdepotGrp}'."
	/usr/sbin/groupadd "${CVSdepotGrp}"
    fi

    ####################################
    # Add the CVSdepotAcct, if running as 'root'
    if ${IsRoot}
    then
	if id "${CVSdepotAcct}" &>/dev/null
	then
	    if [[ -z $(id -Gn "${CVSdepotAcct}" | egrep "${CVSdepotGrp}") ]]
	    then
		info 1 "Adding CVS group '${CVSdepotGrp}' to CVS depot account '${CVSdepotAcct}'."
		Grps=$(id -Gn "${CVSdepotAcct}" | sed -e 's/^[[:alnum:]]\+ \{0,1\}//' -e 's/ /,/g')
		[[ -n "${Grps}" ]] && Grps="${Grps},"
		/usr/sbin/usermod -G "${Grps}${CVSdepotGrp}" "${CVSdepotAcct}"
	    else
		info 2 "CVS depot account '${CVSdepotAcct}' already member of CVS group '${CVSdepotGrp}'."
	    fi
	else
	    info 1 "Creating CVS depot account '${CVSdepotAcct}' w/master group '${CVSdepotGrp}'."
	    /usr/sbin/useradd -g "${CVSdepotGrp}" -G "${CVSdepotGrp}" \
		-c "CVS master repository account" \
		-d "/tmp" -s "/bin/false" "${CVSdepotAcct}"
	fi
    fi

    ####################################
    # Increase the security for the CVS repository by setting the groupship as well
    # as the file permissions rather restrictive
    info 1 "Harding the file permissions on '<CVS depot>/CVSROOT', using"
    if ${CVSdepotCreated}
    then
	CVSpath="${CVSdepot}"
    else
	CVSpath="${CVSroot}"
    fi
    if ${IsRoot}
    then
	info 1 "CVS repository owner account '${CVSdepotAcct}' and group '${CVSdepotGrp}'."
	chown -R "${CVSdepotAcct}:${CVSdepotGrp}" "${CVSpath}"
    else
	info 1 "CVS repository owner group '${CVSdepotGrp}'."
	chgrp -R "${CVSdepotGrp}" "${CVSpath}"
    fi
    find "${CVSpath}" \
	-type d -exec chmod u=rwx,g=rwxs,o=rx '{}' ';' -o -type f -exec chmod u=r,g=r,o=r '{}' ';'

    # Allow write access to the <CVSROOT>/{history,val-tags} files for user and group
    chmod u=rw,g=rw,o=r "${CVSroot}/"{history,val-tags}
fi


# -------------------------------------------------------------------------------------------------
# Import the ALICE sample files into the CVS repository
# During the CVS import run out of the ${ALICEsamples} dir
cd "${ALICEsamples}"
    cmd="cvs -d '${CVSdepot}'"
    cmd="${cmd} import -m 'Import of sample files from ALICE version ${ALICEversion}'"
    cmd="${cmd} '${ALICEmodule}' SUSE_LINUX_AG '${ALICEversion}'"
    [[ $(( ${INFOlevel} & 2 )) -eq 0 ]] && cmd="${cmd} &>/dev/null"
    eval ${cmd}
# Return to where we were
cd -
[[ $? -ne 0 ]] &&
    error 6 "Was not able to import the ALICE configuration files -- Manual intervention required!"

# Harden the security on the newly created module
find "${CVSdepot}/${ALICEmodule}" \
    -type d -exec chmod u=rwx,g=rwxs,o= '{}' ';' -o -type f -exec chmod u=r,g=r,o= '{}' ';'


# -------------------------------------------------------------------------------------------------
# Info the user about what has been done
info 1 "The initial ALICE config files were successfully imported into"
info 1 "the CVS repository under the CVS module name \"${ALICEmodule}\"."
info 2
info 2 "To make use of the newly created CVS module \"${ALICEmodule}\"," "you can make use of:"
info 2
info 2 "\texport CVSROOT=\":ext:${USER}@$(hostname -f):${CVSdepot}\""
info 2 "\texport CVS_RSH=\"ssh\""
info 2
info 2 "\tIn your working directory do: \tcvs -r checkout \"${ALICEmodule}\""
info 2
info 2 "\tFor further instruction on how to use the \"cvs\" command," "\tplease use \"man cvs\"."


# -------------------------------------------------------------------------------------------------
# If called for, create the user group and account as well as the CVS user account, and set the
# access permissions on the CVS repository.
if ${opt_p}
then
    info 2

    ####################################
    # Only if running as 'root', can we do this
    if ${IsRoot}
    then
	####################################
	# Add a CVS group, if not already existing
	if grep -q "^${CVSuserGrp}:" /etc/group
	then
	    info 1 "Using already existing User Group '${CVSuserGrp}'."
	else
	    info 1 "Creating User Group '${CVSuserGrp}'."
	    /usr/sbin/groupadd "${CVSuserGrp}"
	fi

	####################################
	# Creating the anonymous user for alice
	unset Grps GroupOption
	if id "${CVSuserAcct}" &>/dev/null
	then
	    action="Modifying"
	    cmd="mod"
	    Grps=$(id -Gn "${CVSuserAcct}" | sed -e 's/^[[:alnum:]]\+ \{0,1\}//' -e 's/ /,/g')
	    [[ -n "${Grps}" ]] && Grps="${Grps},"
	else
	    action="Creating"
	    cmd="add"
	    GroupOption="-g ${CVSuserGrp}"
	fi
	info 1 "${action} User Account '${CVSuserAcct}'."
	/usr/sbin/user${cmd} ${GroupOption:-} -G "${Grps:-}${CVSdepotGrp}" \
		-c "Anonymous CVS account for ALICE repository" \
		-d "/tmp" -s "/bin/false" "${CVSuserAcct}"
    else
	####################################
	# Otherwise we have to make sure that we can set up things as a normal user

	# The CVS user group must exists
	if ! grep -q "^${CVSuserGrp}:" /etc/group
	then
	    error 10 "CVS user group '${CVSuserGrp}' does not exists."
	    # NEVER REACHED!
	fi

	####################################
	# The CVS user account must exists
	if ! id "${CVSuserAcct}" &>/dev/null
	then
	    error 10 "CVS user account '${CVSuserAcct}' does not exists."
	    # NEVER REACHED!
	fi

	####################################
	# The CVS user account must be a member of the CVS user and depot group
	if [[ -z $(id -Gn "${CVSuserAcct}" | egrep "${CVSuserGrp}") ]]
	then
	    error 10 "CVS user account '${CVSuserAcct}' is not a member of group '${CVSuserGrp}'."
	    # NEVER REACHED!
	fi
	if [[ -z $(id -Gn "${CVSuserAcct}" | egrep "${CVSdepotGrp}") ]]
	then
	    error 10 "CVS user account '${CVSuserAcct}' is not a member of group '${CVSdepotGrp}'."
	    # NEVER REACHED!
	fi
    fi

    ####################################
    # Restrict the access to the created ${ALICEmodule}
    info 1 -c "Restricting access to CVS module '${ALICEmodule}' to "
    if ${IsRoot}
    then
	info 1 -p "'${CVSuserAcct}:${CVSuserGrp}'."
	chown -R "${CVSuserAcct}:${CVSuserGrp}" "${CVSdepot}/${ALICEmodule}"
    else
	info 1 -p "'$(ls -ld "${CVSdepot}/${ALICEmodule}" | awk '{print $3}'):${CVSuserGrp}'."
	chgrp -R "${CVSuserGrp}" "${CVSdepot}/${ALICEmodule}"
    fi
    find "${CVSdepot}/${ALICEmodule}" \
    	-type d -exec chmod u=rwx,g=rwxs,o= '{}' ';' -o -type f -exec chmod u=r,g=r,o= '{}' ';'

    ####################################
    # Create an user entry for ${CVSuser} in the 'readers' list, if not already existing
    file="${CVSdepot}/CVSROOT/readers"
    if grep -q "^${CVSuser}\$" "${file}" 2>/dev/null
    then
	info 2 "User '${CVSuser}' is already present in '<CVSROOT>/readers'."
    else
	info 1 "Adding CVS readers entry for user '${CVSuser}'."
	[[ ! -e "${file}" ]] && touch "${file}"
	chmod u+w "${file}"
	echo "${CVSuser}" >>"${file}"
	if ${IsRoot}
	then
	    chown "${CVSdepotAcct}:${CVSdepotGrp}" "${file}"
	else
	    chgrp "${CVSdepotGrp}" "${file}"
	fi
    fi
    chmod u=r,g=r,o=r "${file}"

    ####################################
    # Create a password entry for the ${CVSuser}, if not already existing
    file="${CVSdepot}/CVSROOT/passwd"
    if grep -q "^${CVSuser}:" "${file}" 2>/dev/null
    then
	info 2 "CVS User '${CVSuser}' already present in '<CVSROOT>/passwd' - Left unchanged."
    else
	info 1 "Adding CVS password entry for CVS user '${CVSuser}'."
	[[ ! -e "${file}" ]] && touch "${file}"
	chmod u+w "${file}"
	echo "${CVSuser}:${CVSuserPwd}:${CVSuserAcct}" >>"${file}"
	if ${IsRoot}
	then
	    chown "${CVSdepotAcct}:${CVSdepotGrp}" "${file}"
	else
	    chgrp "${CVSdepotGrp}" "${file}"
	fi
    fi
    chmod u=r,g=r,o=r "${file}"

    ####################################
    # Verify the existence of needed RPM package, before trying to configure the CVS pserver
    if $IsRoot
    then
	if rpm -q "${Inetd}" &>/dev/null
	then
	    ####################################
	    # Activating the CVS pserver
	    if [[ "${Inetd}" == "xinetd" ]]
	    then
		createXinetdEntry

	    elif [[ "${Inetd}" == "inetd" ]]
	    then
		createInetdEntry

	    else
		error 10 \
		    "Unknown value for \${Inetd} = '${Inetd}'." \
		    "Cannot configure the INET-daemon to handle the CVS pserver!"
		# NEVER REACHED!
	    fi
	else
	    error 5 -C \
		"The '${Inetd}' RPM package has not been installed." \
		"Cannot configure the INET-daemon to handle the CVS pserver!"
	fi
    else
	if rpm -q "${Inetd}" &>/dev/null
	then
	    conf="/etc/${Inetd}.conf"
	    if [[ -e "${conf}" ]]
	    then
		if [[ "${Inetd}" = "xinetd" ]]
		then
		    cvs="${conf%.*}.d/cvs"
		    egrep -q "^[[:space:]]*includedir[[:space:]]+${conf%.*}.d}" "${conf}" ||
			cvs="${conf}"
		    awkprg='/^[[:space:]]*service cvspserver/'
		    awkprg="${awkprg}"' {found=1} found==1 && /disable/ && /yes/ {exit 1}'
		    awkprg="${awkprg}"'found==1 && /server_args/'

		elif [[ "${Inetd}" = "inetd" ]]
		then
		    awkprg='/^[[:space:]]*cvspserver[[:space:]]+/'

		else
		    error 10 -C "Unknown value for \${Inetd} = '${Inetd}'."
		fi
		if [[ ${_exit} -eq 0 ]]
		then
		    awkprg="${awkprg}"' {if ($0 ~ path) {exit 0} else {exit 1}}'
		    if awk -v path="--allow-root=${CVSdepot}" "${awkprg}" "${conf}"
		    then
			info 2 "The CVS pserver has been configured for usage via the INET-daemon."
		    else
			error -C 5 "The CVS pserver cannot been used via the INET-daemon."
		    fi
		fi
	    else
		error 5 -C "The INET-daemon '${Inetd}' has not been configured."
	    fi
	else
	    error 5 -C "The '${Inetd}' RPM package has not been installed."
	fi
	[[ ${_exit} -ne 0 ]] && error 5 -C "Cannot use the CVS pserver via the INET-daemon!"
    fi

    ####################################
    # Info user about how to get to the newly created CVS repository server
    if [[ ${_exit} -eq 0 ]]
    then
	info 1 "CVSROOT:\t\t:pserver:${CVSuser}@localhost:${CVSdepot}"
	info 1 "ALICE-URL:\tcvs://${CVSuser}@localhost${CVSdepot}?module=${ALICEmodule}"
    fi
fi # end opt_p

# End-Of-Script
# -------------------------------------------------------------------------------------------------
