#!/bin/sh # # $Id$ # # sc: ser control; tool for maintaining ser's databases # # History: # -------- # 2003-02-23 Thomas's start|stop commands checked in # 2004-07-27 rpid backported from unstable (andrei) # # To-DO: # ----- # - generalization for other than mysql databases # - front-end to updating administrative mysql password would # be a convenient thing to have # configuration for starting/stopping ser PID_FILE=/var/run/ser.pid SYSLOG=1 # 0=output to console, 1=output to syslog STARTOPTIONS= # for example -dddd # ser's FIFO server if [ -z "$SER_FIFO" ]; then SER_FIFO=/tmp/ser_fifo fi # period in which stats are reprinted if [ -z "$WATCH_PERIOD" ] ; then WATCH_PERIOD=2 fi # SQL config if [ -z "$SQL_DB" ] ; then SQL_DB=ser fi if [ -z "$SQL_HOST" ] ; then SQL_HOST=localhost fi if [ -z "$SQL_USER" ] ; then SQL_USER=ser fi # the read-only user for whom password may be stored here if [ -z "$RO_USER" ] ; then RO_USER=serro fi if [ -z "$RO_PW" ] ; then RO_PW=47serro11 fi # binaries GENHA1='gen_ha1' MYSQL='mysql' SER='sr' LAST_LINE='tail -1' # ACL name verification VERIFY_ACL=1 ACL_GROUPS="local ld int voicemail free-pstn" # expiration time for alias table FOREVER='2020-05-28 21:32:15' FOREVER_REL=1003741823 VERSION='$Revision$' #### SQL names # Usr Loc Table USRLOC=location USER_COLUMN=username CALLID_COLUMN=callid # subscriber table TABLE=subscriber REALM_COLUMN=domain HA1_COLUMN=HA1 HA1B_COLUMN=HA1B PASSWORD_COLUMN=password RPID_COLUMN=rpid SUBSCRIBER_COLUMN='username' EMAIL_COLUMN=email_address SUB_CREATED_COLUMN=datetime_created SUB_MODIFIED_COLUMN=datetime_modified PHP_LIB_COLUMN=phplib_id # acl table ACL_TABLE=grp ACL_USER_COLUMN=username ACL_GROUP_COLUMN=grp ACL_MODIFIED_COLUMN=last_modified ACL_DOMAIN_COLUMN=domain # aliases table A_TABLE=aliases A_USER_COLUMN=username A_CONTACT_COLUMN=contact A_EXPIRES_COLUMN=expires A_Q_COLUMN=q A_CALLID_COLUMN=callid A_CSEQ_COLUMN=cseq A_LAST_MODIFIED_COLUMN=last_modified FIFO_DBG=0 EGREP="egrep" #=================================================================== usage() { CMD=`basename $0` if [ "0$VERIFY_ACL" -eq 1 ] ; then EXTRA_TEXT="ACL privileges are: $ACL_GROUPS" fi cat < .. add a new subscriber (*) passwd ......... change user's password (*) rm ...................... delete a user (*) mail .................... send an email to a user alias show [] ............... show aliases alias rm ................... remove an alias alias add ............ add an aliases rpid add ......... add rpid for a user (*) rpid rm ................. set rpid to NULL for a user (*) rpid show ............... show rpid of a user * access control lists * acl show [] .............. show user membership acl grant ....... grant user membership (*) acl revoke [] .... grant user membership(s) (*) * usrloc * ul show []................ show in-RAM online users ul rm ................... delete user's UsrLoc entries ul add ............ introduce a permanent UrLoc entry showdb [] ................ show online users flushed in DB * control and diagnostics * moni ... show internal status start .... start ser ps ..... show runnig processes stop ..... stop ser fifo ... send raw FIFO commands restart .. restart ser ping .. ping a URI (OPTIONS) cisco_restart .. restart a Cisco phone (NOTIFY) Commands labeled with (*) will prompt for a MySQL password. If the variable PW is set, the password will not be prompted. $EXTRA_TEXT EOF } # check the parameter if it is a valid SIP URI # quite simplified now -- it captures just very basic # errors check_uri() { echo "$1" | $EGREP "^sip(s)?:([a-zA-Z0-9_]+@)?.*\..*" } #params: none # output: PW prompt_pw() { if [ -z "$PW" ] ; then savetty=`stty -g` printf "MySql password: " stty -echo read PW stty $savetty echo fi } print_status() { echo $1 | grep "^[1-6][0-9][0-9]" > /dev/null if [ "$?" -eq 0 ] ; then echo $1 else echo "200 OK" fi } # process output from FIFO server; if everything is ok # skip the first "ok" line and proceed to returned # parameters filter_fl() { # tail +2 awk 'BEGIN {line=0;IGNORECASE=1;} {line++} line==1 && /^200 ok/ { next } { print }' } fifo_cmd() { if [ "0${FIFO_DBG}" -eq 1 ] ; then echo "entering fifo_cmd $*" fi if [ "$#" -lt 1 ]; then echo "ERROR: fifo_cmd must take at least command name as parameter" exit fi name=ser_receiver_$$ path=/tmp/$name if [ ! -w $SER_FIFO ]; then echo "Error opening ser's FIFO $SER_FIFO" echo "Make sure you have line fifo=$SER_FIFO in your config" exit 1 fi mkfifo $path if [ $? -ne 0 ] ; then echo "error opening read fifo $path" exit 1 fi chmod a+w $path # construct the command now CMD=":$1:$name\n"; shift while [ -n "$1" ] ; do CMD="${CMD}${1}\n" shift done CMD="${CMD}\n" trap "rm -f $path; kill 0" 2 # start reader now so that it is ready for replies # immediately after a request was sent out cat < $path | filter_fl & # issue FIFO request (printf taken to deal with \n) printf "$CMD" > $SER_FIFO # wait for the reader to complete wait rm $path if [ "0${FIFO_DBG}" -eq 1 ] ; then printf "FIFO command was:\n$CMD" fi } # $1 = name $2=path $3=attempt print_stats() { echo "[cycle #: $3; if constant make sure server lives and fifo is on]" cat < $2 | filter_fl & cat > $SER_FIFO < $SER_FIFO << EOF :uptime:$1 EOF wait echo echo Transaction Statistics cat < $2 | filter_fl & cat > $SER_FIFO < $SER_FIFO < $SER_FIFO <&2 stty $savetty echo >&2 fi $MYSQL $2 -h $SQL_HOST -u $SQL_USER "-p$PW" -e "$1 ;" $SQL_DB } # input: sql query, optional mysql command-line params sql_ro_query() { $MYSQL $2 -h $SQL_HOST -u $RO_USER "-p$RO_PW" \ -e "$1 ;" $SQL_DB } usrloc() { if [ "$#" -lt 2 ] ; then echo "usrloc: too few parameters" exit 1 fi if [ "$1" = "alias" ] ; then UL_TABLE="$A_TABLE" elif [ "$1" = "ul" ] ; then UL_TABLE="$USRLOC" else echo "usrloc: unknown table name" exit 1 fi shift case $1 in show) if [ $# -eq 2 ] ; then fifo_cmd ul_show_contact $UL_TABLE $2 elif [ $# -eq 1 ] ; then printf "Dumping all contacts may take long: are you sure you want to proceed? [Y|N] " read answer if [ "$answer" = "y" -o "$answer" = "Y" ] ; then fifo_cmd ul_dump fi else echo "wrong number of params for usrloc show" usage exit 1 fi exit $? ;; add) if [ $# -ne 3 ] ; then usage exit 1 fi shift check_uri "$2" if [ "$?" -ne "0" ] ; then echo "$2 is not a valid URI" exit 1 fi fifo_cmd ul_add "$UL_TABLE" "$1" "$2" "$FOREVER_REL" "1.00" "0" exit $? ;; rm) if [ $# -ne 2 ] ; then usage exit 1 fi shift fifo_cmd ul_rm $UL_TABLE $1 ;; *) usage exit 1 ;; esac } rpid() { if [ "$#" -lt 2 ] ; then echo "rpid: too few parameters" exit 1 fi shift; case $1 in show) if [ $# -eq 2 ] ; then is_user $2 if [ $? -ne 0 ] ; then echo non-existent user exit 1; fi CLAUSE=" WHERE $SUBSCRIBER_COLUMN='$2' AND $REALM_COLUMN='$SIP_DOMAIN' " elif [ $# -ne 1 ] ; then usage exit 1 fi QUERY="select $SUBSCRIBER_COLUMN, $RPID_COLUMN FROM $TABLE $CLAUSE ; " sql_ro_query "$QUERY" ;; add|rm) MODE=$1; if [ "$MODE" == "add" ] ; then ARG_NUM=3; else ARG_NUM=2; fi if [ $# -lt $ARG_NUM ] ; then usage exit 1 fi prompt_pw is_user $2 if [ $? -ne 0 ] ; then echo non-existent user exit 1 fi SIP_USER="$2" shift 2 if [ "$MODE" = "add" ] ; then RPID_VAL="'$1'"; else RPID_VAL=NULL; fi QUERY="UPDATE $TABLE \ SET $RPID_COLUMN=$RPID_VAL \ WHERE $SUBSCRIBER_COLUMN='$SIP_USER' AND $REALM_COLUMN='$SIP_DOMAIN';" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "SQL Error" exit 1 fi $0 rpid show $SIP_USER ;; *) usage exit 1 ;; esac } acl() { case $1 in show) if [ $# -eq 2 ] ; then is_user $2 if [ $? -ne 0 ] ; then echo non-existent user exit 1; fi CLAUSE=" WHERE $ACL_USER_COLUMN='$2' " elif [ $# -ne 1 ] ; then usage exit 1 fi QUERY="select * FROM $ACL_TABLE $CLAUSE ; " sql_ro_query "$QUERY" ;; grant) if [ $# -lt 3 ] ; then usage exit 1 fi prompt_pw is_user $2 if [ $? -ne 0 ] ; then echo non-existent user exit 1 fi SIP_USER="$2" shift 2 while [ $# -gt 0 ] ; do if [ $VERIFY_ACL -eq 1 ] ; then found=0 for i in $ACL_GROUPS ; do if [ "$1" = "$i" ] ; then found=1 break fi done if [ $found -eq 0 ] ; then echo "Invalid privilege: $1 ignored" shift continue fi fi QUERY="insert into $ACL_TABLE \ ($ACL_USER_COLUMN,$ACL_GROUP_COLUMN,$ACL_MODIFIED_COLUMN, $ACL_DOMAIN_COLUMN ) \ values ('$SIP_USER','$1', now(), '$SIP_DOMAIN' );" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "SQL Error" exit 1 fi shift done $0 acl show $SIP_USER ;; revoke) if [ $# -eq 3 ] ; then CLAUSE=" and $ACL_GROUP_COLUMN='$3' " elif [ $# -ne 2 ] ; then usage exit 1 fi QUERY="delete from $ACL_TABLE where \ $ACL_TABLE.$ACL_USER_COLUMN='$2' $CLAUSE" sql_query "$QUERY" $0 acl show $2 ;; *) usage exit 1 ;; esac } # params: user # output: false if exists, true otherwise is_user() { QUERY="select count(*) from $TABLE \ where $SUBSCRIBER_COLUMN='$1' and $REALM_COLUMN='$SIP_DOMAIN';" CNT=`sql_ro_query "$QUERY" | grep -v ERROR | $LAST_LINE` if [ "0$CNT" -eq 0 ] ; then false else true fi } # params: user, password # output: HA1, HA1B credentials() { HA1=`$GENHA1 $1 $SIP_DOMAIN $2` if [ $? -ne 0 ] ; then echo "HA1 calculation failed" exit 1 fi HA1B=`$GENHA1 "$1@$SIP_DOMAIN" $SIP_DOMAIN $2` if [ $? -ne 0 ] ; then echo "HA1B calculation failed" exit 1 fi } #================================================================ if [ -z "$SIP_DOMAIN" ] ; then echo echo "You need to set environment variable SIP_DOMAIN (e.g. to 'foobar.com') first" echo # This confuses new users cause its easy to miss the information above # usage exit 1 fi # if the script calls itself ... export PW case $1 in start) DIR=`dirname $0` echo printf "Starting SER : " if [ -r $PID_FILE ] ; then echo "PID file exists! ($PID_FILE) already running?" exit 1 else if [ $SYSLOG = 1 ] ; then $DIR/ser -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null else $DIR/ser -P $PID_FILE -E $STARTOPTIONS fi sleep 1 echo "started pid(`cat $PID_FILE`)" fi exit 0 ;; stop) printf "Stopping SER : " if [ -r $PID_FILE ] ; then kill `cat $PID_FILE` echo "stopped" else echo No PID file found! exit 1 fi exit 0 ;; restart) DIR=`dirname $0` printf "Stopping SER : " if [ -r $PID_FILE ] ; then kill `cat $PID_FILE` echo "stopped" else echo No PID file found! SER problably not running exit 1 fi sleep 2 printf "Starting SER : " if [ -r $PID_FILE ] ; then echo "PID file exists! ($PID_FILE) already running?" exit 1 else if [ $SYSLOG = 1 ] ; then $DIR/ser -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null else $DIR/ser -P $PID_FILE -E $STARTOPTIONS fi sleep 1 echo "started pid(`cat $PID_FILE`)" fi exit 0 ;; passwd) if [ $# -ne 3 ] ; then usage exit 1 fi shift credentials $1 $2 prompt_pw is_user $1 if [ $? -ne 0 ] ; then echo non-existent user exit 1 fi QUERY="update $TABLE \ set $HA1_COLUMN='$HA1', $HA1B_COLUMN='$HA1B', $PASSWORD_COLUMN='$2' \ , $SUB_MODIFIED_COLUMN=now() \ WHERE $SUBSCRIBER_COLUMN='$1' and $REALM_COLUMN='$SIP_DOMAIN';" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "password change failed" else echo "password change succeeded" fi ;; add) if [ $# -ne 4 ] ; then usage exit 1 fi shift credentials $1 $2 prompt_pw is_user $1 if [ $? -eq 0 ] ; then echo user already exists exit 1 fi QUERY="insert into $TABLE \ ($SUBSCRIBER_COLUMN,$REALM_COLUMN,$HA1_COLUMN,\ $HA1B_COLUMN,$PASSWORD_COLUMN,$EMAIL_COLUMN, $SUB_CREATED_COLUMN, \ $PHP_LIB_COLUMN ) \ values ('$1','$SIP_DOMAIN','$HA1','$HA1B','$2', '$3', now(), '$HA1' );"; sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "introducing a new user to the database failed" else echo "new user added" fi ;; monitor|console|moni|con) name=ser_receiver_$$ path=/tmp/$name if [ ! -w $SER_FIFO ]; then echo "Error opening ser's FIFO $SER_FIFO" echo "Make sure you have line fifo=$SER_FIFO in your config" exit 1 fi mkfifo $path if [ $? -ne 0 ] ; then echo "error opening read fifo $path" exit 1 fi trap "rm $path; clear; echo sc monitor ^C-ed; exit 1" 2 attempt=0 clear while [ 1 -eq 1 ]; do attempt=`expr $attempt + 1` #clear tput cup 0 0 print_stats $name $path $attempt sleep $WATCH_PERIOD done rm $path exit 0 ;; mail) if [ $# -ne 2 ] ; then usage exit 1 fi shift QUERY="select $TABLE.$EMAIL_COLUMN from $TABLE where \ $TABLE.$SUBSCRIBER_COLUMN='$1'" EA=`sql_ro_query "$QUERY" "-B" | grep -v ERROR | $LAST_LINE` if [ $? -ne 0 ] ; then echo "MySql query failed" exit 1 fi echo "Write email to $1: $EA now ..." mail -s "Message from $SIP_DOMAIN SIP admin" $EA if [ $? -eq 0 ] ; then echo message sent else echo sending message failed fi ;; alias|ul) usrloc "$@" ;; rpid) rpid "$@" ;; online) fifo_cmd ul_dump |grep aor| awk '{print $3}' | sort | sort -mu exit $? ;; showdb|userdb) if [ $# -eq 2 ] ; then is_user $2 if [ $? -ne 0 ] ; then echo non-existent user exit 1; fi elif [ $# -ne 1 ] ; then usage exit 1 fi shift QUERY1="select $TABLE.$EMAIL_COLUMN from $TABLE where \ $TABLE.$SUBSCRIBER_COLUMN='$1'" QUERY2="select $USRLOC.* from $USRLOC where \ $USRLOC.$USER_COLUMN='$1' order by expires desc" QUERY3="select $USRLOC.$USER_COLUMN, $TABLE.$EMAIL_COLUMN, $USRLOC.$CALLID_COLUMN \ from $TABLE, $USRLOC where \ $TABLE.$SUBSCRIBER_COLUMN=$USRLOC.$USER_COLUMN order by $USRLOC.$USER_COLUMN" if [ $# -eq 1 ] ; then sql_ro_query "$QUERY1" sql_ro_query "$QUERY2" else sql_ro_query "$QUERY3" fi echo "Note: Due to usage of cache, server's list " \ "may differ from DB list." ;; rm) if [ $# -ne 2 ] ; then usage exit 1 fi shift prompt_pw is_user $1 if [ $? -ne 0 ] ; then echo non-existent user exit 1 fi # begin with remove all user's privileges $0 acl revoke $1 > /dev/null 2>&1 # destroy the user now QUERY="delete from $TABLE where $TABLE.$SUBSCRIBER_COLUMN='$1'" sql_query "$QUERY" # and also all his contacts $0 ul rm $1 > /dev/null 2>&1 ;; ps) fifo_cmd ps ;; acl) shift acl "$@" ;; fifo) shift fifo_cmd "$@" ;; ping) # error handling is hacked -- filter_fl should not # consume positive status -- that should be done by # calling app if [ "$#" -ne 2 ] ; then usage exit 1 fi RET=`fifo_cmd t_uac_dlg OPTIONS "$2" "." \ "From: sip:daemon@$SIP_DOMAIN" \ "To: <$2>" "Contact: " "." "." \ | head -1 ` print_status $RET ;; cisco_restart) if [ "$#" -ne 2 ] ; then usage exit 1 fi RET=`fifo_cmd t_uac_dlg NOTIFY "$2" "." \ "From: sip:daemon@$SIP_DOMAIN" \ "To: <$2>" "Event: check-sync" \ "Contact: " "." "." | head -1 ` print_status $RET ;; version) echo "$0 $VERSION" ;; *) usage exit 1 ;; esac