summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandru Scvortov <alexandru@rabbitmq.com>2011-09-22 17:34:25 +0100
committerAlexandru Scvortov <alexandru@rabbitmq.com>2011-09-22 17:34:25 +0100
commit8aff707c85ce7049d2cbb6aef920e59e9b2d3606 (patch)
tree0e27cc4547d0d8ffe2c8a6c8bb150bda0ca3f86e
parent1b69e1fa1527fe559130f07b31c3002d8d634b93 (diff)
parentda265ac0ec05eef8a2fb8e3b521a7e488e7a285f (diff)
downloadrabbitmq-server-git-8aff707c85ce7049d2cbb6aef920e59e9b2d3606.tar.gz
merge default into bug21319
-rw-r--r--Makefile28
-rw-r--r--docs/rabbitmq-plugins.1.xml151
-rw-r--r--include/rabbit.hrl7
-rw-r--r--packaging/RPMS/Fedora/Makefile1
-rw-r--r--packaging/RPMS/Fedora/rabbitmq-server.spec13
-rw-r--r--packaging/debs/Debian/Makefile1
-rw-r--r--packaging/debs/Debian/debian/postinst8
-rw-r--r--packaging/debs/Debian/debian/rules2
-rw-r--r--packaging/generic-unix/Makefile1
-rw-r--r--packaging/windows/Makefile10
-rwxr-xr-xscripts/rabbitmq-plugins32
-rwxr-xr-xscripts/rabbitmq-plugins.bat47
-rwxr-xr-xscripts/rabbitmq-server5
-rwxr-xr-x[-rw-r--r--]scripts/rabbitmq-server.bat9
-rwxr-xr-x[-rw-r--r--]scripts/rabbitmq-service.bat9
-rw-r--r--src/rabbit_control.erl19
-rw-r--r--src/rabbit_misc.erl9
-rw-r--r--src/rabbit_plugins.erl315
-rw-r--r--src/rabbit_prelaunch.erl29
19 files changed, 660 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index 5cea016ea6..9bf5d67aa0 100644
--- a/Makefile
+++ b/Makefile
@@ -14,11 +14,11 @@ DOCS_DIR=docs
INCLUDES=$(wildcard $(INCLUDE_DIR)/*.hrl) $(INCLUDE_DIR)/rabbit_framing.hrl
SOURCES=$(wildcard $(SOURCE_DIR)/*.erl) $(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl $(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl $(USAGES_ERL)
BEAM_TARGETS=$(patsubst $(SOURCE_DIR)/%.erl, $(EBIN_DIR)/%.beam, $(SOURCES))
-TARGETS=$(EBIN_DIR)/rabbit.app $(INCLUDE_DIR)/rabbit_framing.hrl $(BEAM_TARGETS)
+TARGETS=$(EBIN_DIR)/rabbit.app $(INCLUDE_DIR)/rabbit_framing.hrl $(BEAM_TARGETS) plugins
WEB_URL=http://www.rabbitmq.com/
MANPAGES=$(patsubst %.xml, %.gz, $(wildcard $(DOCS_DIR)/*.[0-9].xml))
WEB_MANPAGES=$(patsubst %.xml, %.man.xml, $(wildcard $(DOCS_DIR)/*.[0-9].xml) $(DOCS_DIR)/rabbitmq-service.xml)
-USAGES_XML=$(DOCS_DIR)/rabbitmqctl.1.xml
+USAGES_XML=$(DOCS_DIR)/rabbitmqctl.1.xml $(DOCS_DIR)/rabbitmq-plugins.1.xml
USAGES_ERL=$(foreach XML, $(USAGES_XML), $(call usage_xml_to_erl, $(XML)))
QC_MODULES := rabbit_backing_queue_qc
QC_TRIALS ?= 100
@@ -57,6 +57,8 @@ endif
ERLC_OPTS=-I $(INCLUDE_DIR) -o $(EBIN_DIR) -Wall -v +debug_info $(call boolean_macro,$(USE_SPECS),use_specs) $(call boolean_macro,$(USE_PROPER_QC),use_proper_qc)
VERSION=0.0.0
+PLUGINS_SRC_DIR?=$(shell [ -d "plugins-src" ] && echo "plugins-src" || echo )
+PLUGINS_DIST_DIR=plugins-dist
TARBALL_NAME=rabbitmq-server-$(VERSION)
TARGET_SRC_DIR=dist/$(TARBALL_NAME)
@@ -101,6 +103,18 @@ endif
all: $(TARGETS)
+.PHONY: plugins
+ifneq "$(PLUGINS_SRC_DIR)" ""
+plugins:
+ [ -d "$(PLUGINS_SRC_DIR)" ] || { echo "No plugins source distribution found (try linking public-umbrella to $(PLUGINS_SRC_DIR)"; false; }
+ -ln -s "$(CURDIR)" "$(PLUGINS_SRC_DIR)/rabbitmq-server"
+ mkdir -p $(PLUGINS_DIST_DIR)
+ PLUGINS_SRC_DIR="" $(MAKE) -C "$(PLUGINS_SRC_DIR)" plugins-dist PLUGINS_DIST_DIR="$(CURDIR)/$(PLUGINS_DIST_DIR)" VERSION=$(VERSION)
+else
+plugins:
+# Not building plugins
+endif
+
$(DEPS_FILE): $(SOURCES) $(INCLUDES)
rm -f $@
echo $(subst : ,:,$(foreach FILE,$^,$(FILE):)) | escript generate_deps $@ $(EBIN_DIR)
@@ -143,6 +157,8 @@ $(BASIC_PLT): $(BEAM_TARGETS)
clean:
rm -f $(EBIN_DIR)/*.beam
rm -f $(EBIN_DIR)/rabbit.app $(EBIN_DIR)/rabbit.boot $(EBIN_DIR)/rabbit.script $(EBIN_DIR)/rabbit.rel
+ rm -f $(PLUGINS_DIST_DIR)/*.ez
+ -PLUGINS_SRC_DIR="" PRESERVE_CLONE_DIR=1 make -C $(PLUGINS_SRC_DIR) clean
rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc
rm -f $(DOCS_DIR)/*.[0-9].gz $(DOCS_DIR)/*.man.xml $(DOCS_DIR)/*.erl $(USAGES_ERL)
rm -f $(RABBIT_PLT)
@@ -237,7 +253,9 @@ srcdist: distclean
cp -r $(DOCS_DIR) $(TARGET_SRC_DIR)
chmod 0755 $(TARGET_SRC_DIR)/scripts/*
- (cd dist; tar -zcf $(TARBALL_NAME).tar.gz $(TARBALL_NAME))
+ [ "x" != "x$(PLUGINS_SRC_DIR)" ] && ln -s $(PLUGINS_SRC_DIR) $(TARGET_SRC_DIR)/plugins-src || echo No plugins source distribution found
+
+ (cd dist; tar -zchf $(TARBALL_NAME).tar.gz $(TARBALL_NAME))
(cd dist; zip -q -r $(TARBALL_NAME).zip $(TARBALL_NAME))
rm -rf $(TARGET_SRC_DIR)
@@ -284,12 +302,14 @@ install_bin: all install_dirs
cp -r ebin include LICENSE LICENSE-MPL-RabbitMQ INSTALL $(TARGET_DIR)
chmod 0755 scripts/*
- for script in rabbitmq-env rabbitmq-server rabbitmqctl; do \
+ for script in rabbitmq-env rabbitmq-server rabbitmqctl rabbitmq-plugins; do \
cp scripts/$$script $(TARGET_DIR)/sbin; \
[ -e $(SBIN_DIR)/$$script ] || ln -s $(SCRIPTS_REL_PATH)/$$script $(SBIN_DIR)/$$script; \
done
mkdir -p $(TARGET_DIR)/plugins
echo Put your .ez plugin files in this directory. > $(TARGET_DIR)/plugins/README
+ mkdir -p $(TARGET_DIR)/$(PLUGINS_DIST_DIR)
+ -cp $(PLUGINS_DIST_DIR)/*.ez $(TARGET_DIR)/$(PLUGINS_DIST_DIR)
install_docs: docs_all install_dirs
for section in 1 5; do \
diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml
new file mode 100644
index 0000000000..edc1ef9365
--- /dev/null
+++ b/docs/rabbitmq-plugins.1.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd">
+<!--
+ There is some extra magic in this document besides the usual DocBook semantics
+ to allow us to derive manpages, HTML and usage messages from the same source
+ document.
+
+ Examples need to be moved to the end for man pages. To this end, <para>s and
+ <screen>s with role="example" will be moved, and with role="example-prefix"
+ will be removed.
+
+ The usage messages are more involved. We have some magic in usage.xsl to pull
+ out the command synopsis, global option and subcommand synopses. We also pull
+ out <para>s with role="usage".
+
+ Finally we construct lists of possible values for subcommand options, if the
+ subcommand's <varlistentry> has role="usage-has-option-list". The option which
+ takes the values should be marked with role="usage-option-list".
+-->
+
+<refentry lang="en">
+ <refentryinfo>
+ <productname>RabbitMQ Server</productname>
+ <authorgroup>
+ <corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>&gt;</corpauthor>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>rabbitmq-plugins</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="manual">RabbitMQ Service</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>rabbitmq-plugins</refname>
+ <refpurpose>command line tool for managing a RabbitMQ broker plugins</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>rabbitmq-plugins</command>
+ <arg choice="req"><replaceable>command</replaceable></arg>
+ <arg choice="opt" rep="repeat"><replaceable>command options</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+ <command>rabbitmq-plugins</command> is a command line tool for
+ managing RabbitMQ broker plugins. It allows one to enable,
+ disable and browse plugins. Note that it must be run by a user
+ with write permissions to the RabbitMQ configuration directory.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><cmdsynopsis><command>list</command> <arg choice="opt">-c</arg> <arg choice="opt"><replaceable>pattern</replaceable></arg></cmdsynopsis></term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term>-c</term>
+ <listitem><para>List plugins compactly.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>pattern</term>
+ <listitem><para>Pattern to filter the plugin names by.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ Lists available plugins, their versions, dependencies
+ and descriptions.
+ </para>
+ <para>
+ If the optional pattern is given, only plugins whose
+ name mathes <command>pattern</command> are shown.
+ </para>
+ <para role="example-prefix">For example:</para>
+ <screen role="example">rabbitmq-plugins list</screen>
+ <para role="example">
+ This command lists all the plugins available.
+ </para>
+ <screen role="example">rabbitmq-plugins list -c</screen>
+ <para role="example">
+ This command lists all the plugins available, on one line each.
+ </para>
+ <screen role="example">rabbitmq-plugins list -c management</screen>
+ <para role="example">
+ This command lists all the plugins available, on one
+ line each, but does not display plugins whose name does
+ not contain "management".
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><cmdsynopsis><command>enable</command> <arg choice="req"><replaceable>plugin</replaceable> ...</arg></cmdsynopsis></term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term>plugin</term>
+ <listitem><para>One or more plugins to enable.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ Enables the specified plugins and all their
+ dependencies.
+ </para>
+
+ <para role="example-prefix">For example:</para>
+ <screen role="example">rabbitmq-plugins enable rabbitmq_shovel rabbitmq_management</screen>
+ <para role="example">
+ This command enables the <command>shovel</command> and
+ <command>management</command> plugins and all their
+ dependencies.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><cmdsynopsis><command>disable</command> <arg choice="req"><replaceable>plugin</replaceable> ...</arg></cmdsynopsis></term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term>plugin</term>
+ <listitem><para>One or more plugins to disable.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ Disables the specified plugins and all plugins that
+ depend on them.
+ </para>
+
+ <para role="example-prefix">For example:</para>
+ <screen role="example">rabbitmq-plugins disable amqp_client</screen>
+ <para role="example">
+ This command disables <command>amqp_client</command> and
+ all plugins that depend on it.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+</refentry>
diff --git a/include/rabbit.hrl b/include/rabbit.hrl
index ac6399c64e..a603886c4b 100644
--- a/include/rabbit.hrl
+++ b/include/rabbit.hrl
@@ -75,6 +75,13 @@
-record(message_properties, {expiry, needs_confirming = false}).
+-record(plugin, {name, %% atom()
+ version, %% string()
+ description, %% string()
+ type, %% 'ez' or 'dir'
+ dependencies, %% [{atom(), string()}]
+ location}). %% string()
+
%%----------------------------------------------------------------------------
-define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2011 VMware, Inc.").
diff --git a/packaging/RPMS/Fedora/Makefile b/packaging/RPMS/Fedora/Makefile
index c67d8fd6e1..be37646234 100644
--- a/packaging/RPMS/Fedora/Makefile
+++ b/packaging/RPMS/Fedora/Makefile
@@ -2,6 +2,7 @@ TARBALL_DIR=../../../dist
TARBALL=$(notdir $(wildcard $(TARBALL_DIR)/rabbitmq-server-[0-9.]*.tar.gz))
COMMON_DIR=../../common
VERSION=$(shell echo $(TARBALL) | sed -e 's:rabbitmq-server-\(.*\)\.tar\.gz:\1:g')
+PLUGINS_DIST_DIR=
TOP_DIR=$(shell pwd)
#Under debian we do not want to check build dependencies, since that
diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec
index 0c5aa96a1f..57174b239a 100644
--- a/packaging/RPMS/Fedora/rabbitmq-server.spec
+++ b/packaging/RPMS/Fedora/rabbitmq-server.spec
@@ -55,6 +55,7 @@ mkdir -p %{buildroot}%{_localstatedir}/log/rabbitmq
install -p -D -m 0755 %{S:1} %{buildroot}%{_initrddir}/rabbitmq-server
install -p -D -m 0755 %{_rabbit_wrapper} %{buildroot}%{_sbindir}/rabbitmqctl
install -p -D -m 0755 %{_rabbit_wrapper} %{buildroot}%{_sbindir}/rabbitmq-server
+install -p -D -m 0755 %{_rabbit_wrapper} %{buildroot}%{_sbindir}/rabbitmq-plugin
install -p -D -m 0755 %{_rabbit_server_ocf} %{buildroot}%{_exec_prefix}/lib/ocf/resource.d/rabbitmq/rabbitmq-server
install -p -D -m 0644 %{S:3} %{buildroot}%{_sysconfdir}/logrotate.d/rabbitmq-server
@@ -85,12 +86,24 @@ if ! getent passwd rabbitmq >/dev/null; then
-c "RabbitMQ messaging server"
fi
+chown -R rabbitmq:rabbitmq %{_rabbit_erllibdir}/plugins/
+
%post
/sbin/chkconfig --add %{name}
if [ -f %{_sysconfdir}/rabbitmq/rabbitmq.conf ] && [ ! -f %{_sysconfdir}/rabbitmq/rabbitmq-env.conf ]; then
mv %{_sysconfdir}/rabbitmq/rabbitmq.conf %{_sysconfdir}/rabbitmq/rabbitmq-env.conf
fi
+if [ $1 -gt 1 ]; then
+ # Upgrade - find the old enabled_plugins file, copy it to the new
+ # version and re-enable plugins
+ ENABLED_PLUGINS_FILE=find %{_rabbit_libdir} -name 'enabled_plugins'
+ if [ "x" != "x$ENABLED_PLUGINS_FILE" ]; then
+ cp $ENABLED_PLUGINS_FILE %{_maindir}/plugins/
+ rabbitmq-plugin enable
+ fi
+fi
+
%preun
if [ $1 = 0 ]; then
#Complete uninstall
diff --git a/packaging/debs/Debian/Makefile b/packaging/debs/Debian/Makefile
index 38c81134a2..1b2fdd9e20 100644
--- a/packaging/debs/Debian/Makefile
+++ b/packaging/debs/Debian/Makefile
@@ -2,6 +2,7 @@ TARBALL_DIR=../../../dist
TARBALL=$(notdir $(wildcard $(TARBALL_DIR)/rabbitmq-server-[0-9.]*.tar.gz))
COMMON_DIR=../../common
VERSION=$(shell echo $(TARBALL) | sed -e 's:rabbitmq-server-\(.*\)\.tar\.gz:\1:g')
+PLUGINS_DIST_DIR=
DEBIAN_ORIG_TARBALL=$(shell echo $(TARBALL) | sed -e 's:\(.*\)-\(.*\)\(\.tar\.gz\):\1_\2\.orig\3:g')
UNPACKED_DIR=rabbitmq-server-$(VERSION)
diff --git a/packaging/debs/Debian/debian/postinst b/packaging/debs/Debian/debian/postinst
index b11340ef8a..8e8523b3ae 100644
--- a/packaging/debs/Debian/debian/postinst
+++ b/packaging/debs/Debian/debian/postinst
@@ -32,6 +32,7 @@ fi
chown -R rabbitmq:rabbitmq /var/lib/rabbitmq
chown -R rabbitmq:rabbitmq /var/log/rabbitmq
+chown -R rabbitmq:rabbitmq /usr/lib/rabbitmq/lib/rabbitmq_server-*/plugins/
case "$1" in
configure)
@@ -39,6 +40,13 @@ case "$1" in
[ ! -f /etc/rabbitmq/rabbitmq-env.conf ]; then
mv /etc/rabbitmq/rabbitmq.conf /etc/rabbitmq/rabbitmq-env.conf
fi
+
+ ENABLED_PLUGINS_FILE="/usr/lib/rabbitmq/lib/rabbitmq_server-$2/plugins/enabled_plugins"
+ if [ -f "$ENABLED_PLUGINS_FILE" ]; then
+ RABBIT_LIB=$(ls -1td /usr/lib/rabbitmq/lib/rabbitmq_server-* | head -1)
+ cp $ENABLED_PLUGINS_FILE $RABBIT_LIB/plugins/
+ rabbitmq-plugin enable
+ fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
diff --git a/packaging/debs/Debian/debian/rules b/packaging/debs/Debian/debian/rules
index a785b292af..ffb4461680 100644
--- a/packaging/debs/Debian/debian/rules
+++ b/packaging/debs/Debian/debian/rules
@@ -14,7 +14,7 @@ DOCDIR=$(DEB_DESTDIR)usr/share/doc/rabbitmq-server/
install/rabbitmq-server::
mkdir -p $(DOCDIR)
rm $(RABBIT_LIB)LICENSE* $(RABBIT_LIB)INSTALL*
- for script in rabbitmqctl rabbitmq-server; do \
+ for script in rabbitmqctl rabbitmq-server rabbitmq-plugin; do \
install -p -D -m 0755 debian/rabbitmq-script-wrapper $(DEB_DESTDIR)usr/sbin/$$script; \
done
sed -e 's|@RABBIT_LIB@|/usr/lib/rabbitmq/lib/rabbitmq_server-$(DEB_UPSTREAM_VERSION)|g' <debian/postrm.in >debian/postrm
diff --git a/packaging/generic-unix/Makefile b/packaging/generic-unix/Makefile
index b5c342aad7..0f2a47b69a 100644
--- a/packaging/generic-unix/Makefile
+++ b/packaging/generic-unix/Makefile
@@ -2,6 +2,7 @@ VERSION=0.0.0
SOURCE_DIR=rabbitmq-server-$(VERSION)
TARGET_DIR=rabbitmq_server-$(VERSION)
TARGET_TARBALL=rabbitmq-server-generic-unix-$(VERSION)
+PLUGINS_DIST_DIR=
dist:
tar -zxf ../../dist/$(SOURCE_DIR).tar.gz
diff --git a/packaging/windows/Makefile b/packaging/windows/Makefile
index a0be8d89e0..6abcafc543 100644
--- a/packaging/windows/Makefile
+++ b/packaging/windows/Makefile
@@ -2,6 +2,7 @@ VERSION=0.0.0
SOURCE_DIR=rabbitmq-server-$(VERSION)
TARGET_DIR=rabbitmq_server-$(VERSION)
TARGET_ZIP=rabbitmq-server-windows-$(VERSION)
+PLUGINS_DIST_DIR=
dist:
tar -zxf ../../dist/$(SOURCE_DIR).tar.gz
@@ -10,20 +11,25 @@ dist:
mkdir $(SOURCE_DIR)/sbin
mv $(SOURCE_DIR)/scripts/rabbitmq-server.bat $(SOURCE_DIR)/sbin
mv $(SOURCE_DIR)/scripts/rabbitmq-service.bat $(SOURCE_DIR)/sbin
+ mv $(SOURCE_DIR)/scripts/rabbitmq-plugin.bat $(SOURCE_DIR)/sbin
mv $(SOURCE_DIR)/scripts/rabbitmqctl.bat $(SOURCE_DIR)/sbin
rm -rf $(SOURCE_DIR)/scripts
- rm -rf $(SOURCE_DIR)/codegen* $(SOURCE_DIR)/Makefile
+ rm -rf $(SOURCE_DIR)/codegen* $(SOURCE_DIR)/Makefile $(SOURCE_DIR)/*mk
rm -f $(SOURCE_DIR)/README
rm -rf $(SOURCE_DIR)/docs
+ rm -rf $(SOURCE_DIR)/src
+ rm -rf $(SOURCE_DIR)/dist
mv $(SOURCE_DIR) $(TARGET_DIR)
mkdir -p $(TARGET_DIR)
mkdir -p $(TARGET_DIR)/plugins
- echo Put your .ez plugin files in this directory > $(TARGET_DIR)/plugins/README
+ echo Put your .ez plugin files in this directory > $(TARGET_DIR)/plugins/README.txt
+ mv $(TARGET_DIR)/plugins-src/README $(TARGET_DIR)/plugins-dist/README.txt
xmlto -o . xhtml-nochunks ../../docs/rabbitmq-service.xml
elinks -dump -no-references -no-numbering rabbitmq-service.html \
> $(TARGET_DIR)/readme-service.txt
todos $(TARGET_DIR)/readme-service.txt
+ rm -rf $(TARGET_DIR)/plugins-src
zip -q -r $(TARGET_ZIP).zip $(TARGET_DIR)
rm -rf $(TARGET_DIR) rabbitmq-service.html
diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins
new file mode 100755
index 0000000000..b3137a32de
--- /dev/null
+++ b/scripts/rabbitmq-plugins
@@ -0,0 +1,32 @@
+#!/bin/sh
+## The contents of this file are subject to the Mozilla Public License
+## Version 1.1 (the "License"); you may not use this file except in
+## compliance with the License. You may obtain a copy of the License
+## at http://www.mozilla.org/MPL/
+##
+## Software distributed under the License is distributed on an "AS IS"
+## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+## the License for the specific language governing rights and
+## limitations under the License.
+##
+## The Original Code is RabbitMQ.
+##
+## The Initial Developer of the Original Code is VMware, Inc.
+## Copyright (c) 2007-2011 VMware, Inc. All rights reserved.
+##
+
+. `dirname $0`/rabbitmq-env
+
+[ "x" = "x$RABBITMQ_ENABLED_PLUGINS_FILE" ] && RABBITMQ_ENABLED_PLUGINS_FILE=/etc/rabbitmq/enabled_plugins
+
+[ "x" = "x$RABBITMQ_PLUGINS_DIST_DIR" ] && RABBITMQ_PLUGINS_DIST_DIR="${RABBITMQ_HOME}/plugins-dist"
+
+exec erl \
+ -pa "${RABBITMQ_HOME}/ebin" \
+ -noinput \
+ -hidden \
+ -sname rabbitmq-plugins$$ \
+ -s rabbit_plugins \
+ -enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \
+ -plugins_dist_dir "$RABBITMQ_PLUGINS_DIST_DIR" \
+ -extra "$@"
diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat
new file mode 100755
index 0000000000..8455a7dc90
--- /dev/null
+++ b/scripts/rabbitmq-plugins.bat
@@ -0,0 +1,47 @@
+@echo off
+REM The contents of this file are subject to the Mozilla Public License
+REM Version 1.1 (the "License"); you may not use this file except in
+REM compliance with the License. You may obtain a copy of the License
+REM at http://www.mozilla.org/MPL/
+REM
+REM Software distributed under the License is distributed on an "AS IS"
+REM basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+REM the License for the specific language governing rights and
+REM limitations under the License.
+REM
+REM The Original Code is RabbitMQ.
+REM
+REM The Initial Developer of the Original Code is VMware, Inc.
+REM Copyright (c) 2007-2011 VMware, Inc. All rights reserved.
+REM
+
+setlocal
+
+rem Preserve values that might contain exclamation marks before
+rem enabling delayed expansion
+set TDP0=%~dp0
+set STAR=%*
+setlocal enabledelayedexpansion
+
+if not exist "!ERLANG_HOME!\bin\erl.exe" (
+ echo.
+ echo ******************************
+ echo ERLANG_HOME not set correctly.
+ echo ******************************
+ echo.
+ echo Please either set ERLANG_HOME to point to your Erlang installation or place the
+ echo RabbitMQ server distribution in the Erlang lib folder.
+ echo.
+ exit /B
+)
+
+if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" (
+ set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins
+)
+
+set RABBITMQ_PLUGINS_DIST_DIR=!TDP0!..\plugins-dist
+
+"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIST_DIR:\=/!" -extra !STAR!
+
+endlocal
+endlocal
diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server
index 7176d80130..302eb92f39 100755
--- a/scripts/rabbitmq-server
+++ b/scripts/rabbitmq-server
@@ -52,7 +52,8 @@ fi
[ "x" = "x$RABBITMQ_PLUGINS_EXPAND_DIR" ] && RABBITMQ_PLUGINS_EXPAND_DIR=${PLUGINS_EXPAND_DIR}
[ "x" = "x$RABBITMQ_PLUGINS_EXPAND_DIR" ] && RABBITMQ_PLUGINS_EXPAND_DIR=${RABBITMQ_MNESIA_BASE}/${RABBITMQ_NODENAME}-plugins-expand
-[ "x" = "x$RABBITMQ_PLUGINS_DIR" ] && RABBITMQ_PLUGINS_DIR="${RABBITMQ_HOME}/plugins"
+[ "x" = "x$RABBITMQ_ENABLED_PLUGINS_FILE" ] && RABBITMQ_ENABLED_PLUGINS_FILE=/etc/rabbitmq/enabled_plugins
+[ "x" = "x$RABBITMQ_PLUGINS_DIST_DIR" ] && RABBITMQ_PLUGINS_DIST_DIR="${RABBITMQ_HOME}/plugins-dist"
## Log rotation
[ "x" = "x$RABBITMQ_LOGS" ] && RABBITMQ_LOGS=${LOGS}
@@ -79,7 +80,7 @@ if [ "x" = "x$RABBITMQ_NODE_ONLY" ]; then
-hidden \
-s rabbit_prelaunch \
-sname rabbitmqprelaunch$$ \
- -extra "$RABBITMQ_PLUGINS_DIR" "${RABBITMQ_PLUGINS_EXPAND_DIR}" "${RABBITMQ_NODENAME}"
+ -extra "$RABBITMQ_ENABLED_PLUGINS_FILE" "$RABBITMQ_PLUGINS_DIST_DIR" "${RABBITMQ_PLUGINS_EXPAND_DIR}" "${RABBITMQ_NODENAME}"
then
RABBITMQ_BOOT_FILE="${RABBITMQ_PLUGINS_EXPAND_DIR}/rabbit"
RABBITMQ_EBIN_PATH=""
diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat
index 84d24c458b..df38013869 100644..100755
--- a/scripts/rabbitmq-server.bat
+++ b/scripts/rabbitmq-server.bat
@@ -93,7 +93,11 @@ if "!RABBITMQ_PLUGINS_EXPAND_DIR!"=="" (
set RABBITMQ_PLUGINS_EXPAND_DIR=!RABBITMQ_MNESIA_BASE!/!RABBITMQ_NODENAME!-plugins-expand
)
-set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins
+if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" (
+ set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins
+)
+
+set RABBITMQ_PLUGINS_DIST_DIR=!TDP0!..\plugins-dist
set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin
"!ERLANG_HOME!\bin\erl.exe" ^
@@ -101,7 +105,8 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin
-noinput -hidden ^
-s rabbit_prelaunch ^
-sname rabbitmqprelaunch!RANDOM! ^
--extra "!RABBITMQ_PLUGINS_DIR:\=/!" ^
+-extra "!RABBITMQ_ENABLED_PLUGINS_FILE!" ^
+ "!RABBITMQ_PLUGINS_DIST_DIR:\=/!" ^
"!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^
"!RABBITMQ_NODENAME!"
diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat
index 60697d0b49..295a7d70d0 100644..100755
--- a/scripts/rabbitmq-service.bat
+++ b/scripts/rabbitmq-service.bat
@@ -162,14 +162,19 @@ if errorlevel 1 (
echo !RABBITMQ_SERVICENAME! service is already present - only updating service parameters
)
-set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins
+if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" (
+ set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins
+)
+
+set RABBITMQ_PLUGINS_DIST_DIR=!TDP0!..\plugins-dist
set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin
"!ERLANG_HOME!\bin\erl.exe" ^
-pa "!RABBITMQ_EBIN_ROOT!" ^
-noinput -hidden ^
-s rabbit_prelaunch ^
--extra "!RABBITMQ_PLUGINS_DIR:\=/!" ^
+-extra "!RABBITMQ_ENABLED_PLUGINS_FILE!" ^
+ "!RABBITMQ_PLUGINS_DIST_DIR:\=/!" ^
"!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^
""
diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl
index 1163ae9d86..b28748538e 100644
--- a/src/rabbit_control.erl
+++ b/src/rabbit_control.erl
@@ -86,24 +86,24 @@ start() ->
true -> ok;
false -> io:format("...done.~n")
end,
- quit(0);
+ rabbit_misc:quit(0);
{'EXIT', {function_clause, [{?MODULE, action, _} | _]}} ->
print_error("invalid command '~s'",
[string:join([atom_to_list(Command) | Args], " ")]),
usage();
{error, Reason} ->
print_error("~p", [Reason]),
- quit(2);
+ rabbit_misc:quit(2);
{badrpc, {'EXIT', Reason}} ->
print_error("~p", [Reason]),
- quit(2);
+ rabbit_misc:quit(2);
{badrpc, Reason} ->
print_error("unable to connect to node ~w: ~w", [Node, Reason]),
print_badrpc_diagnostics(Node),
- quit(2);
+ rabbit_misc:quit(2);
Other ->
print_error("~p", [Other]),
- quit(2)
+ rabbit_misc:quit(2)
end.
fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args).
@@ -154,7 +154,7 @@ stop() ->
usage() ->
io:format("~s", [rabbit_ctl_usage:usage()]),
- quit(1).
+ rabbit_misc:quit(1).
%%----------------------------------------------------------------------------
@@ -506,10 +506,3 @@ prettify_typed_amqp_value(table, Value) -> prettify_amqp_table(Value);
prettify_typed_amqp_value(array, Value) -> [prettify_typed_amqp_value(T, V) ||
{T, V} <- Value];
prettify_typed_amqp_value(_Type, Value) -> Value.
-
-%% the slower shutdown on windows required to flush stdout
-quit(Status) ->
- case os:type() of
- {unix, _} -> halt(Status);
- {win32, _} -> init:stop(Status)
- end.
diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl
index d47041d669..f50730033c 100644
--- a/src/rabbit_misc.erl
+++ b/src/rabbit_misc.erl
@@ -54,6 +54,7 @@
-export([pget/2, pget/3, pget_or_die/2]).
-export([format_message_queue/2]).
-export([append_rpc_all_nodes/4]).
+-export([quit/1]).
%%----------------------------------------------------------------------------
@@ -191,6 +192,7 @@
-spec(pget_or_die/2 :: (term(), [term()]) -> term() | no_return()).
-spec(format_message_queue/2 :: (any(), priority_queue:q()) -> term()).
-spec(append_rpc_all_nodes/4 :: ([node()], atom(), atom(), [any()]) -> [any()]).
+-spec(quit/1 :: (integer() | string()) -> no_return()).
-endif.
@@ -806,3 +808,10 @@ append_rpc_all_nodes(Nodes, M, F, A) ->
{badrpc, _} -> [];
_ -> Res
end || Res <- ResL]).
+
+%% the slower shutdown on windows required to flush stdout
+quit(Status) ->
+ case os:type() of
+ {unix, _} -> halt(Status);
+ {win32, _} -> init:stop(Status)
+ end.
diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl
new file mode 100644
index 0000000000..b93b7bed99
--- /dev/null
+++ b/src/rabbit_plugins.erl
@@ -0,0 +1,315 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is VMware, Inc.
+%% Copyright (c) 2011 VMware, Inc. All rights reserved.
+%%
+
+-module(rabbit_plugins).
+-include("rabbit.hrl").
+
+-export([start/0, stop/0, find_plugins/1, read_enabled_plugins/1,
+ lookup_plugins/2, calculate_required_plugins/2]).
+
+-define(COMPACT_OPT, "-c").
+
+%%----------------------------------------------------------------------------
+
+-ifdef(use_specs).
+
+-spec(start/0 :: () -> no_return()).
+-spec(stop/0 :: () -> 'ok').
+
+-endif.
+
+%%----------------------------------------------------------------------------
+
+start() ->
+ {ok, [[EnabledPluginsFile|_]|_]} = init:get_argument(enabled_plugins_file),
+ put(enabled_plugins_file, EnabledPluginsFile),
+ {ok, [[PluginsDistDir|_]|_]} = init:get_argument(plugins_dist_dir),
+ put(plugins_dist_dir, PluginsDistDir),
+ {[Command0 | Args], Opts} =
+ case rabbit_misc:get_options([{flag, ?COMPACT_OPT}],
+ init:get_plain_arguments()) of
+ {[], _Opts} -> usage();
+ CmdArgsAndOpts -> CmdArgsAndOpts
+ end,
+ Command = list_to_atom(Command0),
+
+ case catch action(Command, Args, Opts) of
+ ok ->
+ rabbit_misc:quit(0);
+ {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} ->
+ print_error("invalid command '~s'",
+ [string:join([atom_to_list(Command) | Args], " ")]),
+ usage();
+ {error, Reason} ->
+ print_error("~p", [Reason]),
+ rabbit_misc:quit(2);
+ Other ->
+ print_error("~p", [Other]),
+ rabbit_misc:quit(2)
+ end.
+
+stop() ->
+ ok.
+
+print_error(Format, Args) ->
+ rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args).
+
+usage() ->
+ io:format("~s", [rabbit_plugins_usage:usage()]),
+ rabbit_misc:quit(1).
+
+%%----------------------------------------------------------------------------
+
+action(list, [], Opts) ->
+ action(list, [".*"], Opts);
+action(list, [Pat], Opts) ->
+ format_plugins(Pat, proplists:get_bool(?COMPACT_OPT, Opts));
+
+action(enable, ToEnable0, _Opts) ->
+ AllPlugins = find_plugins(),
+ EnabledPlugins = lookup_plugins(read_enabled_plugins(), AllPlugins),
+ ToEnable = [list_to_atom(Name) || Name <- ToEnable0],
+ ToEnablePlugins = lookup_plugins(ToEnable, AllPlugins),
+ Missing = ToEnable -- plugin_names(ToEnablePlugins),
+ case Missing of
+ [] -> ok;
+ _ -> io:format("Warning: the following plugins could not be found: ~p~n",
+ [Missing])
+ end,
+ NewEnabledPlugins = merge_plugin_lists(EnabledPlugins, ToEnablePlugins),
+ update_enabled_plugins(plugin_names(NewEnabledPlugins));
+
+action(disable, ToDisable0, _Opts) ->
+ ToDisable = [list_to_atom(Name) || Name <- ToDisable0],
+ Enabled = read_enabled_plugins(),
+ AllPlugins = find_plugins(),
+ Missing = ToDisable -- plugin_names(AllPlugins),
+ case Missing of
+ [] -> ok;
+ _ -> io:format("Warning: the following plugins could not be found: ~p~n",
+ [Missing])
+ end,
+ ToDisable1 = ToDisable -- Missing,
+ ToDisable2 = calculate_dependencies(true, ToDisable1, AllPlugins),
+ AlsoDisabled = sets:to_list(
+ sets:intersection(sets:from_list(ToDisable2 -- ToDisable1),
+ sets:from_list(Enabled))),
+ case AlsoDisabled of
+ [] -> ok;
+ _ -> io:format("Warning: the following plugins will also be disabled "
+ "because their dependencies are no longer met: ~p~n",
+ [AlsoDisabled])
+ end,
+ update_enabled_plugins(Enabled -- ToDisable2).
+
+%%----------------------------------------------------------------------------
+
+%% Get the #plugin{}s ready to be enabled.
+find_plugins() ->
+ find_plugins(get(plugins_dist_dir)).
+find_plugins(PluginsDistDir) ->
+ EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDistDir)],
+ FreeApps = [{app, App} ||
+ App <- filelib:wildcard("*/ebin/*.app", PluginsDistDir)],
+ {Plugins, Problems} =
+ lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) ->
+ {Plugins1, [{EZ, Reason} | Problems1]};
+ (Plugin = #plugin{}, {Plugins1, Problems1}) ->
+ {[Plugin|Plugins1], Problems1}
+ end, {[], []},
+ [get_plugin_info(PluginsDistDir, Plug) ||
+ Plug <- EZs ++ FreeApps]),
+ case Problems of
+ [] -> ok;
+ _ -> io:format("Warning: Problem reading some plugins: ~p~n", [Problems])
+ end,
+ Plugins.
+
+%% Get the #plugin{} from an .ez.
+get_plugin_info(Base, {ez, EZ0}) ->
+ EZ = filename:join([Base, EZ0]),
+ case read_app_file(EZ) of
+ {application, Name, Props} ->
+ Version = proplists:get_value(vsn, Props, "0"),
+ Description = proplists:get_value(description, Props, ""),
+ Dependencies =
+ filter_applications(proplists:get_value(applications, Props, [])),
+ #plugin{name = Name, version = Version, description = Description,
+ dependencies = Dependencies, location = EZ, type = ez};
+ {error, Reason} ->
+ {error, EZ, Reason}
+ end;
+%% Get the #plugin{} from an .app.
+get_plugin_info(Base, {app, App0}) ->
+ App = filename:join([Base, App0]),
+ case rabbit_file:read_term_file(App) of
+ {ok, [{application, Name, Props}]} ->
+ Version = proplists:get_value(vsn, Props, "0"),
+ Description = proplists:get_value(description, Props, ""),
+ Dependencies =
+ filter_applications(proplists:get_value(applications, Props, [])),
+ Location = filename:absname(filename:dirname(filename:dirname(App))),
+ #plugin{name = Name, version = Version, description = Description,
+ dependencies = Dependencies, location = Location, type = dir};
+ {error, Reason} ->
+ {error, App, {invalid_app, Reason}}
+ end.
+
+%% Read the .app file from an ez.
+read_app_file(EZ) ->
+ case zip:list_dir(EZ) of
+ {ok, [_|ZippedFiles]} ->
+ case find_app_files(ZippedFiles) of
+ [AppPath|_] ->
+ {ok, [{AppPath, AppFile}]} =
+ zip:extract(EZ, [{file_list, [AppPath]}, memory]),
+ parse_binary(AppFile);
+ [] ->
+ {error, no_app_file}
+ end;
+ {error, Reason} ->
+ {error, {invalid_ez, Reason}}
+ end.
+
+%% Return the path of the .app files in ebin/.
+find_app_files(ZippedFiles) ->
+ {ok, RE} = re:compile("^.*/ebin/.*.app$"),
+ [Path || {zip_file, Path, _, _, _, _} <- ZippedFiles,
+ re:run(Path, RE, [{capture, none}]) =:= match].
+
+%% Parse a binary into a term.
+parse_binary(Bin) ->
+ try
+ {ok, Ts, _} = erl_scan:string(binary:bin_to_list(Bin)),
+ {ok, Term} = erl_parse:parse_term(Ts),
+ Term
+ catch
+ Err -> {error, {invalid_app, Err}}
+ end.
+
+%% Pretty print a list of plugins.
+format_plugins(Pattern, Compact) ->
+ AvailablePlugins = find_plugins(),
+ EnabledExplicitly = read_enabled_plugins(),
+ EnabledImplicitly =
+ calculate_required_plugins(EnabledExplicitly, AvailablePlugins) --
+ EnabledExplicitly,
+ {ok, RE} = re:compile(Pattern),
+ [ format_plugin(P, EnabledExplicitly, EnabledImplicitly, Compact)
+ || P = #plugin{name = Name} <- AvailablePlugins,
+ re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match],
+ ok.
+
+format_plugin(#plugin{name = Name, version = Version, description = Description,
+ dependencies = Dependencies},
+ EnabledExplicitly, EnabledImplicitly, Compact) ->
+ Glyph = case {lists:member(Name, EnabledExplicitly),
+ lists:member(Name, EnabledImplicitly)} of
+ {true, false} -> "[E]";
+ {false, true} -> "[e]";
+ _ -> "[A]"
+ end,
+ case Compact of
+ true ->
+ io:format("~s ~w-~s: ~s~n", [Glyph, Name, Version, Description]);
+ false ->
+ io:format("~s ~w~n", [Glyph, Name]),
+ io:format(" Version: \t~s~n", [Version]),
+ case Dependencies of
+ [] -> ok;
+ _ -> io:format(" Dependencies:\t~p~n", [Dependencies])
+ end,
+ io:format(" Description:\t~s~n", [Description]),
+ io:format("~n")
+ end.
+
+usort_plugins(Plugins) ->
+ lists:usort(fun plugins_cmp/2, Plugins).
+
+%% Merge two plugin lists. In case of duplicates, only keep highest
+%% version.
+merge_plugin_lists(Ps1, Ps2) ->
+ filter_duplicates(usort_plugins(Ps1 ++ Ps2)).
+
+filter_duplicates([P1 = #plugin{name = N, version = V1},
+ P2 = #plugin{name = N, version = V2} | Ps]) ->
+ if V1 < V2 -> filter_duplicates([P2 | Ps]);
+ true -> filter_duplicates([P1 | Ps])
+ end;
+filter_duplicates([P | Ps]) ->
+ [P | filter_duplicates(Ps)];
+filter_duplicates(Ps) ->
+ Ps.
+
+plugins_cmp(#plugin{name = N1, version = V1}, #plugin{name = N2, version = V2}) ->
+ {N1, V1} =< {N2, V2}.
+
+%% Filter applications that can be loaded *right now*.
+filter_applications(Applications) ->
+ [Application || Application <- Applications,
+ case application:load(Application) of
+ {error, {already_loaded, _}} -> false;
+ ok -> application:unload(Application),
+ false;
+ _ -> true
+ end].
+
+%% Return the names of the given plugins.
+plugin_names(Plugins) ->
+ [Name || #plugin{name = Name} <- Plugins].
+
+%% Find plugins by name in a list of plugins.
+lookup_plugins(Names, AllPlugins) ->
+ AllPlugins1 = filter_duplicates(usort_plugins(AllPlugins)),
+ [P || P = #plugin{name = Name} <- AllPlugins1, lists:member(Name, Names)].
+
+%% Read the enabled plugin names from disk.
+read_enabled_plugins() ->
+ read_enabled_plugins(get(enabled_plugins_file)).
+
+read_enabled_plugins(FileName) ->
+ case rabbit_file:read_term_file(FileName) of
+ {ok, [Plugins]} -> Plugins;
+ {error, enoent} -> [];
+ {error, Reason} -> throw({error, {cannot_read_enabled_plugins_file,
+ FileName, Reason}})
+ end.
+
+%% Update the enabled plugin names on disk.
+update_enabled_plugins(Plugins) ->
+ FileName = get(enabled_plugins_file),
+ case rabbit_file:write_term_file(FileName, [Plugins]) of
+ ok -> ok;
+ {error, Reason} -> throw({error, {cannot_write_enabled_plugins_file,
+ FileName, Reason}})
+ end.
+
+calculate_required_plugins(Sources, AllPlugins) ->
+ calculate_dependencies(false, Sources, AllPlugins).
+
+calculate_dependencies(Reverse, Sources, AllPlugins) ->
+ AllPlugins1 = filter_duplicates(usort_plugins(AllPlugins)),
+ {ok, G} = rabbit_misc:build_acyclic_graph(
+ fun (App, _Deps) -> [{App, App}] end,
+ fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end,
+ [{Name, Deps}
+ || #plugin{name = Name, dependencies = Deps} <- AllPlugins1]),
+ Dests = case Reverse of
+ false -> digraph_utils:reachable(Sources, G);
+ true -> digraph_utils:reaching(Sources, G)
+ end,
+ true = digraph:delete(G),
+ Dests.
diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl
index cd0c322b6d..e6c7344afe 100644
--- a/src/rabbit_prelaunch.erl
+++ b/src/rabbit_prelaunch.erl
@@ -18,6 +18,8 @@
-export([start/0, stop/0]).
+-include("rabbit.hrl").
+
-define(BaseApps, [rabbit]).
-define(ERROR_CODE, 1).
@@ -41,14 +43,14 @@ start() ->
io:format("Activating RabbitMQ plugins ...~n"),
%% Determine our various directories
- [PluginDir, UnpackedPluginDir, NodeStr] = init:get_plain_arguments(),
+ [EnabledPluginsFile, PluginsDistDir, UnpackedPluginDir, NodeStr] =
+ init:get_plain_arguments(),
RootName = UnpackedPluginDir ++ "/rabbit",
- %% Unpack any .ez plugins
- unpack_ez_plugins(PluginDir, UnpackedPluginDir),
+ prepare_plugins(EnabledPluginsFile, PluginsDistDir, UnpackedPluginDir),
%% Build a list of required apps based on the fixed set, and any plugins
- PluginApps = find_plugins(PluginDir) ++ find_plugins(UnpackedPluginDir),
+ PluginApps = find_plugins(UnpackedPluginDir),
RequiredApps = ?BaseApps ++ PluginApps,
%% Build the entire set of dependencies - this will load the
@@ -145,7 +147,11 @@ delete_recursively(Fn) ->
Error -> Error
end.
-unpack_ez_plugins(SrcDir, DestDir) ->
+prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) ->
+ AllPlugins = rabbit_plugins:find_plugins(PluginsDistDir),
+ Enabled = rabbit_plugins:read_enabled_plugins(EnabledPluginsFile),
+ ToUnpack = rabbit_plugins:calculate_required_plugins(Enabled, AllPlugins),
+
%% Eliminate the contents of the destination directory
case delete_recursively(DestDir) of
ok -> ok;
@@ -155,12 +161,15 @@ unpack_ez_plugins(SrcDir, DestDir) ->
ok -> ok;
{error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2])
end,
- [unpack_ez_plugin(PluginName, DestDir) ||
- PluginName <- filelib:wildcard(SrcDir ++ "/*.ez")].
-unpack_ez_plugin(PluginFn, PluginDestDir) ->
- zip:unzip(PluginFn, [{cwd, PluginDestDir}]),
- ok.
+ [prepare_plugin(Plugin, DestDir) ||
+ Plugin <- rabbit_plugins:lookup_plugins(ToUnpack, AllPlugins)].
+
+prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) ->
+ zip:unzip(Location, [{cwd, PluginDestDir}]);
+prepare_plugin(#plugin{type = dir, name = Name, location = Location},
+ PluginsDestDir) ->
+ file:make_symlink(Location, filename:join([PluginsDestDir, Name])).
find_plugins(PluginDir) ->
[prepare_dir_plugin(PluginName) ||