ACC SHELL
#!/bin/bash
#
# Copyright (c) 2008 SUSE LINUX Products 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: Michael Calmer <mc@suse.de>
# Marius Tomaschewski <mt@suse.de>
#
#
# Exit codes
#
# 0 success
# 1 error
# 20 a module was not able to change the configuration
# because it was changed since the last run
#
unset POSIXLY_CORRECT ; set +o posix # we're using non-posix bash features
# The environment variable ROOT indicates the root of the system to be
# managed by SuSEconfig when that root is not '/'
r=$ROOT
. $r/etc/sysconfig/network/scripts/functions.netconfig
PROGNAME=${0##*/}
STATEDIR=$r/var/run/netconfig/
MODULEDIR=$r/etc/netconfig.d/
. $r/etc/sysconfig/network/config
INPUTFORMAT=dhcpcd
function modify() {
if [ "x$SERVICE" = "x" ]; then
warn "No service informations available"
unLock "$PROGNAME"
exit 1
fi
if test "x${LEASEFILE}" != x -a ! -r "${LEASEFILE}" ; then
echo "Unable to read specified input file '${LEASEFILE}'" >&2
exit 1
fi
local a b
IFS=. read a b < /proc/uptime
local CREATETIME=$a
local _INTERFACE='' _SERVICE=''
local -a KEYS VALS
if [ "$INPUTFORMAT" == "dhcpcd" ]; then
local key val err=0
# add CREATETIME at index 0 !!
KEYS=('CREATETIME' 'SERVICE')
VALS=("$CREATETIME" "$SERVICE")
while IFS== read -sr key val ; do
test -n "$key" || {
test -n "$val" || continue
err=$val ; break
}
case $key in
(CREATETIME) continue ;;
(SERVICE|INTERFACE)
eval "_$key='$val'"
if test "x${!key}" != x -a "x${!key}" != "x$val" ; then
echo "Input value $key conflicts with command line argument" >&1
exit 1
fi
;;
esac
for k in ${KEYS[@]} ; do
test "x$k" = "x$key" && continue 2
done
KEYS[${#KEYS[@]}]=$key
VALS[${#VALS[@]}]=$val
done < <(netconfig_kv_filter ${LEASEFILE:+"$LEASEFILE"})
if test $err -ne 0 ; then
echo "Invalid syntax in ${LEASEFILE:+${LEASEFILE}, }line $err" >&2
exit 1
fi
else
echo "Unsupported lease file / input format '$INPUTFORMAT'" >&2
exit 1
fi
# NetworkManager policy merged settings
if [ "x$SERVICE" = "xNetworkManager" -a \
"x$INTERFACE" = "x" ] ;
then
# NetworkManager policy merged config
CFGFILE="$STATEDIR/NetworkManager.netconfig"
# first get the createtime. we want to keep it.
get_variable CREATETIME "$CFGFILE"
KEYS[0]='CREATETIME'
VALS[0]="$CREATETIME"
# remove it also in modify.
# We want to create it with the same name again.
rm -f "$CFGFILE"
else
# use interface name from config if provided
if [ "x$INTERFACE" = "x" -a "x$_INTERFACE" != "x" ]; then
INTERFACE="$_INTERFACE"
fi
if [ "x$INTERFACE" = "x" ]; then
warn "No interface informations available"
unLock "$PROGNAME"
exit 1
fi
if [ ! -d "$STATEDIR/$INTERFACE" ]; then
mkdir -p "$STATEDIR/$INTERFACE" || {
warn "Can not create interface state dir"
unLock "$PROGNAME"
exit 1
}
fi
local CFGFILE=`find_cfgfile "$INTERFACE" "$SERVICE"`
if [ "x$CFGFILE" != "x" ]; then
# first get the createtime. we want to keep it.
get_variable CREATETIME "$CFGFILE"
KEYS[0]='CREATETIME'
VALS[0]="$CREATETIME"
# remove it also in modify.
# We want to create it with the same name again.
rm -f "$CFGFILE"
fi
if [ "x$CFGFILE" = "x" ]; then
local CFGNAME=`get_new_cfgname`
CFGFILE="$STATEDIR/$INTERFACE/$CFGNAME"
fi
fi
debug "write new STATE file $CFGFILE"
for((i=0; i<${#KEYS[@]} && i<${#VALS[@]}; i++)) ; do
printf "%s='%s'\n" "${KEYS[$i]}" "${VALS[$i]}"
done > "$CFGFILE"
return 0
}
function remove() {
if [ "x$SERVICE" = "x" ]; then
warn "No service informations available"
unLock "$PROGNAME"
exit 1
fi
# NetworkManager policy merged settings
if [ "x$SERVICE" = "xNetworkManager" -a \
"x$INTERFACE" = "x" ] ;
then
# NetworkManager policy merged config
CFGFILE="$STATEDIR/NetworkManager.netconfig"
else
if [ "x$INTERFACE" = "x" ]; then
warn "No interface informations available"
unLock "$PROGNAME"
exit 1
fi
CFGFILE=`find_cfgfile "$INTERFACE" "$SERVICE"`
fi
if [ "x$CFGFILE" != "x" ]; then
debug "remove STATE file $CFGFILE"
rm -f "$CFGFILE"
fi
return 0
}
#
# find_cfgfile <interface> <service>
#
function find_cfgfile() {
test -z "$1" && return 1
test -z "$2" && return 1
local INTERFACE=$1
local CUR_SERVICE=$2
for CFG in `ls -X $STATEDIR/$INTERFACE/`; do
get_variable "SERVICE" $STATEDIR/$INTERFACE/$CFG
if [ "$CUR_SERVICE" = "$SERVICE" ]; then
CFGNAME=$CFG
echo "$STATEDIR/$INTERFACE/$CFG"
return;
fi
done
return 0;
}
function get_new_cfgname() {
local CFGNAME=""
for CFG in `ls -X -r $STATEDIR/$INTERFACE/`; do
CNT=`echo "$CFG" | sed 's/netconfig//'`
((CNT++))
CFGNAME="netconfig$CNT"
break
done
if [ "x$CFGNAME" = "x" ]; then
CFGNAME="netconfig0"
fi
echo $CFGNAME;
}
function run_modules() {
local mfilter="$1"
local errors=()
local msg=""
local ret=0 err=0
if [ -n "$FORCE_REPLACE" ]; then
export FORCE_REPLACE=$FORCE_REPLACE
fi
if [ -n "$r" ]; then
export ROOT=$r
fi
debug "Module order: $NETCONFIG_MODULES_ORDER"
for plg in $NETCONFIG_MODULES_ORDER; do
case $plg in
-*)
debug "${plg:1} module is disabled" ;
continue
;;
esac
if [ ! -x "$MODULEDIR/$plg" ]; then
debug "$plg module is not executable"
continue
fi
case "x$mfilter" in
"x"|"x${plg}"|"x${plg%%-*}") ;;
*) debug "$plg module skipped" ;
continue ;;
esac
msg=`"$MODULEDIR/$plg" 2>&1` ; ret=$?
if [ "$ret" != "0" -a "$err" = "0" ]; then
err=$ret
fi
if [ "x$msg" != x ]; then
errors[${#errors[@]}]="$msg"
fi
done
if [ ${#errors[@]} -gt 0 ]; then
for msg in "${errors[@]}" ; do
echo "$msg"
done
fi
return $err
}
function usage ()
{
typeset -a MODULES
typeset -a MGROUPS
local grp g plg
for plg in $NETCONFIG_MODULES_ORDER; do
case $plg in
-*) continue ;;
esac
if [ ! -x "$MODULEDIR/$plg" ]; then
continue
fi
MODULES=(${MODULES[@]} $plg)
grp=${plg%%-*}
for g in ${MGROUPS[@]} ; do
test "x$grp" = "x$g" && continue 2
done
MGROUPS=(${MGROUPS[@]} $grp)
done
cat << EOT >&2
Usage:
$PROGNAME <global options>
$PROGNAME <action> <action options>
actions:
modify Requires an interface and service specific settings via STDIN
or as file using the --input-file or --lease-file option.
Already existing settings for this interface and service will
be replaced with the new one, otherwise netconfig creates a
new state file. Finaly, netconfig updates the managed files.
remove Removes the interface and service specific settings and
updates the managed files.
update Updates the managed files with the current set of settings.
modify options:
< -s|--service <service name> > service providing settings
[ -i|--interface <interface name> ] interface providing settings
[ -F|--input-format <input format> ] currently 'dhcpcd' supported only
[ -I|--input-file <file name> ] file name to read, stdin by default
[ -l|--lease-file <file name> ] alias for --input-file
[ -m|--module-only <name | prefix> ] module or module group updates only
[ -f|--force-replace ] generate files, even user modified
[ -v|--verbose ] enable debug and be verbose
remove options:
< -s|--service <service name> > service providing settings
[ -i|--interface <interface name> ] interface providing settings
[ -m|--module-only <name | prefix> ] module or module group updates only
[ -f|--force-replace ] generate files, even user modified
[ -v|--verbose ] enable debug and be verbose
update options:
[ -m|--module-only <name | prefix> ] module or module group updates only
[ -f|--force-replace ] generate files, even user modified
[ -v|--verbose ] enable debug and be verbose
global options:
<-h|--help> show this help text
Active modules: ${MODULES[@]}
Module groups : ${MGROUPS[@]}
EOT
if [ -n "$1" ] ; then
ERROR=" ERROR: "
echo -e "$*\n" | while read line; do
echo "${ERROR}${line}" >&2
ERROR=" "
done
fi
exit 1
}
COMMANDLINE="$@"
ACTION=""
SERVICE=""
INTERFACE=""
VARIABLE=""
MODFILTER=""
while true ; do
# Interface is checked against sysfs before the data is used;
# reject just the most the ugliest characters...
REGEX=''
VARIABLE=''
case "$1" in
-s|--service) VARIABLE=SERVICE ;
REGEX='^[[:alnum:]_-]+$' ;;
-i|--interface) VARIABLE=INTERFACE ;
REGEX='^[^'"'"'`"\\/[:space:][:cntrl:]]+$' ;;
-l|--lease-file) VARIABLE=LEASEFILE;;
-I|--input-file) VARIABLE=LEASEFILE;;
-F|--input-format) VARIABLE=INPUTFORMAT;;
-f|--force-replace) FORCE_REPLACE="true";;
-v|--verbose) VERBOSE="yes";;
-m|--module-only) VARIABLE=MODFILTER;;
-h|--help) usage;;
"") break ;;
--)
shift
test -n "$ACTION" -o $# -gt 1 \
&& usage "Exactly one action may be given.\n"\
"Currently given actions: $ACTION $*"
ACTION="$1"
shift
break
;;
*)
test -n "$ACTION" && usage "Exactly one action may be given.\n"\
"Currently given actions: $ACTION $1"
ACTION="$1"
;;
-*) usage Unknown option $1;;
esac
if [ -n "$VARIABLE" ] ; then
case $2 in
(""|-*) usage "Option $1 needs an argument" ;;
esac
if [ -n "$REGEX" ] ; then
if ! [[ ${1} =~ ${REGEX} ]] ; then
usage "Invalid character in value for option $VARIABLE"
fi
fi
eval $VARIABLE=\$2
shift
fi
shift
done
test -z "$ACTION" && usage No action was given
if [ "$VERBOSE" = "yes" ]; then
export VERBOSE=$VERBOSE
fi
#
# before we start, check for root
#
if test "$UID" != "0" -a "$USER" != root -a -z "$ROOT" ; then
echo "You must be root to start $0." >&2
exit 1
fi
# Check if we can write in /tmp
if ! touch $r/tmp &>/dev/null; then
echo "Filesystem read only: Cannot modify anything" >&2
exit 1
fi
# Set usefull default umask
umask 0022
# Create state directory
if ! mkdir -p "$STATEDIR" ; then
echo "Unable to create netconfig state directory '$STATEDIR'" >&2
exit 1
fi
#
# try to get the lock
#
openLockWait "$PROGNAME" 5
if [ "$?" != 0 ]; then
log "open lock for $PROGNAME failed. Abort."
exit 1;
fi
RET=0
ERR=0
case "$ACTION" in
modify)
modify ; RET=$?
if [ "$RET" != "0" -a "$ERR" = "0" ]; then ERR=$RET; fi
run_modules "$MODFILTER" ; RET=$?
if [ "$RET" != "0" -a "$ERR" = "0" ]; then ERR=$RET; fi
;;
update)
run_modules "$MODFILTER" ; RET=$?
if [ "$RET" != "0" -a "$ERR" = "0" ]; then ERR=$RET; fi
;;
remove)
remove ; RET=$?
if [ "$RET" != "0" -a "$ERR" = "0" ]; then ERR=$RET; fi
run_modules "$MODFILTER" ; RET=$?
if [ "$RET" != "0" -a "$ERR" = "0" ]; then ERR=$RET; fi
;;
*)
usage "Invalid action given.\n"
;;
esac
unLock "$PROGNAME"
exit $ERR
# vim: set ts=8 sts=4 sw=4 ai et:
ACC SHELL 2018