Blob Blame History Raw
From 5990c7f1e5b2fa106806d9ed03a4a59cfa75959a Mon Sep 17 00:00:00 2001
From: Martin Langhoff <martin@laptop.org>
Date: Sun, 24 Jan 2010 14:39:15 +0100
Subject: [PATCH] ejabberdctl: support concurrent connections with bound conn names

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.
---
 src/Makefile.in          |   10 ++++++
 src/ejabberdctl.template |   75 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 78 insertions(+), 7 deletions(-)

diff --git a/src/Makefile.in b/src/Makefile.in
index 8485caa..02f05e8 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -110,6 +110,9 @@ MSGSDIR = $(PRIVDIR)/msgs
 # /var/lib/ejabberd/
 SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
 
+# /var/lock/ejabberdctl
+CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl
+
 # /var/lib/ejabberd/.erlang.cookie
 COOKIEFILE = $(SPOOLDIR)/.erlang.cookie
 
@@ -230,6 +233,12 @@ install: all
 	install -d -m 750 $(O_USER) $(SPOOLDIR)
 	$(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
 	chmod -R 750 $(SPOOLDIR)
+
+	# ejabberdctl lock directory
+	install -d -m 750 $(O_USER) $(CTLLOCKDIR)
+	$(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT)
+	chmod -R 750 $(CTLLOCKDIR)
+
 	[ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; }
 	#
 	# Log directory
@@ -265,6 +274,7 @@ uninstall-all: uninstall-binary
 	rm -rf $(ETCDIR)
 	rm -rf $(EJABBERDDIR)
 	rm -rf $(SPOOLDIR)
+	rm -rf $(CTLLOCKDIR)
 	rm -rf $(LOGDIR)
 
 clean: clean-recursive clean-local
diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
index 438481b..d2aa5fe 100644
--- a/src/ejabberdctl.template
+++ b/src/ejabberdctl.template
@@ -14,6 +14,13 @@ 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=`type -p flock` 
+
 # parse command line parameters
 ARGS=
 while [ $# -ne 0 ] ; do
@@ -228,13 +235,55 @@ help ()
 ctl ()
 {
     COMMAND=$@
-    $EXEC_CMD "$ERL \
-      $NAME ctl-${ERLANG_NODE} \
-      -noinput \
-      -hidden \
-      -pa $EJABBERD_EBIN_PATH \
-      -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
-    result=$?
+
+    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"
+	    (
+		if flock --nb 200; then
+                    # see the '200' file descriptor
+		    # at the bottom of the block
+		    ctlexec $CTL_CONN $COMMAND
+                    ssresult=$?
+                    # segregate from possible flock exit(1)
+		    ssresult=$(expr $ssresult \* 10)
+		    exit $ssresult
+		else
+		    exit 1
+		fi
+            ) 200>"$CTL_LOCKFILE"
+	    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) :;;
@@ -244,6 +293,18 @@ ctl ()
     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 ()
 {
-- 
1.6.2.5