#!/bin/sh # # $Id: sc_unixsock,v 1.1 2004/03/09 18:32:58 janakj Exp $ # # sc: ser control; tool for maintaining ser's databases using UNIX sockets # # History: # ------- # 2004-03-09 Derived from the original FIFO version (janakj) # # 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 DIR=`dirname $0` SERBIN=$DIR/ser # ser's UNIX domain socket if [ -z "$SER_UNIXSOCK" ]; then SER_UNIXSOCK=/tmp/ser.sock 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' SERUNIX='serunix' MYSQL='mysql' SER='sr' LAST_LINE='tail -1' # ACL name verification VERIFY_ACL=1 ACL_GROUPS="local ld int voicemail free-pstn prepaid" VERSION='$Revision: 1.1 $' #### SQL names # Usr Loc Table if [ -z "$UL_TABLE" ] ; then UL_TABLE=location fi USER_COLUMN=username CALLID_COLUMN=callid # subscriber table if [ -z "$SUB_TABLE" ] ; then SUB_TABLE=subscriber fi 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 if [ -z "$ACL_TABLE" ] ; then ACL_TABLE=grp fi ACL_USER_COLUMN=username ACL_DOMAIN_COLUMN=domain ACL_GROUP_COLUMN=grp ACL_MODIFIED_COLUMN=last_modified ACL_DOMAIN_COLUMN=domain # aliases table if [ -z "$ALS_TABLE" ] ; then ALS_TABLE=aliases fi 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 # domain table DOMAIN_TABLE=domain # URI table if [ -z "$URI_TABLE" ] ; then URI_TABLE=uri fi URIUSER_COLUMN=uri_user MODIFIED_COLUMN=last_modified UNIXSOCK_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 * domains * domain show ........................ show list of served domains domain add ............ add a new served domain domain rm ............. remove a served domain * control and diagnostics * moni ... show internal status start .... start ser ps ..... show runnig processes stop ..... stop ser unixsock ... send raw unixsock 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 } # determine host name, typically for use in printing UAC # messages; we use today a simplistic but portable uname -n way -- # no domain name is displayed ; unixsock_uac expands !! to host # address only for optional header fields; uname output without # domain is sufficient for informational header fields such as # From # get_my_host() { uname -n } # calculate name and domain of current user set_user() { SERUSER=`echo $1|awk -F @ '{print $1}'` SERDOMAIN=`echo $1|awk -F @ '{print $2}'` if [ -z "$SERDOMAIN" ] ; then SERDOMAIN="$SIP_DOMAIN" fi if [ -z "$SERDOMAIN" ] ; then echo "domain unknown: use usernames with domain or set default domain in SIP_DOMAIN" exit 1 fi } # 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_]+@)?.*\..*" } # check for alias duplicates check_alias() { unixsock_cmd ul_show_contact "$ALS_TABLE" "$1" | grep 404 > /dev/null if [ $? -ne 0 ]; then echo error: overlap with an existing alias exit 1 fi } #params: none # output: PW prompt_pw() { if [ -z "$PW" ] ; then savetty=`stty -g` printf "MySql password: " > /dev/stderr 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 unixsock 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/ { next } { print }' } unixsock_cmd() { if [ "0${UNIXSOCK_DBG}" -eq 1 ] ; then echo "entering unixsock_cmd $*" fi if [ "$#" -lt 1 ]; then echo "ERROR: unixsock_cmd must take at least command name as parameter" exit fi # construct the command now CMD=":$1:\n"; shift while [ -n "$1" ] ; do CMD="${CMD}${1}\n" shift done CMD="${CMD}\n" printf "$CMD" | $SERUNIX $SER_UNIXSOCK | filter_fl if [ "0${UNIXSOCK_DBG}" -eq 1 ] ; then printf "UNIXSOCK command was:\n$CMD" fi } # $1=attempt number print_stats() { echo "[cycle #: $1; if constant make sure server lives]" unixsock_cmd version unixsock_cmd uptime echo Transaction Statistics unixsock_cmd t_stats echo Stateless Server Statistics unixsock_cmd sl_stats echo UsrLoc Stats unixsock_cmd ul_stats } # input: sql query, optional mysql command-line params sql_query() { # if password not yet queried, query it now if [ -z "$PW" ] ; then savetty=`stty -g` printf "MySql password: " > /dev/stderr stty -echo read PW >&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 USRLOC_TABLE="$ALS_TABLE" CHECK_SUB=1 elif [ "$1" = "ul" ] ; then USRLOC_TABLE="$UL_TABLE" CHECK_SUB=0 else echo "usrloc: unknown table name" exit 1 fi shift case $1 in show) if [ $# -eq 2 ] ; then set_user $2 unixsock_cmd ul_show_contact $USRLOC_TABLE "$SERUSER@$SERDOMAIN" elif [ $# -eq 1 ] ; then printf "Dumping all contacts may take long: are you sure you want to proceed? [Y|N] " > /dev/stderr read answer if [ "$answer" = "y" -o "$answer" = "Y" ] ; then unixsock_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" set_user $1 if [ "$?" -ne "0" ] ; then echo "$2 is not a valid URI" exit 1 fi if [ "$CHECK_SUB" -ne 0 ] ; then is_user if [ $? -eq 0 ] ; then echo overlap of alias with an existing subscriber name exit 1; fi fi check_alias "$SERUSER@$SERDOMAIN" # 128 means FL_PERSISTENT is on unixsock_cmd ul_add "$USRLOC_TABLE" "$SERUSER@$SERDOMAIN" "$2" "0" "1.00" "0" "128" exit $? ;; rm) if [ $# -ne 2 ] ; then usage exit 1 fi shift set_user $1 unixsock_cmd ul_rm $USRLOC_TABLE "$SERUSER@$SERDOMAIN" ;; *) 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 set_user $2 is_user if [ $? -ne 0 ] ; then echo non-existent user exit 1; fi CLAUSE=" WHERE $SUBSCRIBER_COLUMN='$SERUSER' AND $REALM_COLUMN='$SERDOMAIN' " elif [ $# -ne 1 ] ; then usage exit 1 fi QUERY="select $SUBSCRIBER_COLUMN, $RPID_COLUMN FROM $SUB_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 set_user $2 is_user if [ $? -ne 0 ] ; then echo non-existent user exit 1 fi shift 2 if [ "$MODE" = "add" ] ; then RPID_VAL="'$1'"; else RPID_VAL=NULL; fi QUERY="UPDATE $SUB_TABLE \ SET $RPID_COLUMN=$RPID_VAL \ WHERE $SUBSCRIBER_COLUMN='$SERUSER' AND $REALM_COLUMN='$SERDOMAIN';" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "SQL Error" exit 1 fi $0 rpid show $SERUSER@$SERDOMAIN ;; *) usage exit 1 ;; esac } domain() { case $1 in show) # QUERY="select * FROM $DOMAIN_TABLE ; " # sql_ro_query "$QUERY" unixsock_cmd domain_dump ;; add) shift if [ $# -ne 1 ] ; then echo missing domain to be added exit 1 fi prompt_pw QUERY="insert into $DOMAIN_TABLE (domain) VALUES ('$1');" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "SQL Error" exit 1 fi unixsock_cmd domain_reload ;; rm) shift if [ $# -ne 1 ] ; then echo missing domain to be removed exit 1 fi prompt_pw QUERY="delete from $DOMAIN_TABLE where domain='$1';" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "SQL Error" exit 1 fi unixsock_cmd domain_reload ;; *) usage exit 1 esac } acl() { case $1 in show) if [ $# -eq 2 ] ; then set_user $2 is_user if [ $? -ne 0 ] ; then echo non-existent user exit 1; fi CLAUSE=" WHERE $ACL_USER_COLUMN='$SERUSER' AND $ACL_DOMAIN_COLUMN='$SERDOMAIN' " 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 set_user $2 is_user if [ $? -ne 0 ] ; then echo non-existent user exit 1 fi 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 ('$SERUSER','$1', now(), '$SERDOMAIN' );" sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "SQL Error" exit 1 fi shift done $0 acl show $SERUSER@$SERDOMAIN ;; revoke) if [ $# -eq 3 ] ; then CLAUSE=" and $ACL_GROUP_COLUMN='$3' " elif [ $# -ne 2 ] ; then usage exit 1 fi set_user $2 QUERY="delete from $ACL_TABLE where \ $ACL_TABLE.$ACL_USER_COLUMN='$SERUSER' AND $ACL_DOMAIN_COLUMN='$SERDOMAIN' $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 $SUB_TABLE \ where $SUBSCRIBER_COLUMN='$SERUSER' and $REALM_COLUMN='$SERDOMAIN';" 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() { set_user $1 HA1=`$GENHA1 $SERUSER $SERDOMAIN $2` if [ $? -ne 0 ] ; then echo "HA1 calculation failed" exit 1 fi HA1B=`$GENHA1 "$SERUSER@$SERDOMAIN" $SERDOMAIN $2` if [ $? -ne 0 ] ; then echo "HA1B calculation failed" exit 1 fi } #================================================================ # if the script calls itself ... export PW case $1 in start) echo printf "Starting SER : " if [ -r $PID_FILE ] ; then echo "PID file exists! ($PID_FILE) already running?" exit 1 else if [ ! -x "$SERBIN" ] ; then echo "SER binaries not found at $SERBIN; reconfigure SERBIN in $0" exit 1 fi if [ $SYSLOG = 1 ] ; then $SERBIN -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null else $SERBIN -P $PID_FILE -E $STARTOPTIONS fi sleep 1 if [ ! -s $PID_FILE ] ; then echo "PID file $PID_FILE does not exist -- SER start failed" exit 1 fi 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! SER probably not running exit 1 fi exit 0 ;; restart) $0 stop if [ "$?" -ne 0 ] ; then exit 1 fi sleep 2 $0 start 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 $SUB_TABLE \ set $HA1_COLUMN='$HA1', $HA1B_COLUMN='$HA1B', $PASSWORD_COLUMN='$2' \ , $SUB_MODIFIED_COLUMN=now() \ WHERE $SUBSCRIBER_COLUMN='$SERUSER' and $REALM_COLUMN='$SERDOMAIN';" 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 check_alias "$SERUSER@$SERDOMAIN" QUERY="insert into $SUB_TABLE \ ($SUBSCRIBER_COLUMN,$REALM_COLUMN,$HA1_COLUMN,\ $HA1B_COLUMN,$PASSWORD_COLUMN,$EMAIL_COLUMN, $SUB_CREATED_COLUMN, \ $PHP_LIB_COLUMN ) \ values ('$SERUSER','$SERDOMAIN','$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 QUERY="insert into $URI_TABLE \ ($SUBSCRIBER_COLUMN,$REALM_COLUMN,$URIUSER_COLUMN,$MODIFIED_COLUMN) \ values ('$SERUSER','$SERDOMAIN','$SERUSER',now());"; sql_query "$QUERY" if [ $? -ne 0 ] ; then echo "introducing a new user into uri table failed" else echo "new user into uri table added" fi ;; monitor|console|moni|con) attempt=0 if [ "$2" == "" ]; then loops=-1; else loops=$2; fi clear while [ $loops -ne $attempt ] ; do attempt=$(($attempt + 1)) #clear tput cup 0 0 print_stats $attempt if [ $loops -ne $attempt ] ; then sleep $WATCH_PERIOD fi done exit 0 ;; mail) if [ $# -ne 2 ] ; then usage exit 1 fi shift set_user $1 QUERY="select $SUB_TABLE.$EMAIL_COLUMN from $SUB_TABLE where \ $SUB_TABLE.$SUBSCRIBER_COLUMN='$SERUSER' and $SUB_TABLE.$REALM_COLUMN='$SERDOMAIN'" 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 $SERDOMAIN SIP admin" $EA if [ $? -eq 0 ] ; then echo message sent else echo sending message failed fi ;; alias|ul) usrloc "$@" ;; rpid) rpid "$@" ;; online) unixsock_cmd ul_dump |grep aor| awk '{print $3}' | sort | sort -mu exit $? ;; showdb|userdb) if [ $# -ne 2 ] ; then usage exit 1 fi shift set_user $1 is_user if [ $? -ne 0 ] ; then echo non-existent user exit 1; fi QUERY1="select $SUB_TABLE.$EMAIL_COLUMN from $SUB_TABLE where \ $SUB_TABLE.$SUBSCRIBER_COLUMN='$SERUSER' and $SUB_TABLE.$REALM_COLUMN='$SERDOMAIN' " QUERY2="select $UL_TABLE.* from $UL_TABLE where \ $UL_TABLE.$USER_COLUMN='$1' order by expires desc" QUERY3="select $UL_TABLE.$USER_COLUMN, $SUB_TABLE.$EMAIL_COLUMN, $UL_TABLE.$CALLID_COLUMN \ from $SUB_TABLE, $UL_TABLE where \ $SUB_TABLE.$SUBSCRIBER_COLUMN=$UL_TABLE.$USER_COLUMN order by $UL_TABLE.$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 set_user $1 is_user 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 QUERY="delete from $URI_TABLE where $SUBSCRIBER_COLUMN='$SERUSER'" sql_query "$QUERY" # destroy the user now QUERY="delete from $SUB_TABLE where $SUB_TABLE.$SUBSCRIBER_COLUMN='$SERUSER' and $SUB_TABLE.$REALM_COLUMN='$SERDOMAIN'" sql_query "$QUERY" # and also all his contacts $0 ul rm $1 > /dev/null 2>&1 ;; ps) unixsock_cmd ps ;; acl) shift acl "$@" ;; domain) shift domain "$@" ;; unixsock) shift unixsock_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 myhost=`get_my_host` RET=`unixsock_cmd t_uac_dlg OPTIONS "$2" "." \ "From: sip:daemon@$myhost" \ "To: <$2>" "Contact: " "." "." \ | head -1 ` print_status $RET ;; cisco_restart) if [ "$#" -ne 2 ] ; then usage exit 1 fi myhost=`get_my_host` RET=`unixsock_cmd t_uac_dlg NOTIFY "$2" "." \ "From: sip:daemon@$myhost" \ "To: <$2>" "Event: check-sync" \ "Contact: " "." "." | head -1 ` print_status $RET ;; version) echo "$0 $VERSION" ;; *) usage exit 1 ;; esac