mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
6557136426
If flock is available, ejabberdctl will use it to grab one of a bound number of connection names. This allows concurrent connections while using a bound number of atoms. Using PID, timestamps or random strings for transient connection IDs (which would avoid the need for flock) uses an unbound number of atoms. This can effectively DoS servers, as these connection names are not garbage collected.
357 lines
9.2 KiB
Bash
357 lines
9.2 KiB
Bash
#!/bin/sh
|
|
|
|
# define default configuration
|
|
POLL=true
|
|
SMP=auto
|
|
ERL_MAX_PORTS=32000
|
|
ERL_PROCESSES=250000
|
|
ERL_MAX_ETS_TABLES=1400
|
|
|
|
# define default environment variables
|
|
NODE=ejabberd
|
|
HOST=localhost
|
|
ERLANG_NODE=$NODE@$HOST
|
|
ERL=@erl@
|
|
INSTALLUSER=@installuser@
|
|
|
|
# Control number of connections identifiers
|
|
# using flock if available. Expects a linux-style
|
|
# flock that can lock a file descriptor.
|
|
MAXCONNID=100
|
|
CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl
|
|
FLOCK='/usr/bin/flock'
|
|
if [ ! -x "$FLOCK" ];then
|
|
FLOCK=""
|
|
fi
|
|
|
|
|
|
# parse command line parameters
|
|
ARGS=
|
|
while [ $# -ne 0 ] ; do
|
|
PARAM=$1
|
|
shift
|
|
case $PARAM in
|
|
--) break ;;
|
|
--node) ERLANG_NODE_ARG=$1; shift ;;
|
|
--config-dir) ETCDIR=$1 ; shift ;;
|
|
--config) EJABBERD_CONFIG_PATH=$1 ; shift ;;
|
|
--ctl-config) EJABBERDCTL_CONFIG_PATH=$1 ; shift ;;
|
|
--logs) LOGS_DIR=$1 ; shift ;;
|
|
--spool) SPOOLDIR=$1 ; shift ;;
|
|
*) ARGS="$ARGS $PARAM" ;;
|
|
esac
|
|
done
|
|
|
|
# Define ejabberd variable if they have not been defined from the command line
|
|
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 [ "$LOGS_DIR" = "" ] ; then
|
|
LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd
|
|
fi
|
|
if [ "$SPOOLDIR" = "" ] ; then
|
|
SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd
|
|
fi
|
|
if [ "$EJABBERD_DOC_PATH" = "" ] ; then
|
|
EJABBERD_DOC_PATH=@DOCDIR@
|
|
fi
|
|
if [ "$ERLANG_NODE_ARG" != "" ] ; then
|
|
ERLANG_NODE=$ERLANG_NODE_ARG
|
|
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 1
|
|
fi
|
|
|
|
NAME=-name
|
|
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
|
|
|
|
if [ "$FIREWALL_WINDOW" = "" ] ; then
|
|
KERNEL_OPTS=""
|
|
else
|
|
KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
|
fi
|
|
|
|
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS $KERNEL_OPTS"
|
|
|
|
# define additional environment variables
|
|
if [ "$EJABBERDDIR" = "" ]; then
|
|
EJABBERDDIR=@LIBDIR@/ejabberd
|
|
fi
|
|
if [ "$EJABBERD_EBIN_PATH" = "" ]; then
|
|
EJABBERD_EBIN_PATH=$EJABBERDDIR/ebin
|
|
fi
|
|
if [ "$EJABBERD_PRIV_PATH" = "" ]; then
|
|
EJABBERD_PRIV_PATH=$EJABBERDDIR/priv
|
|
fi
|
|
if [ "$EJABBERD_BIN_PATH" = "" ]; then
|
|
EJABBERD_BIN_PATH=$EJABBERD_PRIV_PATH/bin
|
|
fi
|
|
if [ "$EJABBERD_SO_PATH" = "" ]; then
|
|
EJABBERD_SO_PATH=$EJABBERD_PRIV_PATH/lib
|
|
fi
|
|
if [ "$EJABBERD_MSGS_PATH" = "" ]; then
|
|
EJABBERD_MSGS_PATH=$EJABBERD_PRIV_PATH/msgs
|
|
fi
|
|
|
|
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
|
|
SASL_LOG_PATH=$LOGS_DIR/erlang.log
|
|
DATETIME=`date "+%Y%m%d-%H%M%S"`
|
|
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"
|
|
|
|
# export global variables
|
|
export EJABBERD_CONFIG_PATH
|
|
export EJABBERD_MSGS_PATH
|
|
export EJABBERD_LOG_PATH
|
|
export EJABBERD_SO_PATH
|
|
export EJABBERD_BIN_PATH
|
|
export EJABBERD_DOC_PATH
|
|
export EJABBERD_PID_PATH
|
|
export ERL_CRASH_DUMP
|
|
export ERL_INETRC
|
|
export ERL_MAX_PORTS
|
|
export ERL_MAX_ETS_TABLES
|
|
export HOME
|
|
export EXEC_CMD
|
|
|
|
|
|
# Compatibility in ZSH
|
|
#setopt shwordsplit 2>/dev/null
|
|
|
|
# start server
|
|
start ()
|
|
{
|
|
$EXEC_CMD "$ERL \
|
|
$NAME $ERLANG_NODE \
|
|
-noinput -detached \
|
|
-pa $EJABBERD_EBIN_PATH \
|
|
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
|
|
-s ejabberd \
|
|
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
|
|
$ERLANG_OPTS $ARGS \"$@\""
|
|
}
|
|
|
|
# attach to server
|
|
debug ()
|
|
{
|
|
echo "--------------------------------------------------------------------"
|
|
echo ""
|
|
echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell"
|
|
echo "to an already running ejabberd node."
|
|
echo "If an ERROR is printed, it means the connection was not succesfull."
|
|
echo "You can interact with the ejabberd node if you know how to use it."
|
|
echo "Please be extremely cautious with your actions,"
|
|
echo "and exit immediately if you are not completely sure."
|
|
echo ""
|
|
echo "To detach this shell from ejabberd, press:"
|
|
echo " control+c, control+c"
|
|
echo ""
|
|
echo "--------------------------------------------------------------------"
|
|
echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:"
|
|
echo " EJABBERD_BYPASS_WARNINGS=true"
|
|
echo "Press any key to continue"
|
|
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
|
read foo
|
|
fi
|
|
echo ""
|
|
$EXEC_CMD "$ERL \
|
|
$NAME debug-${ERLANG_NODE} \
|
|
-remsh $ERLANG_NODE \
|
|
-hidden \
|
|
$ERLANG_OPTS $ARGS \"$@\""
|
|
}
|
|
|
|
# start interactive server
|
|
live ()
|
|
{
|
|
echo "--------------------------------------------------------------------"
|
|
echo ""
|
|
echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode."
|
|
echo "All log messages will be shown in the command shell."
|
|
echo "You can interact with the ejabberd node if you know how to use it."
|
|
echo "Please be extremely cautious with your actions,"
|
|
echo "and exit immediately if you are not completely sure."
|
|
echo ""
|
|
echo "To exit this LIVE mode and stop ejabberd, press:"
|
|
echo " q(). and press the Enter key"
|
|
echo ""
|
|
echo "--------------------------------------------------------------------"
|
|
echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:"
|
|
echo " EJABBERD_BYPASS_WARNINGS=true"
|
|
echo "Press any key to continue"
|
|
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
|
read foo
|
|
fi
|
|
echo ""
|
|
$EXEC_CMD "$ERL \
|
|
$NAME $ERLANG_NODE \
|
|
-pa $EJABBERD_EBIN_PATH \
|
|
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
|
|
-s ejabberd \
|
|
$ERLANG_OPTS $ARGS \"$@\""
|
|
}
|
|
|
|
help ()
|
|
{
|
|
echo ""
|
|
echo "Commands to start an ejabberd node:"
|
|
echo " start Start an ejabberd node in server mode"
|
|
echo " debug Attach an interactive Erlang shell to a running ejabberd node"
|
|
echo " live Start an ejabberd node in live (interactive) mode"
|
|
echo ""
|
|
echo "Optional parameters when starting an ejabberd node:"
|
|
echo " --config-dir dir Config ejabberd: $ETCDIR"
|
|
echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH"
|
|
echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH"
|
|
echo " --logs dir Directory for logs: $LOGS_DIR"
|
|
echo " --spool dir Database spool dir: $SPOOLDIR"
|
|
echo " --node nodename ejabberd node name: $ERLANG_NODE"
|
|
echo ""
|
|
}
|
|
|
|
# common control function
|
|
ctl ()
|
|
{
|
|
COMMAND=$@
|
|
|
|
if [ ! "$FLOCK" ];then
|
|
# no flock, simply invoke ctlexec()
|
|
CTL_CONN="ctl-${EJABBERD_NODE}"
|
|
ctlexec $CTL_CONN $COMMAND
|
|
result=$?
|
|
else
|
|
# we have flock so we get a lock
|
|
# on one of a limited number of
|
|
# conn names -- this allows
|
|
# concurrent invokations 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
|
|
)
|
|
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)
|
|
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 MAXCONNIDS" >&2
|
|
exit 1;
|
|
fi
|
|
|
|
case $result in
|
|
0) :;;
|
|
1) :;;
|
|
2) help;;
|
|
3) help;;
|
|
esac
|
|
return $result
|
|
}
|
|
|
|
ctlexec ()
|
|
{
|
|
CONN_NAME=$1; shift
|
|
COMMAND=$@
|
|
$EXEC_CMD "$ERL \
|
|
$NAME ${CONN_NAME} \
|
|
-noinput \
|
|
-hidden \
|
|
-pa $EJABBERD_EBIN_PATH \
|
|
-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
|
|
}
|
|
|
|
# allow sync calls
|
|
wait_for_status()
|
|
{
|
|
# args: status try delay
|
|
# return: 0 OK, 1 KO
|
|
timeout=$2
|
|
status=4
|
|
while [ $status -ne $1 ]; do
|
|
sleep $3
|
|
timeout=$(($timeout - 1))
|
|
[ $timeout -eq 0 ] && {
|
|
status=$1
|
|
} || {
|
|
ctl status > /dev/null
|
|
status=$?
|
|
}
|
|
done
|
|
[ $timeout -eq 0 ] && {
|
|
status=1
|
|
} || {
|
|
status=0
|
|
}
|
|
return $status
|
|
}
|
|
|
|
case $ARGS in
|
|
' start') start;;
|
|
' debug') debug;;
|
|
' live') live;;
|
|
' started') wait_for_status 0 30 2;; # wait 30x2s before timeout
|
|
' stopped') wait_for_status 3 15 2; stop_epmd;; # wait 15x2s before timeout
|
|
*) ctl $ARGS;;
|
|
esac
|