Index: trunk/scripts/samhainrc_update.sh
===================================================================
--- trunk/scripts/samhainrc_update.sh	(revision 1)
+++ trunk/scripts/samhainrc_update.sh	(revision 29)
@@ -1,227 +1,297 @@
-#!/bin/bash
-# ----------------------------------------------------------------------------- 
-# @brief: update the kernel options in the samhain configuration
-#         file, after a new kernel has been compiled
-# @author: marc heisterkamp <marzheister@web.de>
-# ----------------------------------------------------------------------------- 
-
-SAMHAIN_CFG="/etc/samhainrc"
-
-BLUE="[34;01m"
-CYAN="[36;01m"
-GREEN="[32;01m"
-DARK_GREEN="[32m"
-RED="[31;01m"
-PURPLE="[35;01m"
-WHITE="[37;01m"
-DARK_GRAY="[30;01m"
-LIGHT_GRAY="[37m"
-YELLOW="[33;01m"
-BROWN="[33m"
-OFF="[0m"
-
-
-SYSTEM_MAP=""
-new_cfg=''
-scriptname="$0"
-
-# global variables for system adresses (extracted from System.map)
-SYS_CALL=''
-SYS_CALL_TABLE=''
-PROC_ROOT=''
-PROC_ROOT_IOPS=''
-PROC_ROOT_LOOKUP=''
-
-# Make sure the user has root permissions
-if [ $UID -ne 0 ] ; then
-  echo "You must be root to run this script. Exiting."
-  exit 1
+#! /bin/sh
+
+# -----------------------------------------------------------------------
+# The default configuration file
+# -----------------------------------------------------------------------
+
+cfgfile="/etc/samhainrc"
+
+# -----------------------------------------------------------------------
+# Be Bourne compatible
+# -----------------------------------------------------------------------
+
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
 fi
 
-
-#------------------------------------------------------------------------------
-# usage 
-#------------------------------------------------------------------------------
-function print_usage() {
-
-  cat >&2 <<EOHELP
-
-  update the samhainrc configuration file with new kernel system addresses
-  (i.e: after kernel compilation) by extracting these from the new System.map 
-  file
-
-  SYNOPSIS
-     $scriptname [ ${GREEN}--help${OFF} ]
-                           [ ${GREEN}--nocolor${OFF} ]
-                           [ ${GREEN}--print-only${OFF} ] <System.map>
-                           [ ${GREEN}--update${OFF} ]     <System.map>
-
-  OPTIONS
-     ${GREEN}-h${OFF} ${GREEN}--help${OFF}
-        Show help. 
-
-     ${GREEN}--nocolor${OFF}
-        Disable color hilighting for non ANSI-compatible terms.
-
-     ${GREEN}-p${OFF} ${GREEN}--print-only${OFF} <System.map>
-        Print the extracted system adresses and do not write them to the 
-        samhain configuration file. 
-
-     ${GREEN}-u${OFF} ${GREEN}--update${OFF} <System.map>
-        Update the samhainrc configuration file with new kernel system
-        addresses from the given System.map file 
-
-EOHELP
- exit 0
-}
-
-
-#------------------------------------------------------------------------------
-# parses the command line options 
-# param in: all parameters given to the script 
-#------------------------------------------------------------------------------
-function parse_cmd_line() {
-
-  # parse the command-line
-  while [ -n "$1" ]; do
-    case "$1" in
-      --help|-h) 
-        print_usage
-        ;;
-      --nocolor|-n)
-        unset DARK_GREEN GREEN RED BROWN LIGHT_GRAY WHITE OFF 
-        ;;
-      --print-only|-p)
-        shift
-        SYSTEM_MAP="$1"
-        get_system_addresses
-        print_system_addresses
-        break
-        ;;
-      --update|-u)
-        shift
-        SYSTEM_MAP="$1"
-        get_system_addresses
-        print_system_addresses
-        replace_system_addresses
-        ;;
-      -*)
-        echo "$scriptname: unknown option $1. Exiting" >&2
-        exit 1
-        ;;
-    esac
-    shift
-  done
-}
-
-
-#------------------------------------------------------------------------------
-# extract system adresses from given System.map file and save to global
-# variables 
-#------------------------------------------------------------------------------
-function get_system_addresses() { 
-
-  if [ -z "$SYSTEM_MAP" ] ; then
-    echo
-    echo "No System.map specified. Exiting" >&2
-    echo
-    exit 1
+programname="$0"
+sysmap=
+
+# -----------------------------------------------------------------------
+# Print help
+# -----------------------------------------------------------------------
+
+showhelp() {
+    echo
+    echo "$programname - update samhain config file after kernel update"
+    echo
+    echo "OPTIONS:"
+    echo
+    echo " -u|--update </path/to/System.map>"
+    echo "         Update the configuration file with new"
+    echo "         settings as taken from </path/to/System.map>"
+    echo
+    echo " -c|--config-file </path/to/config-file>"
+    echo "         Specify the configuration file to update [${cfgfile}]"
+    echo
+    echo " -p|--print-only </path/to/System.map>"
+    echo "         Print new settings, don't modify anything"
+    echo
+    echo " -h|--help"
+    echo "         Print this help"
+    echo
+    echo " -n|--nocolor"
+    echo "         (ignored, legacy support)"
+    echo
+}
+
+
+# -----------------------------------------------------------------------
+# Death strikes
+# -----------------------------------------------------------------------
+
+die() {
+    echo ${1+"$@"} >&2
+    { (exit 1); exit 1; }
+}
+
+# -----------------------------------------------------------------------
+# Get new settings from </path/to/System.map>
+# -----------------------------------------------------------------------
+
+system_call=
+syscall_table=
+proc_root=
+proc_root_inode_operations=
+proc_root_lookup=
+
+get_new_settings() {
+
+    if [ -z "$sysmap" ]; then
+	die "No System.map specified"
+    fi
+    if [ -f "$sysmap" ]; then
+	if [ -r "$sysmap" ]; then
+	    system_call=`egrep '[[:alnum:]]{8}[[:space:]]+[[:alpha:]]{1}[[:space:]]+system_call$' ${sysmap} | awk '{ print $1 }'`
+	    syscall_table=`egrep '[[:alnum:]]{8}[[:space:]]+[[:alpha:]]{1}[[:space:]]+sys_call_table$' ${sysmap} | awk '{ print $1 }'`
+	    proc_root=`egrep '[[:alnum:]]{8}[[:space:]]+[[:alpha:]]{1}[[:space:]]+proc_root$' ${sysmap} | awk '{ print $1 }'`
+	    proc_root_inode_operations=`egrep '[[:alnum:]]{8}[[:space:]]+[[:alpha:]]{1}[[:space:]]+proc_root_inode_operations$' ${sysmap} | awk '{ print $1 }'`
+	    proc_root_lookup=`egrep '[[:alnum:]]{8}[[:space:]]+[[:alpha:]]{1}[[:space:]]+proc_root_lookup$' ${sysmap} | awk '{ print $1 }'`
+	else
+	    die "System.map ${sysmap} not readable"
+	fi
+    else
+	die "System.map ${sysmap} not found"
+    fi
+    test -z "${system_call}" && die "system_call not found in ${cfgfile}"
+    test -z "${syscall_table}" && die "sys_call_table not found in ${cfgfile}"
+    test -z "${proc_root}" && die "proc_root not found in ${cfgfile}"
+    test -z "${proc_root_inode_operations}" && die "proc_root_inode_operations not found in ${cfgfile}"
+    test -z "${proc_root_lookup}" && die "proc_root_lookup not found in ${cfgfile}"
+
+}
+
+# -----------------------------------------------------------------------
+# Print new settings
+# -----------------------------------------------------------------------
+
+run_print() {
+    get_new_settings
+    echo
+    echo "KernelSystemCall =     0x${system_call}"
+    echo "KernelSyscallTable =   0x${syscall_table}"
+    echo "KernelProcRoot =       0x${proc_root}"
+    echo "KernelProcRootIops =   0x${proc_root_inode_operations}"
+    echo "KernelProcRootLookup = 0x${proc_root_lookup}"
+    echo
+}
+
+# -----------------------------------------------------------------------
+# Replace a setting
+# -----------------------------------------------------------------------
+
+# set ignorecase
+# search pattern
+# delete current line
+# insert
+# single dot == end of insert text
+# save and exit
+
+run_replace() {
+    item="$1"
+    address="$2"
+    ex -s "$cfgfile" <<EOF
+:set ic
+:/^[[:blank:]]*$1[[:blank:]]*=
+:d
+:i
+$item = $address
+.
+:x
+EOF
+}
+
+# -----------------------------------------------------------------------
+# Add a setting
+# -----------------------------------------------------------------------
+
+# set ignorecase
+# search pattern ([Kernel] section)
+# append (next line)
+# single dot == end of insert text
+# save and exit
+
+run_add() {
+    item="$1"
+    address="$2"
+    ex -s "$cfgfile" <<EOF
+:set ic
+:/^[[:space:]]*\[Kernel\]
+:a
+$item = $address
+.
+:x
+EOF
+}
+
+# -----------------------------------------------------------------------
+# Update with new settings
+# -----------------------------------------------------------------------
+
+run_update() {
+
+    get_new_settings
+
+    if [ -z "$cfgfile" ]; then
+	die "No configuration file specified"
+    fi
+    if [ ! -w "$cfgfile" ]; then
+	die "Configuration file ${cfgfile} not writeable"
+    fi
+    egrep '^[[:space:]]*\[Kernel\]' "$cfgfile" >/dev/null
+    if [ $? -ne 0 ]; then
+	die "No [Kernel] section in configuration file $cfgfile"
+    fi
+
+    cat "$cfgfile" | egrep -i 'KernelProcRootLookup' >/dev/null
+    if [ $? -eq 0 ]; then
+	run_replace 'KernelProcRootLookup' "0x${proc_root_lookup}"
+    else
+	run_add 'KernelProcRootLookup' "0x${proc_root_lookup}"
+    fi
+ 
+    cat "$cfgfile" | egrep -i 'KernelProcRootIops' >/dev/null
+    if [ $? -eq 0 ]; then
+	run_replace 'KernelProcRootIops' "0x${proc_root_inode_operations}"
+    else
+	run_add 'KernelProcRootIops' "0x${proc_root_inode_operations}"
+    fi
+
+    cat "$cfgfile" | egrep -i 'KernelProcRoot[[:space:]]*=' >/dev/null
+    if [ $? -eq 0 ]; then
+	run_replace 'KernelProcRoot' "0x${proc_root}"
+    else
+	run_add 'KernelProcRoot' "0x${proc_root}"
+    fi
+
+    cat "$cfgfile" | egrep -i 'KernelSyscallTable' >/dev/null
+    if [ $? -eq 0 ]; then
+	run_replace 'KernelSyscallTable' "0x${syscall_table}"
+    else
+	run_add 'KernelSyscallTable' "0x${syscall_table}"
+    fi
+
+    cat "$cfgfile" | egrep -i 'KernelSystemCall' >/dev/null
+    if [ $? -eq 0 ]; then
+	run_replace 'KernelSystemCall' "0x${system_call}"
+    else
+	run_add 'KernelSystemCall' "0x${system_call}"
+    fi
+
+}
+
+# -----------------------------------------------------------------------
+# Parse command line
+# -----------------------------------------------------------------------
+
+sysmap=
+action=
+
+for option
+do
+
+  # If the previous option needs an argument, assign it.
+  #
+  if test -n "$opt_prev"; then
+    eval "$opt_prev=\$option"
+    eval export "$opt_prev"
+    opt_prev=
+    continue
   fi
 
-  if [ ! -f "$SYSTEM_MAP" ] ; then
-    echo
-    echo "Could not find System.map: $SYSTEM_MAP. Exiting" >&2
-    echo
-    exit 1
-  fi
-
-  # 1. this is the address of system_call (grep system_call System.map) 
-  #    KernelSystemCall = 0xc0106cf8
-  SYS_CALL="0x`grep system_call $SYSTEM_MAP | cut -d' ' -f1`"
-
-  # 2. this is the address of sys_call_table (grep ' sys_call_table' System.map) 
-  #    KernelSyscallTable = 0xc01efb98
-  SYS_CALL_TABLE="0x`grep sys_call_table $SYSTEM_MAP | cut -d' ' -f1`"
-
-  # 3. this is the address of proc_root (grep ' proc_root$' System.map) 
-  #    KernelProcRoot = 0xc01efb98
-  PROC_ROOT="0x`grep ' proc_root$' $SYSTEM_MAP | cut -d' ' -f1`"
-
-  # 4. this is the address of proc_root_inode_operations 
-  #    (grep proc_root_inode_operations System.map) 
-  #    KernelProcRootIops = 0xc01efb98
-  PROC_ROOT_IOPS="0x`grep proc_root_inode_operations $SYSTEM_MAP | cut -d' ' -f1`"
-
-  # 5. this is the address of proc_root_lookup
-  #    (grep proc_root_lookup System.map) 
-  #    KernelProcRootLookup = 0xc01efb98
-  PROC_ROOT_LOOKUP="0x`grep proc_root_lookup $SYSTEM_MAP | cut -d' ' -f1`"
-}
-
-
-#------------------------------------------------------------------------------
-# extract system adresses from given System.map file and save to global
-# variables 
-#------------------------------------------------------------------------------
-function replace_system_addresses() { 
-
-  if [ -z "$SAMHAIN_CFG" ] ; then
-    echo "Could not find your samhainrc config file: $SAMHAIN_CFG. Exiting" >&2
-    exit 1
-  fi
-
-  echo
-  echo "Replacing current kernel system addresses in: $SAMHAIN_CFG"
-
-  # 1. replace current 'KernelSystemCall' setting
-  new_cfg=`sed -e "s/^\(KernelSystemCall[[:blank:]]*=\)[[:blank:]]*\(.*\)/\1 ${SYS_CALL}/" $SAMHAIN_CFG` 
-
-  # 2. replace current 'KernelSyscallTable' setting
-  new_cfg=`echo "$new_cfg" | sed -e "s/^\(KernelSyscallTable[[:blank:]]*=\)[[:blank:]]*\(.*\)/\1 ${SYS_CALL_TABLE}/"` 
-
-  # 3. replace current 'KernelProcRoot' setting
-  new_cfg=`echo "$new_cfg" | sed -e "s/^\(KernelProcRoot[[:blank:]]*=\)[[:blank:]]*\(.*\)/\1 ${PROC_ROOT}/"` 
-
-  # 4. replace current 'KernelProcRootIops' setting
-  new_cfg=`echo "$new_cfg" | sed -e "s/^\(KernelProcRootIops[[:blank:]]*=\)[[:blank:]]*\(.*\)/\1 ${PROC_ROOT_IOPS}/"` 
-
-  # 5. replace current 'KernelSystemCall' setting
-  new_cfg=`echo "$new_cfg" | sed -e "s/^\(KernelProcRootLookup[[:blank:]]*=\)[[:blank:]]*\(.*\)/\1 ${PROC_ROOT_LOOKUP}/"` 
-
-  echo "Backup old samhainrc $SAMHAIN_CFG to $SAMHAIN_CFG.bak"
-
-  # backup old samhainrc config file 
-  mv "$SAMHAIN_CFG" "$SAMHAIN_CFG.bak"
-
-  # write new samhainrc config file 
-  echo "$new_cfg" > "$SAMHAIN_CFG"
-
-  echo "Successfully updated kernel system addresses."
-  echo
-}
-
-
-#------------------------------------------------------------------------------
-# print samhain required system adresses 
-#------------------------------------------------------------------------------
-function print_system_addresses() {
-
-  echo 
-  echo "your kernel system addresses from: `basename $SYSTEM_MAP`"
-  echo 
-  echo "  KernelSystemCall     = $SYS_CALL"
-  echo "  KernelSyscallTable   = $SYS_CALL_TABLE"
-  echo "  KernelProcRoot       = $PROC_ROOT"
-  echo "  KernelProcRootIops   = $PROC_ROOT_IOPS"
-  echo "  KernelProcRootLookup = $PROC_ROOT_LOOKUP"
-  echo 
-
-}
-
-if [ $# -eq 0 ] ; then
-  print_usage
+  case "$option" in
+      -*=*) 
+	  optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` 
+	  ;;
+      *) 
+	  optarg= 
+	  ;;
+  esac
+
+  case "$option" in
+
+      -h|--help)
+	  showhelp
+	  exit 0
+	  ;;
+
+      -n|--nocolor)
+	  ;;
+
+      -c|--config-file)
+	  opt_prev=cfgfile
+	  ;;
+
+      -c=* | --config-file=*)
+	  cfgfile="$optarg"
+	  ;;
+
+      -p|--print-only)
+	  opt_prev=sysmap
+	  action=p
+	  ;;
+
+
+      -p=* | --print-only=*)
+	  sysmap="$optarg"
+	  action=p
+	  ;;
+    
+      -u|--update)
+	  opt_prev=sysmap
+	  action=u
+	  ;;
+
+      -u=* | --update=*)
+	  sysmap="$optarg"
+	  action=u
+	  ;;
+
+  esac
+
+done
+
+if [ x"$action" = xp ]; then
+    run_print
+    exit 0
 fi
-
-parse_cmd_line $*
-
-exit 0
+if [ x"$action" = xu ]; then
+    run_update
+    exit 0
+fi
+
+showhelp
+exit 1
