commit d5e15641fd19eeb8d984a99e4202ac25983447a8 Author: Rob Pearce Date: Sun Jul 11 10:20:48 2021 +1000 Initial checkin. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b54ee20 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Overview + +Script to manage custom./etc/services entries. + +# Usage + # Show usage + rpearce@crom:serv$ ./serv.sh -h + usage: ./serv.sh [OPTIONS] command [command_args] + + Manage custom /etc/services entries. + + Valid commands are: diff sync init add del query cat + + -f Force removal of service even if multiple match. + -w Update system services file (/etc/services) + -y Same as -w + + # Create working area in ~/.serv/ + rpearce@crom:serv$ ./serv.sh init + Initialisation of /Users/rpearce/.serv complete. + + # Add a custom service + rpearce@crom:serv$ ./serv.sh add myservice 34777/udp + Added 'myservice' (34777/udp) to custom services file. + + NOTE: /etc/services not touched - use -w to update it. + The following changes were NOT made: + 13921a13922,13924 + > + > # custom services from serv.sh + > myservice 34777/udp + + # Look up a custom service + rpearce@crom:serv$ ./serv.sh query myservice + Found in custom services file: + myservice 34777/udp + + # Update system /etc/services with custom services + rpearce@crom:serv$ ./serv.sh -w sync + + (using sudo - you may be prompted for your password) + Password: + /etc/services updated: + 13921a13922,13924 + > + > # custom services from serv.sh + > myservice 34777/udp + + rpearce@crom:serv$ tail -5 /etc/services + # 48557-49150 Unassigned + # 49151 IANA Reserved + + # custom services from serv.sh + myservice 34777/udp + rpearce@crom:serv$ + + # Remove a custom service, and also update /etc/services + rpearce@crom:serv$ ./serv.sh -w rm myservice + Removed 'myservice' from custom services file (1 line). + + (using sudo - you may be prompted for your password) + /etc/services updated: + 13924d13923 + < myservice 34777/udp + + rpearce@crom:serv$ tail -5 /etc/services + com-bardac-dw 48556/udp # com-bardac-dw + com-bardac-dw 48556/tcp # com-bardac-dw + # Nicholas J Howes + # 48557-49150 Unassigned + # 49151 IANA Reserved + rpearce@crom:serv$ diff --git a/serv.sh b/serv.sh new file mode 100755 index 0000000..e2a0e98 --- /dev/null +++ b/serv.sh @@ -0,0 +1,320 @@ +#!/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