#!/bin/bash function lookup() { # lookup where_var res_var what to lookup ... local all what found rv localres where_var res_var where_var="$1" ; shift res_var="$1" ; shift all="${*}" what=${all// /|} found="" localres=$(egrep -i "$what" $SRVFILE_CUSTOM) if [[ ! -z $localres ]]; then found="custom" else localres=$(egrep -i "$what" $SRVFILE_BASE) [[ ! -z $localres ]] && found="system" fi eval "$where_var=${found}" eval "$res_var='${localres}'" if [[ -z $found ]]; then rv=1 else rv=0 fi return $rv echo "$found" } function usage() { echo "usage: $0 [OPTIONS] command [command_args]" echo echo "Manage custom /etc/services entries." echo echo " Valid commands are: $VALIDMODES" echo echo " -f Force removal of service even if multiple match." echo " -w Update system services file ($SRVFILE_ACTIVE)" echo " -y Same as -w" echo } function getalt() { local newmode newmode="$1" case $1 in a) newmode="add"; ;; q) newmode="query"; ;; rm) newmode="del"; ;; esac echo "$newmode" } function makeboth() { local n p both n="$1" p="$2" both="$n" if [[ ! -z $p ]]; then if [[ ! -z $n ]]; then both="$both (" fi both="$both$p" if [[ ! -z $n ]]; then both="$both)" fi fi echo "$both" } function stageforsync() { cp -fp "$SRVFILE_BASE" "$SRVFILE_STAGE" echo >> "$SRVFILE_STAGE" echo "# custom services from serv.sh" >> "$SRVFILE_STAGE" cat "$SRVFILE_CUSTOM" >> "$SRVFILE_STAGE" } function synctoactive() { local perm dres stageforsync dres=$(diff "$SRVFILE_ACTIVE" "$SRVFILE_STAGE") echo if [[ $DOIT -eq 1 ]]; then perm=$(stat -f "%p" "$SRVFILE_ACTIVE") if [[ $UID -ne 0 && $EUID -ne 0 ]]; then echo "(using sudo - you may be prompted for your password)" sudo cp -fp "$SRVFILE_ACTIVE" "$SRVFILE_ACTIVE.orig" sudo cp -fp "$SRVFILE_STAGE" "$SRVFILE_ACTIVE" sudo chmod $perm "$SRVFILE_ACTIVE" else cp -fp "$SRVFILE_ACTIVE" "$SRVFILE_ACTIVE.orig" cp -fp "$SRVFILE_STAGE" "$SRVFILE_ACTIVE" chmod $perm "$SRVFILE_ACTIVE" fi if [[ -z $dres ]]; then echo "No changes required to $SRVFILE_ACTIVE." else echo "$SRVFILE_ACTIVE updated:" echo "$dres" | sed -e 's/^/ /' fi else echo "NOTE: $SRVFILE_ACTIVE not touched - use -w to update it." echo "The following changes were NOT made:" if [[ -z $dres ]]; then echo " (none)" else echo "$dres" | sed -e 's/^/ /' fi fi } # Handle args VALIDPROTOS="tcp udp" VALIDMODES="diff sync init add del query cat" MODE="" SRVFILE_ACTIVE="/etc/services" SRVDIR="${HOME}/.serv" SRVFILE_BASE="${SRVDIR}/base" SRVFILE_CUSTOM="${SRVDIR}/custom" SRVFILE_STAGE="${SRVDIR}/stage" PROTOFILE=/etc/protocols FORCE=0 DOIT=0 ARGS="fhwy" while getopts "$ARGS" i; do case "$i" in f) FORCE=1 ;; h) usage; exit 1 ;; w) DOIT=1; ;; y) DOIT=1; ;; *) echo "ERROR: invalid argument: $i"; usage; ;; esac done shift $((OPTIND - 1)) if [[ $# -eq 0 ]]; then echo "Error - no mode provided." usage exit 1 fi MODE="$1" shift 1 MODE=$(getalt $MODE) if [[ $VALIDMODES != *$MODE* ]]; then echo "Error - invalid mode '$MODE'" echo "Valid modes are: $VALIDMODES" exit 1 fi if [[ $MODE == "cat" ]]; then echo "Current custom services (from $SRVFILE_CUSTOM):" cat "$SRVFILE_CUSTOM" | sed -e 's/^/ /' exit 0 elif [[ $MODE == "diff" ]]; then stageforsync dres=$(diff "$SRVFILE_ACTIVE" "$SRVFILE_STAGE") echo "Changes to be synced into $SRVFILE_ACTIVE:" if [[ -z $dres ]]; then echo " (none)" else echo "$dres" | sed -e 's/^/ /' fi exit 0 elif [[ $MODE == "sync" ]]; then # just update /etc/services synctoactive exit 0 elif [[ $MODE == "init" ]]; then wantargs=0 elif [[ $MODE == "add" ]]; then wantargs=2 else wantargs=1 fi if [[ $# -lt $wantargs ]]; then echo "Bad arguments - want $wantargs, got $#" echo usage exit 1 fi name="$1" protoport="$2" if [[ -z $protoport ]]; then proto="" port="" else if [[ $protoport != */* ]]; then echo "Error - bad service specification '$protoport'" echo "Must use the format: port/protocol" exit 1 fi proto="${protoport#*/}" port="${protoport%/*}" re='^[0-9]+$' if [[ ! $port =~ $re ]]; then echo "Error - port '$port' is not a number." echo "Must use the format: port/protocol" exit 1 fi if [[ $port -lt 0 || $port -gt 65535 ]]; then echo "Error - port '$port' must be in the range 0-65535." exit 1 fi if [[ $VALIDPROTOS != *$proto* ]]; then echo "Error - invalid protocol '$proto'." echo "Valid protocols are: $VALIDPROTOS" exit 1 fi shift 2 comment="$*" fi both=$(makeboth $name $protoport) if [[ $MODE == "init" ]]; then if [[ -d $SRVDIR ]]; then echo "error -i $SRVDIR already exists." exit 1 fi mkdir -p "$SRVDIR" cp -p "$SRVFILE_ACTIVE" "$SRVFILE_BASE" cp /dev/null "$SRVFILE_CUSTOM" echo "Initialisation of $SRVDIR complete." exit 0 elif [[ ! -d $SRVDIR ]]; then echo "Error - $SRVDIR is missing." echo "Use -i to initialise it." exit 1 fi if [[ $MODE == "query" ]]; then lookup whichfile res $name $protoport if [[ ! -z $whichfile ]]; then echo "Found in $whichfile services file:" echo "$res" | sed -e 's/^/ /' exit 0 else echo "'$both' not found." exit 1 fi elif [[ $MODE == "add" ]]; then lookup whichfile res $name $protoport if [[ ! -z $whichfile ]]; then echo "Error - this already exists in $whichfile services file:" echo "$res" | sed -e 's/^/ /' exit 1 fi if [[ ${#name} -gt 15 ]]; then echo "Error - service name '$name' exceeds 15 characters." exit 1 fi if [[ -z $comment ]]; then printf "%-16s%s\n" "$name" "$protoport" >> $SRVFILE_CUSTOM else printf "%-16s%s # %s\n" "$name" "$protoport" "$comment" >> $SRVFILE_CUSTOM fi echo "Added '$name' ($protoport) to custom services file." synctoactive elif [[ $MODE == "del" ]]; then lookup whichfile res $name $protoport if [[ $whichfile != "custom" ]]; then echo "Error - can't find service '$both' in custom file." exit 1 fi re="$name" if [[ ! -z $protoport ]]; then if [[ ! -z $re ]]; then re="$re|" fi re="$re${protoport}" fi re="${re/\//\\\/}" # escape the slash count=$(egrep -c "$re" $SRVFILE_CUSTOM) [[ $count -eq 1 ]] && ess="" || ess="s" if [[ $count -gt 1 && $FORCE -ne 1 ]]; then echo "Error: '$both' matches multiple lines for removal, use -f to confirm:" echo "$res" | sed -e 's/^/ /' exit 1 fi sed -ibak "/$re/d" $SRVFILE_CUSTOM sedrv=$? if [[ $sedrv -eq 0 ]]; then echo "Removed '$both' from custom services file ($count line$ess)." rv=0 else echo "Error failed to remove '$both' from custom services file using sed." rv=1 fi synctoactive exit $rv fi # TODO: sync to real serviceafile exit 0