improve ejabberdctl, and support different node names (thanks to Zach Calvert)(EJAB-1611)

This commit is contained in:
Christophe Romain 2013-06-18 15:56:28 +02:00
parent 1ea5614209
commit 55fb85107e
2 changed files with 151 additions and 149 deletions

View File

@ -55,11 +55,10 @@
#
# This communication is used by ejabberdctl command line tool,
# and in a cluster of several ejabberd nodes.
# Notice that the IP address must be specified in the Erlang syntax.
#
# Default: {127,0,0,1}
# Default: 127.0.0.1
#
INET_DIST_INTERFACE={127,0,0,1}
#INET_DIST_INTERFACE=127.0.0.1
#.
#' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections

View File

@ -6,24 +6,45 @@ SMP=auto
ERL_MAX_PORTS=32000
ERL_PROCESSES=250000
ERL_MAX_ETS_TABLES=1400
SCRIPT_DIR=$(cd ${0%/*} && pwd)
FIREWALL_WINDOW=""
INET_DIST_INTERFACE="127.0.0.1"
ERLANG_NODE=ejabberd@localhost
# define default environment variables
NODE=ejabberd
HOST=localhost
ERLANG_NODE=$NODE@$HOST
SCRIPT_DIR=`cd ${0%/*} && pwd`
ERL={{erl}}
INSTALLUSER={{installuser}}
# Compatibility in ZSH
#setopt shwordsplit 2>/dev/null
# check the proper system user is used if defined
if [ "$INSTALLUSER" != "" ] ; then
EXEC_CMD="false"
for GID in `id -G`; do
if [ $GID -eq 0 ] ; then
EXEC_CMD="su $INSTALLUSER -p -c"
fi
done
if [ `id -g` -eq `id -g $INSTALLUSER` ] ; then
EXEC_CMD="sh -c"
fi
if [ "$EXEC_CMD" = "false" ] ; then
echo "This command can only be run by root or the user $INSTALLUSER" >&2
exit 4
fi
else
EXEC_CMD="sh -c"
fi
# parse command line parameters
ARGS=
ARGS=""
while [ $# -ne 0 ] ; do
PARAM=$1
shift
case $PARAM in
--) break ;;
--node) ERLANG_NODE_ARG=$1; shift ;;
--node) ERLANG_NODE_ARG=$1 ; shift ;;
--config-dir) ETCDIR=$1 ; shift ;;
--config) EJABBERD_CONFIG_PATH=$1 ; shift ;;
--ctl-config) EJABBERDCTL_CONFIG_PATH=$1 ; shift ;;
@ -37,13 +58,15 @@ done
if [ "$ETCDIR" = "" ] ; then
ETCDIR={{sysconfdir}}/ejabberd
fi
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
fi
if [ "$EJABBERDCTL_CONFIG_PATH" = "" ] ; then
EJABBERDCTL_CONFIG_PATH=$ETCDIR/ejabberdctl.cfg
fi
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
if [ -f "$EJABBERDCTL_CONFIG_PATH" ] ; then
. "$EJABBERDCTL_CONFIG_PATH"
fi
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
fi
if [ "$LOGS_DIR" = "" ] ; then
LOGS_DIR={{localstatedir}}/log/ejabberd
fi
@ -57,60 +80,26 @@ if [ "$ERLANG_NODE_ARG" != "" ] ; then
ERLANG_NODE=$ERLANG_NODE_ARG
NODE=${ERLANG_NODE%@*}
fi
# check the proper system user is used
ID=`id -g`
GIDS=`id -G`
EJID=`id -g $INSTALLUSER`
EXEC_CMD="false"
for GID in $GIDS; do
if [ $GID -eq 0 ] ; then
EXEC_CMD="su ${INSTALLUSER} -p -c"
fi
done
if [ "$ID" -eq "$EJID" ] ; then
EXEC_CMD="sh -c"
fi
if [ "$EXEC_CMD" = "false" ] ; then
echo "This command can only be run by root or the user $INSTALLUSER" >&2
exit 4
fi
NAME=-name
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
KERNEL_OPTS=""
if [ "$FIREWALL_WINDOW" != "" ] ; then
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
fi
if [ "$INET_DIST_INTERFACE" != "" ] ; then
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"${INET_DIST_INTERFACE}\""
fi
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
# define additional environment variables
if [ "{{release}}" != "true" ] ; then
if [ "$EJABBERDDIR" = "" ] ; then
EJABBERDDIR={{libdir}}/ejabberd
EJABBERDDIR={{libdir}}/ejabberd
fi
if [ "$EJABBERD_EBIN_PATH" = "" ] ; then
EJABBERD_EBIN_PATH=$EJABBERDDIR/ebin
EJABBERD_EBIN_PATH=$EJABBERDDIR/ebin
fi
if [ "$EJABBERD_PRIV_PATH" = "" ] ; then
EJABBERD_PRIV_PATH=$EJABBERDDIR/priv
EJABBERD_PRIV_PATH=$EJABBERDDIR/priv
fi
if [ "$EJABBERD_BIN_PATH" = "" ] ; then
EJABBERD_BIN_PATH=$EJABBERD_PRIV_PATH/bin
EJABBERD_BIN_PATH=$EJABBERD_PRIV_PATH/bin
fi
if [ "$EJABBERD_SO_PATH" = "" ] ; then
EJABBERD_SO_PATH=$EJABBERD_PRIV_PATH/lib
EJABBERD_SO_PATH=$EJABBERD_PRIV_PATH/lib
fi
if [ "$EJABBERD_MSGS_PATH" = "" ] ; then
EJABBERD_MSGS_PATH=$EJABBERD_PRIV_PATH/msgs
EJABBERD_MSGS_PATH=$EJABBERD_PRIV_PATH/msgs
fi
fi
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
SASL_LOG_PATH=$LOGS_DIR/erlang.log
DATETIME=`date "+%Y%m%d-%H%M%S"`
@ -118,11 +107,25 @@ ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump
ERL_INETRC=$ETCDIR/inetrc
HOME=$SPOOLDIR
# create the home dir with the proper user if doesn't exist, because it stores cookie file
[ -d $HOME ] || $EXEC_CMD "mkdir -p $HOME"
# define erl parameters
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
KERNEL_OPTS=""
if [ "$FIREWALL_WINDOW" != "" ] ; then
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
fi
if [ "$INET_DIST_INTERFACE" != "" ] ; then
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"{${INET_DIST_INTERFACE//./,}}\""
fi
if [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] ; then
NAME="-sname"
else
NAME="-name"
fi
# Change to a directory readable by INSTALLUSER to
# create the ejabberd home dir with the proper user if doesn't exist
# then change to that directory readable by INSTALLUSER to
# prevent "File operation error: eacces." messages
[ -d $HOME ] || $EXEC_CMD "mkdir -p $HOME"
cd $HOME
# export global variables
@ -141,16 +144,10 @@ export ERL_MAX_ETS_TABLES
export HOME
export EXEC_CMD
# Compatibility in ZSH
#setopt shwordsplit 2>/dev/null
# start server
start ()
start()
{
checknodenameusage
[ "$?" -eq 0 ] && echo "\nERROR: The node '$ERLANG_NODE' is already running." && return 1
check_start
$EXEC_CMD "$ERL \
$NAME $ERLANG_NODE \
-noinput -detached \
@ -163,7 +160,7 @@ start ()
}
# attach to server
debug ()
debug()
{
echo "--------------------------------------------------------------------"
echo ""
@ -182,7 +179,7 @@ debug ()
echo " EJABBERD_BYPASS_WARNINGS=true"
echo "Press any key to continue"
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
read foo
read foo
fi
echo ""
TTY=`tty | sed -e 's/.*\///g'`
@ -195,11 +192,9 @@ debug ()
}
# start interactive server
live ()
live()
{
checknodenameusage
[ "$?" -eq 0 ] && echo "\nERROR: The node '$ERLANG_NODE' is already running." && return 1
check_start
echo "--------------------------------------------------------------------"
echo ""
echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode."
@ -216,7 +211,7 @@ live ()
echo " EJABBERD_BYPASS_WARNINGS=true"
echo "Press any key to continue"
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
read foo
read foo
fi
echo ""
$EXEC_CMD "$ERL \
@ -228,7 +223,14 @@ live ()
$ERLANG_OPTS $ARGS \"$@\""
}
help ()
etop()
{
$EXEC_CMD "$ERL \
$NAME debug-${TTY}-${ERLANG_NODE} \
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
}
help()
{
echo ""
echo "Commands to start an ejabberd node:"
@ -247,7 +249,7 @@ help ()
}
# common control function
ctl ()
ctl()
{
COMMAND=$@
@ -258,71 +260,71 @@ ctl ()
CONNLOCKDIR={{localstatedir}}/lock/ejabberdctl
FLOCK='/usr/bin/flock'
if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
JOT='/usr/bin/jot'
if [ ! -x "$JOT" ] ; then
# no flock or jot, simply invoke ctlexec()
CTL_CONN="ctl-${ERLANG_NODE}"
ctlexec $CTL_CONN $COMMAND
result=$?
else
# no flock, but at least there is jot
RAND=`jot -r 1 0 $MAXCONNID`
CTL_CONN="ctl-${RAND}-${ERLANG_NODE}"
ctlexec $CTL_CONN $COMMAND
result=$?
fi
JOT='/usr/bin/jot'
if [ ! -x "$JOT" ] ; then
# no flock or jot, simply invoke ctlexec()
CTL_CONN="ctl-${ERLANG_NODE}"
ctlexec $CTL_CONN $COMMAND
result=$?
else
# no flock, but at least there is jot
RAND=`jot -r 1 0 $MAXCONNID`
CTL_CONN="ctl-${RAND}-${ERLANG_NODE}"
ctlexec $CTL_CONN $COMMAND
result=$?
fi
else
# we have flock so we get a lock
# on one of a limited number of
# conn names -- this allows
# concurrent invocations using a bound
# number of atoms
for N in $(seq 1 $MAXCONNID); do
CTL_CONN="ejabberdctl-$N"
CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
(
exec 8>"$CTL_LOCKFILE"
if flock --nb 8; then
ctlexec $CTL_CONN $COMMAND
# we have flock so we get a lock
# on one of a limited number of
# conn names -- this allows
# concurrent invocations using a bound
# number of atoms
for N in `seq 1 $MAXCONNID`; do
CTL_CONN="ejabberdctl-$N"
CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
(
exec 8>"$CTL_LOCKFILE"
if flock --nb 8; then
ctlexec $CTL_CONN $COMMAND
ssresult=$?
# segregate from possible flock exit(1)
ssresult=$(expr $ssresult \* 10)
exit $ssresult
else
exit 1
fi
ssresult=`expr $ssresult \* 10`
exit $ssresult
else
exit 1
fi
)
result=$?
if [ $result -eq 1 ]; then
result=$?
if [ $result -eq 1 ] ; then
# means we errored out in flock
# rather than in the exec - stay in the loop
# trying other conn names...
badlock=1
else
badlock=""
break;
fi
done
result=$(expr $result / 10)
badlock=1
else
badlock=""
break;
fi
done
result=`expr $result / 10`
fi
if [ "$badlock" ];then
echo "Ran out of connections to try. Your ejabberd processes" >&2
echo "may be stuck or this is a very busy server. For very" >&2
echo "busy servers, consider raising MAXCONNID in ejabberdctl">&2
exit 1;
if [ "$badlock" ] ;then
echo "Ran out of connections to try. Your ejabberd processes" >&2
echo "may be stuck or this is a very busy server. For very" >&2
echo "busy servers, consider raising MAXCONNID in ejabberdctl">&2
exit 1;
fi
case $result in
0) :;;
1) :;;
2) help;;
3) help;;
0) :;;
1) :;;
2) help;;
3) help;;
esac
return $result
}
ctlexec ()
ctlexec()
{
CONN_NAME=$1; shift
COMMAND=$@
@ -335,17 +337,30 @@ ctlexec ()
-s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
}
# display ctl usage
usage ()
{
ctl
exit
}
# stop epmd if there is no other running node
stop_epmd()
{
epmd -names | grep -q name || epmd -kill
epmd -names 2>/dev/null | grep -q name || epmd -kill >/dev/null
}
# make sure node not already running and node name unregistered
check_start()
{
epmd -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && {
ps ux | grep -v grep | grep -q " $ERLANG_NODE " && {
echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running."
exit 4
} || {
ps ux | grep -v grep | grep -q beam && {
echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered,"
echo " but no related beam process has been found."
echo "Shutdown all other erlang nodes, and call 'epmd -kill'."
exit 5
} || {
epmd -kill >/dev/null
}
}
}
}
# allow sync calls
@ -355,9 +370,9 @@ wait_for_status()
# return: 0 OK, 1 KO
timeout=$2
status=4
while [ $status -ne $1 ]; do
while [ $status -ne $1 ] ; do
sleep $3
timeout=$(($timeout - 1))
timeout=`expr $timeout - 1`
[ $timeout -eq 0 ] && {
status=$1
} || {
@ -365,28 +380,16 @@ wait_for_status()
status=$?
}
done
[ $timeout -eq 0 ] && {
status=1
} || {
status=0
}
return $status
}
# check node name is used or not
# $? --> 0=alreadyused 1=available
checknodenameusage ()
{
$EXEC_CMD "$ERL \
$NAME $ERLANG_NODE \
-s erlang halt | grep -c duplicate_name > /dev/null "
[ $timeout -eq 0 ] && return 1 || return 0
}
# main handler
case $ARGS in
' start') start;;
' debug') debug;;
' live') live;;
' etop') etop;;
' started') wait_for_status 0 30 2;; # wait 30x2s before timeout
' stopped') wait_for_status 3 15 2; stop_epmd;; # wait 15x2s before timeout
' stopped') wait_for_status 3 15 2 && stop_epmd;; # wait 15x2s before timeout
*) ctl $ARGS;;
esac