]> granicus.if.org Git - ejabberd/commitdiff
ejabberdctl: support concurrent connections with bound conn names
authorMartin Langhoff <martin@laptop.org>
Sun, 24 Jan 2010 13:39:15 +0000 (14:39 +0100)
committerBadlop <badlop@process-one.net>
Fri, 12 Feb 2010 19:23:33 +0000 (20:23 +0100)
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
src/ejabberdctl.template

index 8485caa09f4dc59c2a3c37a4ebc4c1c30dc0e841..02f05e89ddd2c3f0957e29c795520a1f1c1df591 100644 (file)
@@ -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
index 2df7be710e3ed0aab0eacd7ef3d7a14ccabcd50a..f5af13be23c030e5b64e1c6b0c235f601e84b5ff 100644 (file)
@@ -14,6 +14,17 @@ 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
@@ -228,13 +239,54 @@ 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"
+           (
+               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) :;;
@@ -244,6 +296,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 ()
 {