diff options
98 files changed, 7484 insertions, 21528 deletions
diff --git a/.gitignore b/.gitignore index cc625ee5c4..902eaa6f84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,35 @@ -*~ .sw? .*.sw? *.beam -*.orig -*.patch -*.plt -*.tmp -\#* -.#* -TAGS -erl_crash.dump -/ebin/rabbit.app -/build/ +/.erlang.mk/ /cover/ -/dist/ -/etc/ +/deps/ +/doc/ +/ebin/ +/logs/ /plugins/ -/priv/plugins/ -/deps.mk -/include/rabbit_framing.hrl -/include/rabbit_framing_spec.hrl -/src/rabbit_framing_amqp*.erl -/src/*_usage.erl -/packaging/RPMS/Fedora/{BUILD,RPMS,SOURCES,SPECS,SRPMS}/ -/packaging/debs/Debian/rabbitmq-server_*.{dsc,diff.gz,tar.gz,deb,changes} -/packaging/debs/apt-repository/debian -/packaging/macports/macports -/packaging/generic-unix/rabbitmq-server-generic-unix-*.tar.gz -/packaging/windows/rabbitmq-server-windows-*.zip -/packaging/windows-exe/rabbitmq_server-* -/packaging/windows-exe/rabbitmq-*.nsi -/packaging/windows-exe/rabbitmq-server-*.exe +/rabbit.d -/docs/*.[15].gz -/docs/*.man.xml +# Generated sources files. +/src/rabbit_ctl_usage.erl +/src/rabbit_plugins_usage.erl + +# Generated documentation. +/docs/rabbitmq-echopid.man.xml +/docs/rabbitmq-env.conf.5 +/docs/rabbitmq-env.conf.5.man.xml +/docs/rabbitmq-plugins.1 +/docs/rabbitmq-plugins.1.man.xml +/docs/rabbitmq-server.1 +/docs/rabbitmq-server.1.man.xml +/docs/rabbitmq-service.man.xml +/docs/rabbitmqctl.1 +/docs/rabbitmqctl.1.man.xml + +# Source distribution. +/rabbitmq-server-*/ +/rabbitmq-server-*.tar.gz +/rabbitmq-server-*.tar.bz2 +/rabbitmq-server-*.tar.xz +/rabbitmq-server-*.zip @@ -1,422 +1,420 @@ -TMPDIR ?= /tmp - -RABBITMQ_NODENAME ?= rabbit -RABBITMQ_SERVER_START_ARGS ?= -RABBITMQ_MNESIA_DIR ?= $(TMPDIR)/rabbitmq-$(RABBITMQ_NODENAME)-mnesia -RABBITMQ_PLUGINS_EXPAND_DIR ?= $(TMPDIR)/rabbitmq-$(RABBITMQ_NODENAME)-plugins-scratch -RABBITMQ_LOG_BASE ?= $(TMPDIR) - -DEPS_FILE=deps.mk -SOURCE_DIR=src -TEST_DIR=test/src -EBIN_DIR=ebin -TEST_EBIN_DIR=test/ebin -INCLUDE_DIR=include -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) -TEST_SOURCES=$(wildcard $(TEST_DIR)/*.erl) -BEAM_TARGETS=$(patsubst $(SOURCE_DIR)/%.erl, $(EBIN_DIR)/%.beam, $(SOURCES)) -TEST_BEAM_TARGETS=$(patsubst $(TEST_DIR)/%.erl, $(TEST_EBIN_DIR)/%.beam, $(TEST_SOURCES)) -TARGETS=$(EBIN_DIR)/rabbit.app $(INCLUDE_DIR)/rabbit_framing.hrl $(BEAM_TARGETS) plugins -TEST_TARGETS=$(TEST_BEAM_TARGETS) -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 $(DOCS_DIR)/rabbitmq-echopid.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))) - -BASIC_PLT=basic.plt -RABBIT_PLT=rabbit.plt +PROJECT = rabbit +VERSION ?= $(call get_app_version,src/$(PROJECT).app.src) -ifndef USE_PROPER_QC -# PropEr needs to be installed for property checking -# http://proper.softlab.ntua.gr/ -USE_PROPER_QC=$(shell erl -noshell -eval 'io:format({module, proper} =:= code:ensure_loaded(proper)), halt().') -endif +DEPS = $(PLUGINS) -#other args: +native +"{hipe,[o3,verbose]}" -Ddebug=true +debug_info +no_strict_record_tests -ERLC_OPTS=-I $(INCLUDE_DIR) -Wall +warn_export_vars -v +debug_info $(call boolean_macro,$(USE_SPECS),use_specs) $(call boolean_macro,$(USE_PROPER_QC),use_proper_qc) - -# Our type specs rely on dict:dict/0 etc, which are only available in -# 17.0 upwards. -define compare_version -$(shell awk 'BEGIN { - split("$(1)", v1, "\."); - version1 = v1[1] * 1000000 + v1[2] * 10000 + v1[3] * 100 + v1[4]; - - split("$(2)", v2, "\."); - version2 = v2[1] * 1000000 + v2[2] * 10000 + v2[3] * 100 + v2[4]; - - if (version1 $(3) version2) { - print "true"; - } else { - print "false"; - } -}') -endef +SRCDIST_DEPS ?= rabbitmq_shovel -ERTS_VER = $(shell erl -version 2>&1 | sed -E 's/.* version //') -USE_SPECS_MIN_ERTS_VER = 5.11 -ifeq ($(call compare_version,$(ERTS_VER),$(USE_SPECS_MIN_ERTS_VER),>=),true) -ERLC_OPTS += -Duse_specs +ifneq ($(IS_DEP),1) +ifneq ($(filter source-dist packages package-%,$(MAKECMDGOALS)),) +DEPS += $(SRCDIST_DEPS) endif - -ifdef INSTRUMENT_FOR_QC -ERLC_OPTS += -DINSTR_MOD=gm_qc -else -ERLC_OPTS += -DINSTR_MOD=gm +ifneq ($(wildcard git-revisions.txt),) +DEPS += $(SRCDIST_DEPS) endif - -ifdef CREDIT_FLOW_TRACING -ERLC_OPTS += -DCREDIT_FLOW_TRACING=true endif -include version.mk - -PLUGINS_SRC_DIR?=$(shell [ -d "plugins-src" ] && echo "plugins-src" || echo ) -PLUGINS_DIR=plugins -TARBALL_NAME=rabbitmq-server-$(VERSION) -TARGET_SRC_DIR=dist/$(TARBALL_NAME) - -SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ -AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) -AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json $(AMQP_CODEGEN_DIR)/credit_extension.json -AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.8.json - -ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e - -ERL_EBIN=erl -noinput -pa $(EBIN_DIR) - define usage_xml_to_erl - $(subst __,_,$(patsubst $(DOCS_DIR)/rabbitmq%.1.xml, $(SOURCE_DIR)/rabbit_%_usage.erl, $(subst -,_,$(1)))) +$(subst __,_,$(patsubst $(DOCS_DIR)/rabbitmq%.1.xml, src/rabbit_%_usage.erl, $(subst -,_,$(1)))) endef define usage_dep - $(call usage_xml_to_erl, $(1)): $(1) $(DOCS_DIR)/usage.xsl +$(call usage_xml_to_erl, $(1)):: $(1) $(DOCS_DIR)/usage.xsl endef -define boolean_macro -$(if $(filter true,$(1)),-D$(2)) -endef +DOCS_DIR = docs +MANPAGES = $(patsubst %.xml, %, $(wildcard $(DOCS_DIR)/*.[0-9].xml)) +WEB_MANPAGES = $(patsubst %.xml, %.man.xml, $(wildcard $(DOCS_DIR)/*.[0-9].xml) $(DOCS_DIR)/rabbitmq-service.xml $(DOCS_DIR)/rabbitmq-echopid.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))) -ifneq "$(SBIN_DIR)" "" -ifneq "$(TARGET_DIR)" "" -SCRIPTS_REL_PATH=$(shell ./calculate-relative $(TARGET_DIR)/sbin $(SBIN_DIR)) -endif -endif +.DEFAULT_GOAL = all -# Versions prior to this are not supported -NEED_MAKE := 3.80 -ifneq "$(NEED_MAKE)" "$(firstword $(sort $(NEED_MAKE) $(MAKE_VERSION)))" -$(error Versions of make prior to $(NEED_MAKE) are not supported) -endif +EXTRA_SOURCES += $(USAGES_ERL) -# .DEFAULT_GOAL introduced in 3.81 -DEFAULT_GOAL_MAKE := 3.81 -ifneq "$(DEFAULT_GOAL_MAKE)" "$(firstword $(sort $(DEFAULT_GOAL_MAKE) $(MAKE_VERSION)))" -.DEFAULT_GOAL=all -endif +$(PROJECT).d:: $(EXTRA_SOURCES) + +DEP_PLUGINS = rabbit_common/mk/rabbitmq-run.mk \ + rabbit_common/mk/rabbitmq-dist.mk + +# FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be +# reviewed and merged. + +ERLANG_MK_REPO = https://github.com/rabbitmq/erlang.mk.git +ERLANG_MK_COMMIT = rabbitmq-tmp -all: $(TARGETS) $(TEST_TARGETS) +include rabbitmq-components.mk +include erlang.mk -.PHONY: plugins check-xref -ifneq "$(PLUGINS_SRC_DIR)" "" -plugins: - [ -d "$(PLUGINS_SRC_DIR)/rabbitmq-server" ] || ln -s "$(CURDIR)" "$(PLUGINS_SRC_DIR)/rabbitmq-server" - mkdir -p $(PLUGINS_DIR) - PLUGINS_SRC_DIR="" $(MAKE) -C "$(PLUGINS_SRC_DIR)" plugins-dist PLUGINS_DIST_DIR="$(CURDIR)/$(PLUGINS_DIR)" VERSION=$(VERSION) - echo "Put your EZs here and use rabbitmq-plugins to enable them." > $(PLUGINS_DIR)/README - rm -f $(PLUGINS_DIR)/rabbit_common*.ez +# -------------------------------------------------------------------- +# Compilation. +# -------------------------------------------------------------------- -# add -q to remove printout of warnings.... -check-xref: $(BEAM_TARGETS) $(PLUGINS_DIR) - rm -rf lib - ./check_xref $(PLUGINS_DIR) -q +RMQ_ERLC_OPTS += -I $(DEPS_DIR)/rabbit_common/include +ifdef INSTRUMENT_FOR_QC +RMQ_ERLC_OPTS += -DINSTR_MOD=gm_qc else -plugins: -# Not building plugins +RMQ_ERLC_OPTS += -DINSTR_MOD=gm +endif -check-xref: - $(info xref checks are disabled as there is no plugins-src directory) +ifdef CREDIT_FLOW_TRACING +RMQ_ERLC_OPTS += -DCREDIT_FLOW_TRACING=true +endif +ERTS_VER = $(shell erl -version 2>&1 | sed -E 's/.* version //') +USE_SPECS_MIN_ERTS_VER = 5.11 +ifeq ($(call compare_version,$(ERTS_VER),$(USE_SPECS_MIN_ERTS_VER),>=),true) +RMQ_ERLC_OPTS += -Duse_specs endif -$(DEPS_FILE): $(SOURCES) $(INCLUDES) - rm -f $@ - echo $(subst : ,:,$(foreach FILE,$^,$(FILE):)) | escript generate_deps $@ $(EBIN_DIR) - -$(EBIN_DIR)/rabbit.app: $(EBIN_DIR)/rabbit_app.in $(SOURCES) generate_app - escript generate_app $< $@ $(SOURCE_DIR) - -$(EBIN_DIR)/%.beam: $(SOURCE_DIR)/%.erl | $(DEPS_FILE) - erlc -o $(EBIN_DIR) $(ERLC_OPTS) -pa $(EBIN_DIR) $< - -$(TEST_EBIN_DIR)/%.beam: $(TEST_DIR)/%.erl | $(TEST_EBIN_DIR) - erlc -o $(TEST_EBIN_DIR) $(ERLC_OPTS) -pa $(EBIN_DIR) -pa $(TEST_EBIN_DIR) $< - -$(TEST_EBIN_DIR): - mkdir -p $(TEST_EBIN_DIR) - -$(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) - ./codegen.py --ignore-conflicts header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ - -$(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) - ./codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $@ - -$(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_8) - ./codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $@ - -dialyze: $(BEAM_TARGETS) $(BASIC_PLT) - dialyzer --plt $(BASIC_PLT) --no_native --fullpath \ - $(BEAM_TARGETS) - -# rabbit.plt is used by rabbitmq-erlang-client's dialyze make target -create-plt: $(RABBIT_PLT) - -$(RABBIT_PLT): $(BEAM_TARGETS) $(BASIC_PLT) - dialyzer --plt $(BASIC_PLT) --output_plt $@ --no_native \ - --add_to_plt $(BEAM_TARGETS) - -$(BASIC_PLT): $(BEAM_TARGETS) - if [ -f $@ ]; then \ - touch $@; \ - else \ - dialyzer --output_plt $@ --build_plt \ - --apps erts kernel stdlib compiler sasl os_mon mnesia tools \ - public_key crypto ssl xmerl; \ - fi - -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 -rf $(TEST_EBIN_DIR) - rm -f $(PLUGINS_DIR)/*.ez - [ -d "$(PLUGINS_SRC_DIR)" ] && PLUGINS_SRC_DIR="" PRESERVE_CLONE_DIR=1 make -C $(PLUGINS_SRC_DIR) clean || true - 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) - rm -f $(DEPS_FILE) - -cleandb: - rm -rf $(RABBITMQ_MNESIA_DIR)/* - -############ various tasks to interact with RabbitMQ ################### - -BASIC_SCRIPT_ENVIRONMENT_SETTINGS=\ - RABBITMQ_NODE_IP_ADDRESS="$(RABBITMQ_NODE_IP_ADDRESS)" \ - RABBITMQ_NODE_PORT="$(RABBITMQ_NODE_PORT)" \ - RABBITMQ_LOG_BASE="$(RABBITMQ_LOG_BASE)" \ - RABBITMQ_MNESIA_DIR="$(RABBITMQ_MNESIA_DIR)" \ - RABBITMQ_PLUGINS_EXPAND_DIR="$(RABBITMQ_PLUGINS_EXPAND_DIR)" - -run: all - $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ - RABBITMQ_ALLOW_INPUT=true \ - RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ - ./scripts/rabbitmq-server - -run-background: all - $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ - RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ - ./scripts/rabbitmq-server -detached - -run-node: all - $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ - RABBITMQ_NODE_ONLY=true \ - RABBITMQ_ALLOW_INPUT=true \ - RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ - ./scripts/rabbitmq-server - -run-background-node: all - $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ - RABBITMQ_NODE_ONLY=true \ - RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ - ./scripts/rabbitmq-server -detached - -run-tests: all - echo 'code:add_path("$(TEST_EBIN_DIR)").' | $(ERL_CALL) - echo 'code:add_path("$(TEST_EBIN_DIR)").' | $(ERL_CALL) -n hare || true - OUT=$$(echo "rabbit_tests:all_tests()." | $(ERL_CALL)) ; \ - echo $$OUT ; echo $$OUT | grep '^{ok, passed}$$' > /dev/null - -run-qc: all - echo 'code:add_path("$(TEST_EBIN_DIR)").' | $(ERL_CALL) - ./quickcheck $(RABBITMQ_NODENAME) rabbit_backing_queue_qc 100 40 - ./quickcheck $(RABBITMQ_NODENAME) gm_qc 1000 200 - -start-background-node: all - -rm -f $(RABBITMQ_MNESIA_DIR).pid - mkdir -p $(RABBITMQ_MNESIA_DIR) - $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ - RABBITMQ_NODE_ONLY=true \ - RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ - ./scripts/rabbitmq-server \ - > $(RABBITMQ_MNESIA_DIR)/startup_log \ - 2> $(RABBITMQ_MNESIA_DIR)/startup_err & - ./scripts/rabbitmqctl -n $(RABBITMQ_NODENAME) wait $(RABBITMQ_MNESIA_DIR).pid kernel - -start-rabbit-on-node: all - echo "rabbit:start()." | $(ERL_CALL) - ./scripts/rabbitmqctl -n $(RABBITMQ_NODENAME) wait $(RABBITMQ_MNESIA_DIR).pid - -stop-rabbit-on-node: all - echo "rabbit:stop()." | $(ERL_CALL) - -set-resource-alarm: all - echo "rabbit_alarm:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ - $(ERL_CALL) - -clear-resource-alarm: all - echo "rabbit_alarm:clear_alarm({resource_limit, $(SOURCE), node()})." | \ - $(ERL_CALL) - -stop-node: - -( \ - pid=$$(./scripts/rabbitmqctl -n $(RABBITMQ_NODENAME) eval 'os:getpid().') && \ - $(ERL_CALL) -q && \ - while ps -p $$pid >/dev/null 2>&1; do sleep 1; done \ - ) - -# code coverage will be created for subdirectory "ebin" of COVER_DIR -COVER_DIR=. - -start-cover: all - echo "rabbit_misc:start_cover([\"rabbit\", \"hare\"])." | $(ERL_CALL) - echo "rabbit_misc:enable_cover([\"$(COVER_DIR)\"])." | $(ERL_CALL) - -stop-cover: all - echo "rabbit_misc:report_cover(), cover:stop()." | $(ERL_CALL) - cat cover/summary.txt - -######################################################################## - -srcdist: distclean - mkdir -p $(TARGET_SRC_DIR)/codegen - cp -r ebin src include LICENSE LICENSE-MPL-RabbitMQ INSTALL README $(TARGET_SRC_DIR) - sed 's/%%VSN%%/$(VERSION)/' $(TARGET_SRC_DIR)/ebin/rabbit_app.in > $(TARGET_SRC_DIR)/ebin/rabbit_app.in.tmp && \ - mv $(TARGET_SRC_DIR)/ebin/rabbit_app.in.tmp $(TARGET_SRC_DIR)/ebin/rabbit_app.in - - cp -r $(AMQP_CODEGEN_DIR)/* $(TARGET_SRC_DIR)/codegen/ - cp codegen.py Makefile generate_app generate_deps calculate-relative $(TARGET_SRC_DIR) - - echo "VERSION?=${VERSION}" > $(TARGET_SRC_DIR)/version.mk - - cp -r scripts $(TARGET_SRC_DIR) - cp -r $(DOCS_DIR) $(TARGET_SRC_DIR) - chmod 0755 $(TARGET_SRC_DIR)/scripts/* - -ifneq "$(PLUGINS_SRC_DIR)" "" - cp -r $(PLUGINS_SRC_DIR) $(TARGET_SRC_DIR)/plugins-src - rm $(TARGET_SRC_DIR)/LICENSE - cat packaging/common/LICENSE.head >> $(TARGET_SRC_DIR)/LICENSE - cat $(AMQP_CODEGEN_DIR)/license_info >> $(TARGET_SRC_DIR)/LICENSE - find $(PLUGINS_SRC_DIR)/licensing -name "license_info_*" -exec cat '{}' >> $(TARGET_SRC_DIR)/LICENSE \; - cat packaging/common/LICENSE.tail >> $(TARGET_SRC_DIR)/LICENSE - find $(PLUGINS_SRC_DIR)/licensing -name "LICENSE-*" -exec cp '{}' $(TARGET_SRC_DIR) \; - rm -rf $(TARGET_SRC_DIR)/licensing -else - @echo No plugins source distribution found +ifndef USE_PROPER_QC +# PropEr needs to be installed for property checking +# http://proper.softlab.ntua.gr/ +USE_PROPER_QC = $(shell $(ERL) -eval 'io:format({module, proper} =:= code:ensure_loaded(proper)), halt().') +RMQ_ERLC_OPTS += $(if $(filter true,$(USE_PROPER_QC)),-Duse_proper_qc) endif - (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) +ERLC_OPTS += $(RMQ_ERLC_OPTS) + +clean:: clean-extra-sources + +clean-extra-sources: + $(gen_verbose) rm -f $(EXTRA_SOURCES) -distclean: clean - $(MAKE) -C $(AMQP_CODEGEN_DIR) distclean - rm -rf dist - find . -regex '.*\(~\|#\|\.swp\|\.dump\)' -exec rm {} \; +# -------------------------------------------------------------------- +# Tests. +# -------------------------------------------------------------------- + +TEST_ERLC_OPTS += $(RMQ_ERLC_OPTS) + +# -------------------------------------------------------------------- +# Documentation. +# -------------------------------------------------------------------- # xmlto can not read from standard input, so we mess with a tmp file. -%.gz: %.xml $(DOCS_DIR)/examples-to-end.xsl - xmlto --version | grep -E '^xmlto version 0\.0\.([0-9]|1[1-8])$$' >/dev/null || opt='--stringparam man.indent.verbatims=0' ; \ - xsltproc --novalid $(DOCS_DIR)/examples-to-end.xsl $< > $<.tmp && \ - xmlto -o $(DOCS_DIR) $$opt man $<.tmp && \ - gzip -f $(DOCS_DIR)/`basename $< .xml` - rm -f $<.tmp +%: %.xml $(DOCS_DIR)/examples-to-end.xsl + $(gen_verbose) xmlto --version | \ + grep -E '^xmlto version 0\.0\.([0-9]|1[1-8])$$' >/dev/null || \ + opt='--stringparam man.indent.verbatims=0' ; \ + xsltproc --novalid $(DOCS_DIR)/examples-to-end.xsl $< > $<.tmp && \ + (xmlto -o $(DOCS_DIR) $$opt man $< 2>&1 | (grep -qv '^Note: Writing' || :)) && \ + rm $<.tmp # Use tmp files rather than a pipeline so that we get meaningful errors # Do not fold the cp into previous line, it's there to stop the file being # generated but empty if we fail -$(SOURCE_DIR)/%_usage.erl: - xsltproc --novalid --stringparam modulename "`basename $@ .erl`" \ - $(DOCS_DIR)/usage.xsl $< > $@.tmp - sed -e 's/"/\\"/g' -e 's/%QUOTE%/"/g' $@.tmp > $@.tmp2 - fold -s $@.tmp2 > $@.tmp3 - mv $@.tmp3 $@ +src/%_usage.erl:: + $(gen_verbose) xsltproc --novalid --stringparam modulename "`basename $@ .erl`" \ + $(DOCS_DIR)/usage.xsl $< > $@.tmp && \ + sed -e 's/"/\\"/g' -e 's/%QUOTE%/"/g' $@.tmp > $@.tmp2 && \ + fold -s $@.tmp2 > $@.tmp3 && \ + mv $@.tmp3 $@ && \ rm $@.tmp $@.tmp2 # We rename the file before xmlto sees it since xmlto will use the name of # the file to make internal links. %.man.xml: %.xml $(DOCS_DIR)/html-to-website-xml.xsl - cp $< `basename $< .xml`.xml && \ - xmlto xhtml-nochunks `basename $< .xml`.xml ; rm `basename $< .xml`.xml + $(gen_verbose) cp $< `basename $< .xml`.xml && \ + xmlto xhtml-nochunks `basename $< .xml`.xml ; \ + rm `basename $< .xml`.xml && \ cat `basename $< .xml`.html | \ xsltproc --novalid $(DOCS_DIR)/remove-namespaces.xsl - | \ - xsltproc --novalid --stringparam original `basename $<` $(DOCS_DIR)/html-to-website-xml.xsl - | \ - xmllint --format - > $@ + xsltproc --novalid --stringparam original `basename $<` $(DOCS_DIR)/html-to-website-xml.xsl - | \ + xmllint --format - > $@ && \ rm `basename $< .xml`.html -docs_all: $(MANPAGES) $(WEB_MANPAGES) +$(foreach XML,$(USAGES_XML),$(eval $(call usage_dep, $(XML)))) -install: install_bin install_docs +.PHONY: manpages web-manpages distclean-manpages + +docs:: manpages web-manpages + +manpages: $(MANPAGES) + @: + +web-manpages: $(WEB_MANPAGES) + @: + +distclean:: distclean-manpages + +distclean-manpages:: + $(gen_verbose) rm -f $(MANPAGES) $(WEB_MANPAGES) + +# -------------------------------------------------------------------- +# Distribution. +# -------------------------------------------------------------------- + +.PHONY: source-dist + +SOURCE_DIST_BASE ?= rabbitmq-server +SOURCE_DIST_SUFFIXES ?= tar.xz zip +SOURCE_DIST ?= $(SOURCE_DIST_BASE)-$(VERSION) + +# The first source distribution file is used by packages: if the archive +# type changes, you must update all packages' Makefile. +SOURCE_DIST_FILES = $(addprefix $(SOURCE_DIST).,$(SOURCE_DIST_SUFFIXES)) + +.PHONY: $(SOURCE_DIST_FILES) + +source-dist: $(SOURCE_DIST_FILES) + @: + +RSYNC ?= rsync +RSYNC_V_0 = +RSYNC_V_1 = -v +RSYNC_V = $(RSYNC_V_$(V)) +RSYNC_FLAGS += -a $(RSYNC_V) \ + --exclude '.sw?' --exclude '.*.sw?' \ + --exclude '*.beam' \ + --exclude '*.pyc' \ + --exclude '.git*' \ + --exclude '$(notdir $(ERLANG_MK_TMP))' \ + --exclude 'ebin' \ + --exclude 'packaging' \ + --exclude 'erl_crash.dump' \ + --exclude 'deps' \ + --exclude '/$(SOURCE_DIST_BASE)-*' \ + --delete \ + --delete-excluded + +TAR ?= tar +TAR_V_0 = +TAR_V_1 = -v +TAR_V = $(TAR_V_$(V)) + +GZIP ?= gzip +BZIP2 ?= bzip2 +XZ ?= xz + +ZIP ?= zip +ZIP_V_0 = -q +ZIP_V_1 = +ZIP_V = $(ZIP_V_$(V)) + +.PHONY: $(SOURCE_DIST) + +$(SOURCE_DIST): $(ERLANG_MK_RECURSIVE_DEPS_LIST) + $(gen_verbose) $(RSYNC) $(RSYNC_FLAGS) ./ $(SOURCE_DIST)/ + $(verbose) cat packaging/common/LICENSE.head > $(SOURCE_DIST)/LICENSE + $(verbose) mkdir -p $(SOURCE_DIST)/deps + $(verbose) for dep in $$(cat $(ERLANG_MK_RECURSIVE_DEPS_LIST)); do \ + $(RSYNC) $(RSYNC_FLAGS) \ + $$dep \ + $(SOURCE_DIST)/deps; \ + if test -f $(SOURCE_DIST)/deps/$$(basename $$dep)/erlang.mk; then \ + sed -E -i.bak -e 's,^include[[:blank:]]+$(abspath erlang.mk),include ../../erlang.mk,' \ + $(SOURCE_DIST)/deps/$$(basename $$dep)/erlang.mk; \ + rm $(SOURCE_DIST)/deps/$$(basename $$dep)/erlang.mk.bak; \ + fi; \ + if test -f "$$dep/license_info"; then \ + cat "$$dep/license_info" >> $(SOURCE_DIST)/LICENSE; \ + fi; \ + done + $(verbose) cat packaging/common/LICENSE.tail >> $(SOURCE_DIST)/LICENSE + $(verbose) for file in $$(find $(SOURCE_DIST) -name '*.app.src'); do \ + sed -E -i.bak -e 's/[{]vsn[[:blank:]]*,[^}]+}/{vsn, "$(VERSION)"}/' $$file; \ + rm $$file.bak; \ + done + $(verbose) echo "rabbit $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)" > $(SOURCE_DIST)/git-revisions.txt + $(verbose) for dep in $$(cat $(ERLANG_MK_RECURSIVE_DEPS_LIST)); do \ + (cd $$dep; echo "$$(basename "$$dep") $$(git rev-parse HEAD) $$(git describe --tags --exact-match 2>/dev/null || git symbolic-ref -q --short HEAD)") >> $(SOURCE_DIST)/git-revisions.txt; \ + done -install_bin: all install_dirs - cp -r ebin include LICENSE* INSTALL $(TARGET_DIR) +$(SOURCE_DIST).tar.gz: $(SOURCE_DIST) + $(gen_verbose) $(TAR) -cf - $(TAR_V) $(SOURCE_DIST) | $(GZIP) --best > $@ - chmod 0755 scripts/* - for script in rabbitmq-env rabbitmq-server rabbitmqctl rabbitmq-plugins rabbitmq-defaults; do \ - cp scripts/$$script $(TARGET_DIR)/sbin; \ - [ -e $(SBIN_DIR)/$$script ] || ln -s $(SCRIPTS_REL_PATH)/$$script $(SBIN_DIR)/$$script; \ - done +$(SOURCE_DIST).tar.bz2: $(SOURCE_DIST) + $(gen_verbose) $(TAR) -cf - $(TAR_V) $(SOURCE_DIST) | $(BZIP2) > $@ - mkdir -p $(TARGET_DIR)/$(PLUGINS_DIR) - [ -d "$(PLUGINS_DIR)" ] && cp $(PLUGINS_DIR)/*.ez $(PLUGINS_DIR)/README $(TARGET_DIR)/$(PLUGINS_DIR) || true +$(SOURCE_DIST).tar.xz: $(SOURCE_DIST) + $(gen_verbose) $(TAR) -cf - $(TAR_V) $(SOURCE_DIST) | $(XZ) > $@ -install_docs: docs_all install_dirs - for section in 1 5; do \ - mkdir -p $(MAN_DIR)/man$$section; \ - for manpage in $(DOCS_DIR)/*.$$section.gz; do \ - cp $$manpage $(MAN_DIR)/man$$section; \ - done; \ - done - if test "$(DOC_INSTALL_DIR)"; then \ - cp $(DOCS_DIR)/rabbitmq.config.example $(DOC_INSTALL_DIR)/rabbitmq.config.example; \ - fi - -install_dirs: - @ OK=true && \ - { [ -n "$(TARGET_DIR)" ] || { echo "Please set TARGET_DIR."; OK=false; }; } && \ - { [ -n "$(SBIN_DIR)" ] || { echo "Please set SBIN_DIR."; OK=false; }; } && \ - { [ -n "$(MAN_DIR)" ] || { echo "Please set MAN_DIR."; OK=false; }; } && $$OK - - mkdir -p $(TARGET_DIR)/sbin - mkdir -p $(SBIN_DIR) - mkdir -p $(MAN_DIR) - if test "$(DOC_INSTALL_DIR)"; then \ - mkdir -p $(DOC_INSTALL_DIR); \ - fi +$(SOURCE_DIST).zip: $(SOURCE_DIST) + $(verbose) rm -f $@ + $(gen_verbose) $(ZIP) -r $(ZIP_V) $@ $(SOURCE_DIST) -$(foreach XML,$(USAGES_XML),$(eval $(call usage_dep, $(XML)))) +clean:: clean-source-dist -# Note that all targets which depend on clean must have clean in their -# name. Also any target that doesn't depend on clean should not have -# clean in its name, unless you know that you don't need any of the -# automatic dependency generation for that target (e.g. cleandb). +clean-source-dist: + $(gen_verbose) rm -rf -- $(SOURCE_DIST_BASE)-* -# We want to load the dep file if *any* target *doesn't* contain -# "clean" - i.e. if removing all clean-like targets leaves something. +# -------------------------------------------------------------------- +# Installation. +# -------------------------------------------------------------------- -ifeq "$(MAKECMDGOALS)" "" -TESTABLEGOALS:=$(.DEFAULT_GOAL) -else -TESTABLEGOALS:=$(MAKECMDGOALS) -endif +.PHONY: install install-erlapp install-scripts install-bin install-man +.PHONY: install-windows install-windows-erlapp install-windows-scripts install-windows-docs -ifneq "$(strip $(patsubst clean%,,$(patsubst %clean,,$(TESTABLEGOALS))))" "" -include $(DEPS_FILE) -endif +DESTDIR ?= + +PREFIX ?= /usr/local +WINDOWS_PREFIX ?= rabbitmq-server-windows-$(VERSION) + +MANDIR ?= $(PREFIX)/share/man +RMQ_ROOTDIR ?= $(PREFIX)/lib/erlang +RMQ_BINDIR ?= $(RMQ_ROOTDIR)/bin +RMQ_LIBDIR ?= $(RMQ_ROOTDIR)/lib +RMQ_ERLAPP_DIR ?= $(RMQ_LIBDIR)/rabbitmq_server-$(VERSION) + +SCRIPTS = rabbitmq-defaults \ + rabbitmq-env \ + rabbitmq-server \ + rabbitmqctl \ + rabbitmq-plugins + +WINDOWS_SCRIPTS = rabbitmq-defaults.bat \ + rabbitmq-echopid.bat \ + rabbitmq-env.bat \ + rabbitmq-plugins.bat \ + rabbitmq-server.bat \ + rabbitmq-service.bat \ + rabbitmqctl.bat + +UNIX_TO_DOS ?= todos + +inst_verbose_0 = @echo " INST " $@; +inst_verbose = $(inst_verbose_$(V)) + +install: install-erlapp install-scripts + +install-erlapp: dist + $(verbose) mkdir -p $(DESTDIR)$(RMQ_ERLAPP_DIR) + $(inst_verbose) cp -a include ebin plugins LICENSE* INSTALL \ + $(DESTDIR)$(RMQ_ERLAPP_DIR) + $(verbose) echo "Put your EZs here and use rabbitmq-plugins to enable them." \ + > $(DESTDIR)$(RMQ_ERLAPP_DIR)/plugins/README + + @# rabbitmq-common provides headers too: copy them to + @# rabbitmq_server/include. + $(verbose) cp -a $(DEPS_DIR)/rabbit_common/include $(DESTDIR)$(RMQ_ERLAPP_DIR) + +install-scripts: + $(verbose) mkdir -p $(DESTDIR)$(RMQ_ERLAPP_DIR)/sbin + $(inst_verbose) for script in $(SCRIPTS); do \ + cp -a "scripts/$$script" "$(DESTDIR)$(RMQ_ERLAPP_DIR)/sbin"; \ + chmod 0755 "$(DESTDIR)$(RMQ_ERLAPP_DIR)/sbin/$$script"; \ + done + +# FIXME: We do symlinks to scripts in $(RMQ_ERLAPP_DIR))/sbin but this +# code assumes a certain hierarchy to make relative symlinks. +install-bin: install-scripts + $(verbose) mkdir -p $(DESTDIR)$(RMQ_BINDIR) + $(inst_verbose) for script in $(SCRIPTS); do \ + test -e $(DESTDIR)$(RMQ_BINDIR)/$$script || \ + ln -sf ../lib/$(notdir $(RMQ_ERLAPP_DIR))/sbin/$$script \ + $(DESTDIR)$(RMQ_BINDIR)/$$script; \ + done + +install-man: manpages + $(inst_verbose) sections=$$(ls -1 docs/*.[1-9] \ + | sed -E 's/.*\.([1-9])$$/\1/' | uniq | sort); \ + for section in $$sections; do \ + mkdir -p $(DESTDIR)$(MANDIR)/man$$section; \ + for manpage in $(DOCS_DIR)/*.$$section; do \ + gzip < $$manpage \ + > $(DESTDIR)$(MANDIR)/man$$section/$$(basename $$manpage).gz; \ + done; \ + done + +install-windows: install-windows-erlapp install-windows-scripts install-windows-docs + +install-windows-erlapp: dist + $(verbose) mkdir -p $(DESTDIR)$(WINDOWS_PREFIX) + $(inst_verbose) cp -a include ebin plugins LICENSE* INSTALL \ + $(DESTDIR)$(WINDOWS_PREFIX) + $(verbose) echo "Put your EZs here and use rabbitmq-plugins.bat to enable them." \ + > $(DESTDIR)$(WINDOWS_PREFIX)/plugins/README.txt + $(verbose) $(UNIX_TO_DOS) $(DESTDIR)$(WINDOWS_PREFIX)/plugins/README.txt + +# rabbitmq-common provides headers too: copy them to +# rabbitmq_server/include. + $(verbose) cp -a $(DEPS_DIR)/rabbit_common/include $(DESTDIR)$(WINDOWS_PREFIX) + +install-windows-scripts: + $(verbose) mkdir -p $(DESTDIR)$(WINDOWS_PREFIX)/sbin + $(inst_verbose) for script in $(WINDOWS_SCRIPTS); do \ + cp -a "scripts/$$script" "$(DESTDIR)$(WINDOWS_PREFIX)/sbin"; \ + chmod 0755 "$(DESTDIR)$(WINDOWS_PREFIX)/sbin/$$script"; \ + done + +install-windows-docs: install-windows-erlapp + $(verbose) mkdir -p $(DESTDIR)$(WINDOWS_PREFIX)/etc + $(inst_verbose) xmlto -o . xhtml-nochunks docs/rabbitmq-service.xml + $(verbose) elinks -dump -no-references -no-numbering rabbitmq-service.html \ + > $(DESTDIR)$(WINDOWS_PREFIX)/readme-service.txt + $(verbose) rm rabbitmq-service.html + $(verbose) cp -a docs/rabbitmq.config.example $(DESTDIR)$(WINDOWS_PREFIX)/etc + $(verbose) for file in $(DESTDIR)$(WINDOWS_PREFIX)/readme-service.txt \ + $(DESTDIR)$(WINDOWS_PREFIX)/LICENSE* $(DESTDIR)$(WINDOWS_PREFIX)/INSTALL \ + $(DESTDIR)$(WINDOWS_PREFIX)/etc/rabbitmq.config.example; do \ + $(UNIX_TO_DOS) "$$file"; \ + case "$$file" in \ + *.txt) ;; \ + *.example) ;; \ + *) mv "$$file" "$$file.txt" ;; \ + esac; \ + done -.PHONY: run-qc +# -------------------------------------------------------------------- +# Packaging. +# -------------------------------------------------------------------- + +.PHONY: packages package-deb \ + package-rpm package-rpm-fedora package-rpm-suse \ + package-windows package-standalone-macosx \ + package-generic-unix + +PACKAGES_DIR ?= $(abspath PACKAGES) + +# This variable is exported so sub-make instances know where to find the +# archive. +PACKAGES_SOURCE_DIST_FILE ?= $(firstword $(SOURCE_DIST_FILES)) + +packages: package-deb package-rpm package-windows package-standalone-macosx \ + package-generic-unix + @: + +package-deb: $(PACKAGES_SOURCE_DIST_FILE) + $(gen_verbose) $(MAKE) -C packaging/debs/Debian \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + all clean + +package-rpm: package-rpm-fedora package-rpm-suse + @: + +package-rpm-fedora: $(PACKAGES_SOURCE_DIST_FILE) + $(gen_verbose) $(MAKE) -C packaging/RPMS/Fedora \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + all clean + +package-rpm-suse: $(PACKAGES_SOURCE_DIST_FILE) + $(gen_verbose) $(MAKE) -C packaging/RPMS/Fedora \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + RPM_OS=suse \ + all clean + +package-windows: $(PACKAGES_SOURCE_DIST_FILE) + $(gen_verbose) $(MAKE) -C packaging/windows \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + all clean + $(verbose) $(MAKE) -C packaging/windows-exe \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + all clean + +package-standalone-macosx: $(PACKAGES_SOURCE_DIST_FILE) + $(gen_verbose) $(MAKE) -C packaging/standalone OS=mac \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + all clean + +package-generic-unix: $(PACKAGES_SOURCE_DIST_FILE) + $(gen_verbose) $(MAKE) -C packaging/generic-unix \ + SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) \ + PACKAGES_DIR=$(PACKAGES_DIR) \ + all clean diff --git a/build.config b/build.config new file mode 100644 index 0000000000..138af905ca --- /dev/null +++ b/build.config @@ -0,0 +1,40 @@ +# Do *not* comment or remove core modules +# unless you know what you are doing. +# +# Feel free to comment plugins out however. + +# Core modules. +core/core +index/* +core/index +core/deps + +# Plugins that must run before Erlang code gets compiled. +plugins/protobuffs + +# Core modules, continued. +core/erlc +core/docs +core/rel +core/test +core/compat + +# Plugins. +plugins/asciidoc +plugins/bootstrap +plugins/c_src +plugins/ci +plugins/ct +plugins/dialyzer +# plugins/edoc +plugins/elvis +plugins/erlydtl +plugins/escript +plugins/eunit +plugins/relx +plugins/shell +plugins/triq +plugins/xref + +# Plugins enhancing the functionality of other plugins. +plugins/cover diff --git a/calculate-relative b/calculate-relative deleted file mode 100755 index 3af18e8ff8..0000000000 --- a/calculate-relative +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# -# relpath.py -# R.Barran 30/08/2004 -# Retrieved from http://code.activestate.com/recipes/302594/ - -import os -import sys - -def relpath(target, base=os.curdir): - """ - Return a relative path to the target from either the current dir or an optional base dir. - Base can be a directory specified either as absolute or relative to current dir. - """ - - if not os.path.exists(target): - raise OSError, 'Target does not exist: '+target - - if not os.path.isdir(base): - raise OSError, 'Base is not a directory or does not exist: '+base - - base_list = (os.path.abspath(base)).split(os.sep) - target_list = (os.path.abspath(target)).split(os.sep) - - # On the windows platform the target may be on a completely different drive from the base. - if os.name in ['nt','dos','os2'] and base_list[0] <> target_list[0]: - raise OSError, 'Target is on a different drive to base. Target: '+target_list[0].upper()+', base: '+base_list[0].upper() - - # Starting from the filepath root, work out how much of the filepath is - # shared by base and target. - for i in range(min(len(base_list), len(target_list))): - if base_list[i] <> target_list[i]: break - else: - # If we broke out of the loop, i is pointing to the first differing path elements. - # If we didn't break out of the loop, i is pointing to identical path elements. - # Increment i so that in all cases it points to the first differing path elements. - i+=1 - - rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] - if (len(rel_list) == 0): - return "." - return os.path.join(*rel_list) - -if __name__ == "__main__": - print(relpath(sys.argv[1], sys.argv[2])) diff --git a/codegen.py b/codegen.py deleted file mode 100755 index 10829f29dc..0000000000 --- a/codegen.py +++ /dev/null @@ -1,596 +0,0 @@ -#!/usr/bin/env python2 -## 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 Pivotal Software, Inc. -## Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -## - -from __future__ import nested_scopes - -import sys -sys.path.append("../rabbitmq-codegen") # in case we're next to an experimental revision -sys.path.append("codegen") # in case we're building from a distribution package - -from amqp_codegen import * -import string -import re - -# Coming up with a proper encoding of AMQP tables in JSON is too much -# hassle at this stage. Given that the only default value we are -# interested in is for the empty table, we only support that. -def convertTable(d): - if len(d) == 0: - return "[]" - else: - raise Exception('Non-empty table defaults not supported ' + d) - -erlangDefaultValueTypeConvMap = { - bool : lambda x: str(x).lower(), - str : lambda x: "<<\"" + x + "\">>", - int : lambda x: str(x), - float : lambda x: str(x), - dict: convertTable, - unicode: lambda x: "<<\"" + x.encode("utf-8") + "\">>" -} - -def erlangize(s): - s = s.replace('-', '_') - s = s.replace(' ', '_') - return s - -AmqpMethod.erlangName = lambda m: "'" + erlangize(m.klass.name) + '.' + erlangize(m.name) + "'" - -AmqpClass.erlangName = lambda c: "'" + erlangize(c.name) + "'" - -def erlangConstantName(s): - return '_'.join(re.split('[- ]', s.upper())) - -class PackedMethodBitField: - def __init__(self, index): - self.index = index - self.domain = 'bit' - self.contents = [] - - def extend(self, f): - self.contents.append(f) - - def count(self): - return len(self.contents) - - def full(self): - return self.count() == 8 - -def multiLineFormat(things, prologue, separator, lineSeparator, epilogue, thingsPerLine = 4): - r = [prologue] - i = 0 - for t in things: - if i != 0: - if i % thingsPerLine == 0: - r += [lineSeparator] - else: - r += [separator] - r += [t] - i += 1 - r += [epilogue] - return "".join(r) - -def prettyType(typeName, subTypes, typesPerLine = 4): - """Pretty print a type signature made up of many alternative subtypes""" - sTs = multiLineFormat(subTypes, - "( ", " | ", "\n | ", " )", - thingsPerLine = typesPerLine) - return "-type(%s ::\n %s)." % (typeName, sTs) - -def printFileHeader(): - print """%% Autogenerated code. Do not edit. -%% -%% 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 Pivotal Software, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%%""" - -def genErl(spec): - def erlType(domain): - return erlangize(spec.resolveDomain(domain)) - - def fieldTypeList(fields): - return '[' + ', '.join([erlType(f.domain) for f in fields]) + ']' - - def fieldNameList(fields): - return '[' + ', '.join([erlangize(f.name) for f in fields]) + ']' - - def fieldTempList(fields): - return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']' - - def fieldMapList(fields): - return ', '.join([erlangize(f.name) + " = F" + str(f.index) for f in fields]) - - def genLookupMethodName(m): - print "lookup_method_name({%d, %d}) -> %s;" % (m.klass.index, m.index, m.erlangName()) - - def genLookupClassName(c): - print "lookup_class_name(%d) -> %s;" % (c.index, c.erlangName()) - - def genMethodId(m): - print "method_id(%s) -> {%d, %d};" % (m.erlangName(), m.klass.index, m.index) - - def genMethodHasContent(m): - print "method_has_content(%s) -> %s;" % (m.erlangName(), str(m.hasContent).lower()) - - def genMethodIsSynchronous(m): - hasNoWait = "nowait" in fieldNameList(m.arguments) - if m.isSynchronous and hasNoWait: - print "is_method_synchronous(#%s{nowait = NoWait}) -> not(NoWait);" % (m.erlangName()) - else: - print "is_method_synchronous(#%s{}) -> %s;" % (m.erlangName(), str(m.isSynchronous).lower()) - - def genMethodFieldTypes(m): - """Not currently used - may be useful in future?""" - print "method_fieldtypes(%s) -> %s;" % (m.erlangName(), fieldTypeList(m.arguments)) - - def genMethodFieldNames(m): - print "method_fieldnames(%s) -> %s;" % (m.erlangName(), fieldNameList(m.arguments)) - - def packMethodFields(fields): - packed = [] - bitfield = None - for f in fields: - if erlType(f.domain) == 'bit': - if not(bitfield) or bitfield.full(): - bitfield = PackedMethodBitField(f.index) - packed.append(bitfield) - bitfield.extend(f) - else: - bitfield = None - packed.append(f) - return packed - - def methodFieldFragment(f): - type = erlType(f.domain) - p = 'F' + str(f.index) - if type == 'shortstr': - return p+'Len:8/unsigned, '+p+':'+p+'Len/binary' - elif type == 'longstr': - return p+'Len:32/unsigned, '+p+':'+p+'Len/binary' - elif type == 'octet': - return p+':8/unsigned' - elif type == 'short': - return p+':16/unsigned' - elif type == 'long': - return p+':32/unsigned' - elif type == 'longlong': - return p+':64/unsigned' - elif type == 'timestamp': - return p+':64/unsigned' - elif type == 'bit': - return p+'Bits:8' - elif type == 'table': - return p+'Len:32/unsigned, '+p+'Tab:'+p+'Len/binary' - - def genFieldPostprocessing(packed, hasContent): - for f in packed: - type = erlType(f.domain) - if type == 'bit': - for index in range(f.count()): - print " F%d = ((F%dBits band %d) /= 0)," % \ - (f.index + index, - f.index, - 1 << index) - elif type == 'table': - print " F%d = rabbit_binary_parser:parse_table(F%dTab)," % \ - (f.index, f.index) - # We skip the check on content-bearing methods for - # speed. This is a sanity check, not a security thing. - elif type == 'shortstr' and not hasContent: - print " rabbit_binary_parser:assert_utf8(F%d)," % (f.index) - else: - pass - - def genMethodRecord(m): - print "method_record(%s) -> #%s{};" % (m.erlangName(), m.erlangName()) - - def genDecodeMethodFields(m): - packedFields = packMethodFields(m.arguments) - binaryPattern = ', '.join([methodFieldFragment(f) for f in packedFields]) - if binaryPattern: - restSeparator = ', ' - else: - restSeparator = '' - recordConstructorExpr = '#%s{%s}' % (m.erlangName(), fieldMapList(m.arguments)) - print "decode_method_fields(%s, <<%s>>) ->" % (m.erlangName(), binaryPattern) - genFieldPostprocessing(packedFields, m.hasContent) - print " %s;" % (recordConstructorExpr,) - - def genDecodeProperties(c): - def presentBin(fields): - ps = ', '.join(['P' + str(f.index) + ':1' for f in fields]) - return '<<' + ps + ', _:%d, R0/binary>>' % (16 - len(fields),) - def writePropFieldLine(field): - i = str(field.index) - if field.domain == 'bit': - print " {F%s, R%s} = {P%s =/= 0, R%s}," % \ - (i, str(field.index + 1), i, i) - else: - print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> ?%s_VAL(R%s, L%s, V%s, X%s) end," % \ - (i, str(field.index + 1), i, i, erlType(field.domain).upper(), i, i, i, i) - - if len(c.fields) == 0: - print "decode_properties(%d, <<>>) ->" % (c.index,) - else: - print ("decode_properties(%d, %s) ->" % - (c.index, presentBin(c.fields))) - for field in c.fields: - writePropFieldLine(field) - print " <<>> = %s," % ('R' + str(len(c.fields))) - print " #'P_%s'{%s};" % (erlangize(c.name), fieldMapList(c.fields)) - - def genFieldPreprocessing(packed): - for f in packed: - type = erlType(f.domain) - if type == 'bit': - print " F%dBits = (%s)," % \ - (f.index, - ' bor '.join(['(bitvalue(F%d) bsl %d)' % (x.index, x.index - f.index) - for x in f.contents])) - elif type == 'table': - print " F%dTab = rabbit_binary_generator:generate_table(F%d)," % (f.index, f.index) - print " F%dLen = size(F%dTab)," % (f.index, f.index) - elif type == 'shortstr': - print " F%dLen = shortstr_size(F%d)," % (f.index, f.index) - elif type == 'longstr': - print " F%dLen = size(F%d)," % (f.index, f.index) - else: - pass - - def genEncodeMethodFields(m): - packedFields = packMethodFields(m.arguments) - print "encode_method_fields(#%s{%s}) ->" % (m.erlangName(), fieldMapList(m.arguments)) - genFieldPreprocessing(packedFields) - print " <<%s>>;" % (', '.join([methodFieldFragment(f) for f in packedFields])) - - def genEncodeProperties(c): - def presentBin(fields): - ps = ', '.join(['P' + str(f.index) + ':1' for f in fields]) - return '<<' + ps + ', 0:%d>>' % (16 - len(fields),) - def writePropFieldLine(field): - i = str(field.index) - if field.domain == 'bit': - print " {P%s, R%s} = {F%s =:= 1, R%s}," % \ - (i, str(field.index + 1), i, i) - else: - print " {P%s, R%s} = if F%s =:= undefined -> {0, R%s}; true -> {1, [?%s_PROP(F%s, L%s) | R%s]} end," % \ - (i, str(field.index + 1), i, i, erlType(field.domain).upper(), i, i, i) - - print "encode_properties(#'P_%s'{%s}) ->" % (erlangize(c.name), fieldMapList(c.fields)) - if len(c.fields) == 0: - print " <<>>;" - else: - print " R0 = [<<>>]," - for field in c.fields: - writePropFieldLine(field) - print " list_to_binary([%s | lists:reverse(R%s)]);" % \ - (presentBin(c.fields), str(len(c.fields))) - - def messageConstantClass(cls): - # We do this because 0.8 uses "soft error" and 8.1 uses "soft-error". - return erlangConstantName(cls) - - def genLookupException(c,v,cls): - mCls = messageConstantClass(cls) - if mCls == 'SOFT_ERROR': genLookupException1(c,'false') - elif mCls == 'HARD_ERROR': genLookupException1(c, 'true') - elif mCls == '': pass - else: raise Exception('Unknown constant class' + cls) - - def genLookupException1(c,hardErrorBoolStr): - n = erlangConstantName(c) - print 'lookup_amqp_exception(%s) -> {%s, ?%s, <<"%s">>};' % \ - (n.lower(), hardErrorBoolStr, n, n) - - def genAmqpException(c,v,cls): - n = erlangConstantName(c) - print 'amqp_exception(?%s) -> %s;' % \ - (n, n.lower()) - - methods = spec.allMethods() - - printFileHeader() - module = "rabbit_framing_amqp_%d_%d" % (spec.major, spec.minor) - if spec.revision != 0: - module = "%s_%d" % (module, spec.revision) - if module == "rabbit_framing_amqp_8_0": - module = "rabbit_framing_amqp_0_8" - print "-module(%s)." % module - print """-include("rabbit_framing.hrl"). - --export([version/0]). --export([lookup_method_name/1]). --export([lookup_class_name/1]). - --export([method_id/1]). --export([method_has_content/1]). --export([is_method_synchronous/1]). --export([method_record/1]). --export([method_fieldnames/1]). --export([decode_method_fields/2]). --export([decode_properties/2]). --export([encode_method_fields/1]). --export([encode_properties/1]). --export([lookup_amqp_exception/1]). --export([amqp_exception/1]). - -""" - print "%% Various types" - print "-ifdef(use_specs)." - - print """-export_type([amqp_field_type/0, amqp_property_type/0, - amqp_table/0, amqp_array/0, amqp_value/0, - amqp_method_name/0, amqp_method/0, amqp_method_record/0, - amqp_method_field_name/0, amqp_property_record/0, - amqp_exception/0, amqp_exception_code/0, amqp_class_id/0]). - --type(amqp_field_type() :: - 'longstr' | 'signedint' | 'decimal' | 'timestamp' | - 'table' | 'byte' | 'double' | 'float' | 'long' | - 'short' | 'bool' | 'binary' | 'void' | 'array'). --type(amqp_property_type() :: - 'shortstr' | 'longstr' | 'octet' | 'short' | 'long' | - 'longlong' | 'timestamp' | 'bit' | 'table'). - --type(amqp_table() :: [{binary(), amqp_field_type(), amqp_value()}]). --type(amqp_array() :: [{amqp_field_type(), amqp_value()}]). --type(amqp_value() :: binary() | % longstr - integer() | % signedint - {non_neg_integer(), non_neg_integer()} | % decimal - amqp_table() | - amqp_array() | - byte() | % byte - float() | % double - integer() | % long - integer() | % short - boolean() | % bool - binary() | % binary - 'undefined' | % void - non_neg_integer() % timestamp - ). -""" - - print prettyType("amqp_method_name()", - [m.erlangName() for m in methods]) - print prettyType("amqp_method()", - ["{%s, %s}" % (m.klass.index, m.index) for m in methods], - 6) - print prettyType("amqp_method_record()", - ["#%s{}" % (m.erlangName()) for m in methods]) - fieldNames = set() - for m in methods: - fieldNames.update(m.arguments) - fieldNames = [erlangize(f.name) for f in fieldNames] - print prettyType("amqp_method_field_name()", - fieldNames) - print prettyType("amqp_property_record()", - ["#'P_%s'{}" % erlangize(c.name) for c in spec.allClasses()]) - print prettyType("amqp_exception()", - ["'%s'" % erlangConstantName(c).lower() for (c, v, cls) in spec.constants]) - print prettyType("amqp_exception_code()", - ["%i" % v for (c, v, cls) in spec.constants]) - classIds = set() - for m in spec.allMethods(): - classIds.add(m.klass.index) - print prettyType("amqp_class_id()", - ["%i" % ci for ci in classIds]) - print prettyType("amqp_class_name()", - ["%s" % c.erlangName() for c in spec.allClasses()]) - print "-endif. % use_specs" - - print """ -%% Method signatures --ifdef(use_specs). --spec(version/0 :: () -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}). --spec(lookup_method_name/1 :: (amqp_method()) -> amqp_method_name()). --spec(lookup_class_name/1 :: (amqp_class_id()) -> amqp_class_name()). --spec(method_id/1 :: (amqp_method_name()) -> amqp_method()). --spec(method_has_content/1 :: (amqp_method_name()) -> boolean()). --spec(is_method_synchronous/1 :: (amqp_method_record()) -> boolean()). --spec(method_record/1 :: (amqp_method_name()) -> amqp_method_record()). --spec(method_fieldnames/1 :: (amqp_method_name()) -> [amqp_method_field_name()]). --spec(decode_method_fields/2 :: - (amqp_method_name(), binary()) -> amqp_method_record() | rabbit_types:connection_exit()). --spec(decode_properties/2 :: (non_neg_integer(), binary()) -> amqp_property_record()). --spec(encode_method_fields/1 :: (amqp_method_record()) -> binary()). --spec(encode_properties/1 :: (amqp_property_record()) -> binary()). --spec(lookup_amqp_exception/1 :: (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}). --spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()). --endif. % use_specs - -bitvalue(true) -> 1; -bitvalue(false) -> 0; -bitvalue(undefined) -> 0. - -shortstr_size(S) -> - case size(S) of - Len when Len =< 255 -> Len; - _ -> exit(method_field_shortstr_overflow) - end. - --define(SHORTSTR_VAL(R, L, V, X), - begin - <<L:8/unsigned, V:L/binary, X/binary>> = R, - {V, X} - end). - --define(LONGSTR_VAL(R, L, V, X), - begin - <<L:32/unsigned, V:L/binary, X/binary>> = R, - {V, X} - end). - --define(SHORT_VAL(R, L, V, X), - begin - <<V:8/unsigned, X/binary>> = R, - {V, X} - end). - --define(LONG_VAL(R, L, V, X), - begin - <<V:32/unsigned, X/binary>> = R, - {V, X} - end). - --define(LONGLONG_VAL(R, L, V, X), - begin - <<V:64/unsigned, X/binary>> = R, - {V, X} - end). - --define(OCTET_VAL(R, L, V, X), - begin - <<V:8/unsigned, X/binary>> = R, - {V, X} - end). - --define(TABLE_VAL(R, L, V, X), - begin - <<L:32/unsigned, V:L/binary, X/binary>> = R, - {rabbit_binary_parser:parse_table(V), X} - end). - --define(TIMESTAMP_VAL(R, L, V, X), - begin - <<V:64/unsigned, X/binary>> = R, - {V, X} - end). - --define(SHORTSTR_PROP(X, L), - begin - L = size(X), - if L < 256 -> <<L:8, X:L/binary>>; - true -> exit(content_properties_shortstr_overflow) - end - end). - --define(LONGSTR_PROP(X, L), - begin - L = size(X), - <<L:32, X:L/binary>> - end). - --define(OCTET_PROP(X, L), <<X:8/unsigned>>). --define(SHORT_PROP(X, L), <<X:16/unsigned>>). --define(LONG_PROP(X, L), <<X:32/unsigned>>). --define(LONGLONG_PROP(X, L), <<X:64/unsigned>>). --define(TIMESTAMP_PROP(X, L), <<X:64/unsigned>>). - --define(TABLE_PROP(X, T), - begin - T = rabbit_binary_generator:generate_table(X), - <<(size(T)):32, T/binary>> - end). -""" - version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) - if version == '{8, 0, 0}': version = '{0, 8, 0}' - print "version() -> %s." % (version) - - for m in methods: genLookupMethodName(m) - print "lookup_method_name({_ClassId, _MethodId} = Id) -> exit({unknown_method_id, Id})." - - for c in spec.allClasses(): genLookupClassName(c) - print "lookup_class_name(ClassId) -> exit({unknown_class_id, ClassId})." - - for m in methods: genMethodId(m) - print "method_id(Name) -> exit({unknown_method_name, Name})." - - for m in methods: genMethodHasContent(m) - print "method_has_content(Name) -> exit({unknown_method_name, Name})." - - for m in methods: genMethodIsSynchronous(m) - print "is_method_synchronous(Name) -> exit({unknown_method_name, Name})." - - for m in methods: genMethodRecord(m) - print "method_record(Name) -> exit({unknown_method_name, Name})." - - for m in methods: genMethodFieldNames(m) - print "method_fieldnames(Name) -> exit({unknown_method_name, Name})." - - for m in methods: genDecodeMethodFields(m) - print "decode_method_fields(Name, BinaryFields) ->" - print " rabbit_misc:frame_error(Name, BinaryFields)." - - for c in spec.allClasses(): genDecodeProperties(c) - print "decode_properties(ClassId, _BinaryFields) -> exit({unknown_class_id, ClassId})." - - for m in methods: genEncodeMethodFields(m) - print "encode_method_fields(Record) -> exit({unknown_method_name, element(1, Record)})." - - for c in spec.allClasses(): genEncodeProperties(c) - print "encode_properties(Record) -> exit({unknown_properties_record, Record})." - - for (c,v,cls) in spec.constants: genLookupException(c,v,cls) - print "lookup_amqp_exception(Code) ->" - print " rabbit_log:warning(\"Unknown AMQP error code '~p'~n\", [Code])," - print " {true, ?INTERNAL_ERROR, <<\"INTERNAL_ERROR\">>}." - - for(c,v,cls) in spec.constants: genAmqpException(c,v,cls) - print "amqp_exception(_Code) -> undefined." - -def genHrl(spec): - def fieldNameList(fields): - return ', '.join([erlangize(f.name) for f in fields]) - - def fieldNameListDefaults(fields): - def fillField(field): - result = erlangize(f.name) - if field.defaultvalue != None: - conv_fn = erlangDefaultValueTypeConvMap[type(field.defaultvalue)] - result += ' = ' + conv_fn(field.defaultvalue) - return result - return ', '.join([fillField(f) for f in fields]) - - methods = spec.allMethods() - - printFileHeader() - print "-define(PROTOCOL_PORT, %d)." % (spec.port) - - for (c,v,cls) in spec.constants: - print "-define(%s, %s)." % (erlangConstantName(c), v) - - print "%% Method field records." - for m in methods: - print "-record(%s, {%s})." % (m.erlangName(), fieldNameListDefaults(m.arguments)) - - print "%% Class property records." - for c in spec.allClasses(): - print "-record('P_%s', {%s})." % (erlangize(c.name), fieldNameList(c.fields)) - - -def generateErl(specPath): - genErl(AmqpSpec(specPath)) - -def generateHrl(specPath): - genHrl(AmqpSpec(specPath)) - -if __name__ == "__main__": - do_main_dict({"header": generateHrl, - "body": generateErl}) - diff --git a/packaging/common/README b/docs/README-for-packages index 35a1523ac3..35a1523ac3 100644 --- a/packaging/common/README +++ b/docs/README-for-packages diff --git a/erlang.mk b/erlang.mk new file mode 100644 index 0000000000..c82720e3db --- /dev/null +++ b/erlang.mk @@ -0,0 +1,6384 @@ +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +.PHONY: all app deps search rel docs install-docs check tests clean distclean help erlang-mk + +ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST))) + +ERLANG_MK_VERSION = 1.2.0-837-gc291aa8-dirty + +# Core configuration. + +PROJECT ?= $(notdir $(CURDIR)) +PROJECT := $(strip $(PROJECT)) + +PROJECT_VERSION ?= rolling + +# Verbosity. + +V ?= 0 + +verbose_0 = @ +verbose = $(verbose_$(V)) + +gen_verbose_0 = @echo " GEN " $@; +gen_verbose = $(gen_verbose_$(V)) + +# Temporary files directory. + +ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk +export ERLANG_MK_TMP + +# "erl" command. + +ERL = erl +A0 -noinput -boot start_clean + +# Platform detection. + +ifeq ($(PLATFORM),) +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Linux) +PLATFORM = linux +else ifeq ($(UNAME_S),Darwin) +PLATFORM = darwin +else ifeq ($(UNAME_S),SunOS) +PLATFORM = solaris +else ifeq ($(UNAME_S),GNU) +PLATFORM = gnu +else ifeq ($(UNAME_S),FreeBSD) +PLATFORM = freebsd +else ifeq ($(UNAME_S),NetBSD) +PLATFORM = netbsd +else ifeq ($(UNAME_S),OpenBSD) +PLATFORM = openbsd +else ifeq ($(UNAME_S),DragonFly) +PLATFORM = dragonfly +else ifeq ($(shell uname -o),Msys) +PLATFORM = msys2 +else +$(error Unable to detect platform. Please open a ticket with the output of uname -a.) +endif + +export PLATFORM +endif + +# Core targets. + +.NOTPARALLEL: + +all:: deps app rel + +# Noop to avoid a Make warning when there's nothing to do. +rel:: + $(verbose) : + +check:: clean app tests + +clean:: clean-crashdump + +clean-crashdump: +ifneq ($(wildcard erl_crash.dump),) + $(gen_verbose) rm -f erl_crash.dump +endif + +distclean:: clean distclean-tmp + +distclean-tmp: + $(gen_verbose) rm -rf $(ERLANG_MK_TMP) + +help:: + $(verbose) printf "%s\n" \ + "erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \ + "Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \ + "" \ + "Usage: [V=1] $(MAKE) [target]..." \ + "" \ + "Core targets:" \ + " all Run deps, app and rel targets in that order" \ + " app Compile the project" \ + " deps Fetch dependencies (if needed) and compile them" \ + " fetch-deps Fetch dependencies (if needed) without compiling them" \ + " list-deps Fetch dependencies (if needed) and list them" \ + " search q=... Search for a package in the built-in index" \ + " rel Build a release for this project, if applicable" \ + " docs Build the documentation for this project" \ + " install-docs Install the man pages for this project" \ + " check Compile and run all tests and analysis for this project" \ + " tests Run the tests for this project" \ + " clean Delete temporary and output files from most targets" \ + " distclean Delete all temporary and output files" \ + " help Display this help and exit" \ + " erlang-mk Update erlang.mk to the latest version" + +# Core functions. + +empty := +space := $(empty) $(empty) +tab := $(empty) $(empty) +comma := , + +define newline + + +endef + +define comma_list +$(subst $(space),$(comma),$(strip $(1))) +endef + +# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy. +define erlang +$(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk +endef + +ifeq ($(PLATFORM),msys2) +core_native_path = $(subst \,\\\\,$(shell cygpath -w $1)) +else +core_native_path = $1 +endif + +ifeq ($(shell which wget 2>/dev/null | wc -l), 1) +define core_http_get + wget --no-check-certificate -O $(1) $(2)|| rm $(1) +endef +else +define core_http_get.erl + ssl:start(), + inets:start(), + case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of + {ok, {{_, 200, _}, _, Body}} -> + case file:write_file("$(1)", Body) of + ok -> ok; + {error, R1} -> halt(R1) + end; + {error, R2} -> + halt(R2) + end, + halt(0). +endef + +define core_http_get + $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2)) +endef +endif + +core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1))) + +core_find = $(if $(wildcard $1),$(shell find $(1:%/=%) -type f -name $(subst *,\*,$2))) + +core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1))))))))))))))))))))))))))) + +core_ls = $(filter-out $(1),$(shell echo $(1))) + +# @todo Use a solution that does not require using perl. +core_relpath = $(shell perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $1 $2) + +# Automated update. + +ERLANG_MK_REPO ?= https://github.com/ninenines/erlang.mk +ERLANG_MK_COMMIT ?= +ERLANG_MK_BUILD_CONFIG ?= build.config +ERLANG_MK_BUILD_DIR ?= .erlang.mk.build + +erlang-mk: + git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR) + if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi + cd $(ERLANG_MK_BUILD_DIR) && $(if $(ERLANG_MK_COMMIT),git checkout $(ERLANG_MK_COMMIT) &&) $(MAKE) + cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk + rm -rf $(ERLANG_MK_BUILD_DIR) + +# The erlang.mk package index is bundled in the default erlang.mk build. +# Search for the string "copyright" to skip to the rest of the code. + +PACKAGES += aberth +pkg_aberth_name = aberth +pkg_aberth_description = Generic BERT-RPC server in Erlang +pkg_aberth_homepage = https://github.com/a13x/aberth +pkg_aberth_fetch = git +pkg_aberth_repo = https://github.com/a13x/aberth +pkg_aberth_commit = master + +PACKAGES += active +pkg_active_name = active +pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running +pkg_active_homepage = https://github.com/proger/active +pkg_active_fetch = git +pkg_active_repo = https://github.com/proger/active +pkg_active_commit = master + +PACKAGES += actordb_core +pkg_actordb_core_name = actordb_core +pkg_actordb_core_description = ActorDB main source +pkg_actordb_core_homepage = http://www.actordb.com/ +pkg_actordb_core_fetch = git +pkg_actordb_core_repo = https://github.com/biokoda/actordb_core +pkg_actordb_core_commit = master + +PACKAGES += actordb_thrift +pkg_actordb_thrift_name = actordb_thrift +pkg_actordb_thrift_description = Thrift API for ActorDB +pkg_actordb_thrift_homepage = http://www.actordb.com/ +pkg_actordb_thrift_fetch = git +pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift +pkg_actordb_thrift_commit = master + +PACKAGES += aleppo +pkg_aleppo_name = aleppo +pkg_aleppo_description = Alternative Erlang Pre-Processor +pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo +pkg_aleppo_fetch = git +pkg_aleppo_repo = https://github.com/ErlyORM/aleppo +pkg_aleppo_commit = master + +PACKAGES += alog +pkg_alog_name = alog +pkg_alog_description = Simply the best logging framework for Erlang +pkg_alog_homepage = https://github.com/siberian-fast-food/alogger +pkg_alog_fetch = git +pkg_alog_repo = https://github.com/siberian-fast-food/alogger +pkg_alog_commit = master + +PACKAGES += amqp_client +pkg_amqp_client_name = amqp_client +pkg_amqp_client_description = RabbitMQ Erlang AMQP client +pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html +pkg_amqp_client_fetch = git +pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git +pkg_amqp_client_commit = master + +PACKAGES += annotations +pkg_annotations_name = annotations +pkg_annotations_description = Simple code instrumentation utilities +pkg_annotations_homepage = https://github.com/hyperthunk/annotations +pkg_annotations_fetch = git +pkg_annotations_repo = https://github.com/hyperthunk/annotations +pkg_annotations_commit = master + +PACKAGES += antidote +pkg_antidote_name = antidote +pkg_antidote_description = Large-scale computation without synchronisation +pkg_antidote_homepage = https://syncfree.lip6.fr/ +pkg_antidote_fetch = git +pkg_antidote_repo = https://github.com/SyncFree/antidote +pkg_antidote_commit = master + +PACKAGES += apns +pkg_apns_name = apns +pkg_apns_description = Apple Push Notification Server for Erlang +pkg_apns_homepage = http://inaka.github.com/apns4erl +pkg_apns_fetch = git +pkg_apns_repo = https://github.com/inaka/apns4erl +pkg_apns_commit = 1.0.4 + +PACKAGES += azdht +pkg_azdht_name = azdht +pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang +pkg_azdht_homepage = https://github.com/arcusfelis/azdht +pkg_azdht_fetch = git +pkg_azdht_repo = https://github.com/arcusfelis/azdht +pkg_azdht_commit = master + +PACKAGES += backoff +pkg_backoff_name = backoff +pkg_backoff_description = Simple exponential backoffs in Erlang +pkg_backoff_homepage = https://github.com/ferd/backoff +pkg_backoff_fetch = git +pkg_backoff_repo = https://github.com/ferd/backoff +pkg_backoff_commit = master + +PACKAGES += barrel_tcp +pkg_barrel_tcp_name = barrel_tcp +pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang. +pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp +pkg_barrel_tcp_fetch = git +pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp +pkg_barrel_tcp_commit = master + +PACKAGES += basho_bench +pkg_basho_bench_name = basho_bench +pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for. +pkg_basho_bench_homepage = https://github.com/basho/basho_bench +pkg_basho_bench_fetch = git +pkg_basho_bench_repo = https://github.com/basho/basho_bench +pkg_basho_bench_commit = master + +PACKAGES += bcrypt +pkg_bcrypt_name = bcrypt +pkg_bcrypt_description = Bcrypt Erlang / C library +pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt +pkg_bcrypt_fetch = git +pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt +pkg_bcrypt_commit = master + +PACKAGES += beam +pkg_beam_name = beam +pkg_beam_description = BEAM emulator written in Erlang +pkg_beam_homepage = https://github.com/tonyrog/beam +pkg_beam_fetch = git +pkg_beam_repo = https://github.com/tonyrog/beam +pkg_beam_commit = master + +PACKAGES += beanstalk +pkg_beanstalk_name = beanstalk +pkg_beanstalk_description = An Erlang client for beanstalkd +pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk +pkg_beanstalk_fetch = git +pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk +pkg_beanstalk_commit = master + +PACKAGES += bear +pkg_bear_name = bear +pkg_bear_description = a set of statistics functions for erlang +pkg_bear_homepage = https://github.com/boundary/bear +pkg_bear_fetch = git +pkg_bear_repo = https://github.com/boundary/bear +pkg_bear_commit = master + +PACKAGES += bertconf +pkg_bertconf_name = bertconf +pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded +pkg_bertconf_homepage = https://github.com/ferd/bertconf +pkg_bertconf_fetch = git +pkg_bertconf_repo = https://github.com/ferd/bertconf +pkg_bertconf_commit = master + +PACKAGES += bifrost +pkg_bifrost_name = bifrost +pkg_bifrost_description = Erlang FTP Server Framework +pkg_bifrost_homepage = https://github.com/thorstadt/bifrost +pkg_bifrost_fetch = git +pkg_bifrost_repo = https://github.com/thorstadt/bifrost +pkg_bifrost_commit = master + +PACKAGES += binpp +pkg_binpp_name = binpp +pkg_binpp_description = Erlang Binary Pretty Printer +pkg_binpp_homepage = https://github.com/jtendo/binpp +pkg_binpp_fetch = git +pkg_binpp_repo = https://github.com/jtendo/binpp +pkg_binpp_commit = master + +PACKAGES += bisect +pkg_bisect_name = bisect +pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang +pkg_bisect_homepage = https://github.com/knutin/bisect +pkg_bisect_fetch = git +pkg_bisect_repo = https://github.com/knutin/bisect +pkg_bisect_commit = master + +PACKAGES += bitcask +pkg_bitcask_name = bitcask +pkg_bitcask_description = because you need another a key/value storage engine +pkg_bitcask_homepage = https://github.com/basho/bitcask +pkg_bitcask_fetch = git +pkg_bitcask_repo = https://github.com/basho/bitcask +pkg_bitcask_commit = master + +PACKAGES += bitstore +pkg_bitstore_name = bitstore +pkg_bitstore_description = A document based ontology development environment +pkg_bitstore_homepage = https://github.com/bdionne/bitstore +pkg_bitstore_fetch = git +pkg_bitstore_repo = https://github.com/bdionne/bitstore +pkg_bitstore_commit = master + +PACKAGES += bootstrap +pkg_bootstrap_name = bootstrap +pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application. +pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap +pkg_bootstrap_fetch = git +pkg_bootstrap_repo = https://github.com/schlagert/bootstrap +pkg_bootstrap_commit = master + +PACKAGES += boss +pkg_boss_name = boss +pkg_boss_description = Erlang web MVC, now featuring Comet +pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss +pkg_boss_fetch = git +pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss +pkg_boss_commit = master + +PACKAGES += boss_db +pkg_boss_db_name = boss_db +pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang +pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db +pkg_boss_db_fetch = git +pkg_boss_db_repo = https://github.com/ErlyORM/boss_db +pkg_boss_db_commit = master + +PACKAGES += bson +pkg_bson_name = bson +pkg_bson_description = BSON documents in Erlang, see bsonspec.org +pkg_bson_homepage = https://github.com/comtihon/bson-erlang +pkg_bson_fetch = git +pkg_bson_repo = https://github.com/comtihon/bson-erlang +pkg_bson_commit = master + +PACKAGES += bullet +pkg_bullet_name = bullet +pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy. +pkg_bullet_homepage = http://ninenines.eu +pkg_bullet_fetch = git +pkg_bullet_repo = https://github.com/extend/bullet +pkg_bullet_commit = master + +PACKAGES += cache +pkg_cache_name = cache +pkg_cache_description = Erlang in-memory cache +pkg_cache_homepage = https://github.com/fogfish/cache +pkg_cache_fetch = git +pkg_cache_repo = https://github.com/fogfish/cache +pkg_cache_commit = master + +PACKAGES += cake +pkg_cake_name = cake +pkg_cake_description = Really simple terminal colorization +pkg_cake_homepage = https://github.com/darach/cake-erl +pkg_cake_fetch = git +pkg_cake_repo = https://github.com/darach/cake-erl +pkg_cake_commit = v0.1.2 + +PACKAGES += carotene +pkg_carotene_name = carotene +pkg_carotene_description = Real-time server +pkg_carotene_homepage = https://github.com/carotene/carotene +pkg_carotene_fetch = git +pkg_carotene_repo = https://github.com/carotene/carotene +pkg_carotene_commit = master + +PACKAGES += cberl +pkg_cberl_name = cberl +pkg_cberl_description = NIF based Erlang bindings for Couchbase +pkg_cberl_homepage = https://github.com/chitika/cberl +pkg_cberl_fetch = git +pkg_cberl_repo = https://github.com/chitika/cberl +pkg_cberl_commit = master + +PACKAGES += cecho +pkg_cecho_name = cecho +pkg_cecho_description = An ncurses library for Erlang +pkg_cecho_homepage = https://github.com/mazenharake/cecho +pkg_cecho_fetch = git +pkg_cecho_repo = https://github.com/mazenharake/cecho +pkg_cecho_commit = master + +PACKAGES += cferl +pkg_cferl_name = cferl +pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client +pkg_cferl_homepage = https://github.com/ddossot/cferl +pkg_cferl_fetch = git +pkg_cferl_repo = https://github.com/ddossot/cferl +pkg_cferl_commit = master + +PACKAGES += chaos_monkey +pkg_chaos_monkey_name = chaos_monkey +pkg_chaos_monkey_description = This is The CHAOS MONKEY. It will kill your processes. +pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey +pkg_chaos_monkey_fetch = git +pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey +pkg_chaos_monkey_commit = master + +PACKAGES += check_node +pkg_check_node_name = check_node +pkg_check_node_description = Nagios Scripts for monitoring Riak +pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios +pkg_check_node_fetch = git +pkg_check_node_repo = https://github.com/basho-labs/riak_nagios +pkg_check_node_commit = master + +PACKAGES += chronos +pkg_chronos_name = chronos +pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests. +pkg_chronos_homepage = https://github.com/lehoff/chronos +pkg_chronos_fetch = git +pkg_chronos_repo = https://github.com/lehoff/chronos +pkg_chronos_commit = master + +PACKAGES += cl +pkg_cl_name = cl +pkg_cl_description = OpenCL binding for Erlang +pkg_cl_homepage = https://github.com/tonyrog/cl +pkg_cl_fetch = git +pkg_cl_repo = https://github.com/tonyrog/cl +pkg_cl_commit = master + +PACKAGES += classifier +pkg_classifier_name = classifier +pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier +pkg_classifier_homepage = https://github.com/inaka/classifier +pkg_classifier_fetch = git +pkg_classifier_repo = https://github.com/inaka/classifier +pkg_classifier_commit = master + +PACKAGES += clique +pkg_clique_name = clique +pkg_clique_description = CLI Framework for Erlang +pkg_clique_homepage = https://github.com/basho/clique +pkg_clique_fetch = git +pkg_clique_repo = https://github.com/basho/clique +pkg_clique_commit = develop + +PACKAGES += cloudi_core +pkg_cloudi_core_name = cloudi_core +pkg_cloudi_core_description = CloudI internal service runtime +pkg_cloudi_core_homepage = http://cloudi.org/ +pkg_cloudi_core_fetch = git +pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core +pkg_cloudi_core_commit = master + +PACKAGES += cloudi_service_api_requests +pkg_cloudi_service_api_requests_name = cloudi_service_api_requests +pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support) +pkg_cloudi_service_api_requests_homepage = http://cloudi.org/ +pkg_cloudi_service_api_requests_fetch = git +pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests +pkg_cloudi_service_api_requests_commit = master + +PACKAGES += cloudi_service_db +pkg_cloudi_service_db_name = cloudi_service_db +pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic) +pkg_cloudi_service_db_homepage = http://cloudi.org/ +pkg_cloudi_service_db_fetch = git +pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db +pkg_cloudi_service_db_commit = master + +PACKAGES += cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service +pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/ +pkg_cloudi_service_db_cassandra_fetch = git +pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_commit = master + +PACKAGES += cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service +pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_cassandra_cql_fetch = git +pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_commit = master + +PACKAGES += cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service +pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/ +pkg_cloudi_service_db_couchdb_fetch = git +pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_commit = master + +PACKAGES += cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service +pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/ +pkg_cloudi_service_db_elasticsearch_fetch = git +pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_commit = master + +PACKAGES += cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_description = memcached CloudI Service +pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/ +pkg_cloudi_service_db_memcached_fetch = git +pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_commit = master + +PACKAGES += cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_description = MySQL CloudI Service +pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_mysql_fetch = git +pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_commit = master + +PACKAGES += cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service +pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_pgsql_fetch = git +pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_commit = master + +PACKAGES += cloudi_service_db_riak +pkg_cloudi_service_db_riak_name = cloudi_service_db_riak +pkg_cloudi_service_db_riak_description = Riak CloudI Service +pkg_cloudi_service_db_riak_homepage = http://cloudi.org/ +pkg_cloudi_service_db_riak_fetch = git +pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak +pkg_cloudi_service_db_riak_commit = master + +PACKAGES += cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service +pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/ +pkg_cloudi_service_db_tokyotyrant_fetch = git +pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_commit = master + +PACKAGES += cloudi_service_filesystem +pkg_cloudi_service_filesystem_name = cloudi_service_filesystem +pkg_cloudi_service_filesystem_description = Filesystem CloudI Service +pkg_cloudi_service_filesystem_homepage = http://cloudi.org/ +pkg_cloudi_service_filesystem_fetch = git +pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem +pkg_cloudi_service_filesystem_commit = master + +PACKAGES += cloudi_service_http_client +pkg_cloudi_service_http_client_name = cloudi_service_http_client +pkg_cloudi_service_http_client_description = HTTP client CloudI Service +pkg_cloudi_service_http_client_homepage = http://cloudi.org/ +pkg_cloudi_service_http_client_fetch = git +pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client +pkg_cloudi_service_http_client_commit = master + +PACKAGES += cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service +pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/ +pkg_cloudi_service_http_cowboy_fetch = git +pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_commit = master + +PACKAGES += cloudi_service_http_elli +pkg_cloudi_service_http_elli_name = cloudi_service_http_elli +pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service +pkg_cloudi_service_http_elli_homepage = http://cloudi.org/ +pkg_cloudi_service_http_elli_fetch = git +pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli +pkg_cloudi_service_http_elli_commit = master + +PACKAGES += cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service +pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/ +pkg_cloudi_service_map_reduce_fetch = git +pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_commit = master + +PACKAGES += cloudi_service_oauth1 +pkg_cloudi_service_oauth1_name = cloudi_service_oauth1 +pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service +pkg_cloudi_service_oauth1_homepage = http://cloudi.org/ +pkg_cloudi_service_oauth1_fetch = git +pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1 +pkg_cloudi_service_oauth1_commit = master + +PACKAGES += cloudi_service_queue +pkg_cloudi_service_queue_name = cloudi_service_queue +pkg_cloudi_service_queue_description = Persistent Queue Service +pkg_cloudi_service_queue_homepage = http://cloudi.org/ +pkg_cloudi_service_queue_fetch = git +pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue +pkg_cloudi_service_queue_commit = master + +PACKAGES += cloudi_service_quorum +pkg_cloudi_service_quorum_name = cloudi_service_quorum +pkg_cloudi_service_quorum_description = CloudI Quorum Service +pkg_cloudi_service_quorum_homepage = http://cloudi.org/ +pkg_cloudi_service_quorum_fetch = git +pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum +pkg_cloudi_service_quorum_commit = master + +PACKAGES += cloudi_service_router +pkg_cloudi_service_router_name = cloudi_service_router +pkg_cloudi_service_router_description = CloudI Router Service +pkg_cloudi_service_router_homepage = http://cloudi.org/ +pkg_cloudi_service_router_fetch = git +pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router +pkg_cloudi_service_router_commit = master + +PACKAGES += cloudi_service_tcp +pkg_cloudi_service_tcp_name = cloudi_service_tcp +pkg_cloudi_service_tcp_description = TCP CloudI Service +pkg_cloudi_service_tcp_homepage = http://cloudi.org/ +pkg_cloudi_service_tcp_fetch = git +pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp +pkg_cloudi_service_tcp_commit = master + +PACKAGES += cloudi_service_timers +pkg_cloudi_service_timers_name = cloudi_service_timers +pkg_cloudi_service_timers_description = Timers CloudI Service +pkg_cloudi_service_timers_homepage = http://cloudi.org/ +pkg_cloudi_service_timers_fetch = git +pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers +pkg_cloudi_service_timers_commit = master + +PACKAGES += cloudi_service_udp +pkg_cloudi_service_udp_name = cloudi_service_udp +pkg_cloudi_service_udp_description = UDP CloudI Service +pkg_cloudi_service_udp_homepage = http://cloudi.org/ +pkg_cloudi_service_udp_fetch = git +pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp +pkg_cloudi_service_udp_commit = master + +PACKAGES += cloudi_service_validate +pkg_cloudi_service_validate_name = cloudi_service_validate +pkg_cloudi_service_validate_description = CloudI Validate Service +pkg_cloudi_service_validate_homepage = http://cloudi.org/ +pkg_cloudi_service_validate_fetch = git +pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate +pkg_cloudi_service_validate_commit = master + +PACKAGES += cloudi_service_zeromq +pkg_cloudi_service_zeromq_name = cloudi_service_zeromq +pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service +pkg_cloudi_service_zeromq_homepage = http://cloudi.org/ +pkg_cloudi_service_zeromq_fetch = git +pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq +pkg_cloudi_service_zeromq_commit = master + +PACKAGES += cluster_info +pkg_cluster_info_name = cluster_info +pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app +pkg_cluster_info_homepage = https://github.com/basho/cluster_info +pkg_cluster_info_fetch = git +pkg_cluster_info_repo = https://github.com/basho/cluster_info +pkg_cluster_info_commit = master + +PACKAGES += color +pkg_color_name = color +pkg_color_description = ANSI colors for your Erlang +pkg_color_homepage = https://github.com/julianduque/erlang-color +pkg_color_fetch = git +pkg_color_repo = https://github.com/julianduque/erlang-color +pkg_color_commit = master + +PACKAGES += confetti +pkg_confetti_name = confetti +pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids +pkg_confetti_homepage = https://github.com/jtendo/confetti +pkg_confetti_fetch = git +pkg_confetti_repo = https://github.com/jtendo/confetti +pkg_confetti_commit = master + +PACKAGES += couchbeam +pkg_couchbeam_name = couchbeam +pkg_couchbeam_description = Apache CouchDB client in Erlang +pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam +pkg_couchbeam_fetch = git +pkg_couchbeam_repo = https://github.com/benoitc/couchbeam +pkg_couchbeam_commit = master + +PACKAGES += covertool +pkg_covertool_name = covertool +pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports +pkg_covertool_homepage = https://github.com/idubrov/covertool +pkg_covertool_fetch = git +pkg_covertool_repo = https://github.com/idubrov/covertool +pkg_covertool_commit = master + +PACKAGES += cowboy +pkg_cowboy_name = cowboy +pkg_cowboy_description = Small, fast and modular HTTP server. +pkg_cowboy_homepage = http://ninenines.eu +pkg_cowboy_fetch = git +pkg_cowboy_repo = https://github.com/ninenines/cowboy +pkg_cowboy_commit = 1.0.1 + +PACKAGES += cowdb +pkg_cowdb_name = cowdb +pkg_cowdb_description = Pure Key/Value database library for Erlang Applications +pkg_cowdb_homepage = https://github.com/refuge/cowdb +pkg_cowdb_fetch = git +pkg_cowdb_repo = https://github.com/refuge/cowdb +pkg_cowdb_commit = master + +PACKAGES += cowlib +pkg_cowlib_name = cowlib +pkg_cowlib_description = Support library for manipulating Web protocols. +pkg_cowlib_homepage = http://ninenines.eu +pkg_cowlib_fetch = git +pkg_cowlib_repo = https://github.com/ninenines/cowlib +pkg_cowlib_commit = 1.0.1 + +PACKAGES += cpg +pkg_cpg_name = cpg +pkg_cpg_description = CloudI Process Groups +pkg_cpg_homepage = https://github.com/okeuday/cpg +pkg_cpg_fetch = git +pkg_cpg_repo = https://github.com/okeuday/cpg +pkg_cpg_commit = master + +PACKAGES += cqerl +pkg_cqerl_name = cqerl +pkg_cqerl_description = Native Erlang CQL client for Cassandra +pkg_cqerl_homepage = https://matehat.github.io/cqerl/ +pkg_cqerl_fetch = git +pkg_cqerl_repo = https://github.com/matehat/cqerl +pkg_cqerl_commit = master + +PACKAGES += cr +pkg_cr_name = cr +pkg_cr_description = Chain Replication +pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm +pkg_cr_fetch = git +pkg_cr_repo = https://github.com/spawnproc/cr +pkg_cr_commit = master + +PACKAGES += cuttlefish +pkg_cuttlefish_name = cuttlefish +pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me? +pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish +pkg_cuttlefish_fetch = git +pkg_cuttlefish_repo = https://github.com/basho/cuttlefish +pkg_cuttlefish_commit = master + +PACKAGES += damocles +pkg_damocles_name = damocles +pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box. +pkg_damocles_homepage = https://github.com/lostcolony/damocles +pkg_damocles_fetch = git +pkg_damocles_repo = https://github.com/lostcolony/damocles +pkg_damocles_commit = master + +PACKAGES += debbie +pkg_debbie_name = debbie +pkg_debbie_description = .DEB Built In Erlang +pkg_debbie_homepage = https://github.com/crownedgrouse/debbie +pkg_debbie_fetch = git +pkg_debbie_repo = https://github.com/crownedgrouse/debbie +pkg_debbie_commit = master + +PACKAGES += decimal +pkg_decimal_name = decimal +pkg_decimal_description = An Erlang decimal arithmetic library +pkg_decimal_homepage = https://github.com/tim/erlang-decimal +pkg_decimal_fetch = git +pkg_decimal_repo = https://github.com/tim/erlang-decimal +pkg_decimal_commit = master + +PACKAGES += detergent +pkg_detergent_name = detergent +pkg_detergent_description = An emulsifying Erlang SOAP library +pkg_detergent_homepage = https://github.com/devinus/detergent +pkg_detergent_fetch = git +pkg_detergent_repo = https://github.com/devinus/detergent +pkg_detergent_commit = master + +PACKAGES += detest +pkg_detest_name = detest +pkg_detest_description = Tool for running tests on a cluster of erlang nodes +pkg_detest_homepage = https://github.com/biokoda/detest +pkg_detest_fetch = git +pkg_detest_repo = https://github.com/biokoda/detest +pkg_detest_commit = master + +PACKAGES += dh_date +pkg_dh_date_name = dh_date +pkg_dh_date_description = Date formatting / parsing library for erlang +pkg_dh_date_homepage = https://github.com/daleharvey/dh_date +pkg_dh_date_fetch = git +pkg_dh_date_repo = https://github.com/daleharvey/dh_date +pkg_dh_date_commit = master + +PACKAGES += dhtcrawler +pkg_dhtcrawler_name = dhtcrawler +pkg_dhtcrawler_description = dhtcrawler is a DHT crawler written in erlang. It can join a DHT network and crawl many P2P torrents. +pkg_dhtcrawler_homepage = https://github.com/kevinlynx/dhtcrawler +pkg_dhtcrawler_fetch = git +pkg_dhtcrawler_repo = https://github.com/kevinlynx/dhtcrawler +pkg_dhtcrawler_commit = master + +PACKAGES += dirbusterl +pkg_dirbusterl_name = dirbusterl +pkg_dirbusterl_description = DirBuster successor in Erlang +pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl +pkg_dirbusterl_fetch = git +pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl +pkg_dirbusterl_commit = master + +PACKAGES += dispcount +pkg_dispcount_name = dispcount +pkg_dispcount_description = Erlang task dispatcher based on ETS counters. +pkg_dispcount_homepage = https://github.com/ferd/dispcount +pkg_dispcount_fetch = git +pkg_dispcount_repo = https://github.com/ferd/dispcount +pkg_dispcount_commit = master + +PACKAGES += dlhttpc +pkg_dlhttpc_name = dlhttpc +pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints +pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc +pkg_dlhttpc_fetch = git +pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc +pkg_dlhttpc_commit = master + +PACKAGES += dns +pkg_dns_name = dns +pkg_dns_description = Erlang DNS library +pkg_dns_homepage = https://github.com/aetrion/dns_erlang +pkg_dns_fetch = git +pkg_dns_repo = https://github.com/aetrion/dns_erlang +pkg_dns_commit = master + +PACKAGES += dnssd +pkg_dnssd_name = dnssd +pkg_dnssd_description = Erlang interface to Apple's Bonjour D NS Service Discovery implementation +pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang +pkg_dnssd_fetch = git +pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang +pkg_dnssd_commit = master + +PACKAGES += dtl +pkg_dtl_name = dtl +pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang. +pkg_dtl_homepage = https://github.com/oinksoft/dtl +pkg_dtl_fetch = git +pkg_dtl_repo = https://github.com/oinksoft/dtl +pkg_dtl_commit = master + +PACKAGES += dynamic_compile +pkg_dynamic_compile_name = dynamic_compile +pkg_dynamic_compile_description = compile and load erlang modules from string input +pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile +pkg_dynamic_compile_fetch = git +pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile +pkg_dynamic_compile_commit = master + +PACKAGES += e2 +pkg_e2_name = e2 +pkg_e2_description = Library to simply writing correct OTP applications. +pkg_e2_homepage = http://e2project.org +pkg_e2_fetch = git +pkg_e2_repo = https://github.com/gar1t/e2 +pkg_e2_commit = master + +PACKAGES += eamf +pkg_eamf_name = eamf +pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang +pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf +pkg_eamf_fetch = git +pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf +pkg_eamf_commit = master + +PACKAGES += eavro +pkg_eavro_name = eavro +pkg_eavro_description = Apache Avro encoder/decoder +pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro +pkg_eavro_fetch = git +pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro +pkg_eavro_commit = master + +PACKAGES += ecapnp +pkg_ecapnp_name = ecapnp +pkg_ecapnp_description = Cap'n Proto library for Erlang +pkg_ecapnp_homepage = https://github.com/kaos/ecapnp +pkg_ecapnp_fetch = git +pkg_ecapnp_repo = https://github.com/kaos/ecapnp +pkg_ecapnp_commit = master + +PACKAGES += econfig +pkg_econfig_name = econfig +pkg_econfig_description = simple Erlang config handler using INI files +pkg_econfig_homepage = https://github.com/benoitc/econfig +pkg_econfig_fetch = git +pkg_econfig_repo = https://github.com/benoitc/econfig +pkg_econfig_commit = master + +PACKAGES += edate +pkg_edate_name = edate +pkg_edate_description = date manipulation library for erlang +pkg_edate_homepage = https://github.com/dweldon/edate +pkg_edate_fetch = git +pkg_edate_repo = https://github.com/dweldon/edate +pkg_edate_commit = master + +PACKAGES += edgar +pkg_edgar_name = edgar +pkg_edgar_description = Erlang Does GNU AR +pkg_edgar_homepage = https://github.com/crownedgrouse/edgar +pkg_edgar_fetch = git +pkg_edgar_repo = https://github.com/crownedgrouse/edgar +pkg_edgar_commit = master + +PACKAGES += edis +pkg_edis_name = edis +pkg_edis_description = An Erlang implementation of Redis KV Store +pkg_edis_homepage = http://inaka.github.com/edis/ +pkg_edis_fetch = git +pkg_edis_repo = https://github.com/inaka/edis +pkg_edis_commit = master + +PACKAGES += edns +pkg_edns_name = edns +pkg_edns_description = Erlang/OTP DNS server +pkg_edns_homepage = https://github.com/hcvst/erlang-dns +pkg_edns_fetch = git +pkg_edns_repo = https://github.com/hcvst/erlang-dns +pkg_edns_commit = master + +PACKAGES += edown +pkg_edown_name = edown +pkg_edown_description = EDoc extension for generating Github-flavored Markdown +pkg_edown_homepage = https://github.com/uwiger/edown +pkg_edown_fetch = git +pkg_edown_repo = https://github.com/uwiger/edown +pkg_edown_commit = master + +PACKAGES += eep +pkg_eep_name = eep +pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy +pkg_eep_homepage = https://github.com/virtan/eep +pkg_eep_fetch = git +pkg_eep_repo = https://github.com/virtan/eep +pkg_eep_commit = master + +PACKAGES += eep_app +pkg_eep_app_name = eep_app +pkg_eep_app_description = Embedded Event Processing +pkg_eep_app_homepage = https://github.com/darach/eep-erl +pkg_eep_app_fetch = git +pkg_eep_app_repo = https://github.com/darach/eep-erl +pkg_eep_app_commit = master + +PACKAGES += efene +pkg_efene_name = efene +pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX +pkg_efene_homepage = https://github.com/efene/efene +pkg_efene_fetch = git +pkg_efene_repo = https://github.com/efene/efene +pkg_efene_commit = master + +PACKAGES += eganglia +pkg_eganglia_name = eganglia +pkg_eganglia_description = Erlang library to interact with Ganglia +pkg_eganglia_homepage = https://github.com/inaka/eganglia +pkg_eganglia_fetch = git +pkg_eganglia_repo = https://github.com/inaka/eganglia +pkg_eganglia_commit = v0.9.1 + +PACKAGES += egeoip +pkg_egeoip_name = egeoip +pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database. +pkg_egeoip_homepage = https://github.com/mochi/egeoip +pkg_egeoip_fetch = git +pkg_egeoip_repo = https://github.com/mochi/egeoip +pkg_egeoip_commit = master + +PACKAGES += ehsa +pkg_ehsa_name = ehsa +pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules +pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa +pkg_ehsa_fetch = hg +pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa +pkg_ehsa_commit = 2.0.4 + +PACKAGES += ej +pkg_ej_name = ej +pkg_ej_description = Helper module for working with Erlang terms representing JSON +pkg_ej_homepage = https://github.com/seth/ej +pkg_ej_fetch = git +pkg_ej_repo = https://github.com/seth/ej +pkg_ej_commit = master + +PACKAGES += ejabberd +pkg_ejabberd_name = ejabberd +pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform +pkg_ejabberd_homepage = https://github.com/processone/ejabberd +pkg_ejabberd_fetch = git +pkg_ejabberd_repo = https://github.com/processone/ejabberd +pkg_ejabberd_commit = master + +PACKAGES += ejwt +pkg_ejwt_name = ejwt +pkg_ejwt_description = erlang library for JSON Web Token +pkg_ejwt_homepage = https://github.com/artefactop/ejwt +pkg_ejwt_fetch = git +pkg_ejwt_repo = https://github.com/artefactop/ejwt +pkg_ejwt_commit = master + +PACKAGES += ekaf +pkg_ekaf_name = ekaf +pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang. +pkg_ekaf_homepage = https://github.com/helpshift/ekaf +pkg_ekaf_fetch = git +pkg_ekaf_repo = https://github.com/helpshift/ekaf +pkg_ekaf_commit = master + +PACKAGES += elarm +pkg_elarm_name = elarm +pkg_elarm_description = Alarm Manager for Erlang. +pkg_elarm_homepage = https://github.com/esl/elarm +pkg_elarm_fetch = git +pkg_elarm_repo = https://github.com/esl/elarm +pkg_elarm_commit = master + +PACKAGES += eleveldb +pkg_eleveldb_name = eleveldb +pkg_eleveldb_description = Erlang LevelDB API +pkg_eleveldb_homepage = https://github.com/basho/eleveldb +pkg_eleveldb_fetch = git +pkg_eleveldb_repo = https://github.com/basho/eleveldb +pkg_eleveldb_commit = master + +PACKAGES += elli +pkg_elli_name = elli +pkg_elli_description = Simple, robust and performant Erlang web server +pkg_elli_homepage = https://github.com/knutin/elli +pkg_elli_fetch = git +pkg_elli_repo = https://github.com/knutin/elli +pkg_elli_commit = master + +PACKAGES += elvis +pkg_elvis_name = elvis +pkg_elvis_description = Erlang Style Reviewer +pkg_elvis_homepage = https://github.com/inaka/elvis +pkg_elvis_fetch = git +pkg_elvis_repo = https://github.com/inaka/elvis +pkg_elvis_commit = 0.2.4 + +PACKAGES += emagick +pkg_emagick_name = emagick +pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool. +pkg_emagick_homepage = https://github.com/kivra/emagick +pkg_emagick_fetch = git +pkg_emagick_repo = https://github.com/kivra/emagick +pkg_emagick_commit = master + +PACKAGES += emysql +pkg_emysql_name = emysql +pkg_emysql_description = Stable, pure Erlang MySQL driver. +pkg_emysql_homepage = https://github.com/Eonblast/Emysql +pkg_emysql_fetch = git +pkg_emysql_repo = https://github.com/Eonblast/Emysql +pkg_emysql_commit = master + +PACKAGES += enm +pkg_enm_name = enm +pkg_enm_description = Erlang driver for nanomsg +pkg_enm_homepage = https://github.com/basho/enm +pkg_enm_fetch = git +pkg_enm_repo = https://github.com/basho/enm +pkg_enm_commit = master + +PACKAGES += entop +pkg_entop_name = entop +pkg_entop_description = A top-like tool for monitoring an Erlang node +pkg_entop_homepage = https://github.com/mazenharake/entop +pkg_entop_fetch = git +pkg_entop_repo = https://github.com/mazenharake/entop +pkg_entop_commit = master + +PACKAGES += epcap +pkg_epcap_name = epcap +pkg_epcap_description = Erlang packet capture interface using pcap +pkg_epcap_homepage = https://github.com/msantos/epcap +pkg_epcap_fetch = git +pkg_epcap_repo = https://github.com/msantos/epcap +pkg_epcap_commit = master + +PACKAGES += eper +pkg_eper_name = eper +pkg_eper_description = Erlang performance and debugging tools. +pkg_eper_homepage = https://github.com/massemanet/eper +pkg_eper_fetch = git +pkg_eper_repo = https://github.com/massemanet/eper +pkg_eper_commit = master + +PACKAGES += epgsql +pkg_epgsql_name = epgsql +pkg_epgsql_description = Erlang PostgreSQL client library. +pkg_epgsql_homepage = https://github.com/epgsql/epgsql +pkg_epgsql_fetch = git +pkg_epgsql_repo = https://github.com/epgsql/epgsql +pkg_epgsql_commit = master + +PACKAGES += episcina +pkg_episcina_name = episcina +pkg_episcina_description = A simple non intrusive resource pool for connections +pkg_episcina_homepage = https://github.com/erlware/episcina +pkg_episcina_fetch = git +pkg_episcina_repo = https://github.com/erlware/episcina +pkg_episcina_commit = master + +PACKAGES += eplot +pkg_eplot_name = eplot +pkg_eplot_description = A plot engine written in erlang. +pkg_eplot_homepage = https://github.com/psyeugenic/eplot +pkg_eplot_fetch = git +pkg_eplot_repo = https://github.com/psyeugenic/eplot +pkg_eplot_commit = master + +PACKAGES += epocxy +pkg_epocxy_name = epocxy +pkg_epocxy_description = Erlang Patterns of Concurrency +pkg_epocxy_homepage = https://github.com/duomark/epocxy +pkg_epocxy_fetch = git +pkg_epocxy_repo = https://github.com/duomark/epocxy +pkg_epocxy_commit = master + +PACKAGES += epubnub +pkg_epubnub_name = epubnub +pkg_epubnub_description = Erlang PubNub API +pkg_epubnub_homepage = https://github.com/tsloughter/epubnub +pkg_epubnub_fetch = git +pkg_epubnub_repo = https://github.com/tsloughter/epubnub +pkg_epubnub_commit = master + +PACKAGES += eqm +pkg_eqm_name = eqm +pkg_eqm_description = Erlang pub sub with supply-demand channels +pkg_eqm_homepage = https://github.com/loucash/eqm +pkg_eqm_fetch = git +pkg_eqm_repo = https://github.com/loucash/eqm +pkg_eqm_commit = master + +PACKAGES += eredis +pkg_eredis_name = eredis +pkg_eredis_description = Erlang Redis client +pkg_eredis_homepage = https://github.com/wooga/eredis +pkg_eredis_fetch = git +pkg_eredis_repo = https://github.com/wooga/eredis +pkg_eredis_commit = master + +PACKAGES += eredis_pool +pkg_eredis_pool_name = eredis_pool +pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy. +pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool +pkg_eredis_pool_fetch = git +pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool +pkg_eredis_pool_commit = master + +PACKAGES += erl_streams +pkg_erl_streams_name = erl_streams +pkg_erl_streams_description = Streams in Erlang +pkg_erl_streams_homepage = https://github.com/epappas/erl_streams +pkg_erl_streams_fetch = git +pkg_erl_streams_repo = https://github.com/epappas/erl_streams +pkg_erl_streams_commit = master + +PACKAGES += erlang_cep +pkg_erlang_cep_name = erlang_cep +pkg_erlang_cep_description = A basic CEP package written in erlang +pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep +pkg_erlang_cep_fetch = git +pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep +pkg_erlang_cep_commit = master + +PACKAGES += erlang_js +pkg_erlang_js_name = erlang_js +pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime. +pkg_erlang_js_homepage = https://github.com/basho/erlang_js +pkg_erlang_js_fetch = git +pkg_erlang_js_repo = https://github.com/basho/erlang_js +pkg_erlang_js_commit = master + +PACKAGES += erlang_localtime +pkg_erlang_localtime_name = erlang_localtime +pkg_erlang_localtime_description = Erlang library for conversion from one local time to another +pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime +pkg_erlang_localtime_fetch = git +pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime +pkg_erlang_localtime_commit = master + +PACKAGES += erlang_smtp +pkg_erlang_smtp_name = erlang_smtp +pkg_erlang_smtp_description = Erlang SMTP and POP3 server code. +pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp +pkg_erlang_smtp_fetch = git +pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp +pkg_erlang_smtp_commit = master + +PACKAGES += erlang_term +pkg_erlang_term_name = erlang_term +pkg_erlang_term_description = Erlang Term Info +pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term +pkg_erlang_term_fetch = git +pkg_erlang_term_repo = https://github.com/okeuday/erlang_term +pkg_erlang_term_commit = master + +PACKAGES += erlastic_search +pkg_erlastic_search_name = erlastic_search +pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface. +pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search +pkg_erlastic_search_fetch = git +pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search +pkg_erlastic_search_commit = master + +PACKAGES += erlasticsearch +pkg_erlasticsearch_name = erlasticsearch +pkg_erlasticsearch_description = Erlang thrift interface to elastic_search +pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch +pkg_erlasticsearch_fetch = git +pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch +pkg_erlasticsearch_commit = master + +PACKAGES += erlbrake +pkg_erlbrake_name = erlbrake +pkg_erlbrake_description = Erlang Airbrake notification client +pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake +pkg_erlbrake_fetch = git +pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake +pkg_erlbrake_commit = master + +PACKAGES += erlcloud +pkg_erlcloud_name = erlcloud +pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB) +pkg_erlcloud_homepage = https://github.com/gleber/erlcloud +pkg_erlcloud_fetch = git +pkg_erlcloud_repo = https://github.com/gleber/erlcloud +pkg_erlcloud_commit = master + +PACKAGES += erlcron +pkg_erlcron_name = erlcron +pkg_erlcron_description = Erlang cronish system +pkg_erlcron_homepage = https://github.com/erlware/erlcron +pkg_erlcron_fetch = git +pkg_erlcron_repo = https://github.com/erlware/erlcron +pkg_erlcron_commit = master + +PACKAGES += erldb +pkg_erldb_name = erldb +pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang +pkg_erldb_homepage = http://erldb.org +pkg_erldb_fetch = git +pkg_erldb_repo = https://github.com/erldb/erldb +pkg_erldb_commit = master + +PACKAGES += erldis +pkg_erldis_name = erldis +pkg_erldis_description = redis erlang client library +pkg_erldis_homepage = https://github.com/cstar/erldis +pkg_erldis_fetch = git +pkg_erldis_repo = https://github.com/cstar/erldis +pkg_erldis_commit = master + +PACKAGES += erldns +pkg_erldns_name = erldns +pkg_erldns_description = DNS server, in erlang. +pkg_erldns_homepage = https://github.com/aetrion/erl-dns +pkg_erldns_fetch = git +pkg_erldns_repo = https://github.com/aetrion/erl-dns +pkg_erldns_commit = master + +PACKAGES += erldocker +pkg_erldocker_name = erldocker +pkg_erldocker_description = Docker Remote API client for Erlang +pkg_erldocker_homepage = https://github.com/proger/erldocker +pkg_erldocker_fetch = git +pkg_erldocker_repo = https://github.com/proger/erldocker +pkg_erldocker_commit = master + +PACKAGES += erlfsmon +pkg_erlfsmon_name = erlfsmon +pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX +pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon +pkg_erlfsmon_fetch = git +pkg_erlfsmon_repo = https://github.com/proger/erlfsmon +pkg_erlfsmon_commit = master + +PACKAGES += erlgit +pkg_erlgit_name = erlgit +pkg_erlgit_description = Erlang convenience wrapper around git executable +pkg_erlgit_homepage = https://github.com/gleber/erlgit +pkg_erlgit_fetch = git +pkg_erlgit_repo = https://github.com/gleber/erlgit +pkg_erlgit_commit = master + +PACKAGES += erlguten +pkg_erlguten_name = erlguten +pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang. +pkg_erlguten_homepage = https://github.com/richcarl/erlguten +pkg_erlguten_fetch = git +pkg_erlguten_repo = https://github.com/richcarl/erlguten +pkg_erlguten_commit = master + +PACKAGES += erlmc +pkg_erlmc_name = erlmc +pkg_erlmc_description = Erlang memcached binary protocol client +pkg_erlmc_homepage = https://github.com/jkvor/erlmc +pkg_erlmc_fetch = git +pkg_erlmc_repo = https://github.com/jkvor/erlmc +pkg_erlmc_commit = master + +PACKAGES += erlmongo +pkg_erlmongo_name = erlmongo +pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support +pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo +pkg_erlmongo_fetch = git +pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo +pkg_erlmongo_commit = master + +PACKAGES += erlog +pkg_erlog_name = erlog +pkg_erlog_description = Prolog interpreter in and for Erlang +pkg_erlog_homepage = https://github.com/rvirding/erlog +pkg_erlog_fetch = git +pkg_erlog_repo = https://github.com/rvirding/erlog +pkg_erlog_commit = master + +PACKAGES += erlpass +pkg_erlpass_name = erlpass +pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever. +pkg_erlpass_homepage = https://github.com/ferd/erlpass +pkg_erlpass_fetch = git +pkg_erlpass_repo = https://github.com/ferd/erlpass +pkg_erlpass_commit = master + +PACKAGES += erlport +pkg_erlport_name = erlport +pkg_erlport_description = ErlPort - connect Erlang to other languages +pkg_erlport_homepage = https://github.com/hdima/erlport +pkg_erlport_fetch = git +pkg_erlport_repo = https://github.com/hdima/erlport +pkg_erlport_commit = master + +PACKAGES += erlsh +pkg_erlsh_name = erlsh +pkg_erlsh_description = Erlang shell tools +pkg_erlsh_homepage = https://github.com/proger/erlsh +pkg_erlsh_fetch = git +pkg_erlsh_repo = https://github.com/proger/erlsh +pkg_erlsh_commit = master + +PACKAGES += erlsha2 +pkg_erlsha2_name = erlsha2 +pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs. +pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2 +pkg_erlsha2_fetch = git +pkg_erlsha2_repo = https://github.com/vinoski/erlsha2 +pkg_erlsha2_commit = master + +PACKAGES += erlsom +pkg_erlsom_name = erlsom +pkg_erlsom_description = XML parser for Erlang +pkg_erlsom_homepage = https://github.com/willemdj/erlsom +pkg_erlsom_fetch = git +pkg_erlsom_repo = https://github.com/willemdj/erlsom +pkg_erlsom_commit = master + +PACKAGES += erlubi +pkg_erlubi_name = erlubi +pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer) +pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi +pkg_erlubi_fetch = git +pkg_erlubi_repo = https://github.com/krestenkrab/erlubi +pkg_erlubi_commit = master + +PACKAGES += erlvolt +pkg_erlvolt_name = erlvolt +pkg_erlvolt_description = VoltDB Erlang Client Driver +pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang +pkg_erlvolt_fetch = git +pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang +pkg_erlvolt_commit = master + +PACKAGES += erlware_commons +pkg_erlware_commons_name = erlware_commons +pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components. +pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons +pkg_erlware_commons_fetch = git +pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons +pkg_erlware_commons_commit = master + +PACKAGES += erlydtl +pkg_erlydtl_name = erlydtl +pkg_erlydtl_description = Django Template Language for Erlang. +pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl +pkg_erlydtl_fetch = git +pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl +pkg_erlydtl_commit = master + +PACKAGES += errd +pkg_errd_name = errd +pkg_errd_description = Erlang RRDTool library +pkg_errd_homepage = https://github.com/archaelus/errd +pkg_errd_fetch = git +pkg_errd_repo = https://github.com/archaelus/errd +pkg_errd_commit = master + +PACKAGES += erserve +pkg_erserve_name = erserve +pkg_erserve_description = Erlang/Rserve communication interface +pkg_erserve_homepage = https://github.com/del/erserve +pkg_erserve_fetch = git +pkg_erserve_repo = https://github.com/del/erserve +pkg_erserve_commit = master + +PACKAGES += erwa +pkg_erwa_name = erwa +pkg_erwa_description = A WAMP router and client written in Erlang. +pkg_erwa_homepage = https://github.com/bwegh/erwa +pkg_erwa_fetch = git +pkg_erwa_repo = https://github.com/bwegh/erwa +pkg_erwa_commit = 0.1.1 + +PACKAGES += espec +pkg_espec_name = espec +pkg_espec_description = ESpec: Behaviour driven development framework for Erlang +pkg_espec_homepage = https://github.com/lucaspiller/espec +pkg_espec_fetch = git +pkg_espec_repo = https://github.com/lucaspiller/espec +pkg_espec_commit = master + +PACKAGES += estatsd +pkg_estatsd_name = estatsd +pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite +pkg_estatsd_homepage = https://github.com/RJ/estatsd +pkg_estatsd_fetch = git +pkg_estatsd_repo = https://github.com/RJ/estatsd +pkg_estatsd_commit = master + +PACKAGES += etap +pkg_etap_name = etap +pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output. +pkg_etap_homepage = https://github.com/ngerakines/etap +pkg_etap_fetch = git +pkg_etap_repo = https://github.com/ngerakines/etap +pkg_etap_commit = master + +PACKAGES += etest +pkg_etest_name = etest +pkg_etest_description = A lightweight, convention over configuration test framework for Erlang +pkg_etest_homepage = https://github.com/wooga/etest +pkg_etest_fetch = git +pkg_etest_repo = https://github.com/wooga/etest +pkg_etest_commit = master + +PACKAGES += etest_http +pkg_etest_http_name = etest_http +pkg_etest_http_description = etest Assertions around HTTP (client-side) +pkg_etest_http_homepage = https://github.com/wooga/etest_http +pkg_etest_http_fetch = git +pkg_etest_http_repo = https://github.com/wooga/etest_http +pkg_etest_http_commit = master + +PACKAGES += etoml +pkg_etoml_name = etoml +pkg_etoml_description = TOML language erlang parser +pkg_etoml_homepage = https://github.com/kalta/etoml +pkg_etoml_fetch = git +pkg_etoml_repo = https://github.com/kalta/etoml +pkg_etoml_commit = master + +PACKAGES += eunit +pkg_eunit_name = eunit +pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository. +pkg_eunit_homepage = https://github.com/richcarl/eunit +pkg_eunit_fetch = git +pkg_eunit_repo = https://github.com/richcarl/eunit +pkg_eunit_commit = master + +PACKAGES += eunit_formatters +pkg_eunit_formatters_name = eunit_formatters +pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better. +pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters +pkg_eunit_formatters_fetch = git +pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters +pkg_eunit_formatters_commit = master + +PACKAGES += euthanasia +pkg_euthanasia_name = euthanasia +pkg_euthanasia_description = Merciful killer for your Erlang processes +pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia +pkg_euthanasia_fetch = git +pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia +pkg_euthanasia_commit = master + +PACKAGES += evum +pkg_evum_name = evum +pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM +pkg_evum_homepage = https://github.com/msantos/evum +pkg_evum_fetch = git +pkg_evum_repo = https://github.com/msantos/evum +pkg_evum_commit = master + +PACKAGES += exec +pkg_exec_name = exec +pkg_exec_description = Execute and control OS processes from Erlang/OTP. +pkg_exec_homepage = http://saleyn.github.com/erlexec +pkg_exec_fetch = git +pkg_exec_repo = https://github.com/saleyn/erlexec +pkg_exec_commit = master + +PACKAGES += exml +pkg_exml_name = exml +pkg_exml_description = XML parsing library in Erlang +pkg_exml_homepage = https://github.com/paulgray/exml +pkg_exml_fetch = git +pkg_exml_repo = https://github.com/paulgray/exml +pkg_exml_commit = master + +PACKAGES += exometer +pkg_exometer_name = exometer +pkg_exometer_description = Basic measurement objects and probe behavior +pkg_exometer_homepage = https://github.com/Feuerlabs/exometer +pkg_exometer_fetch = git +pkg_exometer_repo = https://github.com/Feuerlabs/exometer +pkg_exometer_commit = 1.2 + +PACKAGES += exs1024 +pkg_exs1024_name = exs1024 +pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang. +pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024 +pkg_exs1024_fetch = git +pkg_exs1024_repo = https://github.com/jj1bdx/exs1024 +pkg_exs1024_commit = master + +PACKAGES += exs64 +pkg_exs64_name = exs64 +pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang. +pkg_exs64_homepage = https://github.com/jj1bdx/exs64 +pkg_exs64_fetch = git +pkg_exs64_repo = https://github.com/jj1bdx/exs64 +pkg_exs64_commit = master + +PACKAGES += exsplus116 +pkg_exsplus116_name = exsplus116 +pkg_exsplus116_description = Xorshift116plus for Erlang +pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116 +pkg_exsplus116_fetch = git +pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116 +pkg_exsplus116_commit = master + +PACKAGES += exsplus128 +pkg_exsplus128_name = exsplus128 +pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang. +pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128 +pkg_exsplus128_fetch = git +pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128 +pkg_exsplus128_commit = master + +PACKAGES += ezmq +pkg_ezmq_name = ezmq +pkg_ezmq_description = zMQ implemented in Erlang +pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq +pkg_ezmq_fetch = git +pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq +pkg_ezmq_commit = master + +PACKAGES += ezmtp +pkg_ezmtp_name = ezmtp +pkg_ezmtp_description = ZMTP protocol in pure Erlang. +pkg_ezmtp_homepage = https://github.com/a13x/ezmtp +pkg_ezmtp_fetch = git +pkg_ezmtp_repo = https://github.com/a13x/ezmtp +pkg_ezmtp_commit = master + +PACKAGES += fast_disk_log +pkg_fast_disk_log_name = fast_disk_log +pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger +pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log +pkg_fast_disk_log_fetch = git +pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log +pkg_fast_disk_log_commit = master + +PACKAGES += feeder +pkg_feeder_name = feeder +pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds. +pkg_feeder_homepage = https://github.com/michaelnisi/feeder +pkg_feeder_fetch = git +pkg_feeder_repo = https://github.com/michaelnisi/feeder +pkg_feeder_commit = v1.4.6 + +PACKAGES += fix +pkg_fix_name = fix +pkg_fix_description = http://fixprotocol.org/ implementation. +pkg_fix_homepage = https://github.com/maxlapshin/fix +pkg_fix_fetch = git +pkg_fix_repo = https://github.com/maxlapshin/fix +pkg_fix_commit = master + +PACKAGES += flower +pkg_flower_name = flower +pkg_flower_description = FlowER - a Erlang OpenFlow development platform +pkg_flower_homepage = https://github.com/travelping/flower +pkg_flower_fetch = git +pkg_flower_repo = https://github.com/travelping/flower +pkg_flower_commit = master + +PACKAGES += fn +pkg_fn_name = fn +pkg_fn_description = Function utilities for Erlang +pkg_fn_homepage = https://github.com/reiddraper/fn +pkg_fn_fetch = git +pkg_fn_repo = https://github.com/reiddraper/fn +pkg_fn_commit = master + +PACKAGES += folsom +pkg_folsom_name = folsom +pkg_folsom_description = Expose Erlang Events and Metrics +pkg_folsom_homepage = https://github.com/boundary/folsom +pkg_folsom_fetch = git +pkg_folsom_repo = https://github.com/boundary/folsom +pkg_folsom_commit = master + +PACKAGES += folsom_cowboy +pkg_folsom_cowboy_name = folsom_cowboy +pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper. +pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy +pkg_folsom_cowboy_fetch = git +pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy +pkg_folsom_cowboy_commit = master + +PACKAGES += folsomite +pkg_folsomite_name = folsomite +pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics +pkg_folsomite_homepage = https://github.com/campanja/folsomite +pkg_folsomite_fetch = git +pkg_folsomite_repo = https://github.com/campanja/folsomite +pkg_folsomite_commit = master + +PACKAGES += fs +pkg_fs_name = fs +pkg_fs_description = Erlang FileSystem Listener +pkg_fs_homepage = https://github.com/synrc/fs +pkg_fs_fetch = git +pkg_fs_repo = https://github.com/synrc/fs +pkg_fs_commit = master + +PACKAGES += fuse +pkg_fuse_name = fuse +pkg_fuse_description = A Circuit Breaker for Erlang +pkg_fuse_homepage = https://github.com/jlouis/fuse +pkg_fuse_fetch = git +pkg_fuse_repo = https://github.com/jlouis/fuse +pkg_fuse_commit = master + +PACKAGES += gcm +pkg_gcm_name = gcm +pkg_gcm_description = An Erlang application for Google Cloud Messaging +pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang +pkg_gcm_fetch = git +pkg_gcm_repo = https://github.com/pdincau/gcm-erlang +pkg_gcm_commit = master + +PACKAGES += gcprof +pkg_gcprof_name = gcprof +pkg_gcprof_description = Garbage Collection profiler for Erlang +pkg_gcprof_homepage = https://github.com/knutin/gcprof +pkg_gcprof_fetch = git +pkg_gcprof_repo = https://github.com/knutin/gcprof +pkg_gcprof_commit = master + +PACKAGES += geas +pkg_geas_name = geas +pkg_geas_description = Guess Erlang Application Scattering +pkg_geas_homepage = https://github.com/crownedgrouse/geas +pkg_geas_fetch = git +pkg_geas_repo = https://github.com/crownedgrouse/geas +pkg_geas_commit = master + +PACKAGES += geef +pkg_geef_name = geef +pkg_geef_description = Git NEEEEF (Erlang NIF) +pkg_geef_homepage = https://github.com/carlosmn/geef +pkg_geef_fetch = git +pkg_geef_repo = https://github.com/carlosmn/geef +pkg_geef_commit = master + +PACKAGES += gen_cycle +pkg_gen_cycle_name = gen_cycle +pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks +pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle +pkg_gen_cycle_fetch = git +pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle +pkg_gen_cycle_commit = develop + +PACKAGES += gen_icmp +pkg_gen_icmp_name = gen_icmp +pkg_gen_icmp_description = Erlang interface to ICMP sockets +pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp +pkg_gen_icmp_fetch = git +pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp +pkg_gen_icmp_commit = master + +PACKAGES += gen_nb_server +pkg_gen_nb_server_name = gen_nb_server +pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers +pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server +pkg_gen_nb_server_fetch = git +pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server +pkg_gen_nb_server_commit = master + +PACKAGES += gen_paxos +pkg_gen_paxos_name = gen_paxos +pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol +pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos +pkg_gen_paxos_fetch = git +pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos +pkg_gen_paxos_commit = master + +PACKAGES += gen_smtp +pkg_gen_smtp_name = gen_smtp +pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules +pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp +pkg_gen_smtp_fetch = git +pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp +pkg_gen_smtp_commit = master + +PACKAGES += gen_tracker +pkg_gen_tracker_name = gen_tracker +pkg_gen_tracker_description = supervisor with ets handling of children and their metadata +pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker +pkg_gen_tracker_fetch = git +pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker +pkg_gen_tracker_commit = master + +PACKAGES += gen_unix +pkg_gen_unix_name = gen_unix +pkg_gen_unix_description = Erlang Unix socket interface +pkg_gen_unix_homepage = https://github.com/msantos/gen_unix +pkg_gen_unix_fetch = git +pkg_gen_unix_repo = https://github.com/msantos/gen_unix +pkg_gen_unix_commit = master + +PACKAGES += getopt +pkg_getopt_name = getopt +pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax +pkg_getopt_homepage = https://github.com/jcomellas/getopt +pkg_getopt_fetch = git +pkg_getopt_repo = https://github.com/jcomellas/getopt +pkg_getopt_commit = master + +PACKAGES += gettext +pkg_gettext_name = gettext +pkg_gettext_description = Erlang internationalization library. +pkg_gettext_homepage = https://github.com/etnt/gettext +pkg_gettext_fetch = git +pkg_gettext_repo = https://github.com/etnt/gettext +pkg_gettext_commit = master + +PACKAGES += giallo +pkg_giallo_name = giallo +pkg_giallo_description = Small and flexible web framework on top of Cowboy +pkg_giallo_homepage = https://github.com/kivra/giallo +pkg_giallo_fetch = git +pkg_giallo_repo = https://github.com/kivra/giallo +pkg_giallo_commit = master + +PACKAGES += gin +pkg_gin_name = gin +pkg_gin_description = The guards and for Erlang parse_transform +pkg_gin_homepage = https://github.com/mad-cocktail/gin +pkg_gin_fetch = git +pkg_gin_repo = https://github.com/mad-cocktail/gin +pkg_gin_commit = master + +PACKAGES += gitty +pkg_gitty_name = gitty +pkg_gitty_description = Git access in erlang +pkg_gitty_homepage = https://github.com/maxlapshin/gitty +pkg_gitty_fetch = git +pkg_gitty_repo = https://github.com/maxlapshin/gitty +pkg_gitty_commit = master + +PACKAGES += gold_fever +pkg_gold_fever_name = gold_fever +pkg_gold_fever_description = A Treasure Hunt for Erlangers +pkg_gold_fever_homepage = https://github.com/inaka/gold_fever +pkg_gold_fever_fetch = git +pkg_gold_fever_repo = https://github.com/inaka/gold_fever +pkg_gold_fever_commit = master + +PACKAGES += gossiperl +pkg_gossiperl_name = gossiperl +pkg_gossiperl_description = Gossip middleware in Erlang +pkg_gossiperl_homepage = http://gossiperl.com/ +pkg_gossiperl_fetch = git +pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl +pkg_gossiperl_commit = master + +PACKAGES += gpb +pkg_gpb_name = gpb +pkg_gpb_description = A Google Protobuf implementation for Erlang +pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb +pkg_gpb_fetch = git +pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb +pkg_gpb_commit = master + +PACKAGES += gproc +pkg_gproc_name = gproc +pkg_gproc_description = Extended process registry for Erlang +pkg_gproc_homepage = https://github.com/uwiger/gproc +pkg_gproc_fetch = git +pkg_gproc_repo = https://github.com/uwiger/gproc +pkg_gproc_commit = master + +PACKAGES += grapherl +pkg_grapherl_name = grapherl +pkg_grapherl_description = Create graphs of Erlang systems and programs +pkg_grapherl_homepage = https://github.com/eproxus/grapherl +pkg_grapherl_fetch = git +pkg_grapherl_repo = https://github.com/eproxus/grapherl +pkg_grapherl_commit = master + +PACKAGES += gun +pkg_gun_name = gun +pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang. +pkg_gun_homepage = http//ninenines.eu +pkg_gun_fetch = git +pkg_gun_repo = https://github.com/ninenines/gun +pkg_gun_commit = master + +PACKAGES += gut +pkg_gut_name = gut +pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman +pkg_gut_homepage = https://github.com/unbalancedparentheses/gut +pkg_gut_fetch = git +pkg_gut_repo = https://github.com/unbalancedparentheses/gut +pkg_gut_commit = master + +PACKAGES += hackney +pkg_hackney_name = hackney +pkg_hackney_description = simple HTTP client in Erlang +pkg_hackney_homepage = https://github.com/benoitc/hackney +pkg_hackney_fetch = git +pkg_hackney_repo = https://github.com/benoitc/hackney +pkg_hackney_commit = master + +PACKAGES += hamcrest +pkg_hamcrest_name = hamcrest +pkg_hamcrest_description = Erlang port of Hamcrest +pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang +pkg_hamcrest_fetch = git +pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang +pkg_hamcrest_commit = master + +PACKAGES += hanoidb +pkg_hanoidb_name = hanoidb +pkg_hanoidb_description = Erlang LSM BTree Storage +pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb +pkg_hanoidb_fetch = git +pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb +pkg_hanoidb_commit = master + +PACKAGES += hottub +pkg_hottub_name = hottub +pkg_hottub_description = Permanent Erlang Worker Pool +pkg_hottub_homepage = https://github.com/bfrog/hottub +pkg_hottub_fetch = git +pkg_hottub_repo = https://github.com/bfrog/hottub +pkg_hottub_commit = master + +PACKAGES += hpack +pkg_hpack_name = hpack +pkg_hpack_description = HPACK Implementation for Erlang +pkg_hpack_homepage = https://github.com/joedevivo/hpack +pkg_hpack_fetch = git +pkg_hpack_repo = https://github.com/joedevivo/hpack +pkg_hpack_commit = master + +PACKAGES += hyper +pkg_hyper_name = hyper +pkg_hyper_description = Erlang implementation of HyperLogLog +pkg_hyper_homepage = https://github.com/GameAnalytics/hyper +pkg_hyper_fetch = git +pkg_hyper_repo = https://github.com/GameAnalytics/hyper +pkg_hyper_commit = master + +PACKAGES += ibrowse +pkg_ibrowse_name = ibrowse +pkg_ibrowse_description = Erlang HTTP client +pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse +pkg_ibrowse_fetch = git +pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse +pkg_ibrowse_commit = v4.1.1 + +PACKAGES += ierlang +pkg_ierlang_name = ierlang +pkg_ierlang_description = An Erlang language kernel for IPython. +pkg_ierlang_homepage = https://github.com/robbielynch/ierlang +pkg_ierlang_fetch = git +pkg_ierlang_repo = https://github.com/robbielynch/ierlang +pkg_ierlang_commit = master + +PACKAGES += iota +pkg_iota_name = iota +pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code +pkg_iota_homepage = https://github.com/jpgneves/iota +pkg_iota_fetch = git +pkg_iota_repo = https://github.com/jpgneves/iota +pkg_iota_commit = master + +PACKAGES += irc_lib +pkg_irc_lib_name = irc_lib +pkg_irc_lib_description = Erlang irc client library +pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib +pkg_irc_lib_fetch = git +pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib +pkg_irc_lib_commit = master + +PACKAGES += ircd +pkg_ircd_name = ircd +pkg_ircd_description = A pluggable IRC daemon application/library for Erlang. +pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd +pkg_ircd_fetch = git +pkg_ircd_repo = https://github.com/tonyg/erlang-ircd +pkg_ircd_commit = master + +PACKAGES += iris +pkg_iris_name = iris +pkg_iris_description = Iris Erlang binding +pkg_iris_homepage = https://github.com/project-iris/iris-erl +pkg_iris_fetch = git +pkg_iris_repo = https://github.com/project-iris/iris-erl +pkg_iris_commit = master + +PACKAGES += iso8601 +pkg_iso8601_name = iso8601 +pkg_iso8601_description = Erlang ISO 8601 date formatter/parser +pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601 +pkg_iso8601_fetch = git +pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601 +pkg_iso8601_commit = master + +PACKAGES += itweet +pkg_itweet_name = itweet +pkg_itweet_description = Twitter Stream API on ibrowse +pkg_itweet_homepage = http://inaka.github.com/itweet/ +pkg_itweet_fetch = git +pkg_itweet_repo = https://github.com/inaka/itweet +pkg_itweet_commit = v2.0 + +PACKAGES += jamdb_sybase +pkg_jamdb_sybase_name = jamdb_sybase +pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE +pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase +pkg_jamdb_sybase_fetch = git +pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase +pkg_jamdb_sybase_commit = 0.6.0 + +PACKAGES += jerg +pkg_jerg_name = jerg +pkg_jerg_description = JSON Schema to Erlang Records Generator +pkg_jerg_homepage = https://github.com/ddossot/jerg +pkg_jerg_fetch = git +pkg_jerg_repo = https://github.com/ddossot/jerg +pkg_jerg_commit = master + +PACKAGES += jesse +pkg_jesse_name = jesse +pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang. +pkg_jesse_homepage = https://github.com/klarna/jesse +pkg_jesse_fetch = git +pkg_jesse_repo = https://github.com/klarna/jesse +pkg_jesse_commit = master + +PACKAGES += jiffy +pkg_jiffy_name = jiffy +pkg_jiffy_description = JSON NIFs for Erlang. +pkg_jiffy_homepage = https://github.com/davisp/jiffy +pkg_jiffy_fetch = git +pkg_jiffy_repo = https://github.com/davisp/jiffy +pkg_jiffy_commit = master + +PACKAGES += jiffy_v +pkg_jiffy_v_name = jiffy_v +pkg_jiffy_v_description = JSON validation utility +pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v +pkg_jiffy_v_fetch = git +pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v +pkg_jiffy_v_commit = 0.3.3 + +PACKAGES += jobs +pkg_jobs_name = jobs +pkg_jobs_description = a Job scheduler for load regulation +pkg_jobs_homepage = https://github.com/esl/jobs +pkg_jobs_fetch = git +pkg_jobs_repo = https://github.com/esl/jobs +pkg_jobs_commit = 0.3 + +PACKAGES += joxa +pkg_joxa_name = joxa +pkg_joxa_description = A Modern Lisp for the Erlang VM +pkg_joxa_homepage = https://github.com/joxa/joxa +pkg_joxa_fetch = git +pkg_joxa_repo = https://github.com/joxa/joxa +pkg_joxa_commit = master + +PACKAGES += json +pkg_json_name = json +pkg_json_description = a high level json library for erlang (17.0+) +pkg_json_homepage = https://github.com/talentdeficit/json +pkg_json_fetch = git +pkg_json_repo = https://github.com/talentdeficit/json +pkg_json_commit = master + +PACKAGES += json_rec +pkg_json_rec_name = json_rec +pkg_json_rec_description = JSON to erlang record +pkg_json_rec_homepage = https://github.com/justinkirby/json_rec +pkg_json_rec_fetch = git +pkg_json_rec_repo = https://github.com/justinkirby/json_rec +pkg_json_rec_commit = master + +PACKAGES += jsonerl +pkg_jsonerl_name = jsonerl +pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder +pkg_jsonerl_homepage = https://github.com/lambder/jsonerl +pkg_jsonerl_fetch = git +pkg_jsonerl_repo = https://github.com/lambder/jsonerl +pkg_jsonerl_commit = master + +PACKAGES += jsonpath +pkg_jsonpath_name = jsonpath +pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation +pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath +pkg_jsonpath_fetch = git +pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath +pkg_jsonpath_commit = master + +PACKAGES += jsonx +pkg_jsonx_name = jsonx +pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C. +pkg_jsonx_homepage = https://github.com/iskra/jsonx +pkg_jsonx_fetch = git +pkg_jsonx_repo = https://github.com/iskra/jsonx +pkg_jsonx_commit = master + +PACKAGES += jsx +pkg_jsx_name = jsx +pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON. +pkg_jsx_homepage = https://github.com/talentdeficit/jsx +pkg_jsx_fetch = git +pkg_jsx_repo = https://github.com/talentdeficit/jsx +pkg_jsx_commit = master + +PACKAGES += kafka +pkg_kafka_name = kafka +pkg_kafka_description = Kafka consumer and producer in Erlang +pkg_kafka_homepage = https://github.com/wooga/kafka-erlang +pkg_kafka_fetch = git +pkg_kafka_repo = https://github.com/wooga/kafka-erlang +pkg_kafka_commit = master + +PACKAGES += kai +pkg_kai_name = kai +pkg_kai_description = DHT storage by Takeshi Inoue +pkg_kai_homepage = https://github.com/synrc/kai +pkg_kai_fetch = git +pkg_kai_repo = https://github.com/synrc/kai +pkg_kai_commit = master + +PACKAGES += katja +pkg_katja_name = katja +pkg_katja_description = A simple Riemann client written in Erlang. +pkg_katja_homepage = https://github.com/nifoc/katja +pkg_katja_fetch = git +pkg_katja_repo = https://github.com/nifoc/katja +pkg_katja_commit = master + +PACKAGES += kdht +pkg_kdht_name = kdht +pkg_kdht_description = kdht is an erlang DHT implementation +pkg_kdht_homepage = https://github.com/kevinlynx/kdht +pkg_kdht_fetch = git +pkg_kdht_repo = https://github.com/kevinlynx/kdht +pkg_kdht_commit = master + +PACKAGES += key2value +pkg_key2value_name = key2value +pkg_key2value_description = Erlang 2-way map +pkg_key2value_homepage = https://github.com/okeuday/key2value +pkg_key2value_fetch = git +pkg_key2value_repo = https://github.com/okeuday/key2value +pkg_key2value_commit = master + +PACKAGES += keys1value +pkg_keys1value_name = keys1value +pkg_keys1value_description = Erlang set associative map for key lists +pkg_keys1value_homepage = https://github.com/okeuday/keys1value +pkg_keys1value_fetch = git +pkg_keys1value_repo = https://github.com/okeuday/keys1value +pkg_keys1value_commit = master + +PACKAGES += kinetic +pkg_kinetic_name = kinetic +pkg_kinetic_description = Erlang Kinesis Client +pkg_kinetic_homepage = https://github.com/AdRoll/kinetic +pkg_kinetic_fetch = git +pkg_kinetic_repo = https://github.com/AdRoll/kinetic +pkg_kinetic_commit = master + +PACKAGES += kjell +pkg_kjell_name = kjell +pkg_kjell_description = Erlang Shell +pkg_kjell_homepage = https://github.com/karlll/kjell +pkg_kjell_fetch = git +pkg_kjell_repo = https://github.com/karlll/kjell +pkg_kjell_commit = master + +PACKAGES += kraken +pkg_kraken_name = kraken +pkg_kraken_description = Distributed Pubsub Server for Realtime Apps +pkg_kraken_homepage = https://github.com/Asana/kraken +pkg_kraken_fetch = git +pkg_kraken_repo = https://github.com/Asana/kraken +pkg_kraken_commit = master + +PACKAGES += kucumberl +pkg_kucumberl_name = kucumberl +pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber +pkg_kucumberl_homepage = https://github.com/openshine/kucumberl +pkg_kucumberl_fetch = git +pkg_kucumberl_repo = https://github.com/openshine/kucumberl +pkg_kucumberl_commit = master + +PACKAGES += kvc +pkg_kvc_name = kvc +pkg_kvc_description = KVC - Key Value Coding for Erlang data structures +pkg_kvc_homepage = https://github.com/etrepum/kvc +pkg_kvc_fetch = git +pkg_kvc_repo = https://github.com/etrepum/kvc +pkg_kvc_commit = master + +PACKAGES += kvlists +pkg_kvlists_name = kvlists +pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang +pkg_kvlists_homepage = https://github.com/jcomellas/kvlists +pkg_kvlists_fetch = git +pkg_kvlists_repo = https://github.com/jcomellas/kvlists +pkg_kvlists_commit = master + +PACKAGES += kvs +pkg_kvs_name = kvs +pkg_kvs_description = Container and Iterator +pkg_kvs_homepage = https://github.com/synrc/kvs +pkg_kvs_fetch = git +pkg_kvs_repo = https://github.com/synrc/kvs +pkg_kvs_commit = master + +PACKAGES += lager +pkg_lager_name = lager +pkg_lager_description = A logging framework for Erlang/OTP. +pkg_lager_homepage = https://github.com/basho/lager +pkg_lager_fetch = git +pkg_lager_repo = https://github.com/basho/lager +pkg_lager_commit = master + +PACKAGES += lager_amqp_backend +pkg_lager_amqp_backend_name = lager_amqp_backend +pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend +pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend +pkg_lager_amqp_backend_fetch = git +pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend +pkg_lager_amqp_backend_commit = master + +PACKAGES += lager_syslog +pkg_lager_syslog_name = lager_syslog +pkg_lager_syslog_description = Syslog backend for lager +pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog +pkg_lager_syslog_fetch = git +pkg_lager_syslog_repo = https://github.com/basho/lager_syslog +pkg_lager_syslog_commit = master + +PACKAGES += lambdapad +pkg_lambdapad_name = lambdapad +pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang. +pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad +pkg_lambdapad_fetch = git +pkg_lambdapad_repo = https://github.com/gar1t/lambdapad +pkg_lambdapad_commit = master + +PACKAGES += lasp +pkg_lasp_name = lasp +pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations +pkg_lasp_homepage = http://lasp-lang.org/ +pkg_lasp_fetch = git +pkg_lasp_repo = https://github.com/lasp-lang/lasp +pkg_lasp_commit = master + +PACKAGES += lasse +pkg_lasse_name = lasse +pkg_lasse_description = SSE handler for Cowboy +pkg_lasse_homepage = https://github.com/inaka/lasse +pkg_lasse_fetch = git +pkg_lasse_repo = https://github.com/inaka/lasse +pkg_lasse_commit = 0.1.0 + +PACKAGES += ldap +pkg_ldap_name = ldap +pkg_ldap_description = LDAP server written in Erlang +pkg_ldap_homepage = https://github.com/spawnproc/ldap +pkg_ldap_fetch = git +pkg_ldap_repo = https://github.com/spawnproc/ldap +pkg_ldap_commit = master + +PACKAGES += lethink +pkg_lethink_name = lethink +pkg_lethink_description = erlang driver for rethinkdb +pkg_lethink_homepage = https://github.com/taybin/lethink +pkg_lethink_fetch = git +pkg_lethink_repo = https://github.com/taybin/lethink +pkg_lethink_commit = master + +PACKAGES += lfe +pkg_lfe_name = lfe +pkg_lfe_description = Lisp Flavoured Erlang (LFE) +pkg_lfe_homepage = https://github.com/rvirding/lfe +pkg_lfe_fetch = git +pkg_lfe_repo = https://github.com/rvirding/lfe +pkg_lfe_commit = master + +PACKAGES += ling +pkg_ling_name = ling +pkg_ling_description = Erlang on Xen +pkg_ling_homepage = https://github.com/cloudozer/ling +pkg_ling_fetch = git +pkg_ling_repo = https://github.com/cloudozer/ling +pkg_ling_commit = master + +PACKAGES += live +pkg_live_name = live +pkg_live_description = Automated module and configuration reloader. +pkg_live_homepage = http://ninenines.eu +pkg_live_fetch = git +pkg_live_repo = https://github.com/ninenines/live +pkg_live_commit = master + +PACKAGES += lmq +pkg_lmq_name = lmq +pkg_lmq_description = Lightweight Message Queue +pkg_lmq_homepage = https://github.com/iij/lmq +pkg_lmq_fetch = git +pkg_lmq_repo = https://github.com/iij/lmq +pkg_lmq_commit = master + +PACKAGES += locker +pkg_locker_name = locker +pkg_locker_description = Atomic distributed 'check and set' for short-lived keys +pkg_locker_homepage = https://github.com/wooga/locker +pkg_locker_fetch = git +pkg_locker_repo = https://github.com/wooga/locker +pkg_locker_commit = master + +PACKAGES += locks +pkg_locks_name = locks +pkg_locks_description = A scalable, deadlock-resolving resource locker +pkg_locks_homepage = https://github.com/uwiger/locks +pkg_locks_fetch = git +pkg_locks_repo = https://github.com/uwiger/locks +pkg_locks_commit = master + +PACKAGES += log4erl +pkg_log4erl_name = log4erl +pkg_log4erl_description = A logger for erlang in the spirit of Log4J. +pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl +pkg_log4erl_fetch = git +pkg_log4erl_repo = https://github.com/ahmednawras/log4erl +pkg_log4erl_commit = master + +PACKAGES += lol +pkg_lol_name = lol +pkg_lol_description = Lisp on erLang, and programming is fun again +pkg_lol_homepage = https://github.com/b0oh/lol +pkg_lol_fetch = git +pkg_lol_repo = https://github.com/b0oh/lol +pkg_lol_commit = master + +PACKAGES += lucid +pkg_lucid_name = lucid +pkg_lucid_description = HTTP/2 server written in Erlang +pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid +pkg_lucid_fetch = git +pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid +pkg_lucid_commit = master + +PACKAGES += luerl +pkg_luerl_name = luerl +pkg_luerl_description = Lua in Erlang +pkg_luerl_homepage = https://github.com/rvirding/luerl +pkg_luerl_fetch = git +pkg_luerl_repo = https://github.com/rvirding/luerl +pkg_luerl_commit = develop + +PACKAGES += luwak +pkg_luwak_name = luwak +pkg_luwak_description = Large-object storage interface for Riak +pkg_luwak_homepage = https://github.com/basho/luwak +pkg_luwak_fetch = git +pkg_luwak_repo = https://github.com/basho/luwak +pkg_luwak_commit = master + +PACKAGES += lux +pkg_lux_name = lux +pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands +pkg_lux_homepage = https://github.com/hawk/lux +pkg_lux_fetch = git +pkg_lux_repo = https://github.com/hawk/lux +pkg_lux_commit = master + +PACKAGES += machi +pkg_machi_name = machi +pkg_machi_description = Machi file store +pkg_machi_homepage = https://github.com/basho/machi +pkg_machi_fetch = git +pkg_machi_repo = https://github.com/basho/machi +pkg_machi_commit = master + +PACKAGES += mad +pkg_mad_name = mad +pkg_mad_description = Small and Fast Rebar Replacement +pkg_mad_homepage = https://github.com/synrc/mad +pkg_mad_fetch = git +pkg_mad_repo = https://github.com/synrc/mad +pkg_mad_commit = master + +PACKAGES += marina +pkg_marina_name = marina +pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client +pkg_marina_homepage = https://github.com/lpgauth/marina +pkg_marina_fetch = git +pkg_marina_repo = https://github.com/lpgauth/marina +pkg_marina_commit = master + +PACKAGES += mavg +pkg_mavg_name = mavg +pkg_mavg_description = Erlang :: Exponential moving average library +pkg_mavg_homepage = https://github.com/EchoTeam/mavg +pkg_mavg_fetch = git +pkg_mavg_repo = https://github.com/EchoTeam/mavg +pkg_mavg_commit = master + +PACKAGES += mc_erl +pkg_mc_erl_name = mc_erl +pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang. +pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl +pkg_mc_erl_fetch = git +pkg_mc_erl_repo = https://github.com/clonejo/mc-erl +pkg_mc_erl_commit = master + +PACKAGES += mcd +pkg_mcd_name = mcd +pkg_mcd_description = Fast memcached protocol client in pure Erlang +pkg_mcd_homepage = https://github.com/EchoTeam/mcd +pkg_mcd_fetch = git +pkg_mcd_repo = https://github.com/EchoTeam/mcd +pkg_mcd_commit = master + +PACKAGES += mcerlang +pkg_mcerlang_name = mcerlang +pkg_mcerlang_description = The McErlang model checker for Erlang +pkg_mcerlang_homepage = https://github.com/fredlund/McErlang +pkg_mcerlang_fetch = git +pkg_mcerlang_repo = https://github.com/fredlund/McErlang +pkg_mcerlang_commit = master + +PACKAGES += meck +pkg_meck_name = meck +pkg_meck_description = A mocking library for Erlang +pkg_meck_homepage = https://github.com/eproxus/meck +pkg_meck_fetch = git +pkg_meck_repo = https://github.com/eproxus/meck +pkg_meck_commit = master + +PACKAGES += mekao +pkg_mekao_name = mekao +pkg_mekao_description = SQL constructor +pkg_mekao_homepage = https://github.com/ddosia/mekao +pkg_mekao_fetch = git +pkg_mekao_repo = https://github.com/ddosia/mekao +pkg_mekao_commit = master + +PACKAGES += memo +pkg_memo_name = memo +pkg_memo_description = Erlang memoization server +pkg_memo_homepage = https://github.com/tuncer/memo +pkg_memo_fetch = git +pkg_memo_repo = https://github.com/tuncer/memo +pkg_memo_commit = master + +PACKAGES += merge_index +pkg_merge_index_name = merge_index +pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop). +pkg_merge_index_homepage = https://github.com/basho/merge_index +pkg_merge_index_fetch = git +pkg_merge_index_repo = https://github.com/basho/merge_index +pkg_merge_index_commit = master + +PACKAGES += merl +pkg_merl_name = merl +pkg_merl_description = Metaprogramming in Erlang +pkg_merl_homepage = https://github.com/richcarl/merl +pkg_merl_fetch = git +pkg_merl_repo = https://github.com/richcarl/merl +pkg_merl_commit = master + +PACKAGES += mimetypes +pkg_mimetypes_name = mimetypes +pkg_mimetypes_description = Erlang MIME types library +pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes +pkg_mimetypes_fetch = git +pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes +pkg_mimetypes_commit = master + +PACKAGES += mixer +pkg_mixer_name = mixer +pkg_mixer_description = Mix in functions from other modules +pkg_mixer_homepage = https://github.com/chef/mixer +pkg_mixer_fetch = git +pkg_mixer_repo = https://github.com/chef/mixer +pkg_mixer_commit = master + +PACKAGES += mochiweb +pkg_mochiweb_name = mochiweb +pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers. +pkg_mochiweb_homepage = https://github.com/mochi/mochiweb +pkg_mochiweb_fetch = git +pkg_mochiweb_repo = https://github.com/mochi/mochiweb +pkg_mochiweb_commit = master + +PACKAGES += mochiweb_xpath +pkg_mochiweb_xpath_name = mochiweb_xpath +pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser +pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath +pkg_mochiweb_xpath_fetch = git +pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath +pkg_mochiweb_xpath_commit = master + +PACKAGES += mockgyver +pkg_mockgyver_name = mockgyver +pkg_mockgyver_description = A mocking library for Erlang +pkg_mockgyver_homepage = https://github.com/klajo/mockgyver +pkg_mockgyver_fetch = git +pkg_mockgyver_repo = https://github.com/klajo/mockgyver +pkg_mockgyver_commit = master + +PACKAGES += modlib +pkg_modlib_name = modlib +pkg_modlib_description = Web framework based on Erlang's inets httpd +pkg_modlib_homepage = https://github.com/gar1t/modlib +pkg_modlib_fetch = git +pkg_modlib_repo = https://github.com/gar1t/modlib +pkg_modlib_commit = master + +PACKAGES += mongodb +pkg_mongodb_name = mongodb +pkg_mongodb_description = MongoDB driver for Erlang +pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang +pkg_mongodb_fetch = git +pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang +pkg_mongodb_commit = master + +PACKAGES += mongooseim +pkg_mongooseim_name = mongooseim +pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions +pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform +pkg_mongooseim_fetch = git +pkg_mongooseim_repo = https://github.com/esl/MongooseIM +pkg_mongooseim_commit = master + +PACKAGES += moyo +pkg_moyo_name = moyo +pkg_moyo_description = Erlang utility functions library +pkg_moyo_homepage = https://github.com/dwango/moyo +pkg_moyo_fetch = git +pkg_moyo_repo = https://github.com/dwango/moyo +pkg_moyo_commit = master + +PACKAGES += msgpack +pkg_msgpack_name = msgpack +pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang +pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang +pkg_msgpack_fetch = git +pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang +pkg_msgpack_commit = master + +PACKAGES += mu2 +pkg_mu2_name = mu2 +pkg_mu2_description = Erlang mutation testing tool +pkg_mu2_homepage = https://github.com/ramsay-t/mu2 +pkg_mu2_fetch = git +pkg_mu2_repo = https://github.com/ramsay-t/mu2 +pkg_mu2_commit = master + +PACKAGES += mustache +pkg_mustache_name = mustache +pkg_mustache_description = Mustache template engine for Erlang. +pkg_mustache_homepage = https://github.com/mojombo/mustache.erl +pkg_mustache_fetch = git +pkg_mustache_repo = https://github.com/mojombo/mustache.erl +pkg_mustache_commit = master + +PACKAGES += myproto +pkg_myproto_name = myproto +pkg_myproto_description = MySQL Server Protocol in Erlang +pkg_myproto_homepage = https://github.com/altenwald/myproto +pkg_myproto_fetch = git +pkg_myproto_repo = https://github.com/altenwald/myproto +pkg_myproto_commit = master + +PACKAGES += mysql +pkg_mysql_name = mysql +pkg_mysql_description = Erlang MySQL Driver (from code.google.com) +pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver +pkg_mysql_fetch = git +pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver +pkg_mysql_commit = master + +PACKAGES += n2o +pkg_n2o_name = n2o +pkg_n2o_description = WebSocket Application Server +pkg_n2o_homepage = https://github.com/5HT/n2o +pkg_n2o_fetch = git +pkg_n2o_repo = https://github.com/5HT/n2o +pkg_n2o_commit = master + +PACKAGES += nat_upnp +pkg_nat_upnp_name = nat_upnp +pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD +pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp +pkg_nat_upnp_fetch = git +pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp +pkg_nat_upnp_commit = master + +PACKAGES += neo4j +pkg_neo4j_name = neo4j +pkg_neo4j_description = Erlang client library for Neo4J. +pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang +pkg_neo4j_fetch = git +pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang +pkg_neo4j_commit = master + +PACKAGES += neotoma +pkg_neotoma_name = neotoma +pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars. +pkg_neotoma_homepage = https://github.com/seancribbs/neotoma +pkg_neotoma_fetch = git +pkg_neotoma_repo = https://github.com/seancribbs/neotoma +pkg_neotoma_commit = master + +PACKAGES += newrelic +pkg_newrelic_name = newrelic +pkg_newrelic_description = Erlang library for sending metrics to New Relic +pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang +pkg_newrelic_fetch = git +pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang +pkg_newrelic_commit = master + +PACKAGES += nifty +pkg_nifty_name = nifty +pkg_nifty_description = Erlang NIF wrapper generator +pkg_nifty_homepage = https://github.com/parapluu/nifty +pkg_nifty_fetch = git +pkg_nifty_repo = https://github.com/parapluu/nifty +pkg_nifty_commit = master + +PACKAGES += nitrogen_core +pkg_nitrogen_core_name = nitrogen_core +pkg_nitrogen_core_description = The core Nitrogen library. +pkg_nitrogen_core_homepage = http://nitrogenproject.com/ +pkg_nitrogen_core_fetch = git +pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core +pkg_nitrogen_core_commit = master + +PACKAGES += nkbase +pkg_nkbase_name = nkbase +pkg_nkbase_description = NkBASE distributed database +pkg_nkbase_homepage = https://github.com/Nekso/nkbase +pkg_nkbase_fetch = git +pkg_nkbase_repo = https://github.com/Nekso/nkbase +pkg_nkbase_commit = develop + +PACKAGES += nkdocker +pkg_nkdocker_name = nkdocker +pkg_nkdocker_description = Erlang Docker client +pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker +pkg_nkdocker_fetch = git +pkg_nkdocker_repo = https://github.com/Nekso/nkdocker +pkg_nkdocker_commit = master + +PACKAGES += nkpacket +pkg_nkpacket_name = nkpacket +pkg_nkpacket_description = Generic Erlang transport layer +pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket +pkg_nkpacket_fetch = git +pkg_nkpacket_repo = https://github.com/Nekso/nkpacket +pkg_nkpacket_commit = master + +PACKAGES += nodefinder +pkg_nodefinder_name = nodefinder +pkg_nodefinder_description = automatic node discovery via UDP multicast +pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder +pkg_nodefinder_fetch = git +pkg_nodefinder_repo = https://github.com/okeuday/nodefinder +pkg_nodefinder_commit = master + +PACKAGES += nprocreg +pkg_nprocreg_name = nprocreg +pkg_nprocreg_description = Minimal Distributed Erlang Process Registry +pkg_nprocreg_homepage = http://nitrogenproject.com/ +pkg_nprocreg_fetch = git +pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg +pkg_nprocreg_commit = master + +PACKAGES += oauth +pkg_oauth_name = oauth +pkg_oauth_description = An Erlang OAuth 1.0 implementation +pkg_oauth_homepage = https://github.com/tim/erlang-oauth +pkg_oauth_fetch = git +pkg_oauth_repo = https://github.com/tim/erlang-oauth +pkg_oauth_commit = master + +PACKAGES += oauth2 +pkg_oauth2_name = oauth2 +pkg_oauth2_description = Erlang Oauth2 implementation +pkg_oauth2_homepage = https://github.com/kivra/oauth2 +pkg_oauth2_fetch = git +pkg_oauth2_repo = https://github.com/kivra/oauth2 +pkg_oauth2_commit = master + +PACKAGES += oauth2c +pkg_oauth2c_name = oauth2c +pkg_oauth2c_description = Erlang OAuth2 Client +pkg_oauth2c_homepage = https://github.com/kivra/oauth2_client +pkg_oauth2c_fetch = git +pkg_oauth2c_repo = https://github.com/kivra/oauth2_client +pkg_oauth2c_commit = master + +PACKAGES += octopus +pkg_octopus_name = octopus +pkg_octopus_description = Small and flexible pool manager written in Erlang +pkg_octopus_homepage = https://github.com/erlangbureau/octopus +pkg_octopus_fetch = git +pkg_octopus_repo = https://github.com/erlangbureau/octopus +pkg_octopus_commit = 1.0.0 + +PACKAGES += of_protocol +pkg_of_protocol_name = of_protocol +pkg_of_protocol_description = OpenFlow Protocol Library for Erlang +pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol +pkg_of_protocol_fetch = git +pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol +pkg_of_protocol_commit = master + +PACKAGES += opencouch +pkg_opencouch_name = couch +pkg_opencouch_description = A embeddable document oriented database compatible with Apache CouchDB +pkg_opencouch_homepage = https://github.com/benoitc/opencouch +pkg_opencouch_fetch = git +pkg_opencouch_repo = https://github.com/benoitc/opencouch +pkg_opencouch_commit = master + +PACKAGES += openflow +pkg_openflow_name = openflow +pkg_openflow_description = An OpenFlow controller written in pure erlang +pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow +pkg_openflow_fetch = git +pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow +pkg_openflow_commit = master + +PACKAGES += openid +pkg_openid_name = openid +pkg_openid_description = Erlang OpenID +pkg_openid_homepage = https://github.com/brendonh/erl_openid +pkg_openid_fetch = git +pkg_openid_repo = https://github.com/brendonh/erl_openid +pkg_openid_commit = master + +PACKAGES += openpoker +pkg_openpoker_name = openpoker +pkg_openpoker_description = Genesis Texas hold'em Game Server +pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker +pkg_openpoker_fetch = git +pkg_openpoker_repo = https://github.com/hpyhacking/openpoker +pkg_openpoker_commit = master + +PACKAGES += pal +pkg_pal_name = pal +pkg_pal_description = Pragmatic Authentication Library +pkg_pal_homepage = https://github.com/manifest/pal +pkg_pal_fetch = git +pkg_pal_repo = https://github.com/manifest/pal +pkg_pal_commit = master + +PACKAGES += parse_trans +pkg_parse_trans_name = parse_trans +pkg_parse_trans_description = Parse transform utilities for Erlang +pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans +pkg_parse_trans_fetch = git +pkg_parse_trans_repo = https://github.com/uwiger/parse_trans +pkg_parse_trans_commit = master + +PACKAGES += parsexml +pkg_parsexml_name = parsexml +pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API +pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml +pkg_parsexml_fetch = git +pkg_parsexml_repo = https://github.com/maxlapshin/parsexml +pkg_parsexml_commit = master + +PACKAGES += pegjs +pkg_pegjs_name = pegjs +pkg_pegjs_description = An implementation of PEG.js grammar for Erlang. +pkg_pegjs_homepage = https://github.com/dmitriid/pegjs +pkg_pegjs_fetch = git +pkg_pegjs_repo = https://github.com/dmitriid/pegjs +pkg_pegjs_commit = 0.3 + +PACKAGES += percept2 +pkg_percept2_name = percept2 +pkg_percept2_description = Concurrent profiling tool for Erlang +pkg_percept2_homepage = https://github.com/huiqing/percept2 +pkg_percept2_fetch = git +pkg_percept2_repo = https://github.com/huiqing/percept2 +pkg_percept2_commit = master + +PACKAGES += pgsql +pkg_pgsql_name = pgsql +pkg_pgsql_description = Erlang PostgreSQL driver +pkg_pgsql_homepage = https://github.com/semiocast/pgsql +pkg_pgsql_fetch = git +pkg_pgsql_repo = https://github.com/semiocast/pgsql +pkg_pgsql_commit = master + +PACKAGES += pkgx +pkg_pkgx_name = pkgx +pkg_pkgx_description = Build .deb packages from Erlang releases +pkg_pkgx_homepage = https://github.com/arjan/pkgx +pkg_pkgx_fetch = git +pkg_pkgx_repo = https://github.com/arjan/pkgx +pkg_pkgx_commit = master + +PACKAGES += pkt +pkg_pkt_name = pkt +pkg_pkt_description = Erlang network protocol library +pkg_pkt_homepage = https://github.com/msantos/pkt +pkg_pkt_fetch = git +pkg_pkt_repo = https://github.com/msantos/pkt +pkg_pkt_commit = master + +PACKAGES += plain_fsm +pkg_plain_fsm_name = plain_fsm +pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs. +pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm +pkg_plain_fsm_fetch = git +pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm +pkg_plain_fsm_commit = master + +PACKAGES += plumtree +pkg_plumtree_name = plumtree +pkg_plumtree_description = Epidemic Broadcast Trees +pkg_plumtree_homepage = https://github.com/helium/plumtree +pkg_plumtree_fetch = git +pkg_plumtree_repo = https://github.com/helium/plumtree +pkg_plumtree_commit = master + +PACKAGES += pmod_transform +pkg_pmod_transform_name = pmod_transform +pkg_pmod_transform_description = Parse transform for parameterized modules +pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform +pkg_pmod_transform_fetch = git +pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform +pkg_pmod_transform_commit = master + +PACKAGES += pobox +pkg_pobox_name = pobox +pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang +pkg_pobox_homepage = https://github.com/ferd/pobox +pkg_pobox_fetch = git +pkg_pobox_repo = https://github.com/ferd/pobox +pkg_pobox_commit = master + +PACKAGES += ponos +pkg_ponos_name = ponos +pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang +pkg_ponos_homepage = https://github.com/klarna/ponos +pkg_ponos_fetch = git +pkg_ponos_repo = https://github.com/klarna/ponos +pkg_ponos_commit = master + +PACKAGES += poolboy +pkg_poolboy_name = poolboy +pkg_poolboy_description = A hunky Erlang worker pool factory +pkg_poolboy_homepage = https://github.com/devinus/poolboy +pkg_poolboy_fetch = git +pkg_poolboy_repo = https://github.com/devinus/poolboy +pkg_poolboy_commit = master + +PACKAGES += pooler +pkg_pooler_name = pooler +pkg_pooler_description = An OTP Process Pool Application +pkg_pooler_homepage = https://github.com/seth/pooler +pkg_pooler_fetch = git +pkg_pooler_repo = https://github.com/seth/pooler +pkg_pooler_commit = master + +PACKAGES += pqueue +pkg_pqueue_name = pqueue +pkg_pqueue_description = Erlang Priority Queues +pkg_pqueue_homepage = https://github.com/okeuday/pqueue +pkg_pqueue_fetch = git +pkg_pqueue_repo = https://github.com/okeuday/pqueue +pkg_pqueue_commit = master + +PACKAGES += procket +pkg_procket_name = procket +pkg_procket_description = Erlang interface to low level socket operations +pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket +pkg_procket_fetch = git +pkg_procket_repo = https://github.com/msantos/procket +pkg_procket_commit = master + +PACKAGES += prop +pkg_prop_name = prop +pkg_prop_description = An Erlang code scaffolding and generator system. +pkg_prop_homepage = https://github.com/nuex/prop +pkg_prop_fetch = git +pkg_prop_repo = https://github.com/nuex/prop +pkg_prop_commit = master + +PACKAGES += proper +pkg_proper_name = proper +pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang. +pkg_proper_homepage = http://proper.softlab.ntua.gr +pkg_proper_fetch = git +pkg_proper_repo = https://github.com/manopapad/proper +pkg_proper_commit = master + +PACKAGES += props +pkg_props_name = props +pkg_props_description = Property structure library +pkg_props_homepage = https://github.com/greyarea/props +pkg_props_fetch = git +pkg_props_repo = https://github.com/greyarea/props +pkg_props_commit = master + +PACKAGES += protobuffs +pkg_protobuffs_name = protobuffs +pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs. +pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs +pkg_protobuffs_fetch = git +pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs +pkg_protobuffs_commit = master + +PACKAGES += psycho +pkg_psycho_name = psycho +pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware. +pkg_psycho_homepage = https://github.com/gar1t/psycho +pkg_psycho_fetch = git +pkg_psycho_repo = https://github.com/gar1t/psycho +pkg_psycho_commit = master + +PACKAGES += ptrackerl +pkg_ptrackerl_name = ptrackerl +pkg_ptrackerl_description = Pivotal Tracker API Client written in Erlang +pkg_ptrackerl_homepage = https://github.com/inaka/ptrackerl +pkg_ptrackerl_fetch = git +pkg_ptrackerl_repo = https://github.com/inaka/ptrackerl +pkg_ptrackerl_commit = master + +PACKAGES += purity +pkg_purity_name = purity +pkg_purity_description = A side-effect analyzer for Erlang +pkg_purity_homepage = https://github.com/mpitid/purity +pkg_purity_fetch = git +pkg_purity_repo = https://github.com/mpitid/purity +pkg_purity_commit = master + +PACKAGES += push_service +pkg_push_service_name = push_service +pkg_push_service_description = Push service +pkg_push_service_homepage = https://github.com/hairyhum/push_service +pkg_push_service_fetch = git +pkg_push_service_repo = https://github.com/hairyhum/push_service +pkg_push_service_commit = master + +PACKAGES += qdate +pkg_qdate_name = qdate +pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang. +pkg_qdate_homepage = https://github.com/choptastic/qdate +pkg_qdate_fetch = git +pkg_qdate_repo = https://github.com/choptastic/qdate +pkg_qdate_commit = 0.4.0 + +PACKAGES += qrcode +pkg_qrcode_name = qrcode +pkg_qrcode_description = QR Code encoder in Erlang +pkg_qrcode_homepage = https://github.com/komone/qrcode +pkg_qrcode_fetch = git +pkg_qrcode_repo = https://github.com/komone/qrcode +pkg_qrcode_commit = master + +PACKAGES += quest +pkg_quest_name = quest +pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang. +pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest +pkg_quest_fetch = git +pkg_quest_repo = https://github.com/eriksoe/ErlangQuest +pkg_quest_commit = master + +PACKAGES += quickrand +pkg_quickrand_name = quickrand +pkg_quickrand_description = Quick Erlang Random Number Generation +pkg_quickrand_homepage = https://github.com/okeuday/quickrand +pkg_quickrand_fetch = git +pkg_quickrand_repo = https://github.com/okeuday/quickrand +pkg_quickrand_commit = master + +PACKAGES += rabbit +pkg_rabbit_name = rabbit +pkg_rabbit_description = RabbitMQ Server +pkg_rabbit_homepage = https://www.rabbitmq.com/ +pkg_rabbit_fetch = git +pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git +pkg_rabbit_commit = master + +PACKAGES += rabbit_exchange_type_riak +pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak +pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak +pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange +pkg_rabbit_exchange_type_riak_fetch = git +pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange +pkg_rabbit_exchange_type_riak_commit = master + +PACKAGES += rack +pkg_rack_name = rack +pkg_rack_description = Rack handler for erlang +pkg_rack_homepage = https://github.com/erlyvideo/rack +pkg_rack_fetch = git +pkg_rack_repo = https://github.com/erlyvideo/rack +pkg_rack_commit = master + +PACKAGES += radierl +pkg_radierl_name = radierl +pkg_radierl_description = RADIUS protocol stack implemented in Erlang. +pkg_radierl_homepage = https://github.com/vances/radierl +pkg_radierl_fetch = git +pkg_radierl_repo = https://github.com/vances/radierl +pkg_radierl_commit = master + +PACKAGES += rafter +pkg_rafter_name = rafter +pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol +pkg_rafter_homepage = https://github.com/andrewjstone/rafter +pkg_rafter_fetch = git +pkg_rafter_repo = https://github.com/andrewjstone/rafter +pkg_rafter_commit = master + +PACKAGES += ranch +pkg_ranch_name = ranch +pkg_ranch_description = Socket acceptor pool for TCP protocols. +pkg_ranch_homepage = http://ninenines.eu +pkg_ranch_fetch = git +pkg_ranch_repo = https://github.com/ninenines/ranch +pkg_ranch_commit = 1.1.0 + +PACKAGES += rbeacon +pkg_rbeacon_name = rbeacon +pkg_rbeacon_description = LAN discovery and presence in Erlang. +pkg_rbeacon_homepage = https://github.com/refuge/rbeacon +pkg_rbeacon_fetch = git +pkg_rbeacon_repo = https://github.com/refuge/rbeacon +pkg_rbeacon_commit = master + +PACKAGES += rebar +pkg_rebar_name = rebar +pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. +pkg_rebar_homepage = http://www.rebar3.org +pkg_rebar_fetch = git +pkg_rebar_repo = https://github.com/rebar/rebar3 +pkg_rebar_commit = master + +PACKAGES += rebus +pkg_rebus_name = rebus +pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang. +pkg_rebus_homepage = https://github.com/olle/rebus +pkg_rebus_fetch = git +pkg_rebus_repo = https://github.com/olle/rebus +pkg_rebus_commit = master + +PACKAGES += rec2json +pkg_rec2json_name = rec2json +pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily. +pkg_rec2json_homepage = https://github.com/lordnull/rec2json +pkg_rec2json_fetch = git +pkg_rec2json_repo = https://github.com/lordnull/rec2json +pkg_rec2json_commit = master + +PACKAGES += recon +pkg_recon_name = recon +pkg_recon_description = Collection of functions and scripts to debug Erlang in production. +pkg_recon_homepage = https://github.com/ferd/recon +pkg_recon_fetch = git +pkg_recon_repo = https://github.com/ferd/recon +pkg_recon_commit = 2.2.1 + +PACKAGES += record_info +pkg_record_info_name = record_info +pkg_record_info_description = Convert between record and proplist +pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info +pkg_record_info_fetch = git +pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info +pkg_record_info_commit = master + +PACKAGES += redgrid +pkg_redgrid_name = redgrid +pkg_redgrid_description = automatic Erlang node discovery via redis +pkg_redgrid_homepage = https://github.com/jkvor/redgrid +pkg_redgrid_fetch = git +pkg_redgrid_repo = https://github.com/jkvor/redgrid +pkg_redgrid_commit = master + +PACKAGES += redo +pkg_redo_name = redo +pkg_redo_description = pipelined erlang redis client +pkg_redo_homepage = https://github.com/jkvor/redo +pkg_redo_fetch = git +pkg_redo_repo = https://github.com/jkvor/redo +pkg_redo_commit = master + +PACKAGES += reload_mk +pkg_reload_mk_name = reload_mk +pkg_reload_mk_description = Live reload plugin for erlang.mk. +pkg_reload_mk_homepage = https://github.com/bullno1/reload.mk +pkg_reload_mk_fetch = git +pkg_reload_mk_repo = https://github.com/bullno1/reload.mk +pkg_reload_mk_commit = master + +PACKAGES += reltool_util +pkg_reltool_util_name = reltool_util +pkg_reltool_util_description = Erlang reltool utility functionality application +pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util +pkg_reltool_util_fetch = git +pkg_reltool_util_repo = https://github.com/okeuday/reltool_util +pkg_reltool_util_commit = master + +PACKAGES += relx +pkg_relx_name = relx +pkg_relx_description = Sane, simple release creation for Erlang +pkg_relx_homepage = https://github.com/erlware/relx +pkg_relx_fetch = git +pkg_relx_repo = https://github.com/erlware/relx +pkg_relx_commit = master + +PACKAGES += resource_discovery +pkg_resource_discovery_name = resource_discovery +pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster. +pkg_resource_discovery_homepage = http://erlware.org/ +pkg_resource_discovery_fetch = git +pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery +pkg_resource_discovery_commit = master + +PACKAGES += restc +pkg_restc_name = restc +pkg_restc_description = Erlang Rest Client +pkg_restc_homepage = https://github.com/kivra/restclient +pkg_restc_fetch = git +pkg_restc_repo = https://github.com/kivra/restclient +pkg_restc_commit = master + +PACKAGES += rfc4627_jsonrpc +pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc +pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation. +pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627 +pkg_rfc4627_jsonrpc_fetch = git +pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627 +pkg_rfc4627_jsonrpc_commit = master + +PACKAGES += riak_control +pkg_riak_control_name = riak_control +pkg_riak_control_description = Webmachine-based administration interface for Riak. +pkg_riak_control_homepage = https://github.com/basho/riak_control +pkg_riak_control_fetch = git +pkg_riak_control_repo = https://github.com/basho/riak_control +pkg_riak_control_commit = master + +PACKAGES += riak_core +pkg_riak_core_name = riak_core +pkg_riak_core_description = Distributed systems infrastructure used by Riak. +pkg_riak_core_homepage = https://github.com/basho/riak_core +pkg_riak_core_fetch = git +pkg_riak_core_repo = https://github.com/basho/riak_core +pkg_riak_core_commit = master + +PACKAGES += riak_dt +pkg_riak_dt_name = riak_dt +pkg_riak_dt_description = Convergent replicated datatypes in Erlang +pkg_riak_dt_homepage = https://github.com/basho/riak_dt +pkg_riak_dt_fetch = git +pkg_riak_dt_repo = https://github.com/basho/riak_dt +pkg_riak_dt_commit = master + +PACKAGES += riak_ensemble +pkg_riak_ensemble_name = riak_ensemble +pkg_riak_ensemble_description = Multi-Paxos framework in Erlang +pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble +pkg_riak_ensemble_fetch = git +pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble +pkg_riak_ensemble_commit = master + +PACKAGES += riak_kv +pkg_riak_kv_name = riak_kv +pkg_riak_kv_description = Riak Key/Value Store +pkg_riak_kv_homepage = https://github.com/basho/riak_kv +pkg_riak_kv_fetch = git +pkg_riak_kv_repo = https://github.com/basho/riak_kv +pkg_riak_kv_commit = master + +PACKAGES += riak_pg +pkg_riak_pg_name = riak_pg +pkg_riak_pg_description = Distributed process groups with riak_core. +pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg +pkg_riak_pg_fetch = git +pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg +pkg_riak_pg_commit = master + +PACKAGES += riak_pipe +pkg_riak_pipe_name = riak_pipe +pkg_riak_pipe_description = Riak Pipelines +pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe +pkg_riak_pipe_fetch = git +pkg_riak_pipe_repo = https://github.com/basho/riak_pipe +pkg_riak_pipe_commit = master + +PACKAGES += riak_sysmon +pkg_riak_sysmon_name = riak_sysmon +pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages +pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon +pkg_riak_sysmon_fetch = git +pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon +pkg_riak_sysmon_commit = master + +PACKAGES += riak_test +pkg_riak_test_name = riak_test +pkg_riak_test_description = I'm in your cluster, testing your riaks +pkg_riak_test_homepage = https://github.com/basho/riak_test +pkg_riak_test_fetch = git +pkg_riak_test_repo = https://github.com/basho/riak_test +pkg_riak_test_commit = master + +PACKAGES += riakc +pkg_riakc_name = riakc +pkg_riakc_description = Erlang clients for Riak. +pkg_riakc_homepage = https://github.com/basho/riak-erlang-client +pkg_riakc_fetch = git +pkg_riakc_repo = https://github.com/basho/riak-erlang-client +pkg_riakc_commit = master + +PACKAGES += riakhttpc +pkg_riakhttpc_name = riakhttpc +pkg_riakhttpc_description = Riak Erlang client using the HTTP interface +pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client +pkg_riakhttpc_fetch = git +pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client +pkg_riakhttpc_commit = master + +PACKAGES += riaknostic +pkg_riaknostic_name = riaknostic +pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap +pkg_riaknostic_homepage = https://github.com/basho/riaknostic +pkg_riaknostic_fetch = git +pkg_riaknostic_repo = https://github.com/basho/riaknostic +pkg_riaknostic_commit = master + +PACKAGES += riakpool +pkg_riakpool_name = riakpool +pkg_riakpool_description = erlang riak client pool +pkg_riakpool_homepage = https://github.com/dweldon/riakpool +pkg_riakpool_fetch = git +pkg_riakpool_repo = https://github.com/dweldon/riakpool +pkg_riakpool_commit = master + +PACKAGES += rivus_cep +pkg_rivus_cep_name = rivus_cep +pkg_rivus_cep_description = Complex event processing in Erlang +pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep +pkg_rivus_cep_fetch = git +pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep +pkg_rivus_cep_commit = master + +PACKAGES += rlimit +pkg_rlimit_name = rlimit +pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent +pkg_rlimit_homepage = https://github.com/jlouis/rlimit +pkg_rlimit_fetch = git +pkg_rlimit_repo = https://github.com/jlouis/rlimit +pkg_rlimit_commit = master + +PACKAGES += safetyvalve +pkg_safetyvalve_name = safetyvalve +pkg_safetyvalve_description = A safety valve for your erlang node +pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve +pkg_safetyvalve_fetch = git +pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve +pkg_safetyvalve_commit = master + +PACKAGES += seestar +pkg_seestar_name = seestar +pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol +pkg_seestar_homepage = https://github.com/iamaleksey/seestar +pkg_seestar_fetch = git +pkg_seestar_repo = https://github.com/iamaleksey/seestar +pkg_seestar_commit = master + +PACKAGES += service +pkg_service_name = service +pkg_service_description = A minimal Erlang behavior for creating CloudI internal services +pkg_service_homepage = http://cloudi.org/ +pkg_service_fetch = git +pkg_service_repo = https://github.com/CloudI/service +pkg_service_commit = master + +PACKAGES += setup +pkg_setup_name = setup +pkg_setup_description = Generic setup utility for Erlang-based systems +pkg_setup_homepage = https://github.com/uwiger/setup +pkg_setup_fetch = git +pkg_setup_repo = https://github.com/uwiger/setup +pkg_setup_commit = master + +PACKAGES += sext +pkg_sext_name = sext +pkg_sext_description = Sortable Erlang Term Serialization +pkg_sext_homepage = https://github.com/uwiger/sext +pkg_sext_fetch = git +pkg_sext_repo = https://github.com/uwiger/sext +pkg_sext_commit = master + +PACKAGES += sfmt +pkg_sfmt_name = sfmt +pkg_sfmt_description = SFMT pseudo random number generator for Erlang. +pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang +pkg_sfmt_fetch = git +pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang +pkg_sfmt_commit = master + +PACKAGES += sgte +pkg_sgte_name = sgte +pkg_sgte_description = A simple Erlang Template Engine +pkg_sgte_homepage = https://github.com/filippo/sgte +pkg_sgte_fetch = git +pkg_sgte_repo = https://github.com/filippo/sgte +pkg_sgte_commit = master + +PACKAGES += sheriff +pkg_sheriff_name = sheriff +pkg_sheriff_description = Parse transform for type based validation. +pkg_sheriff_homepage = http://ninenines.eu +pkg_sheriff_fetch = git +pkg_sheriff_repo = https://github.com/extend/sheriff +pkg_sheriff_commit = master + +PACKAGES += shotgun +pkg_shotgun_name = shotgun +pkg_shotgun_description = better than just a gun +pkg_shotgun_homepage = https://github.com/inaka/shotgun +pkg_shotgun_fetch = git +pkg_shotgun_repo = https://github.com/inaka/shotgun +pkg_shotgun_commit = 0.1.0 + +PACKAGES += sidejob +pkg_sidejob_name = sidejob +pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang +pkg_sidejob_homepage = https://github.com/basho/sidejob +pkg_sidejob_fetch = git +pkg_sidejob_repo = https://github.com/basho/sidejob +pkg_sidejob_commit = master + +PACKAGES += sieve +pkg_sieve_name = sieve +pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang +pkg_sieve_homepage = https://github.com/benoitc/sieve +pkg_sieve_fetch = git +pkg_sieve_repo = https://github.com/benoitc/sieve +pkg_sieve_commit = master + +PACKAGES += sighandler +pkg_sighandler_name = sighandler +pkg_sighandler_description = Handle UNIX signals in Er lang +pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler +pkg_sighandler_fetch = git +pkg_sighandler_repo = https://github.com/jkingsbery/sighandler +pkg_sighandler_commit = master + +PACKAGES += simhash +pkg_simhash_name = simhash +pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data. +pkg_simhash_homepage = https://github.com/ferd/simhash +pkg_simhash_fetch = git +pkg_simhash_repo = https://github.com/ferd/simhash +pkg_simhash_commit = master + +PACKAGES += simple_bridge +pkg_simple_bridge_name = simple_bridge +pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers. +pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge +pkg_simple_bridge_fetch = git +pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge +pkg_simple_bridge_commit = master + +PACKAGES += simple_oauth2 +pkg_simple_oauth2_name = simple_oauth2 +pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured) +pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2 +pkg_simple_oauth2_fetch = git +pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2 +pkg_simple_oauth2_commit = master + +PACKAGES += skel +pkg_skel_name = skel +pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang +pkg_skel_homepage = https://github.com/ParaPhrase/skel +pkg_skel_fetch = git +pkg_skel_repo = https://github.com/ParaPhrase/skel +pkg_skel_commit = master + +PACKAGES += smother +pkg_smother_name = smother +pkg_smother_description = Extended code coverage metrics for Erlang. +pkg_smother_homepage = https://ramsay-t.github.io/Smother/ +pkg_smother_fetch = git +pkg_smother_repo = https://github.com/ramsay-t/Smother +pkg_smother_commit = master + +PACKAGES += social +pkg_social_name = social +pkg_social_description = Cowboy handler for social login via OAuth2 providers +pkg_social_homepage = https://github.com/dvv/social +pkg_social_fetch = git +pkg_social_repo = https://github.com/dvv/social +pkg_social_commit = master + +PACKAGES += spapi_router +pkg_spapi_router_name = spapi_router +pkg_spapi_router_description = Partially-connected Erlang clustering +pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router +pkg_spapi_router_fetch = git +pkg_spapi_router_repo = https://github.com/spilgames/spapi-router +pkg_spapi_router_commit = master + +PACKAGES += sqerl +pkg_sqerl_name = sqerl +pkg_sqerl_description = An Erlang-flavoured SQL DSL +pkg_sqerl_homepage = https://github.com/hairyhum/sqerl +pkg_sqerl_fetch = git +pkg_sqerl_repo = https://github.com/hairyhum/sqerl +pkg_sqerl_commit = master + +PACKAGES += srly +pkg_srly_name = srly +pkg_srly_description = Native Erlang Unix serial interface +pkg_srly_homepage = https://github.com/msantos/srly +pkg_srly_fetch = git +pkg_srly_repo = https://github.com/msantos/srly +pkg_srly_commit = master + +PACKAGES += sshrpc +pkg_sshrpc_name = sshrpc +pkg_sshrpc_description = Erlang SSH RPC module (experimental) +pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc +pkg_sshrpc_fetch = git +pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc +pkg_sshrpc_commit = master + +PACKAGES += stable +pkg_stable_name = stable +pkg_stable_description = Library of assorted helpers for Cowboy web server. +pkg_stable_homepage = https://github.com/dvv/stable +pkg_stable_fetch = git +pkg_stable_repo = https://github.com/dvv/stable +pkg_stable_commit = master + +PACKAGES += statebox +pkg_statebox_name = statebox +pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak. +pkg_statebox_homepage = https://github.com/mochi/statebox +pkg_statebox_fetch = git +pkg_statebox_repo = https://github.com/mochi/statebox +pkg_statebox_commit = master + +PACKAGES += statebox_riak +pkg_statebox_riak_name = statebox_riak +pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media. +pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak +pkg_statebox_riak_fetch = git +pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak +pkg_statebox_riak_commit = master + +PACKAGES += statman +pkg_statman_name = statman +pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM +pkg_statman_homepage = https://github.com/knutin/statman +pkg_statman_fetch = git +pkg_statman_repo = https://github.com/knutin/statman +pkg_statman_commit = master + +PACKAGES += statsderl +pkg_statsderl_name = statsderl +pkg_statsderl_description = StatsD client (erlang) +pkg_statsderl_homepage = https://github.com/lpgauth/statsderl +pkg_statsderl_fetch = git +pkg_statsderl_repo = https://github.com/lpgauth/statsderl +pkg_statsderl_commit = master + +PACKAGES += stdinout_pool +pkg_stdinout_pool_name = stdinout_pool +pkg_stdinout_pool_description = stdinout_pool : stuff goes in, stuff goes out. there's never any miscommunication. +pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool +pkg_stdinout_pool_fetch = git +pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool +pkg_stdinout_pool_commit = master + +PACKAGES += stockdb +pkg_stockdb_name = stockdb +pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang +pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb +pkg_stockdb_fetch = git +pkg_stockdb_repo = https://github.com/maxlapshin/stockdb +pkg_stockdb_commit = master + +PACKAGES += stripe +pkg_stripe_name = stripe +pkg_stripe_description = Erlang interface to the stripe.com API +pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang +pkg_stripe_fetch = git +pkg_stripe_repo = https://github.com/mattsta/stripe-erlang +pkg_stripe_commit = v1 + +PACKAGES += surrogate +pkg_surrogate_name = surrogate +pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes. +pkg_surrogate_homepage = https://github.com/skruger/Surrogate +pkg_surrogate_fetch = git +pkg_surrogate_repo = https://github.com/skruger/Surrogate +pkg_surrogate_commit = master + +PACKAGES += swab +pkg_swab_name = swab +pkg_swab_description = General purpose buffer handling module +pkg_swab_homepage = https://github.com/crownedgrouse/swab +pkg_swab_fetch = git +pkg_swab_repo = https://github.com/crownedgrouse/swab +pkg_swab_commit = master + +PACKAGES += swarm +pkg_swarm_name = swarm +pkg_swarm_description = Fast and simple acceptor pool for Erlang +pkg_swarm_homepage = https://github.com/jeremey/swarm +pkg_swarm_fetch = git +pkg_swarm_repo = https://github.com/jeremey/swarm +pkg_swarm_commit = master + +PACKAGES += switchboard +pkg_switchboard_name = switchboard +pkg_switchboard_description = A framework for processing email using worker plugins. +pkg_switchboard_homepage = https://github.com/thusfresh/switchboard +pkg_switchboard_fetch = git +pkg_switchboard_repo = https://github.com/thusfresh/switchboard +pkg_switchboard_commit = master + +PACKAGES += syn +pkg_syn_name = syn +pkg_syn_description = A global process registry for Erlang. +pkg_syn_homepage = https://github.com/ostinelli/syn +pkg_syn_fetch = git +pkg_syn_repo = https://github.com/ostinelli/syn +pkg_syn_commit = master + +PACKAGES += sync +pkg_sync_name = sync +pkg_sync_description = On-the-fly recompiling and reloading in Erlang. +pkg_sync_homepage = https://github.com/rustyio/sync +pkg_sync_fetch = git +pkg_sync_repo = https://github.com/rustyio/sync +pkg_sync_commit = master + +PACKAGES += syntaxerl +pkg_syntaxerl_name = syntaxerl +pkg_syntaxerl_description = Syntax checker for Erlang +pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl +pkg_syntaxerl_fetch = git +pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl +pkg_syntaxerl_commit = master + +PACKAGES += syslog +pkg_syslog_name = syslog +pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3) +pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog +pkg_syslog_fetch = git +pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog +pkg_syslog_commit = master + +PACKAGES += taskforce +pkg_taskforce_name = taskforce +pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks. +pkg_taskforce_homepage = https://github.com/g-andrade/taskforce +pkg_taskforce_fetch = git +pkg_taskforce_repo = https://github.com/g-andrade/taskforce +pkg_taskforce_commit = master + +PACKAGES += tddreloader +pkg_tddreloader_name = tddreloader +pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes +pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader +pkg_tddreloader_fetch = git +pkg_tddreloader_repo = https://github.com/version2beta/tddreloader +pkg_tddreloader_commit = master + +PACKAGES += tempo +pkg_tempo_name = tempo +pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang. +pkg_tempo_homepage = https://github.com/selectel/tempo +pkg_tempo_fetch = git +pkg_tempo_repo = https://github.com/selectel/tempo +pkg_tempo_commit = master + +PACKAGES += ticktick +pkg_ticktick_name = ticktick +pkg_ticktick_description = Ticktick is an id generator for message service. +pkg_ticktick_homepage = https://github.com/ericliang/ticktick +pkg_ticktick_fetch = git +pkg_ticktick_repo = https://github.com/ericliang/ticktick +pkg_ticktick_commit = master + +PACKAGES += tinymq +pkg_tinymq_name = tinymq +pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue +pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq +pkg_tinymq_fetch = git +pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq +pkg_tinymq_commit = master + +PACKAGES += tinymt +pkg_tinymt_name = tinymt +pkg_tinymt_description = TinyMT pseudo random number generator for Erlang. +pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang +pkg_tinymt_fetch = git +pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang +pkg_tinymt_commit = master + +PACKAGES += tirerl +pkg_tirerl_name = tirerl +pkg_tirerl_description = Erlang interface to Elastic Search +pkg_tirerl_homepage = https://github.com/inaka/tirerl +pkg_tirerl_fetch = git +pkg_tirerl_repo = https://github.com/inaka/tirerl +pkg_tirerl_commit = master + +PACKAGES += traffic_tools +pkg_traffic_tools_name = traffic_tools +pkg_traffic_tools_description = Simple traffic limiting library +pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools +pkg_traffic_tools_fetch = git +pkg_traffic_tools_repo = https://github.com/systra/traffic_tools +pkg_traffic_tools_commit = master + +PACKAGES += trails +pkg_trails_name = trails +pkg_trails_description = A couple of improvements over Cowboy Routes +pkg_trails_homepage = http://inaka.github.io/cowboy-trails/ +pkg_trails_fetch = git +pkg_trails_repo = https://github.com/inaka/cowboy-trails +pkg_trails_commit = master + +PACKAGES += trane +pkg_trane_name = trane +pkg_trane_description = SAX style broken HTML parser in Erlang +pkg_trane_homepage = https://github.com/massemanet/trane +pkg_trane_fetch = git +pkg_trane_repo = https://github.com/massemanet/trane +pkg_trane_commit = master + +PACKAGES += transit +pkg_transit_name = transit +pkg_transit_description = transit format for erlang +pkg_transit_homepage = https://github.com/isaiah/transit-erlang +pkg_transit_fetch = git +pkg_transit_repo = https://github.com/isaiah/transit-erlang +pkg_transit_commit = master + +PACKAGES += trie +pkg_trie_name = trie +pkg_trie_description = Erlang Trie Implementation +pkg_trie_homepage = https://github.com/okeuday/trie +pkg_trie_fetch = git +pkg_trie_repo = https://github.com/okeuday/trie +pkg_trie_commit = master + +PACKAGES += triq +pkg_triq_name = triq +pkg_triq_description = Trifork QuickCheck +pkg_triq_homepage = https://github.com/krestenkrab/triq +pkg_triq_fetch = git +pkg_triq_repo = https://github.com/krestenkrab/triq +pkg_triq_commit = master + +PACKAGES += tunctl +pkg_tunctl_name = tunctl +pkg_tunctl_description = Erlang TUN/TAP interface +pkg_tunctl_homepage = https://github.com/msantos/tunctl +pkg_tunctl_fetch = git +pkg_tunctl_repo = https://github.com/msantos/tunctl +pkg_tunctl_commit = master + +PACKAGES += twerl +pkg_twerl_name = twerl +pkg_twerl_description = Erlang client for the Twitter Streaming API +pkg_twerl_homepage = https://github.com/lucaspiller/twerl +pkg_twerl_fetch = git +pkg_twerl_repo = https://github.com/lucaspiller/twerl +pkg_twerl_commit = oauth + +PACKAGES += twitter_erlang +pkg_twitter_erlang_name = twitter_erlang +pkg_twitter_erlang_description = An Erlang twitter client +pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter +pkg_twitter_erlang_fetch = git +pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter +pkg_twitter_erlang_commit = master + +PACKAGES += ucol_nif +pkg_ucol_nif_name = ucol_nif +pkg_ucol_nif_description = ICU based collation Erlang module +pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif +pkg_ucol_nif_fetch = git +pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif +pkg_ucol_nif_commit = master + +PACKAGES += unicorn +pkg_unicorn_name = unicorn +pkg_unicorn_description = Generic configuration server +pkg_unicorn_homepage = https://github.com/shizzard/unicorn +pkg_unicorn_fetch = git +pkg_unicorn_repo = https://github.com/shizzard/unicorn +pkg_unicorn_commit = 0.3.0 + +PACKAGES += unsplit +pkg_unsplit_name = unsplit +pkg_unsplit_description = Resolves conflicts in Mnesia after network splits +pkg_unsplit_homepage = https://github.com/uwiger/unsplit +pkg_unsplit_fetch = git +pkg_unsplit_repo = https://github.com/uwiger/unsplit +pkg_unsplit_commit = master + +PACKAGES += uuid +pkg_uuid_name = uuid +pkg_uuid_description = Erlang UUID Implementation +pkg_uuid_homepage = https://github.com/okeuday/uuid +pkg_uuid_fetch = git +pkg_uuid_repo = https://github.com/okeuday/uuid +pkg_uuid_commit = v1.4.0 + +PACKAGES += ux +pkg_ux_name = ux +pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation) +pkg_ux_homepage = https://github.com/erlang-unicode/ux +pkg_ux_fetch = git +pkg_ux_repo = https://github.com/erlang-unicode/ux +pkg_ux_commit = master + +PACKAGES += vert +pkg_vert_name = vert +pkg_vert_description = Erlang binding to libvirt virtualization API +pkg_vert_homepage = https://github.com/msantos/erlang-libvirt +pkg_vert_fetch = git +pkg_vert_repo = https://github.com/msantos/erlang-libvirt +pkg_vert_commit = master + +PACKAGES += verx +pkg_verx_name = verx +pkg_verx_description = Erlang implementation of the libvirtd remote protocol +pkg_verx_homepage = https://github.com/msantos/verx +pkg_verx_fetch = git +pkg_verx_repo = https://github.com/msantos/verx +pkg_verx_commit = master + +PACKAGES += vmq_acl +pkg_vmq_acl_name = vmq_acl +pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_acl_homepage = https://verne.mq/ +pkg_vmq_acl_fetch = git +pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl +pkg_vmq_acl_commit = master + +PACKAGES += vmq_bridge +pkg_vmq_bridge_name = vmq_bridge +pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_bridge_homepage = https://verne.mq/ +pkg_vmq_bridge_fetch = git +pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge +pkg_vmq_bridge_commit = master + +PACKAGES += vmq_graphite +pkg_vmq_graphite_name = vmq_graphite +pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_graphite_homepage = https://verne.mq/ +pkg_vmq_graphite_fetch = git +pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite +pkg_vmq_graphite_commit = master + +PACKAGES += vmq_passwd +pkg_vmq_passwd_name = vmq_passwd +pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_passwd_homepage = https://verne.mq/ +pkg_vmq_passwd_fetch = git +pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd +pkg_vmq_passwd_commit = master + +PACKAGES += vmq_server +pkg_vmq_server_name = vmq_server +pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_server_homepage = https://verne.mq/ +pkg_vmq_server_fetch = git +pkg_vmq_server_repo = https://github.com/erlio/vmq_server +pkg_vmq_server_commit = master + +PACKAGES += vmq_snmp +pkg_vmq_snmp_name = vmq_snmp +pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_snmp_homepage = https://verne.mq/ +pkg_vmq_snmp_fetch = git +pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp +pkg_vmq_snmp_commit = master + +PACKAGES += vmq_systree +pkg_vmq_systree_name = vmq_systree +pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_systree_homepage = https://verne.mq/ +pkg_vmq_systree_fetch = git +pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree +pkg_vmq_systree_commit = master + +PACKAGES += vmstats +pkg_vmstats_name = vmstats +pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs. +pkg_vmstats_homepage = https://github.com/ferd/vmstats +pkg_vmstats_fetch = git +pkg_vmstats_repo = https://github.com/ferd/vmstats +pkg_vmstats_commit = master + +PACKAGES += walrus +pkg_walrus_name = walrus +pkg_walrus_description = Walrus - Mustache-like Templating +pkg_walrus_homepage = https://github.com/devinus/walrus +pkg_walrus_fetch = git +pkg_walrus_repo = https://github.com/devinus/walrus +pkg_walrus_commit = master + +PACKAGES += webmachine +pkg_webmachine_name = webmachine +pkg_webmachine_description = A REST-based system for building web applications. +pkg_webmachine_homepage = https://github.com/basho/webmachine +pkg_webmachine_fetch = git +pkg_webmachine_repo = https://github.com/basho/webmachine +pkg_webmachine_commit = master + +PACKAGES += websocket_client +pkg_websocket_client_name = websocket_client +pkg_websocket_client_description = Erlang websocket client (ws and wss supported) +pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client +pkg_websocket_client_fetch = git +pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client +pkg_websocket_client_commit = master + +PACKAGES += worker_pool +pkg_worker_pool_name = worker_pool +pkg_worker_pool_description = a simple erlang worker pool +pkg_worker_pool_homepage = https://github.com/inaka/worker_pool +pkg_worker_pool_fetch = git +pkg_worker_pool_repo = https://github.com/inaka/worker_pool +pkg_worker_pool_commit = 1.0.3 + +PACKAGES += wrangler +pkg_wrangler_name = wrangler +pkg_wrangler_description = Import of the Wrangler svn repository. +pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html +pkg_wrangler_fetch = git +pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler +pkg_wrangler_commit = master + +PACKAGES += wsock +pkg_wsock_name = wsock +pkg_wsock_description = Erlang library to build WebSocket clients and servers +pkg_wsock_homepage = https://github.com/madtrick/wsock +pkg_wsock_fetch = git +pkg_wsock_repo = https://github.com/madtrick/wsock +pkg_wsock_commit = master + +PACKAGES += xhttpc +pkg_xhttpc_name = xhttpc +pkg_xhttpc_description = Extensible HTTP Client for Erlang +pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc +pkg_xhttpc_fetch = git +pkg_xhttpc_repo = https://github.com/seriyps/xhttpc +pkg_xhttpc_commit = master + +PACKAGES += xref_runner +pkg_xref_runner_name = xref_runner +pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref) +pkg_xref_runner_homepage = https://github.com/inaka/xref_runner +pkg_xref_runner_fetch = git +pkg_xref_runner_repo = https://github.com/inaka/xref_runner +pkg_xref_runner_commit = 0.2.0 + +PACKAGES += yamerl +pkg_yamerl_name = yamerl +pkg_yamerl_description = YAML 1.2 parser in pure Erlang +pkg_yamerl_homepage = https://github.com/yakaz/yamerl +pkg_yamerl_fetch = git +pkg_yamerl_repo = https://github.com/yakaz/yamerl +pkg_yamerl_commit = master + +PACKAGES += yamler +pkg_yamler_name = yamler +pkg_yamler_description = libyaml-based yaml loader for Erlang +pkg_yamler_homepage = https://github.com/goertzenator/yamler +pkg_yamler_fetch = git +pkg_yamler_repo = https://github.com/goertzenator/yamler +pkg_yamler_commit = master + +PACKAGES += yaws +pkg_yaws_name = yaws +pkg_yaws_description = Yaws webserver +pkg_yaws_homepage = http://yaws.hyber.org +pkg_yaws_fetch = git +pkg_yaws_repo = https://github.com/klacke/yaws +pkg_yaws_commit = master + +PACKAGES += zab_engine +pkg_zab_engine_name = zab_engine +pkg_zab_engine_description = zab propotocol implement by erlang +pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine +pkg_zab_engine_fetch = git +pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine +pkg_zab_engine_commit = master + +PACKAGES += zeta +pkg_zeta_name = zeta +pkg_zeta_description = HTTP access log parser in Erlang +pkg_zeta_homepage = https://github.com/s1n4/zeta +pkg_zeta_fetch = git +pkg_zeta_repo = https://github.com/s1n4/zeta +pkg_zeta_commit = + +PACKAGES += zippers +pkg_zippers_name = zippers +pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers +pkg_zippers_homepage = https://github.com/ferd/zippers +pkg_zippers_fetch = git +pkg_zippers_repo = https://github.com/ferd/zippers +pkg_zippers_commit = master + +PACKAGES += zlists +pkg_zlists_name = zlists +pkg_zlists_description = Erlang lazy lists library. +pkg_zlists_homepage = https://github.com/vjache/erlang-zlists +pkg_zlists_fetch = git +pkg_zlists_repo = https://github.com/vjache/erlang-zlists +pkg_zlists_commit = master + +PACKAGES += zraft_lib +pkg_zraft_lib_name = zraft_lib +pkg_zraft_lib_description = Erlang raft consensus protocol implementation +pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib +pkg_zraft_lib_fetch = git +pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib +pkg_zraft_lib_commit = master + +PACKAGES += zucchini +pkg_zucchini_name = zucchini +pkg_zucchini_description = An Erlang INI parser +pkg_zucchini_homepage = https://github.com/devinus/zucchini +pkg_zucchini_fetch = git +pkg_zucchini_repo = https://github.com/devinus/zucchini +pkg_zucchini_commit = master + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: search + +define pkg_print + $(verbose) printf "%s\n" \ + $(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name: $(1)") \ + "App name: $(pkg_$(1)_name)" \ + "Description: $(pkg_$(1)_description)" \ + "Home page: $(pkg_$(1)_homepage)" \ + "Fetch with: $(pkg_$(1)_fetch)" \ + "Repository: $(pkg_$(1)_repo)" \ + "Commit: $(pkg_$(1)_commit)" \ + "" + +endef + +search: +ifdef q + $(foreach p,$(PACKAGES), \ + $(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \ + $(call pkg_print,$(p)))) +else + $(foreach p,$(PACKAGES),$(call pkg_print,$(p))) +endif + +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: fetch-deps list-deps distclean-deps + +# Configuration. + +ifdef OTP_DEPS +$(warning The variable OTP_DEPS is deprecated in favor of LOCAL_DEPS.) +endif + +IGNORE_DEPS ?= +export IGNORE_DEPS + +APPS_DIR ?= $(CURDIR)/apps +export APPS_DIR + +DEPS_DIR ?= $(CURDIR)/deps +export DEPS_DIR + +REBAR_DEPS_DIR = $(DEPS_DIR) +export REBAR_DEPS_DIR + +dep_name = $(if $(dep_$(1)),$(1),$(if $(pkg_$(1)_name),$(pkg_$(1)_name),$(1))) +dep_repo = $(patsubst git://github.com/%,https://github.com/%, \ + $(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo))) +dep_commit = $(if $(dep_$(1)_commit),$(dep_$(1)_commit),$(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit))) + +ALL_APPS_DIRS = $(if $(wildcard $(APPS_DIR)/),$(filter-out $(APPS_DIR),$(shell find $(APPS_DIR) -maxdepth 1 -type d))) +ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(foreach dep,$(filter-out $(IGNORE_DEPS),$(BUILD_DEPS) $(DEPS)),$(call dep_name,$(dep)))) + +ifeq ($(filter $(APPS_DIR) $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),) +ifeq ($(ERL_LIBS),) + ERL_LIBS = $(APPS_DIR):$(DEPS_DIR) +else + ERL_LIBS := $(ERL_LIBS):$(APPS_DIR):$(DEPS_DIR) +endif +endif +export ERL_LIBS + +export NO_AUTOPATCH + +# Verbosity. + +dep_verbose_0 = @echo " DEP " $(1); +dep_verbose = $(dep_verbose_$(V)) + +# Core targets. + +ifneq ($(SKIP_DEPS),) +fetch-deps: +else +fetch-deps: $(ALL_DEPS_DIRS) +ifndef IS_APP + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep fetch-deps IS_APP=1 || exit $$?; \ + done +endif +ifneq ($(IS_DEP),1) + $(verbose) rm -f $(ERLANG_MK_TMP)/fetch-deps.log +endif + $(verbose) mkdir -p $(ERLANG_MK_TMP) + $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \ + if ! grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/fetch-deps.log; then \ + echo $$dep >> $(ERLANG_MK_TMP)/fetch-deps.log; \ + if [ -f $$dep/erlang.mk ]; then \ + $(MAKE) -C $$dep fetch-deps IS_DEP=1 || exit $$?; \ + fi \ + fi \ + done +endif + +ifneq ($(SKIP_DEPS),) +deps:: +else +deps:: $(ALL_DEPS_DIRS) +ifndef IS_APP + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep IS_APP=1 || exit $$?; \ + done +endif +ifneq ($(IS_DEP),1) + $(verbose) rm -f $(ERLANG_MK_TMP)/deps.log +endif + $(verbose) mkdir -p $(ERLANG_MK_TMP) + $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \ + if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \ + :; \ + else \ + echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \ + if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \ + $(MAKE) -C $$dep IS_DEP=1 || exit $$?; \ + else \ + echo "Error: No Makefile to build dependency $$dep."; \ + exit 2; \ + fi \ + fi \ + done +endif + +ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/list-deps.log + +$(ERLANG_MK_RECURSIVE_DEPS_LIST): fetch-deps +ifneq ($(IS_DEP),1) + $(verbose) rm -f $(ERLANG_MK_TMP)/list-deps.log.orig +endif +ifndef IS_APP + $(verbose) for app in $(filter-out $(CURDIR),$(ALL_APPS_DIRS)); do \ + $(MAKE) -C "$$app" --no-print-directory \ + $(ERLANG_MK_RECURSIVE_DEPS_LIST) IS_APP=1 || :; \ + done +endif + $(verbose) for dep in $(filter-out $(CURDIR),$(ALL_DEPS_DIRS)); do \ + (test -f "$$dep/erlang.mk" && \ + $(MAKE) -C "$$dep" --no-print-directory \ + $(ERLANG_MK_RECURSIVE_DEPS_LIST) IS_DEP=1) || :; \ + done + $(verbose) for dep in $(DEPS); do \ + echo $(DEPS_DIR)/$$dep; \ + done >> $(ERLANG_MK_TMP)/list-deps.log.orig +ifndef IS_APP +ifneq ($(IS_DEP),1) + $(verbose) sort < $(ERLANG_MK_TMP)/list-deps.log.orig \ + | uniq > $(ERLANG_MK_TMP)/list-deps.log + $(verbose) rm -f $(ERLANG_MK_TMP)/list-deps.log.orig +endif +endif + +list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST) + $(verbose) cat $(ERLANG_MK_TMP)/list-deps.log + +# Deps related targets. + +# @todo rename GNUmakefile and makefile into Makefile first, if they exist +# While Makefile file could be GNUmakefile or makefile, +# in practice only Makefile is needed so far. +define dep_autopatch + if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ + if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \ + $(call dep_autopatch2,$(1)); \ + elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \ + $(call dep_autopatch2,$(1)); \ + elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \ + $(call dep_autopatch2,$(1)); \ + else \ + if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \ + $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ + $(call dep_autopatch_erlang_mk,$(1)); \ + else \ + $(call erlang,$(call dep_autopatch_app.erl,$(1))); \ + fi \ + fi \ + else \ + if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \ + $(call dep_autopatch_noop,$(1)); \ + else \ + $(call dep_autopatch2,$(1)); \ + fi \ + fi +endef + +define dep_autopatch2 + $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ + if [ -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \ + $(call dep_autopatch_fetch_rebar); \ + $(call dep_autopatch_rebar,$(1)); \ + else \ + $(call dep_autopatch_gen,$(1)); \ + fi +endef + +define dep_autopatch_noop + printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile +endef + +# Overwrite erlang.mk with the current file by default. +ifeq ($(NO_AUTOPATCH_ERLANG_MK),) +define dep_autopatch_erlang_mk + echo "include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk" \ + > $(DEPS_DIR)/$1/erlang.mk +endef +else +define dep_autopatch_erlang_mk + : +endef +endif + +define dep_autopatch_gen + printf "%s\n" \ + "ERLC_OPTS = +debug_info" \ + "include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile +endef + +define dep_autopatch_fetch_rebar + mkdir -p $(ERLANG_MK_TMP); \ + if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \ + git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \ + cd $(ERLANG_MK_TMP)/rebar; \ + git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \ + $(MAKE); \ + cd -; \ + fi +endef + +define dep_autopatch_rebar + if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ + mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \ + fi; \ + $(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \ + rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app +endef + +define dep_autopatch_rebar.erl + application:load(rebar), + application:set_env(rebar, log_level, debug), + Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of + {ok, Conf0} -> Conf0; + _ -> [] + end, + {Conf, OsEnv} = fun() -> + case filelib:is_file("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)") of + false -> {Conf1, []}; + true -> + Bindings0 = erl_eval:new_bindings(), + Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0), + Bindings = erl_eval:add_binding('SCRIPT', "$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings1), + Before = os:getenv(), + {ok, Conf2} = file:script("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config.script)", Bindings), + {Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)} + end + end(), + Write = fun (Text) -> + file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append]) + end, + Escape = fun (Text) -> + re:replace(Text, "\\\\$$$$", "\$$$$$$$$", [global, {return, list}]) + end, + Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package " + "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"), + Write("C_SRC_DIR = /path/do/not/exist\n"), + Write("C_SRC_TYPE = rebar\n"), + Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"), + Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]), + fun() -> + Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"), + case lists:keyfind(erl_opts, 1, Conf) of + false -> ok; + {_, ErlOpts} -> + lists:foreach(fun + ({d, D}) -> + Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n"); + ({i, I}) -> + Write(["ERLC_OPTS += -I ", I, "\n"]); + ({platform_define, Regex, D}) -> + case rebar_utils:is_arch(Regex) of + true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n"); + false -> ok + end; + ({parse_transform, PT}) -> + Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n"); + (_) -> ok + end, ErlOpts) + end, + Write("\n") + end(), + fun() -> + File = case lists:keyfind(deps, 1, Conf) of + false -> []; + {_, Deps} -> + [begin case case Dep of + {N, S} when is_atom(N), is_list(S) -> {N, {hex, S}}; + {N, S} when is_tuple(S) -> {N, S}; + {N, _, S} -> {N, S}; + {N, _, S, _} -> {N, S}; + _ -> false + end of + false -> ok; + {Name, Source} -> + {Method, Repo, Commit} = case Source of + {hex, V} -> {hex, V, undefined}; + {git, R} -> {git, R, master}; + {M, R, {branch, C}} -> {M, R, C}; + {M, R, {ref, C}} -> {M, R, C}; + {M, R, {tag, C}} -> {M, R, C}; + {M, R, C} -> {M, R, C} + end, + Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit])) + end end || Dep <- Deps] + end + end(), + fun() -> + case lists:keyfind(erl_first_files, 1, Conf) of + false -> ok; + {_, Files} -> + Names = [[" ", case lists:reverse(F) of + "lre." ++ Elif -> lists:reverse(Elif); + Elif -> lists:reverse(Elif) + end] || "src/" ++ F <- Files], + Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names])) + end + end(), + FindFirst = fun(F, Fd) -> + case io:parse_erl_form(Fd, undefined) of + {ok, {attribute, _, compile, {parse_transform, PT}}, _} -> + [PT, F(F, Fd)]; + {ok, {attribute, _, compile, CompileOpts}, _} when is_list(CompileOpts) -> + case proplists:get_value(parse_transform, CompileOpts) of + undefined -> [F(F, Fd)]; + PT -> [PT, F(F, Fd)] + end; + {ok, {attribute, _, include, Hrl}, _} -> + case file:open("$(call core_native_path,$(DEPS_DIR)/$1/include/)" ++ Hrl, [read]) of + {ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)]; + _ -> + case file:open("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ Hrl, [read]) of + {ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)]; + _ -> [F(F, Fd)] + end + end; + {ok, {attribute, _, include_lib, "$(1)/include/" ++ Hrl}, _} -> + {ok, HrlFd} = file:open("$(call core_native_path,$(DEPS_DIR)/$1/include/)" ++ Hrl, [read]), + [F(F, HrlFd), F(F, Fd)]; + {ok, {attribute, _, include_lib, Hrl}, _} -> + case file:open("$(call core_native_path,$(DEPS_DIR)/$1/include/)" ++ Hrl, [read]) of + {ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)]; + _ -> [F(F, Fd)] + end; + {ok, {attribute, _, import, {Imp, _}}, _} -> + case file:open("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(Imp) ++ ".erl", [read]) of + {ok, ImpFd} -> [Imp, F(F, ImpFd), F(F, Fd)]; + _ -> [F(F, Fd)] + end; + {eof, _} -> + file:close(Fd), + []; + _ -> + F(F, Fd) + end + end, + fun() -> + ErlFiles = filelib:wildcard("$(call core_native_path,$(DEPS_DIR)/$1/src/)*.erl"), + First0 = lists:usort(lists:flatten([begin + {ok, Fd} = file:open(F, [read]), + FindFirst(FindFirst, Fd) + end || F <- ErlFiles])), + First = lists:flatten([begin + {ok, Fd} = file:open("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(M) ++ ".erl", [read]), + FindFirst(FindFirst, Fd) + end || M <- First0, lists:member("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(M) ++ ".erl", ErlFiles)]) ++ First0, + Write(["COMPILE_FIRST +=", [[" ", atom_to_list(M)] || M <- First, + lists:member("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(M) ++ ".erl", ErlFiles)], "\n"]) + end(), + Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"), + Write("\npreprocess::\n"), + Write("\npre-deps::\n"), + Write("\npre-app::\n"), + PatchHook = fun(Cmd) -> + case Cmd of + "make -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1); + "gmake -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1); + "make " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); + "gmake " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); + _ -> Escape(Cmd) + end + end, + fun() -> + case lists:keyfind(pre_hooks, 1, Conf) of + false -> ok; + {_, Hooks} -> + [case H of + {'get-deps', Cmd} -> + Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n"); + {compile, Cmd} -> + Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); + {Regex, compile, Cmd} -> + case rebar_utils:is_arch(Regex) of + true -> Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); + false -> ok + end; + _ -> ok + end || H <- Hooks] + end + end(), + ShellToMk = fun(V) -> + re:replace(re:replace(V, "(\\\\$$$$)(\\\\w*)", "\\\\1(\\\\2)", [global]), + "-Werror\\\\b", "", [{return, list}, global]) + end, + PortSpecs = fun() -> + case lists:keyfind(port_specs, 1, Conf) of + false -> + case filelib:is_dir("$(call core_native_path,$(DEPS_DIR)/$1/c_src)") of + false -> []; + true -> + [{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"), + proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}] + end; + {_, Specs} -> + lists:flatten([case S of + {Output, Input} -> {ShellToMk(Output), Input, []}; + {Regex, Output, Input} -> + case rebar_utils:is_arch(Regex) of + true -> {ShellToMk(Output), Input, []}; + false -> [] + end; + {Regex, Output, Input, [{env, Env}]} -> + case rebar_utils:is_arch(Regex) of + true -> {ShellToMk(Output), Input, Env}; + false -> [] + end + end || S <- Specs]) + end + end(), + PortSpecWrite = fun (Text) -> + file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/c_src/Makefile.erlang.mk)", Text, [append]) + end, + case PortSpecs of + [] -> ok; + _ -> + Write("\npre-app::\n\t$$$$\(MAKE) -f c_src/Makefile.erlang.mk\n"), + PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n", + [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])), + PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n", + [code:lib_dir(erl_interface, lib)])), + [PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv], + FilterEnv = fun(Env) -> + lists:flatten([case E of + {_, _} -> E; + {Regex, K, V} -> + case rebar_utils:is_arch(Regex) of + true -> {K, V}; + false -> [] + end + end || E <- Env]) + end, + MergeEnv = fun(Env) -> + lists:foldl(fun ({K, V}, Acc) -> + case lists:keyfind(K, 1, Acc) of + false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc]; + {_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc] + end + end, [], Env) + end, + PortEnv = case lists:keyfind(port_env, 1, Conf) of + false -> []; + {_, PortEnv0} -> FilterEnv(PortEnv0) + end, + PortSpec = fun ({Output, Input0, Env}) -> + filelib:ensure_dir("$(call core_native_path,$(DEPS_DIR)/$1/)" ++ Output), + Input = [[" ", I] || I <- Input0], + PortSpecWrite([ + [["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))], + case $(PLATFORM) of + darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress"; + _ -> "" + end, + "\n\nall:: ", Output, "\n\n", + "%.o: %.c\n\t$$$$\(CC) -c -o $$$$\@ $$$$\< $$$$\(CFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", + "%.o: %.C\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", + "%.o: %.cc\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", + "%.o: %.cpp\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", + [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))], + Output, ": $$$$\(foreach ext,.c .C .cc .cpp,", + "$$$$\(patsubst %$$$$\(ext),%.o,$$$$\(filter %$$$$\(ext),$$$$\(wildcard", Input, "))))\n", + "\t$$$$\(CC) -o $$$$\@ $$$$\? $$$$\(LDFLAGS) $$$$\(ERL_LDFLAGS) $$$$\(DRV_LDFLAGS) $$$$\(EXE_LDFLAGS)", + case filename:extension(Output) of + [] -> "\n"; + _ -> " -shared\n" + end]) + end, + [PortSpec(S) || S <- PortSpecs] + end, + Write("\ninclude $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(DEPS_DIR)/app)/erlang.mk"), + RunPlugin = fun(Plugin, Step) -> + case erlang:function_exported(Plugin, Step, 2) of + false -> ok; + true -> + c:cd("$(call core_native_path,$(DEPS_DIR)/$1/)"), + Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(), + dict:store(base_dir, "", dict:new())}, undefined), + io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret]) + end + end, + fun() -> + case lists:keyfind(plugins, 1, Conf) of + false -> ok; + {_, Plugins} -> + [begin + case lists:keyfind(deps, 1, Conf) of + false -> ok; + {_, Deps} -> + case lists:keyfind(P, 1, Deps) of + false -> ok; + _ -> + Path = "$(call core_native_path,$(DEPS_DIR)/)" ++ atom_to_list(P), + io:format("~s", [os:cmd("$(MAKE) -C $(call core_native_path,$(DEPS_DIR)/$1) " ++ Path)]), + io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]), + code:add_patha(Path ++ "/ebin") + end + end + end || P <- Plugins], + [case code:load_file(P) of + {module, P} -> ok; + _ -> + case lists:keyfind(plugin_dir, 1, Conf) of + false -> ok; + {_, PluginsDir} -> + ErlFile = "$(call core_native_path,$(DEPS_DIR)/$1/)" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl", + {ok, P, Bin} = compile:file(ErlFile, [binary]), + {module, P} = code:load_binary(P, ErlFile, Bin) + end + end || P <- Plugins], + [RunPlugin(P, preprocess) || P <- Plugins], + [RunPlugin(P, pre_compile) || P <- Plugins], + [RunPlugin(P, compile) || P <- Plugins] + end + end(), + halt() +endef + +define dep_autopatch_app.erl + UpdateModules = fun(App) -> + case filelib:is_regular(App) of + false -> ok; + true -> + {ok, [{application, '$(1)', L0}]} = file:consult(App), + Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$$$", true, + fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []), + L = lists:keystore(modules, 1, L0, {modules, Mods}), + ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}])) + end + end, + UpdateModules("$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"), + halt() +endef + +define dep_autopatch_appsrc.erl + AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", + AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end, + case filelib:is_regular(AppSrcIn) of + false -> ok; + true -> + {ok, [{application, $(1), L0}]} = file:consult(AppSrcIn), + L1 = lists:keystore(modules, 1, L0, {modules, []}), + L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end, + L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end, + ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])), + case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end + end, + halt() +endef + +define dep_fetch_git + git clone -q -n -- $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ + cd $(DEPS_DIR)/$(call dep_name,$(1)) && git checkout -q $(call dep_commit,$(1)); +endef + +define dep_fetch_git-submodule + git submodule update --init -- $(DEPS_DIR)/$1; +endef + +define dep_fetch_hg + hg clone -q -U $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ + cd $(DEPS_DIR)/$(call dep_name,$(1)) && hg update -q $(call dep_commit,$(1)); +endef + +define dep_fetch_svn + svn checkout -q $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); +endef + +define dep_fetch_cp + cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); +endef + +define dep_fetch_hex.erl + ssl:start(), + inets:start(), + {ok, {{_, 200, _}, _, Body}} = httpc:request(get, + {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []}, + [], [{body_format, binary}]), + {ok, Files} = erl_tar:extract({binary, Body}, [memory]), + {_, Source} = lists:keyfind("contents.tar.gz", 1, Files), + ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]), + halt() +endef + +# Hex only has a package version. No need to look in the Erlang.mk packages. +define dep_fetch_hex + $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1)))))); +endef + +define dep_fetch_fail + echo "Error: Unknown or invalid dependency: $(1)." >&2; \ + exit 78; +endef + +# Kept for compatibility purposes with older Erlang.mk configuration. +define dep_fetch_legacy + $(warning WARNING: '$(1)' dependency configuration uses deprecated format.) \ + git clone -q -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1); \ + cd $(DEPS_DIR)/$(1) && git checkout -q $(if $(word 2,$(dep_$(1))),$(word 2,$(dep_$(1))),master); +endef + +define dep_fetch + $(if $(dep_$(1)), \ + $(if $(dep_fetch_$(word 1,$(dep_$(1)))), \ + $(word 1,$(dep_$(1))), \ + $(if $(IS_DEP),legacy,fail)), \ + $(if $(filter $(1),$(PACKAGES)), \ + $(pkg_$(1)_fetch), \ + fail)) +endef + +define dep_target +$(DEPS_DIR)/$(call dep_name,$1): + $(eval DEP_NAME := $(call dep_name,$1)) + $(eval DEP_STR := $(if $(filter-out $1,$(DEP_NAME)),$1,"$1 ($(DEP_NAME))")) + $(verbose) if test -d $(APPS_DIR)/$(DEP_NAME); then \ + echo "Error: Dependency" $(DEP_STR) "conflicts with application found in $(APPS_DIR)/$(DEP_NAME)."; \ + exit 17; \ + fi + $(verbose) mkdir -p $(DEPS_DIR) + $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$1)),$1) + $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure.ac -o -f $(DEPS_DIR)/$(DEP_NAME)/configure.in ]; then \ + echo " AUTO " $(DEP_STR); \ + cd $(DEPS_DIR)/$(DEP_NAME) && autoreconf -Wall -vif -I m4; \ + fi + - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \ + echo " CONF " $(DEP_STR); \ + cd $(DEPS_DIR)/$(DEP_NAME) && ./configure; \ + fi +ifeq ($(filter $(1),$(NO_AUTOPATCH)),) + $(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \ + if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \ + echo " PATCH Downloading rabbitmq-codegen"; \ + git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ + fi; \ + if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \ + echo " PATCH Downloading rabbitmq-server"; \ + git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \ + fi; \ + ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \ + elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \ + if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \ + echo " PATCH Downloading rabbitmq-codegen"; \ + git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ + fi \ + else \ + $(call dep_autopatch,$(DEP_NAME)) \ + fi +endif +endef + +$(foreach dep,$(BUILD_DEPS) $(DEPS),$(eval $(call dep_target,$(dep)))) + +ifndef IS_APP +clean:: clean-apps + +clean-apps: + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep clean IS_APP=1 || exit $$?; \ + done + +distclean:: distclean-apps + +distclean-apps: + $(verbose) for dep in $(ALL_APPS_DIRS) ; do \ + $(MAKE) -C $$dep distclean IS_APP=1 || exit $$?; \ + done +endif + +ifndef SKIP_DEPS +distclean:: distclean-deps + +distclean-deps: + $(gen_verbose) rm -rf $(DEPS_DIR) +endif + +# External plugins. + +DEP_PLUGINS ?= + +define core_dep_plugin +-include $(DEPS_DIR)/$(1) + +$(DEPS_DIR)/$(1): $(DEPS_DIR)/$(2) ; +endef + +$(foreach p,$(DEP_PLUGINS),\ + $(eval $(if $(findstring /,$p),\ + $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\ + $(call core_dep_plugin,$p/plugins.mk,$p)))) + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# Verbosity. + +proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F)); +proto_verbose = $(proto_verbose_$(V)) + +# Core targets. + +define compile_proto + $(verbose) mkdir -p ebin/ include/ + $(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1))) + $(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl + $(verbose) rm ebin/*.erl +endef + +define compile_proto.erl + [begin + Dir = filename:dirname(filename:dirname(F)), + protobuffs_compile:generate_source(F, + [{output_include_dir, Dir ++ "/include"}, + {output_src_dir, Dir ++ "/ebin"}]) + end || F <- string:tokens("$(1)", " ")], + halt(). +endef + +ifneq ($(wildcard src/),) +ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto)) + $(if $(strip $?),$(call compile_proto,$?)) +endif + +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: clean-app + +# Configuration. + +ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \ + +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec +COMPILE_FIRST ?= +COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST))) +ERLC_EXCLUDE ?= +ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE))) + +ERLC_MIB_OPTS ?= +COMPILE_MIB_FIRST ?= +COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST))) + +# Verbosity. + +app_verbose_0 = @echo " APP " $(PROJECT); +app_verbose = $(app_verbose_$(V)) + +appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; +appsrc_verbose = $(appsrc_verbose_$(V)) + +makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d; +makedep_verbose = $(makedep_verbose_$(V)) + +erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ + $(filter %.erl %.core,$(?F))); +erlc_verbose = $(erlc_verbose_$(V)) + +xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); +xyrl_verbose = $(xyrl_verbose_$(V)) + +asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F)); +asn1_verbose = $(asn1_verbose_$(V)) + +mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F)); +mib_verbose = $(mib_verbose_$(V)) + +ifneq ($(wildcard src/),) + +# Targets. + +ifeq ($(wildcard ebin/test),) +app:: $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build +else +app:: clean $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build +endif + +ifeq ($(wildcard src/$(PROJECT)_app.erl),) +define app_file +{application, $(PROJECT), [ + {description, "$(PROJECT_DESCRIPTION)"}, + {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), + {id$(comma)$(space)"$(1)"}$(comma)) + {modules, [$(call comma_list,$(2))]}, + {registered, []}, + {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]} +]}. +endef +else +define app_file +{application, $(PROJECT), [ + {description, "$(PROJECT_DESCRIPTION)"}, + {vsn, "$(PROJECT_VERSION)"},$(if $(IS_DEP), + {id$(comma)$(space)"$(1)"}$(comma)) + {modules, [$(call comma_list,$(2))]}, + {registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]}, + {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}, + {mod, {$(PROJECT)_app, []}} +]}. +endef +endif + +app-build: ebin/$(PROJECT).app + $(verbose) : + +# Source files. + +ERL_FILES = $(sort $(call core_find,src/,*.erl)) +CORE_FILES = $(sort $(call core_find,src/,*.core)) + +# ASN.1 files. + +ifneq ($(wildcard asn1/),) +ASN1_FILES = $(sort $(call core_find,asn1/,*.asn1)) +ERL_FILES += $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) + +define compile_asn1 + $(verbose) mkdir -p include/ + $(asn1_verbose) erlc -v -I include/ -o asn1/ +noobj $(1) + $(verbose) mv asn1/*.erl src/ + $(verbose) mv asn1/*.hrl include/ + $(verbose) mv asn1/*.asn1db include/ +endef + +$(PROJECT).d:: $(ASN1_FILES) + $(if $(strip $?),$(call compile_asn1,$?)) +endif + +# SNMP MIB files. + +ifneq ($(wildcard mibs/),) +MIB_FILES = $(sort $(call core_find,mibs/,*.mib)) + +$(PROJECT).d:: $(COMPILE_MIB_FIRST_PATHS) $(MIB_FILES) + $(verbose) mkdir -p include/ priv/mibs/ + $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ -I priv/mibs/ $? + $(mib_verbose) erlc -o include/ -- $(addprefix priv/mibs/,$(patsubst %.mib,%.bin,$(notdir $?))) +endif + +# Leex and Yecc files. + +XRL_FILES = $(sort $(call core_find,src/,*.xrl)) +XRL_ERL_FILES = $(addprefix src/,$(patsubst %.xrl,%.erl,$(notdir $(XRL_FILES)))) +ERL_FILES += $(XRL_ERL_FILES) + +YRL_FILES = $(sort $(call core_find,src/,*.yrl)) +YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES)))) +ERL_FILES += $(YRL_ERL_FILES) + +$(PROJECT).d:: $(XRL_FILES) $(YRL_FILES) + $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?) + +# Erlang and Core Erlang files. + +define makedep.erl + ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")), + Modules = [{filename:basename(F, ".erl"), F} || F <- ErlFiles], + Add = fun (Dep, Acc) -> + case lists:keyfind(atom_to_list(Dep), 1, Modules) of + {_, DepFile} -> [DepFile|Acc]; + false -> Acc + end + end, + AddHd = fun (Dep, Acc) -> + case {Dep, lists:keymember(Dep, 2, Modules)} of + {"src/" ++ _, false} -> [Dep|Acc]; + {"include/" ++ _, false} -> [Dep|Acc]; + _ -> Acc + end + end, + CompileFirst = fun (Deps) -> + First0 = [case filename:extension(D) of + ".erl" -> filename:basename(D, ".erl"); + _ -> [] + end || D <- Deps], + case lists:usort(First0) of + [] -> []; + [[]] -> []; + First -> ["COMPILE_FIRST +=", [[" ", F] || F <- First], "\n"] + end + end, + Depend = [begin + case epp:parse_file(F, ["include/"], []) of + {ok, Forms} -> + Deps = lists:usort(lists:foldl(fun + ({attribute, _, behavior, Dep}, Acc) -> Add(Dep, Acc); + ({attribute, _, behaviour, Dep}, Acc) -> Add(Dep, Acc); + ({attribute, _, compile, {parse_transform, Dep}}, Acc) -> Add(Dep, Acc); + ({attribute, _, file, {Dep, _}}, Acc) -> AddHd(Dep, Acc); + (_, Acc) -> Acc + end, [], Forms)), + case Deps of + [] -> ""; + _ -> [F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n", CompileFirst(Deps)] + end; + {error, enoent} -> + [] + end + end || F <- ErlFiles], + ok = file:write_file("$(1)", Depend), + halt() +endef + +ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),) +$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) + $(makedep_verbose) $(call erlang,$(call makedep.erl,$@)) +endif + +# Rebuild everything when the Makefile changes. +$(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(MAKEFILE_LIST) + @touch $@ + +-include $(PROJECT).d + +ebin/$(PROJECT).app:: ebin/ + +ebin/: + $(verbose) mkdir -p ebin/ + +define compile_erl + $(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \ + -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),$(COMPILE_FIRST_PATHS) $(1)) +endef + +ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.src) + $(eval FILES_TO_COMPILE := $(filter-out src/$(PROJECT).app.src,$?)) + $(if $(strip $(FILES_TO_COMPILE)),$(call compile_erl,$(FILES_TO_COMPILE))) + $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true)) + $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \ + $(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES))))))) +ifeq ($(wildcard src/$(PROJECT).app.src),) + $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \ + > ebin/$(PROJECT).app +else + $(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \ + echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \ + exit 1; \ + fi + $(appsrc_verbose) cat src/$(PROJECT).app.src \ + | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \ + | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \ + > ebin/$(PROJECT).app +endif + +clean:: clean-app + +clean-app: + $(gen_verbose) rm -rf $(PROJECT).d ebin/ priv/mibs/ $(XRL_ERL_FILES) $(YRL_ERL_FILES) \ + $(addprefix include/,$(patsubst %.mib,%.hrl,$(notdir $(MIB_FILES)))) \ + $(addprefix include/,$(patsubst %.asn1,%.hrl,$(notdir $(ASN1_FILES)))) \ + $(addprefix include/,$(patsubst %.asn1,%.asn1db,$(notdir $(ASN1_FILES)))) \ + $(addprefix src/,$(patsubst %.asn1,%.erl,$(notdir $(ASN1_FILES)))) + +endif + +# Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: docs-deps + +# Configuration. + +ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS)) + +# Targets. + +$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep)))) + +ifneq ($(SKIP_DEPS),) +doc-deps: +else +doc-deps: $(ALL_DOC_DEPS_DIRS) + $(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done +endif + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: rel-deps + +# Configuration. + +ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS)) + +# Targets. + +$(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep)))) + +ifneq ($(SKIP_DEPS),) +rel-deps: +else +rel-deps: $(ALL_REL_DEPS_DIRS) + $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done +endif + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: test-deps test-dir test-build clean-test-dir + +# Configuration. + +TEST_DIR ?= $(CURDIR)/test + +ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS)) + +TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard +TEST_ERLC_OPTS += -DTEST=1 + +# Targets. + +$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep)))) + +ifneq ($(SKIP_DEPS),) +test-deps: +else +test-deps: $(ALL_TEST_DEPS_DIRS) + $(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done +endif + +ifneq ($(wildcard $(TEST_DIR)),) +test-dir: + $(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \ + $(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/ +endif + +ifeq ($(wildcard ebin/test),) +test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) +test-build:: clean deps test-deps $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" + $(gen_verbose) touch ebin/test +else +test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) +test-build:: deps test-deps $(PROJECT).d + $(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" +endif + +clean:: clean-test-dir + +clean-test-dir: +ifneq ($(wildcard $(TEST_DIR)/*.beam),) + $(gen_verbose) rm -f $(TEST_DIR)/*.beam +endif + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: rebar.config + +# We strip out -Werror because we don't want to fail due to +# warnings when used as a dependency. + +compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/') + +define compat_convert_erlc_opts +$(if $(filter-out -Werror,$1),\ + $(if $(findstring +,$1),\ + $(shell echo $1 | cut -b 2-))) +endef + +define compat_rebar_config +{deps, [$(call comma_list,$(foreach d,$(DEPS),\ + {$(call dep_name,$d),".*",{git,"$(call dep_repo,$d)","$(call dep_commit,$d)"}}))]}. +{erl_opts, [$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$(ERLC_OPTS)),\ + $(call compat_convert_erlc_opts,$o)))]}. +endef + +$(eval _compat_rebar_config = $$(compat_rebar_config)) +$(eval export _compat_rebar_config) + +rebar.config: + $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc + +MAN_INSTALL_PATH ?= /usr/local/share/man +MAN_SECTIONS ?= 3 7 + +docs:: asciidoc + +asciidoc: distclean-asciidoc doc-deps asciidoc-guide asciidoc-manual + +ifeq ($(wildcard doc/src/guide/book.asciidoc),) +asciidoc-guide: +else +asciidoc-guide: + a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf + a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/ +endif + +ifeq ($(wildcard doc/src/manual/*.asciidoc),) +asciidoc-manual: +else +asciidoc-manual: + for f in doc/src/manual/*.asciidoc ; do \ + a2x -v -f manpage $$f ; \ + done + for s in $(MAN_SECTIONS); do \ + mkdir -p doc/man$$s/ ; \ + mv doc/src/manual/*.$$s doc/man$$s/ ; \ + gzip doc/man$$s/*.$$s ; \ + done + +install-docs:: install-asciidoc + +install-asciidoc: asciidoc-manual + for s in $(MAN_SECTIONS); do \ + mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \ + install -g 0 -o 0 -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \ + done +endif + +distclean:: distclean-asciidoc + +distclean-asciidoc: + $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/ + +# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates + +# Core targets. + +help:: + $(verbose) printf "%s\n" "" \ + "Bootstrap targets:" \ + " bootstrap Generate a skeleton of an OTP application" \ + " bootstrap-lib Generate a skeleton of an OTP library" \ + " bootstrap-rel Generate the files needed to build a release" \ + " new-app n=NAME Create a new local OTP application NAME" \ + " new-lib n=NAME Create a new local OTP library NAME" \ + " new t=TPL n=NAME Generate a module NAME based on the template TPL" \ + " new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \ + " list-templates List available templates" + +# Bootstrap templates. + +define bs_appsrc +{application, $p, [ + {description, ""}, + {vsn, "0.1.0"}, + {id, "git"}, + {modules, []}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, {$p_app, []}}, + {env, []} +]}. +endef + +define bs_appsrc_lib +{application, $p, [ + {description, ""}, + {vsn, "0.1.0"}, + {id, "git"}, + {modules, []}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]} +]}. +endef + +ifdef SP +define bs_Makefile +PROJECT = $p +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.0.1 + +# Whitespace to be used when creating files from templates. +SP = $(SP) + +include erlang.mk +endef +else +define bs_Makefile +PROJECT = $p +include erlang.mk +endef +endif + +define bs_apps_Makefile +PROJECT = $p +include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk +endef + +define bs_app +-module($p_app). +-behaviour(application). + +-export([start/2]). +-export([stop/1]). + +start(_Type, _Args) -> + $p_sup:start_link(). + +stop(_State) -> + ok. +endef + +define bs_relx_config +{release, {$p_release, "1"}, [$p]}. +{extended_start_script, true}. +{sys_config, "rel/sys.config"}. +{vm_args, "rel/vm.args"}. +endef + +define bs_sys_config +[ +]. +endef + +define bs_vm_args +-name $p@127.0.0.1 +-setcookie $p +-heart +endef + +# Normal templates. + +define tpl_supervisor +-module($(n)). +-behaviour(supervisor). + +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + Procs = [], + {ok, {{one_for_one, 1, 5}, Procs}}. +endef + +define tpl_gen_server +-module($(n)). +-behaviour(gen_server). + +%% API. +-export([start_link/0]). + +%% gen_server. +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_info/2]). +-export([terminate/2]). +-export([code_change/3]). + +-record(state, { +}). + +%% API. + +-spec start_link() -> {ok, pid()}. +start_link() -> + gen_server:start_link(?MODULE, [], []). + +%% gen_server. + +init([]) -> + {ok, #state{}}. + +handle_call(_Request, _From, State) -> + {reply, ignored, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. +endef + +define tpl_cowboy_http +-module($(n)). +-behaviour(cowboy_http_handler). + +-export([init/3]). +-export([handle/2]). +-export([terminate/3]). + +-record(state, { +}). + +init(_, Req, _Opts) -> + {ok, Req, #state{}}. + +handle(Req, State=#state{}) -> + {ok, Req2} = cowboy_req:reply(200, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. +endef + +define tpl_gen_fsm +-module($(n)). +-behaviour(gen_fsm). + +%% API. +-export([start_link/0]). + +%% gen_fsm. +-export([init/1]). +-export([state_name/2]). +-export([handle_event/3]). +-export([state_name/3]). +-export([handle_sync_event/4]). +-export([handle_info/3]). +-export([terminate/3]). +-export([code_change/4]). + +-record(state, { +}). + +%% API. + +-spec start_link() -> {ok, pid()}. +start_link() -> + gen_fsm:start_link(?MODULE, [], []). + +%% gen_fsm. + +init([]) -> + {ok, state_name, #state{}}. + +state_name(_Event, StateData) -> + {next_state, state_name, StateData}. + +handle_event(_Event, StateName, StateData) -> + {next_state, StateName, StateData}. + +state_name(_Event, _From, StateData) -> + {reply, ignored, state_name, StateData}. + +handle_sync_event(_Event, _From, StateName, StateData) -> + {reply, ignored, StateName, StateData}. + +handle_info(_Info, StateName, StateData) -> + {next_state, StateName, StateData}. + +terminate(_Reason, _StateName, _StateData) -> + ok. + +code_change(_OldVsn, StateName, StateData, _Extra) -> + {ok, StateName, StateData}. +endef + +define tpl_cowboy_loop +-module($(n)). +-behaviour(cowboy_loop_handler). + +-export([init/3]). +-export([info/3]). +-export([terminate/3]). + +-record(state, { +}). + +init(_, Req, _Opts) -> + {loop, Req, #state{}, 5000, hibernate}. + +info(_Info, Req, State) -> + {loop, Req, State, hibernate}. + +terminate(_Reason, _Req, _State) -> + ok. +endef + +define tpl_cowboy_rest +-module($(n)). + +-export([init/3]). +-export([content_types_provided/2]). +-export([get_html/2]). + +init(_, _Req, _Opts) -> + {upgrade, protocol, cowboy_rest}. + +content_types_provided(Req, State) -> + {[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}. + +get_html(Req, State) -> + {<<"<html><body>This is REST!</body></html>">>, Req, State}. +endef + +define tpl_cowboy_ws +-module($(n)). +-behaviour(cowboy_websocket_handler). + +-export([init/3]). +-export([websocket_init/3]). +-export([websocket_handle/3]). +-export([websocket_info/3]). +-export([websocket_terminate/3]). + +-record(state, { +}). + +init(_, _, _) -> + {upgrade, protocol, cowboy_websocket}. + +websocket_init(_, Req, _Opts) -> + Req2 = cowboy_req:compact(Req), + {ok, Req2, #state{}}. + +websocket_handle({text, Data}, Req, State) -> + {reply, {text, Data}, Req, State}; +websocket_handle({binary, Data}, Req, State) -> + {reply, {binary, Data}, Req, State}; +websocket_handle(_Frame, Req, State) -> + {ok, Req, State}. + +websocket_info(_Info, Req, State) -> + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> + ok. +endef + +define tpl_ranch_protocol +-module($(n)). +-behaviour(ranch_protocol). + +-export([start_link/4]). +-export([init/4]). + +-type opts() :: []. +-export_type([opts/0]). + +-record(state, { + socket :: inet:socket(), + transport :: module() +}). + +start_link(Ref, Socket, Transport, Opts) -> + Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]), + {ok, Pid}. + +-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok. +init(Ref, Socket, Transport, _Opts) -> + ok = ranch:accept_ack(Ref), + loop(#state{socket=Socket, transport=Transport}). + +loop(State) -> + loop(State). +endef + +# Plugin-specific targets. + +define render_template + $(verbose) echo "$${_$(1)}" > $(2) +endef + +ifndef WS +ifdef SP +WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a)) +else +WS = $(tab) +endif +endif + +$(foreach template,$(filter bs_% tpl_%,$(.VARIABLES)), \ + $(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \ + $(eval export _$(template))) + +bootstrap: +ifneq ($(wildcard src/),) + $(error Error: src/ directory already exists) +endif + $(eval p := $(PROJECT)) + $(eval n := $(PROJECT)_sup) + $(call render_template,bs_Makefile,Makefile) + $(verbose) mkdir src/ +ifdef LEGACY + $(call render_template,bs_appsrc,src/$(PROJECT).app.src) +endif + $(call render_template,bs_app,src/$(PROJECT)_app.erl) + $(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl) + +bootstrap-lib: +ifneq ($(wildcard src/),) + $(error Error: src/ directory already exists) +endif + $(eval p := $(PROJECT)) + $(call render_template,bs_Makefile,Makefile) + $(verbose) mkdir src/ +ifdef LEGACY + $(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src) +endif + +bootstrap-rel: +ifneq ($(wildcard relx.config),) + $(error Error: relx.config already exists) +endif +ifneq ($(wildcard rel/),) + $(error Error: rel/ directory already exists) +endif + $(eval p := $(PROJECT)) + $(call render_template,bs_relx_config,relx.config) + $(verbose) mkdir rel/ + $(call render_template,bs_sys_config,rel/sys.config) + $(call render_template,bs_vm_args,rel/vm.args) + +new-app: +ifndef in + $(error Usage: $(MAKE) new-app in=APP) +endif +ifneq ($(wildcard $(APPS_DIR)/$in),) + $(error Error: Application $in already exists) +endif + $(eval p := $(in)) + $(eval n := $(in)_sup) + $(verbose) mkdir -p $(APPS_DIR)/$p/src/ + $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile) +ifdef LEGACY + $(call render_template,bs_appsrc,$(APPS_DIR)/$p/src/$p.app.src) +endif + $(call render_template,bs_app,$(APPS_DIR)/$p/src/$p_app.erl) + $(call render_template,tpl_supervisor,$(APPS_DIR)/$p/src/$p_sup.erl) + +new-lib: +ifndef in + $(error Usage: $(MAKE) new-lib in=APP) +endif +ifneq ($(wildcard $(APPS_DIR)/$in),) + $(error Error: Application $in already exists) +endif + $(eval p := $(in)) + $(verbose) mkdir -p $(APPS_DIR)/$p/src/ + $(call render_template,bs_apps_Makefile,$(APPS_DIR)/$p/Makefile) +ifdef LEGACY + $(call render_template,bs_appsrc_lib,$(APPS_DIR)/$p/src/$p.app.src) +endif + +new: +ifeq ($(wildcard src/)$(in),) + $(error Error: src/ directory does not exist) +endif +ifndef t + $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]) +endif +ifndef tpl_$(t) + $(error Unknown template) +endif +ifndef n + $(error Usage: $(MAKE) new t=TEMPLATE n=NAME [in=APP]) +endif +ifdef in + $(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new t=$t n=$n in= +else + $(call render_template,tpl_$(t),src/$(n).erl) +endif + +list-templates: + $(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES)))) + +# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: clean-c_src distclean-c_src-env + +# Configuration. + +C_SRC_DIR ?= $(CURDIR)/c_src +C_SRC_ENV ?= $(C_SRC_DIR)/env.mk +C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so +C_SRC_TYPE ?= shared + +# System type and C compiler/flags. + +ifeq ($(PLATFORM),darwin) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall + LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress +else ifeq ($(PLATFORM),freebsd) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +else ifeq ($(PLATFORM),linux) + CC ?= gcc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +endif + +CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) +CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) + +LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei + +# Verbosity. + +c_verbose_0 = @echo " C " $(?F); +c_verbose = $(c_verbose_$(V)) + +cpp_verbose_0 = @echo " CPP " $(?F); +cpp_verbose = $(cpp_verbose_$(V)) + +link_verbose_0 = @echo " LD " $(@F); +link_verbose = $(link_verbose_$(V)) + +# Targets. + +ifeq ($(wildcard $(C_SRC_DIR)),) +else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),) +app:: app-c_src + +test-build:: app-c_src + +app-c_src: + $(MAKE) -C $(C_SRC_DIR) + +clean:: + $(MAKE) -C $(C_SRC_DIR) clean + +else + +ifeq ($(SOURCES),) +SOURCES := $(sort $(foreach pat,*.c *.C *.cc *.cpp,$(call core_find,$(C_SRC_DIR)/,$(pat)))) +endif +OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) + +COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c +COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c + +app:: $(C_SRC_ENV) $(C_SRC_OUTPUT) + +test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT) + +$(C_SRC_OUTPUT): $(OBJECTS) + $(verbose) mkdir -p priv/ + $(link_verbose) $(CC) $(OBJECTS) \ + $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \ + -o $(C_SRC_OUTPUT) + +%.o: %.c + $(COMPILE_C) $(OUTPUT_OPTION) $< + +%.o: %.cc + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.C + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.cpp + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +clean:: clean-c_src + +clean-c_src: + $(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS) + +endif + +ifneq ($(wildcard $(C_SRC_DIR)),) +$(C_SRC_ENV): + $(verbose) $(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \ + io_lib:format( \ + \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \ + \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \ + \"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \ + [code:root_dir(), erlang:system_info(version), \ + code:lib_dir(erl_interface, include), \ + code:lib_dir(erl_interface, lib)])), \ + halt()." + +distclean:: distclean-c_src-env + +distclean-c_src-env: + $(gen_verbose) rm -f $(C_SRC_ENV) + +-include $(C_SRC_ENV) +endif + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: ci ci-setup distclean-kerl + +KERL ?= $(CURDIR)/kerl +export KERL + +KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl + +OTP_GIT ?= https://github.com/erlang/otp + +CI_INSTALL_DIR ?= $(HOME)/erlang +CI_OTP ?= + +ifeq ($(strip $(CI_OTP)),) +ci:: +else +ci:: $(addprefix ci-,$(CI_OTP)) + +ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP)) + +ci-setup:: + +ci_verbose_0 = @echo " CI " $(1); +ci_verbose = $(ci_verbose_$(V)) + +define ci_target +ci-$(1): $(CI_INSTALL_DIR)/$(1) + $(ci_verbose) \ + PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \ + CI_OTP_RELEASE="$(1)" \ + CT_OPTS="-label $(1)" \ + $(MAKE) clean ci-setup tests +endef + +$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp)))) + +define ci_otp_target +ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),) +$(CI_INSTALL_DIR)/$(1): $(KERL) + $(KERL) build git $(OTP_GIT) $(1) $(1) + $(KERL) install $(1) $(CI_INSTALL_DIR)/$(1) +endif +endef + +$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp)))) + +$(KERL): + $(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL)) + $(verbose) chmod +x $(KERL) + +help:: + $(verbose) printf "%s\n" "" \ + "Continuous Integration targets:" \ + " ci Run '$(MAKE) tests' on all configured Erlang versions." \ + "" \ + "The CI_OTP variable must be defined with the Erlang versions" \ + "that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3" + +distclean:: distclean-kerl + +distclean-kerl: + $(gen_verbose) rm -rf $(KERL) +endif + +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: ct distclean-ct + +# Configuration. + +CT_OPTS ?= +ifneq ($(wildcard $(TEST_DIR)),) + CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl)))) +else + CT_SUITES ?= +endif + +# Core targets. + +tests:: ct + +distclean:: distclean-ct + +help:: + $(verbose) printf "%s\n" "" \ + "Common_test targets:" \ + " ct Run all the common_test suites for this project" \ + "" \ + "All your common_test suites have their associated targets." \ + "A suite named http_SUITE can be ran using the ct-http target." + +# Plugin-specific targets. + +CT_RUN = ct_run \ + -no_auto_compile \ + -noinput \ + -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(TEST_DIR) \ + -dir $(TEST_DIR) \ + -logdir $(CURDIR)/logs + +ifeq ($(CT_SUITES),) +ct: +else +ct: test-build + $(verbose) mkdir -p $(CURDIR)/logs/ + $(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS) +endif + +define ct_suite_target +ct-$(1): test-build + $(verbose) mkdir -p $(CURDIR)/logs/ + $(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS) +endef + +$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) + +distclean-ct: + $(gen_verbose) rm -rf $(CURDIR)/logs/ + +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: plt distclean-plt dialyze + +# Configuration. + +DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt +export DIALYZER_PLT + +PLT_APPS ?= +DIALYZER_DIRS ?= --src -r src +DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \ + -Wunmatched_returns # -Wunderspecs + +# Core targets. + +check:: dialyze + +distclean:: distclean-plt + +help:: + $(verbose) printf "%s\n" "" \ + "Dialyzer targets:" \ + " plt Build a PLT file for this project" \ + " dialyze Analyze the project using Dialyzer" + +# Plugin-specific targets. + +$(DIALYZER_PLT): deps app + $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS) + +plt: $(DIALYZER_PLT) + +distclean-plt: + $(gen_verbose) rm -f $(DIALYZER_PLT) + +ifneq ($(wildcard $(DIALYZER_PLT)),) +dialyze: +else +dialyze: $(DIALYZER_PLT) +endif + $(verbose) dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS) + +# Copyright (c) 2015, Erlang Solutions Ltd. +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: elvis distclean-elvis + +# Configuration. + +ELVIS_CONFIG ?= $(CURDIR)/elvis.config + +ELVIS ?= $(CURDIR)/elvis +export ELVIS + +ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5/elvis +ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5/elvis.config +ELVIS_OPTS ?= + +# Core targets. + +help:: + $(verbose) printf "%s\n" "" \ + "Elvis targets:" \ + " elvis Run Elvis using the local elvis.config or download the default otherwise" + +distclean:: distclean-elvis + +# Plugin-specific targets. + +$(ELVIS): + $(gen_verbose) $(call core_http_get,$(ELVIS),$(ELVIS_URL)) + $(verbose) chmod +x $(ELVIS) + +$(ELVIS_CONFIG): + $(verbose) $(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL)) + +elvis: $(ELVIS) $(ELVIS_CONFIG) + $(verbose) $(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS) + +distclean-elvis: + $(gen_verbose) rm -rf $(ELVIS) + +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# Configuration. + +DTL_FULL_PATH ?= +DTL_PATH ?= templates/ +DTL_SUFFIX ?= _dtl + +# Verbosity. + +dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); +dtl_verbose = $(dtl_verbose_$(V)) + +# Core targets. + +define erlydtl_compile.erl + [begin + Module0 = case "$(strip $(DTL_FULL_PATH))" of + "" -> + filename:basename(F, ".dtl"); + _ -> + "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"), + re:replace(F2, "/", "_", [{return, list}, global]) + end, + Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"), + case erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of + ok -> ok; + {ok, _} -> ok + end + end || F <- string:tokens("$(1)", " ")], + halt(). +endef + +ifneq ($(wildcard src/),) + +DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl)) + +ifdef DTL_FULL_PATH +BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%)))) +else +BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES)))) +endif + +# Rebuild templates when the Makefile changes. +$(DTL_FILES): $(MAKEFILE_LIST) + @touch $@ + +ebin/$(PROJECT).app:: $(DTL_FILES) + $(if $(strip $?),\ + $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?,-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))) +endif + +# Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: distclean-escript escript + +# Configuration. + +ESCRIPT_NAME ?= $(PROJECT) +ESCRIPT_COMMENT ?= This is an -*- erlang -*- file + +ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*" +ESCRIPT_SYS_CONFIG ?= "rel/sys.config" +ESCRIPT_EMU_ARGS ?= -pa . \ + -sasl errlog_type error \ + -escript main $(ESCRIPT_NAME) +ESCRIPT_SHEBANG ?= /usr/bin/env escript +ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**" + +# Core targets. + +distclean:: distclean-escript + +help:: + $(verbose) printf "%s\n" "" \ + "Escript targets:" \ + " escript Build an executable escript archive" \ + +# Plugin-specific targets. + +# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl +# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center +# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE : +# Software may only be used for the great good and the true happiness of all +# sentient beings. + +define ESCRIPT_RAW +'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\ +'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\ +' [F || F <- A, not filelib:is_dir(F) ] end,'\ +'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\ +'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\ +'Ez = fun(Escript) ->'\ +' Static = Files([$(ESCRIPT_STATIC)]),'\ +' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\ +' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\ +' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\ +' {archive, Archive, [memory]},'\ +' {shebang, "$(ESCRIPT_SHEBANG)"},'\ +' {comment, "$(ESCRIPT_COMMENT)"},'\ +' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\ +' ]),'\ +' file:change_mode(Escript, 8#755)'\ +'end,'\ +'Ez("$(ESCRIPT_NAME)"),'\ +'halt().' +endef + +ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW)) + +escript:: distclean-escript deps app + $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND) + +distclean-escript: + $(gen_verbose) rm -f $(ESCRIPT_NAME) + +# Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com> +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is contributed to erlang.mk and subject to the terms of the ISC License. + +.PHONY: eunit + +# Configuration + +EUNIT_OPTS ?= + +# Core targets. + +tests:: eunit + +help:: + $(verbose) printf "%s\n" "" \ + "EUnit targets:" \ + " eunit Run all the EUnit tests for this project" + +# Plugin-specific targets. + +define eunit.erl + case "$(COVER)" of + "" -> ok; + _ -> + case cover:compile_beam_directory("ebin") of + {error, _} -> halt(1); + _ -> ok + end + end, + case eunit:test([$(call comma_list,$(1))], [$(EUNIT_OPTS)]) of + ok -> ok; + error -> halt(2) + end, + case "$(COVER)" of + "" -> ok; + _ -> + cover:export("eunit.coverdata") + end, + halt() +endef + +EUNIT_EBIN_MODS = $(notdir $(basename $(call core_find,ebin/,*.beam))) +EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.beam))) +EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \ + $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),{module,'$(mod)'}) + +eunit: test-build + $(gen_verbose) $(ERL) -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin ebin \ + -eval "$(subst $(newline),,$(subst ",\",$(call eunit.erl,$(EUNIT_MODS))))" + +# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: relx-rel distclean-relx-rel distclean-relx run + +# Configuration. + +RELX ?= $(CURDIR)/relx +RELX_CONFIG ?= $(CURDIR)/relx.config + +RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.5.0/relx +RELX_OPTS ?= +RELX_OUTPUT_DIR ?= _rel + +ifeq ($(firstword $(RELX_OPTS)),-o) + RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS)) +else + RELX_OPTS += -o $(RELX_OUTPUT_DIR) +endif + +# Core targets. + +ifeq ($(IS_DEP),) +ifneq ($(wildcard $(RELX_CONFIG)),) +rel:: distclean-relx-rel relx-rel +endif +endif + +distclean:: distclean-relx-rel distclean-relx + +# Plugin-specific targets. + +$(RELX): + $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL)) + $(verbose) chmod +x $(RELX) + +relx-rel: $(RELX) rel-deps + $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) + +distclean-relx-rel: + $(gen_verbose) rm -rf $(RELX_OUTPUT_DIR) + +distclean-relx: + $(gen_verbose) rm -rf $(RELX) + +# Run target. + +ifeq ($(wildcard $(RELX_CONFIG)),) +run: +else + +define get_relx_release.erl + {ok, Config} = file:consult("$(RELX_CONFIG)"), + {release, {Name, _}, _} = lists:keyfind(release, 1, Config), + io:format("~s", [Name]), + halt(0). +endef + +RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))` + +run: all + $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console + +help:: + $(verbose) printf "%s\n" "" \ + "Relx targets:" \ + " run Compile the project, build the release and run it" + +endif + +# Copyright (c) 2014, M Robert Martin <rob@version2beta.com> +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is contributed to erlang.mk and subject to the terms of the ISC License. + +.PHONY: shell + +# Configuration. + +SHELL_ERL ?= erl +SHELL_PATHS ?= $(CURDIR)/ebin $(APPS_DIR)/*/ebin $(DEPS_DIR)/*/ebin +SHELL_OPTS ?= + +ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS)) + +# Core targets + +help:: + $(verbose) printf "%s\n" "" \ + "Shell targets:" \ + " shell Run an erlang shell with SHELL_OPTS or reasonable default" + +# Plugin-specific targets. + +$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep)))) + +build-shell-deps: $(ALL_SHELL_DEPS_DIRS) + $(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done + +shell: build-shell-deps + $(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS) + +# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq) +.PHONY: triq + +# Targets. + +tests:: triq + +define triq_check.erl + code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]), + try + case $(1) of + all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]); + module -> triq:check($(2)); + function -> triq:check($(2)) + end + of + true -> halt(0); + _ -> halt(1) + catch error:undef -> + io:format("Undefined property or module~n"), + halt(0) + end. +endef + +ifdef t +ifeq (,$(findstring :,$(t))) +triq: test-build + $(verbose) $(call erlang,$(call triq_check.erl,module,$(t))) +else +triq: test-build + $(verbose) echo Testing $(t)/0 + $(verbose) $(call erlang,$(call triq_check.erl,function,$(t)())) +endif +else +triq: test-build + $(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam)))))) + $(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES))) +endif +endif + +# Copyright (c) 2015, Erlang Solutions Ltd. +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: xref distclean-xref + +# Configuration. + +ifeq ($(XREF_CONFIG),) + XREF_ARGS := +else + XREF_ARGS := -c $(XREF_CONFIG) +endif + +XREFR ?= $(CURDIR)/xrefr +export XREFR + +XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr + +# Core targets. + +help:: + $(verbose) printf "%s\n" "" \ + "Xref targets:" \ + " xref Run Xrefr using $XREF_CONFIG as config file if defined" + +distclean:: distclean-xref + +# Plugin-specific targets. + +$(XREFR): + $(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL)) + $(verbose) chmod +x $(XREFR) + +xref: deps app $(XREFR) + $(gen_verbose) $(XREFR) $(XREFR_ARGS) + +distclean-xref: + $(gen_verbose) rm -rf $(XREFR) + +# Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se> +# This file is part of erlang.mk and subject to the terms of the ISC License. + +COVER_REPORT_DIR = cover + +# Hook in coverage to ct + +ifdef COVER +ifdef CT_RUN +# All modules in 'ebin' +COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam))) + +test-build:: $(TEST_DIR)/ct.cover.spec + +$(TEST_DIR)/ct.cover.spec: + $(verbose) echo Cover mods: $(COVER_MODS) + $(gen_verbose) printf "%s\n" \ + '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \ + '{export,"$(CURDIR)/ct.coverdata"}.' > $@ + +CT_RUN += -cover $(TEST_DIR)/ct.cover.spec +endif +endif + +# Core targets + +ifdef COVER +ifneq ($(COVER_REPORT_DIR),) +tests:: + $(verbose) $(MAKE) --no-print-directory cover-report +endif +endif + +clean:: coverdata-clean + +ifneq ($(COVER_REPORT_DIR),) +distclean:: cover-report-clean +endif + +help:: + $(verbose) printf "%s\n" "" \ + "Cover targets:" \ + " cover-report Generate a HTML coverage report from previously collected" \ + " cover data." \ + " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \ + "" \ + "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \ + "target tests additionally generates a HTML coverage report from the combined" \ + "coverdata files from each of these testing tools. HTML reports can be disabled" \ + "by setting COVER_REPORT_DIR to empty." + +# Plugin specific targets + +COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata)) + +.PHONY: coverdata-clean +coverdata-clean: + $(gen_verbose) rm -f *.coverdata ct.cover.spec + +# Merge all coverdata files into one. +all.coverdata: $(COVERDATA) + $(gen_verbose) $(ERL) -eval ' \ + $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \ + cover:export("$@"), halt(0).' + +# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to +# empty if you want the coverdata files but not the HTML report. +ifneq ($(COVER_REPORT_DIR),) + +.PHONY: cover-report-clean cover-report + +cover-report-clean: + $(gen_verbose) rm -rf $(COVER_REPORT_DIR) + +ifeq ($(COVERDATA),) +cover-report: +else + +# Modules which include eunit.hrl always contain one line without coverage +# because eunit defines test/0 which is never called. We compensate for this. +EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \ + grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \ + | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq)) + +define cover_report.erl + $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) + Ms = cover:imported_modules(), + [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M) + ++ ".COVER.html", [html]) || M <- Ms], + Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms], + EunitHrlMods = [$(EUNIT_HRL_MODS)], + Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of + true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report], + TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]), + TotalN = lists:sum([N || {_, {_, N}} <- Report1]), + TotalPerc = round(100 * TotalY / (TotalY + TotalN)), + {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]), + io:format(F, "<!DOCTYPE html><html>~n" + "<head><meta charset=\"UTF-8\">~n" + "<title>Coverage report</title></head>~n" + "<body>~n", []), + io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]), + io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []), + [io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>" + "<td>~p%</td></tr>~n", + [M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1], + How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))", + Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")", + io:format(F, "</table>~n" + "<p>Generated using ~s and erlang.mk on ~s.</p>~n" + "</body></html>", [How, Date]), + halt(). +endef + +cover-report: + $(gen_verbose) mkdir -p $(COVER_REPORT_DIR) + $(gen_verbose) $(call erlang,$(cover_report.erl)) + +endif +endif # ifneq ($(COVER_REPORT_DIR),) diff --git a/generate_app b/generate_app deleted file mode 100644 index fb0eb1ea62..0000000000 --- a/generate_app +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -main([InFile, OutFile | SrcDirs]) -> - Modules = [list_to_atom(filename:basename(F, ".erl")) || - SrcDir <- SrcDirs, - F <- filelib:wildcard("*.erl", SrcDir)], - {ok, [{application, Application, Properties}]} = file:consult(InFile), - NewProperties = - case proplists:get_value(modules, Properties) of - [] -> lists:keyreplace(modules, 1, Properties, {modules, Modules}); - _ -> Properties - end, - file:write_file( - OutFile, - io_lib:format("~p.~n", [{application, Application, NewProperties}])). diff --git a/generate_deps b/generate_deps deleted file mode 100644 index ddfca816b4..0000000000 --- a/generate_deps +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- --mode(compile). - -%% We expect the list of Erlang source and header files to arrive on -%% stdin, with the entries colon-separated. -main([TargetFile, EbinDir]) -> - ErlsAndHrls = [ string:strip(S,left) || - S <- string:tokens(io:get_line(""), ":\n")], - ErlFiles = [F || F <- ErlsAndHrls, lists:suffix(".erl", F)], - Modules = sets:from_list( - [list_to_atom(filename:basename(FileName, ".erl")) || - FileName <- ErlFiles]), - HrlFiles = [F || F <- ErlsAndHrls, lists:suffix(".hrl", F)], - IncludeDirs = lists:usort([filename:dirname(Path) || Path <- HrlFiles]), - Headers = sets:from_list(HrlFiles), - Deps = lists:foldl( - fun (Path, Deps1) -> - dict:store(Path, detect_deps(IncludeDirs, EbinDir, - Modules, Headers, Path), - Deps1) - end, dict:new(), ErlFiles), - {ok, Hdl} = file:open(TargetFile, [write, delayed_write]), - dict:fold( - fun (_Path, [], ok) -> - ok; - (Path, Dep, ok) -> - Module = filename:basename(Path, ".erl"), - ok = file:write(Hdl, [EbinDir, "/", Module, ".beam: ", - Path]), - ok = sets:fold(fun (E, ok) -> file:write(Hdl, [" ", E]) end, - ok, Dep), - file:write(Hdl, ["\n"]) - end, ok, Deps), - ok = file:write(Hdl, [TargetFile, ": ", escript:script_name(), "\n"]), - ok = file:sync(Hdl), - ok = file:close(Hdl). - -detect_deps(IncludeDirs, EbinDir, Modules, Headers, Path) -> - {ok, Forms} = epp:parse_file(Path, IncludeDirs, [{use_specs, true}]), - lists:foldl( - fun ({attribute, _LineNumber, Attribute, Behaviour}, Deps) - when Attribute =:= behaviour orelse Attribute =:= behavior -> - case sets:is_element(Behaviour, Modules) of - true -> sets:add_element( - [EbinDir, "/", atom_to_list(Behaviour), ".beam"], - Deps); - false -> Deps - end; - ({attribute, _LineNumber, file, {FileName, _LineNumber1}}, Deps) -> - case sets:is_element(FileName, Headers) of - true -> sets:add_element(FileName, Deps); - false -> Deps - end; - (_Form, Deps) -> - Deps - end, sets:new(), Forms). diff --git a/include/rabbit.hrl b/include/rabbit.hrl deleted file mode 100644 index 2b6f68af54..0000000000 --- a/include/rabbit.hrl +++ /dev/null @@ -1,159 +0,0 @@ -%% 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 Pivotal Software, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - -%% Passed around most places --record(user, {username, - tags, - authz_backends}). %% List of {Module, AuthUserImpl} pairs - -%% Passed to auth backends --record(auth_user, {username, - tags, - impl}). -%% Passed to authz backends. --record(authz_socket_info, {sockname, peername}). - -%% Implementation for the internal auth backend --record(internal_user, { - username, - password_hash, - tags, - hashing_algorithm}). --record(permission, {configure, write, read}). --record(user_vhost, {username, virtual_host}). --record(user_permission, {user_vhost, permission}). - --record(vhost, {virtual_host, dummy}). - --record(content, - {class_id, - properties, %% either 'none', or a decoded record/tuple - properties_bin, %% either 'none', or an encoded properties binary - %% Note: at most one of properties and properties_bin can be - %% 'none' at once. - protocol, %% The protocol under which properties_bin was encoded - payload_fragments_rev %% list of binaries, in reverse order (!) - }). - --record(resource, {virtual_host, kind, name}). - -%% fields described as 'transient' here are cleared when writing to -%% rabbit_durable_<thing> --record(exchange, { - name, type, durable, auto_delete, internal, arguments, %% immutable - scratches, %% durable, explicitly updated via update_scratch/3 - policy, %% durable, implicitly updated when policy changes - decorators}). %% transient, recalculated in store/1 (i.e. recovery) - --record(amqqueue, { - name, durable, auto_delete, exclusive_owner = none, %% immutable - arguments, %% immutable - pid, %% durable (just so we know home node) - slave_pids, sync_slave_pids, %% transient - recoverable_slaves, %% durable - policy, %% durable, implicit update as above - gm_pids, %% transient - decorators, %% transient, recalculated as above - state}). %% durable (have we crashed?) - --record(exchange_serial, {name, next}). - -%% mnesia doesn't like unary records, so we add a dummy 'value' field --record(route, {binding, value = const}). --record(reverse_route, {reverse_binding, value = const}). - --record(binding, {source, key, destination, args = []}). --record(reverse_binding, {destination, key, source, args = []}). - --record(topic_trie_node, {trie_node, edge_count, binding_count}). --record(topic_trie_edge, {trie_edge, node_id}). --record(topic_trie_binding, {trie_binding, value = const}). - --record(trie_node, {exchange_name, node_id}). --record(trie_edge, {exchange_name, node_id, word}). --record(trie_binding, {exchange_name, node_id, destination, arguments}). - --record(listener, {node, protocol, host, ip_address, port}). - --record(runtime_parameters, {key, value}). - --record(basic_message, {exchange_name, routing_keys = [], content, id, - is_persistent}). - --record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, confirm, sender, message, msg_seq_no, flow}). --record(amqp_error, {name, explanation = "", method = none}). - --record(event, {type, props, reference = undefined, timestamp}). - --record(message_properties, {expiry, needs_confirming = false, size}). - --record(plugin, {name, %% atom() - version, %% string() - description, %% string() - type, %% 'ez' or 'dir' - dependencies, %% [{atom(), string()}] - location}). %% string() - -%%---------------------------------------------------------------------------- - --define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2015 Pivotal Software, Inc."). --define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). --define(OTP_MINIMUM, "R16B03"). --define(ERTS_MINIMUM, "5.10.4"). - -%% EMPTY_FRAME_SIZE, 8 = 1 + 2 + 4 + 1 -%% - 1 byte of frame type -%% - 2 bytes of channel number -%% - 4 bytes of frame payload length -%% - 1 byte of payload trailer FRAME_END byte -%% See rabbit_binary_generator:check_empty_frame_size/0, an assertion -%% called at startup. --define(EMPTY_FRAME_SIZE, 8). - --define(MAX_WAIT, 16#ffffffff). - --define(HIBERNATE_AFTER_MIN, 1000). --define(DESIRED_HIBERNATE, 10000). --define(CREDIT_DISC_BOUND, {2000, 500}). -%% When we discover that we should write some indices to disk for some -%% betas, the IO_BATCH_SIZE sets the number of betas that we must be -%% due to write indices for before we do any work at all. --define(IO_BATCH_SIZE, 2048). %% next power-of-2 after ?CREDIT_DISC_BOUND - --define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). --define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). --define(DELETED_HEADER, <<"BCC">>). - -%% Trying to send a term across a cluster larger than 2^31 bytes will -%% cause the VM to exit with "Absurdly large distribution output data -%% buffer". So we limit the max message size to 2^31 - 10^6 bytes (1MB -%% to allow plenty of leeway for the #basic_message{} and #content{} -%% wrapping the message body). --define(MAX_MSG_SIZE, 2147383648). - -%% First number is maximum size in bytes before we start to -%% truncate. The following 4-tuple is: -%% -%% 1) Maximum size of printable lists and binaries. -%% 2) Maximum size of any structural term. -%% 3) Amount to decrease 1) every time we descend while truncating. -%% 4) Amount to decrease 2) every time we descend while truncating. -%% -%% Whole thing feeds into truncate:log_event/2. --define(LOG_TRUNC, {100000, {2000, 100, 50, 5}}). - --define(store_proc_name(N), rabbit_misc:store_proc_name(?MODULE, N)). diff --git a/include/rabbit_msg_store.hrl b/include/rabbit_msg_store.hrl deleted file mode 100644 index 8bcf2ce629..0000000000 --- a/include/rabbit_msg_store.hrl +++ /dev/null @@ -1,25 +0,0 @@ -%% 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 Pivotal Software, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --include("rabbit.hrl"). - --ifdef(use_specs). - --type(msg() :: any()). - --endif. - --record(msg_location, {msg_id, ref_count, file, offset, total_size}). diff --git a/packaging/RPMS/Fedora/Makefile b/packaging/RPMS/Fedora/Makefile index 4f5f13278d..fc37ceae79 100644 --- a/packaging/RPMS/Fedora/Makefile +++ b/packaging/RPMS/Fedora/Makefile @@ -1,15 +1,24 @@ -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') +SOURCE_DIST_FILE ?= $(wildcard ../../../rabbitmq-server-*.tar.xz) +ifeq ($(SOURCE_DIST_FILE),) +$(error Cannot find source archive; please specify SOURCE_DIST_FILE) +endif +ifneq ($(words $(SOURCE_DIST_FILE)),1) +$(error Multile source archives found; please specify SOURCE_DIST_FILE) +endif + +VERSION ?= $(patsubst rabbitmq-server-%.tar.xz,%,$(notdir $(SOURCE_DIST_FILE))) +ifeq ($(VERSION),) +$(error Cannot determine version; please specify VERSION) +endif -TOP_DIR=$(shell pwd) -#Under debian we do not want to check build dependencies, since that -#only checks build-dependencies using rpms, not debs -DEFINES=--define '_topdir $(TOP_DIR)' --define '_tmppath $(TOP_DIR)/tmp' --define '_sysconfdir /etc' --define '_localstatedir /var' +TOP_DIR = $(shell pwd) +# Under debian we do not want to check build dependencies, since that +# only checks build-dependencies using rpms, not debs +DEFINES = --define '_topdir $(TOP_DIR)' --define '_tmppath $(TOP_DIR)/tmp' \ + --define '_sysconfdir /etc' --define '_localstatedir /var' ifndef RPM_OS -RPM_OS=fedora +RPM_OS = fedora endif ifeq "$(RPM_OS)" "suse" @@ -26,16 +35,18 @@ SPEC_DEFINES=--define 'group_tag Development/Libraries' START_PROG=daemon endif -rpms: clean server +.PHONY: all prepare server clean + +all: clean server + @: prepare: mkdir -p BUILD SOURCES SPECS SRPMS RPMS tmp - cp $(TARBALL_DIR)/$(TARBALL) SOURCES + cp $(SOURCE_DIST_FILE) SOURCES cp rabbitmq-server.spec SPECS sed -i 's|%%VERSION%%|$(VERSION)|;s|%%REQUIRES%%|$(REQUIRES)|' \ SPECS/rabbitmq-server.spec - cp ${COMMON_DIR}/* SOURCES/ cp rabbitmq-server.init SOURCES/rabbitmq-server.init sed -i \ -e 's|^START_PROG=.*$$|START_PROG="$(START_PROG)"|' \ @@ -46,13 +57,16 @@ ifeq "$(RPM_OS)" "fedora" sed -i -e '/^# Default-Start:/d;/^# Default-Stop:/d' \ SOURCES/rabbitmq-server.init endif - sed -i -e 's|@SU_RABBITMQ_SH_C@|su rabbitmq -s /bin/sh -c|' \ - -e 's|@STDOUT_STDERR_REDIRECTION@||' \ - SOURCES/rabbitmq-script-wrapper + cp rabbitmq-server.logrotate SOURCES/rabbitmq-server.logrotate server: prepare rpmbuild -ba --nodeps SPECS/rabbitmq-server.spec $(DEFINES) $(OS_DEFINES) $(SPEC_DEFINES) + if test "$(PACKAGES_DIR)"; then \ + mkdir -p "$(PACKAGES_DIR)"; \ + mv SRPMS/*.rpm RPMS/noarch/*.rpm "$(PACKAGES_DIR)"; \ + fi + clean: rm -rf SOURCES SPECS RPMS SRPMS BUILD tmp diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 75fbcec9c1..a18c3d10a4 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -5,16 +5,12 @@ Version: %%VERSION%% Release: 1%{?dist} License: MPLv1.1 and MIT and ASL 2.0 and BSD Group: %{group_tag} -Source: http://www.rabbitmq.com/releases/rabbitmq-server/v%{version}/%{name}-%{version}.tar.gz +Source: http://www.rabbitmq.com/releases/rabbitmq-server/v%{version}/%{name}-%{version}.tar.xz Source1: rabbitmq-server.init -Source2: rabbitmq-script-wrapper -Source3: rabbitmq-server.logrotate -Source4: rabbitmq-server.ocf -Source5: README -Source6: rabbitmq-server-ha.ocf +Source2: rabbitmq-server.logrotate URL: http://www.rabbitmq.com/ BuildArch: noarch -BuildRequires: erlang >= R16B-03, python-simplejson, xmlto, libxslt, gzip, sed, zip +BuildRequires: erlang >= R16B-03, python-simplejson, xmlto, libxslt, gzip, sed, zip, rsync Requires: erlang >= R16B-03, logrotate BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%{_arch}-root Summary: The RabbitMQ server @@ -29,10 +25,9 @@ scalable implementation of an AMQP broker. # We want to install into /usr/lib, even on 64-bit platforms %define _rabbit_libdir %{_exec_prefix}/lib/rabbitmq %define _rabbit_erllibdir %{_rabbit_libdir}/lib/rabbitmq_server-%{version} -%define _rabbit_wrapper %{_builddir}/`basename %{S:2}` -%define _rabbit_server_ocf %{_builddir}/`basename %{S:4}` +%define _rabbit_server_ocf scripts/rabbitmq-server.ocf %define _plugins_state_dir %{_localstatedir}/lib/rabbitmq/plugins -%define _rabbit_server_ha_ocf %{_builddir}/`basename %{S:6}` +%define _rabbit_server_ha_ocf scripts/rabbitmq-server-ha.ocf %define _maindir %{buildroot}%{_rabbit_erllibdir} @@ -42,34 +37,37 @@ scalable implementation of an AMQP broker. %setup -q %build -cp %{S:2} %{_rabbit_wrapper} -cp %{S:4} %{_rabbit_server_ocf} -cp %{S:5} %{_builddir}/rabbitmq-server-%{version}/README -cp %{S:6} %{_rabbit_server_ha_ocf} -make %{?_smp_mflags} +cp -a docs/README-for-packages %{_builddir}/rabbitmq-server-%{version}/README +make %{?_smp_mflags} dist manpages %install rm -rf %{buildroot} -make install TARGET_DIR=%{_maindir} \ - SBIN_DIR=%{buildroot}%{_rabbit_libdir}/bin \ - MAN_DIR=%{buildroot}%{_mandir} +make install install-bin install-man DESTDIR=%{buildroot} PREFIX=%{_exec_prefix} RMQ_ROOTDIR=%{_rabbit_libdir} MANDIR=%{_mandir} mkdir -p %{buildroot}%{_localstatedir}/lib/rabbitmq/mnesia mkdir -p %{buildroot}%{_localstatedir}/log/rabbitmq #Copy all necessary lib files etc. 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-plugins install -p -D -m 0755 %{_rabbit_server_ocf} %{buildroot}%{_exec_prefix}/lib/ocf/resource.d/rabbitmq/rabbitmq-server install -p -D -m 0755 %{_rabbit_server_ha_ocf} %{buildroot}%{_exec_prefix}/lib/ocf/resource.d/rabbitmq/rabbitmq-server-ha -install -p -D -m 0644 %{S:3} %{buildroot}%{_sysconfdir}/logrotate.d/rabbitmq-server +install -p -D -m 0644 %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/rabbitmq-server mkdir -p %{buildroot}%{_sysconfdir}/rabbitmq -rm %{_maindir}/LICENSE %{_maindir}/LICENSE-MPL-RabbitMQ %{_maindir}/INSTALL +mkdir -p %{buildroot}%{_sbindir} +sed -e 's|@SU_RABBITMQ_SH_C@|su rabbitmq -s /bin/sh -c|' \ + -e 's|@STDOUT_STDERR_REDIRECTION@||' \ + < scripts/rabbitmq-script-wrapper \ + > %{buildroot}%{_sbindir}/rabbitmqctl +chmod 0755 %{buildroot}%{_sbindir}/rabbitmqctl +for script in rabbitmq-server rabbitmq-plugins; do \ + cp -a %{buildroot}%{_sbindir}/rabbitmqctl \ + %{buildroot}%{_sbindir}/$script; \ +done + +rm %{_maindir}/LICENSE* %{_maindir}/INSTALL #Build the list of files echo '%defattr(-,root,root, -)' >%{_builddir}/%{name}.files diff --git a/packaging/debs/Debian/.gitignore b/packaging/debs/Debian/.gitignore new file mode 100644 index 0000000000..6a4aec11b5 --- /dev/null +++ b/packaging/debs/Debian/.gitignore @@ -0,0 +1,3 @@ +/debian/postrm +/debian/stamp-makefile-build +/rabbitmq-server_* diff --git a/packaging/debs/Debian/Makefile b/packaging/debs/Debian/Makefile index d9064c8729..432761c944 100644 --- a/packaging/debs/Debian/Makefile +++ b/packaging/debs/Debian/Makefile @@ -1,12 +1,20 @@ -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') +SOURCE_DIST_FILE ?= $(wildcard ../../../rabbitmq-server-*.tar.xz) +ifeq ($(SOURCE_DIST_FILE),) +$(error Cannot find source archive; please specify SOURCE_DIST_FILE) +endif +ifneq ($(words $(SOURCE_DIST_FILE)),1) +$(error Multile source archives found; please specify SOURCE_DIST_FILE) +endif -DEBIAN_ORIG_TARBALL=$(shell echo $(TARBALL) | sed -e 's:\(.*\)-\(.*\)\(\.tar\.gz\):\1_\2\.orig\3:g') -UNPACKED_DIR=rabbitmq-server-$(VERSION) -PACKAGENAME=rabbitmq-server -SIGNING_KEY_ID=056E8E56 +VERSION ?= $(patsubst rabbitmq-server-%.tar.xz,%,$(notdir $(SOURCE_DIST_FILE))) +ifeq ($(VERSION),) +$(error Cannot determine version; please specify VERSION) +endif + +DEBIAN_ORIG_TARBALL = rabbitmq-server_$(VERSION).orig.tar.xz +UNPACKED_DIR = rabbitmq-server-$(VERSION) +PACKAGENAME = rabbitmq-server +SIGNING_KEY_ID = 056E8E56 ifneq "$(UNOFFICIAL_RELEASE)" "" SIGNING=-us -uc @@ -14,25 +22,27 @@ else SIGNING=-k$(SIGNING_KEY_ID) endif -all: - @echo 'Please choose a target from the Makefile.' +all: package + @: package: clean - cp $(TARBALL_DIR)/$(TARBALL) $(DEBIAN_ORIG_TARBALL) - tar -zxf $(DEBIAN_ORIG_TARBALL) - cp -r debian $(UNPACKED_DIR) - cp $(COMMON_DIR)/* $(UNPACKED_DIR)/debian/ - sed -i -e 's|@SU_RABBITMQ_SH_C@|su rabbitmq -s /bin/sh -c|' \ - -e 's|@STDOUT_STDERR_REDIRECTION@|> "$$RABBITMQ_LOG_BASE/startup_log" 2> "$$RABBITMQ_LOG_BASE/startup_err"|' \ - $(UNPACKED_DIR)/debian/rabbitmq-script-wrapper - chmod a+x $(UNPACKED_DIR)/debian/rules - echo "This package was debianized by Tony Garnock-Jones <tonyg@rabbitmq.com> on\nWed, 3 Jan 2007 15:43:44 +0000.\n\nIt was downloaded from http://www.rabbitmq.com/\n\n" > $(UNPACKED_DIR)/debian/copyright - cat $(UNPACKED_DIR)/LICENSE >> $(UNPACKED_DIR)/debian/copyright - echo "\n\nThe Debian packaging is (C) 2007-2013, GoPivotal, Inc. and is licensed\nunder the MPL 1.1, see above.\n" >> $(UNPACKED_DIR)/debian/copyright + cp -a $(SOURCE_DIST_FILE) $(DEBIAN_ORIG_TARBALL) + tar -Jxf $(DEBIAN_ORIG_TARBALL) + cp -a debian $(UNPACKED_DIR) + rsync -a \ + --exclude '.sw?' --exclude '.*.sw?' \ + --exclude '.git*' \ + --delete --delete-excluded \ + debian/ $(UNPACKED_DIR)/debian/ UNOFFICIAL_RELEASE=$(UNOFFICIAL_RELEASE) VERSION=$(VERSION) ./check-changelog.sh rabbitmq-server $(UNPACKED_DIR) - cd $(UNPACKED_DIR); GNUPGHOME=$(GNUPG_PATH)/.gnupg dpkg-buildpackage -sa -rfakeroot $(SIGNING) + cd $(UNPACKED_DIR); GNUPGHOME=$(GNUPG_PATH)/.gnupg dpkg-buildpackage -sa $(SIGNING) rm -rf $(UNPACKED_DIR) + if test "$(PACKAGES_DIR)"; then \ + mkdir -p "$(PACKAGES_DIR)"; \ + mv $(PACKAGENAME)_$(VERSION)* "$(PACKAGES_DIR)"; \ + fi + clean: rm -rf $(UNPACKED_DIR) rm -f $(PACKAGENAME)_*.tar.gz diff --git a/packaging/debs/Debian/debian/compat b/packaging/debs/Debian/debian/compat index 7ed6ff82de..ec635144f6 100644 --- a/packaging/debs/Debian/debian/compat +++ b/packaging/debs/Debian/debian/compat @@ -1 +1 @@ -5 +9 diff --git a/packaging/debs/Debian/debian/control b/packaging/debs/Debian/debian/control index 8d018dd46c..56acaa948e 100644 --- a/packaging/debs/Debian/debian/control +++ b/packaging/debs/Debian/debian/control @@ -2,9 +2,19 @@ Source: rabbitmq-server Section: net Priority: extra Maintainer: RabbitMQ Team <info@rabbitmq.com> -Uploaders: RabbitMQ Team <info@rabbitmq.com> -Build-Depends: cdbs, debhelper (>= 5), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:16.b.3), erlang-src (>= 1:16.b.3), unzip, zip -Standards-Version: 3.9.2 +Uploaders: Alvaro Videla <alvaro@rabbitmq.com>, + Michael Klishin <michael@rabbitmq.com>, + Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com>, + Giuseppe Privitera <giuseppe@rabbitmq.com> +Build-Depends: debhelper (>= 9), + erlang-dev, + python-simplejson, + xmlto, + xsltproc, + erlang-nox (>= 1:16.b.3), + zip, + rsync +Standards-Version: 3.9.4 Package: rabbitmq-server Architecture: all diff --git a/packaging/debs/Debian/debian/copyright b/packaging/debs/Debian/debian/copyright new file mode 100644 index 0000000000..521b903754 --- /dev/null +++ b/packaging/debs/Debian/debian/copyright @@ -0,0 +1,52 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: rabbitmq-server +Upstream-Contact: Jean-Sébastien Pédron <jean-sebastien@rabbitmq.com> +Source: https://github.com/rabbitmq/rabbitmq-server + +Files: * +Copyright: 2007-2015 Pivotal Software, Inc. +License: MPL-1.1 + +Files: src/mochinum.erl deps/rabbit_common/src/mochijson2.erl +Copyright: 2007 Mochi Media, Inc. +License: MIT + +License: MPL-1.1 + 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 Pivotal Software, Inc. + Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. + +License: MIT + This is the MIT license + . + Copyright (c) 2007 Mochi Media, Inc + . + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions + : + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packaging/debs/Debian/debian/rabbitmq-server.docs b/packaging/debs/Debian/debian/rabbitmq-server.docs new file mode 100644 index 0000000000..40d4f2dc81 --- /dev/null +++ b/packaging/debs/Debian/debian/rabbitmq-server.docs @@ -0,0 +1 @@ +docs/rabbitmq.config.example diff --git a/packaging/debs/Debian/debian/rabbitmq-server.manpages b/packaging/debs/Debian/debian/rabbitmq-server.manpages new file mode 100644 index 0000000000..e0220b47c3 --- /dev/null +++ b/packaging/debs/Debian/debian/rabbitmq-server.manpages @@ -0,0 +1,4 @@ +docs/rabbitmq-env.conf.5 +docs/rabbitmq-plugins.1 +docs/rabbitmq-server.1 +docs/rabbitmqctl.1 diff --git a/packaging/debs/Debian/debian/rules b/packaging/debs/Debian/debian/rules index 434172defc..053df18115 100644..100755 --- a/packaging/debs/Debian/debian/rules +++ b/packaging/debs/Debian/debian/rules @@ -1,26 +1,57 @@ #!/usr/bin/make -f +# -*- makefile -*- -include /usr/share/cdbs/1/rules/debhelper.mk -include /usr/share/cdbs/1/class/makefile.mk +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 -RABBIT_LIB=$(DEB_DESTDIR)usr/lib/rabbitmq/lib/rabbitmq_server-$(DEB_UPSTREAM_VERSION)/ -RABBIT_BIN=$(DEB_DESTDIR)usr/lib/rabbitmq/bin/ +DEB_DESTDIR = debian/rabbitmq-server +VERSION = $(shell dpkg-parsechangelog | awk '/^Version:/ {version=$$0; sub(/Version: /, "", version); sub(/-.*/, "", version); print version;}') -DOCDIR=$(DEB_DESTDIR)usr/share/doc/rabbitmq-server/ -DEB_MAKE_INSTALL_TARGET := install TARGET_DIR=$(RABBIT_LIB) SBIN_DIR=$(RABBIT_BIN) DOC_INSTALL_DIR=$(DOCDIR) MAN_DIR=$(DEB_DESTDIR)usr/share/man/ -DEB_MAKE_CLEAN_TARGET:= distclean -DEB_INSTALL_DOCS_ALL=debian/README +%: + dh $@ --parallel -install/rabbitmq-server:: - mkdir -p $(DOCDIR) - rm $(RABBIT_LIB)LICENSE* $(RABBIT_LIB)INSTALL* - for script in rabbitmqctl rabbitmq-server rabbitmq-plugins; do \ - install -p -D -m 0755 debian/rabbitmq-script-wrapper $(DEB_DESTDIR)usr/sbin/$$script; \ +override_dh_auto_clean: + $(MAKE) clean distclean-manpages + +override_dh_auto_build: + $(MAKE) dist manpages + +override_dh_auto_test: + @: + +export PREFIX RMQ_ROOTDIR + +override_dh_auto_install: PREFIX = /usr +override_dh_auto_install: RMQ_ROOTDIR = $(PREFIX)/lib/rabbitmq +override_dh_auto_install: RMQ_ERLAPP_DIR = $(RMQ_ROOTDIR)/lib/rabbitmq_server-$(VERSION) +override_dh_auto_install: + dh_auto_install + + $(MAKE) install-bin DESTDIR=$(DEB_DESTDIR) + + sed -e 's|@RABBIT_LIB@|$(RMQ_ERLAPP_DIR)|g' \ + < debian/postrm.in > debian/postrm + + sed -e 's|@SU_RABBITMQ_SH_C@|su rabbitmq -s /bin/sh -c|' \ + -e 's|@STDOUT_STDERR_REDIRECTION@|> "$$RABBITMQ_LOG_BASE/startup_log" 2> "$$RABBITMQ_LOG_BASE/startup_err"|' \ + < scripts/rabbitmq-script-wrapper \ + > $(DEB_DESTDIR)$(PREFIX)/sbin/rabbitmqctl + chmod 0755 $(DEB_DESTDIR)$(PREFIX)/sbin/rabbitmqctl + for script in rabbitmq-server rabbitmq-plugins; do \ + cp -a $(DEB_DESTDIR)$(PREFIX)/sbin/rabbitmqctl \ + $(DEB_DESTDIR)$(PREFIX)/sbin/$$script; \ done - sed -e 's|@RABBIT_LIB@|/usr/lib/rabbitmq/lib/rabbitmq_server-$(DEB_UPSTREAM_VERSION)|g' <debian/postrm.in >debian/postrm - install -p -D -m 0755 debian/rabbitmq-server.ocf $(DEB_DESTDIR)usr/lib/ocf/resource.d/rabbitmq/rabbitmq-server - install -p -D -m 0755 debian/rabbitmq-server-ha.ocf $(DEB_DESTDIR)usr/lib/ocf/resource.d/rabbitmq/rabbitmq-server-ha - install -p -D -m 0644 debian/rabbitmq-server.default $(DEB_DESTDIR)etc/default/rabbitmq-server -clean:: - rm -f plugins-src/rabbitmq-server debian/postrm plugins/README + install -p -D -m 0644 debian/rabbitmq-server.default \ + $(DEB_DESTDIR)/etc/default/rabbitmq-server + + install -p -D -m 0755 scripts/rabbitmq-server.ocf \ + $(DEB_DESTDIR)$(PREFIX)/lib/ocf/resource.d/rabbitmq/rabbitmq-server + install -p -D -m 0755 scripts/rabbitmq-server-ha.ocf \ + $(DEB_DESTDIR)$(PREFIX)/lib/ocf/resource.d/rabbitmq/rabbitmq-server-ha + + rm $(DEB_DESTDIR)$(RMQ_ERLAPP_DIR)/LICENSE* \ + $(DEB_DESTDIR)$(RMQ_ERLAPP_DIR)/INSTALL + + rmdir $(DEB_DESTDIR)$(PREFIX)/lib/erlang/lib \ + $(DEB_DESTDIR)$(PREFIX)/lib/erlang diff --git a/packaging/debs/Debian/debian/source/format b/packaging/debs/Debian/debian/source/format new file mode 100644 index 0000000000..163aaf8d82 --- /dev/null +++ b/packaging/debs/Debian/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/packaging/generic-unix/Makefile b/packaging/generic-unix/Makefile index ddad8c09fa..4f5e20c03c 100644 --- a/packaging/generic-unix/Makefile +++ b/packaging/generic-unix/Makefile @@ -1,17 +1,31 @@ -VERSION=0.0.0 -SOURCE_DIR=rabbitmq-server-$(VERSION) -TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_TARBALL=rabbitmq-server-generic-unix-$(VERSION) +SOURCE_DIST_FILE ?= $(wildcard ../../../rabbitmq-server-*.tar.xz) +ifeq ($(SOURCE_DIST_FILE),) +$(error Cannot find source archive; please specify SOURCE_DIST_FILE) +endif +ifneq ($(words $(SOURCE_DIST_FILE)),1) +$(error Multile source archives found; please specify SOURCE_DIST_FILE) +endif + +VERSION ?= $(patsubst rabbitmq-server-%.tar.xz,%,$(notdir $(SOURCE_DIST_FILE))) +ifeq ($(VERSION),) +$(error Cannot determine version; please specify VERSION) +endif + +SOURCE_DIR = rabbitmq-server-$(VERSION) +TARGET_DIR = rabbitmq_server-$(VERSION) +TARGET_TARBALL = rabbitmq-server-generic-unix-$(VERSION) + +all: dist + @: dist: - tar -zxf ../../dist/$(SOURCE_DIR).tar.gz + tar -Jxf $(SOURCE_DIST_FILE) $(MAKE) -C $(SOURCE_DIR) \ - TARGET_DIR=`pwd`/$(TARGET_DIR) \ - SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ - MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ - DOC_INSTALL_DIR=`pwd`/$(TARGET_DIR)/etc/rabbitmq \ - install + PREFIX= RMQ_ROOTDIR= \ + RMQ_ERLAPP_DIR=`pwd`/$(TARGET_DIR) \ + MANDIR=`pwd`/$(TARGET_DIR)/share/man \ + manpages install install-man sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ @@ -23,6 +37,11 @@ dist: tar -zcf $(TARGET_TARBALL).tar.gz $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) + if test "$(PACKAGES_DIR)"; then \ + mkdir -p "$(PACKAGES_DIR)"; \ + mv $(TARGET_TARBALL).tar.gz "$(PACKAGES_DIR)"; \ + fi + clean: clean_partial rm -f rabbitmq-server-generic-unix-*.tar.gz diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 903458836c..e75c656e2f 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -1,4 +1,16 @@ -VERSION=0.0.0 +SOURCE_DIST_FILE ?= $(wildcard ../../../rabbitmq-server-*.tar.xz) +ifeq ($(SOURCE_DIST_FILE),) +$(error Cannot find source archive; please specify SOURCE_DIST_FILE) +endif +ifneq ($(words $(SOURCE_DIST_FILE)),1) +$(error Multile source archives found; please specify SOURCE_DIST_FILE) +endif + +VERSION ?= $(patsubst rabbitmq-server-%.tar.xz,%,$(notdir $(SOURCE_DIST_FILE))) +ifeq ($(VERSION),) +$(error Cannot determine version; please specify VERSION) +endif + SOURCE_DIR=rabbitmq-server-$(VERSION) TARGET_DIR=rabbitmq_server-$(VERSION) TARGET_TARBALL=rabbitmq-server-$(OS)-standalone-$(VERSION) @@ -18,15 +30,20 @@ RABBITMQ_DEFAULTS=$(TARGET_DIR)/sbin/rabbitmq-defaults fix_defaults = sed -e $(1) $(RABBITMQ_DEFAULTS) > $(RABBITMQ_DEFAULTS).tmp \ && mv $(RABBITMQ_DEFAULTS).tmp $(RABBITMQ_DEFAULTS) +all: dist + @: + dist: - tar -zxf ../../dist/$(SOURCE_DIR).tar.gz + tar -Jxf $(SOURCE_DIST_FILE) $(MAKE) -C $(SOURCE_DIR) \ - TARGET_DIR=`pwd`/$(TARGET_DIR) \ - SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ - MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ - DOC_INSTALL_DIR=`pwd`/$(TARGET_DIR)/etc/rabbitmq \ - install + PREFIX= RMQ_ROOTDIR= \ + RMQ_ERLAPP_DIR=`pwd`/$(TARGET_DIR) \ + MANDIR=`pwd`/$(TARGET_DIR)/share/man \ + manpages install install-man + + mkdir -p $(TARGET_DIR)/etc/rabbitmq + cp -a $(SOURCE_DIR)/docs/rabbitmq.config.example $(TARGET_DIR)/etc/rabbitmq ## Here we set the RABBITMQ_HOME variable, ## then we make ERL_DIR point to our released erl @@ -70,6 +87,11 @@ dist: tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) + if test "$(PACKAGES_DIR)"; then \ + mkdir -p "$(PACKAGES_DIR)"; \ + mv $(TARGET_TARBALL).tar.gz "$(PACKAGES_DIR)"; \ + fi + clean: clean_partial rm -f rabbitmq-server-$(OS)-standalone-*.tar.gz @@ -83,6 +105,7 @@ generate_release: -I $(TARGET_DIR)/include/ -o src -Wall \ -v +debug_info -Duse_specs -Duse_proper_qc \ -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl + ERL_LIBS="$(TARGET_DIR)/plugins:$$ERL_LIBS" \ erl \ -pa "$(RABBITMQ_EBIN_ROOT)" \ -pa src \ diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index f5e1ecf8a1..21bf2484c5 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -40,8 +40,8 @@ start() -> %% add the plugin ebin folder to the code path. add_plugins_to_path(UnpackedPluginDir), - PluginAppNames = [P#plugin.name || - P <- rabbit_plugins:list(PluginsDistDir)], + PluginAppNames = [rabbit_common | [P#plugin.name || + P <- rabbit_plugins:list(PluginsDistDir)]], %% Build the entire set of dependencies - this will load the %% applications along the way diff --git a/packaging/windows-exe/Makefile b/packaging/windows-exe/Makefile index ab50e30b1d..047e208f09 100644 --- a/packaging/windows-exe/Makefile +++ b/packaging/windows-exe/Makefile @@ -1,16 +1,30 @@ -VERSION=0.0.0 -ZIP=../windows/rabbitmq-server-windows-$(VERSION) +ifeq ($(PACKAGES_DIR),) +ZIP_DIR = ../windows +else +ZIP_DIR = $(PACKAGES_DIR) +endif +ZIP = $(notdir $(wildcard $(ZIP_DIR)/rabbitmq-server-windows-[0-9.]*.zip)) + +VERSION = $(patsubst rabbitmq-server-windows-%.zip,%,$(ZIP)) + +all: dist + @: dist: rabbitmq-$(VERSION).nsi rabbitmq_server-$(VERSION) makensis -V2 rabbitmq-$(VERSION).nsi + if test "$(PACKAGES_DIR)"; then \ + mkdir -p "$(PACKAGES_DIR)"; \ + mv rabbitmq-server-$(VERSION).exe "$(PACKAGES_DIR)"; \ + fi + rabbitmq-$(VERSION).nsi: rabbitmq_nsi.in sed \ -e 's|%%VERSION%%|$(VERSION)|' \ $< > $@ rabbitmq_server-$(VERSION): - unzip -q $(ZIP) + unzip -q $(ZIP_DIR)/$(ZIP) clean: rm -rf rabbitmq-*.nsi rabbitmq_server-* rabbitmq-server-*.exe diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index 153ff0ef93..7ad711ba38 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -31,16 +31,6 @@ RequestExecutionLevel admin SetCompressor /solid lzma -VIProductVersion "%%VERSION%%.0" -VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "%%VERSION%%" -VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "RabbitMQ Server" -;VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "" -VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "Pivotal Software, Inc" -;VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "" ; TODO ? -VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved." -VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "RabbitMQ Server" -VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "%%VERSION%%" - ;-------------------------------- ; Pages @@ -64,6 +54,16 @@ VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "%%VERSION%%" ;-------------------------------- +VIProductVersion "%%VERSION%%.0" +VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "%%VERSION%%" +VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "RabbitMQ Server" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "" +VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "Pivotal Software, Inc" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "" ; TODO ? +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved." +VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "RabbitMQ Server" +VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "%%VERSION%%" + ; The stuff to install Section "RabbitMQ Server (required)" Rabbit diff --git a/packaging/windows/Makefile b/packaging/windows/Makefile index 5dc802a8c2..f11293391d 100644 --- a/packaging/windows/Makefile +++ b/packaging/windows/Makefile @@ -1,39 +1,38 @@ -VERSION=0.0.0 -SOURCE_DIR=rabbitmq-server-$(VERSION) -TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_ZIP=rabbitmq-server-windows-$(VERSION) +SOURCE_DIST_FILE ?= $(wildcard ../../../rabbitmq-server-*.tar.xz) +ifeq ($(SOURCE_DIST_FILE),) +$(error Cannot find source archive; please specify SOURCE_DIST_FILE) +endif +ifneq ($(words $(SOURCE_DIST_FILE)),1) +$(error Multile source archives found; please specify SOURCE_DIST_FILE) +endif + +VERSION ?= $(patsubst rabbitmq-server-%.tar.xz,%,$(notdir $(SOURCE_DIST_FILE))) +ifeq ($(VERSION),) +$(error Cannot determine version; please specify VERSION) +endif + +SOURCE_DIR = rabbitmq-server-$(VERSION) +TARGET_DIR = rabbitmq_server-$(VERSION) +TARGET_ZIP = rabbitmq-server-windows-$(VERSION) + +all: dist + @: dist: - tar -zxf ../../dist/$(SOURCE_DIR).tar.gz - $(MAKE) -C $(SOURCE_DIR) - - mkdir -p $(SOURCE_DIR)/sbin - mv $(SOURCE_DIR)/scripts/*.bat $(SOURCE_DIR)/sbin - mkdir -p $(SOURCE_DIR)/etc - cp $(SOURCE_DIR)/docs/rabbitmq.config.example $(SOURCE_DIR)/etc/rabbitmq.config.example - cp README-etc $(SOURCE_DIR)/etc/README.txt - rm -rf $(SOURCE_DIR)/scripts - 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) - mv $(TARGET_DIR)/plugins/README $(TARGET_DIR)/plugins/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 - todos $(TARGET_DIR)/INSTALL - todos $(TARGET_DIR)/LICENSE* - todos $(TARGET_DIR)/plugins/README.txt - todos $(TARGET_DIR)/etc/rabbitmq.config.example - todos $(TARGET_DIR)/etc/README.txt - rm -rf $(TARGET_DIR)/plugins-src + tar -Jxf $(SOURCE_DIST_FILE) + $(MAKE) -C $(SOURCE_DIR) install-windows \ + DESTDIR=$(abspath $(TARGET_DIR)) \ + WINDOWS_PREFIX= + + cp -a README-etc $(TARGET_DIR)/etc/README.txt + zip -q -r $(TARGET_ZIP).zip $(TARGET_DIR) - rm -rf $(TARGET_DIR) rabbitmq-service.html + rm -rf $(SOURCE_DIR) $(TARGET_DIR) + + if test "$(PACKAGES_DIR)"; then \ + mkdir -p "$(PACKAGES_DIR)"; \ + mv $(TARGET_ZIP).zip "$(PACKAGES_DIR)"; \ + fi clean: clean_partial rm -f rabbitmq-server-windows-*.zip diff --git a/packaging/windows/README-etc b/packaging/windows/README-etc index 807698e81f..b431247c6b 100644 --- a/packaging/windows/README-etc +++ b/packaging/windows/README-etc @@ -1,7 +1,7 @@ -In this directory you can find an example configuration file for RabbitMQ. - -Note that this directory is *not* where the real RabbitMQ -configuration lives. The default location for the real configuration -file is %APPDATA%\RabbitMQ\rabbitmq.config. - -%APPDATA% usually expands to C:\Users\%USERNAME%\AppData\Roaming or similar. +In this directory you can find an example configuration file for RabbitMQ.
+
+Note that this directory is *not* where the real RabbitMQ
+configuration lives. The default location for the real configuration
+file is %APPDATA%\RabbitMQ\rabbitmq.config.
+
+%APPDATA% usually expands to C:\Users\%USERNAME%\AppData\Roaming or similar.
diff --git a/rabbitmq-components.mk b/rabbitmq-components.mk new file mode 100644 index 0000000000..a7baa95d72 --- /dev/null +++ b/rabbitmq-components.mk @@ -0,0 +1,266 @@ +ifeq ($(.DEFAULT_GOAL),) +# Define default goal to `all` because this file defines some targets +# before the inclusion of erlang.mk leading to the wrong target becoming +# the default. +.DEFAULT_GOAL = all +endif + +# Automatically add rabbitmq-common to the dependencies, at least for +# the Makefiles. +ifneq ($(PROJECT),rabbit_common) +ifneq ($(PROJECT),rabbitmq_public_umbrella) +ifeq ($(filter rabbit_common,$(DEPS)),) +DEPS += rabbit_common +endif +endif +endif + +# -------------------------------------------------------------------- +# RabbitMQ components. +# -------------------------------------------------------------------- + +# For RabbitMQ repositories, we want to checkout branches which match +# the parent project. For instance, if the parent project is on a +# release tag, dependencies must be on the same release tag. If the +# parent project is on a topic branch, dependencies must be on the same +# topic branch or fallback to `stable` or `master` whichever was the +# base of the topic branch. + +RABBITMQ_REPO_BASE ?= https://github.com/rabbitmq + +dep_amqp_client = git_rmq rabbitmq-erlang-client $(current_rmq_ref) $(base_rmq_ref) +dep_rabbit = git_rmq rabbitmq-server $(current_rmq_ref) $(base_rmq_ref) +dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_federation = git_rmq rabbitmq-federation $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_management_agent = git_rmq rabbitmq-management-agent $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_management_visualiser = git_rmq rabbitmq-management-visualiser $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_metronome = git_rmq rabbitmq-metronome $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_mqtt = git_rmq rabbitmq-mqtt $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_shovel = git_rmq rabbitmq-shovel $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_shovel_management = git_rmq rabbitmq-shovel-management $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_stomp = git_rmq rabbitmq-stomp $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_toke = git_rmq rabbitmq-toke $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_tracing = git_rmq rabbitmq-tracing $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_test = git_rmq rabbitmq-test $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_web_dispatch = git_rmq rabbitmq-web-dispatch $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_web_stomp = git_rmq rabbitmq-web-stomp $(current_rmq_ref) $(base_rmq_ref) +dep_rabbitmq_web_stomp_examples = git_rmq rabbitmq-web-stomp-examples $(current_rmq_ref) $(base_rmq_ref) +dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) +dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) + +RABBITMQ_COMPONENTS = amqp_client \ + rabbit \ + rabbit_common \ + rabbitmq_amqp1_0 \ + rabbitmq_auth_backend_ldap \ + rabbitmq_auth_mechanism_ssl \ + rabbitmq_codegen \ + rabbitmq_consistent_hash_exchange \ + rabbitmq_dotnet_client \ + rabbitmq_federation \ + rabbitmq_federation_management \ + rabbitmq_java_client \ + rabbitmq_management \ + rabbitmq_management_agent \ + rabbitmq_management_visualiser \ + rabbitmq_metronome \ + rabbitmq_mqtt \ + rabbitmq_shovel \ + rabbitmq_shovel_management \ + rabbitmq_stomp \ + rabbitmq_test \ + rabbitmq_toke \ + rabbitmq_tracing \ + rabbitmq_web_dispatch \ + rabbitmq_web_stomp \ + rabbitmq_web_stomp_examples \ + sockjs \ + toke + +ifeq ($(origin current_rmq_ref),undefined) +ifneq ($(wildcard .git),) +current_rmq_ref := $(shell \ + git describe --tags --exact-match 2>/dev/null || \ + git symbolic-ref -q --short HEAD) +else +current_rmq_ref := master +endif +endif +export current_rmq_ref + +ifeq ($(origin base_rmq_ref),undefined) +ifneq ($(wildcard .git),) +base_rmq_ref := $(shell \ + (git rev-parse --verify -q stable >/dev/null && \ + git merge-base --is-ancestor $$(git merge-base master HEAD) stable && \ + echo stable) || \ + echo master) +else +base_rmq_ref := master +endif +endif +export base_rmq_ref + +dep_rmq_repo = $(if $(dep_$(1)), \ + $(RABBITMQ_REPO_BASE)/$(word 2,$(dep_$(1))).git, \ + $(pkg_$(1)_repo)) +dep_rmq_commits = $(if $(dep_$(1)), \ + $(wordlist 3,$(words $(dep_$(1))),$(dep_$(1))), \ + $(pkg_$(1)_commit)) + +define dep_fetch_git_rmq + git clone -q -n -- \ + $(call dep_rmq_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \ + cd $(DEPS_DIR)/$(call dep_name,$(1)) && ( \ + $(foreach ref,$(call dep_rmq_commits,$(1)), \ + git checkout -q $(ref) >/dev/null 2>&1 || \ + ) \ + (echo "error: no valid pathspec among: $(call dep_rmq_commits,$(1))" \ + 1>&2 && false) ) +endef + +# -------------------------------------------------------------------- +# Component usage. +# -------------------------------------------------------------------- + +# We need to pass the location of codegen to the Java client ant +# process. +CODEGEN_DIR = $(DEPS_DIR)/rabbitmq_codegen +PYTHONPATH = $(CODEGEN_DIR) +export PYTHONPATH + +ANT ?= ant +ANT_FLAGS += -Dmake.bin=$(MAKE) \ + -DUMBRELLA_AVAILABLE=true \ + -Drabbitmqctl.bin=$(RABBITMQCTL) \ + -Dsibling.codegen.dir=$(CODEGEN_DIR) +ifeq ($(PROJECT),rabbitmq_test) +ANT_FLAGS += -Dsibling.rabbitmq_test.dir=$(CURDIR) +else +ANT_FLAGS += -Dsibling.rabbitmq_test.dir=$(DEPS_DIR)/rabbitmq_test +endif +export ANT ANT_FLAGS + +ifeq ($(PROJECT),rabbit) +RABBITMQ_SCRIPTS_DIR ?= $(CURDIR)/scripts +else +RABBITMQ_SCRIPTS_DIR ?= $(DEPS_DIR)/rabbit/scripts +endif + +RABBITMQCTL ?= $(RABBITMQ_SCRIPTS_DIR)/rabbitmqctl +RABBITMQ_PLUGINS ?= $(RABBITMQ_SCRIPTS_DIR)/rabbitmq-plugins +export RABBITMQ_SCRIPTS_DIR RABBITMQCTL RABBITMQ_PLUGINS + +# -------------------------------------------------------------------- +# Component distribution. +# -------------------------------------------------------------------- + +list-dist-deps:: + @: + +prepare-dist:: + @: + +# -------------------------------------------------------------------- +# Run a RabbitMQ node (moved from rabbitmq-run.mk as a workaround). +# -------------------------------------------------------------------- + +# Add "rabbit" to the build dependencies when the user wants to start +# a broker or to the test dependencies when the user wants to test a +# project. +# +# NOTE: This should belong to rabbitmq-run.mk. Unfortunately, it is +# loaded *after* erlang.mk which is too late to add a dependency. That's +# why rabbitmq-components.mk knows the list of targets which start a +# broker and add "rabbit" to the dependencies in this case. + +ifneq ($(PROJECT),rabbit) +ifeq ($(filter rabbit,$(DEPS) $(BUILD_DEPS)),) +RUN_RMQ_TARGETS = run-broker \ + run-background-broker \ + run-node \ + run-background-node \ + start-background-node + +ifneq ($(filter $(RUN_RMQ_TARGETS),$(MAKECMDGOALS)),) +BUILD_DEPS += rabbit +endif +endif + +ifeq ($(filter rabbit,$(DEPS) $(BUILD_DEPS) $(TEST_DEPS)),) +ifneq ($(filter tests tests-with-broker,$(MAKECMDGOALS)),) +TEST_DEPS += rabbit +endif +endif +endif + +ifeq ($(filter rabbit_public_umbrella rabbit rabbit_common rabbitmq_test,$(PROJECT)),) +ifeq ($(filter rabbitmq_test,$(DEPS) $(BUILD_DEPS) $(TEST_DEPS)),) +TEST_DEPS += rabbitmq_test +endif +endif + +# -------------------------------------------------------------------- +# rabbitmq-components.mk checks. +# -------------------------------------------------------------------- + +ifeq ($(PROJECT),rabbit_common) +else ifeq ($(IS_DEP),1) +else +deps:: check-rabbitmq-components.mk +fetch-deps: check-rabbitmq-components.mk +endif + +# If this project is under the Umbrella project, we override $(DEPS_DIR) +# to point to the Umbrella's one. We also disable `make distclean` so +# $(DEPS_DIR) is not accidentally removed. + +ifneq ($(wildcard ../../UMBRELLA.md),) +UNDER_UMBRELLA = 1 +else ifneq ($(wildcard UMBRELLA.md),) +UNDER_UMBRELLA = 1 +endif + +ifeq ($(UNDER_UMBRELLA),1) +ifneq ($(PROJECT),rabbitmq_public_umbrella) +DEPS_DIR ?= $(abspath ..) + +distclean:: distclean-components + @: + +distclean-components: +endif + +ifneq ($(filter distclean distclean-deps,$(MAKECMDGOALS)),) +SKIP_DEPS = 1 +endif +endif + +UPSTREAM_RMQ_COMPONENTS_MK = $(DEPS_DIR)/rabbit_common/mk/rabbitmq-components.mk + +check-rabbitmq-components.mk: + $(verbose) cmp -s rabbitmq-components.mk \ + $(UPSTREAM_RMQ_COMPONENTS_MK) || \ + (echo "error: rabbitmq-components.mk must be updated!" 1>&2; \ + false) + +ifeq ($(PROJECT),rabbit_common) +rabbitmq-components-mk: + @: +else +rabbitmq-components-mk: + $(gen_verbose) cp -a $(UPSTREAM_RMQ_COMPONENTS_MK) . +ifeq ($(DO_COMMIT),yes) + $(verbose) git diff --quiet rabbitmq-components.mk \ + || git commit -m 'Update rabbitmq-components.mk' rabbitmq-components.mk +endif +endif diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults index 26f6af7cff..c5d87822a2 100644 --- a/scripts/rabbitmq-defaults +++ b/scripts/rabbitmq-defaults @@ -24,6 +24,12 @@ ERL_DIR= CLEAN_BOOT_FILE=start_clean SASL_BOOT_FILE=start_sasl +if [ -f "${RABBITMQ_HOME}/erlang.mk" ]; then + # RabbitMQ is executed from its source directory. The plugins + # directory and ERL_LIBS are tuned based on this. + RABBITMQ_DEV_ENV=1 +fi + ## Set default values BOOT_MODULE="rabbit" diff --git a/scripts/rabbitmq-echopid.bat b/scripts/rabbitmq-echopid.bat index 6262a1638f..6262a1638f 100755..100644 --- a/scripts/rabbitmq-echopid.bat +++ b/scripts/rabbitmq-echopid.bat diff --git a/scripts/rabbitmq-env b/scripts/rabbitmq-env index 3eef90e141..cc05015cef 100755..100644 --- a/scripts/rabbitmq-env +++ b/scripts/rabbitmq-env @@ -235,6 +235,62 @@ rmq_check_if_shared_with_mnesia \ ##--- End of overridden <var_name> variables +## Development-specific environment. +if [ "${RABBITMQ_DEV_ENV}" ]; then + if [ "$(basename "$0")" = 'rabbitmq-plugins' ]; then + # We need to query the running node for the plugins directory + # and the "enabled plugins" file. + eval $( (${RABBITMQ_SCRIPTS_DIR}/rabbitmqctl eval \ + '{ok, P} = application:get_env(rabbit, plugins_dir), + {ok, E} = application:get_env(rabbit, enabled_plugins_file), + io:format( + "plugins_dir=\"~s\"~n" + "enabled_plugins_file=\"~s\"~n", [P, E]).' \ + 2>/dev/null | head -n 2) || :) + if [ "${plugins_dir}" ]; then + RABBITMQ_PLUGINS_DIR="${plugins_dir}" + fi + if [ "${enabled_plugins_file}" ]; then + RABBITMQ_ENABLED_PLUGINS_FILE="${enabled_plugins_file}" + fi + fi + + if [ -d "${RABBITMQ_PLUGINS_DIR}" ]; then + # RabbitMQ was started with "make run-broker" from its own + # source tree. Take rabbit_common from the plugins directory. + ERL_LIBS="${RABBITMQ_PLUGINS_DIR}:${ERL_LIBS}" + else + # RabbitMQ runs from a testsuite or a plugin. The .ez files are + # not available under RabbitMQ source tree. We need to look at + # $DEPS_DIR and default locations. + + if [ "${DEPS_DIR}" -a -d "${DEPS_DIR}/rabbit_common/ebin" ]; then + # $DEPS_DIR is set, and it contains rabbitmq-common, use + # this. + DEPS_DIR_norm="${DEPS_DIR}" + elif [ -f "${RABBITMQ_SCRIPTS_DIR}/../../../erlang.mk" -a \ + -d "${RABBITMQ_SCRIPTS_DIR}/../../rabbit_common/ebin" ]; then + # Look at default locations: "deps" subdirectory inside a + # plugin or the Umbrella. + DEPS_DIR_norm="${RABBITMQ_SCRIPTS_DIR}/../.." + fi + DEPS_DIR_norm=$(rmq_realpath "${DEPS_DIR_norm}") + + ERL_LIBS="${DEPS_DIR_norm}:${ERL_LIBS}" + fi +else + if [ -d "${RABBITMQ_PLUGINS_DIR}" ]; then + # RabbitMQ was started from its install directory. Take + # rabbit_common from the plugins directory. + ERL_LIBS="${RABBITMQ_PLUGINS_DIR}:${ERL_LIBS}" + fi +fi + +ERL_LIBS=${ERL_LIBS%:} +if [ "$ERL_LIBS" ]; then + export ERL_LIBS +fi + RABBITMQ_ENV_LOADED=1 # Since we source this elsewhere, don't accidentally stop execution diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 6fb2f4f546..6fb2f4f546 100755..100644 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat diff --git a/packaging/common/rabbitmq-script-wrapper b/scripts/rabbitmq-script-wrapper index ed4c276e53..ed4c276e53 100644 --- a/packaging/common/rabbitmq-script-wrapper +++ b/scripts/rabbitmq-script-wrapper diff --git a/packaging/common/rabbitmq-server-ha.ocf b/scripts/rabbitmq-server-ha.ocf index 6404a7dbd2..6404a7dbd2 100755..100644 --- a/packaging/common/rabbitmq-server-ha.ocf +++ b/scripts/rabbitmq-server-ha.ocf diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 62da2f6256..62da2f6256 100755..100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat diff --git a/packaging/common/rabbitmq-server.ocf b/scripts/rabbitmq-server.ocf index 804e65423d..804e65423d 100755..100644 --- a/packaging/common/rabbitmq-server.ocf +++ b/scripts/rabbitmq-server.ocf diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 0845bbf58e..0845bbf58e 100755..100644 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat diff --git a/scripts/rabbitmqctl.bat b/scripts/rabbitmqctl.bat index 9ad855bf87..9ad855bf87 100755..100644 --- a/scripts/rabbitmqctl.bat +++ b/scripts/rabbitmqctl.bat diff --git a/src/app_utils.erl b/src/app_utils.erl deleted file mode 100644 index bab327eab6..0000000000 --- a/src/app_utils.erl +++ /dev/null @@ -1,127 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% --module(app_utils). - --export([load_applications/1, start_applications/1, start_applications/2, - stop_applications/1, stop_applications/2, app_dependency_order/2, - app_dependencies/1]). - --ifdef(use_specs). - --type error_handler() :: fun((atom(), any()) -> 'ok'). - --spec load_applications([atom()]) -> 'ok'. --spec start_applications([atom()]) -> 'ok'. --spec stop_applications([atom()]) -> 'ok'. --spec start_applications([atom()], error_handler()) -> 'ok'. --spec stop_applications([atom()], error_handler()) -> 'ok'. --spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. --spec app_dependencies(atom()) -> [atom()]. - --endif. - -%%--------------------------------------------------------------------------- -%% Public API - -load_applications(Apps) -> - load_applications(queue:from_list(Apps), sets:new()), - ok. - -start_applications(Apps) -> - start_applications( - Apps, fun (App, Reason) -> - throw({error, {cannot_start_application, App, Reason}}) - end). - -stop_applications(Apps) -> - stop_applications( - Apps, fun (App, Reason) -> - throw({error, {cannot_stop_application, App, Reason}}) - end). - -start_applications(Apps, ErrorHandler) -> - manage_applications(fun lists:foldl/3, - fun application:start/1, - fun application:stop/1, - already_started, - ErrorHandler, - Apps). - -stop_applications(Apps, ErrorHandler) -> - manage_applications(fun lists:foldr/3, - fun application:stop/1, - fun application:start/1, - not_started, - ErrorHandler, - Apps). - -app_dependency_order(RootApps, StripUnreachable) -> - {ok, G} = rabbit_misc:build_acyclic_graph( - fun ({App, _Deps}) -> [{App, App}] end, - fun ({App, Deps}) -> [{Dep, App} || Dep <- Deps] end, - [{App, app_dependencies(App)} || - {App, _Desc, _Vsn} <- application:loaded_applications()]), - try - case StripUnreachable of - true -> digraph:del_vertices(G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)); - false -> ok - end, - digraph_utils:topsort(G) - after - true = digraph:delete(G) - end. - -%%--------------------------------------------------------------------------- -%% Private API - -load_applications(Worklist, Loaded) -> - case queue:out(Worklist) of - {empty, _WorkList} -> - ok; - {{value, App}, Worklist1} -> - case sets:is_element(App, Loaded) of - true -> load_applications(Worklist1, Loaded); - false -> case application:load(App) of - ok -> ok; - {error, {already_loaded, App}} -> ok; - Error -> throw(Error) - end, - load_applications( - queue:join(Worklist1, - queue:from_list(app_dependencies(App))), - sets:add_element(App, Loaded)) - end - end. - -app_dependencies(App) -> - case application:get_key(App, applications) of - undefined -> []; - {ok, Lst} -> Lst - end. - -manage_applications(Iterate, Do, Undo, SkipError, ErrorHandler, Apps) -> - Iterate(fun (App, Acc) -> - case Do(App) of - ok -> [App | Acc]; - {error, {SkipError, _}} -> Acc; - {error, Reason} -> - lists:foreach(Undo, Acc), - ErrorHandler(App, Reason) - end - end, [], Apps), - ok. - diff --git a/src/credit_flow.erl b/src/credit_flow.erl deleted file mode 100644 index 8c8d340601..0000000000 --- a/src/credit_flow.erl +++ /dev/null @@ -1,220 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(credit_flow). - -%% Credit flow is controlled by a credit specification - a -%% {InitialCredit, MoreCreditAfter} tuple. For the message sender, -%% credit starts at InitialCredit and is decremented with every -%% message sent. The message receiver grants more credit to the sender -%% by sending it a {bump_credit, ...} control message after receiving -%% MoreCreditAfter messages. The sender should pass this message in to -%% handle_bump_msg/1. The sender should block when it goes below 0 -%% (check by invoking blocked/0). If a process is both a sender and a -%% receiver it will not grant any more credit to its senders when it -%% is itself blocked - thus the only processes that need to check -%% blocked/0 are ones that read from network sockets. -%% -%% Credit flows left to right when process send messags down the -%% chain, starting at the rabbit_reader, ending at the msg_store: -%% reader -> channel -> queue_process -> msg_store. -%% -%% If the message store has a back log, then it will block the -%% queue_process, which will block the channel, and finally the reader -%% will be blocked, throttling down publishers. -%% -%% Once a process is unblocked, it will grant credits up the chain, -%% possibly unblocking other processes: -%% reader <--grant channel <--grant queue_process <--grant msg_store. -%% -%% Grepping the project files for `credit_flow` will reveal the places -%% where this module is currently used, with extra comments on what's -%% going on at each instance. Note that credit flow between mirrors -%% synchronization has not been documented, since this doesn't affect -%% client publishes. - --define(DEFAULT_INITIAL_CREDIT, 200). --define(DEFAULT_MORE_CREDIT_AFTER, 50). - --define(DEFAULT_CREDIT, - case get(credit_flow_default_credit) of - undefined -> - Val = rabbit_misc:get_env(rabbit, credit_flow_default_credit, - {?DEFAULT_INITIAL_CREDIT, - ?DEFAULT_MORE_CREDIT_AFTER}), - put(credit_flow_default_credit, Val), - Val; - Val -> Val - end). - --export([send/1, send/2, ack/1, ack/2, handle_bump_msg/1, blocked/0, state/0]). --export([peer_down/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([bump_msg/0]). - --opaque(bump_msg() :: {pid(), non_neg_integer()}). --type(credit_spec() :: {non_neg_integer(), non_neg_integer()}). - --spec(send/1 :: (pid()) -> 'ok'). --spec(send/2 :: (pid(), credit_spec()) -> 'ok'). --spec(ack/1 :: (pid()) -> 'ok'). --spec(ack/2 :: (pid(), credit_spec()) -> 'ok'). --spec(handle_bump_msg/1 :: (bump_msg()) -> 'ok'). --spec(blocked/0 :: () -> boolean()). --spec(peer_down/1 :: (pid()) -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -%% process dict update macro - eliminates the performance-hurting -%% closure creation a HOF would introduce --define(UPDATE(Key, Default, Var, Expr), - begin - %% We deliberately allow Var to escape from the case here - %% to be used in Expr. Any temporary var we introduced - %% would also escape, and might conflict. - Var = case get(Key) of - undefined -> Default; - V -> V - end, - put(Key, Expr) - end). - -%% If current process was blocked by credit flow in the last -%% STATE_CHANGE_INTERVAL milliseconds, state/0 will report it as "in -%% flow". --define(STATE_CHANGE_INTERVAL, 1000000). - --ifdef(CREDIT_FLOW_TRACING). --define(TRACE_BLOCKED(SELF, FROM), rabbit_event:notify(credit_flow_blocked, - [{process, SELF}, - {process_info, erlang:process_info(SELF)}, - {from, FROM}, - {from_info, erlang:process_info(FROM)}, - {timestamp, - time_compat:os_system_time( - milliseconds)}])). --define(TRACE_UNBLOCKED(SELF, FROM), rabbit_event:notify(credit_flow_unblocked, - [{process, SELF}, - {from, FROM}, - {timestamp, - time_compat:os_system_time( - milliseconds)}])). --else. --define(TRACE_BLOCKED(SELF, FROM), ok). --define(TRACE_UNBLOCKED(SELF, FROM), ok). --endif. - -%%---------------------------------------------------------------------------- - -%% There are two "flows" here; of messages and of credit, going in -%% opposite directions. The variable names "From" and "To" refer to -%% the flow of credit, but the function names refer to the flow of -%% messages. This is the clearest I can make it (since the function -%% names form the API and want to make sense externally, while the -%% variable names are used in credit bookkeeping and want to make -%% sense internally). - -%% For any given pair of processes, ack/2 and send/2 must always be -%% called with the same credit_spec(). - -send(From) -> send(From, ?DEFAULT_CREDIT). - -send(From, {InitialCredit, _MoreCreditAfter}) -> - ?UPDATE({credit_from, From}, InitialCredit, C, - if C == 1 -> block(From), - 0; - true -> C - 1 - end). - -ack(To) -> ack(To, ?DEFAULT_CREDIT). - -ack(To, {_InitialCredit, MoreCreditAfter}) -> - ?UPDATE({credit_to, To}, MoreCreditAfter, C, - if C == 1 -> grant(To, MoreCreditAfter), - MoreCreditAfter; - true -> C - 1 - end). - -handle_bump_msg({From, MoreCredit}) -> - ?UPDATE({credit_from, From}, 0, C, - if C =< 0 andalso C + MoreCredit > 0 -> unblock(From), - C + MoreCredit; - true -> C + MoreCredit - end). - -blocked() -> case get(credit_blocked) of - undefined -> false; - [] -> false; - _ -> true - end. - -state() -> case blocked() of - true -> flow; - false -> case get(credit_blocked_at) of - undefined -> running; - B -> Now = time_compat:monotonic_time(), - Diff = time_compat:convert_time_unit(Now - B, - native, - micro_seconds), - case Diff < ?STATE_CHANGE_INTERVAL of - true -> flow; - false -> running - end - end - end. - -peer_down(Peer) -> - %% In theory we could also remove it from credit_deferred here, but it - %% doesn't really matter; at some point later we will drain - %% credit_deferred and thus send messages into the void... - unblock(Peer), - erase({credit_from, Peer}), - erase({credit_to, Peer}), - ok. - -%% -------------------------------------------------------------------------- - -grant(To, Quantity) -> - Msg = {bump_credit, {self(), Quantity}}, - case blocked() of - false -> To ! Msg; - true -> ?UPDATE(credit_deferred, [], Deferred, [{To, Msg} | Deferred]) - end. - -block(From) -> - ?TRACE_BLOCKED(self(), From), - case blocked() of - false -> put(credit_blocked_at, time_compat:monotonic_time()); - true -> ok - end, - ?UPDATE(credit_blocked, [], Blocks, [From | Blocks]). - -unblock(From) -> - ?TRACE_UNBLOCKED(self(), From), - ?UPDATE(credit_blocked, [], Blocks, Blocks -- [From]), - case blocked() of - false -> case erase(credit_deferred) of - undefined -> ok; - Credits -> [To ! Msg || {To, Msg} <- Credits] - end; - true -> ok - end. diff --git a/src/gen_server2.erl b/src/gen_server2.erl deleted file mode 100644 index 23494399d2..0000000000 --- a/src/gen_server2.erl +++ /dev/null @@ -1,1364 +0,0 @@ -%% This file is a copy of gen_server.erl from the R13B-1 Erlang/OTP -%% distribution, with the following modifications: -%% -%% 1) the module name is gen_server2 -%% -%% 2) more efficient handling of selective receives in callbacks -%% gen_server2 processes drain their message queue into an internal -%% buffer before invoking any callback module functions. Messages are -%% dequeued from the buffer for processing. Thus the effective message -%% queue of a gen_server2 process is the concatenation of the internal -%% buffer and the real message queue. -%% As a result of the draining, any selective receive invoked inside a -%% callback is less likely to have to scan a large message queue. -%% -%% 3) gen_server2:cast is guaranteed to be order-preserving -%% The original code could reorder messages when communicating with a -%% process on a remote node that was not currently connected. -%% -%% 4) The callback module can optionally implement prioritise_call/4, -%% prioritise_cast/3 and prioritise_info/3. These functions take -%% Message, From, Length and State or just Message, Length and State -%% (where Length is the current number of messages waiting to be -%% processed) and return a single integer representing the priority -%% attached to the message, or 'drop' to ignore it (for -%% prioritise_cast/3 and prioritise_info/3 only). Messages with -%% higher priorities are processed before requests with lower -%% priorities. The default priority is 0. -%% -%% 5) The callback module can optionally implement -%% handle_pre_hibernate/1 and handle_post_hibernate/1. These will be -%% called immediately prior to and post hibernation, respectively. If -%% handle_pre_hibernate returns {hibernate, NewState} then the process -%% will hibernate. If the module does not implement -%% handle_pre_hibernate/1 then the default action is to hibernate. -%% -%% 6) init can return a 4th arg, {backoff, InitialTimeout, -%% MinimumTimeout, DesiredHibernatePeriod} (all in milliseconds, -%% 'infinity' does not make sense here). Then, on all callbacks which -%% can return a timeout (including init), timeout can be -%% 'hibernate'. When this is the case, the current timeout value will -%% be used (initially, the InitialTimeout supplied from init). After -%% this timeout has occurred, hibernation will occur as normal. Upon -%% awaking, a new current timeout value will be calculated. -%% -%% The purpose is that the gen_server2 takes care of adjusting the -%% current timeout value such that the process will increase the -%% timeout value repeatedly if it is unable to sleep for the -%% DesiredHibernatePeriod. If it is able to sleep for the -%% DesiredHibernatePeriod it will decrease the current timeout down to -%% the MinimumTimeout, so that the process is put to sleep sooner (and -%% hopefully stays asleep for longer). In short, should a process -%% using this receive a burst of messages, it should not hibernate -%% between those messages, but as the messages become less frequent, -%% the process will not only hibernate, it will do so sooner after -%% each message. -%% -%% When using this backoff mechanism, normal timeout values (i.e. not -%% 'hibernate') can still be used, and if they are used then the -%% handle_info(timeout, State) will be called as normal. In this case, -%% returning 'hibernate' from handle_info(timeout, State) will not -%% hibernate the process immediately, as it would if backoff wasn't -%% being used. Instead it'll wait for the current timeout as described -%% above. -%% -%% 7) The callback module can return from any of the handle_* -%% functions, a {become, Module, State} triple, or a {become, Module, -%% State, Timeout} quadruple. This allows the gen_server to -%% dynamically change the callback module. The State is the new state -%% which will be passed into any of the callback functions in the new -%% module. Note there is no form also encompassing a reply, thus if -%% you wish to reply in handle_call/3 and change the callback module, -%% you need to use gen_server2:reply/2 to issue the reply -%% manually. The init function can similarly return a 5th argument, -%% Module, in order to dynamically decide the callback module on init. -%% -%% 8) The callback module can optionally implement -%% format_message_queue/2 which is the equivalent of format_status/2 -%% but where the second argument is specifically the priority_queue -%% which contains the prioritised message_queue. -%% -%% 9) The function with_state/2 can be used to debug a process with -%% heavyweight state (without needing to copy the entire state out of -%% process as sys:get_status/1 would). Pass through a function which -%% can be invoked on the state, get back the result. The state is not -%% modified. -%% -%% 10) an mcall/1 function has been added for performing multiple -%% call/3 in parallel. Unlike multi_call, which sends the same request -%% to same-named processes residing on a supplied list of nodes, it -%% operates on name/request pairs, where name is anything accepted by -%% call/3, i.e. a pid, global name, local name, or local name on a -%% particular node. -%% - -%% All modifications are (C) 2009-2013 GoPivotal, Inc. - -%% ``The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved via the world wide web at http://www.erlang.org/. -%% -%% 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 Initial Developer of the Original Code is Ericsson Utvecklings AB. -%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings -%% AB. All Rights Reserved.'' -%% -%% $Id$ -%% --module(gen_server2). - -%%% --------------------------------------------------- -%%% -%%% The idea behind THIS server is that the user module -%%% provides (different) functions to handle different -%%% kind of inputs. -%%% If the Parent process terminates the Module:terminate/2 -%%% function is called. -%%% -%%% The user module should export: -%%% -%%% init(Args) -%%% ==> {ok, State} -%%% {ok, State, Timeout} -%%% {ok, State, Timeout, Backoff} -%%% {ok, State, Timeout, Backoff, Module} -%%% ignore -%%% {stop, Reason} -%%% -%%% handle_call(Msg, {From, Tag}, State) -%%% -%%% ==> {reply, Reply, State} -%%% {reply, Reply, State, Timeout} -%%% {noreply, State} -%%% {noreply, State, Timeout} -%%% {stop, Reason, Reply, State} -%%% Reason = normal | shutdown | Term terminate(State) is called -%%% -%%% handle_cast(Msg, State) -%%% -%%% ==> {noreply, State} -%%% {noreply, State, Timeout} -%%% {stop, Reason, State} -%%% Reason = normal | shutdown | Term terminate(State) is called -%%% -%%% handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ... -%%% -%%% ==> {noreply, State} -%%% {noreply, State, Timeout} -%%% {stop, Reason, State} -%%% Reason = normal | shutdown | Term, terminate(State) is called -%%% -%%% terminate(Reason, State) Let the user module clean up -%%% Reason = normal | shutdown | {shutdown, Term} | Term -%%% always called when server terminates -%%% -%%% ==> ok | Term -%%% -%%% handle_pre_hibernate(State) -%%% -%%% ==> {hibernate, State} -%%% {stop, Reason, State} -%%% Reason = normal | shutdown | Term, terminate(State) is called -%%% -%%% handle_post_hibernate(State) -%%% -%%% ==> {noreply, State} -%%% {stop, Reason, State} -%%% Reason = normal | shutdown | Term, terminate(State) is called -%%% -%%% The work flow (of the server) can be described as follows: -%%% -%%% User module Generic -%%% ----------- ------- -%%% start -----> start -%%% init <----- . -%%% -%%% loop -%%% handle_call <----- . -%%% -----> reply -%%% -%%% handle_cast <----- . -%%% -%%% handle_info <----- . -%%% -%%% terminate <----- . -%%% -%%% -----> reply -%%% -%%% -%%% --------------------------------------------------- - -%% API --export([start/3, start/4, - start_link/3, start_link/4, - call/2, call/3, - cast/2, reply/2, - abcast/2, abcast/3, - multi_call/2, multi_call/3, multi_call/4, - mcall/1, - with_state/2, - enter_loop/3, enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/1]). - -%% System exports --export([system_continue/3, - system_terminate/4, - system_code_change/4, - format_status/2]). - -%% Internal exports --export([init_it/6]). - --import(error_logger, [format/2]). - -%% State record --record(gs2_state, {parent, name, state, mod, time, - timeout_state, queue, debug, prioritisers}). - --ifdef(use_specs). - -%%%========================================================================= -%%% Specs. These exist only to shut up dialyzer's warnings -%%%========================================================================= - --type(gs2_state() :: #gs2_state{}). - --spec(handle_common_termination/3 :: - (any(), atom(), gs2_state()) -> no_return()). --spec(hibernate/1 :: (gs2_state()) -> no_return()). --spec(pre_hibernate/1 :: (gs2_state()) -> no_return()). --spec(system_terminate/4 :: (_, _, _, gs2_state()) -> no_return()). - --type(millis() :: non_neg_integer()). - -%%%========================================================================= -%%% API -%%%========================================================================= - --callback init(Args :: term()) -> - {ok, State :: term()} | - {ok, State :: term(), timeout() | hibernate} | - {ok, State :: term(), timeout() | hibernate, - {backoff, millis(), millis(), millis()}} | - {ok, State :: term(), timeout() | hibernate, - {backoff, millis(), millis(), millis()}, atom()} | - ignore | - {stop, Reason :: term()}. --callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, - State :: term()) -> - {reply, Reply :: term(), NewState :: term()} | - {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} | - {noreply, NewState :: term()} | - {noreply, NewState :: term(), timeout() | hibernate} | - {stop, Reason :: term(), - Reply :: term(), NewState :: term()}. --callback handle_cast(Request :: term(), State :: term()) -> - {noreply, NewState :: term()} | - {noreply, NewState :: term(), timeout() | hibernate} | - {stop, Reason :: term(), NewState :: term()}. --callback handle_info(Info :: term(), State :: term()) -> - {noreply, NewState :: term()} | - {noreply, NewState :: term(), timeout() | hibernate} | - {stop, Reason :: term(), NewState :: term()}. --callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), - State :: term()) -> - ok | term(). --callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), - Extra :: term()) -> - {ok, NewState :: term()} | {error, Reason :: term()}. - -%% It's not possible to define "optional" -callbacks, so putting specs -%% for handle_pre_hibernate/1 and handle_post_hibernate/1 will result -%% in warnings (the same applied for the behaviour_info before). - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{init,1},{handle_call,3},{handle_cast,2},{handle_info,2}, - {terminate,2},{code_change,3}]; -behaviour_info(_Other) -> - undefined. - --endif. - -%%% ----------------------------------------------------------------- -%%% Starts a generic server. -%%% start(Mod, Args, Options) -%%% start(Name, Mod, Args, Options) -%%% start_link(Mod, Args, Options) -%%% start_link(Name, Mod, Args, Options) where: -%%% Name ::= {local, atom()} | {global, atom()} -%%% Mod ::= atom(), callback module implementing the 'real' server -%%% Args ::= term(), init arguments (to Mod:init/1) -%%% Options ::= [{timeout, Timeout} | {debug, [Flag]}] -%%% Flag ::= trace | log | {logfile, File} | statistics | debug -%%% (debug == log && statistics) -%%% Returns: {ok, Pid} | -%%% {error, {already_started, Pid}} | -%%% {error, Reason} -%%% ----------------------------------------------------------------- -start(Mod, Args, Options) -> - gen:start(?MODULE, nolink, Mod, Args, Options). - -start(Name, Mod, Args, Options) -> - gen:start(?MODULE, nolink, Name, Mod, Args, Options). - -start_link(Mod, Args, Options) -> - gen:start(?MODULE, link, Mod, Args, Options). - -start_link(Name, Mod, Args, Options) -> - gen:start(?MODULE, link, Name, Mod, Args, Options). - - -%% ----------------------------------------------------------------- -%% Make a call to a generic server. -%% If the server is located at another node, that node will -%% be monitored. -%% If the client is trapping exits and is linked server termination -%% is handled here (? Shall we do that here (or rely on timeouts) ?). -%% ----------------------------------------------------------------- -call(Name, Request) -> - case catch gen:call(Name, '$gen_call', Request) of - {ok,Res} -> - Res; - {'EXIT',Reason} -> - exit({Reason, {?MODULE, call, [Name, Request]}}) - end. - -call(Name, Request, Timeout) -> - case catch gen:call(Name, '$gen_call', Request, Timeout) of - {ok,Res} -> - Res; - {'EXIT',Reason} -> - exit({Reason, {?MODULE, call, [Name, Request, Timeout]}}) - end. - -%% ----------------------------------------------------------------- -%% Make a cast to a generic server. -%% ----------------------------------------------------------------- -cast({global,Name}, Request) -> - catch global:send(Name, cast_msg(Request)), - ok; -cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) -> - do_cast(Dest, Request); -cast(Dest, Request) when is_atom(Dest) -> - do_cast(Dest, Request); -cast(Dest, Request) when is_pid(Dest) -> - do_cast(Dest, Request). - -do_cast(Dest, Request) -> - do_send(Dest, cast_msg(Request)), - ok. - -cast_msg(Request) -> {'$gen_cast',Request}. - -%% ----------------------------------------------------------------- -%% Send a reply to the client. -%% ----------------------------------------------------------------- -reply({To, Tag}, Reply) -> - catch To ! {Tag, Reply}. - -%% ----------------------------------------------------------------- -%% Asyncronous broadcast, returns nothing, it's just send'n pray -%% ----------------------------------------------------------------- -abcast(Name, Request) when is_atom(Name) -> - do_abcast([node() | nodes()], Name, cast_msg(Request)). - -abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) -> - do_abcast(Nodes, Name, cast_msg(Request)). - -do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) -> - do_send({Name,Node},Msg), - do_abcast(Nodes, Name, Msg); -do_abcast([], _,_) -> abcast. - -%%% ----------------------------------------------------------------- -%%% Make a call to servers at several nodes. -%%% Returns: {[Replies],[BadNodes]} -%%% A Timeout can be given -%%% -%%% A middleman process is used in case late answers arrives after -%%% the timeout. If they would be allowed to glog the callers message -%%% queue, it would probably become confused. Late answers will -%%% now arrive to the terminated middleman and so be discarded. -%%% ----------------------------------------------------------------- -multi_call(Name, Req) - when is_atom(Name) -> - do_multi_call([node() | nodes()], Name, Req, infinity). - -multi_call(Nodes, Name, Req) - when is_list(Nodes), is_atom(Name) -> - do_multi_call(Nodes, Name, Req, infinity). - -multi_call(Nodes, Name, Req, infinity) -> - do_multi_call(Nodes, Name, Req, infinity); -multi_call(Nodes, Name, Req, Timeout) - when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 -> - do_multi_call(Nodes, Name, Req, Timeout). - -%%% ----------------------------------------------------------------- -%%% Make multiple calls to multiple servers, given pairs of servers -%%% and messages. -%%% Returns: {[{Dest, Reply}], [{Dest, Error}]} -%%% -%%% Dest can be pid() | RegName :: atom() | -%%% {Name :: atom(), Node :: atom()} | {global, Name :: atom()} -%%% -%%% A middleman process is used to avoid clogging up the callers -%%% message queue. -%%% ----------------------------------------------------------------- -mcall(CallSpecs) -> - Tag = make_ref(), - {_, MRef} = spawn_monitor( - fun() -> - Refs = lists:foldl( - fun ({Dest, _Request}=S, Dict) -> - dict:store(do_mcall(S), Dest, Dict) - end, dict:new(), CallSpecs), - collect_replies(Tag, Refs, [], []) - end), - receive - {'DOWN', MRef, _, _, {Tag, Result}} -> Result; - {'DOWN', MRef, _, _, Reason} -> exit(Reason) - end. - -do_mcall({{global,Name}=Dest, Request}) -> - %% whereis_name is simply an ets lookup, and is precisely what - %% global:send/2 does, yet we need a Ref to put in the call to the - %% server, so invoking whereis_name makes a lot more sense here. - case global:whereis_name(Name) of - Pid when is_pid(Pid) -> - MRef = erlang:monitor(process, Pid), - catch msend(Pid, MRef, Request), - MRef; - undefined -> - Ref = make_ref(), - self() ! {'DOWN', Ref, process, Dest, noproc}, - Ref - end; -do_mcall({{Name,Node}=Dest, Request}) when is_atom(Name), is_atom(Node) -> - {_Node, MRef} = start_monitor(Node, Name), %% NB: we don't handle R6 - catch msend(Dest, MRef, Request), - MRef; -do_mcall({Dest, Request}) when is_atom(Dest); is_pid(Dest) -> - MRef = erlang:monitor(process, Dest), - catch msend(Dest, MRef, Request), - MRef. - -msend(Dest, MRef, Request) -> - erlang:send(Dest, {'$gen_call', {self(), MRef}, Request}, [noconnect]). - -collect_replies(Tag, Refs, Replies, Errors) -> - case dict:size(Refs) of - 0 -> exit({Tag, {Replies, Errors}}); - _ -> receive - {MRef, Reply} -> - {Refs1, Replies1} = handle_call_result(MRef, Reply, - Refs, Replies), - collect_replies(Tag, Refs1, Replies1, Errors); - {'DOWN', MRef, _, _, Reason} -> - Reason1 = case Reason of - noconnection -> nodedown; - _ -> Reason - end, - {Refs1, Errors1} = handle_call_result(MRef, Reason1, - Refs, Errors), - collect_replies(Tag, Refs1, Replies, Errors1) - end - end. - -handle_call_result(MRef, Result, Refs, AccList) -> - %% we avoid the mailbox scanning cost of a call to erlang:demonitor/{1,2} - %% here, so we must cope with MRefs that we've already seen and erased - case dict:find(MRef, Refs) of - {ok, Pid} -> {dict:erase(MRef, Refs), [{Pid, Result}|AccList]}; - _ -> {Refs, AccList} - end. - -%% ----------------------------------------------------------------- -%% Apply a function to a generic server's state. -%% ----------------------------------------------------------------- -with_state(Name, Fun) -> - case catch gen:call(Name, '$with_state', Fun, infinity) of - {ok,Res} -> - Res; - {'EXIT',Reason} -> - exit({Reason, {?MODULE, with_state, [Name, Fun]}}) - end. - -%%----------------------------------------------------------------- -%% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>, <Backoff>) ->_ -%% -%% Description: Makes an existing process into a gen_server. -%% The calling process will enter the gen_server receive -%% loop and become a gen_server process. -%% The process *must* have been started using one of the -%% start functions in proc_lib, see proc_lib(3). -%% The user is responsible for any initialization of the -%% process, including registering a name for it. -%%----------------------------------------------------------------- -enter_loop(Mod, Options, State) -> - enter_loop(Mod, Options, State, self(), infinity, undefined). - -enter_loop(Mod, Options, State, Backoff = {backoff, _, _ , _}) -> - enter_loop(Mod, Options, State, self(), infinity, Backoff); - -enter_loop(Mod, Options, State, ServerName = {_, _}) -> - enter_loop(Mod, Options, State, ServerName, infinity, undefined); - -enter_loop(Mod, Options, State, Timeout) -> - enter_loop(Mod, Options, State, self(), Timeout, undefined). - -enter_loop(Mod, Options, State, ServerName, Backoff = {backoff, _, _, _}) -> - enter_loop(Mod, Options, State, ServerName, infinity, Backoff); - -enter_loop(Mod, Options, State, ServerName, Timeout) -> - enter_loop(Mod, Options, State, ServerName, Timeout, undefined). - -enter_loop(Mod, Options, State, ServerName, Timeout, Backoff) -> - Name = get_proc_name(ServerName), - Parent = get_parent(), - Debug = debug_options(Name, Options), - Queue = priority_queue:new(), - Backoff1 = extend_backoff(Backoff), - loop(find_prioritisers( - #gs2_state { parent = Parent, name = Name, state = State, - mod = Mod, time = Timeout, timeout_state = Backoff1, - queue = Queue, debug = Debug })). - -%%%======================================================================== -%%% Gen-callback functions -%%%======================================================================== - -%%% --------------------------------------------------- -%%% Initiate the new process. -%%% Register the name using the Rfunc function -%%% Calls the Mod:init/Args function. -%%% Finally an acknowledge is sent to Parent and the main -%%% loop is entered. -%%% --------------------------------------------------- -init_it(Starter, self, Name, Mod, Args, Options) -> - init_it(Starter, self(), Name, Mod, Args, Options); -init_it(Starter, Parent, Name0, Mod, Args, Options) -> - Name = name(Name0), - Debug = debug_options(Name, Options), - Queue = priority_queue:new(), - GS2State = find_prioritisers( - #gs2_state { parent = Parent, - name = Name, - mod = Mod, - queue = Queue, - debug = Debug }), - case catch Mod:init(Args) of - {ok, State} -> - proc_lib:init_ack(Starter, {ok, self()}), - loop(GS2State #gs2_state { state = State, - time = infinity, - timeout_state = undefined }); - {ok, State, Timeout} -> - proc_lib:init_ack(Starter, {ok, self()}), - loop(GS2State #gs2_state { state = State, - time = Timeout, - timeout_state = undefined }); - {ok, State, Timeout, Backoff = {backoff, _, _, _}} -> - Backoff1 = extend_backoff(Backoff), - proc_lib:init_ack(Starter, {ok, self()}), - loop(GS2State #gs2_state { state = State, - time = Timeout, - timeout_state = Backoff1 }); - {ok, State, Timeout, Backoff = {backoff, _, _, _}, Mod1} -> - Backoff1 = extend_backoff(Backoff), - proc_lib:init_ack(Starter, {ok, self()}), - loop(find_prioritisers( - GS2State #gs2_state { mod = Mod1, - state = State, - time = Timeout, - timeout_state = Backoff1 })); - {stop, Reason} -> - %% For consistency, we must make sure that the - %% registered name (if any) is unregistered before - %% the parent process is notified about the failure. - %% (Otherwise, the parent process could get - %% an 'already_started' error if it immediately - %% tried starting the process again.) - unregister_name(Name0), - proc_lib:init_ack(Starter, {error, Reason}), - exit(Reason); - ignore -> - unregister_name(Name0), - proc_lib:init_ack(Starter, ignore), - exit(normal); - {'EXIT', Reason} -> - unregister_name(Name0), - proc_lib:init_ack(Starter, {error, Reason}), - exit(Reason); - Else -> - Error = {bad_return_value, Else}, - proc_lib:init_ack(Starter, {error, Error}), - exit(Error) - end. - -name({local,Name}) -> Name; -name({global,Name}) -> Name; -%% name(Pid) when is_pid(Pid) -> Pid; -%% when R12 goes away, drop the line beneath and uncomment the line above -name(Name) -> Name. - -unregister_name({local,Name}) -> - _ = (catch unregister(Name)); -unregister_name({global,Name}) -> - _ = global:unregister_name(Name); -unregister_name(Pid) when is_pid(Pid) -> - Pid; -%% Under R12 let's just ignore it, as we have a single term as Name. -%% On R13 it will never get here, as we get tuple with 'local/global' atom. -unregister_name(_Name) -> ok. - -extend_backoff(undefined) -> - undefined; -extend_backoff({backoff, InitialTimeout, MinimumTimeout, DesiredHibPeriod}) -> - {backoff, InitialTimeout, MinimumTimeout, DesiredHibPeriod, - {erlang:phash2([node()]), - time_compat:monotonic_time(), - time_compat:unique_integer()}}. - -%%%======================================================================== -%%% Internal functions -%%%======================================================================== -%%% --------------------------------------------------- -%%% The MAIN loop. -%%% --------------------------------------------------- -loop(GS2State = #gs2_state { time = hibernate, - timeout_state = undefined, - queue = Queue }) -> - case priority_queue:is_empty(Queue) of - true -> - pre_hibernate(GS2State); - false -> - process_next_msg(GS2State) - end; - -loop(GS2State) -> - process_next_msg(drain(GS2State)). - -drain(GS2State) -> - receive - Input -> drain(in(Input, GS2State)) - after 0 -> GS2State - end. - -process_next_msg(GS2State = #gs2_state { time = Time, - timeout_state = TimeoutState, - queue = Queue }) -> - case priority_queue:out(Queue) of - {{value, Msg}, Queue1} -> - process_msg(Msg, GS2State #gs2_state { queue = Queue1 }); - {empty, Queue1} -> - {Time1, HibOnTimeout} - = case {Time, TimeoutState} of - {hibernate, {backoff, Current, _Min, _Desired, _RSt}} -> - {Current, true}; - {hibernate, _} -> - %% wake_hib/7 will set Time to hibernate. If - %% we were woken and didn't receive a msg - %% then we will get here and need a sensible - %% value for Time1, otherwise we crash. - %% R13B1 always waits infinitely when waking - %% from hibernation, so that's what we do - %% here too. - {infinity, false}; - _ -> {Time, false} - end, - receive - Input -> - %% Time could be 'hibernate' here, so *don't* call loop - process_next_msg( - drain(in(Input, GS2State #gs2_state { queue = Queue1 }))) - after Time1 -> - case HibOnTimeout of - true -> - pre_hibernate( - GS2State #gs2_state { queue = Queue1 }); - false -> - process_msg(timeout, - GS2State #gs2_state { queue = Queue1 }) - end - end - end. - -wake_hib(GS2State = #gs2_state { timeout_state = TS }) -> - TimeoutState1 = case TS of - undefined -> - undefined; - {SleptAt, TimeoutState} -> - adjust_timeout_state(SleptAt, - time_compat:monotonic_time(), - TimeoutState) - end, - post_hibernate( - drain(GS2State #gs2_state { timeout_state = TimeoutState1 })). - -hibernate(GS2State = #gs2_state { timeout_state = TimeoutState }) -> - TS = case TimeoutState of - undefined -> undefined; - {backoff, _, _, _, _} -> {time_compat:monotonic_time(), - TimeoutState} - end, - proc_lib:hibernate(?MODULE, wake_hib, - [GS2State #gs2_state { timeout_state = TS }]). - -pre_hibernate(GS2State = #gs2_state { state = State, - mod = Mod }) -> - case erlang:function_exported(Mod, handle_pre_hibernate, 1) of - true -> - case catch Mod:handle_pre_hibernate(State) of - {hibernate, NState} -> - hibernate(GS2State #gs2_state { state = NState } ); - Reply -> - handle_common_termination(Reply, pre_hibernate, GS2State) - end; - false -> - hibernate(GS2State) - end. - -post_hibernate(GS2State = #gs2_state { state = State, - mod = Mod }) -> - case erlang:function_exported(Mod, handle_post_hibernate, 1) of - true -> - case catch Mod:handle_post_hibernate(State) of - {noreply, NState} -> - process_next_msg(GS2State #gs2_state { state = NState, - time = infinity }); - {noreply, NState, Time} -> - process_next_msg(GS2State #gs2_state { state = NState, - time = Time }); - Reply -> - handle_common_termination(Reply, post_hibernate, GS2State) - end; - false -> - %% use hibernate here, not infinity. This matches - %% R13B. The key is that we should be able to get through - %% to process_msg calling sys:handle_system_msg with Time - %% still set to hibernate, iff that msg is the very msg - %% that woke us up (or the first msg we receive after - %% waking up). - process_next_msg(GS2State #gs2_state { time = hibernate }) - end. - -adjust_timeout_state(SleptAt, AwokeAt, {backoff, CurrentTO, MinimumTO, - DesiredHibPeriod, RandomState}) -> - NapLengthMicros = time_compat:convert_time_unit(AwokeAt - SleptAt, - native, micro_seconds), - CurrentMicros = CurrentTO * 1000, - MinimumMicros = MinimumTO * 1000, - DesiredHibMicros = DesiredHibPeriod * 1000, - GapBetweenMessagesMicros = NapLengthMicros + CurrentMicros, - Base = - %% If enough time has passed between the last two messages then we - %% should consider sleeping sooner. Otherwise stay awake longer. - case GapBetweenMessagesMicros > (MinimumMicros + DesiredHibMicros) of - true -> lists:max([MinimumTO, CurrentTO div 2]); - false -> CurrentTO - end, - {Extra, RandomState1} = random:uniform_s(Base, RandomState), - CurrentTO1 = Base + Extra, - {backoff, CurrentTO1, MinimumTO, DesiredHibPeriod, RandomState1}. - -in({'$gen_cast', Msg} = Input, - GS2State = #gs2_state { prioritisers = {_, F, _} }) -> - in(Input, F(Msg, GS2State), GS2State); -in({'$gen_call', From, Msg} = Input, - GS2State = #gs2_state { prioritisers = {F, _, _} }) -> - in(Input, F(Msg, From, GS2State), GS2State); -in({'$with_state', _From, _Fun} = Input, GS2State) -> - in(Input, 0, GS2State); -in({'EXIT', Parent, _R} = Input, GS2State = #gs2_state { parent = Parent }) -> - in(Input, infinity, GS2State); -in({system, _From, _Req} = Input, GS2State) -> - in(Input, infinity, GS2State); -in(Input, GS2State = #gs2_state { prioritisers = {_, _, F} }) -> - in(Input, F(Input, GS2State), GS2State). - -in(_Input, drop, GS2State) -> - GS2State; - -in(Input, Priority, GS2State = #gs2_state { queue = Queue }) -> - GS2State # gs2_state { queue = priority_queue:in(Input, Priority, Queue) }. - -process_msg({system, From, Req}, - GS2State = #gs2_state { parent = Parent, debug = Debug }) -> - %% gen_server puts Hib on the end as the 7th arg, but that version - %% of the fun seems not to be documented so leaving out for now. - sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, GS2State); -process_msg({'$with_state', From, Fun}, - GS2State = #gs2_state{state = State}) -> - reply(From, catch Fun(State)), - loop(GS2State); -process_msg({'EXIT', Parent, Reason} = Msg, - GS2State = #gs2_state { parent = Parent }) -> - terminate(Reason, Msg, GS2State); -process_msg(Msg, GS2State = #gs2_state { debug = [] }) -> - handle_msg(Msg, GS2State); -process_msg(Msg, GS2State = #gs2_state { name = Name, debug = Debug }) -> - Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {in, Msg}), - handle_msg(Msg, GS2State #gs2_state { debug = Debug1 }). - -%%% --------------------------------------------------- -%%% Send/recive functions -%%% --------------------------------------------------- -do_send(Dest, Msg) -> - catch erlang:send(Dest, Msg). - -do_multi_call(Nodes, Name, Req, infinity) -> - Tag = make_ref(), - Monitors = send_nodes(Nodes, Name, Tag, Req), - rec_nodes(Tag, Monitors, Name, undefined); -do_multi_call(Nodes, Name, Req, Timeout) -> - Tag = make_ref(), - Caller = self(), - Receiver = - spawn( - fun () -> - %% Middleman process. Should be unsensitive to regular - %% exit signals. The sychronization is needed in case - %% the receiver would exit before the caller started - %% the monitor. - process_flag(trap_exit, true), - Mref = erlang:monitor(process, Caller), - receive - {Caller,Tag} -> - Monitors = send_nodes(Nodes, Name, Tag, Req), - TimerId = erlang:start_timer(Timeout, self(), ok), - Result = rec_nodes(Tag, Monitors, Name, TimerId), - exit({self(),Tag,Result}); - {'DOWN',Mref,_,_,_} -> - %% Caller died before sending us the go-ahead. - %% Give up silently. - exit(normal) - end - end), - Mref = erlang:monitor(process, Receiver), - Receiver ! {self(),Tag}, - receive - {'DOWN',Mref,_,_,{Receiver,Tag,Result}} -> - Result; - {'DOWN',Mref,_,_,Reason} -> - %% The middleman code failed. Or someone did - %% exit(_, kill) on the middleman process => Reason==killed - exit(Reason) - end. - -send_nodes(Nodes, Name, Tag, Req) -> - send_nodes(Nodes, Name, Tag, Req, []). - -send_nodes([Node|Tail], Name, Tag, Req, Monitors) - when is_atom(Node) -> - Monitor = start_monitor(Node, Name), - %% Handle non-existing names in rec_nodes. - catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req}, - send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]); -send_nodes([_Node|Tail], Name, Tag, Req, Monitors) -> - %% Skip non-atom Node - send_nodes(Tail, Name, Tag, Req, Monitors); -send_nodes([], _Name, _Tag, _Req, Monitors) -> - Monitors. - -%% Against old nodes: -%% If no reply has been delivered within 2 secs. (per node) check that -%% the server really exists and wait for ever for the answer. -%% -%% Against contemporary nodes: -%% Wait for reply, server 'DOWN', or timeout from TimerId. - -rec_nodes(Tag, Nodes, Name, TimerId) -> - rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId). - -rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) -> - receive - {'DOWN', R, _, _, _} -> - rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId); - {{Tag, N}, Reply} -> %% Tag is bound !!! - unmonitor(R), - rec_nodes(Tag, Tail, Name, Badnodes, - [{N,Reply}|Replies], Time, TimerId); - {timeout, TimerId, _} -> - unmonitor(R), - %% Collect all replies that already have arrived - rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) - end; -rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) -> - %% R6 node - receive - {nodedown, N} -> - monitor_node(N, false), - rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId); - {{Tag, N}, Reply} -> %% Tag is bound !!! - receive {nodedown, N} -> ok after 0 -> ok end, - monitor_node(N, false), - rec_nodes(Tag, Tail, Name, Badnodes, - [{N,Reply}|Replies], 2000, TimerId); - {timeout, TimerId, _} -> - receive {nodedown, N} -> ok after 0 -> ok end, - monitor_node(N, false), - %% Collect all replies that already have arrived - rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies) - after Time -> - case rpc:call(N, erlang, whereis, [Name]) of - Pid when is_pid(Pid) -> % It exists try again. - rec_nodes(Tag, [N|Tail], Name, Badnodes, - Replies, infinity, TimerId); - _ -> % badnode - receive {nodedown, N} -> ok after 0 -> ok end, - monitor_node(N, false), - rec_nodes(Tag, Tail, Name, [N|Badnodes], - Replies, 2000, TimerId) - end - end; -rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) -> - case catch erlang:cancel_timer(TimerId) of - false -> % It has already sent it's message - receive - {timeout, TimerId, _} -> ok - after 0 -> - ok - end; - _ -> % Timer was cancelled, or TimerId was 'undefined' - ok - end, - {Replies, Badnodes}. - -%% Collect all replies that already have arrived -rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) -> - receive - {'DOWN', R, _, _, _} -> - rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); - {{Tag, N}, Reply} -> %% Tag is bound !!! - unmonitor(R), - rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) - after 0 -> - unmonitor(R), - rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) - end; -rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) -> - %% R6 node - receive - {nodedown, N} -> - monitor_node(N, false), - rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); - {{Tag, N}, Reply} -> %% Tag is bound !!! - receive {nodedown, N} -> ok after 0 -> ok end, - monitor_node(N, false), - rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) - after 0 -> - receive {nodedown, N} -> ok after 0 -> ok end, - monitor_node(N, false), - rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) - end; -rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) -> - {Replies, Badnodes}. - - -%%% --------------------------------------------------- -%%% Monitor functions -%%% --------------------------------------------------- - -start_monitor(Node, Name) when is_atom(Node), is_atom(Name) -> - if node() =:= nonode@nohost, Node =/= nonode@nohost -> - Ref = make_ref(), - self() ! {'DOWN', Ref, process, {Name, Node}, noconnection}, - {Node, Ref}; - true -> - case catch erlang:monitor(process, {Name, Node}) of - {'EXIT', _} -> - %% Remote node is R6 - monitor_node(Node, true), - Node; - Ref when is_reference(Ref) -> - {Node, Ref} - end - end. - -%% Cancels a monitor started with Ref=erlang:monitor(_, _). -unmonitor(Ref) when is_reference(Ref) -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, _, _, _} -> - true - after 0 -> - true - end. - -%%% --------------------------------------------------- -%%% Message handling functions -%%% --------------------------------------------------- - -dispatch({'$gen_cast', Msg}, Mod, State) -> - Mod:handle_cast(Msg, State); -dispatch(Info, Mod, State) -> - Mod:handle_info(Info, State). - -common_reply(_Name, From, Reply, _NState, [] = _Debug) -> - reply(From, Reply), - []; -common_reply(Name, {To, _Tag} = From, Reply, NState, Debug) -> - reply(From, Reply), - sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, NState}). - -common_noreply(_Name, _NState, [] = _Debug) -> - []; -common_noreply(Name, NState, Debug) -> - sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}). - -common_become(_Name, _Mod, _NState, [] = _Debug) -> - []; -common_become(Name, Mod, NState, Debug) -> - sys:handle_debug(Debug, fun print_event/3, Name, {become, Mod, NState}). - -handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod, - state = State, - name = Name, - debug = Debug }) -> - case catch Mod:handle_call(Msg, From, State) of - {reply, Reply, NState} -> - Debug1 = common_reply(Name, From, Reply, NState, Debug), - loop(GS2State #gs2_state { state = NState, - time = infinity, - debug = Debug1 }); - {reply, Reply, NState, Time1} -> - Debug1 = common_reply(Name, From, Reply, NState, Debug), - loop(GS2State #gs2_state { state = NState, - time = Time1, - debug = Debug1}); - {stop, Reason, Reply, NState} -> - {'EXIT', R} = - (catch terminate(Reason, Msg, - GS2State #gs2_state { state = NState })), - common_reply(Name, From, Reply, NState, Debug), - exit(R); - Other -> - handle_common_reply(Other, Msg, GS2State) - end; -handle_msg(Msg, GS2State = #gs2_state { mod = Mod, state = State }) -> - Reply = (catch dispatch(Msg, Mod, State)), - handle_common_reply(Reply, Msg, GS2State). - -handle_common_reply(Reply, Msg, GS2State = #gs2_state { name = Name, - debug = Debug}) -> - case Reply of - {noreply, NState} -> - Debug1 = common_noreply(Name, NState, Debug), - loop(GS2State #gs2_state {state = NState, - time = infinity, - debug = Debug1}); - {noreply, NState, Time1} -> - Debug1 = common_noreply(Name, NState, Debug), - loop(GS2State #gs2_state {state = NState, - time = Time1, - debug = Debug1}); - {become, Mod, NState} -> - Debug1 = common_become(Name, Mod, NState, Debug), - loop(find_prioritisers( - GS2State #gs2_state { mod = Mod, - state = NState, - time = infinity, - debug = Debug1 })); - {become, Mod, NState, Time1} -> - Debug1 = common_become(Name, Mod, NState, Debug), - loop(find_prioritisers( - GS2State #gs2_state { mod = Mod, - state = NState, - time = Time1, - debug = Debug1 })); - _ -> - handle_common_termination(Reply, Msg, GS2State) - end. - -handle_common_termination(Reply, Msg, GS2State) -> - case Reply of - {stop, Reason, NState} -> - terminate(Reason, Msg, GS2State #gs2_state { state = NState }); - {'EXIT', What} -> - terminate(What, Msg, GS2State); - _ -> - terminate({bad_return_value, Reply}, Msg, GS2State) - end. - -%%----------------------------------------------------------------- -%% Callback functions for system messages handling. -%%----------------------------------------------------------------- -system_continue(Parent, Debug, GS2State) -> - loop(GS2State #gs2_state { parent = Parent, debug = Debug }). - -system_terminate(Reason, _Parent, Debug, GS2State) -> - terminate(Reason, [], GS2State #gs2_state { debug = Debug }). - -system_code_change(GS2State = #gs2_state { mod = Mod, - state = State }, - _Module, OldVsn, Extra) -> - case catch Mod:code_change(OldVsn, State, Extra) of - {ok, NewState} -> - NewGS2State = find_prioritisers( - GS2State #gs2_state { state = NewState }), - {ok, [NewGS2State]}; - Else -> - Else - end. - -%%----------------------------------------------------------------- -%% Format debug messages. Print them as the call-back module sees -%% them, not as the real erlang messages. Use trace for that. -%%----------------------------------------------------------------- -print_event(Dev, {in, Msg}, Name) -> - case Msg of - {'$gen_call', {From, _Tag}, Call} -> - io:format(Dev, "*DBG* ~p got call ~p from ~w~n", - [Name, Call, From]); - {'$gen_cast', Cast} -> - io:format(Dev, "*DBG* ~p got cast ~p~n", - [Name, Cast]); - _ -> - io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg]) - end; -print_event(Dev, {out, Msg, To, State}, Name) -> - io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n", - [Name, Msg, To, State]); -print_event(Dev, {noreply, State}, Name) -> - io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]); -print_event(Dev, Event, Name) -> - io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]). - - -%%% --------------------------------------------------- -%%% Terminate the server. -%%% --------------------------------------------------- - -terminate(Reason, Msg, #gs2_state { name = Name, - mod = Mod, - state = State, - debug = Debug }) -> - case catch Mod:terminate(Reason, State) of - {'EXIT', R} -> - error_info(R, Reason, Name, Msg, State, Debug), - exit(R); - _ -> - case Reason of - normal -> - exit(normal); - shutdown -> - exit(shutdown); - {shutdown,_}=Shutdown -> - exit(Shutdown); - _ -> - error_info(Reason, undefined, Name, Msg, State, Debug), - exit(Reason) - end - end. - -error_info(_Reason, _RootCause, application_controller, _Msg, _State, _Debug) -> - %% OTP-5811 Don't send an error report if it's the system process - %% application_controller which is terminating - let init take care - %% of it instead - ok; -error_info(Reason, RootCause, Name, Msg, State, Debug) -> - Reason1 = error_reason(Reason), - Fmt = - "** Generic server ~p terminating~n" - "** Last message in was ~p~n" - "** When Server state == ~p~n" - "** Reason for termination == ~n** ~p~n", - case RootCause of - undefined -> format(Fmt, [Name, Msg, State, Reason1]); - _ -> format(Fmt ++ "** In 'terminate' callback " - "with reason ==~n** ~p~n", - [Name, Msg, State, Reason1, - error_reason(RootCause)]) - end, - sys:print_log(Debug), - ok. - -error_reason({undef,[{M,F,A}|MFAs]} = Reason) -> - case code:is_loaded(M) of - false -> {'module could not be loaded',[{M,F,A}|MFAs]}; - _ -> case erlang:function_exported(M, F, length(A)) of - true -> Reason; - false -> {'function not exported',[{M,F,A}|MFAs]} - end - end; -error_reason(Reason) -> - Reason. - -%%% --------------------------------------------------- -%%% Misc. functions. -%%% --------------------------------------------------- - -opt(Op, [{Op, Value}|_]) -> - {ok, Value}; -opt(Op, [_|Options]) -> - opt(Op, Options); -opt(_, []) -> - false. - -debug_options(Name, Opts) -> - case opt(debug, Opts) of - {ok, Options} -> dbg_options(Name, Options); - _ -> dbg_options(Name, []) - end. - -dbg_options(Name, []) -> - Opts = - case init:get_argument(generic_debug) of - error -> - []; - _ -> - [log, statistics] - end, - dbg_opts(Name, Opts); -dbg_options(Name, Opts) -> - dbg_opts(Name, Opts). - -dbg_opts(Name, Opts) -> - case catch sys:debug_options(Opts) of - {'EXIT',_} -> - format("~p: ignoring erroneous debug options - ~p~n", - [Name, Opts]), - []; - Dbg -> - Dbg - end. - -get_proc_name(Pid) when is_pid(Pid) -> - Pid; -get_proc_name({local, Name}) -> - case process_info(self(), registered_name) of - {registered_name, Name} -> - Name; - {registered_name, _Name} -> - exit(process_not_registered); - [] -> - exit(process_not_registered) - end; -get_proc_name({global, Name}) -> - case whereis_name(Name) of - undefined -> - exit(process_not_registered_globally); - Pid when Pid =:= self() -> - Name; - _Pid -> - exit(process_not_registered_globally) - end. - -get_parent() -> - case get('$ancestors') of - [Parent | _] when is_pid(Parent)-> - Parent; - [Parent | _] when is_atom(Parent)-> - name_to_pid(Parent); - _ -> - exit(process_was_not_started_by_proc_lib) - end. - -name_to_pid(Name) -> - case whereis(Name) of - undefined -> - case whereis_name(Name) of - undefined -> - exit(could_not_find_registerd_name); - Pid -> - Pid - end; - Pid -> - Pid - end. - -whereis_name(Name) -> - case ets:lookup(global_names, Name) of - [{_Name, Pid, _Method, _RPid, _Ref}] -> - if node(Pid) == node() -> - case is_process_alive(Pid) of - true -> Pid; - false -> undefined - end; - true -> - Pid - end; - [] -> undefined - end. - -find_prioritisers(GS2State = #gs2_state { mod = Mod }) -> - PCall = function_exported_or_default(Mod, 'prioritise_call', 4, - fun (_Msg, _From, _State) -> 0 end), - PCast = function_exported_or_default(Mod, 'prioritise_cast', 3, - fun (_Msg, _State) -> 0 end), - PInfo = function_exported_or_default(Mod, 'prioritise_info', 3, - fun (_Msg, _State) -> 0 end), - GS2State #gs2_state { prioritisers = {PCall, PCast, PInfo} }. - -function_exported_or_default(Mod, Fun, Arity, Default) -> - case erlang:function_exported(Mod, Fun, Arity) of - true -> case Arity of - 3 -> fun (Msg, GS2State = #gs2_state { queue = Queue, - state = State }) -> - Length = priority_queue:len(Queue), - case catch Mod:Fun(Msg, Length, State) of - drop -> - drop; - Res when is_integer(Res) -> - Res; - Err -> - handle_common_termination(Err, Msg, GS2State) - end - end; - 4 -> fun (Msg, From, GS2State = #gs2_state { queue = Queue, - state = State }) -> - Length = priority_queue:len(Queue), - case catch Mod:Fun(Msg, From, Length, State) of - Res when is_integer(Res) -> - Res; - Err -> - handle_common_termination(Err, Msg, GS2State) - end - end - end; - false -> Default - end. - -%%----------------------------------------------------------------- -%% Status information -%%----------------------------------------------------------------- -format_status(Opt, StatusData) -> - [PDict, SysState, Parent, Debug, - #gs2_state{name = Name, state = State, mod = Mod, queue = Queue}] = - StatusData, - NameTag = if is_pid(Name) -> - pid_to_list(Name); - is_atom(Name) -> - Name - end, - Header = lists:concat(["Status for generic server ", NameTag]), - Log = sys:get_debug(log, Debug, []), - Specfic = callback(Mod, format_status, [Opt, [PDict, State]], - fun () -> [{data, [{"State", State}]}] end), - Messages = callback(Mod, format_message_queue, [Opt, Queue], - fun () -> priority_queue:to_list(Queue) end), - [{header, Header}, - {data, [{"Status", SysState}, - {"Parent", Parent}, - {"Logged events", Log}, - {"Queued messages", Messages}]} | - Specfic]. - -callback(Mod, FunName, Args, DefaultThunk) -> - case erlang:function_exported(Mod, FunName, length(Args)) of - true -> case catch apply(Mod, FunName, Args) of - {'EXIT', _} -> DefaultThunk(); - Success -> Success - end; - false -> DefaultThunk() - end. diff --git a/src/mirrored_supervisor.erl b/src/mirrored_supervisor.erl deleted file mode 100644 index 96c1418791..0000000000 --- a/src/mirrored_supervisor.erl +++ /dev/null @@ -1,517 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(mirrored_supervisor). - -%% Mirrored Supervisor -%% =================== -%% -%% This module implements a new type of supervisor. It acts like a -%% normal supervisor, but at creation time you also provide the name -%% of a process group to join. All the supervisors within the -%% process group act like a single large distributed supervisor: -%% -%% * A process with a given child_id will only exist on one -%% supervisor within the group. -%% -%% * If one supervisor fails, children may migrate to surviving -%% supervisors within the group. -%% -%% In almost all cases you will want to use the module name for the -%% process group. Using multiple process groups with the same module -%% name is supported. Having multiple module names for the same -%% process group will lead to undefined behaviour. -%% -%% Motivation -%% ---------- -%% -%% Sometimes you have processes which: -%% -%% * Only need to exist once per cluster. -%% -%% * Does not contain much state (or can reconstruct its state easily). -%% -%% * Needs to be restarted elsewhere should it be running on a node -%% which fails. -%% -%% By creating a mirrored supervisor group with one supervisor on -%% each node, that's what you get. -%% -%% -%% API use -%% ------- -%% -%% This is basically the same as for supervisor, except that: -%% -%% 1) start_link(Module, Args) becomes -%% start_link(Group, TxFun, Module, Args). -%% -%% 2) start_link({local, Name}, Module, Args) becomes -%% start_link({local, Name}, Group, TxFun, Module, Args). -%% -%% 3) start_link({global, Name}, Module, Args) is not available. -%% -%% 4) The restart strategy simple_one_for_one is not available. -%% -%% 5) Mnesia is used to hold global state. At some point your -%% application should invoke create_tables() (or table_definitions() -%% if it wants to manage table creation itself). -%% -%% The TxFun parameter to start_link/{4,5} is a function which the -%% mirrored supervisor can use to execute Mnesia transactions. In the -%% RabbitMQ server this goes via a worker pool; in other cases a -%% function like: -%% -%% tx_fun(Fun) -> -%% case mnesia:sync_transaction(Fun) of -%% {atomic, Result} -> Result; -%% {aborted, Reason} -> throw({error, Reason}) -%% end. -%% -%% could be used. -%% -%% Internals -%% --------- -%% -%% Each mirrored_supervisor consists of three processes - the overall -%% supervisor, the delegate supervisor and the mirroring server. The -%% overall supervisor supervises the other two processes. Its pid is -%% the one returned from start_link; the pids of the other two -%% processes are effectively hidden in the API. -%% -%% The delegate supervisor is in charge of supervising all the child -%% processes that are added to the supervisor as usual. -%% -%% The mirroring server intercepts calls to the supervisor API -%% (directed at the overall supervisor), does any special handling, -%% and forwards everything to the delegate supervisor. -%% -%% This module implements all three, hence init/1 is somewhat overloaded. -%% -%% The mirroring server creates and joins a process group on -%% startup. It monitors all the existing members of this group, and -%% broadcasts a "hello" message to them so that they can monitor it in -%% turn. When it receives a 'DOWN' message, it checks to see if it's -%% the "first" server in the group and restarts all the child -%% processes from the dead supervisor if so. -%% -%% In the future we might load balance this. -%% -%% Startup is slightly fiddly. The mirroring server needs to know the -%% Pid of the overall supervisor, but we don't have that until it has -%% started. Therefore we set this after the fact. We also start any -%% children we found in Module:init() at this point, since starting -%% children requires knowing the overall supervisor pid. - --define(SUPERVISOR, supervisor2). --define(GEN_SERVER, gen_server2). --define(PG2, pg2_fixed). --define(SUP_MODULE, mirrored_supervisor_sups). - --define(TABLE, mirrored_sup_childspec). --define(TABLE_DEF, - {?TABLE, - [{record_name, mirrored_sup_childspec}, - {type, ordered_set}, - {attributes, record_info(fields, mirrored_sup_childspec)}]}). --define(TABLE_MATCH, {match, #mirrored_sup_childspec{ _ = '_' }}). - --export([start_link/4, start_link/5, - start_child/2, restart_child/2, - delete_child/2, terminate_child/2, - which_children/1, count_children/1, check_childspecs/1]). - --behaviour(?GEN_SERVER). - --export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3, - handle_cast/2]). - --export([start_internal/3]). --export([create_tables/0, table_definitions/0]). - --record(mirrored_sup_childspec, {key, mirroring_pid, childspec}). - --record(state, {overall, - delegate, - group, - tx_fun, - initial_childspecs}). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - -%%-------------------------------------------------------------------------- -%% Callback behaviour -%%-------------------------------------------------------------------------- - --callback init(Args :: term()) -> - {ok, {{RestartStrategy :: supervisor2:strategy(), - MaxR :: non_neg_integer(), - MaxT :: non_neg_integer()}, - [ChildSpec :: supervisor2:child_spec()]}} - | ignore. - -%%-------------------------------------------------------------------------- -%% Specs -%%-------------------------------------------------------------------------- - --type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). --type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. - --type group_name() :: any(). - --type(tx_fun() :: fun((fun(() -> A)) -> A)). - --spec start_link(GroupName, TxFun, Module, Args) -> startlink_ret() when - GroupName :: group_name(), - TxFun :: tx_fun(), - Module :: module(), - Args :: term(). - --spec start_link(SupName, GroupName, TxFun, Module, Args) -> - startlink_ret() when - SupName :: supervisor2:sup_name(), - GroupName :: group_name(), - TxFun :: tx_fun(), - Module :: module(), - Args :: term(). - --spec start_internal(Group, TxFun, ChildSpecs) -> Result when - Group :: group_name(), - TxFun :: tx_fun(), - ChildSpecs :: [supervisor2:child_spec()], - Result :: {'ok', pid()} | {'error', term()}. - --spec create_tables() -> Result when - Result :: 'ok'. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> [{init,1}]; -behaviour_info(_Other) -> undefined. - --endif. - -%%---------------------------------------------------------------------------- - -start_link(Group, TxFun, Mod, Args) -> - start_link0([], Group, TxFun, init(Mod, Args)). - -start_link({local, SupName}, Group, TxFun, Mod, Args) -> - start_link0([{local, SupName}], Group, TxFun, init(Mod, Args)); - -start_link({global, _SupName}, _Group, _TxFun, _Mod, _Args) -> - erlang:error(badarg). - -start_link0(Prefix, Group, TxFun, Init) -> - case apply(?SUPERVISOR, start_link, - Prefix ++ [?SUP_MODULE, {overall, Group, TxFun, Init}]) of - {ok, Pid} -> case catch call(Pid, {init, Pid}) of - ok -> {ok, Pid}; - E -> E - end; - Other -> Other - end. - -init(Mod, Args) -> - case Mod:init(Args) of - {ok, {{Bad, _, _}, _ChildSpecs}} when - Bad =:= simple_one_for_one -> erlang:error(badarg); - Init -> Init - end. - -start_child(Sup, ChildSpec) -> call(Sup, {start_child, ChildSpec}). -delete_child(Sup, Id) -> find_call(Sup, Id, {delete_child, Id}). -restart_child(Sup, Id) -> find_call(Sup, Id, {msg, restart_child, [Id]}). -terminate_child(Sup, Id) -> find_call(Sup, Id, {msg, terminate_child, [Id]}). -which_children(Sup) -> fold(which_children, Sup, fun lists:append/2). -count_children(Sup) -> fold(count_children, Sup, fun add_proplists/2). -check_childspecs(Specs) -> ?SUPERVISOR:check_childspecs(Specs). - -call(Sup, Msg) -> ?GEN_SERVER:call(mirroring(Sup), Msg, infinity). -cast(Sup, Msg) -> with_exit_handler( - fun() -> ok end, - fun() -> ?GEN_SERVER:cast(mirroring(Sup), Msg) end). - -find_call(Sup, Id, Msg) -> - Group = call(Sup, group), - MatchHead = #mirrored_sup_childspec{mirroring_pid = '$1', - key = {Group, Id}, - _ = '_'}, - %% If we did this inside a tx we could still have failover - %% immediately after the tx - we can't be 100% here. So we may as - %% well dirty_select. - case mnesia:dirty_select(?TABLE, [{MatchHead, [], ['$1']}]) of - [Mirror] -> call(Mirror, Msg); - [] -> {error, not_found} - end. - -fold(FunAtom, Sup, AggFun) -> - Group = call(Sup, group), - lists:foldl(AggFun, [], - [apply(?SUPERVISOR, FunAtom, [D]) || - M <- ?PG2:get_members(Group), - D <- [delegate(M)]]). - -child(Sup, Id) -> - [Pid] = [Pid || {Id1, Pid, _, _} <- ?SUPERVISOR:which_children(Sup), - Id1 =:= Id], - Pid. - -delegate(Sup) -> child(Sup, delegate). -mirroring(Sup) -> child(Sup, mirroring). - -%%---------------------------------------------------------------------------- - -start_internal(Group, TxFun, ChildSpecs) -> - ?GEN_SERVER:start_link(?MODULE, {Group, TxFun, ChildSpecs}, - [{timeout, infinity}]). - -%%---------------------------------------------------------------------------- - -init({Group, TxFun, ChildSpecs}) -> - {ok, #state{group = Group, - tx_fun = TxFun, - initial_childspecs = ChildSpecs}}. - -handle_call({init, Overall}, _From, - State = #state{overall = undefined, - delegate = undefined, - group = Group, - tx_fun = TxFun, - initial_childspecs = ChildSpecs}) -> - process_flag(trap_exit, true), - ?PG2:create(Group), - ok = ?PG2:join(Group, Overall), - Rest = ?PG2:get_members(Group) -- [Overall], - case Rest of - [] -> TxFun(fun() -> delete_all(Group) end); - _ -> ok - end, - [begin - ?GEN_SERVER:cast(mirroring(Pid), {ensure_monitoring, Overall}), - erlang:monitor(process, Pid) - end || Pid <- Rest], - Delegate = delegate(Overall), - erlang:monitor(process, Delegate), - State1 = State#state{overall = Overall, delegate = Delegate}, - case errors([maybe_start(Group, TxFun, Overall, Delegate, S) - || S <- ChildSpecs]) of - [] -> {reply, ok, State1}; - Errors -> {stop, {shutdown, Errors}, State1} - end; - -handle_call({start_child, ChildSpec}, _From, - State = #state{overall = Overall, - delegate = Delegate, - group = Group, - tx_fun = TxFun}) -> - {reply, case maybe_start(Group, TxFun, Overall, Delegate, ChildSpec) of - already_in_mnesia -> {error, already_present}; - {already_in_mnesia, Pid} -> {error, {already_started, Pid}}; - Else -> Else - end, State}; - -handle_call({delete_child, Id}, _From, State = #state{delegate = Delegate, - group = Group, - tx_fun = TxFun}) -> - {reply, stop(Group, TxFun, Delegate, Id), State}; - -handle_call({msg, F, A}, _From, State = #state{delegate = Delegate}) -> - {reply, apply(?SUPERVISOR, F, [Delegate | A]), State}; - -handle_call(group, _From, State = #state{group = Group}) -> - {reply, Group, State}; - -handle_call(Msg, _From, State) -> - {stop, {unexpected_call, Msg}, State}. - -handle_cast({ensure_monitoring, Pid}, State) -> - erlang:monitor(process, Pid), - {noreply, State}; - -handle_cast({die, Reason}, State = #state{group = Group}) -> - tell_all_peers_to_die(Group, Reason), - {stop, Reason, State}; - -handle_cast(Msg, State) -> - {stop, {unexpected_cast, Msg}, State}. - -handle_info({'DOWN', _Ref, process, Pid, Reason}, - State = #state{delegate = Pid, group = Group}) -> - %% Since the delegate is temporary, its death won't cause us to - %% die. Since the overall supervisor kills processes in reverse - %% order when shutting down "from above" and we started after the - %% delegate, if we see the delegate die then that means it died - %% "from below" i.e. due to the behaviour of its children, not - %% because the whole app was being torn down. - %% - %% Therefore if we get here we know we need to cause the entire - %% mirrored sup to shut down, not just fail over. - tell_all_peers_to_die(Group, Reason), - {stop, Reason, State}; - -handle_info({'DOWN', _Ref, process, Pid, _Reason}, - State = #state{delegate = Delegate, - group = Group, - tx_fun = TxFun, - overall = O}) -> - %% TODO load balance this - %% No guarantee pg2 will have received the DOWN before us. - R = case lists:sort(?PG2:get_members(Group)) -- [Pid] of - [O | _] -> ChildSpecs = - TxFun(fun() -> update_all(O, Pid) end), - [start(Delegate, ChildSpec) || ChildSpec <- ChildSpecs]; - _ -> [] - end, - case errors(R) of - [] -> {noreply, State}; - Errors -> {stop, {shutdown, Errors}, State} - end; - -handle_info(Info, State) -> - {stop, {unexpected_info, Info}, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%---------------------------------------------------------------------------- - -tell_all_peers_to_die(Group, Reason) -> - [cast(P, {die, Reason}) || P <- ?PG2:get_members(Group) -- [self()]]. - -maybe_start(Group, TxFun, Overall, Delegate, ChildSpec) -> - try TxFun(fun() -> check_start(Group, Overall, Delegate, ChildSpec) end) of - start -> start(Delegate, ChildSpec); - undefined -> already_in_mnesia; - Pid -> {already_in_mnesia, Pid} - catch - %% If we are torn down while in the transaction... - {error, E} -> {error, E} - end. - -check_start(Group, Overall, Delegate, ChildSpec) -> - case mnesia:wread({?TABLE, {Group, id(ChildSpec)}}) of - [] -> write(Group, Overall, ChildSpec), - start; - [S] -> #mirrored_sup_childspec{key = {Group, Id}, - mirroring_pid = Pid} = S, - case Overall of - Pid -> child(Delegate, Id); - _ -> case supervisor(Pid) of - dead -> write(Group, Overall, ChildSpec), - start; - Delegate0 -> child(Delegate0, Id) - end - end - end. - -supervisor(Pid) -> with_exit_handler(fun() -> dead end, - fun() -> delegate(Pid) end). - -write(Group, Overall, ChildSpec) -> - S = #mirrored_sup_childspec{key = {Group, id(ChildSpec)}, - mirroring_pid = Overall, - childspec = ChildSpec}, - ok = mnesia:write(?TABLE, S, write), - ChildSpec. - -delete(Group, Id) -> - ok = mnesia:delete({?TABLE, {Group, Id}}). - -start(Delegate, ChildSpec) -> - apply(?SUPERVISOR, start_child, [Delegate, ChildSpec]). - -stop(Group, TxFun, Delegate, Id) -> - try TxFun(fun() -> check_stop(Group, Delegate, Id) end) of - deleted -> apply(?SUPERVISOR, delete_child, [Delegate, Id]); - running -> {error, running} - catch - {error, E} -> {error, E} - end. - -check_stop(Group, Delegate, Id) -> - case child(Delegate, Id) of - undefined -> delete(Group, Id), - deleted; - _ -> running - end. - -id({Id, _, _, _, _, _}) -> Id. - -update_all(Overall, OldOverall) -> - MatchHead = #mirrored_sup_childspec{mirroring_pid = OldOverall, - key = '$1', - childspec = '$2', - _ = '_'}, - [write(Group, Overall, C) || - [{Group, _Id}, C] <- mnesia:select(?TABLE, [{MatchHead, [], ['$$']}])]. - -delete_all(Group) -> - MatchHead = #mirrored_sup_childspec{key = {Group, '_'}, - childspec = '$1', - _ = '_'}, - [delete(Group, id(C)) || - C <- mnesia:select(?TABLE, [{MatchHead, [], ['$1']}])]. - -errors(Results) -> [E || {error, E} <- Results]. - -%%---------------------------------------------------------------------------- - -create_tables() -> create_tables([?TABLE_DEF]). - -create_tables([]) -> - ok; -create_tables([{Table, Attributes} | Ts]) -> - case mnesia:create_table(Table, Attributes) of - {atomic, ok} -> create_tables(Ts); - {aborted, {already_exists, ?TABLE}} -> create_tables(Ts); - Err -> Err - end. - -table_definitions() -> - {Name, Attributes} = ?TABLE_DEF, - [{Name, [?TABLE_MATCH | Attributes]}]. - -%%---------------------------------------------------------------------------- - -with_exit_handler(Handler, Thunk) -> - try - Thunk() - catch - exit:{R, _} when R =:= noproc; R =:= nodedown; - R =:= normal; R =:= shutdown -> - Handler(); - exit:{{R, _}, _} when R =:= nodedown; R =:= shutdown -> - Handler() - end. - -add_proplists(P1, P2) -> - add_proplists(lists:keysort(1, P1), lists:keysort(1, P2), []). -add_proplists([], P2, Acc) -> P2 ++ Acc; -add_proplists(P1, [], Acc) -> P1 ++ Acc; -add_proplists([{K, V1} | P1], [{K, V2} | P2], Acc) -> - add_proplists(P1, P2, [{K, V1 + V2} | Acc]); -add_proplists([{K1, _} = KV | P1], [{K2, _} | _] = P2, Acc) when K1 < K2 -> - add_proplists(P1, P2, [KV | Acc]); -add_proplists(P1, [KV | P2], Acc) -> - add_proplists(P1, P2, [KV | Acc]). diff --git a/src/mochijson2.erl b/src/mochijson2.erl deleted file mode 100644 index bddb52cc6f..0000000000 --- a/src/mochijson2.erl +++ /dev/null @@ -1,893 +0,0 @@ -%% This file is a copy of `mochijson2.erl' from mochiweb, revision -%% d541e9a0f36c00dcadc2e589f20e47fbf46fc76f. For the license, see -%% `LICENSE-MIT-Mochi'. - -%% @author Bob Ippolito <bob@mochimedia.com> -%% @copyright 2007 Mochi Media, Inc. - -%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works -%% with binaries as strings, arrays as lists (without an {array, _}) -%% wrapper and it only knows how to decode UTF-8 (and ASCII). -%% -%% JSON terms are decoded as follows (javascript -> erlang): -%% <ul> -%% <li>{"key": "value"} -> -%% {struct, [{<<"key">>, <<"value">>}]}</li> -%% <li>["array", 123, 12.34, true, false, null] -> -%% [<<"array">>, 123, 12.34, true, false, null] -%% </li> -%% </ul> -%% <ul> -%% <li>Strings in JSON decode to UTF-8 binaries in Erlang</li> -%% <li>Objects decode to {struct, PropList}</li> -%% <li>Numbers decode to integer or float</li> -%% <li>true, false, null decode to their respective terms.</li> -%% </ul> -%% The encoder will accept the same format that the decoder will produce, -%% but will also allow additional cases for leniency: -%% <ul> -%% <li>atoms other than true, false, null will be considered UTF-8 -%% strings (even as a proplist key) -%% </li> -%% <li>{json, IoList} will insert IoList directly into the output -%% with no validation -%% </li> -%% <li>{array, Array} will be encoded as Array -%% (legacy mochijson style) -%% </li> -%% <li>A non-empty raw proplist will be encoded as an object as long -%% as the first pair does not have an atom key of json, struct, -%% or array -%% </li> -%% </ul> - --module(mochijson2). --author('bob@mochimedia.com'). --export([encoder/1, encode/1]). --export([decoder/1, decode/1, decode/2]). - -%% This is a macro to placate syntax highlighters.. --define(Q, $\"). --define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset, - column=N+S#decoder.column}). --define(INC_COL(S), S#decoder{offset=1+S#decoder.offset, - column=1+S#decoder.column}). --define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset, - column=1, - line=1+S#decoder.line}). --define(INC_CHAR(S, C), - case C of - $\n -> - S#decoder{column=1, - line=1+S#decoder.line, - offset=1+S#decoder.offset}; - _ -> - S#decoder{column=1+S#decoder.column, - offset=1+S#decoder.offset} - end). --define(IS_WHITESPACE(C), - (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). - -%% @type json_string() = atom | binary() -%% @type json_number() = integer() | float() -%% @type json_array() = [json_term()] -%% @type json_object() = {struct, [{json_string(), json_term()}]} -%% @type json_eep18_object() = {[{json_string(), json_term()}]} -%% @type json_iolist() = {json, iolist()} -%% @type json_term() = json_string() | json_number() | json_array() | -%% json_object() | json_eep18_object() | json_iolist() - --record(encoder, {handler=null, - utf8=false}). - --record(decoder, {object_hook=null, - offset=0, - line=1, - column=1, - state=null}). - -%% @spec encoder([encoder_option()]) -> function() -%% @doc Create an encoder/1 with the given options. -%% @type encoder_option() = handler_option() | utf8_option() -%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false) -encoder(Options) -> - State = parse_encoder_options(Options, #encoder{}), - fun (O) -> json_encode(O, State) end. - -%% @spec encode(json_term()) -> iolist() -%% @doc Encode the given as JSON to an iolist. -encode(Any) -> - json_encode(Any, #encoder{}). - -%% @spec decoder([decoder_option()]) -> function() -%% @doc Create a decoder/1 with the given options. -decoder(Options) -> - State = parse_decoder_options(Options, #decoder{}), - fun (O) -> json_decode(O, State) end. - -%% @spec decode(iolist(), [{format, proplist | eep18 | struct}]) -> json_term() -%% @doc Decode the given iolist to Erlang terms using the given object format -%% for decoding, where proplist returns JSON objects as [{binary(), json_term()}] -%% proplists, eep18 returns JSON objects as {[binary(), json_term()]}, and struct -%% returns them as-is. -decode(S, Options) -> - json_decode(S, parse_decoder_options(Options, #decoder{})). - -%% @spec decode(iolist()) -> json_term() -%% @doc Decode the given iolist to Erlang terms. -decode(S) -> - json_decode(S, #decoder{}). - -%% Internal API - -parse_encoder_options([], State) -> - State; -parse_encoder_options([{handler, Handler} | Rest], State) -> - parse_encoder_options(Rest, State#encoder{handler=Handler}); -parse_encoder_options([{utf8, Switch} | Rest], State) -> - parse_encoder_options(Rest, State#encoder{utf8=Switch}). - -parse_decoder_options([], State) -> - State; -parse_decoder_options([{object_hook, Hook} | Rest], State) -> - parse_decoder_options(Rest, State#decoder{object_hook=Hook}); -parse_decoder_options([{format, Format} | Rest], State) - when Format =:= struct orelse Format =:= eep18 orelse Format =:= proplist -> - parse_decoder_options(Rest, State#decoder{object_hook=Format}). - -json_encode(true, _State) -> - <<"true">>; -json_encode(false, _State) -> - <<"false">>; -json_encode(null, _State) -> - <<"null">>; -json_encode(I, _State) when is_integer(I) -> - integer_to_list(I); -json_encode(F, _State) when is_float(F) -> - mochinum:digits(F); -json_encode(S, State) when is_binary(S); is_atom(S) -> - json_encode_string(S, State); -json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso - K =/= array andalso - K =/= json) -> - json_encode_proplist(Props, State); -json_encode({struct, Props}, State) when is_list(Props) -> - json_encode_proplist(Props, State); -json_encode({Props}, State) when is_list(Props) -> - json_encode_proplist(Props, State); -json_encode({}, State) -> - json_encode_proplist([], State); -json_encode(Array, State) when is_list(Array) -> - json_encode_array(Array, State); -json_encode({array, Array}, State) when is_list(Array) -> - json_encode_array(Array, State); -json_encode({json, IoList}, _State) -> - IoList; -json_encode(Bad, #encoder{handler=null}) -> - exit({json_encode, {bad_term, Bad}}); -json_encode(Bad, State=#encoder{handler=Handler}) -> - json_encode(Handler(Bad), State). - -json_encode_array([], _State) -> - <<"[]">>; -json_encode_array(L, State) -> - F = fun (O, Acc) -> - [$,, json_encode(O, State) | Acc] - end, - [$, | Acc1] = lists:foldl(F, "[", L), - lists:reverse([$\] | Acc1]). - -json_encode_proplist([], _State) -> - <<"{}">>; -json_encode_proplist(Props, State) -> - F = fun ({K, V}, Acc) -> - KS = json_encode_string(K, State), - VS = json_encode(V, State), - [$,, VS, $:, KS | Acc] - end, - [$, | Acc1] = lists:foldl(F, "{", Props), - lists:reverse([$\} | Acc1]). - -json_encode_string(A, State) when is_atom(A) -> - L = atom_to_list(A), - case json_string_is_safe(L) of - true -> - [?Q, L, ?Q]; - false -> - json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q]) - end; -json_encode_string(B, State) when is_binary(B) -> - case json_bin_is_safe(B) of - true -> - [?Q, B, ?Q]; - false -> - json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q]) - end; -json_encode_string(I, _State) when is_integer(I) -> - [?Q, integer_to_list(I), ?Q]; -json_encode_string(L, State) when is_list(L) -> - case json_string_is_safe(L) of - true -> - [?Q, L, ?Q]; - false -> - json_encode_string_unicode(L, State, [?Q]) - end. - -json_string_is_safe([]) -> - true; -json_string_is_safe([C | Rest]) -> - case C of - ?Q -> - false; - $\\ -> - false; - $\b -> - false; - $\f -> - false; - $\n -> - false; - $\r -> - false; - $\t -> - false; - C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> - false; - C when C < 16#7f -> - json_string_is_safe(Rest); - _ -> - false - end. - -json_bin_is_safe(<<>>) -> - true; -json_bin_is_safe(<<C, Rest/binary>>) -> - case C of - ?Q -> - false; - $\\ -> - false; - $\b -> - false; - $\f -> - false; - $\n -> - false; - $\r -> - false; - $\t -> - false; - C when C >= 0, C < $\s; C >= 16#7f -> - false; - C when C < 16#7f -> - json_bin_is_safe(Rest) - end. - -json_encode_string_unicode([], _State, Acc) -> - lists:reverse([$\" | Acc]); -json_encode_string_unicode([C | Cs], State, Acc) -> - Acc1 = case C of - ?Q -> - [?Q, $\\ | Acc]; - %% Escaping solidus is only useful when trying to protect - %% against "</script>" injection attacks which are only - %% possible when JSON is inserted into a HTML document - %% in-line. mochijson2 does not protect you from this, so - %% if you do insert directly into HTML then you need to - %% uncomment the following case or escape the output of encode. - %% - %% $/ -> - %% [$/, $\\ | Acc]; - %% - $\\ -> - [$\\, $\\ | Acc]; - $\b -> - [$b, $\\ | Acc]; - $\f -> - [$f, $\\ | Acc]; - $\n -> - [$n, $\\ | Acc]; - $\r -> - [$r, $\\ | Acc]; - $\t -> - [$t, $\\ | Acc]; - C when C >= 0, C < $\s -> - [unihex(C) | Acc]; - C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 -> - [xmerl_ucs:to_utf8(C) | Acc]; - C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 -> - [unihex(C) | Acc]; - C when C < 16#7f -> - [C | Acc]; - _ -> - exit({json_encode, {bad_char, C}}) - end, - json_encode_string_unicode(Cs, State, Acc1). - -hexdigit(C) when C >= 0, C =< 9 -> - C + $0; -hexdigit(C) when C =< 15 -> - C + $a - 10. - -unihex(C) when C < 16#10000 -> - <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>, - Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], - [$\\, $u | Digits]; -unihex(C) when C =< 16#10FFFF -> - N = C - 16#10000, - S1 = 16#d800 bor ((N bsr 10) band 16#3ff), - S2 = 16#dc00 bor (N band 16#3ff), - [unihex(S1), unihex(S2)]. - -json_decode(L, S) when is_list(L) -> - json_decode(iolist_to_binary(L), S); -json_decode(B, S) -> - {Res, S1} = decode1(B, S), - {eof, _} = tokenize(B, S1#decoder{state=trim}), - Res. - -decode1(B, S=#decoder{state=null}) -> - case tokenize(B, S#decoder{state=any}) of - {{const, C}, S1} -> - {C, S1}; - {start_array, S1} -> - decode_array(B, S1); - {start_object, S1} -> - decode_object(B, S1) - end. - -make_object(V, #decoder{object_hook=N}) when N =:= null orelse N =:= struct -> - V; -make_object({struct, P}, #decoder{object_hook=eep18}) -> - {P}; -make_object({struct, P}, #decoder{object_hook=proplist}) -> - P; -make_object(V, #decoder{object_hook=Hook}) -> - Hook(V). - -decode_object(B, S) -> - decode_object(B, S#decoder{state=key}, []). - -decode_object(B, S=#decoder{state=key}, Acc) -> - case tokenize(B, S) of - {end_object, S1} -> - V = make_object({struct, lists:reverse(Acc)}, S1), - {V, S1#decoder{state=null}}; - {{const, K}, S1} -> - {colon, S2} = tokenize(B, S1), - {V, S3} = decode1(B, S2#decoder{state=null}), - decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc]) - end; -decode_object(B, S=#decoder{state=comma}, Acc) -> - case tokenize(B, S) of - {end_object, S1} -> - V = make_object({struct, lists:reverse(Acc)}, S1), - {V, S1#decoder{state=null}}; - {comma, S1} -> - decode_object(B, S1#decoder{state=key}, Acc) - end. - -decode_array(B, S) -> - decode_array(B, S#decoder{state=any}, []). - -decode_array(B, S=#decoder{state=any}, Acc) -> - case tokenize(B, S) of - {end_array, S1} -> - {lists:reverse(Acc), S1#decoder{state=null}}; - {start_array, S1} -> - {Array, S2} = decode_array(B, S1), - decode_array(B, S2#decoder{state=comma}, [Array | Acc]); - {start_object, S1} -> - {Array, S2} = decode_object(B, S1), - decode_array(B, S2#decoder{state=comma}, [Array | Acc]); - {{const, Const}, S1} -> - decode_array(B, S1#decoder{state=comma}, [Const | Acc]) - end; -decode_array(B, S=#decoder{state=comma}, Acc) -> - case tokenize(B, S) of - {end_array, S1} -> - {lists:reverse(Acc), S1#decoder{state=null}}; - {comma, S1} -> - decode_array(B, S1#decoder{state=any}, Acc) - end. - -tokenize_string(B, S=#decoder{offset=O}) -> - case tokenize_string_fast(B, O) of - {escape, O1} -> - Length = O1 - O, - S1 = ?ADV_COL(S, Length), - <<_:O/binary, Head:Length/binary, _/binary>> = B, - tokenize_string(B, S1, lists:reverse(binary_to_list(Head))); - O1 -> - Length = O1 - O, - <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B, - {{const, String}, ?ADV_COL(S, Length + 1)} - end. - -tokenize_string_fast(B, O) -> - case B of - <<_:O/binary, ?Q, _/binary>> -> - O; - <<_:O/binary, $\\, _/binary>> -> - {escape, O}; - <<_:O/binary, C1, _/binary>> when C1 < 128 -> - tokenize_string_fast(B, 1 + O); - <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, - C2 >= 128, C2 =< 191 -> - tokenize_string_fast(B, 2 + O); - <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191 -> - tokenize_string_fast(B, 3 + O); - <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191, - C4 >= 128, C4 =< 191 -> - tokenize_string_fast(B, 4 + O); - _ -> - throw(invalid_utf8) - end. - -tokenize_string(B, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, ?Q, _/binary>> -> - {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)}; - <<_:O/binary, "\\\"", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]); - <<_:O/binary, "\\\\", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]); - <<_:O/binary, "\\/", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]); - <<_:O/binary, "\\b", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]); - <<_:O/binary, "\\f", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]); - <<_:O/binary, "\\n", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]); - <<_:O/binary, "\\r", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]); - <<_:O/binary, "\\t", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]); - <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> -> - C = erlang:list_to_integer([C3, C2, C1, C0], 16), - if C > 16#D7FF, C < 16#DC00 -> - %% coalesce UTF-16 surrogate pair - <<"\\u", D3, D2, D1, D0, _/binary>> = Rest, - D = erlang:list_to_integer([D3,D2,D1,D0], 16), - [CodePoint] = xmerl_ucs:from_utf16be(<<C:16/big-unsigned-integer, - D:16/big-unsigned-integer>>), - Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc), - tokenize_string(B, ?ADV_COL(S, 12), Acc1); - true -> - Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), - tokenize_string(B, ?ADV_COL(S, 6), Acc1) - end; - <<_:O/binary, C1, _/binary>> when C1 < 128 -> - tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]); - <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, - C2 >= 128, C2 =< 191 -> - tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]); - <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191 -> - tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]); - <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191, - C4 >= 128, C4 =< 191 -> - tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]); - _ -> - throw(invalid_utf8) - end. - -tokenize_number(B, S) -> - case tokenize_number(B, sign, S, []) of - {{int, Int}, S1} -> - {{const, list_to_integer(Int)}, S1}; - {{float, Float}, S1} -> - {{const, list_to_float(Float)}, S1} - end. - -tokenize_number(B, sign, S=#decoder{offset=O}, []) -> - case B of - <<_:O/binary, $-, _/binary>> -> - tokenize_number(B, int, ?INC_COL(S), [$-]); - _ -> - tokenize_number(B, int, S, []) - end; -tokenize_number(B, int, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, $0, _/binary>> -> - tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]); - <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 -> - tokenize_number(B, int1, ?INC_COL(S), [C | Acc]) - end; -tokenize_number(B, int1, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, int1, ?INC_COL(S), [C | Acc]); - _ -> - tokenize_number(B, frac, S, Acc) - end; -tokenize_number(B, frac, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 -> - tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); - <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> - tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]); - _ -> - {{int, lists:reverse(Acc)}, S} - end; -tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]); - <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> - tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]); - _ -> - {{float, lists:reverse(Acc)}, S} - end; -tokenize_number(B, esign, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ -> - tokenize_number(B, eint, ?INC_COL(S), [C | Acc]); - _ -> - tokenize_number(B, eint, S, Acc) - end; -tokenize_number(B, eint, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]) - end; -tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]); - _ -> - {{float, lists:reverse(Acc)}, S} - end. - -tokenize(B, S=#decoder{offset=O}) -> - case B of - <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> - tokenize(B, ?INC_CHAR(S, C)); - <<_:O/binary, "{", _/binary>> -> - {start_object, ?INC_COL(S)}; - <<_:O/binary, "}", _/binary>> -> - {end_object, ?INC_COL(S)}; - <<_:O/binary, "[", _/binary>> -> - {start_array, ?INC_COL(S)}; - <<_:O/binary, "]", _/binary>> -> - {end_array, ?INC_COL(S)}; - <<_:O/binary, ",", _/binary>> -> - {comma, ?INC_COL(S)}; - <<_:O/binary, ":", _/binary>> -> - {colon, ?INC_COL(S)}; - <<_:O/binary, "null", _/binary>> -> - {{const, null}, ?ADV_COL(S, 4)}; - <<_:O/binary, "true", _/binary>> -> - {{const, true}, ?ADV_COL(S, 4)}; - <<_:O/binary, "false", _/binary>> -> - {{const, false}, ?ADV_COL(S, 5)}; - <<_:O/binary, "\"", _/binary>> -> - tokenize_string(B, ?INC_COL(S)); - <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9) - orelse C =:= $- -> - tokenize_number(B, S); - <<_:O/binary>> -> - trim = S#decoder.state, - {eof, S} - end. -%% -%% Tests -%% --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - - -%% testing constructs borrowed from the Yaws JSON implementation. - -%% Create an object from a list of Key/Value pairs. - -obj_new() -> - {struct, []}. - -is_obj({struct, Props}) -> - F = fun ({K, _}) when is_binary(K) -> true end, - lists:all(F, Props). - -obj_from_list(Props) -> - Obj = {struct, Props}, - ?assert(is_obj(Obj)), - Obj. - -%% Test for equivalence of Erlang terms. -%% Due to arbitrary order of construction, equivalent objects might -%% compare unequal as erlang terms, so we need to carefully recurse -%% through aggregates (tuples and objects). - -equiv({struct, Props1}, {struct, Props2}) -> - equiv_object(Props1, Props2); -equiv(L1, L2) when is_list(L1), is_list(L2) -> - equiv_list(L1, L2); -equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; -equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2; -equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true. - -%% Object representation and traversal order is unknown. -%% Use the sledgehammer and sort property lists. - -equiv_object(Props1, Props2) -> - L1 = lists:keysort(1, Props1), - L2 = lists:keysort(1, Props2), - Pairs = lists:zip(L1, L2), - true = lists:all(fun({{K1, V1}, {K2, V2}}) -> - equiv(K1, K2) and equiv(V1, V2) - end, Pairs). - -%% Recursively compare tuple elements for equivalence. - -equiv_list([], []) -> - true; -equiv_list([V1 | L1], [V2 | L2]) -> - equiv(V1, V2) andalso equiv_list(L1, L2). - -decode_test() -> - [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), - <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]). - -e2j_vec_test() -> - test_one(e2j_test_vec(utf8), 1). - -test_one([], _N) -> - %% io:format("~p tests passed~n", [N-1]), - ok; -test_one([{E, J} | Rest], N) -> - %% io:format("[~p] ~p ~p~n", [N, E, J]), - true = equiv(E, decode(J)), - true = equiv(E, decode(encode(E))), - test_one(Rest, 1+N). - -e2j_test_vec(utf8) -> - [ - {1, "1"}, - {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes - {-1, "-1"}, - {-3.1416, "-3.14160"}, - {12.0e10, "1.20000e+11"}, - {1.234E+10, "1.23400e+10"}, - {-1.234E-10, "-1.23400e-10"}, - {10.0, "1.0e+01"}, - {123.456, "1.23456E+2"}, - {10.0, "1e1"}, - {<<"foo">>, "\"foo\""}, - {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""}, - {<<"">>, "\"\""}, - {<<"\n\n\n">>, "\"\\n\\n\\n\""}, - {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, - {obj_new(), "{}"}, - {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"}, - {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]), - "{\"foo\":\"bar\",\"baz\":123}"}, - {[], "[]"}, - {[[]], "[[]]"}, - {[1, <<"foo">>], "[1,\"foo\"]"}, - - %% json array in a json object - {obj_from_list([{<<"foo">>, [123]}]), - "{\"foo\":[123]}"}, - - %% json object in a json object - {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]), - "{\"foo\":{\"bar\":true}}"}, - - %% fold evaluation order - {obj_from_list([{<<"foo">>, []}, - {<<"bar">>, obj_from_list([{<<"baz">>, true}])}, - {<<"alice">>, <<"bob">>}]), - "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, - - %% json object in a json array - {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null], - "[-123,\"foo\",{\"bar\":[]},null]"} - ]. - -%% test utf8 encoding -encoder_utf8_test() -> - %% safe conversion case (default) - [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] = - encode(<<1,"\321\202\320\265\321\201\321\202">>), - - %% raw utf8 output (optional) - Enc = mochijson2:encoder([{utf8, true}]), - [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] = - Enc(<<1,"\321\202\320\265\321\201\321\202">>). - -input_validation_test() -> - Good = [ - {16#00A3, <<?Q, 16#C2, 16#A3, ?Q>>}, %% pound - {16#20AC, <<?Q, 16#E2, 16#82, 16#AC, ?Q>>}, %% euro - {16#10196, <<?Q, 16#F0, 16#90, 16#86, 16#96, ?Q>>} %% denarius - ], - lists:foreach(fun({CodePoint, UTF8}) -> - Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)), - Expect = decode(UTF8) - end, Good), - - Bad = [ - %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte - <<?Q, 16#80, ?Q>>, - %% missing continuations, last byte in each should be 80-BF - <<?Q, 16#C2, 16#7F, ?Q>>, - <<?Q, 16#E0, 16#80,16#7F, ?Q>>, - <<?Q, 16#F0, 16#80, 16#80, 16#7F, ?Q>>, - %% we don't support code points > 10FFFF per RFC 3629 - <<?Q, 16#F5, 16#80, 16#80, 16#80, ?Q>>, - %% escape characters trigger a different code path - <<?Q, $\\, $\n, 16#80, ?Q>> - ], - lists:foreach( - fun(X) -> - ok = try decode(X) catch invalid_utf8 -> ok end, - %% could be {ucs,{bad_utf8_character_code}} or - %% {json_encode,{bad_char,_}} - {'EXIT', _} = (catch encode(X)) - end, Bad). - -inline_json_test() -> - ?assertEqual(<<"\"iodata iodata\"">>, - iolist_to_binary( - encode({json, [<<"\"iodata">>, " iodata\""]}))), - ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]}, - decode( - encode({struct, - [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))), - ok. - -big_unicode_test() -> - UTF8Seq = list_to_binary(xmerl_ucs:to_utf8(16#0001d120)), - ?assertEqual( - <<"\"\\ud834\\udd20\"">>, - iolist_to_binary(encode(UTF8Seq))), - ?assertEqual( - UTF8Seq, - decode(iolist_to_binary(encode(UTF8Seq)))), - ok. - -custom_decoder_test() -> - ?assertEqual( - {struct, [{<<"key">>, <<"value">>}]}, - (decoder([]))("{\"key\": \"value\"}")), - F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end, - ?assertEqual( - win, - (decoder([{object_hook, F}]))("{\"key\": \"value\"}")), - ok. - -atom_test() -> - %% JSON native atoms - [begin - ?assertEqual(A, decode(atom_to_list(A))), - ?assertEqual(iolist_to_binary(atom_to_list(A)), - iolist_to_binary(encode(A))) - end || A <- [true, false, null]], - %% Atom to string - ?assertEqual( - <<"\"foo\"">>, - iolist_to_binary(encode(foo))), - ?assertEqual( - <<"\"\\ud834\\udd20\"">>, - iolist_to_binary(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))), - ok. - -key_encode_test() -> - %% Some forms are accepted as keys that would not be strings in other - %% cases - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode({struct, [{foo, 1}]}))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode({struct, [{"foo", 1}]}))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode([{foo, 1}]))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode([{<<"foo">>, 1}]))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode([{"foo", 1}]))), - ?assertEqual( - <<"{\"\\ud834\\udd20\":1}">>, - iolist_to_binary( - encode({struct, [{[16#0001d120], 1}]}))), - ?assertEqual( - <<"{\"1\":1}">>, - iolist_to_binary(encode({struct, [{1, 1}]}))), - ok. - -unsafe_chars_test() -> - Chars = "\"\\\b\f\n\r\t", - [begin - ?assertEqual(false, json_string_is_safe([C])), - ?assertEqual(false, json_bin_is_safe(<<C>>)), - ?assertEqual(<<C>>, decode(encode(<<C>>))) - end || C <- Chars], - ?assertEqual( - false, - json_string_is_safe([16#0001d120])), - ?assertEqual( - false, - json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8(16#0001d120)))), - ?assertEqual( - [16#0001d120], - xmerl_ucs:from_utf8( - binary_to_list( - decode(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))))), - ?assertEqual( - false, - json_string_is_safe([16#110000])), - ?assertEqual( - false, - json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8([16#110000])))), - %% solidus can be escaped but isn't unsafe by default - ?assertEqual( - <<"/">>, - decode(<<"\"\\/\"">>)), - ok. - -int_test() -> - ?assertEqual(0, decode("0")), - ?assertEqual(1, decode("1")), - ?assertEqual(11, decode("11")), - ok. - -large_int_test() -> - ?assertEqual(<<"-2147483649214748364921474836492147483649">>, - iolist_to_binary(encode(-2147483649214748364921474836492147483649))), - ?assertEqual(<<"2147483649214748364921474836492147483649">>, - iolist_to_binary(encode(2147483649214748364921474836492147483649))), - ok. - -float_test() -> - ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))), - ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))), - ok. - -handler_test() -> - ?assertEqual( - {'EXIT',{json_encode,{bad_term,{x,y}}}}, - catch encode({x,y})), - F = fun ({x,y}) -> [] end, - ?assertEqual( - <<"[]">>, - iolist_to_binary((encoder([{handler, F}]))({x, y}))), - ok. - -encode_empty_test_() -> - [{A, ?_assertEqual(<<"{}">>, iolist_to_binary(encode(B)))} - || {A, B} <- [{"eep18 {}", {}}, - {"eep18 {[]}", {[]}}, - {"{struct, []}", {struct, []}}]]. - -encode_test_() -> - P = [{<<"k">>, <<"v">>}], - JSON = iolist_to_binary(encode({struct, P})), - [{atom_to_list(F), - ?_assertEqual(JSON, iolist_to_binary(encode(decode(JSON, [{format, F}]))))} - || F <- [struct, eep18, proplist]]. - -format_test_() -> - P = [{<<"k">>, <<"v">>}], - JSON = iolist_to_binary(encode({struct, P})), - [{atom_to_list(F), - ?_assertEqual(A, decode(JSON, [{format, F}]))} - || {F, A} <- [{struct, {struct, P}}, - {eep18, {P}}, - {proplist, P}]]. - --endif. diff --git a/src/pmon.erl b/src/pmon.erl deleted file mode 100644 index f42530022a..0000000000 --- a/src/pmon.erl +++ /dev/null @@ -1,109 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(pmon). - -%% Process Monitor -%% ================ -%% -%% This module monitors processes so that every process has at most -%% 1 monitor. -%% Processes monitored can be dynamically added and removed. -%% -%% Unlike erlang:[de]monitor* functions, this module -%% provides basic querying capability and avoids contacting down nodes. -%% -%% It is used to monitor nodes, queue mirrors, and by -%% the queue collector, among other things. - --export([new/0, new/1, monitor/2, monitor_all/2, demonitor/2, - is_monitored/2, erase/2, monitored/1, is_empty/1]). - --compile({no_auto_import, [monitor/2]}). - --record(state, {dict, module}). - --ifdef(use_specs). - -%%---------------------------------------------------------------------------- - --export_type([?MODULE/0]). - --opaque(?MODULE() :: #state{dict :: dict:dict(), - module :: atom()}). - --type(item() :: pid() | {atom(), node()}). - --spec(new/0 :: () -> ?MODULE()). --spec(new/1 :: ('erlang' | 'delegate') -> ?MODULE()). --spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). --spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). --spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). --spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). --spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). --spec(monitored/1 :: (?MODULE()) -> [item()]). --spec(is_empty/1 :: (?MODULE()) -> boolean()). - --endif. - -new() -> new(erlang). - -new(Module) -> #state{dict = dict:new(), - module = Module}. - -monitor(Item, S = #state{dict = M, module = Module}) -> - case dict:is_key(Item, M) of - true -> S; - false -> case node_alive_shortcut(Item) of - true -> Ref = Module:monitor(process, Item), - S#state{dict = dict:store(Item, Ref, M)}; - false -> self() ! {'DOWN', fake_ref, process, Item, - nodedown}, - S - end - end. - -monitor_all([], S) -> S; %% optimisation -monitor_all([Item], S) -> monitor(Item, S); %% optimisation -monitor_all(Items, S) -> lists:foldl(fun monitor/2, S, Items). - -demonitor(Item, S = #state{dict = M, module = Module}) -> - case dict:find(Item, M) of - {ok, MRef} -> Module:demonitor(MRef), - S#state{dict = dict:erase(Item, M)}; - error -> M - end. - -is_monitored(Item, #state{dict = M}) -> dict:is_key(Item, M). - -erase(Item, S = #state{dict = M}) -> S#state{dict = dict:erase(Item, M)}. - -monitored(#state{dict = M}) -> dict:fetch_keys(M). - -is_empty(#state{dict = M}) -> dict:size(M) == 0. - -%%---------------------------------------------------------------------------- - -%% We check here to see if the node is alive in order to avoid trying -%% to connect to it if it isn't - this can cause substantial -%% slowdowns. We can't perform this shortcut if passed {Name, Node} -%% since we would need to convert that into a pid for the fake 'DOWN' -%% message, so we always return true here - but that's OK, it's just -%% an optimisation. -node_alive_shortcut(P) when is_pid(P) -> - lists:member(node(P), [node() | nodes()]); -node_alive_shortcut({_Name, _Node}) -> - true. diff --git a/src/priority_queue.erl b/src/priority_queue.erl deleted file mode 100644 index 88c69513d7..0000000000 --- a/src/priority_queue.erl +++ /dev/null @@ -1,227 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - -%% Priority queues have essentially the same interface as ordinary -%% queues, except that a) there is an in/3 that takes a priority, and -%% b) we have only implemented the core API we need. -%% -%% Priorities should be integers - the higher the value the higher the -%% priority - but we don't actually check that. -%% -%% in/2 inserts items with priority 0. -%% -%% We optimise the case where a priority queue is being used just like -%% an ordinary queue. When that is the case we represent the priority -%% queue as an ordinary queue. We could just call into the 'queue' -%% module for that, but for efficiency we implement the relevant -%% functions directly in here, thus saving on inter-module calls and -%% eliminating a level of boxing. -%% -%% When the queue contains items with non-zero priorities, it is -%% represented as a sorted kv list with the inverted Priority as the -%% key and an ordinary queue as the value. Here again we use our own -%% ordinary queue implemention for efficiency, often making recursive -%% calls into the same function knowing that ordinary queues represent -%% a base case. - - --module(priority_queue). - --export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1, - in/2, in/3, out/1, out_p/1, join/2, filter/2, fold/3, highest/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([q/0]). - --type(q() :: pqueue()). --type(priority() :: integer() | 'infinity'). --type(squeue() :: {queue, [any()], [any()], non_neg_integer()}). --type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}). - --spec(new/0 :: () -> pqueue()). --spec(is_queue/1 :: (any()) -> boolean()). --spec(is_empty/1 :: (pqueue()) -> boolean()). --spec(len/1 :: (pqueue()) -> non_neg_integer()). --spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]). --spec(from_list/1 :: ([{priority(), any()}]) -> pqueue()). --spec(in/2 :: (any(), pqueue()) -> pqueue()). --spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()). --spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}). --spec(out_p/1 :: (pqueue()) -> {empty | {value, any(), priority()}, pqueue()}). --spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). --spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). --spec(fold/3 :: - (fun ((any(), priority(), A) -> A), A, pqueue()) -> A). --spec(highest/1 :: (pqueue()) -> priority() | 'empty'). - --endif. - -%%---------------------------------------------------------------------------- - -new() -> - {queue, [], [], 0}. - -is_queue({queue, R, F, L}) when is_list(R), is_list(F), is_integer(L) -> - true; -is_queue({pqueue, Queues}) when is_list(Queues) -> - lists:all(fun ({infinity, Q}) -> is_queue(Q); - ({P, Q}) -> is_integer(P) andalso is_queue(Q) - end, Queues); -is_queue(_) -> - false. - -is_empty({queue, [], [], 0}) -> - true; -is_empty(_) -> - false. - -len({queue, _R, _F, L}) -> - L; -len({pqueue, Queues}) -> - lists:sum([len(Q) || {_, Q} <- Queues]). - -to_list({queue, In, Out, _Len}) when is_list(In), is_list(Out) -> - [{0, V} || V <- Out ++ lists:reverse(In, [])]; -to_list({pqueue, Queues}) -> - [{maybe_negate_priority(P), V} || {P, Q} <- Queues, - {0, V} <- to_list(Q)]. - -from_list(L) -> - lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L). - -in(Item, Q) -> - in(Item, 0, Q). - -in(X, 0, {queue, [_] = In, [], 1}) -> - {queue, [X], In, 2}; -in(X, 0, {queue, In, Out, Len}) when is_list(In), is_list(Out) -> - {queue, [X|In], Out, Len + 1}; -in(X, Priority, _Q = {queue, [], [], 0}) -> - in(X, Priority, {pqueue, []}); -in(X, Priority, Q = {queue, _, _, _}) -> - in(X, Priority, {pqueue, [{0, Q}]}); -in(X, Priority, {pqueue, Queues}) -> - P = maybe_negate_priority(Priority), - {pqueue, case lists:keysearch(P, 1, Queues) of - {value, {_, Q}} -> - lists:keyreplace(P, 1, Queues, {P, in(X, Q)}); - false when P == infinity -> - [{P, {queue, [X], [], 1}} | Queues]; - false -> - case Queues of - [{infinity, InfQueue} | Queues1] -> - [{infinity, InfQueue} | - lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues1])]; - _ -> - lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues]) - end - end}. - -out({queue, [], [], 0} = Q) -> - {empty, Q}; -out({queue, [V], [], 1}) -> - {{value, V}, {queue, [], [], 0}}; -out({queue, [Y|In], [], Len}) -> - [V|Out] = lists:reverse(In, []), - {{value, V}, {queue, [Y], Out, Len - 1}}; -out({queue, In, [V], Len}) when is_list(In) -> - {{value,V}, r2f(In, Len - 1)}; -out({queue, In,[V|Out], Len}) when is_list(In) -> - {{value, V}, {queue, In, Out, Len - 1}}; -out({pqueue, [{P, Q} | Queues]}) -> - {R, Q1} = out(Q), - NewQ = case is_empty(Q1) of - true -> case Queues of - [] -> {queue, [], [], 0}; - [{0, OnlyQ}] -> OnlyQ; - [_|_] -> {pqueue, Queues} - end; - false -> {pqueue, [{P, Q1} | Queues]} - end, - {R, NewQ}. - -out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0); -out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)). - -add_p(R, P) -> case R of - {empty, Q} -> {empty, Q}; - {{value, V}, Q} -> {{value, V, P}, Q} - end. - -join(A, {queue, [], [], 0}) -> - A; -join({queue, [], [], 0}, B) -> - B; -join({queue, AIn, AOut, ALen}, {queue, BIn, BOut, BLen}) -> - {queue, BIn, AOut ++ lists:reverse(AIn, BOut), ALen + BLen}; -join(A = {queue, _, _, _}, {pqueue, BPQ}) -> - {Pre, Post} = - lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, BPQ), - Post1 = case Post of - [] -> [ {0, A} ]; - [ {0, ZeroQueue} | Rest ] -> [ {0, join(A, ZeroQueue)} | Rest ]; - _ -> [ {0, A} | Post ] - end, - {pqueue, Pre ++ Post1}; -join({pqueue, APQ}, B = {queue, _, _, _}) -> - {Pre, Post} = - lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, APQ), - Post1 = case Post of - [] -> [ {0, B} ]; - [ {0, ZeroQueue} | Rest ] -> [ {0, join(ZeroQueue, B)} | Rest ]; - _ -> [ {0, B} | Post ] - end, - {pqueue, Pre ++ Post1}; -join({pqueue, APQ}, {pqueue, BPQ}) -> - {pqueue, merge(APQ, BPQ, [])}. - -merge([], BPQ, Acc) -> - lists:reverse(Acc, BPQ); -merge(APQ, [], Acc) -> - lists:reverse(Acc, APQ); -merge([{P, A}|As], [{P, B}|Bs], Acc) -> - merge(As, Bs, [ {P, join(A, B)} | Acc ]); -merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity -> - merge(As, Bs, [ {PA, A} | Acc ]); -merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) -> - merge(As, Bs, [ {PB, B} | Acc ]). - -filter(Pred, Q) -> fold(fun(V, P, Acc) -> - case Pred(V) of - true -> in(V, P, Acc); - false -> Acc - end - end, new(), Q). - -fold(Fun, Init, Q) -> case out_p(Q) of - {empty, _Q} -> Init; - {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) - end. - -highest({queue, [], [], 0}) -> empty; -highest({queue, _, _, _}) -> 0; -highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). - -r2f([], 0) -> {queue, [], [], 0}; -r2f([_] = R, 1) -> {queue, [], R, 1}; -r2f([X,Y], 2) -> {queue, [X], [Y], 2}; -r2f([X,Y|R], L) -> {queue, [X,Y], lists:reverse(R, []), L}. - -maybe_negate_priority(infinity) -> infinity; -maybe_negate_priority(P) -> -P. diff --git a/ebin/rabbit_app.in b/src/rabbit.app.src index 92c9c2983d..91c486a3c4 100644 --- a/ebin/rabbit_app.in +++ b/src/rabbit.app.src @@ -1,7 +1,7 @@ {application, rabbit, %% -*- erlang -*- [{description, "RabbitMQ"}, {id, "RabbitMQ"}, - {vsn, "%%VSN%%"}, + {vsn, "0.0.0"}, {modules, []}, {registered, [rabbit_amqqueue_sup, rabbit_log, diff --git a/src/rabbit.erl b/src/rabbit.erl index 7c85685276..b3e6c95438 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -192,7 +192,7 @@ -include("rabbit_framing.hrl"). -include("rabbit.hrl"). --define(APPS, [os_mon, mnesia, rabbit]). +-define(APPS, [os_mon, mnesia, rabbit_common, rabbit]). %% HiPE compilation uses multiple cores anyway, but some bits are %% IO-bound so we can go faster if we parallelise a bit more. In diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl deleted file mode 100644 index 65e4255a73..0000000000 --- a/src/rabbit_amqqueue.erl +++ /dev/null @@ -1,908 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_amqqueue). - --export([recover/0, stop/0, start/1, declare/5, declare/6, - delete_immediately/1, delete/3, purge/1, forget_all_durable/1, - delete_crashed/1, delete_crashed_internal/1]). --export([pseudo_queue/2, immutable/1]). --export([lookup/1, not_found_or_absent/1, with/2, with/3, with_or_die/2, - assert_equivalence/5, - check_exclusive_access/2, with_exclusive_access_or_die/3, - stat/1, deliver/2, requeue/3, ack/3, reject/4]). --export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). --export([list_down/1]). --export([force_event_refresh/1, notify_policy_changed/1]). --export([consumers/1, consumers_all/1, consumer_info_keys/0]). --export([basic_get/4, basic_consume/10, basic_cancel/4, notify_decorators/1]). --export([notify_sent/2, notify_sent_queue_down/1, resume/2]). --export([notify_down_all/2, activate_limit_all/2, credit/5]). --export([on_node_up/1, on_node_down/1]). --export([update/2, store_queue/1, update_decorators/1, policy_changed/2]). --export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1, - cancel_sync_mirrors/1]). - -%% internal --export([internal_declare/2, internal_delete/1, run_backing_queue/3, - set_ram_duration_target/2, set_maximum_since_use/2]). - --include("rabbit.hrl"). --include_lib("stdlib/include/qlc.hrl"). - --define(INTEGER_ARG_TYPES, [byte, short, signedint, long]). - --define(MORE_CONSUMER_CREDIT_AFTER, 50). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([name/0, qmsg/0, absent_reason/0]). - --type(name() :: rabbit_types:r('queue')). --type(qpids() :: [pid()]). --type(qlen() :: rabbit_types:ok(non_neg_integer())). --type(qfun(A) :: fun ((rabbit_types:amqqueue()) -> A | no_return())). --type(qmsg() :: {name(), pid(), msg_id(), boolean(), rabbit_types:message()}). --type(msg_id() :: non_neg_integer()). --type(ok_or_errors() :: - 'ok' | {'error', [{'error' | 'exit' | 'throw', any()}]}). --type(absent_reason() :: 'nodedown' | 'crashed'). --type(queue_or_absent() :: rabbit_types:amqqueue() | - {'absent', rabbit_types:amqqueue(),absent_reason()}). --type(not_found_or_absent() :: - 'not_found' | {'absent', rabbit_types:amqqueue(), absent_reason()}). --spec(recover/0 :: () -> [rabbit_types:amqqueue()]). --spec(stop/0 :: () -> 'ok'). --spec(start/1 :: ([rabbit_types:amqqueue()]) -> 'ok'). --spec(declare/5 :: - (name(), boolean(), boolean(), - rabbit_framing:amqp_table(), rabbit_types:maybe(pid())) - -> {'new' | 'existing' | 'absent' | 'owner_died', - rabbit_types:amqqueue()} | rabbit_types:channel_exit()). --spec(declare/6 :: - (name(), boolean(), boolean(), - rabbit_framing:amqp_table(), rabbit_types:maybe(pid()), node()) - -> {'new' | 'existing' | 'owner_died', rabbit_types:amqqueue()} | - {'absent', rabbit_types:amqqueue(), absent_reason()} | - rabbit_types:channel_exit()). --spec(internal_declare/2 :: - (rabbit_types:amqqueue(), boolean()) - -> queue_or_absent() | rabbit_misc:thunk(queue_or_absent())). --spec(update/2 :: - (name(), - fun((rabbit_types:amqqueue()) -> rabbit_types:amqqueue())) - -> 'not_found' | rabbit_types:amqqueue()). --spec(lookup/1 :: - (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | - rabbit_types:error('not_found'); - ([name()]) -> [rabbit_types:amqqueue()]). --spec(not_found_or_absent/1 :: (name()) -> not_found_or_absent()). --spec(with/2 :: (name(), qfun(A)) -> - A | rabbit_types:error(not_found_or_absent())). --spec(with/3 :: (name(), qfun(A), fun((not_found_or_absent()) -> B)) -> A | B). --spec(with_or_die/2 :: - (name(), qfun(A)) -> A | rabbit_types:channel_exit()). --spec(assert_equivalence/5 :: - (rabbit_types:amqqueue(), boolean(), boolean(), - rabbit_framing:amqp_table(), rabbit_types:maybe(pid())) - -> 'ok' | rabbit_types:channel_exit() | - rabbit_types:connection_exit()). --spec(check_exclusive_access/2 :: - (rabbit_types:amqqueue(), pid()) - -> 'ok' | rabbit_types:channel_exit()). --spec(with_exclusive_access_or_die/3 :: - (name(), pid(), qfun(A)) -> A | rabbit_types:channel_exit()). --spec(list/0 :: () -> [rabbit_types:amqqueue()]). --spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:amqqueue()]). --spec(list_down/1 :: (rabbit_types:vhost()) -> [rabbit_types:amqqueue()]). --spec(info_keys/0 :: () -> rabbit_types:info_keys()). --spec(info/1 :: (rabbit_types:amqqueue()) -> rabbit_types:infos()). --spec(info/2 :: - (rabbit_types:amqqueue(), rabbit_types:info_keys()) - -> rabbit_types:infos()). --spec(info_all/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). --spec(info_all/2 :: (rabbit_types:vhost(), rabbit_types:info_keys()) - -> [rabbit_types:infos()]). --spec(force_event_refresh/1 :: (reference()) -> 'ok'). --spec(notify_policy_changed/1 :: (rabbit_types:amqqueue()) -> 'ok'). --spec(consumers/1 :: (rabbit_types:amqqueue()) - -> [{pid(), rabbit_types:ctag(), boolean(), - non_neg_integer(), rabbit_framing:amqp_table()}]). --spec(consumer_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(consumers_all/1 :: - (rabbit_types:vhost()) - -> [{name(), pid(), rabbit_types:ctag(), boolean(), - non_neg_integer(), rabbit_framing:amqp_table()}]). --spec(stat/1 :: - (rabbit_types:amqqueue()) - -> {'ok', non_neg_integer(), non_neg_integer()}). --spec(delete_immediately/1 :: (qpids()) -> 'ok'). --spec(delete/3 :: - (rabbit_types:amqqueue(), 'false', 'false') - -> qlen(); - (rabbit_types:amqqueue(), 'true' , 'false') - -> qlen() | rabbit_types:error('in_use'); - (rabbit_types:amqqueue(), 'false', 'true' ) - -> qlen() | rabbit_types:error('not_empty'); - (rabbit_types:amqqueue(), 'true' , 'true' ) - -> qlen() | - rabbit_types:error('in_use') | - rabbit_types:error('not_empty')). --spec(delete_crashed/1 :: (rabbit_types:amqqueue()) -> 'ok'). --spec(delete_crashed_internal/1 :: (rabbit_types:amqqueue()) -> 'ok'). --spec(purge/1 :: (rabbit_types:amqqueue()) -> qlen()). --spec(forget_all_durable/1 :: (node()) -> 'ok'). --spec(deliver/2 :: ([rabbit_types:amqqueue()], rabbit_types:delivery()) -> - qpids()). --spec(requeue/3 :: (pid(), [msg_id()], pid()) -> 'ok'). --spec(ack/3 :: (pid(), [msg_id()], pid()) -> 'ok'). --spec(reject/4 :: (pid(), [msg_id()], boolean(), pid()) -> 'ok'). --spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). --spec(activate_limit_all/2 :: (qpids(), pid()) -> ok_or_errors()). --spec(basic_get/4 :: (rabbit_types:amqqueue(), pid(), boolean(), pid()) -> - {'ok', non_neg_integer(), qmsg()} | 'empty'). --spec(credit/5 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), - non_neg_integer(), boolean()) -> 'ok'). --spec(basic_consume/10 :: - (rabbit_types:amqqueue(), boolean(), pid(), pid(), boolean(), - non_neg_integer(), rabbit_types:ctag(), boolean(), - rabbit_framing:amqp_table(), any()) - -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). --spec(basic_cancel/4 :: - (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). --spec(notify_decorators/1 :: (rabbit_types:amqqueue()) -> 'ok'). --spec(notify_sent/2 :: (pid(), pid()) -> 'ok'). --spec(notify_sent_queue_down/1 :: (pid()) -> 'ok'). --spec(resume/2 :: (pid(), pid()) -> 'ok'). --spec(internal_delete/1 :: - (name()) -> rabbit_types:ok_or_error('not_found') | - rabbit_types:connection_exit() | - fun (() -> rabbit_types:ok_or_error('not_found') | - rabbit_types:connection_exit())). --spec(run_backing_queue/3 :: - (pid(), atom(), - (fun ((atom(), A) -> {[rabbit_types:msg_id()], A}))) -> 'ok'). --spec(set_ram_duration_target/2 :: (pid(), number() | 'infinity') -> 'ok'). --spec(set_maximum_since_use/2 :: (pid(), non_neg_integer()) -> 'ok'). --spec(on_node_up/1 :: (node()) -> 'ok'). --spec(on_node_down/1 :: (node()) -> 'ok'). --spec(pseudo_queue/2 :: (name(), pid()) -> rabbit_types:amqqueue()). --spec(immutable/1 :: (rabbit_types:amqqueue()) -> rabbit_types:amqqueue()). --spec(store_queue/1 :: (rabbit_types:amqqueue()) -> 'ok'). --spec(update_decorators/1 :: (name()) -> 'ok'). --spec(policy_changed/2 :: - (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). --spec(start_mirroring/1 :: (pid()) -> 'ok'). --spec(stop_mirroring/1 :: (pid()) -> 'ok'). --spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('not_mirrored')). --spec(cancel_sync_mirrors/1 :: (pid()) -> 'ok' | {'ok', 'not_syncing'}). - --endif. - -%%---------------------------------------------------------------------------- - --define(CONSUMER_INFO_KEYS, - [queue_name, channel_pid, consumer_tag, ack_required, prefetch_count, - arguments]). - -recover() -> - %% Clear out remnants of old incarnation, in case we restarted - %% faster than other nodes handled DOWN messages from us. - on_node_down(node()), - DurableQueues = find_durable_queues(), - {ok, BQ} = application:get_env(rabbit, backing_queue_module), - - %% We rely on BQ:start/1 returning the recovery terms in the same - %% order as the supplied queue names, so that we can zip them together - %% for further processing in recover_durable_queues. - {ok, OrderedRecoveryTerms} = - BQ:start([QName || #amqqueue{name = QName} <- DurableQueues]), - {ok,_} = supervisor:start_child( - rabbit_sup, - {rabbit_amqqueue_sup_sup, - {rabbit_amqqueue_sup_sup, start_link, []}, - transient, infinity, supervisor, [rabbit_amqqueue_sup_sup]}), - recover_durable_queues(lists:zip(DurableQueues, OrderedRecoveryTerms)). - -stop() -> - ok = supervisor:terminate_child(rabbit_sup, rabbit_amqqueue_sup_sup), - ok = supervisor:delete_child(rabbit_sup, rabbit_amqqueue_sup_sup), - {ok, BQ} = application:get_env(rabbit, backing_queue_module), - ok = BQ:stop(). - -start(Qs) -> - %% At this point all recovered queues and their bindings are - %% visible to routing, so now it is safe for them to complete - %% their initialisation (which may involve interacting with other - %% queues). - [Pid ! {self(), go} || #amqqueue{pid = Pid} <- Qs], - ok. - -find_durable_queues() -> - Node = node(), - mnesia:async_dirty( - fun () -> - qlc:e(qlc:q([Q || Q = #amqqueue{name = Name, - pid = Pid} - <- mnesia:table(rabbit_durable_queue), - node(Pid) == Node, - mnesia:read(rabbit_queue, Name, read) =:= []])) - end). - -recover_durable_queues(QueuesAndRecoveryTerms) -> - {Results, Failures} = - gen_server2:mcall( - [{rabbit_amqqueue_sup_sup:start_queue_process(node(), Q, recovery), - {init, {self(), Terms}}} || {Q, Terms} <- QueuesAndRecoveryTerms]), - [rabbit_log:error("Queue ~p failed to initialise: ~p~n", - [Pid, Error]) || {Pid, Error} <- Failures], - [Q || {_, {new, Q}} <- Results]. - -declare(QueueName, Durable, AutoDelete, Args, Owner) -> - declare(QueueName, Durable, AutoDelete, Args, Owner, node()). - - -%% The Node argument suggests where the queue (master if mirrored) -%% should be. Note that in some cases (e.g. with "nodes" policy in -%% effect) this might not be possible to satisfy. -declare(QueueName, Durable, AutoDelete, Args, Owner, Node) -> - ok = check_declare_arguments(QueueName, Args), - Q = rabbit_queue_decorator:set( - rabbit_policy:set(#amqqueue{name = QueueName, - durable = Durable, - auto_delete = AutoDelete, - arguments = Args, - exclusive_owner = Owner, - pid = none, - slave_pids = [], - sync_slave_pids = [], - recoverable_slaves = [], - gm_pids = [], - state = live})), - - Node1 = case rabbit_queue_master_location_misc:get_location(Q) of - {ok, Node0} -> Node0; - {error, _} -> Node - end, - - Node1 = rabbit_mirror_queue_misc:initial_queue_node(Q, Node1), - gen_server2:call( - rabbit_amqqueue_sup_sup:start_queue_process(Node1, Q, declare), - {init, new}, infinity). - -internal_declare(Q, true) -> - rabbit_misc:execute_mnesia_tx_with_tail( - fun () -> - ok = store_queue(Q#amqqueue{state = live}), - rabbit_misc:const(Q) - end); -internal_declare(Q = #amqqueue{name = QueueName}, false) -> - rabbit_misc:execute_mnesia_tx_with_tail( - fun () -> - case mnesia:wread({rabbit_queue, QueueName}) of - [] -> - case not_found_or_absent(QueueName) of - not_found -> Q1 = rabbit_policy:set(Q), - Q2 = Q1#amqqueue{state = live}, - ok = store_queue(Q2), - B = add_default_binding(Q1), - fun () -> B(), Q1 end; - {absent, _Q, _} = R -> rabbit_misc:const(R) - end; - [ExistingQ] -> - rabbit_misc:const(ExistingQ) - end - end). - -update(Name, Fun) -> - case mnesia:wread({rabbit_queue, Name}) of - [Q = #amqqueue{durable = Durable}] -> - Q1 = Fun(Q), - ok = mnesia:write(rabbit_queue, Q1, write), - case Durable of - true -> ok = mnesia:write(rabbit_durable_queue, Q1, write); - _ -> ok - end, - Q1; - [] -> - not_found - end. - -store_queue(Q = #amqqueue{durable = true}) -> - ok = mnesia:write(rabbit_durable_queue, - Q#amqqueue{slave_pids = [], - sync_slave_pids = [], - gm_pids = [], - decorators = undefined}, write), - store_queue_ram(Q); -store_queue(Q = #amqqueue{durable = false}) -> - store_queue_ram(Q). - -store_queue_ram(Q) -> - ok = mnesia:write(rabbit_queue, rabbit_queue_decorator:set(Q), write). - -update_decorators(Name) -> - rabbit_misc:execute_mnesia_transaction( - fun() -> - case mnesia:wread({rabbit_queue, Name}) of - [Q] -> store_queue_ram(Q), - ok; - [] -> ok - end - end). - -policy_changed(Q1 = #amqqueue{decorators = Decorators1}, - Q2 = #amqqueue{decorators = Decorators2}) -> - rabbit_mirror_queue_misc:update_mirrors(Q1, Q2), - D1 = rabbit_queue_decorator:select(Decorators1), - D2 = rabbit_queue_decorator:select(Decorators2), - [ok = M:policy_changed(Q1, Q2) || M <- lists:usort(D1 ++ D2)], - %% Make sure we emit a stats event even if nothing - %% mirroring-related has changed - the policy may have changed anyway. - notify_policy_changed(Q1). - -add_default_binding(#amqqueue{name = QueueName}) -> - ExchangeName = rabbit_misc:r(QueueName, exchange, <<>>), - RoutingKey = QueueName#resource.name, - rabbit_binding:add(#binding{source = ExchangeName, - destination = QueueName, - key = RoutingKey, - args = []}). - -lookup([]) -> []; %% optimisation -lookup([Name]) -> ets:lookup(rabbit_queue, Name); %% optimisation -lookup(Names) when is_list(Names) -> - %% Normally we'd call mnesia:dirty_read/1 here, but that is quite - %% expensive for reasons explained in rabbit_misc:dirty_read/1. - lists:append([ets:lookup(rabbit_queue, Name) || Name <- Names]); -lookup(Name) -> - rabbit_misc:dirty_read({rabbit_queue, Name}). - -not_found_or_absent(Name) -> - %% NB: we assume that the caller has already performed a lookup on - %% rabbit_queue and not found anything - case mnesia:read({rabbit_durable_queue, Name}) of - [] -> not_found; - [Q] -> {absent, Q, nodedown} %% Q exists on stopped node - end. - -not_found_or_absent_dirty(Name) -> - %% We should read from both tables inside a tx, to get a - %% consistent view. But the chances of an inconsistency are small, - %% and only affect the error kind. - case rabbit_misc:dirty_read({rabbit_durable_queue, Name}) of - {error, not_found} -> not_found; - {ok, Q} -> {absent, Q, nodedown} - end. - -with(Name, F, E) -> - case lookup(Name) of - {ok, Q = #amqqueue{state = crashed}} -> - E({absent, Q, crashed}); - {ok, Q = #amqqueue{pid = QPid}} -> - %% We check is_process_alive(QPid) in case we receive a - %% nodedown (for example) in F() that has nothing to do - %% with the QPid. F() should be written s.t. that this - %% cannot happen, so we bail if it does since that - %% indicates a code bug and we don't want to get stuck in - %% the retry loop. - rabbit_misc:with_exit_handler( - fun () -> false = rabbit_mnesia:is_process_alive(QPid), - timer:sleep(25), - with(Name, F, E) - end, fun () -> F(Q) end); - {error, not_found} -> - E(not_found_or_absent_dirty(Name)) - end. - -with(Name, F) -> with(Name, F, fun (E) -> {error, E} end). - -with_or_die(Name, F) -> - with(Name, F, fun (not_found) -> rabbit_misc:not_found(Name); - ({absent, Q, Reason}) -> rabbit_misc:absent(Q, Reason) - end). - -assert_equivalence(#amqqueue{name = QName, - durable = Durable, - auto_delete = AD} = Q, - Durable1, AD1, Args1, Owner) -> - rabbit_misc:assert_field_equivalence(Durable, Durable1, QName, durable), - rabbit_misc:assert_field_equivalence(AD, AD1, QName, auto_delete), - assert_args_equivalence(Q, Args1), - check_exclusive_access(Q, Owner, strict). - -check_exclusive_access(Q, Owner) -> check_exclusive_access(Q, Owner, lax). - -check_exclusive_access(#amqqueue{exclusive_owner = Owner}, Owner, _MatchType) -> - ok; -check_exclusive_access(#amqqueue{exclusive_owner = none}, _ReaderPid, lax) -> - ok; -check_exclusive_access(#amqqueue{name = QueueName}, _ReaderPid, _MatchType) -> - rabbit_misc:protocol_error( - resource_locked, - "cannot obtain exclusive access to locked ~s", - [rabbit_misc:rs(QueueName)]). - -with_exclusive_access_or_die(Name, ReaderPid, F) -> - with_or_die(Name, - fun (Q) -> check_exclusive_access(Q, ReaderPid), F(Q) end). - -assert_args_equivalence(#amqqueue{name = QueueName, arguments = Args}, - RequiredArgs) -> - rabbit_misc:assert_args_equivalence(Args, RequiredArgs, QueueName, - [Key || {Key, _Fun} <- declare_args()]). - -check_declare_arguments(QueueName, Args) -> - check_arguments(QueueName, Args, declare_args()). - -check_consume_arguments(QueueName, Args) -> - check_arguments(QueueName, Args, consume_args()). - -check_arguments(QueueName, Args, Validators) -> - [case rabbit_misc:table_lookup(Args, Key) of - undefined -> ok; - TypeVal -> case Fun(TypeVal, Args) of - ok -> ok; - {error, Error} -> rabbit_misc:protocol_error( - precondition_failed, - "invalid arg '~s' for ~s: ~255p", - [Key, rabbit_misc:rs(QueueName), - Error]) - end - end || {Key, Fun} <- Validators], - ok. - -declare_args() -> - [{<<"x-expires">>, fun check_expires_arg/2}, - {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, - {<<"x-dead-letter-exchange">>, fun check_dlxname_arg/2}, - {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}, - {<<"x-max-length">>, fun check_non_neg_int_arg/2}, - {<<"x-max-length-bytes">>, fun check_non_neg_int_arg/2}, - {<<"x-max-priority">>, fun check_non_neg_int_arg/2}]. - -consume_args() -> [{<<"x-priority">>, fun check_int_arg/2}, - {<<"x-cancel-on-ha-failover">>, fun check_bool_arg/2}]. - -check_int_arg({Type, _}, _) -> - case lists:member(Type, ?INTEGER_ARG_TYPES) of - true -> ok; - false -> {error, {unacceptable_type, Type}} - end. - -check_bool_arg({bool, _}, _) -> ok; -check_bool_arg({Type, _}, _) -> {error, {unacceptable_type, Type}}. - -check_non_neg_int_arg({Type, Val}, Args) -> - case check_int_arg({Type, Val}, Args) of - ok when Val >= 0 -> ok; - ok -> {error, {value_negative, Val}}; - Error -> Error - end. - -check_expires_arg({Type, Val}, Args) -> - case check_int_arg({Type, Val}, Args) of - ok when Val == 0 -> {error, {value_zero, Val}}; - ok -> rabbit_misc:check_expiry(Val); - Error -> Error - end. - -check_message_ttl_arg({Type, Val}, Args) -> - case check_int_arg({Type, Val}, Args) of - ok -> rabbit_misc:check_expiry(Val); - Error -> Error - end. - -%% Note that the validity of x-dead-letter-exchange is already verified -%% by rabbit_channel's queue.declare handler. -check_dlxname_arg({longstr, _}, _) -> ok; -check_dlxname_arg({Type, _}, _) -> {error, {unacceptable_type, Type}}. - -check_dlxrk_arg({longstr, _}, Args) -> - case rabbit_misc:table_lookup(Args, <<"x-dead-letter-exchange">>) of - undefined -> {error, routing_key_but_no_dlx_defined}; - _ -> ok - end; -check_dlxrk_arg({Type, _}, _Args) -> - {error, {unacceptable_type, Type}}. - -list() -> mnesia:dirty_match_object(rabbit_queue, #amqqueue{_ = '_'}). - -list(VHostPath) -> list(VHostPath, rabbit_queue). - -%% Not dirty_match_object since that would not be transactional when used in a -%% tx context -list(VHostPath, TableName) -> - mnesia:async_dirty( - fun () -> - mnesia:match_object( - TableName, - #amqqueue{name = rabbit_misc:r(VHostPath, queue), _ = '_'}, - read) - end). - -list_down(VHostPath) -> - Present = list(VHostPath), - Durable = list(VHostPath, rabbit_durable_queue), - PresentS = sets:from_list([N || #amqqueue{name = N} <- Present]), - sets:to_list(sets:filter(fun (#amqqueue{name = N}) -> - not sets:is_element(N, PresentS) - end, sets:from_list(Durable))). - -info_keys() -> rabbit_amqqueue_process:info_keys(). - -map(Qs, F) -> rabbit_misc:filter_exit_map(F, Qs). - -info(Q = #amqqueue{ state = crashed }) -> info_down(Q, crashed); -info(#amqqueue{ pid = QPid }) -> delegate:call(QPid, info). - -info(Q = #amqqueue{ state = crashed }, Items) -> - info_down(Q, Items, crashed); -info(#amqqueue{ pid = QPid }, Items) -> - case delegate:call(QPid, {info, Items}) of - {ok, Res} -> Res; - {error, Error} -> throw(Error) - end. - -info_down(Q, DownReason) -> - info_down(Q, rabbit_amqqueue_process:info_keys(), DownReason). - -info_down(Q, Items, DownReason) -> - [{Item, i_down(Item, Q, DownReason)} || Item <- Items]. - -i_down(name, #amqqueue{name = Name}, _) -> Name; -i_down(durable, #amqqueue{durable = Dur}, _) -> Dur; -i_down(auto_delete, #amqqueue{auto_delete = AD}, _) -> AD; -i_down(arguments, #amqqueue{arguments = Args}, _) -> Args; -i_down(pid, #amqqueue{pid = QPid}, _) -> QPid; -i_down(recoverable_slaves, #amqqueue{recoverable_slaves = RS}, _) -> RS; -i_down(state, _Q, DownReason) -> DownReason; -i_down(K, _Q, _DownReason) -> - case lists:member(K, rabbit_amqqueue_process:info_keys()) of - true -> ''; - false -> throw({bad_argument, K}) - end. - -info_all(VHostPath) -> - map(list(VHostPath), fun (Q) -> info(Q) end) ++ - map(list_down(VHostPath), fun (Q) -> info_down(Q, down) end). - -info_all(VHostPath, Items) -> - map(list(VHostPath), fun (Q) -> info(Q, Items) end) ++ - map(list_down(VHostPath), fun (Q) -> info_down(Q, Items, down) end). - -force_event_refresh(Ref) -> - [gen_server2:cast(Q#amqqueue.pid, - {force_event_refresh, Ref}) || Q <- list()], - ok. - -notify_policy_changed(#amqqueue{pid = QPid}) -> - gen_server2:cast(QPid, policy_changed). - -consumers(#amqqueue{ pid = QPid }) -> delegate:call(QPid, consumers). - -consumer_info_keys() -> ?CONSUMER_INFO_KEYS. - -consumers_all(VHostPath) -> - ConsumerInfoKeys=consumer_info_keys(), - lists:append( - map(list(VHostPath), - fun (Q) -> - [lists:zip( - ConsumerInfoKeys, - [Q#amqqueue.name, ChPid, CTag, AckRequired, Prefetch, Args]) || - {ChPid, CTag, AckRequired, Prefetch, Args} <- consumers(Q)] - end)). - -stat(#amqqueue{pid = QPid}) -> delegate:call(QPid, stat). - -delete_immediately(QPids) -> - [gen_server2:cast(QPid, delete_immediately) || QPid <- QPids], - ok. - -delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> - delegate:call(QPid, {delete, IfUnused, IfEmpty}). - -delete_crashed(#amqqueue{ pid = QPid } = Q) -> - ok = rpc:call(node(QPid), ?MODULE, delete_crashed_internal, [Q]). - -delete_crashed_internal(Q = #amqqueue{ name = QName }) -> - {ok, BQ} = application:get_env(rabbit, backing_queue_module), - BQ:delete_crashed(Q), - ok = internal_delete(QName). - -purge(#amqqueue{ pid = QPid }) -> delegate:call(QPid, purge). - -requeue(QPid, MsgIds, ChPid) -> delegate:call(QPid, {requeue, MsgIds, ChPid}). - -ack(QPid, MsgIds, ChPid) -> delegate:cast(QPid, {ack, MsgIds, ChPid}). - -reject(QPid, Requeue, MsgIds, ChPid) -> - delegate:cast(QPid, {reject, Requeue, MsgIds, ChPid}). - -notify_down_all(QPids, ChPid) -> - {_, Bads} = delegate:call(QPids, {notify_down, ChPid}), - case lists:filter( - fun ({_Pid, {exit, {R, _}, _}}) -> rabbit_misc:is_abnormal_exit(R); - ({_Pid, _}) -> false - end, Bads) of - [] -> ok; - Bads1 -> {error, Bads1} - end. - -activate_limit_all(QPids, ChPid) -> - delegate:cast(QPids, {activate_limit, ChPid}). - -credit(#amqqueue{pid = QPid}, ChPid, CTag, Credit, Drain) -> - delegate:cast(QPid, {credit, ChPid, CTag, Credit, Drain}). - -basic_get(#amqqueue{pid = QPid}, ChPid, NoAck, LimiterPid) -> - delegate:call(QPid, {basic_get, ChPid, NoAck, LimiterPid}). - -basic_consume(#amqqueue{pid = QPid, name = QName}, NoAck, ChPid, LimiterPid, - LimiterActive, ConsumerPrefetchCount, ConsumerTag, - ExclusiveConsume, Args, OkMsg) -> - ok = check_consume_arguments(QName, Args), - delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerPrefetchCount, ConsumerTag, ExclusiveConsume, - Args, OkMsg}). - -basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> - delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). - -notify_decorators(#amqqueue{pid = QPid}) -> - delegate:cast(QPid, notify_decorators). - -notify_sent(QPid, ChPid) -> - Key = {consumer_credit_to, QPid}, - put(Key, case get(Key) of - 1 -> gen_server2:cast( - QPid, {notify_sent, ChPid, - ?MORE_CONSUMER_CREDIT_AFTER}), - ?MORE_CONSUMER_CREDIT_AFTER; - undefined -> erlang:monitor(process, QPid), - ?MORE_CONSUMER_CREDIT_AFTER - 1; - C -> C - 1 - end), - ok. - -notify_sent_queue_down(QPid) -> - erase({consumer_credit_to, QPid}), - ok. - -resume(QPid, ChPid) -> delegate:cast(QPid, {resume, ChPid}). - -internal_delete1(QueueName, OnlyDurable) -> - ok = mnesia:delete({rabbit_queue, QueueName}), - %% this 'guarded' delete prevents unnecessary writes to the mnesia - %% disk log - case mnesia:wread({rabbit_durable_queue, QueueName}) of - [] -> ok; - [_] -> ok = mnesia:delete({rabbit_durable_queue, QueueName}) - end, - %% we want to execute some things, as decided by rabbit_exchange, - %% after the transaction. - rabbit_binding:remove_for_destination(QueueName, OnlyDurable). - -internal_delete(QueueName) -> - rabbit_misc:execute_mnesia_tx_with_tail( - fun () -> - case {mnesia:wread({rabbit_queue, QueueName}), - mnesia:wread({rabbit_durable_queue, QueueName})} of - {[], []} -> - rabbit_misc:const({error, not_found}); - _ -> - Deletions = internal_delete1(QueueName, false), - T = rabbit_binding:process_deletions(Deletions), - fun() -> - ok = T(), - ok = rabbit_event:notify(queue_deleted, - [{name, QueueName}]) - end - end - end). - -forget_all_durable(Node) -> - %% Note rabbit is not running so we avoid e.g. the worker pool. Also why - %% we don't invoke the return from rabbit_binding:process_deletions/1. - {atomic, ok} = - mnesia:sync_transaction( - fun () -> - Qs = mnesia:match_object(rabbit_durable_queue, - #amqqueue{_ = '_'}, write), - [forget_node_for_queue(Node, Q) || - #amqqueue{pid = Pid} = Q <- Qs, - node(Pid) =:= Node], - ok - end), - ok. - -%% Try to promote a slave while down - it should recover as a -%% master. We try to take the oldest slave here for best chance of -%% recovery. -forget_node_for_queue(DeadNode, Q = #amqqueue{recoverable_slaves = RS}) -> - forget_node_for_queue(DeadNode, RS, Q). - -forget_node_for_queue(_DeadNode, [], #amqqueue{name = Name}) -> - %% No slaves to recover from, queue is gone. - %% Don't process_deletions since that just calls callbacks and we - %% are not really up. - internal_delete1(Name, true); - -%% Should not happen, but let's be conservative. -forget_node_for_queue(DeadNode, [DeadNode | T], Q) -> - forget_node_for_queue(DeadNode, T, Q); - -forget_node_for_queue(DeadNode, [H|T], Q) -> - case node_permits_offline_promotion(H) of - false -> forget_node_for_queue(DeadNode, T, Q); - true -> Q1 = Q#amqqueue{pid = rabbit_misc:node_to_fake_pid(H)}, - ok = mnesia:write(rabbit_durable_queue, Q1, write) - end. - -node_permits_offline_promotion(Node) -> - case node() of - Node -> not rabbit:is_running(); %% [1] - _ -> Running = rabbit_mnesia:cluster_nodes(running), - not lists:member(Node, Running) %% [2] - end. -%% [1] In this case if we are a real running node (i.e. rabbitmqctl -%% has RPCed into us) then we cannot allow promotion. If on the other -%% hand we *are* rabbitmqctl impersonating the node for offline -%% node-forgetting then we can. -%% -%% [2] This is simpler; as long as it's down that's OK - -run_backing_queue(QPid, Mod, Fun) -> - gen_server2:cast(QPid, {run_backing_queue, Mod, Fun}). - -set_ram_duration_target(QPid, Duration) -> - gen_server2:cast(QPid, {set_ram_duration_target, Duration}). - -set_maximum_since_use(QPid, Age) -> - gen_server2:cast(QPid, {set_maximum_since_use, Age}). - -start_mirroring(QPid) -> ok = delegate:cast(QPid, start_mirroring). -stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring). - -sync_mirrors(QPid) -> delegate:call(QPid, sync_mirrors). -cancel_sync_mirrors(QPid) -> delegate:call(QPid, cancel_sync_mirrors). - -on_node_up(Node) -> - ok = rabbit_misc:execute_mnesia_transaction( - fun () -> - Qs = mnesia:match_object(rabbit_queue, - #amqqueue{_ = '_'}, write), - [maybe_clear_recoverable_node(Node, Q) || Q <- Qs], - ok - end). - -maybe_clear_recoverable_node(Node, - #amqqueue{sync_slave_pids = SPids, - recoverable_slaves = RSs} = Q) -> - case lists:member(Node, RSs) of - true -> - %% There is a race with - %% rabbit_mirror_queue_slave:record_synchronised/1 called - %% by the incoming slave node and this function, called - %% by the master node. If this function is executed after - %% record_synchronised/1, the node is erroneously removed - %% from the recoverable slaves list. - %% - %% We check if the slave node's queue PID is alive. If it is - %% the case, then this function is executed after. In this - %% situation, we don't touch the queue record, it is already - %% correct. - DoClearNode = - case [SP || SP <- SPids, node(SP) =:= Node] of - [SPid] -> not rabbit_misc:is_process_alive(SPid); - _ -> true - end, - if - DoClearNode -> RSs1 = RSs -- [Node], - store_queue( - Q#amqqueue{recoverable_slaves = RSs1}); - true -> ok - end; - false -> - ok - end. - -on_node_down(Node) -> - rabbit_misc:execute_mnesia_tx_with_tail( - fun () -> QsDels = - qlc:e(qlc:q([{QName, delete_queue(QName)} || - #amqqueue{name = QName, pid = Pid, - slave_pids = []} - <- mnesia:table(rabbit_queue), - node(Pid) == Node andalso - not rabbit_mnesia:is_process_alive(Pid)])), - {Qs, Dels} = lists:unzip(QsDels), - T = rabbit_binding:process_deletions( - lists:foldl(fun rabbit_binding:combine_deletions/2, - rabbit_binding:new_deletions(), Dels)), - fun () -> - T(), - lists:foreach( - fun(QName) -> - ok = rabbit_event:notify(queue_deleted, - [{name, QName}]) - end, Qs) - end - end). - -delete_queue(QueueName) -> - ok = mnesia:delete({rabbit_queue, QueueName}), - rabbit_binding:remove_transient_for_destination(QueueName). - -pseudo_queue(QueueName, Pid) -> - #amqqueue{name = QueueName, - durable = false, - auto_delete = false, - arguments = [], - pid = Pid, - slave_pids = []}. - -immutable(Q) -> Q#amqqueue{pid = none, - slave_pids = none, - sync_slave_pids = none, - recoverable_slaves = none, - gm_pids = none, - policy = none, - decorators = none, - state = none}. - -deliver([], _Delivery) -> - %% /dev/null optimisation - []; - -deliver(Qs, Delivery = #delivery{flow = Flow}) -> - {MPids, SPids} = qpids(Qs), - QPids = MPids ++ SPids, - %% We use up two credits to send to a slave since the message - %% arrives at the slave from two directions. We will ack one when - %% the slave receives the message direct from the channel, and the - %% other when it receives it via GM. - case Flow of - %% Here we are tracking messages sent by the rabbit_channel - %% process. We are accessing the rabbit_channel process - %% dictionary. - flow -> [credit_flow:send(QPid) || QPid <- QPids], - [credit_flow:send(QPid) || QPid <- SPids]; - noflow -> ok - end, - - %% We let slaves know that they were being addressed as slaves at - %% the time - if they receive such a message from the channel - %% after they have become master they should mark the message as - %% 'delivered' since they do not know what the master may have - %% done with it. - MMsg = {deliver, Delivery, false}, - SMsg = {deliver, Delivery, true}, - delegate:cast(MPids, MMsg), - delegate:cast(SPids, SMsg), - QPids. - -qpids([]) -> {[], []}; %% optimisation -qpids([#amqqueue{pid = QPid, slave_pids = SPids}]) -> {[QPid], SPids}; %% opt -qpids(Qs) -> - {MPids, SPids} = lists:foldl(fun (#amqqueue{pid = QPid, slave_pids = SPids}, - {MPidAcc, SPidAcc}) -> - {[QPid | MPidAcc], [SPids | SPidAcc]} - end, {[], []}, Qs), - {MPids, lists:append(SPids)}. diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl deleted file mode 100644 index 78e3e7dd4b..0000000000 --- a/src/rabbit_auth_mechanism.erl +++ /dev/null @@ -1,56 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_auth_mechanism). - --ifdef(use_specs). - -%% A description. --callback description() -> [proplists:property()]. - -%% If this mechanism is enabled, should it be offered for a given socket? -%% (primarily so EXTERNAL can be SSL-only) --callback should_offer(rabbit_net:socket()) -> boolean(). - -%% Called before authentication starts. Should create a state -%% object to be passed through all the stages of authentication. --callback init(rabbit_net:socket()) -> any(). - -%% Handle a stage of authentication. Possible responses: -%% {ok, User} -%% Authentication succeeded, and here's the user record. -%% {challenge, Challenge, NextState} -%% Another round is needed. Here's the state I want next time. -%% {protocol_error, Msg, Args} -%% Client got the protocol wrong. Log and die. -%% {refused, Username, Msg, Args} -%% Client failed authentication. Log and die. --callback handle_response(binary(), any()) -> - {'ok', rabbit_types:user()} | - {'challenge', binary(), any()} | - {'protocol_error', string(), [any()]} | - {'refused', rabbit_types:username() | none, string(), [any()]}. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{description, 0}, {should_offer, 1}, {init, 1}, {handle_response, 2}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_authn_backend.erl b/src/rabbit_authn_backend.erl deleted file mode 100644 index b9cb0d3669..0000000000 --- a/src/rabbit_authn_backend.erl +++ /dev/null @@ -1,49 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_authn_backend). - --include("rabbit.hrl"). - --ifdef(use_specs). - -%% Check a user can log in, given a username and a proplist of -%% authentication information (e.g. [{password, Password}]). If your -%% backend is not to be used for authentication, this should always -%% refuse access. -%% -%% Possible responses: -%% {ok, User} -%% Authentication succeeded, and here's the user record. -%% {error, Error} -%% Something went wrong. Log and die. -%% {refused, Msg, Args} -%% Client failed authentication. Log and die. --callback user_login_authentication(rabbit_types:username(), [term()]) -> - {'ok', rabbit_types:auth_user()} | - {'refused', string(), [any()]} | - {'error', any()}. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{user_login_authentication, 2}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_authz_backend.erl b/src/rabbit_authz_backend.erl deleted file mode 100644 index 495a79695d..0000000000 --- a/src/rabbit_authz_backend.erl +++ /dev/null @@ -1,76 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_authz_backend). - --include("rabbit.hrl"). - --ifdef(use_specs). - -%% Check a user can log in, when this backend is being used for -%% authorisation only. Authentication has already taken place -%% successfully, but we need to check that the user exists in this -%% backend, and initialise any impl field we will want to have passed -%% back in future calls to check_vhost_access/3 and -%% check_resource_access/3. -%% -%% Possible responses: -%% {ok, Impl} -%% {ok, Impl, Tags} -%% User authorisation succeeded, and here's the impl and potential extra tags fields. -%% {error, Error} -%% Something went wrong. Log and die. -%% {refused, Msg, Args} -%% User authorisation failed. Log and die. --callback user_login_authorization(rabbit_types:username()) -> - {'ok', any()} | - {'ok', any(), any()} | - {'refused', string(), [any()]} | - {'error', any()}. - -%% Given #auth_user and vhost, can a user log in to a vhost? -%% Possible responses: -%% true -%% false -%% {error, Error} -%% Something went wrong. Log and die. --callback check_vhost_access(rabbit_types:auth_user(), - rabbit_types:vhost(), rabbit_net:socket()) -> - boolean() | {'error', any()}. - -%% Given #auth_user, resource and permission, can a user access a resource? -%% -%% Possible responses: -%% true -%% false -%% {error, Error} -%% Something went wrong. Log and die. --callback check_resource_access(rabbit_types:auth_user(), - rabbit_types:r(atom()), - rabbit_access_control:permission_atom()) -> - boolean() | {'error', any()}. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{user_login_authorization, 1}, - {check_vhost_access, 3}, {check_resource_access, 3}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl deleted file mode 100644 index 2b808e206c..0000000000 --- a/src/rabbit_backing_queue.erl +++ /dev/null @@ -1,282 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_backing_queue). - --export([info_keys/0]). - --define(INFO_KEYS, [messages_ram, messages_ready_ram, - messages_unacknowledged_ram, messages_persistent, - message_bytes, message_bytes_ready, - message_bytes_unacknowledged, message_bytes_ram, - message_bytes_persistent, head_message_timestamp, - disk_reads, disk_writes, backing_queue_status]). - --ifdef(use_specs). - -%% We can't specify a per-queue ack/state with callback signatures --type(ack() :: any()). --type(state() :: any()). - --type(flow() :: 'flow' | 'noflow'). --type(msg_ids() :: [rabbit_types:msg_id()]). --type(publish() :: {rabbit_types:basic_message(), - rabbit_types:message_properties(), boolean()}). --type(delivered_publish() :: {rabbit_types:basic_message(), - rabbit_types:message_properties()}). --type(fetch_result(Ack) :: - ('empty' | {rabbit_types:basic_message(), boolean(), Ack})). --type(drop_result(Ack) :: - ('empty' | {rabbit_types:msg_id(), Ack})). --type(recovery_terms() :: [term()] | 'non_clean_shutdown'). --type(recovery_info() :: 'new' | recovery_terms()). --type(purged_msg_count() :: non_neg_integer()). --type(async_callback() :: - fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')). --type(duration() :: ('undefined' | 'infinity' | number())). - --type(msg_fun(A) :: fun ((rabbit_types:basic_message(), ack(), A) -> A)). --type(msg_pred() :: fun ((rabbit_types:message_properties()) -> boolean())). - --spec(info_keys/0 :: () -> rabbit_types:info_keys()). - -%% Called on startup with a list of durable queue names. The queues -%% aren't being started at this point, but this call allows the -%% backing queue to perform any checking necessary for the consistency -%% of those queues, or initialise any other shared resources. -%% -%% The list of queue recovery terms returned as {ok, Terms} must be given -%% in the same order as the list of queue names supplied. --callback start([rabbit_amqqueue:name()]) -> rabbit_types:ok(recovery_terms()). - -%% Called to tear down any state/resources. NB: Implementations should -%% not depend on this function being called on shutdown and instead -%% should hook into the rabbit supervision hierarchy. --callback stop() -> 'ok'. - -%% Initialise the backing queue and its state. -%% -%% Takes -%% 1. the amqqueue record -%% 2. a term indicating whether the queue is an existing queue that -%% should be recovered or not. When 'new' is given, no recovery is -%% taking place, otherwise a list of recovery terms is given, or -%% the atom 'non_clean_shutdown' if no recovery terms are available. -%% 3. an asynchronous callback which accepts a function of type -%% backing-queue-state to backing-queue-state. This callback -%% function can be safely invoked from any process, which makes it -%% useful for passing messages back into the backing queue, -%% especially as the backing queue does not have control of its own -%% mailbox. --callback init(rabbit_types:amqqueue(), recovery_info(), - async_callback()) -> state(). - -%% Called on queue shutdown when queue isn't being deleted. --callback terminate(any(), state()) -> state(). - -%% Called when the queue is terminating and needs to delete all its -%% content. --callback delete_and_terminate(any(), state()) -> state(). - -%% Called to clean up after a crashed queue. In this case we don't -%% have a process and thus a state(), we are just removing on-disk data. --callback delete_crashed(rabbit_types:amqqueue()) -> 'ok'. - -%% Remove all 'fetchable' messages from the queue, i.e. all messages -%% except those that have been fetched already and are pending acks. --callback purge(state()) -> {purged_msg_count(), state()}. - -%% Remove all messages in the queue which have been fetched and are -%% pending acks. --callback purge_acks(state()) -> state(). - -%% Publish a message. --callback publish(rabbit_types:basic_message(), - rabbit_types:message_properties(), boolean(), pid(), flow(), - state()) -> state(). - -%% Like publish/6 but for batches of publishes. --callback batch_publish([publish()], pid(), flow(), state()) -> state(). - -%% Called for messages which have already been passed straight -%% out to a client. The queue will be empty for these calls -%% (i.e. saves the round trip through the backing queue). --callback publish_delivered(rabbit_types:basic_message(), - rabbit_types:message_properties(), pid(), flow(), - state()) - -> {ack(), state()}. - -%% Like publish_delivered/5 but for batches of publishes. --callback batch_publish_delivered([delivered_publish()], pid(), flow(), - state()) - -> {[ack()], state()}. - -%% Called to inform the BQ about messages which have reached the -%% queue, but are not going to be further passed to BQ. --callback discard(rabbit_types:msg_id(), pid(), flow(), state()) -> state(). - -%% Return ids of messages which have been confirmed since the last -%% invocation of this function (or initialisation). -%% -%% Message ids should only appear in the result of drain_confirmed -%% under the following circumstances: -%% -%% 1. The message appears in a call to publish_delivered/4 and the -%% first argument (ack_required) is false; or -%% 2. The message is fetched from the queue with fetch/2 and the first -%% argument (ack_required) is false; or -%% 3. The message is acked (ack/2 is called for the message); or -%% 4. The message is fully fsync'd to disk in such a way that the -%% recovery of the message is guaranteed in the event of a crash of -%% this rabbit node (excluding hardware failure). -%% -%% In addition to the above conditions, a message id may only appear -%% in the result of drain_confirmed if -%% #message_properties.needs_confirming = true when the msg was -%% published (through whichever means) to the backing queue. -%% -%% It is legal for the same message id to appear in the results of -%% multiple calls to drain_confirmed, which means that the backing -%% queue is not required to keep track of which messages it has -%% already confirmed. The confirm will be issued to the publisher the -%% first time the message id appears in the result of -%% drain_confirmed. All subsequent appearances of that message id will -%% be ignored. --callback drain_confirmed(state()) -> {msg_ids(), state()}. - -%% Drop messages from the head of the queue while the supplied -%% predicate on message properties returns true. Returns the first -%% message properties for which the predictate returned false, or -%% 'undefined' if the whole backing queue was traversed w/o the -%% predicate ever returning false. --callback dropwhile(msg_pred(), state()) - -> {rabbit_types:message_properties() | undefined, state()}. - -%% Like dropwhile, except messages are fetched in "require -%% acknowledgement" mode and are passed, together with their ack tag, -%% to the supplied function. The function is also fed an -%% accumulator. The result of fetchwhile is as for dropwhile plus the -%% accumulator. --callback fetchwhile(msg_pred(), msg_fun(A), A, state()) - -> {rabbit_types:message_properties() | undefined, - A, state()}. - -%% Produce the next message. --callback fetch(true, state()) -> {fetch_result(ack()), state()}; - (false, state()) -> {fetch_result(undefined), state()}. - -%% Remove the next message. --callback drop(true, state()) -> {drop_result(ack()), state()}; - (false, state()) -> {drop_result(undefined), state()}. - -%% Acktags supplied are for messages which can now be forgotten -%% about. Must return 1 msg_id per Ack, in the same order as Acks. --callback ack([ack()], state()) -> {msg_ids(), state()}. - -%% Reinsert messages into the queue which have already been delivered -%% and were pending acknowledgement. --callback requeue([ack()], state()) -> {msg_ids(), state()}. - -%% Fold over messages by ack tag. The supplied function is called with -%% each message, its ack tag, and an accumulator. --callback ackfold(msg_fun(A), A, state(), [ack()]) -> {A, state()}. - -%% Fold over all the messages in a queue and return the accumulated -%% results, leaving the queue undisturbed. --callback fold(fun((rabbit_types:basic_message(), - rabbit_types:message_properties(), - boolean(), A) -> {('stop' | 'cont'), A}), - A, state()) -> {A, state()}. - -%% How long is my queue? --callback len(state()) -> non_neg_integer(). - -%% Is my queue empty? --callback is_empty(state()) -> boolean(). - -%% What's the queue depth, where depth = length + number of pending acks --callback depth(state()) -> non_neg_integer(). - -%% For the next three functions, the assumption is that you're -%% monitoring something like the ingress and egress rates of the -%% queue. The RAM duration is thus the length of time represented by -%% the messages held in RAM given the current rates. If you want to -%% ignore all of this stuff, then do so, and return 0 in -%% ram_duration/1. - -%% The target is to have no more messages in RAM than indicated by the -%% duration and the current queue rates. --callback set_ram_duration_target(duration(), state()) -> state(). - -%% Optionally recalculate the duration internally (likely to be just -%% update your internal rates), and report how many seconds the -%% messages in RAM represent given the current rates of the queue. --callback ram_duration(state()) -> {duration(), state()}. - -%% Should 'timeout' be called as soon as the queue process can manage -%% (either on an empty mailbox, or when a timer fires)? --callback needs_timeout(state()) -> 'false' | 'timed' | 'idle'. - -%% Called (eventually) after needs_timeout returns 'idle' or 'timed'. -%% Note this may be called more than once for each 'idle' or 'timed' -%% returned from needs_timeout --callback timeout(state()) -> state(). - -%% Called immediately before the queue hibernates. --callback handle_pre_hibernate(state()) -> state(). - -%% Called when more credit has become available for credit_flow. --callback resume(state()) -> state(). - -%% Used to help prioritisation in rabbit_amqqueue_process. The rate of -%% inbound messages and outbound messages at the moment. --callback msg_rates(state()) -> {float(), float()}. - --callback info(atom(), state()) -> any(). - -%% Passed a function to be invoked with the relevant backing queue's -%% state. Useful for when the backing queue or other components need -%% to pass functions into the backing queue. --callback invoke(atom(), fun ((atom(), A) -> A), state()) -> state(). - -%% Called prior to a publish or publish_delivered call. Allows the BQ -%% to signal that it's already seen this message, (e.g. it was published -%% or discarded previously) and thus the message should be dropped. --callback is_duplicate(rabbit_types:basic_message(), state()) - -> {boolean(), state()}. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, - {delete_and_terminate, 2}, {delete_crashed, 1}, {purge, 1}, - {purge_acks, 1}, {publish, 6}, {publish_delivered, 5}, - {batch_publish, 4}, {batch_publish_delivered, 4}, - {discard, 4}, {drain_confirmed, 1}, - {dropwhile, 2}, {fetchwhile, 4}, {fetch, 2}, - {drop, 2}, {ack, 2}, {requeue, 2}, {ackfold, 4}, {fold, 3}, {len, 1}, - {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, - {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, - {handle_pre_hibernate, 1}, {resume, 1}, {msg_rates, 1}, - {info, 2}, {invoke, 3}, {is_duplicate, 2}] ; -behaviour_info(_Other) -> - undefined. - --endif. - -info_keys() -> ?INFO_KEYS. diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl deleted file mode 100644 index d70f4f379d..0000000000 --- a/src/rabbit_basic.erl +++ /dev/null @@ -1,326 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_basic). --include("rabbit.hrl"). --include("rabbit_framing.hrl"). - --export([publish/4, publish/5, publish/1, - message/3, message/4, properties/1, prepend_table_header/3, - extract_headers/1, extract_timestamp/1, map_headers/2, delivery/4, - header_routes/1, parse_expiration/1, header/2, header/3]). --export([build_content/2, from_content/1, msg_size/1, maybe_gc_large_msg/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --type(properties_input() :: - (rabbit_framing:amqp_property_record() | [{atom(), any()}])). --type(publish_result() :: - ({ok, [pid()]} | rabbit_types:error('not_found'))). --type(header() :: any()). --type(headers() :: rabbit_framing:amqp_table() | 'undefined'). - --type(exchange_input() :: (rabbit_types:exchange() | rabbit_exchange:name())). --type(body_input() :: (binary() | [binary()])). - --spec(publish/4 :: - (exchange_input(), rabbit_router:routing_key(), properties_input(), - body_input()) -> publish_result()). --spec(publish/5 :: - (exchange_input(), rabbit_router:routing_key(), boolean(), - properties_input(), body_input()) -> publish_result()). --spec(publish/1 :: - (rabbit_types:delivery()) -> publish_result()). --spec(delivery/4 :: - (boolean(), boolean(), rabbit_types:message(), undefined | integer()) -> - rabbit_types:delivery()). --spec(message/4 :: - (rabbit_exchange:name(), rabbit_router:routing_key(), - properties_input(), binary()) -> rabbit_types:message()). --spec(message/3 :: - (rabbit_exchange:name(), rabbit_router:routing_key(), - rabbit_types:decoded_content()) -> - rabbit_types:ok_or_error2(rabbit_types:message(), any())). --spec(properties/1 :: - (properties_input()) -> rabbit_framing:amqp_property_record()). - --spec(prepend_table_header/3 :: - (binary(), rabbit_framing:amqp_table(), headers()) -> headers()). - --spec(header/2 :: - (header(), headers()) -> 'undefined' | any()). --spec(header/3 :: - (header(), headers(), any()) -> 'undefined' | any()). - --spec(extract_headers/1 :: (rabbit_types:content()) -> headers()). - --spec(map_headers/2 :: (fun((headers()) -> headers()), rabbit_types:content()) - -> rabbit_types:content()). - --spec(header_routes/1 :: - (undefined | rabbit_framing:amqp_table()) -> [string()]). --spec(build_content/2 :: (rabbit_framing:amqp_property_record(), - binary() | [binary()]) -> rabbit_types:content()). --spec(from_content/1 :: (rabbit_types:content()) -> - {rabbit_framing:amqp_property_record(), binary()}). --spec(parse_expiration/1 :: - (rabbit_framing:amqp_property_record()) - -> rabbit_types:ok_or_error2('undefined' | non_neg_integer(), any())). - --spec(msg_size/1 :: (rabbit_types:content() | rabbit_types:message()) -> - non_neg_integer()). - --spec(maybe_gc_large_msg/1 :: - (rabbit_types:content() | rabbit_types:message()) -> non_neg_integer()). - --endif. - -%%---------------------------------------------------------------------------- - -%% Convenience function, for avoiding round-trips in calls across the -%% erlang distributed network. -publish(Exchange, RoutingKeyBin, Properties, Body) -> - publish(Exchange, RoutingKeyBin, false, Properties, Body). - -%% Convenience function, for avoiding round-trips in calls across the -%% erlang distributed network. -publish(X = #exchange{name = XName}, RKey, Mandatory, Props, Body) -> - Message = message(XName, RKey, properties(Props), Body), - publish(X, delivery(Mandatory, false, Message, undefined)); -publish(XName, RKey, Mandatory, Props, Body) -> - Message = message(XName, RKey, properties(Props), Body), - publish(delivery(Mandatory, false, Message, undefined)). - -publish(Delivery = #delivery{ - message = #basic_message{exchange_name = XName}}) -> - case rabbit_exchange:lookup(XName) of - {ok, X} -> publish(X, Delivery); - Err -> Err - end. - -publish(X, Delivery) -> - Qs = rabbit_amqqueue:lookup(rabbit_exchange:route(X, Delivery)), - DeliveredQPids = rabbit_amqqueue:deliver(Qs, Delivery), - {ok, DeliveredQPids}. - -delivery(Mandatory, Confirm, Message, MsgSeqNo) -> - #delivery{mandatory = Mandatory, confirm = Confirm, sender = self(), - message = Message, msg_seq_no = MsgSeqNo, flow = noflow}. - -build_content(Properties, BodyBin) when is_binary(BodyBin) -> - build_content(Properties, [BodyBin]); - -build_content(Properties, PFR) -> - %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 - {ClassId, _MethodId} = - rabbit_framing_amqp_0_9_1:method_id('basic.publish'), - #content{class_id = ClassId, - properties = Properties, - properties_bin = none, - protocol = none, - payload_fragments_rev = PFR}. - -from_content(Content) -> - #content{class_id = ClassId, - properties = Props, - payload_fragments_rev = FragmentsRev} = - rabbit_binary_parser:ensure_content_decoded(Content), - %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 - {ClassId, _MethodId} = - rabbit_framing_amqp_0_9_1:method_id('basic.publish'), - {Props, list_to_binary(lists:reverse(FragmentsRev))}. - -%% This breaks the spec rule forbidding message modification -strip_header(#content{properties = #'P_basic'{headers = undefined}} - = DecodedContent, _Key) -> - DecodedContent; -strip_header(#content{properties = Props = #'P_basic'{headers = Headers}} - = DecodedContent, Key) -> - case lists:keysearch(Key, 1, Headers) of - false -> DecodedContent; - {value, Found} -> Headers0 = lists:delete(Found, Headers), - rabbit_binary_generator:clear_encoded_content( - DecodedContent#content{ - properties = Props#'P_basic'{ - headers = Headers0}}) - end. - -message(XName, RoutingKey, #content{properties = Props} = DecodedContent) -> - try - {ok, #basic_message{ - exchange_name = XName, - content = strip_header(DecodedContent, ?DELETED_HEADER), - id = rabbit_guid:gen(), - is_persistent = is_message_persistent(DecodedContent), - routing_keys = [RoutingKey | - header_routes(Props#'P_basic'.headers)]}} - catch - {error, _Reason} = Error -> Error - end. - -message(XName, RoutingKey, RawProperties, Body) -> - Properties = properties(RawProperties), - Content = build_content(Properties, Body), - {ok, Msg} = message(XName, RoutingKey, Content), - Msg. - -properties(P = #'P_basic'{}) -> - P; -properties(P) when is_list(P) -> - %% Yes, this is O(length(P) * record_info(size, 'P_basic') / 2), - %% i.e. slow. Use the definition of 'P_basic' directly if - %% possible! - lists:foldl(fun ({Key, Value}, Acc) -> - case indexof(record_info(fields, 'P_basic'), Key) of - 0 -> throw({unknown_basic_property, Key}); - N -> setelement(N + 1, Acc, Value) - end - end, #'P_basic'{}, P). - -prepend_table_header(Name, Info, undefined) -> - prepend_table_header(Name, Info, []); -prepend_table_header(Name, Info, Headers) -> - case rabbit_misc:table_lookup(Headers, Name) of - {array, Existing} -> - prepend_table(Name, Info, Existing, Headers); - undefined -> - prepend_table(Name, Info, [], Headers); - Other -> - Headers2 = prepend_table(Name, Info, [], Headers), - set_invalid_header(Name, Other, Headers2) - end. - -prepend_table(Name, Info, Prior, Headers) -> - rabbit_misc:set_table_value(Headers, Name, array, [{table, Info} | Prior]). - -set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> - case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of - undefined -> - set_invalid([{Name, array, [Value]}], Headers); - {table, ExistingHdr} -> - update_invalid(Name, Value, ExistingHdr, Headers); - Other -> - %% somehow the x-invalid-headers header is corrupt - Invalid = [{?INVALID_HEADERS_KEY, array, [Other]}], - set_invalid_header(Name, Value, set_invalid(Invalid, Headers)) - end. - -set_invalid(NewHdr, Headers) -> - rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, table, NewHdr). - -update_invalid(Name, Value, ExistingHdr, Header) -> - Values = case rabbit_misc:table_lookup(ExistingHdr, Name) of - undefined -> [Value]; - {array, Prior} -> [Value | Prior] - end, - NewHdr = rabbit_misc:set_table_value(ExistingHdr, Name, array, Values), - set_invalid(NewHdr, Header). - -header(_Header, undefined) -> - undefined; -header(_Header, []) -> - undefined; -header(Header, Headers) -> - header(Header, Headers, undefined). - -header(Header, Headers, Default) -> - case lists:keysearch(Header, 1, Headers) of - false -> Default; - {value, Val} -> Val - end. - -extract_headers(Content) -> - #content{properties = #'P_basic'{headers = Headers}} = - rabbit_binary_parser:ensure_content_decoded(Content), - Headers. - -extract_timestamp(Content) -> - #content{properties = #'P_basic'{timestamp = Timestamp}} = - rabbit_binary_parser:ensure_content_decoded(Content), - Timestamp. - -map_headers(F, Content) -> - Content1 = rabbit_binary_parser:ensure_content_decoded(Content), - #content{properties = #'P_basic'{headers = Headers} = Props} = Content1, - Headers1 = F(Headers), - rabbit_binary_generator:clear_encoded_content( - Content1#content{properties = Props#'P_basic'{headers = Headers1}}). - -indexof(L, Element) -> indexof(L, Element, 1). - -indexof([], _Element, _N) -> 0; -indexof([Element | _Rest], Element, N) -> N; -indexof([_ | Rest], Element, N) -> indexof(Rest, Element, N + 1). - -is_message_persistent(#content{properties = #'P_basic'{ - delivery_mode = Mode}}) -> - case Mode of - 1 -> false; - 2 -> true; - undefined -> false; - Other -> throw({error, {delivery_mode_unknown, Other}}) - end. - -%% Extract CC routes from headers -header_routes(undefined) -> - []; -header_routes(HeadersTable) -> - lists:append( - [case rabbit_misc:table_lookup(HeadersTable, HeaderKey) of - {array, Routes} -> [Route || {longstr, Route} <- Routes]; - undefined -> []; - {Type, _Val} -> throw({error, {unacceptable_type_in_header, - binary_to_list(HeaderKey), Type}}) - end || HeaderKey <- ?ROUTING_HEADERS]). - -parse_expiration(#'P_basic'{expiration = undefined}) -> - {ok, undefined}; -parse_expiration(#'P_basic'{expiration = Expiration}) -> - case string:to_integer(binary_to_list(Expiration)) of - {error, no_integer} = E -> - E; - {N, ""} -> - case rabbit_misc:check_expiry(N) of - ok -> {ok, N}; - E = {error, _} -> E - end; - {_, S} -> - {error, {leftover_string, S}} - end. - -%% Some processes (channel, writer) can get huge amounts of binary -%% garbage when processing huge messages at high speed (since we only -%% do enough reductions to GC every few hundred messages, and if each -%% message is 1MB then that's ugly). So count how many bytes of -%% message we have processed, and force a GC every so often. -maybe_gc_large_msg(Content) -> - Size = msg_size(Content), - Current = case get(msg_size_for_gc) of - undefined -> 0; - C -> C - end, - New = Current + Size, - put(msg_size_for_gc, case New > 1000000 of - true -> erlang:garbage_collect(), - 0; - false -> New - end), - Size. - -msg_size(#content{payload_fragments_rev = PFR}) -> iolist_size(PFR); -msg_size(#basic_message{content = Content}) -> msg_size(Content). diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl deleted file mode 100644 index 34f2d601aa..0000000000 --- a/src/rabbit_binary_generator.erl +++ /dev/null @@ -1,241 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_binary_generator). --include("rabbit_framing.hrl"). --include("rabbit.hrl"). - --export([build_simple_method_frame/3, - build_simple_content_frames/4, - build_heartbeat_frame/0]). --export([generate_table/1]). --export([check_empty_frame_size/0]). --export([ensure_content_encoded/2, clear_encoded_content/1]). --export([map_exception/3]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --type(frame() :: [binary()]). - --spec(build_simple_method_frame/3 :: - (rabbit_channel:channel_number(), rabbit_framing:amqp_method_record(), - rabbit_types:protocol()) - -> frame()). --spec(build_simple_content_frames/4 :: - (rabbit_channel:channel_number(), rabbit_types:content(), - non_neg_integer(), rabbit_types:protocol()) - -> [frame()]). --spec(build_heartbeat_frame/0 :: () -> frame()). --spec(generate_table/1 :: (rabbit_framing:amqp_table()) -> binary()). --spec(check_empty_frame_size/0 :: () -> 'ok'). --spec(ensure_content_encoded/2 :: - (rabbit_types:content(), rabbit_types:protocol()) -> - rabbit_types:encoded_content()). --spec(clear_encoded_content/1 :: - (rabbit_types:content()) -> rabbit_types:unencoded_content()). --spec(map_exception/3 :: (rabbit_channel:channel_number(), - rabbit_types:amqp_error() | any(), - rabbit_types:protocol()) -> - {rabbit_channel:channel_number(), - rabbit_framing:amqp_method_record()}). - --endif. - -%%---------------------------------------------------------------------------- - -build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> - MethodFields = Protocol:encode_method_fields(MethodRecord), - MethodName = rabbit_misc:method_record_type(MethodRecord), - {ClassId, MethodId} = Protocol:method_id(MethodName), - create_frame(1, ChannelInt, [<<ClassId:16, MethodId:16>>, MethodFields]). - -build_simple_content_frames(ChannelInt, Content, FrameMax, Protocol) -> - #content{class_id = ClassId, - properties_bin = ContentPropertiesBin, - payload_fragments_rev = PayloadFragmentsRev} = - ensure_content_encoded(Content, Protocol), - {BodySize, ContentFrames} = - build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt), - HeaderFrame = create_frame(2, ChannelInt, - [<<ClassId:16, 0:16, BodySize:64>>, - ContentPropertiesBin]), - [HeaderFrame | ContentFrames]. - -build_content_frames(FragsRev, FrameMax, ChannelInt) -> - BodyPayloadMax = if FrameMax == 0 -> iolist_size(FragsRev); - true -> FrameMax - ?EMPTY_FRAME_SIZE - end, - build_content_frames(0, [], BodyPayloadMax, [], - lists:reverse(FragsRev), BodyPayloadMax, ChannelInt). - -build_content_frames(SizeAcc, FramesAcc, _FragSizeRem, [], - [], _BodyPayloadMax, _ChannelInt) -> - {SizeAcc, lists:reverse(FramesAcc)}; -build_content_frames(SizeAcc, FramesAcc, FragSizeRem, FragAcc, - Frags, BodyPayloadMax, ChannelInt) - when FragSizeRem == 0 orelse Frags == [] -> - Frame = create_frame(3, ChannelInt, lists:reverse(FragAcc)), - FrameSize = BodyPayloadMax - FragSizeRem, - build_content_frames(SizeAcc + FrameSize, [Frame | FramesAcc], - BodyPayloadMax, [], Frags, BodyPayloadMax, ChannelInt); -build_content_frames(SizeAcc, FramesAcc, FragSizeRem, FragAcc, - [Frag | Frags], BodyPayloadMax, ChannelInt) -> - Size = size(Frag), - {NewFragSizeRem, NewFragAcc, NewFrags} = - if Size == 0 -> {FragSizeRem, FragAcc, Frags}; - Size =< FragSizeRem -> {FragSizeRem - Size, [Frag | FragAcc], Frags}; - true -> <<Head:FragSizeRem/binary, Tail/binary>> = - Frag, - {0, [Head | FragAcc], [Tail | Frags]} - end, - build_content_frames(SizeAcc, FramesAcc, NewFragSizeRem, NewFragAcc, - NewFrags, BodyPayloadMax, ChannelInt). - -build_heartbeat_frame() -> - create_frame(?FRAME_HEARTBEAT, 0, <<>>). - -create_frame(TypeInt, ChannelInt, Payload) -> - [<<TypeInt:8, ChannelInt:16, (iolist_size(Payload)):32>>, Payload, - ?FRAME_END]. - -%% table_field_to_binary supports the AMQP 0-8/0-9 standard types, S, -%% I, D, T and F, as well as the QPid extensions b, d, f, l, s, t, x, -%% and V. -table_field_to_binary({FName, T, V}) -> - [short_string_to_binary(FName) | field_value_to_binary(T, V)]. - -field_value_to_binary(longstr, V) -> [$S | long_string_to_binary(V)]; -field_value_to_binary(signedint, V) -> [$I, <<V:32/signed>>]; -field_value_to_binary(decimal, V) -> {Before, After} = V, - [$D, Before, <<After:32>>]; -field_value_to_binary(timestamp, V) -> [$T, <<V:64>>]; -field_value_to_binary(table, V) -> [$F | table_to_binary(V)]; -field_value_to_binary(array, V) -> [$A | array_to_binary(V)]; -field_value_to_binary(byte, V) -> [$b, <<V:8/signed>>]; -field_value_to_binary(double, V) -> [$d, <<V:64/float>>]; -field_value_to_binary(float, V) -> [$f, <<V:32/float>>]; -field_value_to_binary(long, V) -> [$l, <<V:64/signed>>]; -field_value_to_binary(short, V) -> [$s, <<V:16/signed>>]; -field_value_to_binary(bool, V) -> [$t, if V -> 1; true -> 0 end]; -field_value_to_binary(binary, V) -> [$x | long_string_to_binary(V)]; -field_value_to_binary(void, _V) -> [$V]. - -table_to_binary(Table) when is_list(Table) -> - BinTable = generate_table_iolist(Table), - [<<(iolist_size(BinTable)):32>> | BinTable]. - -array_to_binary(Array) when is_list(Array) -> - BinArray = generate_array_iolist(Array), - [<<(iolist_size(BinArray)):32>> | BinArray]. - -generate_table(Table) when is_list(Table) -> - list_to_binary(generate_table_iolist(Table)). - -generate_table_iolist(Table) -> - lists:map(fun table_field_to_binary/1, Table). - -generate_array_iolist(Array) -> - lists:map(fun ({T, V}) -> field_value_to_binary(T, V) end, Array). - -short_string_to_binary(String) -> - Len = string_length(String), - if Len < 256 -> [<<Len:8>>, String]; - true -> exit(content_properties_shortstr_overflow) - end. - -long_string_to_binary(String) -> - Len = string_length(String), - [<<Len:32>>, String]. - -string_length(String) when is_binary(String) -> size(String); -string_length(String) -> length(String). - -check_empty_frame_size() -> - %% Intended to ensure that EMPTY_FRAME_SIZE is defined correctly. - case iolist_size(create_frame(?FRAME_BODY, 0, <<>>)) of - ?EMPTY_FRAME_SIZE -> ok; - ComputedSize -> exit({incorrect_empty_frame_size, - ComputedSize, ?EMPTY_FRAME_SIZE}) - end. - -ensure_content_encoded(Content = #content{properties_bin = PropBin, - protocol = Protocol}, Protocol) - when PropBin =/= none -> - Content; -ensure_content_encoded(Content = #content{properties = none, - properties_bin = PropBin, - protocol = Protocol}, Protocol1) - when PropBin =/= none -> - Props = Protocol:decode_properties(Content#content.class_id, PropBin), - Content#content{properties = Props, - properties_bin = Protocol1:encode_properties(Props), - protocol = Protocol1}; -ensure_content_encoded(Content = #content{properties = Props}, Protocol) - when Props =/= none -> - Content#content{properties_bin = Protocol:encode_properties(Props), - protocol = Protocol}. - -clear_encoded_content(Content = #content{properties_bin = none, - protocol = none}) -> - Content; -clear_encoded_content(Content = #content{properties = none}) -> - %% Only clear when we can rebuild the properties_bin later in - %% accordance to the content record definition comment - maximum - %% one of properties and properties_bin can be 'none' - Content; -clear_encoded_content(Content = #content{}) -> - Content#content{properties_bin = none, protocol = none}. - -%% NB: this function is also used by the Erlang client -map_exception(Channel, Reason, Protocol) -> - {SuggestedClose, ReplyCode, ReplyText, FailedMethod} = - lookup_amqp_exception(Reason, Protocol), - {ClassId, MethodId} = case FailedMethod of - {_, _} -> FailedMethod; - none -> {0, 0}; - _ -> Protocol:method_id(FailedMethod) - end, - case SuggestedClose orelse (Channel == 0) of - true -> {0, #'connection.close'{reply_code = ReplyCode, - reply_text = ReplyText, - class_id = ClassId, - method_id = MethodId}}; - false -> {Channel, #'channel.close'{reply_code = ReplyCode, - reply_text = ReplyText, - class_id = ClassId, - method_id = MethodId}} - end. - -lookup_amqp_exception(#amqp_error{name = Name, - explanation = Expl, - method = Method}, - Protocol) -> - {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(Name), - ExplBin = amqp_exception_explanation(Text, Expl), - {ShouldClose, Code, ExplBin, Method}; -lookup_amqp_exception(Other, Protocol) -> - rabbit_log:warning("Non-AMQP exit reason '~p'~n", [Other]), - {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(internal_error), - {ShouldClose, Code, Text, none}. - -amqp_exception_explanation(Text, Expl) -> - ExplBin = list_to_binary(Expl), - CompleteTextBin = <<Text/binary, " - ", ExplBin/binary>>, - if size(CompleteTextBin) > 255 -> <<CompleteTextBin:252/binary, "...">>; - true -> CompleteTextBin - end. diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl deleted file mode 100644 index 8b3bf3e6f5..0000000000 --- a/src/rabbit_binary_parser.erl +++ /dev/null @@ -1,161 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_binary_parser). - --include("rabbit.hrl"). - --export([parse_table/1]). --export([ensure_content_decoded/1, clear_decoded_content/1]). --export([validate_utf8/1, assert_utf8/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(parse_table/1 :: (binary()) -> rabbit_framing:amqp_table()). --spec(ensure_content_decoded/1 :: - (rabbit_types:content()) -> rabbit_types:decoded_content()). --spec(clear_decoded_content/1 :: - (rabbit_types:content()) -> rabbit_types:undecoded_content()). --spec(validate_utf8/1 :: (binary()) -> 'ok' | 'error'). --spec(assert_utf8/1 :: (binary()) -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -%% parse_table supports the AMQP 0-8/0-9 standard types, S, I, D, T -%% and F, as well as the QPid extensions b, d, f, l, s, t, x, and V. - --define(SIMPLE_PARSE_TABLE(BType, Pattern, RType), - parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - BType, Pattern, Rest/binary>>) -> - [{NameString, RType, Value} | parse_table(Rest)]). - -%% Note that we try to put these in approximately the order we expect -%% to hit them, that's why the empty binary is half way through. - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $S, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{NameString, longstr, Value} | parse_table(Rest)]; - -?SIMPLE_PARSE_TABLE($I, Value:32/signed, signedint); -?SIMPLE_PARSE_TABLE($T, Value:64/unsigned, timestamp); - -parse_table(<<>>) -> - []; - -?SIMPLE_PARSE_TABLE($b, Value:8/signed, byte); -?SIMPLE_PARSE_TABLE($d, Value:64/float, double); -?SIMPLE_PARSE_TABLE($f, Value:32/float, float); -?SIMPLE_PARSE_TABLE($l, Value:64/signed, long); -?SIMPLE_PARSE_TABLE($s, Value:16/signed, short); - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $t, Value:8/unsigned, Rest/binary>>) -> - [{NameString, bool, (Value /= 0)} | parse_table(Rest)]; - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $D, Before:8/unsigned, After:32/unsigned, Rest/binary>>) -> - [{NameString, decimal, {Before, After}} | parse_table(Rest)]; - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $F, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{NameString, table, parse_table(Value)} | parse_table(Rest)]; - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $A, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{NameString, array, parse_array(Value)} | parse_table(Rest)]; - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $x, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{NameString, binary, Value} | parse_table(Rest)]; - -parse_table(<<NLen:8/unsigned, NameString:NLen/binary, - $V, Rest/binary>>) -> - [{NameString, void, undefined} | parse_table(Rest)]. - --define(SIMPLE_PARSE_ARRAY(BType, Pattern, RType), - parse_array(<<BType, Pattern, Rest/binary>>) -> - [{RType, Value} | parse_array(Rest)]). - -parse_array(<<$S, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{longstr, Value} | parse_array(Rest)]; - -?SIMPLE_PARSE_ARRAY($I, Value:32/signed, signedint); -?SIMPLE_PARSE_ARRAY($T, Value:64/unsigned, timestamp); - -parse_array(<<>>) -> - []; - -?SIMPLE_PARSE_ARRAY($b, Value:8/signed, byte); -?SIMPLE_PARSE_ARRAY($d, Value:64/float, double); -?SIMPLE_PARSE_ARRAY($f, Value:32/float, float); -?SIMPLE_PARSE_ARRAY($l, Value:64/signed, long); -?SIMPLE_PARSE_ARRAY($s, Value:16/signed, short); - -parse_array(<<$t, Value:8/unsigned, Rest/binary>>) -> - [{bool, (Value /= 0)} | parse_array(Rest)]; - -parse_array(<<$D, Before:8/unsigned, After:32/unsigned, Rest/binary>>) -> - [{decimal, {Before, After}} | parse_array(Rest)]; - -parse_array(<<$F, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{table, parse_table(Value)} | parse_array(Rest)]; - -parse_array(<<$A, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{array, parse_array(Value)} | parse_array(Rest)]; - -parse_array(<<$x, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> - [{binary, Value} | parse_array(Rest)]; - -parse_array(<<$V, Rest/binary>>) -> - [{void, undefined} | parse_array(Rest)]. - -ensure_content_decoded(Content = #content{properties = Props}) - when Props =/= none -> - Content; -ensure_content_decoded(Content = #content{properties_bin = PropBin, - protocol = Protocol}) - when PropBin =/= none -> - Content#content{properties = Protocol:decode_properties( - Content#content.class_id, PropBin)}. - -clear_decoded_content(Content = #content{properties = none}) -> - Content; -clear_decoded_content(Content = #content{properties_bin = none}) -> - %% Only clear when we can rebuild the properties later in - %% accordance to the content record definition comment - maximum - %% one of properties and properties_bin can be 'none' - Content; -clear_decoded_content(Content = #content{}) -> - Content#content{properties = none}. - -assert_utf8(B) -> - case validate_utf8(B) of - ok -> ok; - error -> rabbit_misc:protocol_error( - frame_error, "Malformed UTF-8 in shortstr", []) - end. - -validate_utf8(Bin) -> - try - xmerl_ucs:from_utf8(Bin), - ok - catch exit:{ucs, _} -> - error - end. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl deleted file mode 100644 index f8ed9cae9f..0000000000 --- a/src/rabbit_channel.erl +++ /dev/null @@ -1,2017 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_channel). - -%% rabbit_channel processes represent an AMQP 0-9-1 channels. -%% -%% Connections parse protocol frames coming from clients and -%% dispatch them to channel processes. -%% Channels are responsible for implementing the logic behind -%% the various protocol methods, involving other processes as -%% needed: -%% -%% * Routing messages (using functions in various exchange type -%% modules) to queue processes. -%% * Managing queues, exchanges, and bindings. -%% * Keeping track of consumers -%% * Keeping track of unacknowledged deliveries to consumers -%% * Keeping track of publisher confirms -%% * Keeping track of mandatory message routing confirmations -%% and returns -%% * Transaction management -%% * Authorisation (enforcing permissions) -%% * Publishing trace events if tracing is enabled -%% -%% Every channel has a number of dependent processes: -%% -%% * A writer which is responsible for sending frames to clients. -%% * A limiter which controls how many messages can be delivered -%% to consumers according to active QoS prefetch and internal -%% flow control logic. -%% -%% Channels are also aware of their connection's queue collector. -%% When a queue is declared as exclusive on a channel, the channel -%% will notify queue collector of that queue. - --include("rabbit_framing.hrl"). --include("rabbit.hrl"). - --behaviour(gen_server2). - --export([start_link/11, do/2, do/3, do_flow/3, flush/1, shutdown/1]). --export([send_command/2, deliver/4, deliver_reply/2, - send_credit_reply/2, send_drained/2]). --export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). --export([refresh_config_local/0, ready_for_close/1]). --export([force_event_refresh/1]). - --export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, - handle_info/2, handle_pre_hibernate/1, prioritise_call/4, - prioritise_cast/3, prioritise_info/3, format_message_queue/2]). -%% Internal --export([list_local/0, deliver_reply_local/3]). --export([get_vhost/1, get_user/1]). - --record(ch, { - %% starting | running | flow | closing - state, - %% same as reader's protocol. Used when instantiating - %% (protocol) exceptions. - protocol, - %% channel number - channel, - %% reader process - reader_pid, - %% writer process - writer_pid, - %% - conn_pid, - %% same as reader's name, see #v1.name - %% in rabbit_reader - conn_name, - %% limiter pid, see rabbit_limiter - limiter, - %% none | {Msgs, Acks} | committing | failed | - tx, - %% (consumer) delivery tag sequence - next_tag, - %% messages pending consumer acknowledgement - unacked_message_q, - %% same as #v1.user in the reader, used in - %% authorisation checks - user, - %% same as #v1.user in the reader - virtual_host, - %% when queue.bind's queue field is empty, - %% this name will be used instead - most_recently_declared_queue, - %% a dictionary of queue pid to queue name - queue_names, - %% queue processes are monitored to update - %% queue names - queue_monitors, - %% a dictionary of consumer tags to - %% consumer details: #amqqueue record, acknowledgement mode, - %% consumer exclusivity, etc - consumer_mapping, - %% a dictionary of queue pids to consumer tag lists - queue_consumers, - %% a set of pids of queues that have unacknowledged - %% deliveries - delivering_queues, - %% when a queue is declared as exclusive, queue - %% collector must be notified. - %% see rabbit_queue_collector for more info. - queue_collector_pid, - %% timer used to emit statistics - stats_timer, - %% are publisher confirms enabled for this channel? - confirm_enabled, - %% publisher confirm delivery tag sequence - publish_seqno, - %% a dtree used to track unconfirmed - %% (to publishers) messages - unconfirmed, - %% a list of tags for published messages that were - %% delivered but are yet to be confirmed to the client - confirmed, - %% a dtree used to track oustanding notifications - %% for messages published as mandatory - mandatory, - %% same as capabilities in the reader - capabilities, - %% tracing exchange resource if tracing is enabled, - %% 'none' otherwise - trace_state, - consumer_prefetch, - %% used by "one shot RPC" (amq. - reply_consumer, - %% flow | noflow, see rabbitmq-server#114 - delivery_flow, - interceptor_state -}). - - --define(MAX_PERMISSION_CACHE_SIZE, 12). - --define(STATISTICS_KEYS, - [pid, - transactional, - confirm, - consumer_count, - messages_unacknowledged, - messages_unconfirmed, - messages_uncommitted, - acks_uncommitted, - prefetch_count, - global_prefetch_count, - state]). - --define(CREATION_EVENT_KEYS, - [pid, - name, - connection, - number, - user, - vhost]). - --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). - --define(INCR_STATS(Incs, Measure, State), - case rabbit_event:stats_level(State, #ch.stats_timer) of - fine -> incr_stats(Incs, Measure); - _ -> ok - end). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([channel_number/0]). - --type(channel_number() :: non_neg_integer()). - --export_type([channel/0]). - --type(channel() :: #ch{}). - --spec(start_link/11 :: - (channel_number(), pid(), pid(), pid(), string(), - rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(), - rabbit_framing:amqp_table(), pid(), pid()) -> - rabbit_types:ok_pid_or_error()). --spec(do/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). --spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(), - rabbit_types:maybe(rabbit_types:content())) -> 'ok'). --spec(do_flow/3 :: (pid(), rabbit_framing:amqp_method_record(), - rabbit_types:maybe(rabbit_types:content())) -> 'ok'). --spec(flush/1 :: (pid()) -> 'ok'). --spec(shutdown/1 :: (pid()) -> 'ok'). --spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). --spec(deliver/4 :: - (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg()) - -> 'ok'). --spec(deliver_reply/2 :: (binary(), rabbit_types:delivery()) -> 'ok'). --spec(deliver_reply_local/3 :: - (pid(), binary(), rabbit_types:delivery()) -> 'ok'). --spec(send_credit_reply/2 :: (pid(), non_neg_integer()) -> 'ok'). --spec(send_drained/2 :: (pid(), [{rabbit_types:ctag(), non_neg_integer()}]) - -> 'ok'). --spec(list/0 :: () -> [pid()]). --spec(list_local/0 :: () -> [pid()]). --spec(info_keys/0 :: () -> rabbit_types:info_keys()). --spec(info/1 :: (pid()) -> rabbit_types:infos()). --spec(info/2 :: (pid(), rabbit_types:info_keys()) -> rabbit_types:infos()). --spec(info_all/0 :: () -> [rabbit_types:infos()]). --spec(info_all/1 :: (rabbit_types:info_keys()) -> [rabbit_types:infos()]). --spec(refresh_config_local/0 :: () -> 'ok'). --spec(ready_for_close/1 :: (pid()) -> 'ok'). --spec(force_event_refresh/1 :: (reference()) -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -start_link(Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, - VHost, Capabilities, CollectorPid, Limiter) -> - gen_server2:start_link( - ?MODULE, [Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, - User, VHost, Capabilities, CollectorPid, Limiter], []). - -do(Pid, Method) -> - do(Pid, Method, none). - -do(Pid, Method, Content) -> - gen_server2:cast(Pid, {method, Method, Content, noflow}). - -do_flow(Pid, Method, Content) -> - %% Here we are tracking messages sent by the rabbit_reader - %% process. We are accessing the rabbit_reader process dictionary. - credit_flow:send(Pid), - gen_server2:cast(Pid, {method, Method, Content, flow}). - -flush(Pid) -> - gen_server2:call(Pid, flush, infinity). - -shutdown(Pid) -> - gen_server2:cast(Pid, terminate). - -send_command(Pid, Msg) -> - gen_server2:cast(Pid, {command, Msg}). - -deliver(Pid, ConsumerTag, AckRequired, Msg) -> - gen_server2:cast(Pid, {deliver, ConsumerTag, AckRequired, Msg}). - -deliver_reply(<<"amq.rabbitmq.reply-to.", Rest/binary>>, Delivery) -> - case decode_fast_reply_to(Rest) of - {ok, Pid, Key} -> - delegate:invoke_no_result( - Pid, {?MODULE, deliver_reply_local, [Key, Delivery]}); - error -> - ok - end. - -%% We want to ensure people can't use this mechanism to send a message -%% to an arbitrary process and kill it! -deliver_reply_local(Pid, Key, Delivery) -> - case pg_local:in_group(rabbit_channels, Pid) of - true -> gen_server2:cast(Pid, {deliver_reply, Key, Delivery}); - false -> ok - end. - -declare_fast_reply_to(<<"amq.rabbitmq.reply-to">>) -> - exists; -declare_fast_reply_to(<<"amq.rabbitmq.reply-to.", Rest/binary>>) -> - case decode_fast_reply_to(Rest) of - {ok, Pid, Key} -> - Msg = {declare_fast_reply_to, Key}, - rabbit_misc:with_exit_handler( - rabbit_misc:const(not_found), - fun() -> gen_server2:call(Pid, Msg, infinity) end); - error -> - not_found - end; -declare_fast_reply_to(_) -> - not_found. - -decode_fast_reply_to(Rest) -> - case string:tokens(binary_to_list(Rest), ".") of - [PidEnc, Key] -> Pid = binary_to_term(base64:decode(PidEnc)), - {ok, Pid, Key}; - _ -> error - end. - -send_credit_reply(Pid, Len) -> - gen_server2:cast(Pid, {send_credit_reply, Len}). - -send_drained(Pid, CTagCredit) -> - gen_server2:cast(Pid, {send_drained, CTagCredit}). - -list() -> - rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), - rabbit_channel, list_local, []). - -list_local() -> - pg_local:get_members(rabbit_channels). - -info_keys() -> ?INFO_KEYS. - -info(Pid) -> - gen_server2:call(Pid, info, infinity). - -info(Pid, Items) -> - case gen_server2:call(Pid, {info, Items}, infinity) of - {ok, Res} -> Res; - {error, Error} -> throw(Error) - end. - -info_all() -> - rabbit_misc:filter_exit_map(fun (C) -> info(C) end, list()). - -info_all(Items) -> - rabbit_misc:filter_exit_map(fun (C) -> info(C, Items) end, list()). - -refresh_config_local() -> - rabbit_misc:upmap( - fun (C) -> gen_server2:call(C, refresh_config, infinity) end, - list_local()), - ok. - -ready_for_close(Pid) -> - gen_server2:cast(Pid, ready_for_close). - -force_event_refresh(Ref) -> - [gen_server2:cast(C, {force_event_refresh, Ref}) || C <- list()], - ok. - -%%--------------------------------------------------------------------------- - -init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, - Capabilities, CollectorPid, LimiterPid]) -> - process_flag(trap_exit, true), - ?store_proc_name({ConnName, Channel}), - ok = pg_local:join(rabbit_channels, self()), - Flow = case rabbit_misc:get_env(rabbit, mirroring_flow_control, true) of - true -> flow; - false -> noflow - end, - State = #ch{state = starting, - protocol = Protocol, - channel = Channel, - reader_pid = ReaderPid, - writer_pid = WriterPid, - conn_pid = ConnPid, - conn_name = ConnName, - limiter = rabbit_limiter:new(LimiterPid), - tx = none, - next_tag = 1, - unacked_message_q = queue:new(), - user = User, - virtual_host = VHost, - most_recently_declared_queue = <<>>, - queue_names = dict:new(), - queue_monitors = pmon:new(), - consumer_mapping = dict:new(), - queue_consumers = dict:new(), - delivering_queues = sets:new(), - queue_collector_pid = CollectorPid, - confirm_enabled = false, - publish_seqno = 1, - unconfirmed = dtree:empty(), - confirmed = [], - mandatory = dtree:empty(), - capabilities = Capabilities, - trace_state = rabbit_trace:init(VHost), - consumer_prefetch = 0, - reply_consumer = none, - delivery_flow = Flow, - interceptor_state = undefined}, - State1 = State#ch{ - interceptor_state = rabbit_channel_interceptor:init(State)}, - State2 = rabbit_event:init_stats_timer(State1, #ch.stats_timer), - rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State2)), - rabbit_event:if_enabled(State2, #ch.stats_timer, - fun() -> emit_stats(State2) end), - {ok, State2, hibernate, - {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. - -prioritise_call(Msg, _From, _Len, _State) -> - case Msg of - info -> 9; - {info, _Items} -> 9; - _ -> 0 - end. - -prioritise_cast(Msg, _Len, _State) -> - case Msg of - {confirm, _MsgSeqNos, _QPid} -> 5; - {mandatory_received, _MsgSeqNo, _QPid} -> 5; - _ -> 0 - end. - -prioritise_info(Msg, _Len, _State) -> - case Msg of - emit_stats -> 7; - _ -> 0 - end. - -handle_call(flush, _From, State) -> - reply(ok, State); - -handle_call(info, _From, State) -> - reply(infos(?INFO_KEYS, State), State); - -handle_call({info, Items}, _From, State) -> - try - reply({ok, infos(Items, State)}, State) - catch Error -> reply({error, Error}, State) - end; - -handle_call(refresh_config, _From, State = #ch{virtual_host = VHost}) -> - reply(ok, State#ch{trace_state = rabbit_trace:init(VHost)}); - -handle_call({declare_fast_reply_to, Key}, _From, - State = #ch{reply_consumer = Consumer}) -> - reply(case Consumer of - {_, _, Key} -> exists; - _ -> not_found - end, State); - -handle_call(_Request, _From, State) -> - noreply(State). - -handle_cast({method, Method, Content, Flow}, - State = #ch{reader_pid = Reader, - interceptor_state = IState}) -> - case Flow of - %% We are going to process a message from the rabbit_reader - %% process, so here we ack it. In this case we are accessing - %% the rabbit_channel process dictionary. - flow -> credit_flow:ack(Reader); - noflow -> ok - end, - - try handle_method(rabbit_channel_interceptor:intercept_in( - expand_shortcuts(Method, State), Content, IState), - State) of - {reply, Reply, NewState} -> - ok = send(Reply, NewState), - noreply(NewState); - {noreply, NewState} -> - noreply(NewState); - stop -> - {stop, normal, State} - catch - exit:Reason = #amqp_error{} -> - MethodName = rabbit_misc:method_record_type(Method), - handle_exception(Reason#amqp_error{method = MethodName}, State); - _:Reason -> - {stop, {Reason, erlang:get_stacktrace()}, State} - end; - -handle_cast(ready_for_close, State = #ch{state = closing, - writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command_sync(WriterPid, #'channel.close_ok'{}), - {stop, normal, State}; - -handle_cast(terminate, State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:flush(WriterPid), - {stop, normal, State}; - -handle_cast({command, #'basic.consume_ok'{consumer_tag = CTag} = Msg}, State) -> - ok = send(Msg, State), - noreply(consumer_monitor(CTag, State)); - -handle_cast({command, Msg}, State) -> - ok = send(Msg, State), - noreply(State); - -handle_cast({deliver, _CTag, _AckReq, _Msg}, State = #ch{state = closing}) -> - noreply(State); -handle_cast({deliver, ConsumerTag, AckRequired, - Msg = {_QName, QPid, _MsgId, Redelivered, - #basic_message{exchange_name = ExchangeName, - routing_keys = [RoutingKey | _CcRoutes], - content = Content}}}, - State = #ch{writer_pid = WriterPid, - next_tag = DeliveryTag}) -> - ok = rabbit_writer:send_command_and_notify( - WriterPid, QPid, self(), - #'basic.deliver'{consumer_tag = ConsumerTag, - delivery_tag = DeliveryTag, - redelivered = Redelivered, - exchange = ExchangeName#resource.name, - routing_key = RoutingKey}, - Content), - rabbit_basic:maybe_gc_large_msg(Content), - noreply(record_sent(ConsumerTag, AckRequired, Msg, State)); - -handle_cast({deliver_reply, _K, _Del}, State = #ch{state = closing}) -> - noreply(State); -handle_cast({deliver_reply, _K, _Del}, State = #ch{reply_consumer = none}) -> - noreply(State); -handle_cast({deliver_reply, Key, #delivery{message = - #basic_message{exchange_name = ExchangeName, - routing_keys = [RoutingKey | _CcRoutes], - content = Content}}}, - State = #ch{writer_pid = WriterPid, - next_tag = DeliveryTag, - reply_consumer = {ConsumerTag, _Suffix, Key}}) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.deliver'{consumer_tag = ConsumerTag, - delivery_tag = DeliveryTag, - redelivered = false, - exchange = ExchangeName#resource.name, - routing_key = RoutingKey}, - Content), - noreply(State); -handle_cast({deliver_reply, _K1, _}, State=#ch{reply_consumer = {_, _, _K2}}) -> - noreply(State); - -handle_cast({send_credit_reply, Len}, State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.credit_ok'{available = Len}), - noreply(State); - -handle_cast({send_drained, CTagCredit}, State = #ch{writer_pid = WriterPid}) -> - [ok = rabbit_writer:send_command( - WriterPid, #'basic.credit_drained'{consumer_tag = ConsumerTag, - credit_drained = CreditDrained}) - || {ConsumerTag, CreditDrained} <- CTagCredit], - noreply(State); - -handle_cast({force_event_refresh, Ref}, State) -> - rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State), - Ref), - noreply(rabbit_event:init_stats_timer(State, #ch.stats_timer)); - -handle_cast({mandatory_received, MsgSeqNo}, State = #ch{mandatory = Mand}) -> - %% NB: don't call noreply/1 since we don't want to send confirms. - noreply_coalesce(State#ch{mandatory = dtree:drop(MsgSeqNo, Mand)}); - -handle_cast({confirm, MsgSeqNos, QPid}, State = #ch{unconfirmed = UC}) -> - {MXs, UC1} = dtree:take(MsgSeqNos, QPid, UC), - %% NB: don't call noreply/1 since we don't want to send confirms. - noreply_coalesce(record_confirms(MXs, State#ch{unconfirmed = UC1})). - -handle_info({bump_credit, Msg}, State) -> - %% A rabbit_amqqueue_process is granting credit to our channel. If - %% our channel was being blocked by this process, and no other - %% process is blocking our channel, then this channel will be - %% unblocked. This means that any credit that was deferred will be - %% sent to rabbit_reader processs that might be blocked by this - %% particular channel. - credit_flow:handle_bump_msg(Msg), - noreply(State); - -handle_info(timeout, State) -> - noreply(State); - -handle_info(emit_stats, State) -> - emit_stats(State), - State1 = rabbit_event:reset_stats_timer(State, #ch.stats_timer), - %% NB: don't call noreply/1 since we don't want to kick off the - %% stats timer. - {noreply, send_confirms(State1), hibernate}; - -handle_info({'DOWN', _MRef, process, QPid, Reason}, State) -> - State1 = handle_publishing_queue_down(QPid, Reason, State), - State3 = handle_consuming_queue_down(QPid, State1), - State4 = handle_delivering_queue_down(QPid, State3), - %% A rabbit_amqqueue_process has died. If our channel was being - %% blocked by this process, and no other process is blocking our - %% channel, then this channel will be unblocked. This means that - %% any credit that was deferred will be sent to the rabbit_reader - %% processs that might be blocked by this particular channel. - credit_flow:peer_down(QPid), - #ch{queue_names = QNames, queue_monitors = QMons} = State4, - case dict:find(QPid, QNames) of - {ok, QName} -> erase_queue_stats(QName); - error -> ok - end, - noreply(State4#ch{queue_names = dict:erase(QPid, QNames), - queue_monitors = pmon:erase(QPid, QMons)}); - -handle_info({'EXIT', _Pid, Reason}, State) -> - {stop, Reason, State}. - -handle_pre_hibernate(State) -> - ok = clear_permission_cache(), - rabbit_event:if_enabled( - State, #ch.stats_timer, - fun () -> emit_stats(State, - [{idle_since, - time_compat:os_system_time(milli_seconds)}]) - end), - {hibernate, rabbit_event:stop_stats_timer(State, #ch.stats_timer)}. - -terminate(Reason, State) -> - {Res, _State1} = notify_queues(State), - case Reason of - normal -> ok = Res; - shutdown -> ok = Res; - {shutdown, _Term} -> ok = Res; - _ -> ok - end, - pg_local:leave(rabbit_channels, self()), - rabbit_event:if_enabled(State, #ch.stats_timer, - fun() -> emit_stats(State) end), - rabbit_event:notify(channel_closed, [{pid, self()}]). - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -format_message_queue(Opt, MQ) -> rabbit_misc:format_message_queue(Opt, MQ). - -%%--------------------------------------------------------------------------- - -log(Level, Fmt, Args) -> rabbit_log:log(channel, Level, Fmt, Args). - -reply(Reply, NewState) -> {reply, Reply, next_state(NewState), hibernate}. - -noreply(NewState) -> {noreply, next_state(NewState), hibernate}. - -next_state(State) -> ensure_stats_timer(send_confirms(State)). - -noreply_coalesce(State = #ch{confirmed = C}) -> - Timeout = case C of [] -> hibernate; _ -> 0 end, - {noreply, ensure_stats_timer(State), Timeout}. - -ensure_stats_timer(State) -> - rabbit_event:ensure_stats_timer(State, #ch.stats_timer, emit_stats). - -return_ok(State, true, _Msg) -> {noreply, State}; -return_ok(State, false, Msg) -> {reply, Msg, State}. - -ok_msg(true, _Msg) -> undefined; -ok_msg(false, Msg) -> Msg. - -send(_Command, #ch{state = closing}) -> - ok; -send(Command, #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command(WriterPid, Command). - -handle_exception(Reason, State = #ch{protocol = Protocol, - channel = Channel, - writer_pid = WriterPid, - reader_pid = ReaderPid, - conn_pid = ConnPid, - conn_name = ConnName, - virtual_host = VHost, - user = User}) -> - %% something bad's happened: notify_queues may not be 'ok' - {_Result, State1} = notify_queues(State), - case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of - {Channel, CloseMethod} -> - log(error, "Channel error on connection ~p (~s, vhost: '~s'," - " user: '~s'), channel ~p:~n~p~n", - [ConnPid, ConnName, VHost, User#user.username, - Channel, Reason]), - ok = rabbit_writer:send_command(WriterPid, CloseMethod), - {noreply, State1}; - {0, _} -> - ReaderPid ! {channel_exit, Channel, Reason}, - {stop, normal, State1} - end. - --ifdef(use_specs). --spec(precondition_failed/1 :: (string()) -> no_return()). --endif. -precondition_failed(Format) -> precondition_failed(Format, []). - --ifdef(use_specs). --spec(precondition_failed/2 :: (string(), [any()]) -> no_return()). --endif. -precondition_failed(Format, Params) -> - rabbit_misc:protocol_error(precondition_failed, Format, Params). - -return_queue_declare_ok(#resource{name = ActualName}, - NoWait, MessageCount, ConsumerCount, State) -> - return_ok(State#ch{most_recently_declared_queue = ActualName}, NoWait, - #'queue.declare_ok'{queue = ActualName, - message_count = MessageCount, - consumer_count = ConsumerCount}). - -check_resource_access(User, Resource, Perm) -> - V = {Resource, Perm}, - Cache = case get(permission_cache) of - undefined -> []; - Other -> Other - end, - case lists:member(V, Cache) of - true -> ok; - false -> ok = rabbit_access_control:check_resource_access( - User, Resource, Perm), - CacheTail = lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE-1), - put(permission_cache, [V | CacheTail]) - end. - -clear_permission_cache() -> erase(permission_cache), - ok. - -check_configure_permitted(Resource, #ch{user = User}) -> - check_resource_access(User, Resource, configure). - -check_write_permitted(Resource, #ch{user = User}) -> - check_resource_access(User, Resource, write). - -check_read_permitted(Resource, #ch{user = User}) -> - check_resource_access(User, Resource, read). - -check_user_id_header(#'P_basic'{user_id = undefined}, _) -> - ok; -check_user_id_header(#'P_basic'{user_id = Username}, - #ch{user = #user{username = Username}}) -> - ok; -check_user_id_header( - #'P_basic'{}, #ch{user = #user{authz_backends = - [{rabbit_auth_backend_dummy, _}]}}) -> - ok; -check_user_id_header(#'P_basic'{user_id = Claimed}, - #ch{user = #user{username = Actual, - tags = Tags}}) -> - case lists:member(impersonator, Tags) of - true -> ok; - false -> precondition_failed( - "user_id property set to '~s' but authenticated user was " - "'~s'", [Claimed, Actual]) - end. - -check_expiration_header(Props) -> - case rabbit_basic:parse_expiration(Props) of - {ok, _} -> ok; - {error, E} -> precondition_failed("invalid expiration '~s': ~p", - [Props#'P_basic'.expiration, E]) - end. - -check_internal_exchange(#exchange{name = Name, internal = true}) -> - rabbit_misc:protocol_error(access_refused, - "cannot publish to internal ~s", - [rabbit_misc:rs(Name)]); -check_internal_exchange(_) -> - ok. - -check_msg_size(Content) -> - Size = rabbit_basic:maybe_gc_large_msg(Content), - case Size > ?MAX_MSG_SIZE of - true -> precondition_failed("message size ~B larger than max size ~B", - [Size, ?MAX_MSG_SIZE]); - false -> ok - end. - -qbin_to_resource(QueueNameBin, State) -> - name_to_resource(queue, QueueNameBin, State). - -name_to_resource(Type, NameBin, #ch{virtual_host = VHostPath}) -> - rabbit_misc:r(VHostPath, Type, NameBin). - -expand_queue_name_shortcut(<<>>, #ch{most_recently_declared_queue = <<>>}) -> - rabbit_misc:protocol_error(not_found, "no previously declared queue", []); -expand_queue_name_shortcut(<<>>, #ch{most_recently_declared_queue = MRDQ}) -> - MRDQ; -expand_queue_name_shortcut(QueueNameBin, _) -> - QueueNameBin. - -expand_routing_key_shortcut(<<>>, <<>>, - #ch{most_recently_declared_queue = <<>>}) -> - rabbit_misc:protocol_error(not_found, "no previously declared queue", []); -expand_routing_key_shortcut(<<>>, <<>>, - #ch{most_recently_declared_queue = MRDQ}) -> - MRDQ; -expand_routing_key_shortcut(_QueueNameBin, RoutingKey, _State) -> - RoutingKey. - -expand_shortcuts(#'basic.get' {queue = Q} = M, State) -> - M#'basic.get' {queue = expand_queue_name_shortcut(Q, State)}; -expand_shortcuts(#'basic.consume'{queue = Q} = M, State) -> - M#'basic.consume'{queue = expand_queue_name_shortcut(Q, State)}; -expand_shortcuts(#'queue.delete' {queue = Q} = M, State) -> - M#'queue.delete' {queue = expand_queue_name_shortcut(Q, State)}; -expand_shortcuts(#'queue.purge' {queue = Q} = M, State) -> - M#'queue.purge' {queue = expand_queue_name_shortcut(Q, State)}; -expand_shortcuts(#'queue.bind' {queue = Q, routing_key = K} = M, State) -> - M#'queue.bind' {queue = expand_queue_name_shortcut(Q, State), - routing_key = expand_routing_key_shortcut(Q, K, State)}; -expand_shortcuts(#'queue.unbind' {queue = Q, routing_key = K} = M, State) -> - M#'queue.unbind' {queue = expand_queue_name_shortcut(Q, State), - routing_key = expand_routing_key_shortcut(Q, K, State)}; -expand_shortcuts(M, _State) -> - M. - -check_not_default_exchange(#resource{kind = exchange, name = <<"">>}) -> - rabbit_misc:protocol_error( - access_refused, "operation not permitted on the default exchange", []); -check_not_default_exchange(_) -> - ok. - -check_exchange_deletion(XName = #resource{name = <<"amq.", _/binary>>, - kind = exchange}) -> - rabbit_misc:protocol_error( - access_refused, "deletion of system ~s not allowed", - [rabbit_misc:rs(XName)]); -check_exchange_deletion(_) -> - ok. - -%% check that an exchange/queue name does not contain the reserved -%% "amq." prefix. -%% -%% As per the AMQP 0-9-1 spec, the exclusion of "amq." prefixed names -%% only applies on actual creation, and not in the cases where the -%% entity already exists or passive=true. -%% -%% NB: We deliberately do not enforce the other constraints on names -%% required by the spec. -check_name(Kind, NameBin = <<"amq.", _/binary>>) -> - rabbit_misc:protocol_error( - access_refused, - "~s name '~s' contains reserved prefix 'amq.*'",[Kind, NameBin]); -check_name(_Kind, NameBin) -> - NameBin. - -maybe_set_fast_reply_to( - C = #content{properties = P = #'P_basic'{reply_to = - <<"amq.rabbitmq.reply-to">>}}, - #ch{reply_consumer = ReplyConsumer}) -> - case ReplyConsumer of - none -> rabbit_misc:protocol_error( - precondition_failed, - "fast reply consumer does not exist", []); - {_, Suf, _K} -> Rep = <<"amq.rabbitmq.reply-to.", Suf/binary>>, - rabbit_binary_generator:clear_encoded_content( - C#content{properties = P#'P_basic'{reply_to = Rep}}) - end; -maybe_set_fast_reply_to(C, _State) -> - C. - -record_confirms([], State) -> - State; -record_confirms(MXs, State = #ch{confirmed = C}) -> - State#ch{confirmed = [MXs | C]}. - -handle_method({Method, Content}, State) -> - handle_method(Method, Content, State). - -handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> - %% Don't leave "starting" as the state for 5s. TODO is this TRTTD? - State1 = State#ch{state = running}, - rabbit_event:if_enabled(State1, #ch.stats_timer, - fun() -> emit_stats(State1) end), - {reply, #'channel.open_ok'{}, State1}; - -handle_method(#'channel.open'{}, _, _State) -> - rabbit_misc:protocol_error( - command_invalid, "second 'channel.open' seen", []); - -handle_method(_Method, _, #ch{state = starting}) -> - rabbit_misc:protocol_error(channel_error, "expected 'channel.open'", []); - -handle_method(#'channel.close_ok'{}, _, #ch{state = closing}) -> - stop; - -handle_method(#'channel.close'{}, _, State = #ch{writer_pid = WriterPid, - state = closing}) -> - ok = rabbit_writer:send_command(WriterPid, #'channel.close_ok'{}), - {noreply, State}; - -handle_method(_Method, _, State = #ch{state = closing}) -> - {noreply, State}; - -handle_method(#'channel.close'{}, _, State = #ch{reader_pid = ReaderPid}) -> - {ok, State1} = notify_queues(State), - %% We issue the channel.close_ok response after a handshake with - %% the reader, the other half of which is ready_for_close. That - %% way the reader forgets about the channel before we send the - %% response (and this channel process terminates). If we didn't do - %% that, a channel.open for the same channel number, which a - %% client is entitled to send as soon as it has received the - %% close_ok, might be received by the reader before it has seen - %% the termination and hence be sent to the old, now dead/dying - %% channel process, instead of a new process, and thus lost. - ReaderPid ! {channel_closing, self()}, - {noreply, State1}; - -%% Even though the spec prohibits the client from sending commands -%% while waiting for the reply to a synchronous command, we generally -%% do allow this...except in the case of a pending tx.commit, where -%% it could wreak havoc. -handle_method(_Method, _, #ch{tx = Tx}) - when Tx =:= committing orelse Tx =:= failed -> - rabbit_misc:protocol_error( - channel_error, "unexpected command while processing 'tx.commit'", []); - -handle_method(#'access.request'{},_, State) -> - {reply, #'access.request_ok'{ticket = 1}, State}; - -handle_method(#'basic.publish'{immediate = true}, _Content, _State) -> - rabbit_misc:protocol_error(not_implemented, "immediate=true", []); - -handle_method(#'basic.publish'{exchange = ExchangeNameBin, - routing_key = RoutingKey, - mandatory = Mandatory}, - Content, State = #ch{virtual_host = VHostPath, - tx = Tx, - channel = ChannelNum, - confirm_enabled = ConfirmEnabled, - trace_state = TraceState, - user = #user{username = Username}, - conn_name = ConnName, - delivery_flow = Flow}) -> - check_msg_size(Content), - ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), - check_write_permitted(ExchangeName, State), - Exchange = rabbit_exchange:lookup_or_die(ExchangeName), - check_internal_exchange(Exchange), - %% We decode the content's properties here because we're almost - %% certain to want to look at delivery-mode and priority. - DecodedContent = #content {properties = Props} = - maybe_set_fast_reply_to( - rabbit_binary_parser:ensure_content_decoded(Content), State), - check_user_id_header(Props, State), - check_expiration_header(Props), - DoConfirm = Tx =/= none orelse ConfirmEnabled, - {MsgSeqNo, State1} = - case DoConfirm orelse Mandatory of - false -> {undefined, State}; - true -> SeqNo = State#ch.publish_seqno, - {SeqNo, State#ch{publish_seqno = SeqNo + 1}} - end, - case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of - {ok, Message} -> - Delivery = rabbit_basic:delivery( - Mandatory, DoConfirm, Message, MsgSeqNo), - QNames = rabbit_exchange:route(Exchange, Delivery), - rabbit_trace:tap_in(Message, QNames, ConnName, ChannelNum, - Username, TraceState), - DQ = {Delivery#delivery{flow = Flow}, QNames}, - {noreply, case Tx of - none -> deliver_to_queues(DQ, State1); - {Msgs, Acks} -> Msgs1 = queue:in(DQ, Msgs), - State1#ch{tx = {Msgs1, Acks}} - end}; - {error, Reason} -> - precondition_failed("invalid message: ~p", [Reason]) - end; - -handle_method(#'basic.nack'{delivery_tag = DeliveryTag, - multiple = Multiple, - requeue = Requeue}, _, State) -> - reject(DeliveryTag, Requeue, Multiple, State); - -handle_method(#'basic.ack'{delivery_tag = DeliveryTag, - multiple = Multiple}, - _, State = #ch{unacked_message_q = UAMQ, tx = Tx}) -> - {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), - State1 = State#ch{unacked_message_q = Remaining}, - {noreply, case Tx of - none -> ack(Acked, State1), - State1; - {Msgs, Acks} -> Acks1 = ack_cons(ack, Acked, Acks), - State1#ch{tx = {Msgs, Acks1}} - end}; - -handle_method(#'basic.get'{queue = QueueNameBin, no_ack = NoAck}, - _, State = #ch{writer_pid = WriterPid, - conn_pid = ConnPid, - limiter = Limiter, - next_tag = DeliveryTag}) -> - QueueName = qbin_to_resource(QueueNameBin, State), - check_read_permitted(QueueName, State), - case rabbit_amqqueue:with_exclusive_access_or_die( - QueueName, ConnPid, - fun (Q) -> rabbit_amqqueue:basic_get( - Q, self(), NoAck, rabbit_limiter:pid(Limiter)) - end) of - {ok, MessageCount, - Msg = {QName, QPid, _MsgId, Redelivered, - #basic_message{exchange_name = ExchangeName, - routing_keys = [RoutingKey | _CcRoutes], - content = Content}}} -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.get_ok'{delivery_tag = DeliveryTag, - redelivered = Redelivered, - exchange = ExchangeName#resource.name, - routing_key = RoutingKey, - message_count = MessageCount}, - Content), - State1 = monitor_delivering_queue(NoAck, QPid, QName, State), - {noreply, record_sent(none, not(NoAck), Msg, State1)}; - empty -> - {reply, #'basic.get_empty'{}, State} - end; - -handle_method(#'basic.consume'{queue = <<"amq.rabbitmq.reply-to">>, - consumer_tag = CTag0, - no_ack = NoAck, - nowait = NoWait}, - _, State = #ch{reply_consumer = ReplyConsumer, - consumer_mapping = ConsumerMapping}) -> - case dict:find(CTag0, ConsumerMapping) of - error -> - case {ReplyConsumer, NoAck} of - {none, true} -> - CTag = case CTag0 of - <<>> -> rabbit_guid:binary( - rabbit_guid:gen_secure(), "amq.ctag"); - Other -> Other - end, - %% Precalculate both suffix and key; base64 encoding is - %% expensive - Key = base64:encode(rabbit_guid:gen_secure()), - PidEnc = base64:encode(term_to_binary(self())), - Suffix = <<PidEnc/binary, ".", Key/binary>>, - Consumer = {CTag, Suffix, binary_to_list(Key)}, - State1 = State#ch{reply_consumer = Consumer}, - case NoWait of - true -> {noreply, State1}; - false -> Rep = #'basic.consume_ok'{consumer_tag = CTag}, - {reply, Rep, State1} - end; - {_, false} -> - rabbit_misc:protocol_error( - precondition_failed, - "reply consumer cannot acknowledge", []); - _ -> - rabbit_misc:protocol_error( - precondition_failed, "reply consumer already set", []) - end; - {ok, _} -> - %% Attempted reuse of consumer tag. - rabbit_misc:protocol_error( - not_allowed, "attempt to reuse consumer tag '~s'", [CTag0]) - end; - -handle_method(#'basic.cancel'{consumer_tag = ConsumerTag, nowait = NoWait}, - _, State = #ch{reply_consumer = {ConsumerTag, _, _}}) -> - State1 = State#ch{reply_consumer = none}, - case NoWait of - true -> {noreply, State1}; - false -> Rep = #'basic.cancel_ok'{consumer_tag = ConsumerTag}, - {reply, Rep, State1} - end; - -handle_method(#'basic.consume'{queue = QueueNameBin, - consumer_tag = ConsumerTag, - no_local = _, % FIXME: implement - no_ack = NoAck, - exclusive = ExclusiveConsume, - nowait = NoWait, - arguments = Args}, - _, State = #ch{consumer_prefetch = ConsumerPrefetch, - consumer_mapping = ConsumerMapping}) -> - case dict:find(ConsumerTag, ConsumerMapping) of - error -> - QueueName = qbin_to_resource(QueueNameBin, State), - check_read_permitted(QueueName, State), - ActualConsumerTag = - case ConsumerTag of - <<>> -> rabbit_guid:binary(rabbit_guid:gen_secure(), - "amq.ctag"); - Other -> Other - end, - case basic_consume( - QueueName, NoAck, ConsumerPrefetch, ActualConsumerTag, - ExclusiveConsume, Args, NoWait, State) of - {ok, State1} -> - {noreply, State1}; - {error, exclusive_consume_unavailable} -> - rabbit_misc:protocol_error( - access_refused, "~s in exclusive use", - [rabbit_misc:rs(QueueName)]) - end; - {ok, _} -> - %% Attempted reuse of consumer tag. - rabbit_misc:protocol_error( - not_allowed, "attempt to reuse consumer tag '~s'", [ConsumerTag]) - end; - -handle_method(#'basic.cancel'{consumer_tag = ConsumerTag, nowait = NoWait}, - _, State = #ch{consumer_mapping = ConsumerMapping, - queue_consumers = QCons}) -> - OkMsg = #'basic.cancel_ok'{consumer_tag = ConsumerTag}, - case dict:find(ConsumerTag, ConsumerMapping) of - error -> - %% Spec requires we ignore this situation. - return_ok(State, NoWait, OkMsg); - {ok, {Q = #amqqueue{pid = QPid}, _CParams}} -> - ConsumerMapping1 = dict:erase(ConsumerTag, ConsumerMapping), - QCons1 = - case dict:find(QPid, QCons) of - error -> QCons; - {ok, CTags} -> CTags1 = gb_sets:delete(ConsumerTag, CTags), - case gb_sets:is_empty(CTags1) of - true -> dict:erase(QPid, QCons); - false -> dict:store(QPid, CTags1, QCons) - end - end, - NewState = State#ch{consumer_mapping = ConsumerMapping1, - queue_consumers = QCons1}, - %% In order to ensure that no more messages are sent to - %% the consumer after the cancel_ok has been sent, we get - %% the queue process to send the cancel_ok on our - %% behalf. If we were sending the cancel_ok ourselves it - %% might overtake a message sent previously by the queue. - case rabbit_misc:with_exit_handler( - fun () -> {error, not_found} end, - fun () -> - rabbit_amqqueue:basic_cancel( - Q, self(), ConsumerTag, ok_msg(NoWait, OkMsg)) - end) of - ok -> - {noreply, NewState}; - {error, not_found} -> - %% Spec requires we ignore this situation. - return_ok(NewState, NoWait, OkMsg) - end - end; - -handle_method(#'basic.qos'{prefetch_size = Size}, _, _State) when Size /= 0 -> - rabbit_misc:protocol_error(not_implemented, - "prefetch_size!=0 (~w)", [Size]); - -handle_method(#'basic.qos'{global = false, - prefetch_count = PrefetchCount}, _, State) -> - {reply, #'basic.qos_ok'{}, State#ch{consumer_prefetch = PrefetchCount}}; - -handle_method(#'basic.qos'{global = true, - prefetch_count = 0}, - _, State = #ch{limiter = Limiter}) -> - Limiter1 = rabbit_limiter:unlimit_prefetch(Limiter), - {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter1}}; - -handle_method(#'basic.qos'{global = true, - prefetch_count = PrefetchCount}, - _, State = #ch{limiter = Limiter, unacked_message_q = UAMQ}) -> - %% TODO queue:len(UAMQ) is not strictly right since that counts - %% unacked messages from basic.get too. Pretty obscure though. - Limiter1 = rabbit_limiter:limit_prefetch(Limiter, - PrefetchCount, queue:len(UAMQ)), - case ((not rabbit_limiter:is_active(Limiter)) andalso - rabbit_limiter:is_active(Limiter1)) of - true -> rabbit_amqqueue:activate_limit_all( - consumer_queues(State#ch.consumer_mapping), self()); - false -> ok - end, - {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter1}}; - -handle_method(#'basic.recover_async'{requeue = true}, - _, State = #ch{unacked_message_q = UAMQ, limiter = Limiter}) -> - OkFun = fun () -> ok end, - UAMQL = queue:to_list(UAMQ), - foreach_per_queue( - fun (QPid, MsgIds) -> - rabbit_misc:with_exit_handler( - OkFun, - fun () -> rabbit_amqqueue:requeue(QPid, MsgIds, self()) end) - end, lists:reverse(UAMQL)), - ok = notify_limiter(Limiter, UAMQL), - %% No answer required - basic.recover is the newer, synchronous - %% variant of this method - {noreply, State#ch{unacked_message_q = queue:new()}}; - -handle_method(#'basic.recover_async'{requeue = false}, _, _State) -> - rabbit_misc:protocol_error(not_implemented, "requeue=false", []); - -handle_method(#'basic.recover'{requeue = Requeue}, Content, State) -> - {noreply, State1} = handle_method(#'basic.recover_async'{requeue = Requeue}, - Content, State), - {reply, #'basic.recover_ok'{}, State1}; - -handle_method(#'basic.reject'{delivery_tag = DeliveryTag, requeue = Requeue}, - _, State) -> - reject(DeliveryTag, Requeue, false, State); - -handle_method(#'exchange.declare'{exchange = ExchangeNameBin, - type = TypeNameBin, - passive = false, - durable = Durable, - auto_delete = AutoDelete, - internal = Internal, - nowait = NoWait, - arguments = Args}, - _, State = #ch{virtual_host = VHostPath}) -> - CheckedType = rabbit_exchange:check_type(TypeNameBin), - ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), - check_not_default_exchange(ExchangeName), - check_configure_permitted(ExchangeName, State), - X = case rabbit_exchange:lookup(ExchangeName) of - {ok, FoundX} -> FoundX; - {error, not_found} -> - check_name('exchange', ExchangeNameBin), - AeKey = <<"alternate-exchange">>, - case rabbit_misc:r_arg(VHostPath, exchange, Args, AeKey) of - undefined -> ok; - {error, {invalid_type, Type}} -> - precondition_failed( - "invalid type '~s' for arg '~s' in ~s", - [Type, AeKey, rabbit_misc:rs(ExchangeName)]); - AName -> check_read_permitted(ExchangeName, State), - check_write_permitted(AName, State), - ok - end, - rabbit_exchange:declare(ExchangeName, - CheckedType, - Durable, - AutoDelete, - Internal, - Args) - end, - ok = rabbit_exchange:assert_equivalence(X, CheckedType, Durable, - AutoDelete, Internal, Args), - return_ok(State, NoWait, #'exchange.declare_ok'{}); - -handle_method(#'exchange.declare'{exchange = ExchangeNameBin, - passive = true, - nowait = NoWait}, - _, State = #ch{virtual_host = VHostPath}) -> - ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), - check_not_default_exchange(ExchangeName), - _ = rabbit_exchange:lookup_or_die(ExchangeName), - return_ok(State, NoWait, #'exchange.declare_ok'{}); - -handle_method(#'exchange.delete'{exchange = ExchangeNameBin, - if_unused = IfUnused, - nowait = NoWait}, - _, State = #ch{virtual_host = VHostPath}) -> - ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), - check_not_default_exchange(ExchangeName), - check_exchange_deletion(ExchangeName), - check_configure_permitted(ExchangeName, State), - case rabbit_exchange:delete(ExchangeName, IfUnused) of - {error, not_found} -> - return_ok(State, NoWait, #'exchange.delete_ok'{}); - {error, in_use} -> - precondition_failed("~s in use", [rabbit_misc:rs(ExchangeName)]); - ok -> - return_ok(State, NoWait, #'exchange.delete_ok'{}) - end; - -handle_method(#'exchange.bind'{destination = DestinationNameBin, - source = SourceNameBin, - routing_key = RoutingKey, - nowait = NoWait, - arguments = Arguments}, _, State) -> - binding_action(fun rabbit_binding:add/2, - SourceNameBin, exchange, DestinationNameBin, RoutingKey, - Arguments, #'exchange.bind_ok'{}, NoWait, State); - -handle_method(#'exchange.unbind'{destination = DestinationNameBin, - source = SourceNameBin, - routing_key = RoutingKey, - nowait = NoWait, - arguments = Arguments}, _, State) -> - binding_action(fun rabbit_binding:remove/2, - SourceNameBin, exchange, DestinationNameBin, RoutingKey, - Arguments, #'exchange.unbind_ok'{}, NoWait, State); - -%% Note that all declares to these are effectively passive. If it -%% exists it by definition has one consumer. -handle_method(#'queue.declare'{queue = <<"amq.rabbitmq.reply-to", - _/binary>> = QueueNameBin, - nowait = NoWait}, _, - State = #ch{virtual_host = VHost}) -> - QueueName = rabbit_misc:r(VHost, queue, QueueNameBin), - case declare_fast_reply_to(QueueNameBin) of - exists -> return_queue_declare_ok(QueueName, NoWait, 0, 1, State); - not_found -> rabbit_misc:not_found(QueueName) - end; - -handle_method(#'queue.declare'{queue = QueueNameBin, - passive = false, - durable = DurableDeclare, - exclusive = ExclusiveDeclare, - auto_delete = AutoDelete, - nowait = NoWait, - arguments = Args} = Declare, - _, State = #ch{virtual_host = VHostPath, - conn_pid = ConnPid, - queue_collector_pid = CollectorPid}) -> - Owner = case ExclusiveDeclare of - true -> ConnPid; - false -> none - end, - Durable = DurableDeclare andalso not ExclusiveDeclare, - ActualNameBin = case QueueNameBin of - <<>> -> rabbit_guid:binary(rabbit_guid:gen_secure(), - "amq.gen"); - Other -> check_name('queue', Other) - end, - QueueName = rabbit_misc:r(VHostPath, queue, ActualNameBin), - check_configure_permitted(QueueName, State), - case rabbit_amqqueue:with( - QueueName, - fun (Q) -> ok = rabbit_amqqueue:assert_equivalence( - Q, Durable, AutoDelete, Args, Owner), - maybe_stat(NoWait, Q) - end) of - {ok, MessageCount, ConsumerCount} -> - return_queue_declare_ok(QueueName, NoWait, MessageCount, - ConsumerCount, State); - {error, not_found} -> - DlxKey = <<"x-dead-letter-exchange">>, - case rabbit_misc:r_arg(VHostPath, exchange, Args, DlxKey) of - undefined -> - ok; - {error, {invalid_type, Type}} -> - precondition_failed( - "invalid type '~s' for arg '~s' in ~s", - [Type, DlxKey, rabbit_misc:rs(QueueName)]); - DLX -> - check_read_permitted(QueueName, State), - check_write_permitted(DLX, State), - ok - end, - case rabbit_amqqueue:declare(QueueName, Durable, AutoDelete, - Args, Owner) of - {new, #amqqueue{pid = QPid}} -> - %% We need to notify the reader within the channel - %% process so that we can be sure there are no - %% outstanding exclusive queues being declared as - %% the connection shuts down. - ok = case Owner of - none -> ok; - _ -> rabbit_queue_collector:register( - CollectorPid, QPid) - end, - return_queue_declare_ok(QueueName, NoWait, 0, 0, State); - {existing, _Q} -> - %% must have been created between the stat and the - %% declare. Loop around again. - handle_method(Declare, none, State); - {absent, Q, Reason} -> - rabbit_misc:absent(Q, Reason); - {owner_died, _Q} -> - %% Presumably our own days are numbered since the - %% connection has died. Pretend the queue exists though, - %% just so nothing fails. - return_queue_declare_ok(QueueName, NoWait, 0, 0, State) - end; - {error, {absent, Q, Reason}} -> - rabbit_misc:absent(Q, Reason) - end; - -handle_method(#'queue.declare'{queue = QueueNameBin, - passive = true, - nowait = NoWait}, - _, State = #ch{virtual_host = VHostPath, - conn_pid = ConnPid}) -> - QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), - {{ok, MessageCount, ConsumerCount}, #amqqueue{} = Q} = - rabbit_amqqueue:with_or_die( - QueueName, fun (Q) -> {maybe_stat(NoWait, Q), Q} end), - ok = rabbit_amqqueue:check_exclusive_access(Q, ConnPid), - return_queue_declare_ok(QueueName, NoWait, MessageCount, ConsumerCount, - State); - -handle_method(#'queue.delete'{queue = QueueNameBin, - if_unused = IfUnused, - if_empty = IfEmpty, - nowait = NoWait}, - _, State = #ch{conn_pid = ConnPid}) -> - QueueName = qbin_to_resource(QueueNameBin, State), - check_configure_permitted(QueueName, State), - case rabbit_amqqueue:with( - QueueName, - fun (Q) -> - rabbit_amqqueue:check_exclusive_access(Q, ConnPid), - rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) - end, - fun (not_found) -> {ok, 0}; - ({absent, Q, crashed}) -> rabbit_amqqueue:delete_crashed(Q), - {ok, 0}; - ({absent, Q, Reason}) -> rabbit_misc:absent(Q, Reason) - end) of - {error, in_use} -> - precondition_failed("~s in use", [rabbit_misc:rs(QueueName)]); - {error, not_empty} -> - precondition_failed("~s not empty", [rabbit_misc:rs(QueueName)]); - {ok, PurgedMessageCount} -> - return_ok(State, NoWait, - #'queue.delete_ok'{message_count = PurgedMessageCount}) - end; - -handle_method(#'queue.bind'{queue = QueueNameBin, - exchange = ExchangeNameBin, - routing_key = RoutingKey, - nowait = NoWait, - arguments = Arguments}, _, State) -> - binding_action(fun rabbit_binding:add/2, - ExchangeNameBin, queue, QueueNameBin, RoutingKey, Arguments, - #'queue.bind_ok'{}, NoWait, State); - -handle_method(#'queue.unbind'{queue = QueueNameBin, - exchange = ExchangeNameBin, - routing_key = RoutingKey, - arguments = Arguments}, _, State) -> - binding_action(fun rabbit_binding:remove/2, - ExchangeNameBin, queue, QueueNameBin, RoutingKey, Arguments, - #'queue.unbind_ok'{}, false, State); - -handle_method(#'queue.purge'{queue = QueueNameBin, nowait = NoWait}, - _, State = #ch{conn_pid = ConnPid}) -> - QueueName = qbin_to_resource(QueueNameBin, State), - check_read_permitted(QueueName, State), - {ok, PurgedMessageCount} = rabbit_amqqueue:with_exclusive_access_or_die( - QueueName, ConnPid, - fun (Q) -> rabbit_amqqueue:purge(Q) end), - return_ok(State, NoWait, - #'queue.purge_ok'{message_count = PurgedMessageCount}); - -handle_method(#'tx.select'{}, _, #ch{confirm_enabled = true}) -> - precondition_failed("cannot switch from confirm to tx mode"); - -handle_method(#'tx.select'{}, _, State = #ch{tx = none}) -> - {reply, #'tx.select_ok'{}, State#ch{tx = new_tx()}}; - -handle_method(#'tx.select'{}, _, State) -> - {reply, #'tx.select_ok'{}, State}; - -handle_method(#'tx.commit'{}, _, #ch{tx = none}) -> - precondition_failed("channel is not transactional"); - -handle_method(#'tx.commit'{}, _, State = #ch{tx = {Msgs, Acks}, - limiter = Limiter}) -> - State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), - Rev = fun (X) -> lists:reverse(lists:sort(X)) end, - lists:foreach(fun ({ack, A}) -> ack(Rev(A), State1); - ({Requeue, A}) -> reject(Requeue, Rev(A), Limiter) - end, lists:reverse(Acks)), - {noreply, maybe_complete_tx(State1#ch{tx = committing})}; - -handle_method(#'tx.rollback'{}, _, #ch{tx = none}) -> - precondition_failed("channel is not transactional"); - -handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, - tx = {_Msgs, Acks}}) -> - AcksL = lists:append(lists:reverse([lists:reverse(L) || {_, L} <- Acks])), - UAMQ1 = queue:from_list(lists:usort(AcksL ++ queue:to_list(UAMQ))), - {reply, #'tx.rollback_ok'{}, State#ch{unacked_message_q = UAMQ1, - tx = new_tx()}}; - -handle_method(#'confirm.select'{}, _, #ch{tx = {_, _}}) -> - precondition_failed("cannot switch from tx to confirm mode"); - -handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> - return_ok(State#ch{confirm_enabled = true}, - NoWait, #'confirm.select_ok'{}); - -handle_method(#'channel.flow'{active = true}, _, State) -> - {reply, #'channel.flow_ok'{active = true}, State}; - -handle_method(#'channel.flow'{active = false}, _, _State) -> - rabbit_misc:protocol_error(not_implemented, "active=false", []); - -handle_method(#'basic.credit'{consumer_tag = CTag, - credit = Credit, - drain = Drain}, - _, State = #ch{consumer_mapping = Consumers}) -> - case dict:find(CTag, Consumers) of - {ok, {Q, _CParams}} -> ok = rabbit_amqqueue:credit( - Q, self(), CTag, Credit, Drain), - {noreply, State}; - error -> precondition_failed( - "unknown consumer tag '~s'", [CTag]) - end; - -handle_method(_MethodRecord, _Content, _State) -> - rabbit_misc:protocol_error( - command_invalid, "unimplemented method", []). - -%%---------------------------------------------------------------------------- - -%% We get the queue process to send the consume_ok on our behalf. This -%% is for symmetry with basic.cancel - see the comment in that method -%% for why. -basic_consume(QueueName, NoAck, ConsumerPrefetch, ActualConsumerTag, - ExclusiveConsume, Args, NoWait, - State = #ch{conn_pid = ConnPid, - limiter = Limiter, - consumer_mapping = ConsumerMapping}) -> - case rabbit_amqqueue:with_exclusive_access_or_die( - QueueName, ConnPid, - fun (Q) -> - {rabbit_amqqueue:basic_consume( - Q, NoAck, self(), - rabbit_limiter:pid(Limiter), - rabbit_limiter:is_active(Limiter), - ConsumerPrefetch, ActualConsumerTag, - ExclusiveConsume, Args, - ok_msg(NoWait, #'basic.consume_ok'{ - consumer_tag = ActualConsumerTag})), - Q} - end) of - {ok, Q = #amqqueue{pid = QPid, name = QName}} -> - CM1 = dict:store( - ActualConsumerTag, - {Q, {NoAck, ConsumerPrefetch, ExclusiveConsume, Args}}, - ConsumerMapping), - State1 = monitor_delivering_queue( - NoAck, QPid, QName, - State#ch{consumer_mapping = CM1}), - {ok, case NoWait of - true -> consumer_monitor(ActualConsumerTag, State1); - false -> State1 - end}; - {{error, exclusive_consume_unavailable} = E, _Q} -> - E - end. - -maybe_stat(false, Q) -> rabbit_amqqueue:stat(Q); -maybe_stat(true, _Q) -> {ok, 0, 0}. - -consumer_monitor(ConsumerTag, - State = #ch{consumer_mapping = ConsumerMapping, - queue_monitors = QMons, - queue_consumers = QCons}) -> - {#amqqueue{pid = QPid}, _CParams} = - dict:fetch(ConsumerTag, ConsumerMapping), - QCons1 = dict:update(QPid, fun (CTags) -> - gb_sets:insert(ConsumerTag, CTags) - end, - gb_sets:singleton(ConsumerTag), QCons), - State#ch{queue_monitors = pmon:monitor(QPid, QMons), - queue_consumers = QCons1}. - -monitor_delivering_queue(NoAck, QPid, QName, - State = #ch{queue_names = QNames, - queue_monitors = QMons, - delivering_queues = DQ}) -> - State#ch{queue_names = dict:store(QPid, QName, QNames), - queue_monitors = pmon:monitor(QPid, QMons), - delivering_queues = case NoAck of - true -> DQ; - false -> sets:add_element(QPid, DQ) - end}. - -handle_publishing_queue_down(QPid, Reason, State = #ch{unconfirmed = UC, - mandatory = Mand}) -> - {MMsgs, Mand1} = dtree:take(QPid, Mand), - [basic_return(Msg, State, no_route) || {_, Msg} <- MMsgs], - State1 = State#ch{mandatory = Mand1}, - case rabbit_misc:is_abnormal_exit(Reason) of - true -> {MXs, UC1} = dtree:take_all(QPid, UC), - send_nacks(MXs, State1#ch{unconfirmed = UC1}); - false -> {MXs, UC1} = dtree:take(QPid, UC), - record_confirms(MXs, State1#ch{unconfirmed = UC1}) - - end. - -handle_consuming_queue_down(QPid, State = #ch{queue_consumers = QCons, - queue_names = QNames}) -> - ConsumerTags = case dict:find(QPid, QCons) of - error -> gb_sets:new(); - {ok, CTags} -> CTags - end, - gb_sets:fold( - fun (CTag, StateN = #ch{consumer_mapping = CMap}) -> - QName = dict:fetch(QPid, QNames), - case queue_down_consumer_action(CTag, CMap) of - remove -> - cancel_consumer(CTag, QName, StateN); - {recover, {NoAck, ConsumerPrefetch, Exclusive, Args}} -> - case catch basic_consume( %% [0] - QName, NoAck, ConsumerPrefetch, CTag, - Exclusive, Args, true, StateN) of - {ok, StateN1} -> StateN1; - _ -> cancel_consumer(CTag, QName, StateN) - end - end - end, State#ch{queue_consumers = dict:erase(QPid, QCons)}, ConsumerTags). - -%% [0] There is a slight danger here that if a queue is deleted and -%% then recreated again the reconsume will succeed even though it was -%% not an HA failover. But the likelihood is not great and most users -%% are unlikely to care. - -cancel_consumer(CTag, QName, State = #ch{capabilities = Capabilities, - consumer_mapping = CMap}) -> - case rabbit_misc:table_lookup( - Capabilities, <<"consumer_cancel_notify">>) of - {bool, true} -> ok = send(#'basic.cancel'{consumer_tag = CTag, - nowait = true}, State); - _ -> ok - end, - rabbit_event:notify(consumer_deleted, [{consumer_tag, CTag}, - {channel, self()}, - {queue, QName}]), - State#ch{consumer_mapping = dict:erase(CTag, CMap)}. - -queue_down_consumer_action(CTag, CMap) -> - {_, {_, _, _, Args} = ConsumeSpec} = dict:fetch(CTag, CMap), - case rabbit_misc:table_lookup(Args, <<"x-cancel-on-ha-failover">>) of - {bool, true} -> remove; - _ -> {recover, ConsumeSpec} - end. - -handle_delivering_queue_down(QPid, State = #ch{delivering_queues = DQ}) -> - State#ch{delivering_queues = sets:del_element(QPid, DQ)}. - -binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, - RoutingKey, Arguments, ReturnMethod, NoWait, - State = #ch{virtual_host = VHostPath, - conn_pid = ConnPid }) -> - DestinationName = name_to_resource(DestinationType, DestinationNameBin, State), - check_write_permitted(DestinationName, State), - ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), - [check_not_default_exchange(N) || N <- [DestinationName, ExchangeName]], - check_read_permitted(ExchangeName, State), - case Fun(#binding{source = ExchangeName, - destination = DestinationName, - key = RoutingKey, - args = Arguments}, - fun (_X, Q = #amqqueue{}) -> - try rabbit_amqqueue:check_exclusive_access(Q, ConnPid) - catch exit:Reason -> {error, Reason} - end; - (_X, #exchange{}) -> - ok - end) of - {error, {resources_missing, [{not_found, Name} | _]}} -> - rabbit_misc:not_found(Name); - {error, {resources_missing, [{absent, Q, Reason} | _]}} -> - rabbit_misc:absent(Q, Reason); - {error, binding_not_found} -> - rabbit_misc:protocol_error( - not_found, "no binding ~s between ~s and ~s", - [RoutingKey, rabbit_misc:rs(ExchangeName), - rabbit_misc:rs(DestinationName)]); - {error, {binding_invalid, Fmt, Args}} -> - rabbit_misc:protocol_error(precondition_failed, Fmt, Args); - {error, #amqp_error{} = Error} -> - rabbit_misc:protocol_error(Error); - ok -> return_ok(State, NoWait, ReturnMethod) - end. - -basic_return(#basic_message{exchange_name = ExchangeName, - routing_keys = [RoutingKey | _CcRoutes], - content = Content}, - State = #ch{protocol = Protocol, writer_pid = WriterPid}, - Reason) -> - ?INCR_STATS([{exchange_stats, ExchangeName, 1}], return_unroutable, State), - {_Close, ReplyCode, ReplyText} = Protocol:lookup_amqp_exception(Reason), - ok = rabbit_writer:send_command( - WriterPid, - #'basic.return'{reply_code = ReplyCode, - reply_text = ReplyText, - exchange = ExchangeName#resource.name, - routing_key = RoutingKey}, - Content). - -reject(DeliveryTag, Requeue, Multiple, - State = #ch{unacked_message_q = UAMQ, tx = Tx}) -> - {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), - State1 = State#ch{unacked_message_q = Remaining}, - {noreply, case Tx of - none -> reject(Requeue, Acked, State1#ch.limiter), - State1; - {Msgs, Acks} -> Acks1 = ack_cons(Requeue, Acked, Acks), - State1#ch{tx = {Msgs, Acks1}} - end}. - -%% NB: Acked is in youngest-first order -reject(Requeue, Acked, Limiter) -> - foreach_per_queue( - fun (QPid, MsgIds) -> - rabbit_amqqueue:reject(QPid, Requeue, MsgIds, self()) - end, Acked), - ok = notify_limiter(Limiter, Acked). - -record_sent(ConsumerTag, AckRequired, - Msg = {QName, QPid, MsgId, Redelivered, _Message}, - State = #ch{unacked_message_q = UAMQ, - next_tag = DeliveryTag, - trace_state = TraceState, - user = #user{username = Username}, - conn_name = ConnName, - channel = ChannelNum}) -> - ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of - {none, true} -> get; - {none, false} -> get_no_ack; - {_ , true} -> deliver; - {_ , false} -> deliver_no_ack - end, State), - case Redelivered of - true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); - false -> ok - end, - rabbit_trace:tap_out(Msg, ConnName, ChannelNum, Username, TraceState), - UAMQ1 = case AckRequired of - true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, - UAMQ); - false -> UAMQ - end, - State#ch{unacked_message_q = UAMQ1, next_tag = DeliveryTag + 1}. - -%% NB: returns acks in youngest-first order -collect_acks(Q, 0, true) -> - {lists:reverse(queue:to_list(Q)), queue:new()}; -collect_acks(Q, DeliveryTag, Multiple) -> - collect_acks([], [], Q, DeliveryTag, Multiple). - -collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> - case queue:out(Q) of - {{value, UnackedMsg = {CurrentDeliveryTag, _ConsumerTag, _Msg}}, - QTail} -> - if CurrentDeliveryTag == DeliveryTag -> - {[UnackedMsg | ToAcc], - case PrefixAcc of - [] -> QTail; - _ -> queue:join( - queue:from_list(lists:reverse(PrefixAcc)), - QTail) - end}; - Multiple -> - collect_acks([UnackedMsg | ToAcc], PrefixAcc, - QTail, DeliveryTag, Multiple); - true -> - collect_acks(ToAcc, [UnackedMsg | PrefixAcc], - QTail, DeliveryTag, Multiple) - end; - {empty, _} -> - precondition_failed("unknown delivery tag ~w", [DeliveryTag]) - end. - -%% NB: Acked is in youngest-first order -ack(Acked, State = #ch{queue_names = QNames}) -> - foreach_per_queue( - fun (QPid, MsgIds) -> - ok = rabbit_amqqueue:ack(QPid, MsgIds, self()), - ?INCR_STATS(case dict:find(QPid, QNames) of - {ok, QName} -> Count = length(MsgIds), - [{queue_stats, QName, Count}]; - error -> [] - end, ack, State) - end, Acked), - ok = notify_limiter(State#ch.limiter, Acked). - -%% {Msgs, Acks} -%% -%% Msgs is a queue. -%% -%% Acks looks s.t. like this: -%% [{false,[5,4]},{true,[3]},{ack,[2,1]}, ...] -%% -%% Each element is a pair consisting of a tag and a list of -%% ack'ed/reject'ed msg ids. The tag is one of 'ack' (to ack), 'true' -%% (reject w requeue), 'false' (reject w/o requeue). The msg ids, as -%% well as the list overall, are in "most-recent (generally youngest) -%% ack first" order. -new_tx() -> {queue:new(), []}. - -notify_queues(State = #ch{state = closing}) -> - {ok, State}; -notify_queues(State = #ch{consumer_mapping = Consumers, - delivering_queues = DQ }) -> - QPids = sets:to_list( - sets:union(sets:from_list(consumer_queues(Consumers)), DQ)), - {rabbit_amqqueue:notify_down_all(QPids, self()), State#ch{state = closing}}. - -foreach_per_queue(_F, []) -> - ok; -foreach_per_queue(F, [{_DTag, _CTag, {QPid, MsgId}}]) -> %% common case - F(QPid, [MsgId]); -%% NB: UAL should be in youngest-first order; the tree values will -%% then be in oldest-first order -foreach_per_queue(F, UAL) -> - T = lists:foldl(fun ({_DTag, _CTag, {QPid, MsgId}}, T) -> - rabbit_misc:gb_trees_cons(QPid, MsgId, T) - end, gb_trees:empty(), UAL), - rabbit_misc:gb_trees_foreach(F, T). - -consumer_queues(Consumers) -> - lists:usort([QPid || {_Key, {#amqqueue{pid = QPid}, _CParams}} - <- dict:to_list(Consumers)]). - -%% tell the limiter about the number of acks that have been received -%% for messages delivered to subscribed consumers, but not acks for -%% messages sent in a response to a basic.get (identified by their -%% 'none' consumer tag) -notify_limiter(Limiter, Acked) -> - %% optimisation: avoid the potentially expensive 'foldl' in the - %% common case. - case rabbit_limiter:is_active(Limiter) of - false -> ok; - true -> case lists:foldl(fun ({_, none, _}, Acc) -> Acc; - ({_, _, _}, Acc) -> Acc + 1 - end, 0, Acked) of - 0 -> ok; - Count -> rabbit_limiter:ack(Limiter, Count) - end - end. - -deliver_to_queues({#delivery{message = #basic_message{exchange_name = XName}, - confirm = false, - mandatory = false}, - []}, State) -> %% optimisation - ?INCR_STATS([{exchange_stats, XName, 1}], publish, State), - State; -deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ - exchange_name = XName}, - mandatory = Mandatory, - confirm = Confirm, - msg_seq_no = MsgSeqNo}, - DelQNames}, State = #ch{queue_names = QNames, - queue_monitors = QMons}) -> - Qs = rabbit_amqqueue:lookup(DelQNames), - DeliveredQPids = rabbit_amqqueue:deliver(Qs, Delivery), - %% The pmon:monitor_all/2 monitors all queues to which we - %% delivered. But we want to monitor even queues we didn't deliver - %% to, since we need their 'DOWN' messages to clean - %% queue_names. So we also need to monitor each QPid from - %% queues. But that only gets the masters (which is fine for - %% cleaning queue_names), so we need the union of both. - %% - %% ...and we need to add even non-delivered queues to queue_names - %% since alternative algorithms to update queue_names less - %% frequently would in fact be more expensive in the common case. - {QNames1, QMons1} = - lists:foldl(fun (#amqqueue{pid = QPid, name = QName}, - {QNames0, QMons0}) -> - {case dict:is_key(QPid, QNames0) of - true -> QNames0; - false -> dict:store(QPid, QName, QNames0) - end, pmon:monitor(QPid, QMons0)} - end, {QNames, pmon:monitor_all(DeliveredQPids, QMons)}, Qs), - State1 = State#ch{queue_names = QNames1, - queue_monitors = QMons1}, - %% NB: the order here is important since basic.returns must be - %% sent before confirms. - State2 = process_routing_mandatory(Mandatory, DeliveredQPids, MsgSeqNo, - Message, State1), - State3 = process_routing_confirm( Confirm, DeliveredQPids, MsgSeqNo, - XName, State2), - ?INCR_STATS([{exchange_stats, XName, 1} | - [{queue_exchange_stats, {QName, XName}, 1} || - QPid <- DeliveredQPids, - {ok, QName} <- [dict:find(QPid, QNames1)]]], - publish, State3), - State3. - -process_routing_mandatory(false, _, _MsgSeqNo, _Msg, State) -> - State; -process_routing_mandatory(true, [], _MsgSeqNo, Msg, State) -> - ok = basic_return(Msg, State, no_route), - State; -process_routing_mandatory(true, QPids, MsgSeqNo, Msg, State) -> - State#ch{mandatory = dtree:insert(MsgSeqNo, QPids, Msg, - State#ch.mandatory)}. - -process_routing_confirm(false, _, _MsgSeqNo, _XName, State) -> - State; -process_routing_confirm(true, [], MsgSeqNo, XName, State) -> - record_confirms([{MsgSeqNo, XName}], State); -process_routing_confirm(true, QPids, MsgSeqNo, XName, State) -> - State#ch{unconfirmed = dtree:insert(MsgSeqNo, QPids, XName, - State#ch.unconfirmed)}. - -send_nacks([], State) -> - State; -send_nacks(_MXs, State = #ch{state = closing, - tx = none}) -> %% optimisation - State; -send_nacks(MXs, State = #ch{tx = none}) -> - coalesce_and_send([MsgSeqNo || {MsgSeqNo, _} <- MXs], - fun(MsgSeqNo, Multiple) -> - #'basic.nack'{delivery_tag = MsgSeqNo, - multiple = Multiple} - end, State); -send_nacks(_MXs, State = #ch{state = closing}) -> %% optimisation - State#ch{tx = failed}; -send_nacks(_, State) -> - maybe_complete_tx(State#ch{tx = failed}). - -send_confirms(State = #ch{tx = none, confirmed = []}) -> - State; -send_confirms(State = #ch{tx = none, confirmed = C}) -> - case rabbit_node_monitor:pause_partition_guard() of - ok -> MsgSeqNos = - lists:foldl( - fun ({MsgSeqNo, XName}, MSNs) -> - ?INCR_STATS([{exchange_stats, XName, 1}], - confirm, State), - [MsgSeqNo | MSNs] - end, [], lists:append(C)), - send_confirms(MsgSeqNos, State#ch{confirmed = []}); - pausing -> State - end; -send_confirms(State) -> - case rabbit_node_monitor:pause_partition_guard() of - ok -> maybe_complete_tx(State); - pausing -> State - end. - -send_confirms([], State) -> - State; -send_confirms(_Cs, State = #ch{state = closing}) -> %% optimisation - State; -send_confirms([MsgSeqNo], State) -> - ok = send(#'basic.ack'{delivery_tag = MsgSeqNo}, State), - State; -send_confirms(Cs, State) -> - coalesce_and_send(Cs, fun(MsgSeqNo, Multiple) -> - #'basic.ack'{delivery_tag = MsgSeqNo, - multiple = Multiple} - end, State). - -coalesce_and_send(MsgSeqNos, MkMsgFun, State = #ch{unconfirmed = UC}) -> - SMsgSeqNos = lists:usort(MsgSeqNos), - CutOff = case dtree:is_empty(UC) of - true -> lists:last(SMsgSeqNos) + 1; - false -> {SeqNo, _XName} = dtree:smallest(UC), SeqNo - end, - {Ms, Ss} = lists:splitwith(fun(X) -> X < CutOff end, SMsgSeqNos), - case Ms of - [] -> ok; - _ -> ok = send(MkMsgFun(lists:last(Ms), true), State) - end, - [ok = send(MkMsgFun(SeqNo, false), State) || SeqNo <- Ss], - State. - -ack_cons(Tag, Acked, [{Tag, Acks} | L]) -> [{Tag, Acked ++ Acks} | L]; -ack_cons(Tag, Acked, Acks) -> [{Tag, Acked} | Acks]. - -ack_len(Acks) -> lists:sum([length(L) || {ack, L} <- Acks]). - -maybe_complete_tx(State = #ch{tx = {_, _}}) -> - State; -maybe_complete_tx(State = #ch{unconfirmed = UC}) -> - case dtree:is_empty(UC) of - false -> State; - true -> complete_tx(State#ch{confirmed = []}) - end. - -complete_tx(State = #ch{tx = committing}) -> - ok = send(#'tx.commit_ok'{}, State), - State#ch{tx = new_tx()}; -complete_tx(State = #ch{tx = failed}) -> - {noreply, State1} = handle_exception( - rabbit_misc:amqp_error( - precondition_failed, "partial tx completion", [], - 'tx.commit'), - State), - State1#ch{tx = new_tx()}. - -infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. - -i(pid, _) -> self(); -i(connection, #ch{conn_pid = ConnPid}) -> ConnPid; -i(number, #ch{channel = Channel}) -> Channel; -i(user, #ch{user = User}) -> User#user.username; -i(vhost, #ch{virtual_host = VHost}) -> VHost; -i(transactional, #ch{tx = Tx}) -> Tx =/= none; -i(confirm, #ch{confirm_enabled = CE}) -> CE; -i(name, State) -> name(State); -i(consumer_count, #ch{consumer_mapping = CM}) -> dict:size(CM); -i(messages_unconfirmed, #ch{unconfirmed = UC}) -> dtree:size(UC); -i(messages_unacknowledged, #ch{unacked_message_q = UAMQ}) -> queue:len(UAMQ); -i(messages_uncommitted, #ch{tx = {Msgs, _Acks}}) -> queue:len(Msgs); -i(messages_uncommitted, #ch{}) -> 0; -i(acks_uncommitted, #ch{tx = {_Msgs, Acks}}) -> ack_len(Acks); -i(acks_uncommitted, #ch{}) -> 0; -i(state, #ch{state = running}) -> credit_flow:state(); -i(state, #ch{state = State}) -> State; -i(prefetch_count, #ch{consumer_prefetch = C}) -> C; -i(global_prefetch_count, #ch{limiter = Limiter}) -> - rabbit_limiter:get_prefetch_limit(Limiter); -i(Item, _) -> - throw({bad_argument, Item}). - -name(#ch{conn_name = ConnName, channel = Channel}) -> - list_to_binary(rabbit_misc:format("~s (~p)", [ConnName, Channel])). - -incr_stats(Incs, Measure) -> - [update_measures(Type, Key, Inc, Measure) || {Type, Key, Inc} <- Incs]. - -update_measures(Type, Key, Inc, Measure) -> - Measures = case get({Type, Key}) of - undefined -> []; - D -> D - end, - Cur = case orddict:find(Measure, Measures) of - error -> 0; - {ok, C} -> C - end, - put({Type, Key}, orddict:store(Measure, Cur + Inc, Measures)). - -emit_stats(State) -> emit_stats(State, []). - -emit_stats(State, Extra) -> - Coarse = infos(?STATISTICS_KEYS, State), - case rabbit_event:stats_level(State, #ch.stats_timer) of - coarse -> rabbit_event:notify(channel_stats, Extra ++ Coarse); - fine -> Fine = [{channel_queue_stats, - [{QName, Stats} || - {{queue_stats, QName}, Stats} <- get()]}, - {channel_exchange_stats, - [{XName, Stats} || - {{exchange_stats, XName}, Stats} <- get()]}, - {channel_queue_exchange_stats, - [{QX, Stats} || - {{queue_exchange_stats, QX}, Stats} <- get()]}], - rabbit_event:notify(channel_stats, Extra ++ Coarse ++ Fine) - end. - -erase_queue_stats(QName) -> - erase({queue_stats, QName}), - [erase({queue_exchange_stats, QX}) || - {{queue_exchange_stats, QX = {QName0, _}}, _} <- get(), - QName0 =:= QName]. - -get_vhost(#ch{virtual_host = VHost}) -> VHost. - -get_user(#ch{user = User}) -> User. diff --git a/src/rabbit_channel_interceptor.erl b/src/rabbit_channel_interceptor.erl deleted file mode 100644 index 6a925b69fe..0000000000 --- a/src/rabbit_channel_interceptor.erl +++ /dev/null @@ -1,117 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_channel_interceptor). - --include("rabbit_framing.hrl"). --include("rabbit.hrl"). - --export([init/1, intercept_in/3]). - --ifdef(use_specs). - --type(method_name() :: rabbit_framing:amqp_method_name()). --type(original_method() :: rabbit_framing:amqp_method_record()). --type(processed_method() :: rabbit_framing:amqp_method_record()). --type(original_content() :: rabbit_types:maybe(rabbit_types:content())). --type(processed_content() :: rabbit_types:maybe(rabbit_types:content())). --type(interceptor_state() :: term()). - --callback description() -> [proplists:property()]. -%% Derive some initial state from the channel. This will be passed back -%% as the third argument of intercept/3. --callback init(rabbit_channel:channel()) -> interceptor_state(). --callback intercept(original_method(), original_content(), - interceptor_state()) -> - {processed_method(), processed_content()} | - rabbit_misc:channel_or_connection_exit(). --callback applies_to() -> list(method_name()). - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{description, 0}, {init, 1}, {intercept, 3}, {applies_to, 0}]; -behaviour_info(_Other) -> - undefined. - --endif. - -init(Ch) -> - Mods = [M || {_, M} <- rabbit_registry:lookup_all(channel_interceptor)], - check_no_overlap(Mods), - [{Mod, Mod:init(Ch)} || Mod <- Mods]. - -check_no_overlap(Mods) -> - check_no_overlap1([sets:from_list(Mod:applies_to()) || Mod <- Mods]). - -%% Check no non-empty pairwise intersection in a list of sets -check_no_overlap1(Sets) -> - lists:foldl(fun(Set, Union) -> - Is = sets:intersection(Set, Union), - case sets:size(Is) of - 0 -> ok; - _ -> - internal_error("Interceptor: more than one " - "module handles ~p~n", [Is]) - end, - sets:union(Set, Union) - end, - sets:new(), - Sets), - ok. - -intercept_in(M, C, Mods) -> - lists:foldl(fun({Mod, ModState}, {M1, C1}) -> - call_module(Mod, ModState, M1, C1) - end, - {M, C}, - Mods). - -call_module(Mod, St, M, C) -> - % this little dance is because Mod might be unloaded at any point - case (catch {ok, Mod:intercept(M, C, St)}) of - {ok, R} -> validate_response(Mod, M, C, R); - {'EXIT', {undef, [{Mod, intercept, _, _} | _]}} -> {M, C} - end. - -validate_response(Mod, M1, C1, R = {M2, C2}) -> - case {validate_method(M1, M2), validate_content(C1, C2)} of - {true, true} -> R; - {false, _} -> - internal_error("Interceptor: ~p expected to return " - "method: ~p but returned: ~p", - [Mod, rabbit_misc:method_record_type(M1), - rabbit_misc:method_record_type(M2)]); - {_, false} -> - internal_error("Interceptor: ~p expected to return " - "content iff content is provided but " - "content in = ~p; content out = ~p", - [Mod, C1, C2]) - end. - -validate_method(M, M2) -> - rabbit_misc:method_record_type(M) =:= rabbit_misc:method_record_type(M2). - -validate_content(none, none) -> true; -validate_content(#content{}, #content{}) -> true; -validate_content(_, _) -> false. - -%% keep dialyzer happy --spec internal_error(string(), [any()]) -> no_return(). -internal_error(Format, Args) -> - rabbit_misc:protocol_error(internal_error, Format, Args). diff --git a/src/rabbit_command_assembler.erl b/src/rabbit_command_assembler.erl deleted file mode 100644 index f93b85b122..0000000000 --- a/src/rabbit_command_assembler.erl +++ /dev/null @@ -1,137 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_command_assembler). --include("rabbit_framing.hrl"). --include("rabbit.hrl"). - --export([analyze_frame/3, init/1, process/2]). - -%%---------------------------------------------------------------------------- - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([frame/0]). - --type(frame_type() :: ?FRAME_METHOD | ?FRAME_HEADER | ?FRAME_BODY | - ?FRAME_OOB_METHOD | ?FRAME_OOB_HEADER | ?FRAME_OOB_BODY | - ?FRAME_TRACE | ?FRAME_HEARTBEAT). --type(protocol() :: rabbit_framing:protocol()). --type(method() :: rabbit_framing:amqp_method_record()). --type(class_id() :: rabbit_framing:amqp_class_id()). --type(weight() :: non_neg_integer()). --type(body_size() :: non_neg_integer()). --type(content() :: rabbit_types:undecoded_content()). - --type(frame() :: - {'method', rabbit_framing:amqp_method_name(), binary()} | - {'content_header', class_id(), weight(), body_size(), binary()} | - {'content_body', binary()}). - --type(state() :: - {'method', protocol()} | - {'content_header', method(), class_id(), protocol()} | - {'content_body', method(), body_size(), class_id(), protocol()}). - --spec(analyze_frame/3 :: (frame_type(), binary(), protocol()) -> - frame() | 'heartbeat' | 'error'). - --spec(init/1 :: (protocol()) -> {ok, state()}). --spec(process/2 :: (frame(), state()) -> - {ok, state()} | - {ok, method(), state()} | - {ok, method(), content(), state()} | - {error, rabbit_types:amqp_error()}). - --endif. - -%%-------------------------------------------------------------------- - -analyze_frame(?FRAME_METHOD, - <<ClassId:16, MethodId:16, MethodFields/binary>>, - Protocol) -> - MethodName = Protocol:lookup_method_name({ClassId, MethodId}), - {method, MethodName, MethodFields}; -analyze_frame(?FRAME_HEADER, - <<ClassId:16, Weight:16, BodySize:64, Properties/binary>>, - _Protocol) -> - {content_header, ClassId, Weight, BodySize, Properties}; -analyze_frame(?FRAME_BODY, Body, _Protocol) -> - {content_body, Body}; -analyze_frame(?FRAME_HEARTBEAT, <<>>, _Protocol) -> - heartbeat; -analyze_frame(_Type, _Body, _Protocol) -> - error. - -init(Protocol) -> {ok, {method, Protocol}}. - -process({method, MethodName, FieldsBin}, {method, Protocol}) -> - try - Method = Protocol:decode_method_fields(MethodName, FieldsBin), - case Protocol:method_has_content(MethodName) of - true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), - {ok, {content_header, Method, ClassId, Protocol}}; - false -> {ok, Method, {method, Protocol}} - end - catch exit:#amqp_error{} = Reason -> {error, Reason} - end; -process(_Frame, {method, _Protocol}) -> - unexpected_frame("expected method frame, " - "got non method frame instead", [], none); -process({content_header, ClassId, 0, 0, PropertiesBin}, - {content_header, Method, ClassId, Protocol}) -> - Content = empty_content(ClassId, PropertiesBin, Protocol), - {ok, Method, Content, {method, Protocol}}; -process({content_header, ClassId, 0, BodySize, PropertiesBin}, - {content_header, Method, ClassId, Protocol}) -> - Content = empty_content(ClassId, PropertiesBin, Protocol), - {ok, {content_body, Method, BodySize, Content, Protocol}}; -process({content_header, HeaderClassId, 0, _BodySize, _PropertiesBin}, - {content_header, Method, ClassId, _Protocol}) -> - unexpected_frame("expected content header for class ~w, " - "got one for class ~w instead", - [ClassId, HeaderClassId], Method); -process(_Frame, {content_header, Method, ClassId, _Protocol}) -> - unexpected_frame("expected content header for class ~w, " - "got non content header frame instead", [ClassId], Method); -process({content_body, FragmentBin}, - {content_body, Method, RemainingSize, - Content = #content{payload_fragments_rev = Fragments}, Protocol}) -> - NewContent = Content#content{ - payload_fragments_rev = [FragmentBin | Fragments]}, - case RemainingSize - size(FragmentBin) of - 0 -> {ok, Method, NewContent, {method, Protocol}}; - Sz -> {ok, {content_body, Method, Sz, NewContent, Protocol}} - end; -process(_Frame, {content_body, Method, _RemainingSize, _Content, _Protocol}) -> - unexpected_frame("expected content body, " - "got non content body frame instead", [], Method). - -%%-------------------------------------------------------------------- - -empty_content(ClassId, PropertiesBin, Protocol) -> - #content{class_id = ClassId, - properties = none, - properties_bin = PropertiesBin, - protocol = Protocol, - payload_fragments_rev = []}. - -unexpected_frame(Format, Params, Method) when is_atom(Method) -> - {error, rabbit_misc:amqp_error(unexpected_frame, Format, Params, Method)}; -unexpected_frame(Format, Params, Method) -> - unexpected_frame(Format, Params, rabbit_misc:method_record_type(Method)). diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl deleted file mode 100644 index 4d62dd079b..0000000000 --- a/src/rabbit_event.erl +++ /dev/null @@ -1,163 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_event). - --include("rabbit.hrl"). - --export([start_link/0]). --export([init_stats_timer/2, init_disabled_stats_timer/2, - ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). --export([stats_level/2, if_enabled/3]). --export([notify/2, notify/3, notify_if/3]). --export([sync_notify/2, sync_notify/3]). - -%%---------------------------------------------------------------------------- - --record(state, {level, interval, timer}). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([event_type/0, event_props/0, event_timestamp/0, event/0]). - --type(event_type() :: atom()). --type(event_props() :: term()). --type(event_timestamp() :: non_neg_integer()). - --type(event() :: #event { type :: event_type(), - props :: event_props(), - reference :: 'none' | reference(), - timestamp :: event_timestamp() }). - --type(level() :: 'none' | 'coarse' | 'fine'). - --type(timer_fun() :: fun (() -> 'ok')). --type(container() :: tuple()). --type(pos() :: non_neg_integer()). - --spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(init_stats_timer/2 :: (container(), pos()) -> container()). --spec(init_disabled_stats_timer/2 :: (container(), pos()) -> container()). --spec(ensure_stats_timer/3 :: (container(), pos(), term()) -> container()). --spec(stop_stats_timer/2 :: (container(), pos()) -> container()). --spec(reset_stats_timer/2 :: (container(), pos()) -> container()). --spec(stats_level/2 :: (container(), pos()) -> level()). --spec(if_enabled/3 :: (container(), pos(), timer_fun()) -> 'ok'). --spec(notify/2 :: (event_type(), event_props()) -> 'ok'). --spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok'). --spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok'). --spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok'). --spec(sync_notify/3 :: (event_type(), event_props(), - reference() | 'none') -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -start_link() -> - gen_event:start_link({local, ?MODULE}). - -%% The idea is, for each stat-emitting object: -%% -%% On startup: -%% init_stats_timer(State) -%% notify(created event) -%% if_enabled(internal_emit_stats) - so we immediately send something -%% -%% On wakeup: -%% ensure_stats_timer(State, emit_stats) -%% (Note we can't emit stats immediately, the timer may have fired 1ms ago.) -%% -%% emit_stats: -%% if_enabled(internal_emit_stats) -%% reset_stats_timer(State) - just bookkeeping -%% -%% Pre-hibernation: -%% if_enabled(internal_emit_stats) -%% stop_stats_timer(State) -%% -%% internal_emit_stats: -%% notify(stats) - -init_stats_timer(C, P) -> - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), - {ok, Interval} = application:get_env(rabbit, collect_statistics_interval), - setelement(P, C, #state{level = StatsLevel, interval = Interval, - timer = undefined}). - -init_disabled_stats_timer(C, P) -> - setelement(P, C, #state{level = none, interval = 0, timer = undefined}). - -ensure_stats_timer(C, P, Msg) -> - case element(P, C) of - #state{level = Level, interval = Interval, timer = undefined} = State - when Level =/= none -> - TRef = erlang:send_after(Interval, self(), Msg), - setelement(P, C, State#state{timer = TRef}); - #state{} -> - C - end. - -stop_stats_timer(C, P) -> - case element(P, C) of - #state{timer = TRef} = State when TRef =/= undefined -> - case erlang:cancel_timer(TRef) of - false -> C; - _ -> setelement(P, C, State#state{timer = undefined}) - end; - #state{} -> - C - end. - -reset_stats_timer(C, P) -> - case element(P, C) of - #state{timer = TRef} = State when TRef =/= undefined -> - setelement(P, C, State#state{timer = undefined}); - #state{} -> - C - end. - -stats_level(C, P) -> - #state{level = Level} = element(P, C), - Level. - -if_enabled(C, P, Fun) -> - case element(P, C) of - #state{level = none} -> ok; - #state{} -> Fun(), ok - end. - -notify_if(true, Type, Props) -> notify(Type, Props); -notify_if(false, _Type, _Props) -> ok. - -notify(Type, Props) -> notify(Type, Props, none). - -notify(Type, Props, Ref) -> - gen_event:notify(?MODULE, event_cons(Type, Props, Ref)). - -sync_notify(Type, Props) -> sync_notify(Type, Props, none). - -sync_notify(Type, Props, Ref) -> - gen_event:sync_notify(?MODULE, event_cons(Type, Props, Ref)). - -event_cons(Type, Props, Ref) -> - #event{type = Type, - props = Props, - reference = Ref, - timestamp = time_compat:os_system_time(milli_seconds)}. - diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl deleted file mode 100644 index 7c5bfdf913..0000000000 --- a/src/rabbit_exchange_decorator.erl +++ /dev/null @@ -1,128 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_exchange_decorator). - --include("rabbit.hrl"). - --export([select/2, set/1, register/2, unregister/1]). - -%% This is like an exchange type except that: -%% -%% 1) It applies to all exchanges as soon as it is installed, therefore -%% 2) It is not allowed to affect validation, so no validate/1 or -%% assert_args_equivalence/2 -%% -%% It's possible in the future we might make decorators -%% able to manipulate messages as they are published. - --ifdef(use_specs). - --type(tx() :: 'transaction' | 'none'). --type(serial() :: pos_integer() | tx()). - --callback description() -> [proplists:property()]. - -%% Should Rabbit ensure that all binding events that are -%% delivered to an individual exchange can be serialised? (they -%% might still be delivered out of order, but there'll be a -%% serial number). --callback serialise_events(rabbit_types:exchange()) -> boolean(). - -%% called after declaration and recovery --callback create(tx(), rabbit_types:exchange()) -> 'ok'. - -%% called after exchange (auto)deletion. --callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> - 'ok'. - -%% called when the policy attached to this exchange changes. --callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> - 'ok'. - -%% called after a binding has been added or recovered --callback add_binding(serial(), rabbit_types:exchange(), - rabbit_types:binding()) -> 'ok'. - -%% called after bindings have been deleted. --callback remove_bindings(serial(), rabbit_types:exchange(), - [rabbit_types:binding()]) -> 'ok'. - -%% Allows additional destinations to be added to the routing decision. --callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> - [rabbit_amqqueue:name() | rabbit_exchange:name()]. - -%% Whether the decorator wishes to receive callbacks for the exchange -%% none:no callbacks, noroute:all callbacks except route, all:all callbacks --callback active_for(rabbit_types:exchange()) -> 'none' | 'noroute' | 'all'. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3}, - {route, 2}, {active_for, 1}]; -behaviour_info(_Other) -> - undefined. - --endif. - -%%---------------------------------------------------------------------------- - -%% select a subset of active decorators -select(all, {Route, NoRoute}) -> filter(Route ++ NoRoute); -select(route, {Route, _NoRoute}) -> filter(Route); -select(raw, {Route, NoRoute}) -> Route ++ NoRoute. - -filter(Modules) -> - [M || M <- Modules, code:which(M) =/= non_existing]. - -set(X) -> - Decs = lists:foldl(fun (D, {Route, NoRoute}) -> - ActiveFor = D:active_for(X), - {cons_if_eq(all, ActiveFor, D, Route), - cons_if_eq(noroute, ActiveFor, D, NoRoute)} - end, {[], []}, list()), - X#exchange{decorators = Decs}. - -list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. - -cons_if_eq(Select, Select, Item, List) -> [Item | List]; -cons_if_eq(_Select, _Other, _Item, List) -> List. - -register(TypeName, ModuleName) -> - rabbit_registry:register(exchange_decorator, TypeName, ModuleName), - [maybe_recover(X) || X <- rabbit_exchange:list()], - ok. - -unregister(TypeName) -> - rabbit_registry:unregister(exchange_decorator, TypeName), - [maybe_recover(X) || X <- rabbit_exchange:list()], - ok. - -maybe_recover(X = #exchange{name = Name, - decorators = Decs}) -> - #exchange{decorators = Decs1} = set(X), - Old = lists:sort(select(all, Decs)), - New = lists:sort(select(all, Decs1)), - case New of - Old -> ok; - _ -> %% TODO create a tx here for non-federation decorators - [M:create(none, X) || M <- New -- Old], - rabbit_exchange:update_decorators(Name) - end. diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl deleted file mode 100644 index 92c1de6c21..0000000000 --- a/src/rabbit_exchange_type.erl +++ /dev/null @@ -1,81 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_exchange_type). - --ifdef(use_specs). - --type(tx() :: 'transaction' | 'none'). --type(serial() :: pos_integer() | tx()). - --callback description() -> [proplists:property()]. - -%% Should Rabbit ensure that all binding events that are -%% delivered to an individual exchange can be serialised? (they -%% might still be delivered out of order, but there'll be a -%% serial number). --callback serialise_events() -> boolean(). - -%% The no_return is there so that we can have an "invalid" exchange -%% type (see rabbit_exchange_type_invalid). --callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> - rabbit_router:match_result(). - -%% called BEFORE declaration, to check args etc; may exit with #amqp_error{} --callback validate(rabbit_types:exchange()) -> 'ok'. - -%% called BEFORE declaration, to check args etc --callback validate_binding(rabbit_types:exchange(), rabbit_types:binding()) -> - rabbit_types:ok_or_error({'binding_invalid', string(), [any()]}). - -%% called after declaration and recovery --callback create(tx(), rabbit_types:exchange()) -> 'ok'. - -%% called after exchange (auto)deletion. --callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> - 'ok'. - -%% called when the policy attached to this exchange changes. --callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> - 'ok'. - -%% called after a binding has been added or recovered --callback add_binding(serial(), rabbit_types:exchange(), - rabbit_types:binding()) -> 'ok'. - -%% called after bindings have been deleted. --callback remove_bindings(serial(), rabbit_types:exchange(), - [rabbit_types:binding()]) -> 'ok'. - -%% called when comparing exchanges for equivalence - should return ok or -%% exit with #amqp_error{} --callback assert_args_equivalence(rabbit_types:exchange(), - rabbit_framing:amqp_table()) -> - 'ok' | rabbit_types:connection_exit(). - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{description, 0}, {serialise_events, 0}, {route, 2}, - {validate, 1}, {validate_binding, 2}, {policy_changed, 2}, - {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, - {assert_args_equivalence, 2}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl deleted file mode 100644 index 993076770f..0000000000 --- a/src/rabbit_heartbeat.erl +++ /dev/null @@ -1,166 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_heartbeat). - --export([start/6, start/7]). --export([start_heartbeat_sender/4, start_heartbeat_receiver/4, - pause_monitor/1, resume_monitor/1]). - --export([system_continue/3, system_terminate/4, system_code_change/4]). - --include("rabbit.hrl"). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([heartbeaters/0]). - --type(heartbeaters() :: {rabbit_types:maybe(pid()), rabbit_types:maybe(pid())}). - --type(heartbeat_callback() :: fun (() -> any())). - --spec(start/6 :: - (pid(), rabbit_net:socket(), - non_neg_integer(), heartbeat_callback(), - non_neg_integer(), heartbeat_callback()) -> heartbeaters()). - --spec(start/7 :: - (pid(), rabbit_net:socket(), rabbit_types:proc_name(), - non_neg_integer(), heartbeat_callback(), - non_neg_integer(), heartbeat_callback()) -> heartbeaters()). - --spec(start_heartbeat_sender/4 :: - (rabbit_net:socket(), non_neg_integer(), heartbeat_callback(), - rabbit_types:proc_type_and_name()) -> rabbit_types:ok(pid())). --spec(start_heartbeat_receiver/4 :: - (rabbit_net:socket(), non_neg_integer(), heartbeat_callback(), - rabbit_types:proc_type_and_name()) -> rabbit_types:ok(pid())). - --spec(pause_monitor/1 :: (heartbeaters()) -> 'ok'). --spec(resume_monitor/1 :: (heartbeaters()) -> 'ok'). - --spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}). --spec(system_continue/3 :: (_,_,{_, _}) -> any()). --spec(system_terminate/4 :: (_,_,_,_) -> none()). - --endif. - -%%---------------------------------------------------------------------------- -start(SupPid, Sock, SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> - start(SupPid, Sock, unknown, - SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun). - -start(SupPid, Sock, Identity, - SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> - {ok, Sender} = - start_heartbeater(SendTimeoutSec, SupPid, Sock, - SendFun, heartbeat_sender, - start_heartbeat_sender, Identity), - {ok, Receiver} = - start_heartbeater(ReceiveTimeoutSec, SupPid, Sock, - ReceiveFun, heartbeat_receiver, - start_heartbeat_receiver, Identity), - {Sender, Receiver}. - -start_heartbeat_sender(Sock, TimeoutSec, SendFun, Identity) -> - %% the 'div 2' is there so that we don't end up waiting for nearly - %% 2 * TimeoutSec before sending a heartbeat in the boundary case - %% where the last message was sent just after a heartbeat. - heartbeater({Sock, TimeoutSec * 1000 div 2, send_oct, 0, - fun () -> SendFun(), continue end}, Identity). - -start_heartbeat_receiver(Sock, TimeoutSec, ReceiveFun, Identity) -> - %% we check for incoming data every interval, and time out after - %% two checks with no change. As a result we will time out between - %% 2 and 3 intervals after the last data has been received. - heartbeater({Sock, TimeoutSec * 1000, recv_oct, 1, - fun () -> ReceiveFun(), stop end}, Identity). - -pause_monitor({_Sender, none}) -> ok; -pause_monitor({_Sender, Receiver}) -> Receiver ! pause, ok. - -resume_monitor({_Sender, none}) -> ok; -resume_monitor({_Sender, Receiver}) -> Receiver ! resume, ok. - -system_continue(_Parent, Deb, {Params, State}) -> - heartbeater(Params, Deb, State). - -system_terminate(Reason, _Parent, _Deb, _State) -> - exit(Reason). - -system_code_change(Misc, _Module, _OldVsn, _Extra) -> - {ok, Misc}. - -%%---------------------------------------------------------------------------- -start_heartbeater(0, _SupPid, _Sock, _TimeoutFun, _Name, _Callback, - _Identity) -> - {ok, none}; -start_heartbeater(TimeoutSec, SupPid, Sock, TimeoutFun, Name, Callback, - Identity) -> - supervisor2:start_child( - SupPid, {Name, - {rabbit_heartbeat, Callback, - [Sock, TimeoutSec, TimeoutFun, {Name, Identity}]}, - transient, ?MAX_WAIT, worker, [rabbit_heartbeat]}). - -heartbeater(Params, Identity) -> - Deb = sys:debug_options([]), - {ok, proc_lib:spawn_link(fun () -> - rabbit_misc:store_proc_name(Identity), - heartbeater(Params, Deb, {0, 0}) - end)}. - -heartbeater({Sock, TimeoutMillisec, StatName, Threshold, Handler} = Params, - Deb, {StatVal, SameCount} = State) -> - Recurse = fun (State1) -> heartbeater(Params, Deb, State1) end, - System = fun (From, Req) -> - sys:handle_system_msg( - Req, From, self(), ?MODULE, Deb, {Params, State}) - end, - receive - pause -> - receive - resume -> Recurse({0, 0}); - {system, From, Req} -> System(From, Req); - Other -> exit({unexpected_message, Other}) - end; - {system, From, Req} -> - System(From, Req); - Other -> - exit({unexpected_message, Other}) - after TimeoutMillisec -> - case rabbit_net:getstat(Sock, [StatName]) of - {ok, [{StatName, NewStatVal}]} -> - if NewStatVal =/= StatVal -> - Recurse({NewStatVal, 0}); - SameCount < Threshold -> - Recurse({NewStatVal, SameCount + 1}); - true -> - case Handler() of - stop -> ok; - continue -> Recurse({NewStatVal, 0}) - end - end; - {error, einval} -> - %% the socket is dead, most likely because the - %% connection is being shut down -> terminate - ok; - {error, Reason} -> - exit({cannot_get_socket_stats, Reason}) - end - end. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl deleted file mode 100644 index cfabf1ed5e..0000000000 --- a/src/rabbit_misc.erl +++ /dev/null @@ -1,1165 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_misc). --include("rabbit.hrl"). --include("rabbit_framing.hrl"). - --export([method_record_type/1, polite_pause/0, polite_pause/1]). --export([die/1, frame_error/2, amqp_error/4, quit/1, - protocol_error/3, protocol_error/4, protocol_error/1]). --export([not_found/1, absent/2]). --export([type_class/1, assert_args_equivalence/4, assert_field_equivalence/4]). --export([dirty_read/1]). --export([table_lookup/2, set_table_value/4]). --export([r/3, r/2, r_arg/4, rs/1]). --export([enable_cover/0, report_cover/0]). --export([enable_cover/1, report_cover/1]). --export([start_cover/1]). --export([confirm_to_sender/2]). --export([throw_on_error/2, with_exit_handler/2, is_abnormal_exit/1, - filter_exit_map/2]). --export([with_user/2, with_user_and_vhost/3]). --export([execute_mnesia_transaction/1]). --export([execute_mnesia_transaction/2]). --export([execute_mnesia_tx_with_tail/1]). --export([ensure_ok/2]). --export([tcp_name/3, format_inet_error/1]). --export([upmap/2, map_in_order/2]). --export([table_filter/3]). --export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). --export([format/2, format_many/1, format_stderr/2]). --export([unfold/2, ceil/1, queue_fold/3]). --export([sort_field_table/1]). --export([pid_to_string/1, string_to_pid/1, - pid_change_node/2, node_to_fake_pid/1]). --export([version_compare/2, version_compare/3]). --export([version_minor_equivalent/2]). --export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). --export([gb_trees_fold/3, gb_trees_foreach/2]). --export([all_module_attributes/1, build_acyclic_graph/3]). --export([const/1]). --export([ntoa/1, ntoab/1]). --export([is_process_alive/1]). --export([pget/2, pget/3, pget_or_die/2, pmerge/3, pset/3, plmerge/2]). --export([format_message_queue/2]). --export([append_rpc_all_nodes/4]). --export([os_cmd/1]). --export([is_os_process_alive/1]). --export([gb_sets_difference/2]). --export([version/0, otp_release/0, which_applications/0]). --export([sequence_error/1]). --export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). --export([check_expiry/1]). --export([base64url/1]). --export([interval_operation/5]). --export([ensure_timer/4, stop_timer/2, send_after/3, cancel_timer/1]). --export([get_parent/0]). --export([store_proc_name/1, store_proc_name/2]). --export([moving_average/4]). --export([get_env/3]). - -%% Horrible macro to use in guards --define(IS_BENIGN_EXIT(R), - R =:= noproc; R =:= noconnection; R =:= nodedown; R =:= normal; - R =:= shutdown). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([resource_name/0, thunk/1, channel_or_connection_exit/0]). - --type(ok_or_error() :: rabbit_types:ok_or_error(any())). --type(thunk(T) :: fun(() -> T)). --type(resource_name() :: binary()). --type(channel_or_connection_exit() - :: rabbit_types:channel_exit() | rabbit_types:connection_exit()). --type(digraph_label() :: term()). --type(graph_vertex_fun() :: - fun (({atom(), [term()]}) -> [{digraph:vertex(), digraph_label()}])). --type(graph_edge_fun() :: - fun (({atom(), [term()]}) -> [{digraph:vertex(), digraph:vertex()}])). --type(tref() :: {'erlang', reference()} | {timer, timer:tref()}). - --spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) - -> rabbit_framing:amqp_method_name()). --spec(polite_pause/0 :: () -> 'done'). --spec(polite_pause/1 :: (non_neg_integer()) -> 'done'). --spec(die/1 :: - (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()). - --spec(quit/1 :: (integer()) -> no_return()). - --spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary()) - -> rabbit_types:connection_exit()). --spec(amqp_error/4 :: - (rabbit_framing:amqp_exception(), string(), [any()], - rabbit_framing:amqp_method_name()) - -> rabbit_types:amqp_error()). --spec(protocol_error/3 :: (rabbit_framing:amqp_exception(), string(), [any()]) - -> channel_or_connection_exit()). --spec(protocol_error/4 :: - (rabbit_framing:amqp_exception(), string(), [any()], - rabbit_framing:amqp_method_name()) -> channel_or_connection_exit()). --spec(protocol_error/1 :: - (rabbit_types:amqp_error()) -> channel_or_connection_exit()). --spec(not_found/1 :: (rabbit_types:r(atom())) -> rabbit_types:channel_exit()). --spec(absent/2 :: (rabbit_types:amqqueue(), rabbit_amqqueue:absent_reason()) - -> rabbit_types:channel_exit()). --spec(type_class/1 :: (rabbit_framing:amqp_field_type()) -> atom()). --spec(assert_args_equivalence/4 :: (rabbit_framing:amqp_table(), - rabbit_framing:amqp_table(), - rabbit_types:r(any()), [binary()]) -> - 'ok' | rabbit_types:connection_exit()). --spec(assert_field_equivalence/4 :: - (any(), any(), rabbit_types:r(any()), atom() | binary()) -> - 'ok' | rabbit_types:connection_exit()). --spec(equivalence_fail/4 :: - (any(), any(), rabbit_types:r(any()), atom() | binary()) -> - rabbit_types:connection_exit()). --spec(dirty_read/1 :: - ({atom(), any()}) -> rabbit_types:ok_or_error2(any(), 'not_found')). --spec(table_lookup/2 :: - (rabbit_framing:amqp_table(), binary()) - -> 'undefined' | {rabbit_framing:amqp_field_type(), any()}). --spec(set_table_value/4 :: - (rabbit_framing:amqp_table(), binary(), - rabbit_framing:amqp_field_type(), rabbit_framing:amqp_value()) - -> rabbit_framing:amqp_table()). --spec(r/2 :: (rabbit_types:vhost(), K) - -> rabbit_types:r3(rabbit_types:vhost(), K, '_') - when is_subtype(K, atom())). --spec(r/3 :: - (rabbit_types:vhost() | rabbit_types:r(atom()), K, resource_name()) - -> rabbit_types:r3(rabbit_types:vhost(), K, resource_name()) - when is_subtype(K, atom())). --spec(r_arg/4 :: - (rabbit_types:vhost() | rabbit_types:r(atom()), K, - rabbit_framing:amqp_table(), binary()) -> - undefined | - rabbit_types:error( - {invalid_type, rabbit_framing:amqp_field_type()}) | - rabbit_types:r(K) when is_subtype(K, atom())). --spec(rs/1 :: (rabbit_types:r(atom())) -> string()). --spec(enable_cover/0 :: () -> ok_or_error()). --spec(start_cover/1 :: ([{string(), string()} | string()]) -> 'ok'). --spec(report_cover/0 :: () -> 'ok'). --spec(enable_cover/1 :: ([file:filename() | atom()]) -> ok_or_error()). --spec(report_cover/1 :: ([file:filename() | atom()]) -> 'ok'). --spec(throw_on_error/2 :: - (atom(), thunk(rabbit_types:error(any()) | {ok, A} | A)) -> A). --spec(with_exit_handler/2 :: (thunk(A), thunk(A)) -> A). --spec(is_abnormal_exit/1 :: (any()) -> boolean()). --spec(filter_exit_map/2 :: (fun ((A) -> B), [A]) -> [B]). --spec(with_user/2 :: (rabbit_types:username(), thunk(A)) -> A). --spec(with_user_and_vhost/3 :: - (rabbit_types:username(), rabbit_types:vhost(), thunk(A)) - -> A). --spec(execute_mnesia_transaction/1 :: (thunk(A)) -> A). --spec(execute_mnesia_transaction/2 :: - (thunk(A), fun ((A, boolean()) -> B)) -> B). --spec(execute_mnesia_tx_with_tail/1 :: - (thunk(fun ((boolean()) -> B))) -> B | (fun ((boolean()) -> B))). --spec(ensure_ok/2 :: (ok_or_error(), atom()) -> 'ok'). --spec(tcp_name/3 :: - (atom(), inet:ip_address(), rabbit_networking:ip_port()) - -> atom()). --spec(format_inet_error/1 :: (atom()) -> string()). --spec(upmap/2 :: (fun ((A) -> B), [A]) -> [B]). --spec(map_in_order/2 :: (fun ((A) -> B), [A]) -> [B]). --spec(table_filter/3:: (fun ((A) -> boolean()), fun ((A, boolean()) -> 'ok'), - atom()) -> [A]). --spec(dirty_read_all/1 :: (atom()) -> [any()]). --spec(dirty_foreach_key/2 :: (fun ((any()) -> any()), atom()) - -> 'ok' | 'aborted'). --spec(dirty_dump_log/1 :: (file:filename()) -> ok_or_error()). --spec(format/2 :: (string(), [any()]) -> string()). --spec(format_many/1 :: ([{string(), [any()]}]) -> string()). --spec(format_stderr/2 :: (string(), [any()]) -> 'ok'). --spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}). --spec(ceil/1 :: (number()) -> integer()). --spec(queue_fold/3 :: (fun ((any(), B) -> B), B, queue:queue()) -> B). --spec(sort_field_table/1 :: - (rabbit_framing:amqp_table()) -> rabbit_framing:amqp_table()). --spec(pid_to_string/1 :: (pid()) -> string()). --spec(string_to_pid/1 :: (string()) -> pid()). --spec(pid_change_node/2 :: (pid(), node()) -> pid()). --spec(node_to_fake_pid/1 :: (atom()) -> pid()). --spec(version_compare/2 :: (string(), string()) -> 'lt' | 'eq' | 'gt'). --spec(version_compare/3 :: - (string(), string(), ('lt' | 'lte' | 'eq' | 'gte' | 'gt')) - -> boolean()). --spec(version_minor_equivalent/2 :: (string(), string()) -> boolean()). --spec(dict_cons/3 :: (any(), any(), dict:dict()) -> dict:dict()). --spec(orddict_cons/3 :: (any(), any(), orddict:orddict()) -> orddict:orddict()). --spec(gb_trees_cons/3 :: (any(), any(), gb_trees:tree()) -> gb_trees:tree()). --spec(gb_trees_fold/3 :: (fun ((any(), any(), A) -> A), A, gb_trees:tree()) - -> A). --spec(gb_trees_foreach/2 :: - (fun ((any(), any()) -> any()), gb_trees:tree()) -> 'ok'). --spec(all_module_attributes/1 :: - (atom()) -> [{atom(), atom(), [term()]}]). --spec(build_acyclic_graph/3 :: - (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) - -> rabbit_types:ok_or_error2(digraph:graph(), - {'vertex', 'duplicate', digraph:vertex()} | - {'edge', ({bad_vertex, digraph:vertex()} | - {bad_edge, [digraph:vertex()]}), - digraph:vertex(), digraph:vertex()})). --spec(const/1 :: (A) -> thunk(A)). --spec(ntoa/1 :: (inet:ip_address()) -> string()). --spec(ntoab/1 :: (inet:ip_address()) -> string()). --spec(is_process_alive/1 :: (pid()) -> boolean()). --spec(pget/2 :: (term(), [term()]) -> term()). --spec(pget/3 :: (term(), [term()], term()) -> term()). --spec(pget_or_die/2 :: (term(), [term()]) -> term() | no_return()). --spec(pmerge/3 :: (term(), term(), [term()]) -> [term()]). --spec(plmerge/2 :: ([term()], [term()]) -> [term()]). --spec(pset/3 :: (term(), term(), [term()]) -> [term()]). --spec(format_message_queue/2 :: (any(), priority_queue:q()) -> term()). --spec(append_rpc_all_nodes/4 :: ([node()], atom(), atom(), [any()]) -> [any()]). --spec(os_cmd/1 :: (string()) -> string()). --spec(is_os_process_alive/1 :: (non_neg_integer()) -> boolean()). --spec(gb_sets_difference/2 :: (gb_sets:set(), gb_sets:set()) -> gb_sets:set()). --spec(version/0 :: () -> string()). --spec(otp_release/0 :: () -> string()). --spec(which_applications/0 :: () -> [{atom(), string(), string()}]). --spec(sequence_error/1 :: ([({'error', any()} | any())]) - -> {'error', any()} | any()). --spec(json_encode/1 :: (any()) -> {'ok', string()} | {'error', any()}). --spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). --spec(json_to_term/1 :: (any()) -> any()). --spec(term_to_json/1 :: (any()) -> any()). --spec(check_expiry/1 :: (integer()) -> rabbit_types:ok_or_error(any())). --spec(base64url/1 :: (binary()) -> string()). --spec(interval_operation/5 :: - ({atom(), atom(), any()}, float(), non_neg_integer(), non_neg_integer(), non_neg_integer()) - -> {any(), non_neg_integer()}). --spec(ensure_timer/4 :: (A, non_neg_integer(), non_neg_integer(), any()) -> A). --spec(stop_timer/2 :: (A, non_neg_integer()) -> A). --spec(send_after/3 :: (non_neg_integer(), pid(), any()) -> tref()). --spec(cancel_timer/1 :: (tref()) -> 'ok'). --spec(get_parent/0 :: () -> pid()). --spec(store_proc_name/2 :: (atom(), rabbit_types:proc_name()) -> ok). --spec(store_proc_name/1 :: (rabbit_types:proc_type_and_name()) -> ok). --spec(moving_average/4 :: (float(), float(), float(), float() | 'undefined') - -> float()). --spec(get_env/3 :: (atom(), atom(), term()) -> term()). --endif. - -%%---------------------------------------------------------------------------- - -method_record_type(Record) -> - element(1, Record). - -polite_pause() -> - polite_pause(3000). - -polite_pause(N) -> - receive - after N -> done - end. - -die(Error) -> - protocol_error(Error, "~w", [Error]). - -frame_error(MethodName, BinaryFields) -> - protocol_error(frame_error, "cannot decode ~w", [BinaryFields], MethodName). - -amqp_error(Name, ExplanationFormat, Params, Method) -> - Explanation = format(ExplanationFormat, Params), - #amqp_error{name = Name, explanation = Explanation, method = Method}. - -protocol_error(Name, ExplanationFormat, Params) -> - protocol_error(Name, ExplanationFormat, Params, none). - -protocol_error(Name, ExplanationFormat, Params, Method) -> - protocol_error(amqp_error(Name, ExplanationFormat, Params, Method)). - -protocol_error(#amqp_error{} = Error) -> - exit(Error). - -not_found(R) -> protocol_error(not_found, "no ~s", [rs(R)]). - -absent(#amqqueue{name = QueueName, pid = QPid, durable = true}, nodedown) -> - %% The assertion of durability is mainly there because we mention - %% durability in the error message. That way we will hopefully - %% notice if at some future point our logic changes s.t. we get - %% here with non-durable queues. - protocol_error(not_found, - "home node '~s' of durable ~s is down or inaccessible", - [node(QPid), rs(QueueName)]); - -absent(#amqqueue{name = QueueName}, crashed) -> - protocol_error(not_found, - "~s has crashed and failed to restart", [rs(QueueName)]). - -type_class(byte) -> int; -type_class(short) -> int; -type_class(signedint) -> int; -type_class(long) -> int; -type_class(decimal) -> int; -type_class(float) -> float; -type_class(double) -> float; -type_class(Other) -> Other. - -assert_args_equivalence(Orig, New, Name, Keys) -> - [assert_args_equivalence1(Orig, New, Name, Key) || Key <- Keys], - ok. - -assert_args_equivalence1(Orig, New, Name, Key) -> - {Orig1, New1} = {table_lookup(Orig, Key), table_lookup(New, Key)}, - case {Orig1, New1} of - {Same, Same} -> - ok; - {{OrigType, OrigVal}, {NewType, NewVal}} -> - case type_class(OrigType) == type_class(NewType) andalso - OrigVal == NewVal of - true -> ok; - false -> assert_field_equivalence(OrigVal, NewVal, Name, Key) - end; - {OrigTypeVal, NewTypeVal} -> - assert_field_equivalence(OrigTypeVal, NewTypeVal, Name, Key) - end. - -assert_field_equivalence(_Orig, _Orig, _Name, _Key) -> - ok; -assert_field_equivalence(Orig, New, Name, Key) -> - equivalence_fail(Orig, New, Name, Key). - -equivalence_fail(Orig, New, Name, Key) -> - protocol_error(precondition_failed, "inequivalent arg '~s' " - "for ~s: received ~s but current is ~s", - [Key, rs(Name), val(New), val(Orig)]). - -val(undefined) -> - "none"; -val({Type, Value}) -> - ValFmt = case is_binary(Value) of - true -> "~s"; - false -> "~p" - end, - format("the value '" ++ ValFmt ++ "' of type '~s'", [Value, Type]); -val(Value) -> - format(case is_binary(Value) of - true -> "'~s'"; - false -> "'~p'" - end, [Value]). - -%% Normally we'd call mnesia:dirty_read/1 here, but that is quite -%% expensive due to general mnesia overheads (figuring out table types -%% and locations, etc). We get away with bypassing these because we -%% know that the tables we are looking at here -%% - are not the schema table -%% - have a local ram copy -%% - do not have any indices -dirty_read({Table, Key}) -> - case ets:lookup(Table, Key) of - [Result] -> {ok, Result}; - [] -> {error, not_found} - end. - -table_lookup(Table, Key) -> - case lists:keysearch(Key, 1, Table) of - {value, {_, TypeBin, ValueBin}} -> {TypeBin, ValueBin}; - false -> undefined - end. - -set_table_value(Table, Key, Type, Value) -> - sort_field_table( - lists:keystore(Key, 1, Table, {Key, Type, Value})). - -r(#resource{virtual_host = VHostPath}, Kind, Name) -> - #resource{virtual_host = VHostPath, kind = Kind, name = Name}; -r(VHostPath, Kind, Name) -> - #resource{virtual_host = VHostPath, kind = Kind, name = Name}. - -r(VHostPath, Kind) -> - #resource{virtual_host = VHostPath, kind = Kind, name = '_'}. - -r_arg(#resource{virtual_host = VHostPath}, Kind, Table, Key) -> - r_arg(VHostPath, Kind, Table, Key); -r_arg(VHostPath, Kind, Table, Key) -> - case table_lookup(Table, Key) of - {longstr, NameBin} -> r(VHostPath, Kind, NameBin); - undefined -> undefined; - {Type, _} -> {error, {invalid_type, Type}} - end. - -rs(#resource{virtual_host = VHostPath, kind = Kind, name = Name}) -> - format("~s '~s' in vhost '~s'", [Kind, Name, VHostPath]). - -enable_cover() -> enable_cover(["."]). - -enable_cover(Dirs) -> - lists:foldl(fun (Dir, ok) -> - case cover:compile_beam_directory( - filename:join(lists:concat([Dir]),"ebin")) of - {error, _} = Err -> Err; - _ -> ok - end; - (_Dir, Err) -> - Err - end, ok, Dirs). - -start_cover(NodesS) -> - {ok, _} = cover:start([rabbit_nodes:make(N) || N <- NodesS]), - ok. - -report_cover() -> report_cover(["."]). - -report_cover(Dirs) -> [report_cover1(lists:concat([Dir])) || Dir <- Dirs], ok. - -report_cover1(Root) -> - Dir = filename:join(Root, "cover"), - ok = filelib:ensure_dir(filename:join(Dir, "junk")), - lists:foreach(fun (F) -> file:delete(F) end, - filelib:wildcard(filename:join(Dir, "*.html"))), - {ok, SummaryFile} = file:open(filename:join(Dir, "summary.txt"), [write]), - {CT, NCT} = - lists:foldl( - fun (M,{CovTot, NotCovTot}) -> - {ok, {M, {Cov, NotCov}}} = cover:analyze(M, module), - ok = report_coverage_percentage(SummaryFile, - Cov, NotCov, M), - {ok,_} = cover:analyze_to_file( - M, - filename:join(Dir, atom_to_list(M) ++ ".html"), - [html]), - {CovTot+Cov, NotCovTot+NotCov} - end, - {0, 0}, - lists:sort(cover:modules())), - ok = report_coverage_percentage(SummaryFile, CT, NCT, 'TOTAL'), - ok = file:close(SummaryFile), - ok. - -report_coverage_percentage(File, Cov, NotCov, Mod) -> - io:fwrite(File, "~6.2f ~p~n", - [if - Cov+NotCov > 0 -> 100.0*Cov/(Cov+NotCov); - true -> 100.0 - end, - Mod]). - -confirm_to_sender(Pid, MsgSeqNos) -> - gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). - -%% @doc Halts the emulator returning the given status code to the os. -%% On Windows this function will block indefinitely so as to give the io -%% subsystem time to flush stdout completely. -quit(Status) -> - case os:type() of - {unix, _} -> halt(Status); - {win32, _} -> init:stop(Status), - receive - after infinity -> ok - end - end. - -throw_on_error(E, Thunk) -> - case Thunk() of - {error, Reason} -> throw({E, Reason}); - {ok, Res} -> Res; - Res -> Res - end. - -with_exit_handler(Handler, Thunk) -> - try - Thunk() - catch - exit:{R, _} when ?IS_BENIGN_EXIT(R) -> Handler(); - exit:{{R, _}, _} when ?IS_BENIGN_EXIT(R) -> Handler() - end. - -is_abnormal_exit(R) when ?IS_BENIGN_EXIT(R) -> false; -is_abnormal_exit({R, _}) when ?IS_BENIGN_EXIT(R) -> false; -is_abnormal_exit(_) -> true. - -filter_exit_map(F, L) -> - Ref = make_ref(), - lists:filter(fun (R) -> R =/= Ref end, - [with_exit_handler( - fun () -> Ref end, - fun () -> F(I) end) || I <- L]). - - -with_user(Username, Thunk) -> - fun () -> - case mnesia:read({rabbit_user, Username}) of - [] -> - mnesia:abort({no_such_user, Username}); - [_U] -> - Thunk() - end - end. - -with_user_and_vhost(Username, VHostPath, Thunk) -> - with_user(Username, rabbit_vhost:with(VHostPath, Thunk)). - -execute_mnesia_transaction(TxFun) -> - %% Making this a sync_transaction allows us to use dirty_read - %% elsewhere and get a consistent result even when that read - %% executes on a different node. - case worker_pool:submit( - fun () -> - case mnesia:is_transaction() of - false -> DiskLogBefore = mnesia_dumper:get_log_writes(), - Res = mnesia:sync_transaction(TxFun), - DiskLogAfter = mnesia_dumper:get_log_writes(), - case DiskLogAfter == DiskLogBefore of - true -> file_handle_cache_stats:update( - mnesia_ram_tx), - Res; - false -> file_handle_cache_stats:update( - mnesia_disk_tx), - {sync, Res} - end; - true -> mnesia:sync_transaction(TxFun) - end - end, single) of - {sync, {atomic, Result}} -> mnesia_sync:sync(), Result; - {sync, {aborted, Reason}} -> throw({error, Reason}); - {atomic, Result} -> Result; - {aborted, Reason} -> throw({error, Reason}) - end. - -%% Like execute_mnesia_transaction/1 with additional Pre- and Post- -%% commit function -execute_mnesia_transaction(TxFun, PrePostCommitFun) -> - case mnesia:is_transaction() of - true -> throw(unexpected_transaction); - false -> ok - end, - PrePostCommitFun(execute_mnesia_transaction( - fun () -> - Result = TxFun(), - PrePostCommitFun(Result, true), - Result - end), false). - -%% Like execute_mnesia_transaction/2, but TxFun is expected to return a -%% TailFun which gets called (only) immediately after the tx commit -execute_mnesia_tx_with_tail(TxFun) -> - case mnesia:is_transaction() of - true -> execute_mnesia_transaction(TxFun); - false -> TailFun = execute_mnesia_transaction(TxFun), - TailFun() - end. - -ensure_ok(ok, _) -> ok; -ensure_ok({error, Reason}, ErrorTag) -> throw({error, {ErrorTag, Reason}}). - -tcp_name(Prefix, IPAddress, Port) - when is_atom(Prefix) andalso is_number(Port) -> - list_to_atom( - format("~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port])). - -format_inet_error(E) -> format("~w (~s)", [E, format_inet_error0(E)]). - -format_inet_error0(address) -> "cannot connect to host/port"; -format_inet_error0(timeout) -> "timed out"; -format_inet_error0(Error) -> inet:format_error(Error). - -%% This is a modified version of Luke Gorrie's pmap - -%% http://lukego.livejournal.com/6753.html - that doesn't care about -%% the order in which results are received. -%% -%% WARNING: This is is deliberately lightweight rather than robust -- if F -%% throws, upmap will hang forever, so make sure F doesn't throw! -upmap(F, L) -> - Parent = self(), - Ref = make_ref(), - [receive {Ref, Result} -> Result end - || _ <- [spawn(fun () -> Parent ! {Ref, F(X)} end) || X <- L]]. - -map_in_order(F, L) -> - lists:reverse( - lists:foldl(fun (E, Acc) -> [F(E) | Acc] end, [], L)). - -%% Apply a pre-post-commit function to all entries in a table that -%% satisfy a predicate, and return those entries. -%% -%% We ignore entries that have been modified or removed. -table_filter(Pred, PrePostCommitFun, TableName) -> - lists:foldl( - fun (E, Acc) -> - case execute_mnesia_transaction( - fun () -> mnesia:match_object(TableName, E, read) =/= [] - andalso Pred(E) end, - fun (false, _Tx) -> false; - (true, Tx) -> PrePostCommitFun(E, Tx), true - end) of - false -> Acc; - true -> [E | Acc] - end - end, [], dirty_read_all(TableName)). - -dirty_read_all(TableName) -> - mnesia:dirty_select(TableName, [{'$1',[],['$1']}]). - -dirty_foreach_key(F, TableName) -> - dirty_foreach_key1(F, TableName, mnesia:dirty_first(TableName)). - -dirty_foreach_key1(_F, _TableName, '$end_of_table') -> - ok; -dirty_foreach_key1(F, TableName, K) -> - case catch mnesia:dirty_next(TableName, K) of - {'EXIT', _} -> - aborted; - NextKey -> - F(K), - dirty_foreach_key1(F, TableName, NextKey) - end. - -dirty_dump_log(FileName) -> - {ok, LH} = disk_log:open([{name, dirty_dump_log}, - {mode, read_only}, - {file, FileName}]), - dirty_dump_log1(LH, disk_log:chunk(LH, start)), - disk_log:close(LH). - -dirty_dump_log1(_LH, eof) -> - io:format("Done.~n"); -dirty_dump_log1(LH, {K, Terms}) -> - io:format("Chunk: ~p~n", [Terms]), - dirty_dump_log1(LH, disk_log:chunk(LH, K)); -dirty_dump_log1(LH, {K, Terms, BadBytes}) -> - io:format("Bad Chunk, ~p: ~p~n", [BadBytes, Terms]), - dirty_dump_log1(LH, disk_log:chunk(LH, K)). - -format(Fmt, Args) -> lists:flatten(io_lib:format(Fmt, Args)). - -format_many(List) -> - lists:flatten([io_lib:format(F ++ "~n", A) || {F, A} <- List]). - -format_stderr(Fmt, Args) -> - case os:type() of - {unix, _} -> - Port = open_port({fd, 0, 2}, [out]), - port_command(Port, io_lib:format(Fmt, Args)), - port_close(Port); - {win32, _} -> - %% stderr on Windows is buffered and I can't figure out a - %% way to trigger a fflush(stderr) in Erlang. So rather - %% than risk losing output we write to stdout instead, - %% which appears to be unbuffered. - io:format(Fmt, Args) - end, - ok. - -unfold(Fun, Init) -> - unfold(Fun, [], Init). - -unfold(Fun, Acc, Init) -> - case Fun(Init) of - {true, E, I} -> unfold(Fun, [E|Acc], I); - false -> {Acc, Init} - end. - -ceil(N) -> - T = trunc(N), - case N == T of - true -> T; - false -> 1 + T - end. - -queue_fold(Fun, Init, Q) -> - case queue:out(Q) of - {empty, _Q} -> Init; - {{value, V}, Q1} -> queue_fold(Fun, Fun(V, Init), Q1) - end. - -%% Sorts a list of AMQP table fields as per the AMQP spec -sort_field_table(Arguments) -> - lists:keysort(1, Arguments). - -%% This provides a string representation of a pid that is the same -%% regardless of what node we are running on. The representation also -%% permits easy identification of the pid's node. -pid_to_string(Pid) when is_pid(Pid) -> - {Node, Cre, Id, Ser} = decompose_pid(Pid), - format("<~s.~B.~B.~B>", [Node, Cre, Id, Ser]). - -%% inverse of above -string_to_pid(Str) -> - Err = {error, {invalid_pid_syntax, Str}}, - %% The \ before the trailing $ is only there to keep emacs - %% font-lock from getting confused. - case re:run(Str, "^<(.*)\\.(\\d+)\\.(\\d+)\\.(\\d+)>\$", - [{capture,all_but_first,list}]) of - {match, [NodeStr, CreStr, IdStr, SerStr]} -> - [Cre, Id, Ser] = lists:map(fun list_to_integer/1, - [CreStr, IdStr, SerStr]), - compose_pid(list_to_atom(NodeStr), Cre, Id, Ser); - nomatch -> - throw(Err) - end. - -pid_change_node(Pid, NewNode) -> - {_OldNode, Cre, Id, Ser} = decompose_pid(Pid), - compose_pid(NewNode, Cre, Id, Ser). - -%% node(node_to_fake_pid(Node)) =:= Node. -node_to_fake_pid(Node) -> - compose_pid(Node, 0, 0, 0). - -decompose_pid(Pid) when is_pid(Pid) -> - %% see http://erlang.org/doc/apps/erts/erl_ext_dist.html (8.10 and - %% 8.7) - <<131,103,100,NodeLen:16,NodeBin:NodeLen/binary,Id:32,Ser:32,Cre:8>> - = term_to_binary(Pid), - Node = binary_to_term(<<131,100,NodeLen:16,NodeBin:NodeLen/binary>>), - {Node, Cre, Id, Ser}. - -compose_pid(Node, Cre, Id, Ser) -> - <<131,NodeEnc/binary>> = term_to_binary(Node), - binary_to_term(<<131,103,NodeEnc/binary,Id:32,Ser:32,Cre:8>>). - -version_compare(A, B, lte) -> - case version_compare(A, B) of - eq -> true; - lt -> true; - gt -> false - end; -version_compare(A, B, gte) -> - case version_compare(A, B) of - eq -> true; - gt -> true; - lt -> false - end; -version_compare(A, B, Result) -> - Result =:= version_compare(A, B). - -version_compare(A, A) -> - eq; -version_compare([], [$0 | B]) -> - version_compare([], dropdot(B)); -version_compare([], _) -> - lt; %% 2.3 < 2.3.1 -version_compare([$0 | A], []) -> - version_compare(dropdot(A), []); -version_compare(_, []) -> - gt; %% 2.3.1 > 2.3 -version_compare(A, B) -> - {AStr, ATl} = lists:splitwith(fun (X) -> X =/= $. end, A), - {BStr, BTl} = lists:splitwith(fun (X) -> X =/= $. end, B), - ANum = list_to_integer(AStr), - BNum = list_to_integer(BStr), - if ANum =:= BNum -> version_compare(dropdot(ATl), dropdot(BTl)); - ANum < BNum -> lt; - ANum > BNum -> gt - end. - -%% a.b.c and a.b.d match, but a.b.c and a.d.e don't. If -%% versions do not match that pattern, just compare them. -version_minor_equivalent(A, B) -> - {ok, RE} = re:compile("^(\\d+\\.\\d+)(\\.\\d+)\$"), - Opts = [{capture, all_but_first, list}], - case {re:run(A, RE, Opts), re:run(B, RE, Opts)} of - {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1; - _ -> A =:= B - end. - -dropdot(A) -> lists:dropwhile(fun (X) -> X =:= $. end, A). - -dict_cons(Key, Value, Dict) -> - dict:update(Key, fun (List) -> [Value | List] end, [Value], Dict). - -orddict_cons(Key, Value, Dict) -> - orddict:update(Key, fun (List) -> [Value | List] end, [Value], Dict). - -gb_trees_cons(Key, Value, Tree) -> - case gb_trees:lookup(Key, Tree) of - {value, Values} -> gb_trees:update(Key, [Value | Values], Tree); - none -> gb_trees:insert(Key, [Value], Tree) - end. - -gb_trees_fold(Fun, Acc, Tree) -> - gb_trees_fold1(Fun, Acc, gb_trees:next(gb_trees:iterator(Tree))). - -gb_trees_fold1(_Fun, Acc, none) -> - Acc; -gb_trees_fold1(Fun, Acc, {Key, Val, It}) -> - gb_trees_fold1(Fun, Fun(Key, Val, Acc), gb_trees:next(It)). - -gb_trees_foreach(Fun, Tree) -> - gb_trees_fold(fun (Key, Val, Acc) -> Fun(Key, Val), Acc end, ok, Tree). - -module_attributes(Module) -> - case catch Module:module_info(attributes) of - {'EXIT', {undef, [{Module, module_info, _} | _]}} -> - io:format("WARNING: module ~p not found, so not scanned for boot steps.~n", - [Module]), - []; - {'EXIT', Reason} -> - exit(Reason); - V -> - V - end. - -all_module_attributes(Name) -> - Targets = - lists:usort( - lists:append( - [[{App, Module} || Module <- Modules] || - {App, _, _} <- application:loaded_applications(), - {ok, Modules} <- [application:get_key(App, modules)]])), - lists:foldl( - fun ({App, Module}, Acc) -> - case lists:append([Atts || {N, Atts} <- module_attributes(Module), - N =:= Name]) of - [] -> Acc; - Atts -> [{App, Module, Atts} | Acc] - end - end, [], Targets). - -build_acyclic_graph(VertexFun, EdgeFun, Graph) -> - G = digraph:new([acyclic]), - try - [case digraph:vertex(G, Vertex) of - false -> digraph:add_vertex(G, Vertex, Label); - _ -> ok = throw({graph_error, {vertex, duplicate, Vertex}}) - end || GraphElem <- Graph, - {Vertex, Label} <- VertexFun(GraphElem)], - [case digraph:add_edge(G, From, To) of - {error, E} -> throw({graph_error, {edge, E, From, To}}); - _ -> ok - end || GraphElem <- Graph, - {From, To} <- EdgeFun(GraphElem)], - {ok, G} - catch {graph_error, Reason} -> - true = digraph:delete(G), - {error, Reason} - end. - -const(X) -> fun () -> X end. - -%% Format IPv4-mapped IPv6 addresses as IPv4, since they're what we see -%% when IPv6 is enabled but not used (i.e. 99% of the time). -ntoa({0,0,0,0,0,16#ffff,AB,CD}) -> - inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256}); -ntoa(IP) -> - inet_parse:ntoa(IP). - -ntoab(IP) -> - Str = ntoa(IP), - case string:str(Str, ":") of - 0 -> Str; - _ -> "[" ++ Str ++ "]" - end. - -%% We try to avoid reconnecting to down nodes here; this is used in a -%% loop in rabbit_amqqueue:on_node_down/1 and any delays we incur -%% would be bad news. -%% -%% See also rabbit_mnesia:is_process_alive/1 which also requires the -%% process be in the same running cluster as us (i.e. not partitioned -%% or some random node). -is_process_alive(Pid) -> - Node = node(Pid), - lists:member(Node, [node() | nodes()]) andalso - rpc:call(Node, erlang, is_process_alive, [Pid]) =:= true. - -pget(K, P) -> proplists:get_value(K, P). -pget(K, P, D) -> proplists:get_value(K, P, D). - -pget_or_die(K, P) -> - case proplists:get_value(K, P) of - undefined -> exit({error, key_missing, K}); - V -> V - end. - -%% property merge -pmerge(Key, Val, List) -> - case proplists:is_defined(Key, List) of - true -> List; - _ -> [{Key, Val} | List] - end. - -%% proplists merge -plmerge(P1, P2) -> - dict:to_list(dict:merge(fun(_, V, _) -> - V - end, - dict:from_list(P1), - dict:from_list(P2))). - -pset(Key, Value, List) -> [{Key, Value} | proplists:delete(Key, List)]. - -format_message_queue(_Opt, MQ) -> - Len = priority_queue:len(MQ), - {Len, - case Len > 100 of - false -> priority_queue:to_list(MQ); - true -> {summary, - orddict:to_list( - lists:foldl( - fun ({P, V}, Counts) -> - orddict:update_counter( - {P, format_message_queue_entry(V)}, 1, Counts) - end, orddict:new(), priority_queue:to_list(MQ)))} - end}. - -format_message_queue_entry(V) when is_atom(V) -> - V; -format_message_queue_entry(V) when is_tuple(V) -> - list_to_tuple([format_message_queue_entry(E) || E <- tuple_to_list(V)]); -format_message_queue_entry(_V) -> - '_'. - -append_rpc_all_nodes(Nodes, M, F, A) -> - {ResL, _} = rpc:multicall(Nodes, M, F, A), - lists:append([case Res of - {badrpc, _} -> []; - _ -> Res - end || Res <- ResL]). - -os_cmd(Command) -> - case os:type() of - {win32, _} -> - %% Clink workaround; see - %% http://code.google.com/p/clink/issues/detail?id=141 - os:cmd(" " ++ Command); - _ -> - %% Don't just return "/bin/sh: <cmd>: not found" if not found - Exec = hd(string:tokens(Command, " ")), - case os:find_executable(Exec) of - false -> throw({command_not_found, Exec}); - _ -> os:cmd(Command) - end - end. - -is_os_process_alive(Pid) -> - with_os([{unix, fun () -> - run_ps(Pid) =:= 0 - end}, - {win32, fun () -> - Cmd = "tasklist /nh /fi \"pid eq " ++ Pid ++ "\" ", - Res = os_cmd(Cmd ++ "2>&1"), - case re:run(Res, "erl\\.exe", [{capture, none}]) of - match -> true; - _ -> false - end - end}]). - -with_os(Handlers) -> - {OsFamily, _} = os:type(), - case proplists:get_value(OsFamily, Handlers) of - undefined -> throw({unsupported_os, OsFamily}); - Handler -> Handler() - end. - -run_ps(Pid) -> - Port = erlang:open_port({spawn, "ps -p " ++ Pid}, - [exit_status, {line, 16384}, - use_stdio, stderr_to_stdout]), - exit_loop(Port). - -exit_loop(Port) -> - receive - {Port, {exit_status, Rc}} -> Rc; - {Port, _} -> exit_loop(Port) - end. - -gb_sets_difference(S1, S2) -> - gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). - -version() -> - {ok, VSN} = application:get_key(rabbit, vsn), - VSN. - -%% See http://www.erlang.org/doc/system_principles/versions.html -otp_release() -> - File = filename:join([code:root_dir(), "releases", - erlang:system_info(otp_release), "OTP_VERSION"]), - case file:read_file(File) of - {ok, VerBin} -> - %% 17.0 or later, we need the file for the minor version - string:strip(binary_to_list(VerBin), both, $\n); - {error, _} -> - %% R16B03 or earlier (no file, otp_release is correct) - %% or we couldn't read the file (so this is best we can do) - erlang:system_info(otp_release) - end. - -%% application:which_applications(infinity) is dangerous, since it can -%% cause deadlocks on shutdown. So we have to use a timeout variant, -%% but w/o creating spurious timeout errors. -which_applications() -> - try - application:which_applications() - catch - exit:{timeout, _} -> [] - end. - -sequence_error([T]) -> T; -sequence_error([{error, _} = Error | _]) -> Error; -sequence_error([_ | Rest]) -> sequence_error(Rest). - -json_encode(Term) -> - try - {ok, mochijson2:encode(Term)} - catch - exit:{json_encode, E} -> - {error, E} - end. - -json_decode(Term) -> - try - {ok, mochijson2:decode(Term)} - catch - %% Sadly `mochijson2:decode/1' does not offer a nice way to catch - %% decoding errors... - error:_ -> error - end. - -json_to_term({struct, L}) -> - [{K, json_to_term(V)} || {K, V} <- L]; -json_to_term(L) when is_list(L) -> - [json_to_term(I) || I <- L]; -json_to_term(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse - V =:= true orelse V =:= false -> - V. - -%% This has the flaw that empty lists will never be JSON objects, so use with -%% care. -term_to_json([{_, _}|_] = L) -> - {struct, [{K, term_to_json(V)} || {K, V} <- L]}; -term_to_json(L) when is_list(L) -> - [term_to_json(I) || I <- L]; -term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse - V =:= true orelse V =:= false -> - V. - -check_expiry(N) when N < 0 -> {error, {value_negative, N}}; -check_expiry(_N) -> ok. - -base64url(In) -> - lists:reverse(lists:foldl(fun ($\+, Acc) -> [$\- | Acc]; - ($\/, Acc) -> [$\_ | Acc]; - ($\=, Acc) -> Acc; - (Chr, Acc) -> [Chr | Acc] - end, [], base64:encode_to_string(In))). - -%% Ideally, you'd want Fun to run every IdealInterval. but you don't -%% want it to take more than MaxRatio of IdealInterval. So if it takes -%% more then you want to run it less often. So we time how long it -%% takes to run, and then suggest how long you should wait before -%% running it again with a user specified max interval. Times are in millis. -interval_operation({M, F, A}, MaxRatio, MaxInterval, IdealInterval, LastInterval) -> - {Micros, Res} = timer:tc(M, F, A), - {Res, case {Micros > 1000 * (MaxRatio * IdealInterval), - Micros > 1000 * (MaxRatio * LastInterval)} of - {true, true} -> lists:min([MaxInterval, - round(LastInterval * 1.5)]); - {true, false} -> LastInterval; - {false, false} -> lists:max([IdealInterval, - round(LastInterval / 1.5)]) - end}. - -ensure_timer(State, Idx, After, Msg) -> - case element(Idx, State) of - undefined -> TRef = send_after(After, self(), Msg), - setelement(Idx, State, TRef); - _ -> State - end. - -stop_timer(State, Idx) -> - case element(Idx, State) of - undefined -> State; - TRef -> cancel_timer(TRef), - setelement(Idx, State, undefined) - end. - -%% timer:send_after/3 goes through a single timer process but allows -%% long delays. erlang:send_after/3 does not have a bottleneck but -%% only allows max 2^32-1 millis. --define(MAX_ERLANG_SEND_AFTER, 4294967295). -send_after(Millis, Pid, Msg) when Millis > ?MAX_ERLANG_SEND_AFTER -> - {ok, Ref} = timer:send_after(Millis, Pid, Msg), - {timer, Ref}; -send_after(Millis, Pid, Msg) -> - {erlang, erlang:send_after(Millis, Pid, Msg)}. - -cancel_timer({erlang, Ref}) -> erlang:cancel_timer(Ref), - ok; -cancel_timer({timer, Ref}) -> {ok, cancel} = timer:cancel(Ref), - ok. - -store_proc_name(Type, ProcName) -> store_proc_name({Type, ProcName}). -store_proc_name(TypeProcName) -> put(process_name, TypeProcName). - -%% application:get_env/3 is only available in R16B01 or later. -get_env(Application, Key, Def) -> - case application:get_env(Application, Key) of - {ok, Val} -> Val; - undefined -> Def - end. - -moving_average(_Time, _HalfLife, Next, undefined) -> - Next; -%% We want the Weight to decrease as Time goes up (since Weight is the -%% weight for the current sample, not the new one), so that the moving -%% average decays at the same speed regardless of how long the time is -%% between samplings. So we want Weight = math:exp(Something), where -%% Something turns out to be negative. -%% -%% We want to determine Something here in terms of the Time taken -%% since the last measurement, and a HalfLife. So we want Weight = -%% math:exp(Time * Constant / HalfLife). What should Constant be? We -%% want Weight to be 0.5 when Time = HalfLife. -%% -%% Plug those numbers in and you get 0.5 = math:exp(Constant). Take -%% the log of each side and you get math:log(0.5) = Constant. -moving_average(Time, HalfLife, Next, Current) -> - Weight = math:exp(Time * math:log(0.5) / HalfLife), - Next * (1 - Weight) + Current * Weight. - -%% ------------------------------------------------------------------------- -%% Begin copypasta from gen_server2.erl - -get_parent() -> - case get('$ancestors') of - [Parent | _] when is_pid (Parent) -> Parent; - [Parent | _] when is_atom(Parent) -> name_to_pid(Parent); - _ -> exit(process_was_not_started_by_proc_lib) - end. - -name_to_pid(Name) -> - case whereis(Name) of - undefined -> case whereis_name(Name) of - undefined -> exit(could_not_find_registerd_name); - Pid -> Pid - end; - Pid -> Pid - end. - -whereis_name(Name) -> - case ets:lookup(global_names, Name) of - [{_Name, Pid, _Method, _RPid, _Ref}] -> - if node(Pid) == node() -> case erlang:is_process_alive(Pid) of - true -> Pid; - false -> undefined - end; - true -> Pid - end; - [] -> undefined - end. - -%% End copypasta from gen_server2.erl -%% ------------------------------------------------------------------------- diff --git a/src/rabbit_msg_store_index.erl b/src/rabbit_msg_store_index.erl deleted file mode 100644 index 0c7a37bcd3..0000000000 --- a/src/rabbit_msg_store_index.erl +++ /dev/null @@ -1,59 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_msg_store_index). - --include("rabbit_msg_store.hrl"). - --ifdef(use_specs). - --type(dir() :: any()). --type(index_state() :: any()). --type(keyvalue() :: any()). --type(fieldpos() :: non_neg_integer()). --type(fieldvalue() :: any()). - --callback new(dir()) -> index_state(). --callback recover(dir()) -> rabbit_types:ok_or_error2(index_state(), any()). --callback lookup(rabbit_types:msg_id(), index_state()) -> ('not_found' | keyvalue()). --callback insert(keyvalue(), index_state()) -> 'ok'. --callback update(keyvalue(), index_state()) -> 'ok'. --callback update_fields(rabbit_types:msg_id(), ({fieldpos(), fieldvalue()} | - [{fieldpos(), fieldvalue()}]), - index_state()) -> 'ok'. --callback delete(rabbit_types:msg_id(), index_state()) -> 'ok'. --callback delete_object(keyvalue(), index_state()) -> 'ok'. --callback delete_by_file(fieldvalue(), index_state()) -> 'ok'. --callback terminate(index_state()) -> any(). - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{new, 1}, - {recover, 1}, - {lookup, 2}, - {insert, 2}, - {update, 2}, - {update_fields, 3}, - {delete, 2}, - {delete_by_file, 2}, - {terminate, 1}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl deleted file mode 100644 index b6cf9842ff..0000000000 --- a/src/rabbit_net.erl +++ /dev/null @@ -1,246 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_net). --include("rabbit.hrl"). - --export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, - recv/1, sync_recv/2, async_recv/3, port_command/2, getopts/2, - setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, - peercert/1, connection_string/2, socket_ends/2, is_loopback/1]). - -%%--------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([socket/0]). - --type(stat_option() :: - 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' | - 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'). --type(ok_val_or_error(A) :: rabbit_types:ok_or_error2(A, any())). --type(ok_or_any_error() :: rabbit_types:ok_or_error(any())). --type(socket() :: port() | #ssl_socket{}). --type(opts() :: [{atom(), any()} | - {raw, non_neg_integer(), non_neg_integer(), binary()}]). --type(host_or_ip() :: binary() | inet:ip_address()). --spec(is_ssl/1 :: (socket()) -> boolean()). --spec(ssl_info/1 :: (socket()) - -> 'nossl' | ok_val_or_error( - [{atom(), any()}])). --spec(controlling_process/2 :: (socket(), pid()) -> ok_or_any_error()). --spec(getstat/2 :: - (socket(), [stat_option()]) - -> ok_val_or_error([{stat_option(), integer()}])). --spec(recv/1 :: (socket()) -> - {'data', [char()] | binary()} | 'closed' | - rabbit_types:error(any()) | {'other', any()}). --spec(sync_recv/2 :: (socket(), integer()) -> rabbit_types:ok(binary()) | - rabbit_types:error(any())). --spec(async_recv/3 :: - (socket(), integer(), timeout()) -> rabbit_types:ok(any())). --spec(port_command/2 :: (socket(), iolist()) -> 'true'). --spec(getopts/2 :: (socket(), [atom() | {raw, - non_neg_integer(), - non_neg_integer(), - non_neg_integer() | binary()}]) - -> ok_val_or_error(opts())). --spec(setopts/2 :: (socket(), opts()) -> ok_or_any_error()). --spec(send/2 :: (socket(), binary() | iolist()) -> ok_or_any_error()). --spec(close/1 :: (socket()) -> ok_or_any_error()). --spec(fast_close/1 :: (socket()) -> ok_or_any_error()). --spec(sockname/1 :: - (socket()) - -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). --spec(peername/1 :: - (socket()) - -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). --spec(peercert/1 :: - (socket()) - -> 'nossl' | ok_val_or_error(rabbit_ssl:certificate())). --spec(connection_string/2 :: - (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())). --spec(socket_ends/2 :: - (socket(), 'inbound' | 'outbound') - -> ok_val_or_error({host_or_ip(), rabbit_networking:ip_port(), - host_or_ip(), rabbit_networking:ip_port()})). --spec(is_loopback/1 :: (socket() | inet:ip_address()) -> boolean()). - --endif. - -%%--------------------------------------------------------------------------- - --define(SSL_CLOSE_TIMEOUT, 5000). - --define(IS_SSL(Sock), is_record(Sock, ssl_socket)). - -is_ssl(Sock) -> ?IS_SSL(Sock). - -ssl_info(Sock) when ?IS_SSL(Sock) -> - ssl_compat:connection_information(Sock#ssl_socket.ssl); -ssl_info(_Sock) -> - nossl. - -controlling_process(Sock, Pid) when ?IS_SSL(Sock) -> - ssl:controlling_process(Sock#ssl_socket.ssl, Pid); -controlling_process(Sock, Pid) when is_port(Sock) -> - gen_tcp:controlling_process(Sock, Pid). - -getstat(Sock, Stats) when ?IS_SSL(Sock) -> - inet:getstat(Sock#ssl_socket.tcp, Stats); -getstat(Sock, Stats) when is_port(Sock) -> - inet:getstat(Sock, Stats). - -recv(Sock) when ?IS_SSL(Sock) -> - recv(Sock#ssl_socket.ssl, {ssl, ssl_closed, ssl_error}); -recv(Sock) when is_port(Sock) -> - recv(Sock, {tcp, tcp_closed, tcp_error}). - -recv(S, {DataTag, ClosedTag, ErrorTag}) -> - receive - {DataTag, S, Data} -> {data, Data}; - {ClosedTag, S} -> closed; - {ErrorTag, S, Reason} -> {error, Reason}; - Other -> {other, Other} - end. - -sync_recv(Sock, Length) when ?IS_SSL(Sock) -> - ssl:recv(Sock#ssl_socket.ssl, Length); -sync_recv(Sock, Length) -> - gen_tcp:recv(Sock, Length). - -async_recv(Sock, Length, Timeout) when ?IS_SSL(Sock) -> - Pid = self(), - Ref = make_ref(), - - spawn(fun () -> Pid ! {inet_async, Sock, Ref, - ssl:recv(Sock#ssl_socket.ssl, Length, Timeout)} - end), - - {ok, Ref}; -async_recv(Sock, Length, infinity) when is_port(Sock) -> - prim_inet:async_recv(Sock, Length, -1); -async_recv(Sock, Length, Timeout) when is_port(Sock) -> - prim_inet:async_recv(Sock, Length, Timeout). - -port_command(Sock, Data) when ?IS_SSL(Sock) -> - case ssl:send(Sock#ssl_socket.ssl, Data) of - ok -> self() ! {inet_reply, Sock, ok}, - true; - {error, Reason} -> erlang:error(Reason) - end; -port_command(Sock, Data) when is_port(Sock) -> - erlang:port_command(Sock, Data). - -getopts(Sock, Options) when ?IS_SSL(Sock) -> - ssl:getopts(Sock#ssl_socket.ssl, Options); -getopts(Sock, Options) when is_port(Sock) -> - inet:getopts(Sock, Options). - -setopts(Sock, Options) when ?IS_SSL(Sock) -> - ssl:setopts(Sock#ssl_socket.ssl, Options); -setopts(Sock, Options) when is_port(Sock) -> - inet:setopts(Sock, Options). - -send(Sock, Data) when ?IS_SSL(Sock) -> ssl:send(Sock#ssl_socket.ssl, Data); -send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data). - -close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock#ssl_socket.ssl); -close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). - -fast_close(Sock) when ?IS_SSL(Sock) -> - %% We cannot simply port_close the underlying tcp socket since the - %% TLS protocol is quite insistent that a proper closing handshake - %% should take place (see RFC 5245 s7.2.1). So we call ssl:close - %% instead, but that can block for a very long time, e.g. when - %% there is lots of pending output and there is tcp backpressure, - %% or the ssl_connection process has entered the the - %% workaround_transport_delivery_problems function during - %% termination, which, inexplicably, does a gen_tcp:recv(Socket, - %% 0), which may never return if the client doesn't send a FIN or - %% that gets swallowed by the network. Since there is no timeout - %% variant of ssl:close, we construct our own. - {Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock#ssl_socket.ssl) end), - erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}), - receive - {Pid, ssl_close_timeout} -> - erlang:demonitor(MRef, [flush]), - exit(Pid, kill); - {'DOWN', MRef, process, Pid, _Reason} -> - ok - end, - catch port_close(Sock#ssl_socket.tcp), - ok; -fast_close(Sock) when is_port(Sock) -> - catch port_close(Sock), ok. - -sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock#ssl_socket.ssl); -sockname(Sock) when is_port(Sock) -> inet:sockname(Sock). - -peername(Sock) when ?IS_SSL(Sock) -> ssl:peername(Sock#ssl_socket.ssl); -peername(Sock) when is_port(Sock) -> inet:peername(Sock). - -peercert(Sock) when ?IS_SSL(Sock) -> ssl:peercert(Sock#ssl_socket.ssl); -peercert(Sock) when is_port(Sock) -> nossl. - -connection_string(Sock, Direction) -> - case socket_ends(Sock, Direction) of - {ok, {FromAddress, FromPort, ToAddress, ToPort}} -> - {ok, rabbit_misc:format( - "~s:~p -> ~s:~p", - [maybe_ntoab(FromAddress), FromPort, - maybe_ntoab(ToAddress), ToPort])}; - Error -> - Error - end. - -socket_ends(Sock, Direction) -> - {From, To} = sock_funs(Direction), - case {From(Sock), To(Sock)} of - {{ok, {FromAddress, FromPort}}, {ok, {ToAddress, ToPort}}} -> - {ok, {rdns(FromAddress), FromPort, - rdns(ToAddress), ToPort}}; - {{error, _Reason} = Error, _} -> - Error; - {_, {error, _Reason} = Error} -> - Error - end. - -maybe_ntoab(Addr) when is_tuple(Addr) -> rabbit_misc:ntoab(Addr); -maybe_ntoab(Host) -> Host. - -rdns(Addr) -> - case application:get_env(rabbit, reverse_dns_lookups) of - {ok, true} -> list_to_binary(rabbit_networking:tcp_host(Addr)); - _ -> Addr - end. - -sock_funs(inbound) -> {fun peername/1, fun sockname/1}; -sock_funs(outbound) -> {fun sockname/1, fun peername/1}. - -is_loopback(Sock) when is_port(Sock) ; ?IS_SSL(Sock) -> - case sockname(Sock) of - {ok, {Addr, _Port}} -> is_loopback(Addr); - {error, _} -> false - end; -%% We could parse the results of inet:getifaddrs() instead. But that -%% would be more complex and less maybe Windows-compatible... -is_loopback({127,_,_,_}) -> true; -is_loopback({0,0,0,0,0,0,0,1}) -> true; -is_loopback({0,0,0,0,0,65535,AB,CD}) -> is_loopback(ipv4(AB, CD)); -is_loopback(_) -> false. - -ipv4(AB, CD) -> {AB bsr 8, AB band 255, CD bsr 8, CD band 255}. diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl deleted file mode 100644 index f95f8c5818..0000000000 --- a/src/rabbit_networking.erl +++ /dev/null @@ -1,608 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_networking). - --export([boot/0, start/0, start_tcp_listener/1, start_ssl_listener/2, - stop_tcp_listener/1, on_node_down/1, active_listeners/0, - node_listeners/1, register_connection/1, unregister_connection/1, - connections/0, connection_info_keys/0, - connection_info/1, connection_info/2, - connection_info_all/0, connection_info_all/1, - close_connection/2, force_connection_event_refresh/1, tcp_host/1]). - -%%used by TCP-based transports, e.g. STOMP adapter --export([tcp_listener_addresses/1, tcp_listener_spec/6, - ensure_ssl/0, fix_ssl_options/1, poodle_check/1, ssl_transform_fun/1]). - --export([tcp_listener_started/3, tcp_listener_stopped/3, - start_client/1, start_ssl_client/2]). - -%% Internal --export([connections_local/0]). - --import(rabbit_misc, [pget/2, pget/3, pset/3]). - --include("rabbit.hrl"). --include_lib("kernel/include/inet.hrl"). - --define(FIRST_TEST_BIND_PORT, 10000). - -%% POODLE --define(BAD_SSL_PROTOCOL_VERSIONS, [sslv3]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --export_type([ip_port/0, hostname/0]). - --type(hostname() :: inet:hostname()). --type(ip_port() :: inet:port_number()). - --type(family() :: atom()). --type(listener_config() :: ip_port() | - {hostname(), ip_port()} | - {hostname(), ip_port(), family()}). --type(address() :: {inet:ip_address(), ip_port(), family()}). --type(name_prefix() :: atom()). --type(protocol() :: atom()). --type(label() :: string()). - --spec(start/0 :: () -> 'ok'). --spec(start_tcp_listener/1 :: (listener_config()) -> 'ok'). --spec(start_ssl_listener/2 :: - (listener_config(), rabbit_types:infos()) -> 'ok'). --spec(stop_tcp_listener/1 :: (listener_config()) -> 'ok'). --spec(active_listeners/0 :: () -> [rabbit_types:listener()]). --spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). --spec(register_connection/1 :: (pid()) -> ok). --spec(unregister_connection/1 :: (pid()) -> ok). --spec(connections/0 :: () -> [rabbit_types:connection()]). --spec(connections_local/0 :: () -> [rabbit_types:connection()]). --spec(connection_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(connection_info/1 :: - (rabbit_types:connection()) -> rabbit_types:infos()). --spec(connection_info/2 :: - (rabbit_types:connection(), rabbit_types:info_keys()) - -> rabbit_types:infos()). --spec(connection_info_all/0 :: () -> [rabbit_types:infos()]). --spec(connection_info_all/1 :: - (rabbit_types:info_keys()) -> [rabbit_types:infos()]). --spec(close_connection/2 :: (pid(), string()) -> 'ok'). --spec(force_connection_event_refresh/1 :: (reference()) -> 'ok'). - --spec(on_node_down/1 :: (node()) -> 'ok'). --spec(tcp_listener_addresses/1 :: (listener_config()) -> [address()]). --spec(tcp_listener_spec/6 :: - (name_prefix(), address(), [gen_tcp:listen_option()], protocol(), - label(), rabbit_types:mfargs()) -> supervisor:child_spec()). --spec(ensure_ssl/0 :: () -> rabbit_types:infos()). --spec(fix_ssl_options/1 :: (rabbit_types:infos()) -> rabbit_types:infos()). --spec(poodle_check/1 :: (atom()) -> 'ok' | 'danger'). --spec(ssl_transform_fun/1 :: - (rabbit_types:infos()) - -> fun ((rabbit_net:socket()) - -> rabbit_types:ok_or_error(#ssl_socket{}))). - --spec(boot/0 :: () -> 'ok'). --spec(start_client/1 :: - (port() | #ssl_socket{ssl::{'sslsocket',_,_}}) -> - atom() | pid() | port() | {atom(),atom()}). --spec(start_ssl_client/2 :: - (_,port() | #ssl_socket{ssl::{'sslsocket',_,_}}) -> - atom() | pid() | port() | {atom(),atom()}). --spec(tcp_listener_started/3 :: - (_, - string() | - {byte(),byte(),byte(),byte()} | - {char(),char(),char(),char(),char(),char(),char(),char()}, - _) -> - 'ok'). --spec(tcp_listener_stopped/3 :: - (_, - string() | - {byte(),byte(),byte(),byte()} | - {char(),char(),char(),char(),char(),char(),char(),char()}, - _) -> - 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -boot() -> - ok = record_distribution_listener(), - ok = start(), - ok = boot_tcp(), - ok = boot_ssl(). - -boot_tcp() -> - {ok, TcpListeners} = application:get_env(tcp_listeners), - [ok = start_tcp_listener(Listener) || Listener <- TcpListeners], - ok. - -boot_ssl() -> - case application:get_env(ssl_listeners) of - {ok, []} -> - ok; - {ok, SslListeners} -> - SslOpts = ensure_ssl(), - case poodle_check('AMQP') of - ok -> [start_ssl_listener(L, SslOpts) || L <- SslListeners]; - danger -> ok - end, - ok - end. - -start() -> rabbit_sup:start_supervisor_child( - rabbit_tcp_client_sup, rabbit_client_sup, - [{local, rabbit_tcp_client_sup}, - {rabbit_connection_sup,start_link,[]}]). - -ensure_ssl() -> - {ok, SslAppsConfig} = application:get_env(rabbit, ssl_apps), - ok = app_utils:start_applications(SslAppsConfig), - {ok, SslOptsConfig} = application:get_env(rabbit, ssl_options), - fix_ssl_options(SslOptsConfig). - -poodle_check(Context) -> - {ok, Vsn} = application:get_key(ssl, vsn), - case rabbit_misc:version_compare(Vsn, "5.3", gte) of %% R16B01 - true -> ok; - false -> case application:get_env(rabbit, ssl_allow_poodle_attack) of - {ok, true} -> ok; - _ -> log_poodle_fail(Context), - danger - end - end. - -log_poodle_fail(Context) -> - rabbit_log:error( - "The installed version of Erlang (~s) contains the bug OTP-10905,~n" - "which makes it impossible to disable SSLv3. This makes the system~n" - "vulnerable to the POODLE attack. SSL listeners for ~s have therefore~n" - "been disabled.~n~n" - "You are advised to upgrade to a recent Erlang version; R16B01 is the~n" - "first version in which this bug is fixed, but later is usually~n" - "better.~n~n" - "If you cannot upgrade now and want to re-enable SSL listeners, you can~n" - "set the config item 'ssl_allow_poodle_attack' to 'true' in the~n" - "'rabbit' section of your configuration file.~n", - [rabbit_misc:otp_release(), Context]). - -fix_ssl_options(Config) -> - fix_verify_fun(fix_ssl_protocol_versions(Config)). - -fix_verify_fun(SslOptsConfig) -> - %% Starting with ssl 4.0.1 in Erlang R14B, the verify_fun function - %% takes 3 arguments and returns a tuple. - {ok, SslAppVer} = application:get_key(ssl, vsn), - UseNewVerifyFun = rabbit_misc:version_compare(SslAppVer, "4.0.1", gte), - case rabbit_misc:pget(verify_fun, SslOptsConfig) of - {Module, Function, InitialUserState} -> - Fun = make_verify_fun(Module, Function, InitialUserState, - UseNewVerifyFun), - rabbit_misc:pset(verify_fun, Fun, SslOptsConfig); - {Module, Function} -> - Fun = make_verify_fun(Module, Function, none, - UseNewVerifyFun), - rabbit_misc:pset(verify_fun, Fun, SslOptsConfig); - undefined when UseNewVerifyFun -> - SslOptsConfig; - undefined -> - % unknown_ca errors are silently ignored prior to R14B unless we - % supply this verify_fun - remove when at least R14B is required - case proplists:get_value(verify, SslOptsConfig, verify_none) of - verify_none -> SslOptsConfig; - verify_peer -> [{verify_fun, fun([]) -> true; - ([_|_]) -> false - end} - | SslOptsConfig] - end - end. - -make_verify_fun(Module, Function, InitialUserState, UseNewVerifyFun) -> - try - %% Preload the module: it is required to use - %% erlang:function_exported/3. - Module:module_info() - catch - _:Exception -> - rabbit_log:error("SSL verify_fun: module ~s missing: ~p~n", - [Module, Exception]), - throw({error, {invalid_verify_fun, missing_module}}) - end, - NewForm = erlang:function_exported(Module, Function, 3), - OldForm = erlang:function_exported(Module, Function, 1), - case {NewForm, OldForm} of - {true, _} when UseNewVerifyFun -> - %% This verify_fun is supported by Erlang R14B+ (ssl - %% 4.0.1 and later). - Fun = fun(OtpCert, Event, UserState) -> - Module:Function(OtpCert, Event, UserState) - end, - {Fun, InitialUserState}; - {_, true} -> - %% This verify_fun is supported by: - %% o Erlang up-to R13B; - %% o Erlang R14B+ for undocumented backward - %% compatibility. - %% - %% InitialUserState is ignored in this case. - fun(ErrorList) -> - Module:Function(ErrorList) - end; - {_, false} when not UseNewVerifyFun -> - rabbit_log:error("SSL verify_fun: ~s:~s/1 form required " - "for Erlang R13B~n", [Module, Function]), - throw({error, {invalid_verify_fun, old_form_required}}); - _ -> - Arity = case UseNewVerifyFun of true -> 3; _ -> 1 end, - rabbit_log:error("SSL verify_fun: no ~s:~s/~b exported~n", - [Module, Function, Arity]), - throw({error, {invalid_verify_fun, function_not_exported}}) - end. - -fix_ssl_protocol_versions(Config) -> - case application:get_env(rabbit, ssl_allow_poodle_attack) of - {ok, true} -> - Config; - _ -> - Configured = case pget(versions, Config) of - undefined -> pget(available, ssl:versions(), []); - Vs -> Vs - end, - pset(versions, Configured -- ?BAD_SSL_PROTOCOL_VERSIONS, Config) - end. - -ssl_timeout() -> - {ok, Val} = application:get_env(rabbit, ssl_handshake_timeout), - Val. - -ssl_transform_fun(SslOpts) -> - fun (Sock) -> - Timeout = ssl_timeout(), - case catch ssl:ssl_accept(Sock, SslOpts, Timeout) of - {ok, SslSock} -> - {ok, #ssl_socket{tcp = Sock, ssl = SslSock}}; - {error, timeout} -> - {error, {ssl_upgrade_error, timeout}}; - {error, Reason} -> - %% We have no idea what state the ssl_connection - %% process is in - it could still be happily - %% going, it might be stuck, or it could be just - %% about to fail. There is little that our caller - %% can do but close the TCP socket, but this could - %% cause ssl alerts to get dropped (which is bad - %% form, according to the TLS spec). So we give - %% the ssl_connection a little bit of time to send - %% such alerts. - timer:sleep(Timeout), - {error, {ssl_upgrade_error, Reason}}; - {'EXIT', Reason} -> - {error, {ssl_upgrade_failure, Reason}} - end - end. - -tcp_listener_addresses(Port) when is_integer(Port) -> - tcp_listener_addresses_auto(Port); -tcp_listener_addresses({"auto", Port}) -> - %% Variant to prevent lots of hacking around in bash and batch files - tcp_listener_addresses_auto(Port); -tcp_listener_addresses({Host, Port}) -> - %% auto: determine family IPv4 / IPv6 after converting to IP address - tcp_listener_addresses({Host, Port, auto}); -tcp_listener_addresses({Host, Port, Family0}) - when is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) -> - [{IPAddress, Port, Family} || - {IPAddress, Family} <- getaddr(Host, Family0)]; -tcp_listener_addresses({_Host, Port, _Family0}) -> - rabbit_log:error("invalid port ~p - not 0..65535~n", [Port]), - throw({error, {invalid_port, Port}}). - -tcp_listener_addresses_auto(Port) -> - lists:append([tcp_listener_addresses(Listener) || - Listener <- port_to_listeners(Port)]). - -tcp_listener_spec(NamePrefix, {IPAddress, Port, Family}, SocketOpts, - Protocol, Label, OnConnect) -> - {rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), - {tcp_listener_sup, start_link, - [IPAddress, Port, [Family | SocketOpts], - {?MODULE, tcp_listener_started, [Protocol]}, - {?MODULE, tcp_listener_stopped, [Protocol]}, - OnConnect, Label]}, - transient, infinity, supervisor, [tcp_listener_sup]}. - -start_tcp_listener(Listener) -> - start_listener(Listener, amqp, "TCP Listener", - {?MODULE, start_client, []}). - -start_ssl_listener(Listener, SslOpts) -> - start_listener(Listener, 'amqp/ssl', "SSL Listener", - {?MODULE, start_ssl_client, [SslOpts]}). - -start_listener(Listener, Protocol, Label, OnConnect) -> - [start_listener0(Address, Protocol, Label, OnConnect) || - Address <- tcp_listener_addresses(Listener)], - ok. - -start_listener0(Address, Protocol, Label, OnConnect) -> - Spec = tcp_listener_spec(rabbit_tcp_listener_sup, Address, tcp_opts(), - Protocol, Label, OnConnect), - case supervisor:start_child(rabbit_sup, Spec) of - {ok, _} -> ok; - {error, {shutdown, _}} -> {IPAddress, Port, _Family} = Address, - exit({could_not_start_tcp_listener, - {rabbit_misc:ntoa(IPAddress), Port}}) - end. - -stop_tcp_listener(Listener) -> - [stop_tcp_listener0(Address) || - Address <- tcp_listener_addresses(Listener)], - ok. - -stop_tcp_listener0({IPAddress, Port, _Family}) -> - Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), - ok = supervisor:terminate_child(rabbit_sup, Name), - ok = supervisor:delete_child(rabbit_sup, Name). - -tcp_listener_started(Protocol, IPAddress, Port) -> - %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1 - %% We need the host so we can distinguish multiple instances of the above - %% in a cluster. - ok = mnesia:dirty_write( - rabbit_listener, - #listener{node = node(), - protocol = Protocol, - host = tcp_host(IPAddress), - ip_address = IPAddress, - port = Port}). - -tcp_listener_stopped(Protocol, IPAddress, Port) -> - ok = mnesia:dirty_delete_object( - rabbit_listener, - #listener{node = node(), - protocol = Protocol, - host = tcp_host(IPAddress), - ip_address = IPAddress, - port = Port}). - -record_distribution_listener() -> - {Name, Host} = rabbit_nodes:parts(node()), - {port, Port, _Version} = erl_epmd:port_please(Name, Host), - tcp_listener_started(clustering, {0,0,0,0,0,0,0,0}, Port). - -active_listeners() -> - rabbit_misc:dirty_read_all(rabbit_listener). - -node_listeners(Node) -> - mnesia:dirty_read(rabbit_listener, Node). - -on_node_down(Node) -> - case lists:member(Node, nodes()) of - false -> ok = mnesia:dirty_delete(rabbit_listener, Node); - true -> rabbit_log:info( - "Keep ~s listeners: the node is already back~n", [Node]) - end. - -start_client(Sock, SockTransform) -> - {ok, _Child, Reader} = supervisor:start_child(rabbit_tcp_client_sup, []), - ok = rabbit_net:controlling_process(Sock, Reader), - Reader ! {go, Sock, SockTransform}, - - %% In the event that somebody floods us with connections, the - %% reader processes can spew log events at error_logger faster - %% than it can keep up, causing its mailbox to grow unbounded - %% until we eat all the memory available and crash. So here is a - %% meaningless synchronous call to the underlying gen_event - %% mechanism. When it returns the mailbox is drained, and we - %% return to our caller to accept more connetions. - gen_event:which_handlers(error_logger), - - Reader. - -start_client(Sock) -> - start_client(Sock, fun (S) -> {ok, S} end). - -start_ssl_client(SslOpts, Sock) -> - start_client(Sock, ssl_transform_fun(SslOpts)). - -register_connection(Pid) -> pg_local:join(rabbit_connections, Pid). - -unregister_connection(Pid) -> pg_local:leave(rabbit_connections, Pid). - -connections() -> - rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), - rabbit_networking, connections_local, []). - -connections_local() -> pg_local:get_members(rabbit_connections). - -connection_info_keys() -> rabbit_reader:info_keys(). - -connection_info(Pid) -> rabbit_reader:info(Pid). -connection_info(Pid, Items) -> rabbit_reader:info(Pid, Items). - -connection_info_all() -> cmap(fun (Q) -> connection_info(Q) end). -connection_info_all(Items) -> cmap(fun (Q) -> connection_info(Q, Items) end). - -close_connection(Pid, Explanation) -> - rabbit_log:info("Closing connection ~p because ~p~n", [Pid, Explanation]), - case lists:member(Pid, connections()) of - true -> rabbit_reader:shutdown(Pid, Explanation); - false -> throw({error, {not_a_connection_pid, Pid}}) - end. - -force_connection_event_refresh(Ref) -> - [rabbit_reader:force_event_refresh(C, Ref) || C <- connections()], - ok. - -%%-------------------------------------------------------------------- - -tcp_host({0,0,0,0}) -> - hostname(); - -tcp_host({0,0,0,0,0,0,0,0}) -> - hostname(); - -tcp_host(IPAddress) -> - case inet:gethostbyaddr(IPAddress) of - {ok, #hostent{h_name = Name}} -> Name; - {error, _Reason} -> rabbit_misc:ntoa(IPAddress) - end. - -hostname() -> - {ok, Hostname} = inet:gethostname(), - case inet:gethostbyname(Hostname) of - {ok, #hostent{h_name = Name}} -> Name; - {error, _Reason} -> Hostname - end. - -cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). - -tcp_opts() -> - {ok, ConfigOpts} = application:get_env(rabbit, tcp_listen_options), - merge_essential_tcp_listen_options(ConfigOpts). - --define(ESSENTIAL_LISTEN_OPTIONS, - [binary, - {active, false}, - {packet, raw}, - {reuseaddr, true}, - {nodelay, true}]). - -merge_essential_tcp_listen_options(Opts) -> - lists:foldl(fun ({K, _} = Opt, Acc) -> - lists:keystore(K, 1, Acc, Opt); - (Opt, Acc) -> - [Opt | Acc] - end , Opts, ?ESSENTIAL_LISTEN_OPTIONS). - -%% inet_parse:address takes care of ip string, like "0.0.0.0" -%% inet:getaddr returns immediately for ip tuple {0,0,0,0}, -%% and runs 'inet_gethost' port process for dns lookups. -%% On Windows inet:getaddr runs dns resolver for ip string, which may fail. -getaddr(Host, Family) -> - case inet_parse:address(Host) of - {ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}]; - {error, _} -> gethostaddr(Host, Family) - end. - -gethostaddr(Host, auto) -> - Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]], - case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of - [] -> host_lookup_error(Host, Lookups); - IPs -> IPs - end; - -gethostaddr(Host, Family) -> - case inet:getaddr(Host, Family) of - {ok, IPAddress} -> [{IPAddress, Family}]; - {error, Reason} -> host_lookup_error(Host, Reason) - end. - -host_lookup_error(Host, Reason) -> - rabbit_log:error("invalid host ~p - ~p~n", [Host, Reason]), - throw({error, {invalid_host, Host, Reason}}). - -resolve_family({_,_,_,_}, auto) -> inet; -resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6; -resolve_family(IP, auto) -> throw({error, {strange_family, IP}}); -resolve_family(_, F) -> F. - -%%-------------------------------------------------------------------- - -%% There are three kinds of machine (for our purposes). -%% -%% * Those which treat IPv4 addresses as a special kind of IPv6 address -%% ("Single stack") -%% - Linux by default, Windows Vista and later -%% - We also treat any (hypothetical?) IPv6-only machine the same way -%% * Those which consider IPv6 and IPv4 to be completely separate things -%% ("Dual stack") -%% - OpenBSD, Windows XP / 2003, Linux if so configured -%% * Those which do not support IPv6. -%% - Ancient/weird OSes, Linux if so configured -%% -%% How to reconfigure Linux to test this: -%% Single stack (default): -%% echo 0 > /proc/sys/net/ipv6/bindv6only -%% Dual stack: -%% echo 1 > /proc/sys/net/ipv6/bindv6only -%% IPv4 only: -%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then -%% sudo update-grub && sudo reboot -%% -%% This matters in (and only in) the case where the sysadmin (or the -%% app descriptor) has only supplied a port and we wish to bind to -%% "all addresses". This means different things depending on whether -%% we're single or dual stack. On single stack binding to "::" -%% implicitly includes all IPv4 addresses, and subsequently attempting -%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will -%% only bind to IPv6 addresses, and we need another listener bound to -%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only -%% want to bind to "0.0.0.0". -%% -%% Unfortunately it seems there is no way to detect single vs dual stack -%% apart from attempting to bind to the port. -port_to_listeners(Port) -> - IPv4 = {"0.0.0.0", Port, inet}, - IPv6 = {"::", Port, inet6}, - case ipv6_status(?FIRST_TEST_BIND_PORT) of - single_stack -> [IPv6]; - ipv6_only -> [IPv6]; - dual_stack -> [IPv6, IPv4]; - ipv4_only -> [IPv4] - end. - -ipv6_status(TestPort) -> - IPv4 = [inet, {ip, {0,0,0,0}}], - IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}], - case gen_tcp:listen(TestPort, IPv6) of - {ok, LSock6} -> - case gen_tcp:listen(TestPort, IPv4) of - {ok, LSock4} -> - %% Dual stack - gen_tcp:close(LSock6), - gen_tcp:close(LSock4), - dual_stack; - %% Checking the error here would only let us - %% distinguish single stack IPv6 / IPv4 vs IPv6 only, - %% which we figure out below anyway. - {error, _} -> - gen_tcp:close(LSock6), - case gen_tcp:listen(TestPort, IPv4) of - %% Single stack - {ok, LSock4} -> gen_tcp:close(LSock4), - single_stack; - %% IPv6-only machine. Welcome to the future. - {error, eafnosupport} -> ipv6_only; %% Linux - {error, eprotonosupport}-> ipv6_only; %% FreeBSD - %% Dual stack machine with something already - %% on IPv4. - {error, _} -> ipv6_status(TestPort + 1) - end - end; - %% IPv4-only machine. Welcome to the 90s. - {error, eafnosupport} -> %% Linux - ipv4_only; - {error, eprotonosupport} -> %% FreeBSD - ipv4_only; - %% Port in use - {error, _} -> - ipv6_status(TestPort + 1) - end. diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl deleted file mode 100644 index 57d971715b..0000000000 --- a/src/rabbit_nodes.erl +++ /dev/null @@ -1,221 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_nodes). - --export([names/1, diagnostics/1, make/1, parts/1, cookie_hash/0, - is_running/2, is_process_running/2, - cluster_name/0, set_cluster_name/1, ensure_epmd/0, - all_running/0]). - --include_lib("kernel/include/inet.hrl"). - --define(EPMD_TIMEOUT, 30000). --define(TCP_DIAGNOSTIC_TIMEOUT, 5000). - -%%---------------------------------------------------------------------------- -%% Specs -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(names/1 :: (string()) -> rabbit_types:ok_or_error2( - [{string(), integer()}], term())). --spec(diagnostics/1 :: ([node()]) -> string()). --spec(make/1 :: ({string(), string()} | string()) -> node()). --spec(parts/1 :: (node() | string()) -> {string(), string()}). --spec(cookie_hash/0 :: () -> string()). --spec(is_running/2 :: (node(), atom()) -> boolean()). --spec(is_process_running/2 :: (node(), atom()) -> boolean()). --spec(cluster_name/0 :: () -> binary()). --spec(set_cluster_name/1 :: (binary()) -> 'ok'). --spec(ensure_epmd/0 :: () -> 'ok'). --spec(all_running/0 :: () -> [node()]). - --endif. - -%%---------------------------------------------------------------------------- - -names(Hostname) -> - Self = self(), - Ref = make_ref(), - {Pid, MRef} = spawn_monitor( - fun () -> Self ! {Ref, net_adm:names(Hostname)} end), - timer:exit_after(?EPMD_TIMEOUT, Pid, timeout), - receive - {Ref, Names} -> erlang:demonitor(MRef, [flush]), - Names; - {'DOWN', MRef, process, Pid, Reason} -> {error, Reason} - end. - -diagnostics(Nodes) -> - NodeDiags = [{"~nDIAGNOSTICS~n===========~n~n" - "attempted to contact: ~p~n", [Nodes]}] ++ - [diagnostics_node(Node) || Node <- Nodes] ++ - current_node_details(), - rabbit_misc:format_many(lists:flatten(NodeDiags)). - -current_node_details() -> - [{"~ncurrent node details:~n- node name: ~w", [node()]}, - case init:get_argument(home) of - {ok, [[Home]]} -> {"- home dir: ~s", [Home]}; - Other -> {"- no home dir: ~p", [Other]} - end, - {"- cookie hash: ~s", [cookie_hash()]}]. - -diagnostics_node(Node) -> - {Name, Host} = parts(Node), - [{"~s:", [Node]} | - case names(Host) of - {error, Reason} -> - [{" * unable to connect to epmd (port ~s) on ~s: ~s~n", - [epmd_port(), Host, rabbit_misc:format_inet_error(Reason)]}]; - {ok, NamePorts} -> - [{" * connected to epmd (port ~s) on ~s", - [epmd_port(), Host]}] ++ - case net_adm:ping(Node) of - pong -> dist_working_diagnostics(Node); - pang -> dist_broken_diagnostics(Name, Host, NamePorts) - end - end]. - -epmd_port() -> - case init:get_argument(epmd_port) of - {ok, [[Port | _] | _]} when is_list(Port) -> Port; - error -> "4369" - end. - -dist_working_diagnostics(Node) -> - case rabbit:is_running(Node) of - true -> [{" * node ~s up, 'rabbit' application running", [Node]}]; - false -> [{" * node ~s up, 'rabbit' application not running~n" - " * running applications on ~s: ~p~n" - " * suggestion: start_app on ~s", - [Node, Node, remote_apps(Node), Node]}] - end. - -remote_apps(Node) -> - %% We want a timeout here because really, we don't trust the node, - %% the last thing we want to do is hang. - case rpc:call(Node, application, which_applications, [5000]) of - {badrpc, _} = E -> E; - Apps -> [App || {App, _, _} <- Apps] - end. - -dist_broken_diagnostics(Name, Host, NamePorts) -> - case [{N, P} || {N, P} <- NamePorts, N =:= Name] of - [] -> - {SelfName, SelfHost} = parts(node()), - Others = [list_to_atom(N) || {N, _} <- NamePorts, - N =/= case SelfHost of - Host -> SelfName; - _ -> never_matches - end], - OthersDiag = case Others of - [] -> [{" no other nodes on ~s", - [Host]}]; - _ -> [{" other nodes on ~s: ~p", - [Host, Others]}] - end, - [{" * epmd reports: node '~s' not running at all", [Name]}, - OthersDiag, {" * suggestion: start the node", []}]; - [{Name, Port}] -> - [{" * epmd reports node '~s' running on port ~b", [Name, Port]} | - case diagnose_connect(Host, Port) of - ok -> - [{" * TCP connection succeeded but Erlang distribution " - "failed~n" - " * suggestion: hostname mismatch?~n" - " * suggestion: is the cookie set correctly?~n" - " * suggestion: is the Erlang distribution using TLS?", []}]; - {error, Reason} -> - [{" * can't establish TCP connection, reason: ~s~n" - " * suggestion: blocked by firewall?", - [rabbit_misc:format_inet_error(Reason)]}] - end] - end. - -diagnose_connect(Host, Port) -> - case inet:gethostbyname(Host) of - {ok, #hostent{h_addrtype = Family}} -> - case gen_tcp:connect(Host, Port, [Family], - ?TCP_DIAGNOSTIC_TIMEOUT) of - {ok, Socket} -> gen_tcp:close(Socket), - ok; - {error, _} = E -> E - end; - {error, _} = E -> - E - end. - -make({Prefix, Suffix}) -> list_to_atom(lists:append([Prefix, "@", Suffix])); -make(NodeStr) -> make(parts(NodeStr)). - -parts(Node) when is_atom(Node) -> - parts(atom_to_list(Node)); -parts(NodeStr) -> - case lists:splitwith(fun (E) -> E =/= $@ end, NodeStr) of - {Prefix, []} -> {_, Suffix} = parts(node()), - {Prefix, Suffix}; - {Prefix, Suffix} -> {Prefix, tl(Suffix)} - end. - -cookie_hash() -> - base64:encode_to_string(erlang:md5(atom_to_list(erlang:get_cookie()))). - -is_running(Node, Application) -> - case rpc:call(Node, rabbit_misc, which_applications, []) of - {badrpc, _} -> false; - Apps -> proplists:is_defined(Application, Apps) - end. - -is_process_running(Node, Process) -> - case rpc:call(Node, erlang, whereis, [Process]) of - {badrpc, _} -> false; - undefined -> false; - P when is_pid(P) -> true - end. - -cluster_name() -> - rabbit_runtime_parameters:value_global( - cluster_name, cluster_name_default()). - -cluster_name_default() -> - {ID, _} = rabbit_nodes:parts(node()), - {ok, Host} = inet:gethostname(), - {ok, #hostent{h_name = FQDN}} = inet:gethostbyname(Host), - list_to_binary(atom_to_list(rabbit_nodes:make({ID, FQDN}))). - -set_cluster_name(Name) -> - rabbit_runtime_parameters:set_global(cluster_name, Name). - -ensure_epmd() -> - {ok, Prog} = init:get_argument(progname), - ID = random:uniform(1000000000), - Port = open_port( - {spawn_executable, os:find_executable(Prog)}, - [{args, ["-sname", rabbit_misc:format("epmd-starter-~b", [ID]), - "-noshell", "-eval", "halt()."]}, - exit_status, stderr_to_stdout, use_stdio]), - port_shutdown_loop(Port). - -port_shutdown_loop(Port) -> - receive - {Port, {exit_status, _Rc}} -> ok; - {Port, _} -> port_shutdown_loop(Port) - end. - -all_running() -> rabbit_mnesia:cluster_nodes(running). diff --git a/src/rabbit_password_hashing.erl b/src/rabbit_password_hashing.erl deleted file mode 100644 index 3c87093624..0000000000 --- a/src/rabbit_password_hashing.erl +++ /dev/null @@ -1,33 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_password_hashing). --include("rabbit.hrl"). - --ifdef(use_specs). - --callback hash(rabbit_types:password()) -> rabbit_types:password_hash(). - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{hash, 1}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 3297032346..84828e018f 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -93,6 +93,8 @@ list(PluginsDir) -> {AvailablePlugins, Problems} = lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) -> {Plugins1, [{EZ, Reason} | Problems1]}; + (#plugin{name = rabbit_common}, {Plugins1, Problems1}) -> + {Plugins1, Problems1}; (Plugin = #plugin{}, {Plugins1, Problems1}) -> {[Plugin|Plugins1], Problems1} end, {[], []}, diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl deleted file mode 100644 index 7ebea83516..0000000000 --- a/src/rabbit_policy_validator.erl +++ /dev/null @@ -1,39 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_policy_validator). - --ifdef(use_specs). - --export_type([validate_results/0]). - --type(validate_results() :: - 'ok' | {error, string(), [term()]} | [validate_results()]). - --callback validate_policy([{binary(), term()}]) -> validate_results(). - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [ - {validate_policy, 1} - ]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_queue_collector.erl b/src/rabbit_queue_collector.erl deleted file mode 100644 index d4e1c71da2..0000000000 --- a/src/rabbit_queue_collector.erl +++ /dev/null @@ -1,95 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_queue_collector). - -%% Queue collector keeps track of exclusive queues and cleans them -%% up e.g. when their connection is closed. - --behaviour(gen_server). - --export([start_link/1, register/2, delete_all/1]). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --record(state, {monitors, delete_from}). - --include("rabbit.hrl"). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start_link/1 :: (rabbit_types:proc_name()) -> - rabbit_types:ok_pid_or_error()). --spec(register/2 :: (pid(), pid()) -> 'ok'). --spec(delete_all/1 :: (pid()) -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -start_link(ProcName) -> - gen_server:start_link(?MODULE, [ProcName], []). - -register(CollectorPid, Q) -> - gen_server:call(CollectorPid, {register, Q}, infinity). - -delete_all(CollectorPid) -> - gen_server:call(CollectorPid, delete_all, infinity). - -%%---------------------------------------------------------------------------- - -init([ProcName]) -> - ?store_proc_name(ProcName), - {ok, #state{monitors = pmon:new(), delete_from = undefined}}. - -%%-------------------------------------------------------------------------- - -handle_call({register, QPid}, _From, - State = #state{monitors = QMons, delete_from = Deleting}) -> - case Deleting of - undefined -> ok; - _ -> ok = rabbit_amqqueue:delete_immediately([QPid]) - end, - {reply, ok, State#state{monitors = pmon:monitor(QPid, QMons)}}; - -handle_call(delete_all, From, State = #state{monitors = QMons, - delete_from = undefined}) -> - case pmon:monitored(QMons) of - [] -> {reply, ok, State#state{delete_from = From}}; - QPids -> ok = rabbit_amqqueue:delete_immediately(QPids), - {noreply, State#state{delete_from = From}} - end. - -handle_cast(Msg, State) -> - {stop, {unhandled_cast, Msg}, State}. - -handle_info({'DOWN', _MRef, process, DownPid, _Reason}, - State = #state{monitors = QMons, delete_from = Deleting}) -> - QMons1 = pmon:erase(DownPid, QMons), - case Deleting =/= undefined andalso pmon:is_empty(QMons1) of - true -> gen_server:reply(Deleting, ok); - false -> ok - end, - {noreply, State#state{monitors = QMons1}}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl deleted file mode 100644 index 0c6f0820c7..0000000000 --- a/src/rabbit_queue_decorator.erl +++ /dev/null @@ -1,80 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_queue_decorator). - --include("rabbit.hrl"). - --export([select/1, set/1, register/2, unregister/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --callback startup(rabbit_types:amqqueue()) -> 'ok'. - --callback shutdown(rabbit_types:amqqueue()) -> 'ok'. - --callback policy_changed(rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> - 'ok'. - --callback active_for(rabbit_types:amqqueue()) -> boolean(). - -%% called with Queue, MaxActivePriority, IsEmpty --callback consumer_state_changed( - rabbit_types:amqqueue(), integer(), boolean()) -> 'ok'. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{startup, 1}, {shutdown, 1}, {policy_changed, 2}, - {active_for, 1}, {consumer_state_changed, 3}]; -behaviour_info(_Other) -> - undefined. - --endif. - -%%---------------------------------------------------------------------------- - -select(Modules) -> - [M || M <- Modules, code:which(M) =/= non_existing]. - -set(Q) -> Q#amqqueue{decorators = [D || D <- list(), D:active_for(Q)]}. - -list() -> [M || {_, M} <- rabbit_registry:lookup_all(queue_decorator)]. - -register(TypeName, ModuleName) -> - rabbit_registry:register(queue_decorator, TypeName, ModuleName), - [maybe_recover(Q) || Q <- rabbit_amqqueue:list()], - ok. - -unregister(TypeName) -> - rabbit_registry:unregister(queue_decorator, TypeName), - [maybe_recover(Q) || Q <- rabbit_amqqueue:list()], - ok. - -maybe_recover(Q = #amqqueue{name = Name, - decorators = Decs}) -> - #amqqueue{decorators = Decs1} = set(Q), - Old = lists:sort(select(Decs)), - New = lists:sort(select(Decs1)), - case New of - Old -> ok; - _ -> [M:startup(Q) || M <- New -- Old], - rabbit_amqqueue:update_decorators(Name) - end. diff --git a/src/rabbit_queue_master_locator.erl b/src/rabbit_queue_master_locator.erl deleted file mode 100644 index caaa7bbbde..0000000000 --- a/src/rabbit_queue_master_locator.erl +++ /dev/null @@ -1,33 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_queue_master_locator). - --ifdef(use_specs). - --callback description() -> [proplists:property()]. --callback queue_master_location(pid()) -> {'ok', node()} | {'error', term()}. - --else. - --export([behaviour_info/1]). -behaviour_info(callbacks) -> - [{description, 0}, - {queue_master_location, 1}]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl deleted file mode 100644 index f66a80d811..0000000000 --- a/src/rabbit_reader.erl +++ /dev/null @@ -1,1412 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_reader). - -%% This is an AMQP 0-9-1 connection implementation. If AMQP 1.0 plugin is enabled, -%% this module passes control of incoming AMQP 1.0 connections to it. -%% -%% Every connection (as in, a process using this module) -%% is a controlling process for a server socket. -%% -%% Connections have a number of responsibilities: -%% -%% * Performing protocol handshake -%% * Parsing incoming data and dispatching protocol methods -%% * Authenticating clients (with the help of authentication backends) -%% * Enforcing TCP backpressure (throttling clients) -%% * Enforcing connection limits, e.g. channel_max -%% * Channel management -%% * Setting up heartbeater and alarm notifications -%% * Emitting connection and network activity metric events -%% * Gracefully handling client disconnects, channel termination, etc -%% -%% and a few more. -%% -%% Every connection has -%% -%% * a queue collector which is responsible for keeping -%% track of exclusive queues on the connection and their cleanup. -%% * a heartbeater that's responsible for sending heartbeat frames to clients, -%% keeping track of the incoming ones and notifying connection about -%% heartbeat timeouts -%% * Stats timer, a timer that is used to periodically emit metric events -%% -%% Some dependencies are started under a separate supervisor to avoid deadlocks -%% during system shutdown. See rabbit_channel_sup:start_link/0 for details. -%% -%% Reader processes are special processes (in the OTP sense). - --include("rabbit_framing.hrl"). --include("rabbit.hrl"). - --export([start_link/1, info_keys/0, info/1, info/2, force_event_refresh/2, - shutdown/2]). - --export([system_continue/3, system_terminate/4, system_code_change/4]). - --export([init/2, mainloop/4, recvloop/4]). - --export([conserve_resources/3, server_properties/1]). - --define(NORMAL_TIMEOUT, 3). --define(CLOSING_TIMEOUT, 30). --define(CHANNEL_TERMINATION_TIMEOUT, 3). -%% we wait for this many seconds before closing TCP connection -%% with a client that failed to log in. Provides some relief -%% from connection storms and DoS. --define(SILENT_CLOSE_DELAY, 3). --define(CHANNEL_MIN, 1). - -%%-------------------------------------------------------------------------- - --record(v1, { - %% parent process - parent, - %% socket - sock, - %% connection state, see connection record - connection, - callback, - recv_len, - pending_recv, - %% pre_init | securing | running | blocking | blocked | closing | closed | {become, F} - connection_state, - %% see comment in rabbit_connection_sup:start_link/0 - helper_sup, - %% takes care of cleaning up exclusive queues, - %% see rabbit_queue_collector - queue_collector, - %% sends and receives heartbeat frames, - %% see rabbit_heartbeat - heartbeater, - %% timer used to emit statistics - stats_timer, - %% channel supervisor - channel_sup_sup_pid, - %% how many channels this connection has - channel_count, - %% throttling state, for both - %% credit- and resource-driven flow control - throttle}). - --record(connection, { - %% e.g. <<"127.0.0.1:55054 -> 127.0.0.1:5672">> - name, - %% server host - host, - %% client host - peer_host, - %% server port - port, - %% client port - peer_port, - %% protocol framing implementation module, - %% e.g. rabbit_framing_amqp_0_9_1 - protocol, - user, - %% heartbeat timeout value used, 0 means - %% heartbeats are disabled - timeout_sec, - %% maximum allowed frame size, - %% see frame_max in the AMQP 0-9-1 spec - frame_max, - %% greatest channel number allowed, - %% see channel_max in the AMQP 0-9-1 spec - channel_max, - vhost, - %% client name, version, platform, etc - client_properties, - %% what lists protocol extensions - %% does this client support? - capabilities, - %% authentication mechanism used - %% as a pair of {Name, Module} - auth_mechanism, - %% authentication mechanism state, - %% initialised by rabbit_auth_mechanism:init/1 - %% implementations - auth_state, - %% time of connection - connected_at}). - --record(throttle, { - %% list of active alarms - alarmed_by, - %% flow | resource - last_blocked_by, - %% never | timestamp() - last_blocked_at -}). - --define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, - send_pend, state, channels]). - --define(CREATION_EVENT_KEYS, - [pid, name, port, peer_port, host, - peer_host, ssl, peer_cert_subject, peer_cert_issuer, - peer_cert_validity, auth_mechanism, ssl_protocol, - ssl_key_exchange, ssl_cipher, ssl_hash, protocol, user, vhost, - timeout, frame_max, channel_max, client_properties, connected_at]). - --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). - --define(AUTH_NOTIFICATION_INFO_KEYS, - [host, vhost, name, peer_host, peer_port, protocol, auth_mechanism, - ssl, ssl_protocol, ssl_cipher, peer_cert_issuer, peer_cert_subject, - peer_cert_validity]). - --define(IS_RUNNING(State), - (State#v1.connection_state =:= running orelse - State#v1.connection_state =:= blocking orelse - State#v1.connection_state =:= blocked)). - --define(IS_STOPPING(State), - (State#v1.connection_state =:= closing orelse - State#v1.connection_state =:= closed)). - -%%-------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start_link/1 :: (pid()) -> rabbit_types:ok(pid())). --spec(info_keys/0 :: () -> rabbit_types:info_keys()). --spec(info/1 :: (pid()) -> rabbit_types:infos()). --spec(info/2 :: (pid(), rabbit_types:info_keys()) -> rabbit_types:infos()). --spec(force_event_refresh/2 :: (pid(), reference()) -> 'ok'). --spec(shutdown/2 :: (pid(), string()) -> 'ok'). --spec(conserve_resources/3 :: (pid(), atom(), boolean()) -> 'ok'). --spec(server_properties/1 :: (rabbit_types:protocol()) -> - rabbit_framing:amqp_table()). - -%% These specs only exists to add no_return() to keep dialyzer happy --spec(init/2 :: (pid(), pid()) -> no_return()). --spec(start_connection/5 :: - (pid(), pid(), any(), rabbit_net:socket(), - fun ((rabbit_net:socket()) -> - rabbit_types:ok_or_error2( - rabbit_net:socket(), any()))) -> no_return()). - --spec(mainloop/4 :: (_,[binary()], non_neg_integer(), #v1{}) -> any()). --spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}). --spec(system_continue/3 :: (_,_,{[binary()], non_neg_integer(), #v1{}}) -> - any()). --spec(system_terminate/4 :: (_,_,_,_) -> none()). - --endif. - -%%-------------------------------------------------------------------------- - -start_link(HelperSup) -> - {ok, proc_lib:spawn_link(?MODULE, init, [self(), HelperSup])}. - -shutdown(Pid, Explanation) -> - gen_server:call(Pid, {shutdown, Explanation}, infinity). - -init(Parent, HelperSup) -> - Deb = sys:debug_options([]), - receive - {go, Sock, SockTransform} -> - start_connection(Parent, HelperSup, Deb, Sock, SockTransform) - end. - -system_continue(Parent, Deb, {Buf, BufLen, State}) -> - mainloop(Deb, Buf, BufLen, State#v1{parent = Parent}). - -system_terminate(Reason, _Parent, _Deb, _State) -> - exit(Reason). - -system_code_change(Misc, _Module, _OldVsn, _Extra) -> - {ok, Misc}. - -info_keys() -> ?INFO_KEYS. - -info(Pid) -> - gen_server:call(Pid, info, infinity). - -info(Pid, Items) -> - case gen_server:call(Pid, {info, Items}, infinity) of - {ok, Res} -> Res; - {error, Error} -> throw(Error) - end. - -force_event_refresh(Pid, Ref) -> - gen_server:cast(Pid, {force_event_refresh, Ref}). - -conserve_resources(Pid, Source, Conserve) -> - Pid ! {conserve_resources, Source, Conserve}, - ok. - -server_properties(Protocol) -> - {ok, Product} = application:get_key(rabbit, id), - {ok, Version} = application:get_key(rabbit, vsn), - - %% Get any configuration-specified server properties - {ok, RawConfigServerProps} = application:get_env(rabbit, - server_properties), - - %% Normalize the simplifed (2-tuple) and unsimplified (3-tuple) forms - %% from the config and merge them with the generated built-in properties - NormalizedConfigServerProps = - [{<<"capabilities">>, table, server_capabilities(Protocol)} | - [case X of - {KeyAtom, Value} -> {list_to_binary(atom_to_list(KeyAtom)), - longstr, - maybe_list_to_binary(Value)}; - {BinKey, Type, Value} -> {BinKey, Type, Value} - end || X <- RawConfigServerProps ++ - [{product, Product}, - {version, Version}, - {cluster_name, rabbit_nodes:cluster_name()}, - {platform, "Erlang/OTP"}, - {copyright, ?COPYRIGHT_MESSAGE}, - {information, ?INFORMATION_MESSAGE}]]], - - %% Filter duplicated properties in favour of config file provided values - lists:usort(fun ({K1,_,_}, {K2,_,_}) -> K1 =< K2 end, - NormalizedConfigServerProps). - -maybe_list_to_binary(V) when is_binary(V) -> V; -maybe_list_to_binary(V) when is_list(V) -> list_to_binary(V). - -server_capabilities(rabbit_framing_amqp_0_9_1) -> - [{<<"publisher_confirms">>, bool, true}, - {<<"exchange_exchange_bindings">>, bool, true}, - {<<"basic.nack">>, bool, true}, - {<<"consumer_cancel_notify">>, bool, true}, - {<<"connection.blocked">>, bool, true}, - {<<"consumer_priorities">>, bool, true}, - {<<"authentication_failure_close">>, bool, true}, - {<<"per_consumer_qos">>, bool, true}]; -server_capabilities(_) -> - []. - -%%-------------------------------------------------------------------------- - -log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args). - -socket_error(Reason) when is_atom(Reason) -> - log(error, "Error on AMQP connection ~p: ~s~n", - [self(), rabbit_misc:format_inet_error(Reason)]); -socket_error(Reason) -> - Level = - case Reason of - {ssl_upgrade_error, closed} -> - %% The socket was closed while upgrading to SSL. - %% This is presumably a TCP healthcheck, so don't log - %% it unless specified otherwise. - debug; - _ -> - error - end, - log(Level, "Error on AMQP connection ~p:~n~p~n", [self(), Reason]). - -inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F). - -socket_op(Sock, Fun) -> - case Fun(Sock) of - {ok, Res} -> Res; - {error, Reason} -> socket_error(Reason), - %% NB: this is tcp socket, even in case of ssl - rabbit_net:fast_close(Sock), - exit(normal) - end. - -start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> - process_flag(trap_exit, true), - Name = case rabbit_net:connection_string(Sock, inbound) of - {ok, Str} -> Str; - {error, enotconn} -> rabbit_net:fast_close(Sock), - exit(normal); - {error, Reason} -> socket_error(Reason), - rabbit_net:fast_close(Sock), - exit(normal) - end, - {ok, HandshakeTimeout} = application:get_env(rabbit, handshake_timeout), - InitialFrameMax = application:get_env(rabbit, initial_frame_max, ?FRAME_MIN_SIZE), - ClientSock = socket_op(Sock, SockTransform), - erlang:send_after(HandshakeTimeout, self(), handshake_timeout), - {PeerHost, PeerPort, Host, Port} = - socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end), - ?store_proc_name(list_to_binary(Name)), - State = #v1{parent = Parent, - sock = ClientSock, - connection = #connection{ - name = list_to_binary(Name), - host = Host, - peer_host = PeerHost, - port = Port, - peer_port = PeerPort, - protocol = none, - user = none, - timeout_sec = (HandshakeTimeout / 1000), - frame_max = InitialFrameMax, - vhost = none, - client_properties = none, - capabilities = [], - auth_mechanism = none, - auth_state = none, - connected_at = time_compat:os_system_time( - milli_seconds)}, - callback = uninitialized_callback, - recv_len = 0, - pending_recv = false, - connection_state = pre_init, - queue_collector = undefined, %% started on tune-ok - helper_sup = HelperSup, - heartbeater = none, - channel_sup_sup_pid = none, - channel_count = 0, - throttle = #throttle{ - alarmed_by = [], - last_blocked_by = none, - last_blocked_at = never}}, - try - run({?MODULE, recvloop, - [Deb, [], 0, switch_callback(rabbit_event:init_stats_timer( - State, #v1.stats_timer), - handshake, 8)]}), - log(info, "closing AMQP connection ~p (~s)~n", [self(), Name]) - catch - Ex -> - log_connection_exception(Name, Ex) - after - %% We don't call gen_tcp:close/1 here since it waits for - %% pending output to be sent, which results in unnecessary - %% delays. We could just terminate - the reader is the - %% controlling process and hence its termination will close - %% the socket. However, to keep the file_handle_cache - %% accounting as accurate as possible we ought to close the - %% socket w/o delay before termination. - rabbit_net:fast_close(ClientSock), - rabbit_networking:unregister_connection(self()), - rabbit_event:notify(connection_closed, [{pid, self()}]) - end, - done. - -log_connection_exception(Name, Ex) -> - Severity = case Ex of - connection_closed_with_no_data_received -> debug; - connection_closed_abruptly -> warning; - _ -> error - end, - log_connection_exception(Severity, Name, Ex). - -log_connection_exception(Severity, Name, {heartbeat_timeout, TimeoutSec}) -> - %% Long line to avoid extra spaces and line breaks in log - log(Severity, "closing AMQP connection ~p (~s):~nMissed heartbeats from client, timeout: ~ps~n", - [self(), Name, TimeoutSec]); -log_connection_exception(Severity, Name, Ex) -> - log(Severity, "closing AMQP connection ~p (~s):~n~p~n", - [self(), Name, Ex]). - -run({M, F, A}) -> - try apply(M, F, A) - catch {become, MFA} -> run(MFA) - end. - -recvloop(Deb, Buf, BufLen, State = #v1{pending_recv = true}) -> - mainloop(Deb, Buf, BufLen, State); -recvloop(Deb, Buf, BufLen, State = #v1{connection_state = blocked}) -> - mainloop(Deb, Buf, BufLen, State); -recvloop(Deb, Buf, BufLen, State = #v1{connection_state = {become, F}}) -> - throw({become, F(Deb, Buf, BufLen, State)}); -recvloop(Deb, Buf, BufLen, State = #v1{sock = Sock, recv_len = RecvLen}) - when BufLen < RecvLen -> - case rabbit_net:setopts(Sock, [{active, once}]) of - ok -> mainloop(Deb, Buf, BufLen, - State#v1{pending_recv = true}); - {error, Reason} -> stop(Reason, State) - end; -recvloop(Deb, [B], _BufLen, State) -> - {Rest, State1} = handle_input(State#v1.callback, B, State), - recvloop(Deb, [Rest], size(Rest), State1); -recvloop(Deb, Buf, BufLen, State = #v1{recv_len = RecvLen}) -> - {DataLRev, RestLRev} = binlist_split(BufLen - RecvLen, Buf, []), - Data = list_to_binary(lists:reverse(DataLRev)), - {<<>>, State1} = handle_input(State#v1.callback, Data, State), - recvloop(Deb, lists:reverse(RestLRev), BufLen - RecvLen, State1). - -binlist_split(0, L, Acc) -> - {L, Acc}; -binlist_split(Len, L, [Acc0|Acc]) when Len < 0 -> - {H, T} = split_binary(Acc0, -Len), - {[H|L], [T|Acc]}; -binlist_split(Len, [H|T], Acc) -> - binlist_split(Len - size(H), T, [H|Acc]). - -mainloop(Deb, Buf, BufLen, State = #v1{sock = Sock, - connection_state = CS, - connection = #connection{ - name = ConnName}}) -> - Recv = rabbit_net:recv(Sock), - case CS of - pre_init when Buf =:= [] -> - %% We only log incoming connections when either the - %% first byte was received or there was an error (eg. a - %% timeout). - %% - %% The goal is to not log TCP healthchecks (a connection - %% with no data received) unless specified otherwise. - log(case Recv of - closed -> debug; - _ -> info - end, "accepting AMQP connection ~p (~s)~n", - [self(), ConnName]); - _ -> - ok - end, - case Recv of - {data, Data} -> - recvloop(Deb, [Data | Buf], BufLen + size(Data), - State#v1{pending_recv = false}); - closed when State#v1.connection_state =:= closed -> - ok; - closed when CS =:= pre_init andalso Buf =:= [] -> - stop(tcp_healthcheck, State); - closed -> - stop(closed, State); - {error, Reason} -> - stop(Reason, State); - {other, {system, From, Request}} -> - sys:handle_system_msg(Request, From, State#v1.parent, - ?MODULE, Deb, {Buf, BufLen, State}); - {other, Other} -> - case handle_other(Other, State) of - stop -> ok; - NewState -> recvloop(Deb, Buf, BufLen, NewState) - end - end. - -stop(tcp_healthcheck, State) -> - %% The connection was closed before any packet was received. It's - %% probably a load-balancer healthcheck: don't consider this a - %% failure. - maybe_emit_stats(State), - throw(connection_closed_with_no_data_received); -stop(closed, State) -> - maybe_emit_stats(State), - throw(connection_closed_abruptly); -stop(Reason, State) -> - maybe_emit_stats(State), - throw({inet_error, Reason}). - -handle_other({conserve_resources, Source, Conserve}, - State = #v1{throttle = Throttle = #throttle{alarmed_by = CR}}) -> - CR1 = case Conserve of - true -> lists:usort([Source | CR]); - false -> CR -- [Source] - end, - State1 = control_throttle( - State#v1{throttle = Throttle#throttle{alarmed_by = CR1}}), - case {blocked_by_alarm(State), blocked_by_alarm(State1)} of - {false, true} -> ok = send_blocked(State1); - {true, false} -> ok = send_unblocked(State1); - {_, _} -> ok - end, - State1; -handle_other({channel_closing, ChPid}, State) -> - ok = rabbit_channel:ready_for_close(ChPid), - {_, State1} = channel_cleanup(ChPid, State), - maybe_close(control_throttle(State1)); -handle_other({'EXIT', Parent, Reason}, State = #v1{parent = Parent}) -> - terminate(io_lib:format("broker forced connection closure " - "with reason '~w'", [Reason]), State), - %% this is what we are expected to do according to - %% http://www.erlang.org/doc/man/sys.html - %% - %% If we wanted to be *really* nice we should wait for a while for - %% clients to close the socket at their end, just as we do in the - %% ordinary error case. However, since this termination is - %% initiated by our parent it is probably more important to exit - %% quickly. - maybe_emit_stats(State), - exit(Reason); -handle_other({channel_exit, _Channel, E = {writer, send_failed, _E}}, State) -> - maybe_emit_stats(State), - throw(E); -handle_other({channel_exit, Channel, Reason}, State) -> - handle_exception(State, Channel, Reason); -handle_other({'DOWN', _MRef, process, ChPid, Reason}, State) -> - handle_dependent_exit(ChPid, Reason, State); -handle_other(terminate_connection, State) -> - maybe_emit_stats(State), - stop; -handle_other(handshake_timeout, State) - when ?IS_RUNNING(State) orelse ?IS_STOPPING(State) -> - State; -handle_other(handshake_timeout, State) -> - maybe_emit_stats(State), - throw({handshake_timeout, State#v1.callback}); -handle_other(heartbeat_timeout, State = #v1{connection_state = closed}) -> - State; -handle_other(heartbeat_timeout, - State = #v1{connection = #connection{timeout_sec = T}}) -> - maybe_emit_stats(State), - throw({heartbeat_timeout, T}); -handle_other({'$gen_call', From, {shutdown, Explanation}}, State) -> - {ForceTermination, NewState} = terminate(Explanation, State), - gen_server:reply(From, ok), - case ForceTermination of - force -> stop; - normal -> NewState - end; -handle_other({'$gen_call', From, info}, State) -> - gen_server:reply(From, infos(?INFO_KEYS, State)), - State; -handle_other({'$gen_call', From, {info, Items}}, State) -> - gen_server:reply(From, try {ok, infos(Items, State)} - catch Error -> {error, Error} - end), - State; -handle_other({'$gen_cast', {force_event_refresh, Ref}}, State) - when ?IS_RUNNING(State) -> - rabbit_event:notify( - connection_created, - [{type, network} | infos(?CREATION_EVENT_KEYS, State)], Ref), - rabbit_event:init_stats_timer(State, #v1.stats_timer); -handle_other({'$gen_cast', {force_event_refresh, _Ref}}, State) -> - %% Ignore, we will emit a created event once we start running. - State; -handle_other(ensure_stats, State) -> - ensure_stats_timer(State); -handle_other(emit_stats, State) -> - emit_stats(State); -handle_other({bump_credit, Msg}, State) -> - %% Here we are receiving credit by some channel process. - credit_flow:handle_bump_msg(Msg), - control_throttle(State); -handle_other(Other, State) -> - %% internal error -> something worth dying for - maybe_emit_stats(State), - exit({unexpected_message, Other}). - -switch_callback(State, Callback, Length) -> - State#v1{callback = Callback, recv_len = Length}. - -terminate(Explanation, State) when ?IS_RUNNING(State) -> - {normal, handle_exception(State, 0, - rabbit_misc:amqp_error( - connection_forced, Explanation, [], none))}; -terminate(_Explanation, State) -> - {force, State}. - -control_throttle(State = #v1{connection_state = CS, throttle = Throttle}) -> - IsThrottled = ((Throttle#throttle.alarmed_by =/= []) orelse - credit_flow:blocked()), - case {CS, IsThrottled} of - {running, true} -> State#v1{connection_state = blocking}; - {blocking, false} -> State#v1{connection_state = running}; - {blocked, false} -> ok = rabbit_heartbeat:resume_monitor( - State#v1.heartbeater), - State#v1{connection_state = running}; - {blocked, true} -> State#v1{throttle = update_last_blocked_by( - Throttle)}; - {_, _} -> State - end. - -maybe_block(State = #v1{connection_state = blocking, - throttle = Throttle}) -> - ok = rabbit_heartbeat:pause_monitor(State#v1.heartbeater), - State1 = State#v1{connection_state = blocked, - throttle = update_last_blocked_by( - Throttle#throttle{ - last_blocked_at = - time_compat:monotonic_time()})}, - case {blocked_by_alarm(State), blocked_by_alarm(State1)} of - {false, true} -> ok = send_blocked(State1); - {_, _} -> ok - end, - State1; -maybe_block(State) -> - State. - - -blocked_by_alarm(#v1{connection_state = blocked, - throttle = #throttle{alarmed_by = CR}}) - when CR =/= [] -> - true; -blocked_by_alarm(#v1{}) -> - false. - -send_blocked(#v1{throttle = #throttle{alarmed_by = CR}, - connection = #connection{protocol = Protocol, - capabilities = Capabilities}, - sock = Sock}) -> - case rabbit_misc:table_lookup(Capabilities, <<"connection.blocked">>) of - {bool, true} -> - RStr = string:join([atom_to_list(A) || A <- CR], " & "), - Reason = list_to_binary(rabbit_misc:format("low on ~s", [RStr])), - ok = send_on_channel0(Sock, #'connection.blocked'{reason = Reason}, - Protocol); - _ -> - ok - end. - -send_unblocked(#v1{connection = #connection{protocol = Protocol, - capabilities = Capabilities}, - sock = Sock}) -> - case rabbit_misc:table_lookup(Capabilities, <<"connection.blocked">>) of - {bool, true} -> - ok = send_on_channel0(Sock, #'connection.unblocked'{}, Protocol); - _ -> - ok - end. - -update_last_blocked_by(Throttle = #throttle{alarmed_by = []}) -> - Throttle#throttle{last_blocked_by = flow}; -update_last_blocked_by(Throttle) -> - Throttle#throttle{last_blocked_by = resource}. - -%%-------------------------------------------------------------------------- -%% error handling / termination - -close_connection(State = #v1{queue_collector = Collector, - connection = #connection{ - timeout_sec = TimeoutSec}}) -> - %% The spec says "Exclusive queues may only be accessed by the - %% current connection, and are deleted when that connection - %% closes." This does not strictly imply synchrony, but in - %% practice it seems to be what people assume. - rabbit_queue_collector:delete_all(Collector), - %% We terminate the connection after the specified interval, but - %% no later than ?CLOSING_TIMEOUT seconds. - erlang:send_after((if TimeoutSec > 0 andalso - TimeoutSec < ?CLOSING_TIMEOUT -> TimeoutSec; - true -> ?CLOSING_TIMEOUT - end) * 1000, self(), terminate_connection), - State#v1{connection_state = closed}. - -handle_dependent_exit(ChPid, Reason, State) -> - {Channel, State1} = channel_cleanup(ChPid, State), - case {Channel, termination_kind(Reason)} of - {undefined, controlled} -> State1; - {undefined, uncontrolled} -> exit({abnormal_dependent_exit, - ChPid, Reason}); - {_, controlled} -> maybe_close(control_throttle(State1)); - {_, uncontrolled} -> State2 = handle_exception( - State1, Channel, Reason), - maybe_close(control_throttle(State2)) - end. - -terminate_channels(#v1{channel_count = 0} = State) -> - State; -terminate_channels(#v1{channel_count = ChannelCount} = State) -> - lists:foreach(fun rabbit_channel:shutdown/1, all_channels()), - Timeout = 1000 * ?CHANNEL_TERMINATION_TIMEOUT * ChannelCount, - TimerRef = erlang:send_after(Timeout, self(), cancel_wait), - wait_for_channel_termination(ChannelCount, TimerRef, State). - -wait_for_channel_termination(0, TimerRef, State) -> - case erlang:cancel_timer(TimerRef) of - false -> receive - cancel_wait -> State - end; - _ -> State - end; -wait_for_channel_termination(N, TimerRef, - State = #v1{connection_state = CS, - connection = #connection{ - name = ConnName, - user = User, - vhost = VHost}}) -> - receive - {'DOWN', _MRef, process, ChPid, Reason} -> - {Channel, State1} = channel_cleanup(ChPid, State), - case {Channel, termination_kind(Reason)} of - {undefined, _} -> - exit({abnormal_dependent_exit, ChPid, Reason}); - {_, controlled} -> - wait_for_channel_termination(N-1, TimerRef, State1); - {_, uncontrolled} -> - log(error, "Error on AMQP connection ~p (~s, vhost: '~s'," - " user: '~s', state: ~p), channel ~p:" - "error while terminating:~n~p~n", - [self(), ConnName, VHost, User#user.username, - CS, Channel, Reason]), - wait_for_channel_termination(N-1, TimerRef, State1) - end; - cancel_wait -> - exit(channel_termination_timeout) - end. - -maybe_close(State = #v1{connection_state = closing, - channel_count = 0, - connection = #connection{protocol = Protocol}, - sock = Sock}) -> - NewState = close_connection(State), - ok = send_on_channel0(Sock, #'connection.close_ok'{}, Protocol), - NewState; -maybe_close(State) -> - State. - -termination_kind(normal) -> controlled; -termination_kind(_) -> uncontrolled. - -log_hard_error(#v1{connection_state = CS, - connection = #connection{ - name = ConnName, - user = User, - vhost = VHost}}, Channel, Reason) -> - log(error, - "Error on AMQP connection ~p (~s, vhost: '~s'," - " user: '~s', state: ~p), channel ~p:~n~p~n", - [self(), ConnName, VHost, User#user.username, CS, Channel, Reason]). - -handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> - log_hard_error(State, Channel, Reason), - State; -handle_exception(State = #v1{connection = #connection{protocol = Protocol}, - connection_state = CS}, - Channel, Reason) - when ?IS_RUNNING(State) orelse CS =:= closing -> - log_hard_error(State, Channel, Reason), - {0, CloseMethod} = - rabbit_binary_generator:map_exception(Channel, Reason, Protocol), - State1 = close_connection(terminate_channels(State)), - ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol), - State1; -handle_exception(State, Channel, Reason) -> - %% We don't trust the client at this point - force them to wait - %% for a bit so they can't DOS us with repeated failed logins etc. - timer:sleep(?SILENT_CLOSE_DELAY * 1000), - throw({handshake_error, State#v1.connection_state, Channel, Reason}). - -%% we've "lost sync" with the client and hence must not accept any -%% more input -fatal_frame_error(Error, Type, Channel, Payload, State) -> - frame_error(Error, Type, Channel, Payload, State), - %% grace period to allow transmission of error - timer:sleep(?SILENT_CLOSE_DELAY * 1000), - throw(fatal_frame_error). - -frame_error(Error, Type, Channel, Payload, State) -> - {Str, Bin} = payload_snippet(Payload), - handle_exception(State, Channel, - rabbit_misc:amqp_error(frame_error, - "type ~p, ~s octets = ~p: ~p", - [Type, Str, Bin, Error], none)). - -unexpected_frame(Type, Channel, Payload, State) -> - {Str, Bin} = payload_snippet(Payload), - handle_exception(State, Channel, - rabbit_misc:amqp_error(unexpected_frame, - "type ~p, ~s octets = ~p", - [Type, Str, Bin], none)). - -payload_snippet(Payload) when size(Payload) =< 16 -> - {"all", Payload}; -payload_snippet(<<Snippet:16/binary, _/binary>>) -> - {"first 16", Snippet}. - -%%-------------------------------------------------------------------------- - -create_channel(_Channel, - #v1{channel_count = ChannelCount, - connection = #connection{channel_max = ChannelMax}}) - when ChannelMax /= 0 andalso ChannelCount >= ChannelMax -> - {error, rabbit_misc:amqp_error( - not_allowed, "number of channels opened (~w) has reached the " - "negotiated channel_max (~w)", - [ChannelCount, ChannelMax], 'none')}; -create_channel(Channel, - #v1{sock = Sock, - queue_collector = Collector, - channel_sup_sup_pid = ChanSupSup, - channel_count = ChannelCount, - connection = - #connection{name = Name, - protocol = Protocol, - frame_max = FrameMax, - user = User, - vhost = VHost, - capabilities = Capabilities}} = State) -> - {ok, _ChSupPid, {ChPid, AState}} = - rabbit_channel_sup_sup:start_channel( - ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), Name, - Protocol, User, VHost, Capabilities, Collector}), - MRef = erlang:monitor(process, ChPid), - put({ch_pid, ChPid}, {Channel, MRef}), - put({channel, Channel}, {ChPid, AState}), - {ok, {ChPid, AState}, State#v1{channel_count = ChannelCount + 1}}. - -channel_cleanup(ChPid, State = #v1{channel_count = ChannelCount}) -> - case get({ch_pid, ChPid}) of - undefined -> {undefined, State}; - {Channel, MRef} -> credit_flow:peer_down(ChPid), - erase({channel, Channel}), - erase({ch_pid, ChPid}), - erlang:demonitor(MRef, [flush]), - {Channel, State#v1{channel_count = ChannelCount - 1}} - end. - -all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()]. - -%%-------------------------------------------------------------------------- - -handle_frame(Type, 0, Payload, - State = #v1{connection = #connection{protocol = Protocol}}) - when ?IS_STOPPING(State) -> - case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of - {method, MethodName, FieldsBin} -> - handle_method0(MethodName, FieldsBin, State); - _Other -> State - end; -handle_frame(Type, 0, Payload, - State = #v1{connection = #connection{protocol = Protocol}}) -> - case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of - error -> frame_error(unknown_frame, Type, 0, Payload, State); - heartbeat -> State; - {method, MethodName, FieldsBin} -> - handle_method0(MethodName, FieldsBin, State); - _Other -> unexpected_frame(Type, 0, Payload, State) - end; -handle_frame(Type, Channel, Payload, - State = #v1{connection = #connection{protocol = Protocol}}) - when ?IS_RUNNING(State) -> - case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of - error -> frame_error(unknown_frame, Type, Channel, Payload, State); - heartbeat -> unexpected_frame(Type, Channel, Payload, State); - Frame -> process_frame(Frame, Channel, State) - end; -handle_frame(_Type, _Channel, _Payload, State) when ?IS_STOPPING(State) -> - State; -handle_frame(Type, Channel, Payload, State) -> - unexpected_frame(Type, Channel, Payload, State). - -process_frame(Frame, Channel, State) -> - ChKey = {channel, Channel}, - case (case get(ChKey) of - undefined -> create_channel(Channel, State); - Other -> {ok, Other, State} - end) of - {error, Error} -> - handle_exception(State, Channel, Error); - {ok, {ChPid, AState}, State1} -> - case rabbit_command_assembler:process(Frame, AState) of - {ok, NewAState} -> - put(ChKey, {ChPid, NewAState}), - post_process_frame(Frame, ChPid, State1); - {ok, Method, NewAState} -> - rabbit_channel:do(ChPid, Method), - put(ChKey, {ChPid, NewAState}), - post_process_frame(Frame, ChPid, State1); - {ok, Method, Content, NewAState} -> - rabbit_channel:do_flow(ChPid, Method, Content), - put(ChKey, {ChPid, NewAState}), - post_process_frame(Frame, ChPid, control_throttle(State1)); - {error, Reason} -> - handle_exception(State1, Channel, Reason) - end - end. - -post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> - {_, State1} = channel_cleanup(ChPid, State), - %% This is not strictly necessary, but more obviously - %% correct. Also note that we do not need to call maybe_close/1 - %% since we cannot possibly be in the 'closing' state. - control_throttle(State1); -post_process_frame({content_header, _, _, _, _}, _ChPid, State) -> - maybe_block(State); -post_process_frame({content_body, _}, _ChPid, State) -> - maybe_block(State); -post_process_frame(_Frame, _ChPid, State) -> - State. - -%%-------------------------------------------------------------------------- - -%% We allow clients to exceed the frame size a little bit since quite -%% a few get it wrong - off-by 1 or 8 (empty frame size) are typical. --define(FRAME_SIZE_FUDGE, ?EMPTY_FRAME_SIZE). - -handle_input(frame_header, <<Type:8,Channel:16,PayloadSize:32, _/binary>>, - State = #v1{connection = #connection{frame_max = FrameMax}}) - when FrameMax /= 0 andalso - PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE + ?FRAME_SIZE_FUDGE -> - fatal_frame_error( - {frame_too_large, PayloadSize, FrameMax - ?EMPTY_FRAME_SIZE}, - Type, Channel, <<>>, State); -handle_input(frame_header, <<Type:8,Channel:16,PayloadSize:32, - Payload:PayloadSize/binary, ?FRAME_END, - Rest/binary>>, - State) -> - {Rest, ensure_stats_timer(handle_frame(Type, Channel, Payload, State))}; -handle_input(frame_header, <<Type:8,Channel:16,PayloadSize:32, Rest/binary>>, - State) -> - {Rest, ensure_stats_timer( - switch_callback(State, - {frame_payload, Type, Channel, PayloadSize}, - PayloadSize + 1))}; -handle_input({frame_payload, Type, Channel, PayloadSize}, Data, State) -> - <<Payload:PayloadSize/binary, EndMarker, Rest/binary>> = Data, - case EndMarker of - ?FRAME_END -> State1 = handle_frame(Type, Channel, Payload, State), - {Rest, switch_callback(State1, frame_header, 7)}; - _ -> fatal_frame_error({invalid_frame_end_marker, EndMarker}, - Type, Channel, Payload, State) - end; -handle_input(handshake, <<"AMQP", A, B, C, D, Rest/binary>>, State) -> - {Rest, handshake({A, B, C, D}, State)}; -handle_input(handshake, <<Other:8/binary, _/binary>>, #v1{sock = Sock}) -> - refuse_connection(Sock, {bad_header, Other}); -handle_input(Callback, Data, _State) -> - throw({bad_input, Callback, Data}). - -%% The two rules pertaining to version negotiation: -%% -%% * If the server cannot support the protocol specified in the -%% protocol header, it MUST respond with a valid protocol header and -%% then close the socket connection. -%% -%% * The server MUST provide a protocol version that is lower than or -%% equal to that requested by the client in the protocol header. -handshake({0, 0, 9, 1}, State) -> - start_connection({0, 9, 1}, rabbit_framing_amqp_0_9_1, State); - -%% This is the protocol header for 0-9, which we can safely treat as -%% though it were 0-9-1. -handshake({1, 1, 0, 9}, State) -> - start_connection({0, 9, 0}, rabbit_framing_amqp_0_9_1, State); - -%% This is what most clients send for 0-8. The 0-8 spec, confusingly, -%% defines the version as 8-0. -handshake({1, 1, 8, 0}, State) -> - start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); - -%% The 0-8 spec as on the AMQP web site actually has this as the -%% protocol header; some libraries e.g., py-amqplib, send it when they -%% want 0-8. -handshake({1, 1, 9, 1}, State) -> - start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); - -%% ... and finally, the 1.0 spec is crystal clear! -handshake({Id, 1, 0, 0}, State) -> - become_1_0(Id, State); - -handshake(Vsn, #v1{sock = Sock}) -> - refuse_connection(Sock, {bad_version, Vsn}). - -%% Offer a protocol version to the client. Connection.start only -%% includes a major and minor version number, Luckily 0-9 and 0-9-1 -%% are similar enough that clients will be happy with either. -start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, - Protocol, - State = #v1{sock = Sock, connection = Connection}) -> - rabbit_networking:register_connection(self()), - Start = #'connection.start'{ - version_major = ProtocolMajor, - version_minor = ProtocolMinor, - server_properties = server_properties(Protocol), - mechanisms = auth_mechanisms_binary(Sock), - locales = <<"en_US">> }, - ok = send_on_channel0(Sock, Start, Protocol), - switch_callback(State#v1{connection = Connection#connection{ - timeout_sec = ?NORMAL_TIMEOUT, - protocol = Protocol}, - connection_state = starting}, - frame_header, 7). - -refuse_connection(Sock, Exception, {A, B, C, D}) -> - ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",A,B,C,D>>) end), - throw(Exception). - --ifdef(use_specs). --spec(refuse_connection/2 :: (rabbit_net:socket(), any()) -> no_return()). --endif. -refuse_connection(Sock, Exception) -> - refuse_connection(Sock, Exception, {0, 0, 9, 1}). - -ensure_stats_timer(State = #v1{connection_state = running}) -> - rabbit_event:ensure_stats_timer(State, #v1.stats_timer, emit_stats); -ensure_stats_timer(State) -> - State. - -%%-------------------------------------------------------------------------- - -handle_method0(MethodName, FieldsBin, - State = #v1{connection = #connection{protocol = Protocol}}) -> - try - handle_method0(Protocol:decode_method_fields(MethodName, FieldsBin), - State) - catch throw:{inet_error, E} when E =:= closed; E =:= enotconn -> - maybe_emit_stats(State), - throw(connection_closed_abruptly); - exit:#amqp_error{method = none} = Reason -> - handle_exception(State, 0, Reason#amqp_error{method = MethodName}); - Type:Reason -> - Stack = erlang:get_stacktrace(), - handle_exception(State, 0, {Type, Reason, MethodName, Stack}) - end. - -handle_method0(#'connection.start_ok'{mechanism = Mechanism, - response = Response, - client_properties = ClientProperties}, - State0 = #v1{connection_state = starting, - connection = Connection, - sock = Sock}) -> - AuthMechanism = auth_mechanism_to_module(Mechanism, Sock), - Capabilities = - case rabbit_misc:table_lookup(ClientProperties, <<"capabilities">>) of - {table, Capabilities1} -> Capabilities1; - _ -> [] - end, - State = State0#v1{connection_state = securing, - connection = - Connection#connection{ - client_properties = ClientProperties, - capabilities = Capabilities, - auth_mechanism = {Mechanism, AuthMechanism}, - auth_state = AuthMechanism:init(Sock)}}, - auth_phase(Response, State); - -handle_method0(#'connection.secure_ok'{response = Response}, - State = #v1{connection_state = securing}) -> - auth_phase(Response, State); - -handle_method0(#'connection.tune_ok'{frame_max = FrameMax, - channel_max = ChannelMax, - heartbeat = ClientHeartbeat}, - State = #v1{connection_state = tuning, - connection = Connection, - helper_sup = SupPid, - sock = Sock}) -> - ok = validate_negotiated_integer_value( - frame_max, ?FRAME_MIN_SIZE, FrameMax), - ok = validate_negotiated_integer_value( - channel_max, ?CHANNEL_MIN, ChannelMax), - {ok, Collector} = rabbit_connection_helper_sup:start_queue_collector( - SupPid, Connection#connection.name), - Frame = rabbit_binary_generator:build_heartbeat_frame(), - SendFun = fun() -> catch rabbit_net:send(Sock, Frame) end, - Parent = self(), - ReceiveFun = fun() -> Parent ! heartbeat_timeout end, - Heartbeater = rabbit_heartbeat:start( - SupPid, Sock, Connection#connection.name, - ClientHeartbeat, SendFun, ClientHeartbeat, ReceiveFun), - State#v1{connection_state = opening, - connection = Connection#connection{ - frame_max = FrameMax, - channel_max = ChannelMax, - timeout_sec = ClientHeartbeat}, - queue_collector = Collector, - heartbeater = Heartbeater}; - -handle_method0(#'connection.open'{virtual_host = VHostPath}, - State = #v1{connection_state = opening, - connection = Connection = #connection{ - user = User, - protocol = Protocol}, - helper_sup = SupPid, - sock = Sock, - throttle = Throttle}) -> - ok = rabbit_access_control:check_vhost_access(User, VHostPath, Sock), - NewConnection = Connection#connection{vhost = VHostPath}, - ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol), - Conserve = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}), - Throttle1 = Throttle#throttle{alarmed_by = Conserve}, - {ok, ChannelSupSupPid} = - rabbit_connection_helper_sup:start_channel_sup_sup(SupPid), - State1 = control_throttle( - State#v1{connection_state = running, - connection = NewConnection, - channel_sup_sup_pid = ChannelSupSupPid, - throttle = Throttle1}), - rabbit_event:notify(connection_created, - [{type, network} | - infos(?CREATION_EVENT_KEYS, State1)]), - maybe_emit_stats(State1), - State1; -handle_method0(#'connection.close'{}, State) when ?IS_RUNNING(State) -> - lists:foreach(fun rabbit_channel:shutdown/1, all_channels()), - maybe_close(State#v1{connection_state = closing}); -handle_method0(#'connection.close'{}, - State = #v1{connection = #connection{protocol = Protocol}, - sock = Sock}) - when ?IS_STOPPING(State) -> - %% We're already closed or closing, so we don't need to cleanup - %% anything. - ok = send_on_channel0(Sock, #'connection.close_ok'{}, Protocol), - State; -handle_method0(#'connection.close_ok'{}, - State = #v1{connection_state = closed}) -> - self() ! terminate_connection, - State; -handle_method0(_Method, State) when ?IS_STOPPING(State) -> - State; -handle_method0(_Method, #v1{connection_state = S}) -> - rabbit_misc:protocol_error( - channel_error, "unexpected method in connection state ~w", [S]). - -validate_negotiated_integer_value(Field, Min, ClientValue) -> - ServerValue = get_env(Field), - if ClientValue /= 0 andalso ClientValue < Min -> - fail_negotiation(Field, min, ServerValue, ClientValue); - ServerValue /= 0 andalso (ClientValue =:= 0 orelse - ClientValue > ServerValue) -> - fail_negotiation(Field, max, ServerValue, ClientValue); - true -> - ok - end. - -%% keep dialyzer happy --spec fail_negotiation(atom(), 'min' | 'max', integer(), integer()) -> - no_return(). -fail_negotiation(Field, MinOrMax, ServerValue, ClientValue) -> - {S1, S2} = case MinOrMax of - min -> {lower, minimum}; - max -> {higher, maximum} - end, - rabbit_misc:protocol_error( - not_allowed, "negotiated ~w = ~w is ~w than the ~w allowed value (~w)", - [Field, ClientValue, S1, S2, ServerValue], 'connection.tune'). - -get_env(Key) -> - {ok, Value} = application:get_env(rabbit, Key), - Value. - -send_on_channel0(Sock, Method, Protocol) -> - ok = rabbit_writer:internal_send_command(Sock, 0, Method, Protocol). - -auth_mechanism_to_module(TypeBin, Sock) -> - case rabbit_registry:binary_to_type(TypeBin) of - {error, not_found} -> - rabbit_misc:protocol_error( - command_invalid, "unknown authentication mechanism '~s'", - [TypeBin]); - T -> - case {lists:member(T, auth_mechanisms(Sock)), - rabbit_registry:lookup_module(auth_mechanism, T)} of - {true, {ok, Module}} -> - Module; - _ -> - rabbit_misc:protocol_error( - command_invalid, - "invalid authentication mechanism '~s'", [T]) - end - end. - -auth_mechanisms(Sock) -> - {ok, Configured} = application:get_env(auth_mechanisms), - [Name || {Name, Module} <- rabbit_registry:lookup_all(auth_mechanism), - Module:should_offer(Sock), lists:member(Name, Configured)]. - -auth_mechanisms_binary(Sock) -> - list_to_binary( - string:join([atom_to_list(A) || A <- auth_mechanisms(Sock)], " ")). - -auth_phase(Response, - State = #v1{connection = Connection = - #connection{protocol = Protocol, - auth_mechanism = {Name, AuthMechanism}, - auth_state = AuthState}, - sock = Sock}) -> - case AuthMechanism:handle_response(Response, AuthState) of - {refused, Username, Msg, Args} -> - auth_fail(Username, Msg, Args, Name, State); - {protocol_error, Msg, Args} -> - notify_auth_result(none, user_authentication_failure, - [{error, rabbit_misc:format(Msg, Args)}], - State), - rabbit_misc:protocol_error(syntax_error, Msg, Args); - {challenge, Challenge, AuthState1} -> - Secure = #'connection.secure'{challenge = Challenge}, - ok = send_on_channel0(Sock, Secure, Protocol), - State#v1{connection = Connection#connection{ - auth_state = AuthState1}}; - {ok, User = #user{username = Username}} -> - case rabbit_access_control:check_user_loopback(Username, Sock) of - ok -> - notify_auth_result(Username, user_authentication_success, - [], State); - not_allowed -> - auth_fail(Username, "user '~s' can only connect via " - "localhost", [Username], Name, State) - end, - Tune = #'connection.tune'{frame_max = get_env(frame_max), - channel_max = get_env(channel_max), - heartbeat = get_env(heartbeat)}, - ok = send_on_channel0(Sock, Tune, Protocol), - State#v1{connection_state = tuning, - connection = Connection#connection{user = User, - auth_state = none}} - end. - --ifdef(use_specs). --spec(auth_fail/5 :: - (rabbit_types:username() | none, string(), [any()], binary(), #v1{}) -> - no_return()). --endif. -auth_fail(Username, Msg, Args, AuthName, - State = #v1{connection = #connection{protocol = Protocol, - capabilities = Capabilities}}) -> - notify_auth_result(Username, user_authentication_failure, - [{error, rabbit_misc:format(Msg, Args)}], State), - AmqpError = rabbit_misc:amqp_error( - access_refused, "~s login refused: ~s", - [AuthName, io_lib:format(Msg, Args)], none), - case rabbit_misc:table_lookup(Capabilities, - <<"authentication_failure_close">>) of - {bool, true} -> - SafeMsg = io_lib:format( - "Login was refused using authentication " - "mechanism ~s. For details see the broker " - "logfile.", [AuthName]), - AmqpError1 = AmqpError#amqp_error{explanation = SafeMsg}, - {0, CloseMethod} = rabbit_binary_generator:map_exception( - 0, AmqpError1, Protocol), - ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol); - _ -> ok - end, - rabbit_misc:protocol_error(AmqpError). - -notify_auth_result(Username, AuthResult, ExtraProps, State) -> - EventProps = [{connection_type, network}, - {name, case Username of none -> ''; _ -> Username end}] ++ - [case Item of - name -> {connection_name, i(name, State)}; - _ -> {Item, i(Item, State)} - end || Item <- ?AUTH_NOTIFICATION_INFO_KEYS] ++ - ExtraProps, - rabbit_event:notify(AuthResult, [P || {_, V} = P <- EventProps, V =/= '']). - -%%-------------------------------------------------------------------------- - -infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. - -i(pid, #v1{}) -> self(); -i(SockStat, S) when SockStat =:= recv_oct; - SockStat =:= recv_cnt; - SockStat =:= send_oct; - SockStat =:= send_cnt; - SockStat =:= send_pend -> - socket_info(fun (Sock) -> rabbit_net:getstat(Sock, [SockStat]) end, - fun ([{_, I}]) -> I end, S); -i(ssl, #v1{sock = Sock}) -> rabbit_net:is_ssl(Sock); -i(ssl_protocol, S) -> ssl_info(fun ({P, _}) -> P end, S); -i(ssl_key_exchange, S) -> ssl_info(fun ({_, {K, _, _}}) -> K end, S); -i(ssl_cipher, S) -> ssl_info(fun ({_, {_, C, _}}) -> C end, S); -i(ssl_hash, S) -> ssl_info(fun ({_, {_, _, H}}) -> H end, S); -i(peer_cert_issuer, S) -> cert_info(fun rabbit_ssl:peer_cert_issuer/1, S); -i(peer_cert_subject, S) -> cert_info(fun rabbit_ssl:peer_cert_subject/1, S); -i(peer_cert_validity, S) -> cert_info(fun rabbit_ssl:peer_cert_validity/1, S); -i(channels, #v1{channel_count = ChannelCount}) -> ChannelCount; -i(state, #v1{connection_state = ConnectionState, - throttle = #throttle{alarmed_by = Alarms, - last_blocked_by = WasBlockedBy, - last_blocked_at = T}}) -> - case Alarms =:= [] andalso %% not throttled by resource alarms - (credit_flow:blocked() %% throttled by flow now - orelse %% throttled by flow recently - (WasBlockedBy =:= flow andalso T =/= never andalso - time_compat:convert_time_unit(time_compat:monotonic_time() - T, - native, - micro_seconds) < 5000000)) of - true -> flow; - false -> ConnectionState - end; -i(Item, #v1{connection = Conn}) -> ic(Item, Conn). - -ic(name, #connection{name = Name}) -> Name; -ic(host, #connection{host = Host}) -> Host; -ic(peer_host, #connection{peer_host = PeerHost}) -> PeerHost; -ic(port, #connection{port = Port}) -> Port; -ic(peer_port, #connection{peer_port = PeerPort}) -> PeerPort; -ic(protocol, #connection{protocol = none}) -> none; -ic(protocol, #connection{protocol = P}) -> P:version(); -ic(user, #connection{user = none}) -> ''; -ic(user, #connection{user = U}) -> U#user.username; -ic(vhost, #connection{vhost = VHost}) -> VHost; -ic(timeout, #connection{timeout_sec = Timeout}) -> Timeout; -ic(frame_max, #connection{frame_max = FrameMax}) -> FrameMax; -ic(channel_max, #connection{channel_max = ChMax}) -> ChMax; -ic(client_properties, #connection{client_properties = CP}) -> CP; -ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; -ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; -ic(connected_at, #connection{connected_at = T}) -> T; -ic(Item, #connection{}) -> throw({bad_argument, Item}). - -socket_info(Get, Select, #v1{sock = Sock}) -> - case Get(Sock) of - {ok, T} -> Select(T); - {error, _} -> '' - end. - -ssl_info(F, #v1{sock = Sock}) -> - case rabbit_net:ssl_info(Sock) of - nossl -> ''; - {error, _} -> ''; - {ok, Items} -> - P = proplists:get_value(protocol, Items), - CS = proplists:get_value(cipher_suite, Items), - %% The first form is R14. - %% The second is R13 - the extra term is exportability (by - %% inspection, the docs are wrong). - case CS of - {K, C, H} -> F({P, {K, C, H}}); - {K, C, H, _} -> F({P, {K, C, H}}) - end - end. - -cert_info(F, #v1{sock = Sock}) -> - case rabbit_net:peercert(Sock) of - nossl -> ''; - {error, _} -> ''; - {ok, Cert} -> list_to_binary(F(Cert)) - end. - -maybe_emit_stats(State) -> - rabbit_event:if_enabled(State, #v1.stats_timer, - fun() -> emit_stats(State) end). - -emit_stats(State) -> - Infos = infos(?STATISTICS_KEYS, State), - rabbit_event:notify(connection_stats, Infos), - State1 = rabbit_event:reset_stats_timer(State, #v1.stats_timer), - %% If we emit an event which looks like we are in flow control, it's not a - %% good idea for it to be our last even if we go idle. Keep emitting - %% events, either we stay busy or we drop out of flow control. - case proplists:get_value(state, Infos) of - flow -> ensure_stats_timer(State1); - _ -> State1 - end. - -%% 1.0 stub --ifdef(use_specs). --spec(become_1_0/2 :: (non_neg_integer(), #v1{}) -> no_return()). --endif. -become_1_0(Id, State = #v1{sock = Sock}) -> - case code:is_loaded(rabbit_amqp1_0_reader) of - false -> refuse_connection(Sock, amqp1_0_plugin_not_enabled); - _ -> Mode = case Id of - 0 -> amqp; - 3 -> sasl; - _ -> refuse_connection( - Sock, {unsupported_amqp1_0_protocol_id, Id}, - {3, 1, 0, 0}) - end, - F = fun (_Deb, Buf, BufLen, S) -> - {rabbit_amqp1_0_reader, init, - [Mode, pack_for_1_0(Buf, BufLen, S)]} - end, - State#v1{connection_state = {become, F}} - end. - -pack_for_1_0(Buf, BufLen, #v1{parent = Parent, - sock = Sock, - recv_len = RecvLen, - pending_recv = PendingRecv, - helper_sup = SupPid}) -> - {Parent, Sock, RecvLen, PendingRecv, SupPid, Buf, BufLen}. diff --git a/src/rabbit_runtime_parameter.erl b/src/rabbit_runtime_parameter.erl deleted file mode 100644 index 1d4bc0b575..0000000000 --- a/src/rabbit_runtime_parameter.erl +++ /dev/null @@ -1,42 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_runtime_parameter). - --ifdef(use_specs). - --type(validate_results() :: - 'ok' | {error, string(), [term()]} | [validate_results()]). - --callback validate(rabbit_types:vhost(), binary(), binary(), - term(), rabbit_types:user()) -> validate_results(). --callback notify(rabbit_types:vhost(), binary(), binary(), term()) -> 'ok'. --callback notify_clear(rabbit_types:vhost(), binary(), binary()) -> 'ok'. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [ - {validate, 5}, - {notify, 4}, - {notify_clear, 3} - ]; -behaviour_info(_Other) -> - undefined. - --endif. diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl deleted file mode 100644 index 897d937443..0000000000 --- a/src/rabbit_writer.erl +++ /dev/null @@ -1,390 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_writer). - -%% This module backs writer processes ("writers"). The responsibility of -%% a writer is to serialise protocol methods and write them to the socket. -%% Every writer is associated with a channel and normally it's the channel -%% that delegates method delivery to it. However, rabbit_reader -%% (connection process) can use this module's functions to send data -%% on channel 0, which is only used for connection negotiation and -%% other "special" purposes. -%% -%% This module provides multiple functions that send protocol commands, -%% including some that are credit flow-aware. -%% -%% Writers perform internal buffering. When the amount of data -%% buffered exceeds a threshold, a socket flush is performed. -%% See FLUSH_THRESHOLD for details. -%% -%% When a socket write fails, writer will exit. - --include("rabbit.hrl"). --include("rabbit_framing.hrl"). - --export([start/6, start_link/6, start/7, start_link/7]). - --export([system_continue/3, system_terminate/4, system_code_change/4]). - --export([send_command/2, send_command/3, - send_command_sync/2, send_command_sync/3, - send_command_and_notify/4, send_command_and_notify/5, - send_command_flow/2, send_command_flow/3, - flush/1]). --export([internal_send_command/4, internal_send_command/6]). - -%% internal --export([enter_mainloop/2, mainloop/2, mainloop1/2]). - --record(wstate, { - %% socket (port) - sock, - %% channel number - channel, - %% connection-negotiated frame_max setting - frame_max, - %% see #connection.protocol in rabbit_reader - protocol, - %% connection (rabbit_reader) process - reader, - %% statistics emission timer - stats_timer, - %% data pending delivery (between socket - %% flushes) - pending -}). - --define(HIBERNATE_AFTER, 5000). - -%%--------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start/6 :: - (rabbit_net:socket(), rabbit_channel:channel_number(), - non_neg_integer(), rabbit_types:protocol(), pid(), - rabbit_types:proc_name()) - -> rabbit_types:ok(pid())). --spec(start_link/6 :: - (rabbit_net:socket(), rabbit_channel:channel_number(), - non_neg_integer(), rabbit_types:protocol(), pid(), - rabbit_types:proc_name()) - -> rabbit_types:ok(pid())). --spec(start/7 :: - (rabbit_net:socket(), rabbit_channel:channel_number(), - non_neg_integer(), rabbit_types:protocol(), pid(), - rabbit_types:proc_name(), boolean()) - -> rabbit_types:ok(pid())). --spec(start_link/7 :: - (rabbit_net:socket(), rabbit_channel:channel_number(), - non_neg_integer(), rabbit_types:protocol(), pid(), - rabbit_types:proc_name(), boolean()) - -> rabbit_types:ok(pid())). - --spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}). --spec(system_continue/3 :: (_,_,#wstate{}) -> any()). --spec(system_terminate/4 :: (_,_,_,_) -> none()). - --spec(send_command/2 :: - (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). --spec(send_command/3 :: - (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) - -> 'ok'). --spec(send_command_sync/2 :: - (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). --spec(send_command_sync/3 :: - (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) - -> 'ok'). --spec(send_command_and_notify/4 :: - (pid(), pid(), pid(), rabbit_framing:amqp_method_record()) - -> 'ok'). --spec(send_command_and_notify/5 :: - (pid(), pid(), pid(), rabbit_framing:amqp_method_record(), - rabbit_types:content()) - -> 'ok'). --spec(send_command_flow/2 :: - (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). --spec(send_command_flow/3 :: - (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) - -> 'ok'). --spec(flush/1 :: (pid()) -> 'ok'). --spec(internal_send_command/4 :: - (rabbit_net:socket(), rabbit_channel:channel_number(), - rabbit_framing:amqp_method_record(), rabbit_types:protocol()) - -> 'ok'). --spec(internal_send_command/6 :: - (rabbit_net:socket(), rabbit_channel:channel_number(), - rabbit_framing:amqp_method_record(), rabbit_types:content(), - non_neg_integer(), rabbit_types:protocol()) - -> 'ok'). - --endif. - -%%--------------------------------------------------------------------------- - -start(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity) -> - start(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity, false). - -start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity) -> - start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity, false). - -start(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity, - ReaderWantsStats) -> - State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, - ReaderWantsStats), - {ok, proc_lib:spawn(?MODULE, enter_mainloop, [Identity, State])}. - -start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity, - ReaderWantsStats) -> - State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, - ReaderWantsStats), - {ok, proc_lib:spawn_link(?MODULE, enter_mainloop, [Identity, State])}. - -initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> - (case ReaderWantsStats of - true -> fun rabbit_event:init_stats_timer/2; - false -> fun rabbit_event:init_disabled_stats_timer/2 - end)(#wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax, - protocol = Protocol, - reader = ReaderPid, - pending = []}, - #wstate.stats_timer). - -system_continue(Parent, Deb, State) -> - mainloop(Deb, State#wstate{reader = Parent}). - -system_terminate(Reason, _Parent, _Deb, _State) -> - exit(Reason). - -system_code_change(Misc, _Module, _OldVsn, _Extra) -> - {ok, Misc}. - -enter_mainloop(Identity, State) -> - Deb = sys:debug_options([]), - ?store_proc_name(Identity), - mainloop(Deb, State). - -mainloop(Deb, State) -> - try - mainloop1(Deb, State) - catch - exit:Error -> #wstate{reader = ReaderPid, channel = Channel} = State, - ReaderPid ! {channel_exit, Channel, Error} - end, - done. - -mainloop1(Deb, State = #wstate{pending = []}) -> - receive - Message -> {Deb1, State1} = handle_message(Deb, Message, State), - ?MODULE:mainloop1(Deb1, State1) - after ?HIBERNATE_AFTER -> - erlang:hibernate(?MODULE, mainloop, [Deb, State]) - end; -mainloop1(Deb, State) -> - receive - Message -> {Deb1, State1} = handle_message(Deb, Message, State), - ?MODULE:mainloop1(Deb1, State1) - after 0 -> - ?MODULE:mainloop1(Deb, internal_flush(State)) - end. - -handle_message(Deb, {system, From, Req}, State = #wstate{reader = Parent}) -> - sys:handle_system_msg(Req, From, Parent, ?MODULE, Deb, State); -handle_message(Deb, Message, State) -> - {Deb, handle_message(Message, State)}. - -handle_message({send_command, MethodRecord}, State) -> - internal_send_command_async(MethodRecord, State); -handle_message({send_command, MethodRecord, Content}, State) -> - internal_send_command_async(MethodRecord, Content, State); -handle_message({send_command_flow, MethodRecord, Sender}, State) -> - credit_flow:ack(Sender), - internal_send_command_async(MethodRecord, State); -handle_message({send_command_flow, MethodRecord, Content, Sender}, State) -> - credit_flow:ack(Sender), - internal_send_command_async(MethodRecord, Content, State); -handle_message({'$gen_call', From, {send_command_sync, MethodRecord}}, State) -> - State1 = internal_flush( - internal_send_command_async(MethodRecord, State)), - gen_server:reply(From, ok), - State1; -handle_message({'$gen_call', From, {send_command_sync, MethodRecord, Content}}, - State) -> - State1 = internal_flush( - internal_send_command_async(MethodRecord, Content, State)), - gen_server:reply(From, ok), - State1; -handle_message({'$gen_call', From, flush}, State) -> - State1 = internal_flush(State), - gen_server:reply(From, ok), - State1; -handle_message({send_command_and_notify, QPid, ChPid, MethodRecord}, State) -> - State1 = internal_send_command_async(MethodRecord, State), - rabbit_amqqueue:notify_sent(QPid, ChPid), - State1; -handle_message({send_command_and_notify, QPid, ChPid, MethodRecord, Content}, - State) -> - State1 = internal_send_command_async(MethodRecord, Content, State), - rabbit_amqqueue:notify_sent(QPid, ChPid), - State1; -handle_message({'DOWN', _MRef, process, QPid, _Reason}, State) -> - rabbit_amqqueue:notify_sent_queue_down(QPid), - State; -handle_message({inet_reply, _, ok}, State) -> - rabbit_event:ensure_stats_timer(State, #wstate.stats_timer, emit_stats); -handle_message({inet_reply, _, Status}, _State) -> - exit({writer, send_failed, Status}); -handle_message(emit_stats, State = #wstate{reader = ReaderPid}) -> - ReaderPid ! ensure_stats, - rabbit_event:reset_stats_timer(State, #wstate.stats_timer); -handle_message(Message, _State) -> - exit({writer, message_not_understood, Message}). - -%%--------------------------------------------------------------------------- - -send_command(W, MethodRecord) -> - W ! {send_command, MethodRecord}, - ok. - -send_command(W, MethodRecord, Content) -> - W ! {send_command, MethodRecord, Content}, - ok. - -send_command_flow(W, MethodRecord) -> - credit_flow:send(W), - W ! {send_command_flow, MethodRecord, self()}, - ok. - -send_command_flow(W, MethodRecord, Content) -> - credit_flow:send(W), - W ! {send_command_flow, MethodRecord, Content, self()}, - ok. - -send_command_sync(W, MethodRecord) -> - call(W, {send_command_sync, MethodRecord}). - -send_command_sync(W, MethodRecord, Content) -> - call(W, {send_command_sync, MethodRecord, Content}). - -send_command_and_notify(W, Q, ChPid, MethodRecord) -> - W ! {send_command_and_notify, Q, ChPid, MethodRecord}, - ok. - -send_command_and_notify(W, Q, ChPid, MethodRecord, Content) -> - W ! {send_command_and_notify, Q, ChPid, MethodRecord, Content}, - ok. - -flush(W) -> call(W, flush). - -%%--------------------------------------------------------------------------- - -call(Pid, Msg) -> - {ok, Res} = gen:call(Pid, '$gen_call', Msg, infinity), - Res. - -%%--------------------------------------------------------------------------- - -assemble_frame(Channel, MethodRecord, Protocol) -> - rabbit_binary_generator:build_simple_method_frame( - Channel, MethodRecord, Protocol). - -assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) -> - MethodName = rabbit_misc:method_record_type(MethodRecord), - true = Protocol:method_has_content(MethodName), % assertion - MethodFrame = rabbit_binary_generator:build_simple_method_frame( - Channel, MethodRecord, Protocol), - ContentFrames = rabbit_binary_generator:build_simple_content_frames( - Channel, Content, FrameMax, Protocol), - [MethodFrame | ContentFrames]. - -tcp_send(Sock, Data) -> - rabbit_misc:throw_on_error(inet_error, - fun () -> rabbit_net:send(Sock, Data) end). - -internal_send_command(Sock, Channel, MethodRecord, Protocol) -> - ok = tcp_send(Sock, assemble_frame(Channel, MethodRecord, Protocol)). - -internal_send_command(Sock, Channel, MethodRecord, Content, FrameMax, - Protocol) -> - ok = lists:foldl(fun (Frame, ok) -> tcp_send(Sock, Frame); - (_Frame, Other) -> Other - end, ok, assemble_frames(Channel, MethodRecord, - Content, FrameMax, Protocol)). - -internal_send_command_async(MethodRecord, - State = #wstate{channel = Channel, - protocol = Protocol, - pending = Pending}) -> - Frame = assemble_frame(Channel, MethodRecord, Protocol), - maybe_flush(State#wstate{pending = [Frame | Pending]}). - -internal_send_command_async(MethodRecord, Content, - State = #wstate{channel = Channel, - frame_max = FrameMax, - protocol = Protocol, - pending = Pending}) -> - Frames = assemble_frames(Channel, MethodRecord, Content, FrameMax, - Protocol), - rabbit_basic:maybe_gc_large_msg(Content), - maybe_flush(State#wstate{pending = [Frames | Pending]}). - -%% When the amount of protocol method data buffered exceeds -%% this threshold, a socket flush is performed. -%% -%% This magic number is the tcp-over-ethernet MSS (1460) minus the -%% minimum size of a AMQP 0-9-1 basic.deliver method frame (24) plus basic -%% content header (22). The idea is that we want to flush just before -%% exceeding the MSS. --define(FLUSH_THRESHOLD, 1414). - -maybe_flush(State = #wstate{pending = Pending}) -> - case iolist_size(Pending) >= ?FLUSH_THRESHOLD of - true -> internal_flush(State); - false -> State - end. - -internal_flush(State = #wstate{pending = []}) -> - State; -internal_flush(State = #wstate{sock = Sock, pending = Pending}) -> - ok = port_cmd(Sock, lists:reverse(Pending)), - State#wstate{pending = []}. - -%% gen_tcp:send/2 does a selective receive of {inet_reply, Sock, -%% Status} to obtain the result. That is bad when it is called from -%% the writer since it requires scanning of the writers possibly quite -%% large message queue. -%% -%% So instead we lift the code from prim_inet:send/2, which is what -%% gen_tcp:send/2 calls, do the first half here and then just process -%% the result code in handle_message/2 as and when it arrives. -%% -%% This means we may end up happily sending data down a closed/broken -%% socket, but that's ok since a) data in the buffers will be lost in -%% any case (so qualitatively we are no worse off than if we used -%% gen_tcp:send/2), and b) we do detect the changed socket status -%% eventually, i.e. when we get round to handling the result code. -%% -%% Also note that the port has bounded buffers and port_command blocks -%% when these are full. So the fact that we process the result -%% asynchronously does not impact flow control. -port_cmd(Sock, Data) -> - true = try rabbit_net:port_command(Sock, Data) - catch error:Error -> exit({writer, send_failed, Error}) - end, - ok. diff --git a/src/ssl_compat.erl b/src/ssl_compat.erl deleted file mode 100644 index fc83fbcfa6..0000000000 --- a/src/ssl_compat.erl +++ /dev/null @@ -1,75 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(ssl_compat). - -%% We don't want warnings about the use of erlang:now/0 in -%% this module. --compile(nowarn_deprecated_function). - --export([connection_information/1, - connection_information/2]). - -connection_information(SslSocket) -> - try - ssl:connection_information(SslSocket) - catch - error:undef -> - case ssl:connection_info(SslSocket) of - {ok, {ProtocolVersion, CipherSuite}} -> - {ok, [{protocol, ProtocolVersion}, - {cipher_suite, CipherSuite}]}; - {error, Reason} -> - {error, Reason} - end - end. - -connection_information(SslSocket, Items) -> - try - ssl:connection_information(SslSocket, Items) - catch - error:undef -> - WantProtocolVersion = lists:member(protocol, Items), - WantCipherSuite = lists:member(cipher_suite, Items), - if - WantProtocolVersion orelse WantCipherSuite -> - case ssl:connection_info(SslSocket) of - {ok, {ProtocolVersion, CipherSuite}} -> - filter_information_items(ProtocolVersion, - CipherSuite, - Items, - []); - {error, Reason} -> - {error, Reason} - end; - true -> - {ok, []} - end - end. - -filter_information_items(ProtocolVersion, CipherSuite, [protocol | Rest], - Result) -> - filter_information_items(ProtocolVersion, CipherSuite, Rest, - [{protocol, ProtocolVersion} | Result]); -filter_information_items(ProtocolVersion, CipherSuite, [cipher_suite | Rest], - Result) -> - filter_information_items(ProtocolVersion, CipherSuite, Rest, - [{cipher_suite, CipherSuite} | Result]); -filter_information_items(ProtocolVersion, CipherSuite, [_ | Rest], - Result) -> - filter_information_items(ProtocolVersion, CipherSuite, Rest, Result); -filter_information_items(_ProtocolVersion, _CipherSuite, [], Result) -> - {ok, lists:reverse(Result)}. diff --git a/src/supervisor2.erl b/src/supervisor2.erl deleted file mode 100644 index c8ffbb12ea..0000000000 --- a/src/supervisor2.erl +++ /dev/null @@ -1,1553 +0,0 @@ -%% This file is a copy of supervisor.erl from the R16B Erlang/OTP -%% distribution, with the following modifications: -%% -%% 1) the module name is supervisor2 -%% -%% 2) a find_child/2 utility function has been added -%% -%% 3) Added an 'intrinsic' restart type. Like the transient type, this -%% type means the child should only be restarted if the child exits -%% abnormally. Unlike the transient type, if the child exits -%% normally, the supervisor itself also exits normally. If the -%% child is a supervisor and it exits normally (i.e. with reason of -%% 'shutdown') then the child's parent also exits normally. -%% -%% 4) child specifications can contain, as the restart type, a tuple -%% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay} -%% where Delay >= 0 (see point (4) below for intrinsic). The delay, -%% in seconds, indicates what should happen if a child, upon being -%% restarted, exceeds the MaxT and MaxR parameters. Thus, if a -%% child exits, it is restarted as normal. If it exits sufficiently -%% quickly and often to exceed the boundaries set by the MaxT and -%% MaxR parameters, and a Delay is specified, then rather than -%% stopping the supervisor, the supervisor instead continues and -%% tries to start up the child again, Delay seconds later. -%% -%% Note that if a child is delay-restarted this will reset the -%% count of restarts towrds MaxR and MaxT. This matters if MaxT > -%% Delay, since otherwise we would fail to restart after the delay. -%% -%% Sometimes, you may wish for a transient or intrinsic child to -%% exit abnormally so that it gets restarted, but still log -%% nothing. gen_server will log any exit reason other than -%% 'normal', 'shutdown' or {'shutdown', _}. Thus the exit reason of -%% {'shutdown', 'restart'} is interpreted to mean you wish the -%% child to be restarted according to the delay parameters, but -%% gen_server will not log the error. Thus from gen_server's -%% perspective it's a normal exit, whilst from supervisor's -%% perspective, it's an abnormal exit. -%% -%% 5) normal, and {shutdown, _} exit reasons are all treated the same -%% (i.e. are regarded as normal exits) -%% -%% All modifications are (C) 2010-2013 GoPivotal, Inc. -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% 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. -%% -%% %CopyrightEnd% -%% --module(supervisor2). - --behaviour(gen_server). - -%% External exports --export([start_link/2, start_link/3, - start_child/2, restart_child/2, - delete_child/2, terminate_child/2, - which_children/1, count_children/1, - find_child/2, check_childspecs/1]). - -%% Internal exports --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). --export([try_again_restart/3]). - -%%-------------------------------------------------------------------------- --ifdef(use_specs). --export_type([child_spec/0, startchild_ret/0, strategy/0, sup_name/0]). --endif. -%%-------------------------------------------------------------------------- - --ifdef(use_specs). --type child() :: 'undefined' | pid(). --type child_id() :: term(). --type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. --type modules() :: [module()] | 'dynamic'. --type delay() :: non_neg_integer(). --type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' | {'permanent', delay()} | {'transient', delay()} | {'intrinsic', delay()}. --type shutdown() :: 'brutal_kill' | timeout(). --type worker() :: 'worker' | 'supervisor'. --type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. --type sup_ref() :: (Name :: atom()) - | {Name :: atom(), Node :: node()} - | {'global', Name :: atom()} - | pid(). --type child_spec() :: {Id :: child_id(), - StartFunc :: mfargs(), - Restart :: restart(), - Shutdown :: shutdown(), - Type :: worker(), - Modules :: modules()}. - --type strategy() :: 'one_for_all' | 'one_for_one' - | 'rest_for_one' | 'simple_one_for_one'. --endif. - -%%-------------------------------------------------------------------------- - --ifdef(use_specs). --record(child, {% pid is undefined when child is not running - pid = undefined :: child() | {restarting,pid()} | [pid()], - name :: child_id(), - mfargs :: mfargs(), - restart_type :: restart(), - shutdown :: shutdown(), - child_type :: worker(), - modules = [] :: modules()}). --type child_rec() :: #child{}. --else. --record(child, { - pid = undefined, - name, - mfargs, - restart_type, - shutdown, - child_type, - modules = []}). --endif. - --define(DICT, dict). --define(SETS, sets). --define(SET, set). - --ifdef(use_specs). --record(state, {name, - strategy :: strategy(), - children = [] :: [child_rec()], - dynamics :: ?DICT:?DICT() | ?SETS:?SET(), - intensity :: non_neg_integer(), - period :: pos_integer(), - restarts = [], - module, - args}). --type state() :: #state{}. --else. --record(state, {name, - strategy, - children = [], - dynamics, - intensity, - period, - restarts = [], - module, - args}). --endif. - --define(is_simple(State), State#state.strategy =:= simple_one_for_one). --define(is_permanent(R), ((R =:= permanent) orelse - (is_tuple(R) andalso - tuple_size(R) == 2 andalso - element(1, R) =:= permanent))). --define(is_explicit_restart(R), - R == {shutdown, restart}). - --ifdef(use_specs). --callback init(Args :: term()) -> - {ok, {{RestartStrategy :: strategy(), - MaxR :: non_neg_integer(), - MaxT :: non_neg_integer()}, - [ChildSpec :: child_spec()]}} - | ignore. --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{init,1}]; -behaviour_info(_Other) -> - undefined. - --endif. --define(restarting(_Pid_), {restarting,_Pid_}). - -%%% --------------------------------------------------- -%%% This is a general process supervisor built upon gen_server.erl. -%%% Servers/processes should/could also be built using gen_server.erl. -%%% SupName = {local, atom()} | {global, atom()}. -%%% --------------------------------------------------- --ifdef(use_specs). --type startlink_err() :: {'already_started', pid()} - | {'shutdown', term()} - | term(). --type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. - --spec start_link(Module, Args) -> startlink_ret() when - Module :: module(), - Args :: term(). - --endif. -start_link(Mod, Args) -> - gen_server:start_link(?MODULE, {self, Mod, Args}, []). - --ifdef(use_specs). --spec start_link(SupName, Module, Args) -> startlink_ret() when - SupName :: sup_name(), - Module :: module(), - Args :: term(). --endif. -start_link(SupName, Mod, Args) -> - gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []). - -%%% --------------------------------------------------- -%%% Interface functions. -%%% --------------------------------------------------- --ifdef(use_specs). --type startchild_err() :: 'already_present' - | {'already_started', Child :: child()} | term(). --type startchild_ret() :: {'ok', Child :: child()} - | {'ok', Child :: child(), Info :: term()} - | {'error', startchild_err()}. - --spec start_child(SupRef, ChildSpec) -> startchild_ret() when - SupRef :: sup_ref(), - ChildSpec :: child_spec() | (List :: [term()]). --endif. -start_child(Supervisor, ChildSpec) -> - call(Supervisor, {start_child, ChildSpec}). - --ifdef(use_specs). --spec restart_child(SupRef, Id) -> Result when - SupRef :: sup_ref(), - Id :: child_id(), - Result :: {'ok', Child :: child()} - | {'ok', Child :: child(), Info :: term()} - | {'error', Error}, - Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' | - term(). --endif. -restart_child(Supervisor, Name) -> - call(Supervisor, {restart_child, Name}). - --ifdef(use_specs). --spec delete_child(SupRef, Id) -> Result when - SupRef :: sup_ref(), - Id :: child_id(), - Result :: 'ok' | {'error', Error}, - Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'. --endif. -delete_child(Supervisor, Name) -> - call(Supervisor, {delete_child, Name}). - -%%----------------------------------------------------------------- -%% Func: terminate_child/2 -%% Returns: ok | {error, Reason} -%% Note that the child is *always* terminated in some -%% way (maybe killed). -%%----------------------------------------------------------------- --ifdef(use_specs). --spec terminate_child(SupRef, Id) -> Result when - SupRef :: sup_ref(), - Id :: pid() | child_id(), - Result :: 'ok' | {'error', Error}, - Error :: 'not_found' | 'simple_one_for_one'. --endif. -terminate_child(Supervisor, Name) -> - call(Supervisor, {terminate_child, Name}). - --ifdef(use_specs). --spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when - SupRef :: sup_ref(), - Id :: child_id() | undefined, - Child :: child() | 'restarting', - Type :: worker(), - Modules :: modules(). --endif. -which_children(Supervisor) -> - call(Supervisor, which_children). - --ifdef(use_specs). --spec count_children(SupRef) -> PropListOfCounts when - SupRef :: sup_ref(), - PropListOfCounts :: [Count], - Count :: {specs, ChildSpecCount :: non_neg_integer()} - | {active, ActiveProcessCount :: non_neg_integer()} - | {supervisors, ChildSupervisorCount :: non_neg_integer()} - |{workers, ChildWorkerCount :: non_neg_integer()}. --endif. -count_children(Supervisor) -> - call(Supervisor, count_children). - --ifdef(use_specs). --spec find_child(Supervisor, Name) -> [pid()] when - Supervisor :: sup_ref(), - Name :: child_id(). --endif. -find_child(Supervisor, Name) -> - [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), - Name1 =:= Name]. - -call(Supervisor, Req) -> - gen_server:call(Supervisor, Req, infinity). - --ifdef(use_specs). --spec check_childspecs(ChildSpecs) -> Result when - ChildSpecs :: [child_spec()], - Result :: 'ok' | {'error', Error :: term()}. --endif. -check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> - case check_startspec(ChildSpecs) of - {ok, _} -> ok; - Error -> {error, Error} - end; -check_childspecs(X) -> {error, {badarg, X}}. - -%%%----------------------------------------------------------------- -%%% Called by timer:apply_after from restart/2 --ifdef(use_specs). --spec try_again_restart(SupRef, Child, Reason) -> ok when - SupRef :: sup_ref(), - Child :: child_id() | pid(), - Reason :: term(). --endif. -try_again_restart(Supervisor, Child, Reason) -> - cast(Supervisor, {try_again_restart, Child, Reason}). - -cast(Supervisor, Req) -> - gen_server:cast(Supervisor, Req). - -%%% --------------------------------------------------- -%%% -%%% Initialize the supervisor. -%%% -%%% --------------------------------------------------- --ifdef(use_specs). --type init_sup_name() :: sup_name() | 'self'. - --type stop_rsn() :: {'shutdown', term()} - | {'bad_return', {module(),'init', term()}} - | {'bad_start_spec', term()} - | {'start_spec', term()} - | {'supervisor_data', term()}. - --spec init({init_sup_name(), module(), [term()]}) -> - {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. --endif. -init({SupName, Mod, Args}) -> - process_flag(trap_exit, true), - case Mod:init(Args) of - {ok, {SupFlags, StartSpec}} -> - case init_state(SupName, SupFlags, Mod, Args) of - {ok, State} when ?is_simple(State) -> - init_dynamic(State, StartSpec); - {ok, State} -> - init_children(State, StartSpec); - Error -> - {stop, {supervisor_data, Error}} - end; - ignore -> - ignore; - Error -> - {stop, {bad_return, {Mod, init, Error}}} - end. - -init_children(State, StartSpec) -> - SupName = State#state.name, - case check_startspec(StartSpec) of - {ok, Children} -> - case start_children(Children, SupName) of - {ok, NChildren} -> - {ok, State#state{children = NChildren}}; - {error, NChildren, Reason} -> - terminate_children(NChildren, SupName), - {stop, {shutdown, Reason}} - end; - Error -> - {stop, {start_spec, Error}} - end. - -init_dynamic(State, [StartSpec]) -> - case check_startspec([StartSpec]) of - {ok, Children} -> - {ok, State#state{children = Children}}; - Error -> - {stop, {start_spec, Error}} - end; -init_dynamic(_State, StartSpec) -> - {stop, {bad_start_spec, StartSpec}}. - -%%----------------------------------------------------------------- -%% Func: start_children/2 -%% Args: Children = [child_rec()] in start order -%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} -%% Purpose: Start all children. The new list contains #child's -%% with pids. -%% Returns: {ok, NChildren} | {error, NChildren, Reason} -%% NChildren = [child_rec()] in termination order (reversed -%% start order) -%%----------------------------------------------------------------- -start_children(Children, SupName) -> start_children(Children, [], SupName). - -start_children([Child|Chs], NChildren, SupName) -> - case do_start_child(SupName, Child) of - {ok, undefined} when Child#child.restart_type =:= temporary -> - start_children(Chs, NChildren, SupName); - {ok, Pid} -> - start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); - {ok, Pid, _Extra} -> - start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); - {error, Reason} -> - report_error(start_error, Reason, Child, SupName), - {error, lists:reverse(Chs) ++ [Child | NChildren], - {failed_to_start_child,Child#child.name,Reason}} - end; -start_children([], NChildren, _SupName) -> - {ok, NChildren}. - -do_start_child(SupName, Child) -> - #child{mfargs = {M, F, Args}} = Child, - case catch apply(M, F, Args) of - {ok, Pid} when is_pid(Pid) -> - NChild = Child#child{pid = Pid}, - report_progress(NChild, SupName), - {ok, Pid}; - {ok, Pid, Extra} when is_pid(Pid) -> - NChild = Child#child{pid = Pid}, - report_progress(NChild, SupName), - {ok, Pid, Extra}; - ignore -> - {ok, undefined}; - {error, What} -> {error, What}; - What -> {error, What} - end. - -do_start_child_i(M, F, A) -> - case catch apply(M, F, A) of - {ok, Pid} when is_pid(Pid) -> - {ok, Pid}; - {ok, Pid, Extra} when is_pid(Pid) -> - {ok, Pid, Extra}; - ignore -> - {ok, undefined}; - {error, Error} -> - {error, Error}; - What -> - {error, What} - end. - -%%% --------------------------------------------------- -%%% -%%% Callback functions. -%%% -%%% --------------------------------------------------- --ifdef(use_specs). --type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine --spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. --endif. -handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> - Child = hd(State#state.children), - #child{mfargs = {M, F, A}} = Child, - Args = A ++ EArgs, - case do_start_child_i(M, F, Args) of - {ok, undefined} when Child#child.restart_type =:= temporary -> - {reply, {ok, undefined}, State}; - {ok, Pid} -> - NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), - {reply, {ok, Pid}, NState}; - {ok, Pid, Extra} -> - NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), - {reply, {ok, Pid, Extra}, NState}; - What -> - {reply, What, State} - end; - -%% terminate_child for simple_one_for_one can only be done with pid -handle_call({terminate_child, Name}, _From, State) when not is_pid(Name), - ?is_simple(State) -> - {reply, {error, simple_one_for_one}, State}; - -handle_call({terminate_child, Name}, _From, State) -> - case get_child(Name, State, ?is_simple(State)) of - {value, Child} -> - case do_terminate(Child, State#state.name) of - #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) -> - {reply, ok, state_del_child(Child, State)}; - NChild -> - {reply, ok, replace_child(NChild, State)} - end; - false -> - {reply, {error, not_found}, State} - end; - -%%% The requests delete_child and restart_child are invalid for -%%% simple_one_for_one supervisors. -handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> - {reply, {error, simple_one_for_one}, State}; - -handle_call({start_child, ChildSpec}, _From, State) -> - case check_childspec(ChildSpec) of - {ok, Child} -> - {Resp, NState} = handle_start_child(Child, State), - {reply, Resp, NState}; - What -> - {reply, {error, What}, State} - end; - -handle_call({restart_child, Name}, _From, State) -> - case get_child(Name, State) of - {value, Child} when Child#child.pid =:= undefined -> - case do_start_child(State#state.name, Child) of - {ok, Pid} -> - NState = replace_child(Child#child{pid = Pid}, State), - {reply, {ok, Pid}, NState}; - {ok, Pid, Extra} -> - NState = replace_child(Child#child{pid = Pid}, State), - {reply, {ok, Pid, Extra}, NState}; - Error -> - {reply, Error, State} - end; - {value, #child{pid=?restarting(_)}} -> - {reply, {error, restarting}, State}; - {value, _} -> - {reply, {error, running}, State}; - _ -> - {reply, {error, not_found}, State} - end; - -handle_call({delete_child, Name}, _From, State) -> - case get_child(Name, State) of - {value, Child} when Child#child.pid =:= undefined -> - NState = remove_child(Child, State), - {reply, ok, NState}; - {value, #child{pid=?restarting(_)}} -> - {reply, {error, restarting}, State}; - {value, _} -> - {reply, {error, running}, State}; - _ -> - {reply, {error, not_found}, State} - end; - -handle_call(which_children, _From, #state{children = [#child{restart_type = temporary, - child_type = CT, - modules = Mods}]} = - State) when ?is_simple(State) -> - Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, - ?SETS:to_list(dynamics_db(temporary, State#state.dynamics))), - {reply, Reply, State}; - -handle_call(which_children, _From, #state{children = [#child{restart_type = RType, - child_type = CT, - modules = Mods}]} = - State) when ?is_simple(State) -> - Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods}; - ({Pid, _}) -> {undefined, Pid, CT, Mods} end, - ?DICT:to_list(dynamics_db(RType, State#state.dynamics))), - {reply, Reply, State}; - -handle_call(which_children, _From, State) -> - Resp = - lists:map(fun(#child{pid = ?restarting(_), name = Name, - child_type = ChildType, modules = Mods}) -> - {Name, restarting, ChildType, Mods}; - (#child{pid = Pid, name = Name, - child_type = ChildType, modules = Mods}) -> - {Name, Pid, ChildType, Mods} - end, - State#state.children), - {reply, Resp, State}; - - -handle_call(count_children, _From, #state{children = [#child{restart_type = temporary, - child_type = CT}]} = State) - when ?is_simple(State) -> - {Active, Count} = - ?SETS:fold(fun(Pid, {Alive, Tot}) -> - case is_pid(Pid) andalso is_process_alive(Pid) of - true ->{Alive+1, Tot +1}; - false -> - {Alive, Tot + 1} - end - end, {0, 0}, dynamics_db(temporary, State#state.dynamics)), - Reply = case CT of - supervisor -> [{specs, 1}, {active, Active}, - {supervisors, Count}, {workers, 0}]; - worker -> [{specs, 1}, {active, Active}, - {supervisors, 0}, {workers, Count}] - end, - {reply, Reply, State}; - -handle_call(count_children, _From, #state{children = [#child{restart_type = RType, - child_type = CT}]} = State) - when ?is_simple(State) -> - {Active, Count} = - ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) -> - case is_pid(Pid) andalso is_process_alive(Pid) of - true -> - {Alive+1, Tot +1}; - false -> - {Alive, Tot + 1} - end - end, {0, 0}, dynamics_db(RType, State#state.dynamics)), - Reply = case CT of - supervisor -> [{specs, 1}, {active, Active}, - {supervisors, Count}, {workers, 0}]; - worker -> [{specs, 1}, {active, Active}, - {supervisors, 0}, {workers, Count}] - end, - {reply, Reply, State}; - -handle_call(count_children, _From, State) -> - %% Specs and children are together on the children list... - {Specs, Active, Supers, Workers} = - lists:foldl(fun(Child, Counts) -> - count_child(Child, Counts) - end, {0,0,0,0}, State#state.children), - - %% Reformat counts to a property list. - Reply = [{specs, Specs}, {active, Active}, - {supervisors, Supers}, {workers, Workers}], - {reply, Reply, State}. - - -count_child(#child{pid = Pid, child_type = worker}, - {Specs, Active, Supers, Workers}) -> - case is_pid(Pid) andalso is_process_alive(Pid) of - true -> {Specs+1, Active+1, Supers, Workers+1}; - false -> {Specs+1, Active, Supers, Workers+1} - end; -count_child(#child{pid = Pid, child_type = supervisor}, - {Specs, Active, Supers, Workers}) -> - case is_pid(Pid) andalso is_process_alive(Pid) of - true -> {Specs+1, Active+1, Supers+1, Workers}; - false -> {Specs+1, Active, Supers+1, Workers} - end. - - -%%% If a restart attempt failed, this message is sent via -%%% timer:apply_after(0,...) in order to give gen_server the chance to -%%% check it's inbox before trying again. --ifdef(use_specs). --spec handle_cast({try_again_restart, child_id() | pid(), term()}, state()) -> - {'noreply', state()} | {stop, shutdown, state()}. --endif. -handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State) - when ?is_simple(State) -> - RT = Child#child.restart_type, - RPid = restarting(Pid), - case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of - {ok, Args} -> - {M, F, _} = Child#child.mfargs, - NChild = Child#child{pid = RPid, mfargs = {M, F, Args}}, - try_restart(Child#child.restart_type, Reason, NChild, State); - error -> - {noreply, State} - end; - -handle_cast({try_again_restart,Name,Reason}, State) -> - %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist - case lists:keysearch(Name,#child.name,State#state.children) of - {value, Child = #child{pid=?restarting(_), restart_type=RestartType}} -> - try_restart(RestartType, Reason, Child, State); - _ -> - {noreply,State} - end. - -%% -%% Take care of terminated children. -%% --ifdef(use_specs). --spec handle_info(term(), state()) -> - {'noreply', state()} | {'stop', 'shutdown', state()}. --endif. -handle_info({'EXIT', Pid, Reason}, State) -> - case restart_child(Pid, Reason, State) of - {ok, State1} -> - {noreply, State1}; - {shutdown, State1} -> - {stop, shutdown, State1} - end; - -handle_info({delayed_restart, {RestartType, Reason, Child}}, State) - when ?is_simple(State) -> - try_restart(RestartType, Reason, Child, State#state{restarts = []}); %% [1] -handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> - case get_child(Child#child.name, State) of - {value, Child1} -> - try_restart(RestartType, Reason, Child1, - State#state{restarts = []}); %% [1] - _What -> - {noreply, State} - end; -%% [1] When we receive a delayed_restart message we want to reset the -%% restarts field since otherwise the MaxT might not have elapsed and -%% we would just delay again and again. Since a common use of the -%% delayed restart feature is for MaxR = 1, MaxT = some huge number -%% (so that we don't end up bouncing around in non-delayed restarts) -%% this is important. - -handle_info(Msg, State) -> - error_logger:error_msg("Supervisor received unexpected message: ~p~n", - [Msg]), - {noreply, State}. - -%% -%% Terminate this server. -%% --ifdef(use_specs). --spec terminate(term(), state()) -> 'ok'. --endif. -terminate(_Reason, #state{children=[Child]} = State) when ?is_simple(State) -> - terminate_dynamic_children(Child, dynamics_db(Child#child.restart_type, - State#state.dynamics), - State#state.name); -terminate(_Reason, State) -> - terminate_children(State#state.children, State#state.name). - -%% -%% Change code for the supervisor. -%% Call the new call-back module and fetch the new start specification. -%% Combine the new spec. with the old. If the new start spec. is -%% not valid the code change will not succeed. -%% Use the old Args as argument to Module:init/1. -%% NOTE: This requires that the init function of the call-back module -%% does not have any side effects. -%% --ifdef(use_specs). --spec code_change(term(), state(), term()) -> - {'ok', state()} | {'error', term()}. --endif. -code_change(_, State, _) -> - case (State#state.module):init(State#state.args) of - {ok, {SupFlags, StartSpec}} -> - case catch check_flags(SupFlags) of - ok -> - {Strategy, MaxIntensity, Period} = SupFlags, - update_childspec(State#state{strategy = Strategy, - intensity = MaxIntensity, - period = Period}, - StartSpec); - Error -> - {error, Error} - end; - ignore -> - {ok, State}; - Error -> - Error - end. - -check_flags({Strategy, MaxIntensity, Period}) -> - validStrategy(Strategy), - validIntensity(MaxIntensity), - validPeriod(Period), - ok; -check_flags(What) -> - {bad_flags, What}. - -update_childspec(State, StartSpec) when ?is_simple(State) -> - case check_startspec(StartSpec) of - {ok, [Child]} -> - {ok, State#state{children = [Child]}}; - Error -> - {error, Error} - end; -update_childspec(State, StartSpec) -> - case check_startspec(StartSpec) of - {ok, Children} -> - OldC = State#state.children, % In reverse start order ! - NewC = update_childspec1(OldC, Children, []), - {ok, State#state{children = NewC}}; - Error -> - {error, Error} - end. - -update_childspec1([Child|OldC], Children, KeepOld) -> - case update_chsp(Child, Children) of - {ok,NewChildren} -> - update_childspec1(OldC, NewChildren, KeepOld); - false -> - update_childspec1(OldC, Children, [Child|KeepOld]) - end; -update_childspec1([], Children, KeepOld) -> - %% Return them in (kept) reverse start order. - lists:reverse(Children ++ KeepOld). - -update_chsp(OldCh, Children) -> - case lists:map(fun(Ch) when OldCh#child.name =:= Ch#child.name -> - Ch#child{pid = OldCh#child.pid}; - (Ch) -> - Ch - end, - Children) of - Children -> - false; % OldCh not found in new spec. - NewC -> - {ok, NewC} - end. - -%%% --------------------------------------------------- -%%% Start a new child. -%%% --------------------------------------------------- - -handle_start_child(Child, State) -> - case get_child(Child#child.name, State) of - false -> - case do_start_child(State#state.name, Child) of - {ok, undefined} when Child#child.restart_type =:= temporary -> - {{ok, undefined}, State}; - {ok, Pid} -> - {{ok, Pid}, save_child(Child#child{pid = Pid}, State)}; - {ok, Pid, Extra} -> - {{ok, Pid, Extra}, save_child(Child#child{pid = Pid}, State)}; - {error, What} -> - {{error, {What, Child}}, State} - end; - {value, OldChild} when is_pid(OldChild#child.pid) -> - {{error, {already_started, OldChild#child.pid}}, State}; - {value, _OldChild} -> - {{error, already_present}, State} - end. - -%%% --------------------------------------------------- -%%% Restart. A process has terminated. -%%% Returns: {ok, state()} | {shutdown, state()} -%%% --------------------------------------------------- - -restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) -> - RestartType = Child#child.restart_type, - case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of - {ok, Args} -> - {M, F, _} = Child#child.mfargs, - NChild = Child#child{pid = Pid, mfargs = {M, F, Args}}, - do_restart(RestartType, Reason, NChild, State); - error -> - {ok, State} - end; - -restart_child(Pid, Reason, State) -> - Children = State#state.children, - %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist - case lists:keysearch(Pid, #child.pid, Children) of - {value, #child{restart_type = RestartType} = Child} -> - do_restart(RestartType, Reason, Child, State); - false -> - {ok, State} - end. - -try_restart(RestartType, Reason, Child, State) -> - case handle_restart(RestartType, Reason, Child, State) of - {ok, NState} -> {noreply, NState}; - {shutdown, State2} -> {stop, shutdown, State2} - end. - -do_restart(RestartType, Reason, Child, State) -> - maybe_report_error(RestartType, Reason, Child, State), - handle_restart(RestartType, Reason, Child, State). - -maybe_report_error(permanent, Reason, Child, State) -> - report_child_termination(Reason, Child, State); -maybe_report_error({permanent, _}, Reason, Child, State) -> - report_child_termination(Reason, Child, State); -maybe_report_error(_Type, Reason, Child, State) -> - case is_abnormal_termination(Reason) of - true -> report_child_termination(Reason, Child, State); - false -> ok - end. - -report_child_termination(Reason, Child, State) -> - report_error(child_terminated, Reason, Child, State#state.name). - -handle_restart(permanent, _Reason, Child, State) -> - restart(Child, State); -handle_restart(transient, Reason, Child, State) -> - restart_if_explicit_or_abnormal(fun restart/2, - fun delete_child_and_continue/2, - Reason, Child, State); -handle_restart(intrinsic, Reason, Child, State) -> - restart_if_explicit_or_abnormal(fun restart/2, - fun delete_child_and_stop/2, - Reason, Child, State); -handle_restart(temporary, _Reason, Child, State) -> - delete_child_and_continue(Child, State); -handle_restart({permanent, _Delay}=Restart, Reason, Child, State) -> - do_restart_delay(Restart, Reason, Child, State); -handle_restart({transient, _Delay}=Restart, Reason, Child, State) -> - restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), - fun delete_child_and_continue/2, - Reason, Child, State); -handle_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> - restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), - fun delete_child_and_stop/2, - Reason, Child, State). - -restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) -> - case ?is_explicit_restart(Reason) orelse is_abnormal_termination(Reason) of - true -> RestartHow(Child, State); - false -> Otherwise(Child, State) - end. - -defer_to_restart_delay(Restart, Reason) -> - fun(Child, State) -> do_restart_delay(Restart, Reason, Child, State) end. - -delete_child_and_continue(Child, State) -> - {ok, state_del_child(Child, State)}. - -delete_child_and_stop(Child, State) -> - {shutdown, state_del_child(Child, State)}. - -is_abnormal_termination(normal) -> false; -is_abnormal_termination(shutdown) -> false; -is_abnormal_termination({shutdown, _}) -> false; -is_abnormal_termination(_Other) -> true. - -do_restart_delay({RestartType, Delay}, Reason, Child, State) -> - case add_restart(State) of - {ok, NState} -> - maybe_restart(NState#state.strategy, Child, NState); - {terminate, _NState} -> - %% we've reached the max restart intensity, but the - %% add_restart will have added to the restarts - %% field. Given we don't want to die here, we need to go - %% back to the old restarts field otherwise we'll never - %% attempt to restart later, which is why we ignore - %% NState for this clause. - _TRef = erlang:send_after(trunc(Delay*1000), self(), - {delayed_restart, - {{RestartType, Delay}, Reason, Child}}), - {ok, state_del_child(Child, State)} - end. - -restart(Child, State) -> - case add_restart(State) of - {ok, NState} -> - maybe_restart(NState#state.strategy, Child, NState); - {terminate, NState} -> - report_error(shutdown, reached_max_restart_intensity, - Child, State#state.name), - {shutdown, remove_child(Child, NState)} - end. - -maybe_restart(Strategy, Child, State) -> - case restart(Strategy, Child, State) of - {try_again, Reason, NState2} -> - %% Leaving control back to gen_server before - %% trying again. This way other incoming requsts - %% for the supervisor can be handled - e.g. a - %% shutdown request for the supervisor or the - %% child. - Id = if ?is_simple(State) -> Child#child.pid; - true -> Child#child.name - end, - timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), - {ok,NState2}; - Other -> - Other - end. - -restart(simple_one_for_one, Child, State) -> - #child{pid = OldPid, mfargs = {M, F, A}} = Child, - Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type, - State#state.dynamics)), - case do_start_child_i(M, F, A) of - {ok, Pid} -> - NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, - {ok, NState}; - {ok, Pid, _Extra} -> - NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, - {ok, NState}; - {error, Error} -> - NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A, - Dynamics)}, - report_error(start_error, Error, Child, State#state.name), - {try_again, Error, NState} - end; -restart(one_for_one, Child, State) -> - OldPid = Child#child.pid, - case do_start_child(State#state.name, Child) of - {ok, Pid} -> - NState = replace_child(Child#child{pid = Pid}, State), - {ok, NState}; - {ok, Pid, _Extra} -> - NState = replace_child(Child#child{pid = Pid}, State), - {ok, NState}; - {error, Reason} -> - NState = replace_child(Child#child{pid = restarting(OldPid)}, State), - report_error(start_error, Reason, Child, State#state.name), - {try_again, Reason, NState} - end; -restart(rest_for_one, Child, State) -> - {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children), - ChAfter2 = terminate_children(ChAfter, State#state.name), - case start_children(ChAfter2, State#state.name) of - {ok, ChAfter3} -> - {ok, State#state{children = ChAfter3 ++ ChBefore}}; - {error, ChAfter3, Reason} -> - NChild = Child#child{pid=restarting(Child#child.pid)}, - NState = State#state{children = ChAfter3 ++ ChBefore}, - {try_again, Reason, replace_child(NChild,NState)} - end; -restart(one_for_all, Child, State) -> - Children1 = del_child(Child#child.pid, State#state.children), - Children2 = terminate_children(Children1, State#state.name), - case start_children(Children2, State#state.name) of - {ok, NChs} -> - {ok, State#state{children = NChs}}; - {error, NChs, Reason} -> - NChild = Child#child{pid=restarting(Child#child.pid)}, - NState = State#state{children = NChs}, - {try_again, Reason, replace_child(NChild,NState)} - end. - -restarting(Pid) when is_pid(Pid) -> ?restarting(Pid); -restarting(RPid) -> RPid. - -%%----------------------------------------------------------------- -%% Func: terminate_children/2 -%% Args: Children = [child_rec()] in termination order -%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: NChildren = [child_rec()] in -%% startup order (reversed termination order) -%%----------------------------------------------------------------- -terminate_children(Children, SupName) -> - terminate_children(Children, SupName, []). - -%% Temporary children should not be restarted and thus should -%% be skipped when building the list of terminated children, although -%% we do want them to be shut down as many functions from this module -%% use this function to just clear everything. -terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) -> - do_terminate(Child, SupName), - terminate_children(Children, SupName, Res); -terminate_children([Child | Children], SupName, Res) -> - NChild = do_terminate(Child, SupName), - terminate_children(Children, SupName, [NChild | Res]); -terminate_children([], _SupName, Res) -> - Res. - -do_terminate(Child, SupName) when is_pid(Child#child.pid) -> - case shutdown(Child#child.pid, Child#child.shutdown) of - ok -> - ok; - {error, normal} when not ?is_permanent(Child#child.restart_type) -> - ok; - {error, OtherReason} -> - report_error(shutdown_error, OtherReason, Child, SupName) - end, - Child#child{pid = undefined}; -do_terminate(Child, _SupName) -> - Child#child{pid = undefined}. - -%%----------------------------------------------------------------- -%% Shutdowns a child. We must check the EXIT value -%% of the child, because it might have died with another reason than -%% the wanted. In that case we want to report the error. We put a -%% monitor on the child an check for the 'DOWN' message instead of -%% checking for the 'EXIT' message, because if we check the 'EXIT' -%% message a "naughty" child, who does unlink(Sup), could hang the -%% supervisor. -%% Returns: ok | {error, OtherReason} (this should be reported) -%%----------------------------------------------------------------- -shutdown(Pid, brutal_kill) -> - case monitor_child(Pid) of - ok -> - exit(Pid, kill), - receive - {'DOWN', _MRef, process, Pid, killed} -> - ok; - {'DOWN', _MRef, process, Pid, OtherReason} -> - {error, OtherReason} - end; - {error, Reason} -> - {error, Reason} - end; -shutdown(Pid, Time) -> - case monitor_child(Pid) of - ok -> - exit(Pid, shutdown), %% Try to shutdown gracefully - receive - {'DOWN', _MRef, process, Pid, shutdown} -> - ok; - {'DOWN', _MRef, process, Pid, OtherReason} -> - {error, OtherReason} - after Time -> - exit(Pid, kill), %% Force termination. - receive - {'DOWN', _MRef, process, Pid, OtherReason} -> - {error, OtherReason} - end - end; - {error, Reason} -> - {error, Reason} - end. - -%% Help function to shutdown/2 switches from link to monitor approach -monitor_child(Pid) -> - - %% Do the monitor operation first so that if the child dies - %% before the monitoring is done causing a 'DOWN'-message with - %% reason noproc, we will get the real reason in the 'EXIT'-message - %% unless a naughty child has already done unlink... - erlang:monitor(process, Pid), - unlink(Pid), - - receive - %% If the child dies before the unlik we must empty - %% the mail-box of the 'EXIT'-message and the 'DOWN'-message. - {'EXIT', Pid, Reason} -> - receive - {'DOWN', _, process, Pid, _} -> - {error, Reason} - end - after 0 -> - %% If a naughty child did unlink and the child dies before - %% monitor the result will be that shutdown/2 receives a - %% 'DOWN'-message with reason noproc. - %% If the child should die after the unlink there - %% will be a 'DOWN'-message with a correct reason - %% that will be handled in shutdown/2. - ok - end. - - -%%----------------------------------------------------------------- -%% Func: terminate_dynamic_children/3 -%% Args: Child = child_rec() -%% Dynamics = ?DICT() | ?SET() -%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: ok -%% -%% -%% Shutdown all dynamic children. This happens when the supervisor is -%% stopped. Because the supervisor can have millions of dynamic children, we -%% can have an significative overhead here. -%%----------------------------------------------------------------- -terminate_dynamic_children(Child, Dynamics, SupName) -> - {Pids, EStack0} = monitor_dynamic_children(Child, Dynamics), - Sz = ?SETS:size(Pids), - EStack = case Child#child.shutdown of - brutal_kill -> - ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), - wait_dynamic_children(Child, Pids, Sz, undefined, EStack0); - infinity -> - ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids), - wait_dynamic_children(Child, Pids, Sz, undefined, EStack0); - Time -> - ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids), - TRef = erlang:start_timer(Time, self(), kill), - wait_dynamic_children(Child, Pids, Sz, TRef, EStack0) - end, - %% Unroll stacked errors and report them - ?DICT:fold(fun(Reason, Ls, _) -> - report_error(shutdown_error, Reason, - Child#child{pid=Ls}, SupName) - end, ok, EStack). - - -monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) -> - ?SETS:fold(fun(P, {Pids, EStack}) -> - case monitor_child(P) of - ok -> - {?SETS:add_element(P, Pids), EStack}; - {error, normal} -> - {Pids, EStack}; - {error, Reason} -> - {Pids, ?DICT:append(Reason, P, EStack)} - end - end, {?SETS:new(), ?DICT:new()}, Dynamics); -monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> - ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) -> - case monitor_child(P) of - ok -> - {?SETS:add_element(P, Pids), EStack}; - {error, normal} when not ?is_permanent(RType) -> - {Pids, EStack}; - {error, Reason} -> - {Pids, ?DICT:append(Reason, P, EStack)} - end; - (?restarting(_), _, {Pids, EStack}) -> - {Pids, EStack} - end, {?SETS:new(), ?DICT:new()}, Dynamics). - -wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) -> - EStack; -wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) -> - %% If the timer has expired before its cancellation, we must empty the - %% mail-box of the 'timeout'-message. - erlang:cancel_timer(TRef), - receive - {timeout, TRef, kill} -> - EStack - after 0 -> - EStack - end; -wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz, - TRef, EStack) -> - receive - {'DOWN', _MRef, process, Pid, killed} -> - wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, EStack); - - {'DOWN', _MRef, process, Pid, Reason} -> - wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, ?DICT:append(Reason, Pid, EStack)) - end; -wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, - TRef, EStack) -> - receive - {'DOWN', _MRef, process, Pid, shutdown} -> - wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, EStack); - - {'DOWN', _MRef, process, Pid, normal} when not ?is_permanent(RType) -> - wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, EStack); - - {'DOWN', _MRef, process, Pid, Reason} -> - wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, - TRef, ?DICT:append(Reason, Pid, EStack)); - - {timeout, TRef, kill} -> - ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), - wait_dynamic_children(Child, Pids, Sz-1, undefined, EStack) - end. - -%%----------------------------------------------------------------- -%% Child/State manipulating functions. -%%----------------------------------------------------------------- - -%% Note we do not want to save the parameter list for temporary processes as -%% they will not be restarted, and hence we do not need this information. -%% Especially for dynamic children to simple_one_for_one supervisors -%% it could become very costly as it is not uncommon to spawn -%% very many such processes. -save_child(#child{restart_type = temporary, - mfargs = {M, F, _}} = Child, #state{children = Children} = State) -> - State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]}; -save_child(Child, #state{children = Children} = State) -> - State#state{children = [Child |Children]}. - -save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) -> - State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))}; -save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) -> - State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}. - -dynamics_db(temporary, undefined) -> - ?SETS:new(); -dynamics_db(_, undefined) -> - ?DICT:new(); -dynamics_db(_,Dynamics) -> - Dynamics. - -dynamic_child_args(Pid, Dynamics) -> - case ?SETS:is_set(Dynamics) of - true -> - {ok, undefined}; - false -> - ?DICT:find(Pid, Dynamics) - end. - -state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) -> - NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)), - State#state{dynamics = NDynamics}; -state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) -> - NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)), - State#state{dynamics = NDynamics}; -state_del_child(Child, State) -> - NChildren = del_child(Child#child.name, State#state.children), - State#state{children = NChildren}. - -del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) when Ch#child.name =:= Name -> - Chs; -del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> - Chs; -del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> - [Ch#child{pid = undefined} | Chs]; -del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> - Chs; -del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> - [Ch#child{pid = undefined} | Chs]; -del_child(Name, [Ch|Chs]) -> - [Ch|del_child(Name, Chs)]; -del_child(_, []) -> - []. - -%% Chs = [S4, S3, Ch, S1, S0] -%% Ret: {[S4, S3, Ch], [S1, S0]} -split_child(Name, Chs) -> - split_child(Name, Chs, []). - -split_child(Name, [Ch|Chs], After) when Ch#child.name =:= Name -> - {lists:reverse([Ch#child{pid = undefined} | After]), Chs}; -split_child(Pid, [Ch|Chs], After) when Ch#child.pid =:= Pid -> - {lists:reverse([Ch#child{pid = undefined} | After]), Chs}; -split_child(Name, [Ch|Chs], After) -> - split_child(Name, Chs, [Ch | After]); -split_child(_, [], After) -> - {lists:reverse(After), []}. - -get_child(Name, State) -> - get_child(Name, State, false). -get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) -> - get_dynamic_child(Pid, State); -get_child(Name, State, _) -> - lists:keysearch(Name, #child.name, State#state.children). - -get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) -> - DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics), - case is_dynamic_pid(Pid, DynamicsDb) of - true -> - {value, Child#child{pid=Pid}}; - false -> - RPid = restarting(Pid), - case is_dynamic_pid(RPid, DynamicsDb) of - true -> - {value, Child#child{pid=RPid}}; - false -> - case erlang:is_process_alive(Pid) of - true -> false; - false -> {value, Child} - end - end - end. - -is_dynamic_pid(Pid, Dynamics) -> - case ?SETS:is_set(Dynamics) of - true -> - ?SETS:is_element(Pid, Dynamics); - false -> - ?DICT:is_key(Pid, Dynamics) - end. - -replace_child(Child, State) -> - Chs = do_replace_child(Child, State#state.children), - State#state{children = Chs}. - -do_replace_child(Child, [Ch|Chs]) when Ch#child.name =:= Child#child.name -> - [Child | Chs]; -do_replace_child(Child, [Ch|Chs]) -> - [Ch|do_replace_child(Child, Chs)]. - -remove_child(Child, State) -> - Chs = lists:keydelete(Child#child.name, #child.name, State#state.children), - State#state{children = Chs}. - -%%----------------------------------------------------------------- -%% Func: init_state/4 -%% Args: SupName = {local, atom()} | {global, atom()} | self -%% Type = {Strategy, MaxIntensity, Period} -%% Strategy = one_for_one | one_for_all | simple_one_for_one | -%% rest_for_one -%% MaxIntensity = integer() >= 0 -%% Period = integer() > 0 -%% Mod :== atom() -%% Args :== term() -%% Purpose: Check that Type is of correct type (!) -%% Returns: {ok, state()} | Error -%%----------------------------------------------------------------- -init_state(SupName, Type, Mod, Args) -> - case catch init_state1(SupName, Type, Mod, Args) of - {ok, State} -> - {ok, State}; - Error -> - Error - end. - -init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) -> - validStrategy(Strategy), - validIntensity(MaxIntensity), - validPeriod(Period), - {ok, #state{name = supname(SupName,Mod), - strategy = Strategy, - intensity = MaxIntensity, - period = Period, - module = Mod, - args = Args}}; -init_state1(_SupName, Type, _, _) -> - {invalid_type, Type}. - -validStrategy(simple_one_for_one) -> true; -validStrategy(one_for_one) -> true; -validStrategy(one_for_all) -> true; -validStrategy(rest_for_one) -> true; -validStrategy(What) -> throw({invalid_strategy, What}). - -validIntensity(Max) when is_integer(Max), - Max >= 0 -> true; -validIntensity(What) -> throw({invalid_intensity, What}). - -validPeriod(Period) when is_integer(Period), - Period > 0 -> true; -validPeriod(What) -> throw({invalid_period, What}). - -supname(self, Mod) -> {self(), Mod}; -supname(N, _) -> N. - -%%% ------------------------------------------------------ -%%% Check that the children start specification is valid. -%%% Shall be a six (6) tuple -%%% {Name, Func, RestartType, Shutdown, ChildType, Modules} -%%% where Name is an atom -%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()} -%%% RestartType is permanent | temporary | transient | -%%% intrinsic | {permanent, Delay} | -%%% {transient, Delay} | {intrinsic, Delay} -%% where Delay >= 0 -%%% Shutdown = integer() > 0 | infinity | brutal_kill -%%% ChildType = supervisor | worker -%%% Modules = [atom()] | dynamic -%%% Returns: {ok, [child_rec()]} | Error -%%% ------------------------------------------------------ - -check_startspec(Children) -> check_startspec(Children, []). - -check_startspec([ChildSpec|T], Res) -> - case check_childspec(ChildSpec) of - {ok, Child} -> - case lists:keymember(Child#child.name, #child.name, Res) of - true -> {duplicate_child_name, Child#child.name}; - false -> check_startspec(T, [Child | Res]) - end; - Error -> Error - end; -check_startspec([], Res) -> - {ok, lists:reverse(Res)}. - -check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) -> - catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods); -check_childspec(X) -> {invalid_child_spec, X}. - -check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) -> - validName(Name), - validFunc(Func), - validRestartType(RestartType), - validChildType(ChildType), - validShutdown(Shutdown, ChildType), - validMods(Mods), - {ok, #child{name = Name, mfargs = Func, restart_type = RestartType, - shutdown = Shutdown, child_type = ChildType, modules = Mods}}. - -validChildType(supervisor) -> true; -validChildType(worker) -> true; -validChildType(What) -> throw({invalid_child_type, What}). - -validName(_Name) -> true. - -validFunc({M, F, A}) when is_atom(M), - is_atom(F), - is_list(A) -> true; -validFunc(Func) -> throw({invalid_mfa, Func}). - -validRestartType(permanent) -> true; -validRestartType(temporary) -> true; -validRestartType(transient) -> true; -validRestartType(intrinsic) -> true; -validRestartType({permanent, Delay}) -> validDelay(Delay); -validRestartType({intrinsic, Delay}) -> validDelay(Delay); -validRestartType({transient, Delay}) -> validDelay(Delay); -validRestartType(RestartType) -> throw({invalid_restart_type, - RestartType}). - -validDelay(Delay) when is_number(Delay), - Delay >= 0 -> true; -validDelay(What) -> throw({invalid_delay, What}). - -validShutdown(Shutdown, _) - when is_integer(Shutdown), Shutdown > 0 -> true; -validShutdown(infinity, _) -> true; -validShutdown(brutal_kill, _) -> true; -validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}). - -validMods(dynamic) -> true; -validMods(Mods) when is_list(Mods) -> - lists:foreach(fun(Mod) -> - if - is_atom(Mod) -> ok; - true -> throw({invalid_module, Mod}) - end - end, - Mods); -validMods(Mods) -> throw({invalid_modules, Mods}). - -%%% ------------------------------------------------------ -%%% Add a new restart and calculate if the max restart -%%% intensity has been reached (in that case the supervisor -%%% shall terminate). -%%% All restarts accured inside the period amount of seconds -%%% are kept in the #state.restarts list. -%%% Returns: {ok, State'} | {terminate, State'} -%%% ------------------------------------------------------ - -add_restart(State) -> - I = State#state.intensity, - P = State#state.period, - R = State#state.restarts, - Now = time_compat:monotonic_time(), - R1 = add_restart([Now|R], Now, P), - State1 = State#state{restarts = R1}, - case length(R1) of - CurI when CurI =< I -> - {ok, State1}; - _ -> - {terminate, State1} - end. - -add_restart([R|Restarts], Now, Period) -> - case inPeriod(R, Now, Period) of - true -> - [R|add_restart(Restarts, Now, Period)]; - _ -> - [] - end; -add_restart([], _, _) -> - []. - -inPeriod(Time, Now, Period) -> - case time_compat:convert_time_unit(Now - Time, native, seconds) of - T when T > Period -> - false; - _ -> - true - end. - -%%% ------------------------------------------------------ -%%% Error and progress reporting. -%%% ------------------------------------------------------ - -report_error(Error, Reason, Child, SupName) -> - ErrorMsg = [{supervisor, SupName}, - {errorContext, Error}, - {reason, Reason}, - {offender, extract_child(Child)}], - error_logger:error_report(supervisor_report, ErrorMsg). - - -extract_child(Child) when is_list(Child#child.pid) -> - [{nb_children, length(Child#child.pid)}, - {name, Child#child.name}, - {mfargs, Child#child.mfargs}, - {restart_type, Child#child.restart_type}, - {shutdown, Child#child.shutdown}, - {child_type, Child#child.child_type}]; -extract_child(Child) -> - [{pid, Child#child.pid}, - {name, Child#child.name}, - {mfargs, Child#child.mfargs}, - {restart_type, Child#child.restart_type}, - {shutdown, Child#child.shutdown}, - {child_type, Child#child.child_type}]. - -report_progress(Child, SupName) -> - Progress = [{supervisor, SupName}, - {started, extract_child(Child)}], - error_logger:info_report(progress, Progress). diff --git a/src/time_compat.erl b/src/time_compat.erl deleted file mode 100644 index b87c6cc550..0000000000 --- a/src/time_compat.erl +++ /dev/null @@ -1,305 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2014-2015. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%% If your code need to be able to execute on ERTS versions both -%% earlier and later than 7.0, the best approach is to use the new -%% time API introduced in ERTS 7.0 and implement a fallback -%% solution using the old primitives to be used on old ERTS -%% versions. This way your code can automatically take advantage -%% of the improvements in the API when available. This is an -%% example of how to implement such an API, but it can be used -%% as is if you want to. Just add (a preferrably renamed version of) -%% this module to your project, and call the API via this module -%% instead of calling the BIFs directly. -%% - --module(time_compat). - -%% We don't want warnings about the use of erlang:now/0 in -%% this module. --compile(nowarn_deprecated_function). -%% -%% We don't use -%% -compile({nowarn_deprecated_function, [{erlang, now, 0}]}). -%% since this will produce warnings when compiled on systems -%% where it has not yet been deprecated. -%% - --export([monotonic_time/0, - monotonic_time/1, - erlang_system_time/0, - erlang_system_time/1, - os_system_time/0, - os_system_time/1, - time_offset/0, - time_offset/1, - convert_time_unit/3, - timestamp/0, - unique_integer/0, - unique_integer/1, - monitor/2, - system_info/1, - system_flag/2]). - -monotonic_time() -> - try - erlang:monotonic_time() - catch - error:undef -> - %% Use Erlang system time as monotonic time - erlang_system_time_fallback() - end. - -monotonic_time(Unit) -> - try - erlang:monotonic_time(Unit) - catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - %% Use Erlang system time as monotonic time - STime = erlang_system_time_fallback(), - try - convert_time_unit_fallback(STime, native, Unit) - catch - error:bad_time_unit -> erlang:error(badarg, [Unit]) - end - end. - -erlang_system_time() -> - try - erlang:system_time() - catch - error:undef -> - erlang_system_time_fallback() - end. - -erlang_system_time(Unit) -> - try - erlang:system_time(Unit) - catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - STime = erlang_system_time_fallback(), - try - convert_time_unit_fallback(STime, native, Unit) - catch - error:bad_time_unit -> erlang:error(badarg, [Unit]) - end - end. - -os_system_time() -> - try - os:system_time() - catch - error:undef -> - os_system_time_fallback() - end. - -os_system_time(Unit) -> - try - os:system_time(Unit) - catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - STime = os_system_time_fallback(), - try - convert_time_unit_fallback(STime, native, Unit) - catch - error:bad_time_unit -> erlang:error(badarg, [Unit]) - end - end. - -time_offset() -> - try - erlang:time_offset() - catch - error:undef -> - %% Erlang system time and Erlang monotonic - %% time are always aligned - 0 - end. - -time_offset(Unit) -> - try - erlang:time_offset(Unit) - catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - try - _ = integer_time_unit(Unit) - catch - error:bad_time_unit -> erlang:error(badarg, [Unit]) - end, - %% Erlang system time and Erlang monotonic - %% time are always aligned - 0 - end. - -convert_time_unit(Time, FromUnit, ToUnit) -> - try - erlang:convert_time_unit(Time, FromUnit, ToUnit) - catch - error:undef -> - try - convert_time_unit_fallback(Time, FromUnit, ToUnit) - catch - _:_ -> - erlang:error(badarg, [Time, FromUnit, ToUnit]) - end; - error:Error -> - erlang:error(Error, [Time, FromUnit, ToUnit]) - end. - -timestamp() -> - try - erlang:timestamp() - catch - error:undef -> - erlang:now() - end. - -unique_integer() -> - try - erlang:unique_integer() - catch - error:undef -> - {MS, S, US} = erlang:now(), - (MS*1000000+S)*1000000+US - end. - -unique_integer(Modifiers) -> - try - erlang:unique_integer(Modifiers) - catch - error:badarg -> - erlang:error(badarg, [Modifiers]); - error:undef -> - case is_valid_modifier_list(Modifiers) of - true -> - %% now() converted to an integer - %% fullfill the requirements of - %% all modifiers: unique, positive, - %% and monotonic... - {MS, S, US} = erlang:now(), - (MS*1000000+S)*1000000+US; - false -> - erlang:error(badarg, [Modifiers]) - end - end. - -monitor(Type, Item) -> - try - erlang:monitor(Type, Item) - catch - error:Error -> - case {Error, Type, Item} of - {badarg, time_offset, clock_service} -> - %% Time offset is final and will never change. - %% Return a dummy reference, there will never - %% be any need for 'CHANGE' messages... - make_ref(); - _ -> - erlang:error(Error, [Type, Item]) - end - end. - -system_info(Item) -> - try - erlang:system_info(Item) - catch - error:badarg -> - case Item of - time_correction -> - case erlang:system_info(tolerant_timeofday) of - enabled -> true; - disabled -> false - end; - time_warp_mode -> - no_time_warp; - time_offset -> - final; - NotSupArg when NotSupArg == os_monotonic_time_source; - NotSupArg == os_system_time_source; - NotSupArg == start_time; - NotSupArg == end_time -> - %% Cannot emulate this... - erlang:error(notsup, [NotSupArg]); - _ -> - erlang:error(badarg, [Item]) - end; - error:Error -> - erlang:error(Error, [Item]) - end. - -system_flag(Flag, Value) -> - try - erlang:system_flag(Flag, Value) - catch - error:Error -> - case {Error, Flag, Value} of - {badarg, time_offset, finalize} -> - %% Time offset is final - final; - _ -> - erlang:error(Error, [Flag, Value]) - end - end. - -%% -%% Internal functions -%% - -integer_time_unit(native) -> 1000*1000; -integer_time_unit(nano_seconds) -> 1000*1000*1000; -integer_time_unit(micro_seconds) -> 1000*1000; -integer_time_unit(milli_seconds) -> 1000; -integer_time_unit(seconds) -> 1; -integer_time_unit(I) when is_integer(I), I > 0 -> I; -integer_time_unit(BadRes) -> erlang:error(bad_time_unit, [BadRes]). - -erlang_system_time_fallback() -> - {MS, S, US} = erlang:now(), - (MS*1000000+S)*1000000+US. - -os_system_time_fallback() -> - {MS, S, US} = os:timestamp(), - (MS*1000000+S)*1000000+US. - -convert_time_unit_fallback(Time, FromUnit, ToUnit) -> - FU = integer_time_unit(FromUnit), - TU = integer_time_unit(ToUnit), - case Time < 0 of - true -> TU*Time - (FU - 1); - false -> TU*Time - end div FU. - -is_valid_modifier_list([positive|Ms]) -> - is_valid_modifier_list(Ms); -is_valid_modifier_list([monotonic|Ms]) -> - is_valid_modifier_list(Ms); -is_valid_modifier_list([]) -> - true; -is_valid_modifier_list(_) -> - false. diff --git a/test/src/credit_flow_test.erl b/test/src/credit_flow_test.erl deleted file mode 100644 index 4910da3db3..0000000000 --- a/test/src/credit_flow_test.erl +++ /dev/null @@ -1,50 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(credit_flow_test). - --export([test_credit_flow_settings/0]). - -test_credit_flow_settings() -> - %% default values - passed = test_proc(200, 50), - - application:set_env(rabbit, credit_flow_default_credit, {100, 20}), - passed = test_proc(100, 20), - - application:unset_env(rabbit, credit_flow_default_credit), - - % back to defaults - passed = test_proc(200, 50), - passed. - -test_proc(InitialCredit, MoreCreditAfter) -> - Pid = spawn(fun dummy/0), - Pid ! {credit, self()}, - {InitialCredit, MoreCreditAfter} = - receive - {credit, Val} -> Val - end, - passed. - -dummy() -> - credit_flow:send(self()), - receive - {credit, From} -> - From ! {credit, get(credit_flow_default_credit)}; - _ -> - dummy() - end. diff --git a/test/src/gm_qc.erl b/test/src/gm_qc.erl deleted file mode 100644 index ce6bb40975..0000000000 --- a/test/src/gm_qc.erl +++ /dev/null @@ -1,384 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(gm_qc). --ifdef(use_proper_qc). - --include_lib("proper/include/proper.hrl"). - --define(GROUP, test_group). --define(MAX_SIZE, 5). --define(MSG_TIMEOUT, 1000000). %% micros - --export([prop_gm_test/0]). - --behaviour(proper_statem). --export([initial_state/0, command/1, precondition/2, postcondition/3, - next_state/3]). - --behaviour(gm). --export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). - -%% Helpers --export([do_join/0, do_leave/1, do_send/1, do_proceed1/1, do_proceed2/2]). - -%% For insertion into gm --export([call/3, cast/2, monitor/1, demonitor/1, execute_mnesia_transaction/1]). - --record(state, {seq, %% symbolic and dynamic - instrumented, %% dynamic only - outstanding, %% dynamic only - monitors, %% dynamic only - all_join, %% for symbolic - to_join, %% dynamic only - to_leave %% for symbolic - }). - -prop_gm_test() -> - case ?INSTR_MOD of - ?MODULE -> ok; - _ -> exit(compile_with_INSTRUMENT_FOR_QC) - end, - process_flag(trap_exit, true), - erlang:register(?MODULE, self()), - ?FORALL(Cmds, commands(?MODULE), gm_test(Cmds)). - -gm_test(Cmds) -> - {_H, State, Res} = run_commands(?MODULE, Cmds), - cleanup(State), - ?WHENFAIL( - io:format("Result: ~p~n", [Res]), - aggregate(command_names(Cmds), Res =:= ok)). - -cleanup(S) -> - S2 = ensure_joiners_joined_and_msgs_received(S), - All = gms_joined(S2), - All = gms(S2), %% assertion - none to join - check_stale_members(All), - [gm:leave(GM) || GM <- All], - drain_and_proceed_gms(S2), - [await_death(GM) || GM <- All], - gm:forget_group(?GROUP), - ok. - -check_stale_members(All) -> - GMs = [P || P <- processes(), is_gm_process(?GROUP, P)], - case GMs -- All of - [] -> ok; - Rest -> exit({forgot, Rest}) - end. - -is_gm_process(Group, P) -> - case process_info(P, dictionary) of - undefined -> false; - {dictionary, D} -> {gm, Group} =:= proplists:get_value(process_name, D) - end. - -await_death(P) -> - MRef = erlang:monitor(process, P), - await_death(MRef, P). - -await_death(MRef, P) -> - receive - {'DOWN', MRef, process, P, _} -> ok; - {'DOWN', _, _, _, _} -> await_death(MRef, P); - {'EXIT', _, normal} -> await_death(MRef, P); - {'EXIT', _, Reason} -> exit(Reason); - {joined, _GM} -> await_death(MRef, P); - {left, _GM} -> await_death(MRef, P); - Anything -> exit({stray_msg, Anything}) - end. - -%% --------------------------------------------------------------------------- -%% proper_statem -%% --------------------------------------------------------------------------- - -initial_state() -> #state{seq = 1, - outstanding = dict:new(), - instrumented = dict:new(), - monitors = dict:new(), - all_join = sets:new(), - to_join = sets:new(), - to_leave = sets:new()}. - -command(S) -> - case {length(gms_symb_not_left(S)), length(gms_symb(S))} of - {0, 0} -> qc_join(S); - {0, _} -> frequency([{1, qc_join(S)}, - {3, qc_proceed1(S)}, - {5, qc_proceed2(S)}]); - _ -> frequency([{1, qc_join(S)}, - {1, qc_leave(S)}, - {10, qc_send(S)}, - {5, qc_proceed1(S)}, - {15, qc_proceed2(S)}]) - end. - -qc_join(_S) -> {call,?MODULE,do_join, []}. -qc_leave(S) -> {call,?MODULE,do_leave,[oneof(gms_symb_not_left(S))]}. -qc_send(S) -> {call,?MODULE,do_send, [oneof(gms_symb_not_left(S))]}. -qc_proceed1(S) -> {call,?MODULE,do_proceed1, [oneof(gms_symb(S))]}. -qc_proceed2(S) -> {call,?MODULE,do_proceed2, [oneof(gms_symb(S)), - oneof(gms_symb(S))]}. - -precondition(S, {call, ?MODULE, do_join, []}) -> - length(gms_symb(S)) < ?MAX_SIZE; - -precondition(_S, {call, ?MODULE, do_leave, [_GM]}) -> - true; - -precondition(_S, {call, ?MODULE, do_send, [_GM]}) -> - true; - -precondition(_S, {call, ?MODULE, do_proceed1, [_GM]}) -> - true; - -precondition(_S, {call, ?MODULE, do_proceed2, [GM1, GM2]}) -> - GM1 =/= GM2. - -postcondition(_S, {call, _M, _F, _A}, _Res) -> - true. - -next_state(S = #state{to_join = ToSet, - all_join = AllSet}, GM, {call, ?MODULE, do_join, []}) -> - S#state{to_join = sets:add_element(GM, ToSet), - all_join = sets:add_element(GM, AllSet)}; - -next_state(S = #state{to_leave = Set}, _Res, {call, ?MODULE, do_leave, [GM]}) -> - S#state{to_leave = sets:add_element(GM, Set)}; - -next_state(S = #state{seq = Seq, - outstanding = Outstanding}, _Res, - {call, ?MODULE, do_send, [GM]}) -> - case is_pid(GM) andalso lists:member(GM, gms_joined(S)) of - true -> - %% Dynamic state, i.e. runtime - Msg = [{sequence, Seq}, - {sent_to, GM}, - {dests, gms_joined(S)}], - gm:broadcast(GM, Msg), - Outstanding1 = dict:map( - fun (_GM, Set) -> - gb_sets:add_element(Msg, Set) - end, Outstanding), - drain(S#state{seq = Seq + 1, - outstanding = Outstanding1}); - false -> - S - end; - -next_state(S, _Res, {call, ?MODULE, do_proceed1, [Pid]}) -> - proceed(Pid, S); - -next_state(S, _Res, {call, ?MODULE, do_proceed2, [From, To]}) -> - proceed({From, To}, S). - -proceed(K, S = #state{instrumented = Msgs}) -> - case dict:find(K, Msgs) of - {ok, Q} -> case queue:out(Q) of - {{value, Thing}, Q2} -> - S2 = proceed(K, Thing, S), - S2#state{instrumented = dict:store(K, Q2, Msgs)}; - {empty, _} -> - S - end; - error -> S - end. - -%% --------------------------------------------------------------------------- -%% GM -%% --------------------------------------------------------------------------- - -joined(Pid, _Members) -> Pid ! {joined, self()}, - ok. -members_changed(_Pid, _Bs, _Ds) -> ok. -handle_msg(Pid, _From, Msg) -> Pid ! {gm, self(), Msg}, ok. -handle_terminate(Pid, _Reason) -> Pid ! {left, self()}. - -%% --------------------------------------------------------------------------- -%% Helpers -%% --------------------------------------------------------------------------- - -do_join() -> - {ok, GM} = gm:start_link(?GROUP, ?MODULE, self(), - fun execute_mnesia_transaction/1), - GM. - -do_leave(GM) -> - gm:leave(GM), - GM. - -%% We need to update the state, so do the work in next_state -do_send( _GM) -> ok. -do_proceed1(_Pid) -> ok. -do_proceed2(_From, _To) -> ok. - -%% All GMs, joined and to join -gms(#state{outstanding = Outstanding, - to_join = ToJoin}) -> - dict:fetch_keys(Outstanding) ++ sets:to_list(ToJoin). - -%% All GMs, joined and to join -gms_joined(#state{outstanding = Outstanding}) -> - dict:fetch_keys(Outstanding). - -%% All GMs including those that have left (symbolic) -gms_symb(#state{all_join = AllJoin}) -> - sets:to_list(AllJoin). - -%% All GMs not including those that have left (symbolic) -gms_symb_not_left(#state{all_join = AllJoin, - to_leave = ToLeave}) -> - sets:to_list(sets:subtract(AllJoin, ToLeave)). - -drain(S) -> - receive - Msg -> drain(handle_msg(Msg, S)) - after 10 -> S - end. - -drain_and_proceed_gms(S0) -> - S = #state{instrumented = Msgs} = drain(S0), - case dict:size(Msgs) of - 0 -> S; - _ -> S1 = dict:fold( - fun (Key, Q, Si) -> - lists:foldl( - fun (Msg, Sij) -> - proceed(Key, Msg, Sij) - end, Si, queue:to_list(Q)) - end, S, Msgs), - drain_and_proceed_gms(S1#state{instrumented = dict:new()}) - end. - -handle_msg({gm, GM, Msg}, S = #state{outstanding = Outstanding}) -> - case dict:find(GM, Outstanding) of - {ok, Set} -> - Set2 = gb_sets:del_element(Msg, Set), - S#state{outstanding = dict:store(GM, Set2, Outstanding)}; - error -> - %% Message from GM that has already died. OK. - S - end; -handle_msg({instrumented, Key, Thing}, S = #state{instrumented = Msgs}) -> - Q1 = case dict:find(Key, Msgs) of - {ok, Q} -> queue:in(Thing, Q); - error -> queue:from_list([Thing]) - end, - S#state{instrumented = dict:store(Key, Q1, Msgs)}; -handle_msg({joined, GM}, S = #state{outstanding = Outstanding, - to_join = ToJoin}) -> - S#state{outstanding = dict:store(GM, gb_sets:empty(), Outstanding), - to_join = sets:del_element(GM, ToJoin)}; -handle_msg({left, GM}, S = #state{outstanding = Outstanding, - to_join = ToJoin}) -> - true = dict:is_key(GM, Outstanding) orelse sets:is_element(GM, ToJoin), - S#state{outstanding = dict:erase(GM, Outstanding), - to_join = sets:del_element(GM, ToJoin)}; -handle_msg({'DOWN', MRef, _, From, _} = Msg, S = #state{monitors = Mons}) -> - To = dict:fetch(MRef, Mons), - handle_msg({instrumented, {From, To}, {info, Msg}}, - S#state{monitors = dict:erase(MRef, Mons)}); -handle_msg({'EXIT', _From, normal}, S) -> - S; -handle_msg({'EXIT', _From, Reason}, _S) -> - %% We just trapped exits to get nicer SASL logging. - exit(Reason). - -proceed({_From, To}, {cast, Msg}, S) -> gen_server2:cast(To, Msg), S; -proceed({_From, To}, {info, Msg}, S) -> To ! Msg, S; -proceed({From, _To}, {wait, Ref}, S) -> From ! {proceed, Ref}, S; -proceed({From, To}, {mon, Ref}, S) -> add_monitor(From, To, Ref, S); -proceed(_Pid, {demon, MRef}, S) -> erlang:demonitor(MRef), S; -proceed(Pid, {wait, Ref}, S) -> Pid ! {proceed, Ref}, S. - -%% NB From here is To in handle_msg/DOWN above, since the msg is going -%% the other way -add_monitor(From, To, Ref, S = #state{monitors = Mons}) -> - MRef = erlang:monitor(process, To), - From ! {mref, Ref, MRef}, - S#state{monitors = dict:store(MRef, From, Mons)}. - -%% ---------------------------------------------------------------------------- -%% Assertions -%% ---------------------------------------------------------------------------- - -ensure_joiners_joined_and_msgs_received(S0) -> - S = drain_and_proceed_gms(S0), - case outstanding_joiners(S) of - true -> ensure_joiners_joined_and_msgs_received(S); - false -> case outstanding_msgs(S) of - [] -> S; - Out -> exit({outstanding_msgs, Out}) - end - end. - -outstanding_joiners(#state{to_join = ToJoin}) -> - sets:size(ToJoin) > 0. - -outstanding_msgs(#state{outstanding = Outstanding}) -> - dict:fold(fun (GM, Set, OS) -> - case gb_sets:is_empty(Set) of - true -> OS; - false -> [{GM, gb_sets:to_list(Set)} | OS] - end - end, [], Outstanding). - -%% --------------------------------------------------------------------------- -%% For insertion into GM -%% --------------------------------------------------------------------------- - -call(Pid, Msg, infinity) -> - Ref = make_ref(), - whereis(?MODULE) ! {instrumented, {self(), Pid}, {wait, Ref}}, - receive - {proceed, Ref} -> ok - end, - gen_server2:call(Pid, Msg, infinity). - -cast(Pid, Msg) -> - whereis(?MODULE) ! {instrumented, {self(), Pid}, {cast, Msg}}, - ok. - -monitor(Pid) -> - Ref = make_ref(), - whereis(?MODULE) ! {instrumented, {self(), Pid}, {mon, Ref}}, - receive - {mref, Ref, MRef} -> MRef - end. - -demonitor(MRef) -> - whereis(?MODULE) ! {instrumented, self(), {demon, MRef}}, - true. - -execute_mnesia_transaction(Fun) -> - Ref = make_ref(), - whereis(?MODULE) ! {instrumented, self(), {wait, Ref}}, - receive - {proceed, Ref} -> ok - end, - rabbit_misc:execute_mnesia_transaction(Fun). - --else. - --export([prop_disabled/0]). - -prop_disabled() -> - exit({compiled_without_proper, - "PropEr was not present during compilation of the test module. " - "Hence all tests are disabled."}). - --endif. diff --git a/test/src/gm_soak_test.erl b/test/src/gm_soak_test.erl deleted file mode 100644 index 64baa035b7..0000000000 --- a/test/src/gm_soak_test.erl +++ /dev/null @@ -1,136 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(gm_soak_test). - --export([test/0]). --export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). - --behaviour(gm). - --include("gm_specs.hrl"). - -%% --------------------------------------------------------------------------- -%% Soak test -%% --------------------------------------------------------------------------- - -get_state() -> - get(state). - -with_state(Fun) -> - put(state, Fun(get_state())). - -inc() -> - case 1 + get(count) of - 100000 -> Now = time_compat:monotonic_time(), - Start = put(ts, Now), - Diff = time_compat:convert_time_unit(Now - Start, - native, - micro_seconds), - Rate = 100000 / (Diff / 1000000), - io:format("~p seeing ~p msgs/sec~n", [self(), Rate]), - put(count, 0); - N -> put(count, N) - end. - -joined([], Members) -> - io:format("Joined ~p (~p members)~n", [self(), length(Members)]), - put(state, dict:from_list([{Member, empty} || Member <- Members])), - put(count, 0), - put(ts, time_compat:monotonic_time()), - ok. - -members_changed([], Births, Deaths) -> - with_state( - fun (State) -> - State1 = - lists:foldl( - fun (Born, StateN) -> - false = dict:is_key(Born, StateN), - dict:store(Born, empty, StateN) - end, State, Births), - lists:foldl( - fun (Died, StateN) -> - true = dict:is_key(Died, StateN), - dict:store(Died, died, StateN) - end, State1, Deaths) - end), - ok. - -handle_msg([], From, {test_msg, Num}) -> - inc(), - with_state( - fun (State) -> - ok = case dict:find(From, State) of - {ok, died} -> - exit({{from, From}, - {received_posthumous_delivery, Num}}); - {ok, empty} -> ok; - {ok, Num} -> ok; - {ok, Num1} when Num < Num1 -> - exit({{from, From}, - {duplicate_delivery_of, Num}, - {expecting, Num1}}); - {ok, Num1} -> - exit({{from, From}, - {received_early, Num}, - {expecting, Num1}}); - error -> - exit({{from, From}, - {received_premature_delivery, Num}}) - end, - dict:store(From, Num + 1, State) - end), - ok. - -handle_terminate([], Reason) -> - io:format("Left ~p (~p)~n", [self(), Reason]), - ok. - -spawn_member() -> - spawn_link( - fun () -> - random:seed(erlang:phash2([node()]), - time_compat:monotonic_time(), - time_compat:unique_integer()), - %% start up delay of no more than 10 seconds - timer:sleep(random:uniform(10000)), - {ok, Pid} = gm:start_link( - ?MODULE, ?MODULE, [], - fun rabbit_misc:execute_mnesia_transaction/1), - Start = random:uniform(10000), - send_loop(Pid, Start, Start + random:uniform(10000)), - gm:leave(Pid), - spawn_more() - end). - -spawn_more() -> - [spawn_member() || _ <- lists:seq(1, 4 - random:uniform(4))]. - -send_loop(_Pid, Target, Target) -> - ok; -send_loop(Pid, Count, Target) when Target > Count -> - case random:uniform(3) of - 3 -> gm:confirmed_broadcast(Pid, {test_msg, Count}); - _ -> gm:broadcast(Pid, {test_msg, Count}) - end, - timer:sleep(random:uniform(5) - 1), %% sleep up to 4 ms - send_loop(Pid, Count + 1, Target). - -test() -> - ok = gm:create_tables(), - spawn_member(), - spawn_member(). diff --git a/test/src/gm_speed_test.erl b/test/src/gm_speed_test.erl deleted file mode 100644 index b0693fc136..0000000000 --- a/test/src/gm_speed_test.erl +++ /dev/null @@ -1,85 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(gm_speed_test). - --export([test/3]). --export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). --export([wile_e_coyote/2]). - --behaviour(gm). - --include("gm_specs.hrl"). - -%% callbacks - -joined(Owner, _Members) -> - Owner ! joined, - ok. - -members_changed(_Owner, _Births, _Deaths) -> - ok. - -handle_msg(Owner, _From, ping) -> - Owner ! ping, - ok. - -handle_terminate(Owner, _Reason) -> - Owner ! terminated, - ok. - -%% other - -wile_e_coyote(Time, WriteUnit) -> - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), - receive joined -> ok end, - timer:sleep(1000), %% wait for all to join - timer:send_after(Time, stop), - Start = time_compat:monotonic_time(), - {Sent, Received} = loop(Pid, WriteUnit, 0, 0), - End = time_compat:monotonic_time(), - ok = gm:leave(Pid), - receive terminated -> ok end, - Elapsed = time_compat:convert_time_unit(End - Start, - native, - micro_seconds) / 1000000, - io:format("Sending rate: ~p msgs/sec~nReceiving rate: ~p msgs/sec~n~n", - [Sent/Elapsed, Received/Elapsed]), - ok. - -loop(Pid, WriteUnit, Sent, Received) -> - case read(Received) of - {stop, Received1} -> {Sent, Received1}; - {ok, Received1} -> ok = write(Pid, WriteUnit), - loop(Pid, WriteUnit, Sent + WriteUnit, Received1) - end. - -read(Count) -> - receive - ping -> read(Count + 1); - stop -> {stop, Count} - after 5 -> - {ok, Count} - end. - -write(_Pid, 0) -> ok; -write(Pid, N) -> ok = gm:broadcast(Pid, ping), - write(Pid, N - 1). - -test(Time, WriteUnit, Nodes) -> - ok = gm:create_tables(), - [spawn(Node, ?MODULE, wile_e_coyote, [Time, WriteUnit]) || Node <- Nodes]. diff --git a/test/src/gm_tests.erl b/test/src/gm_tests.erl deleted file mode 100644 index 8daac11125..0000000000 --- a/test/src/gm_tests.erl +++ /dev/null @@ -1,186 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(gm_tests). - --export([test_join_leave/0, - test_broadcast/0, - test_confirmed_broadcast/0, - test_member_death/0, - test_receive_in_order/0, - all_tests/0]). --export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). - --behaviour(gm). - --include("gm_specs.hrl"). - --define(RECEIVE_OR_THROW(Body, Bool, Error), - receive Body -> - true = Bool, - passed - after 1000 -> - throw(Error) - end). - -joined(Pid, Members) -> - Pid ! {joined, self(), Members}, - ok. - -members_changed(Pid, Births, Deaths) -> - Pid ! {members_changed, self(), Births, Deaths}, - ok. - -handle_msg(Pid, From, Msg) -> - Pid ! {msg, self(), From, Msg}, - ok. - -handle_terminate(Pid, Reason) -> - Pid ! {termination, self(), Reason}, - ok. - -%% --------------------------------------------------------------------------- -%% Functional tests -%% --------------------------------------------------------------------------- - -all_tests() -> - passed = test_join_leave(), - passed = test_broadcast(), - passed = test_confirmed_broadcast(), - passed = test_member_death(), - passed = test_receive_in_order(), - passed. - -test_join_leave() -> - with_two_members(fun (_Pid, _Pid2) -> passed end). - -test_broadcast() -> - test_broadcast(fun gm:broadcast/2). - -test_confirmed_broadcast() -> - test_broadcast(fun gm:confirmed_broadcast/2). - -test_member_death() -> - with_two_members( - fun (Pid, Pid2) -> - {ok, Pid3} = gm:start_link( - ?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), - passed = receive_joined(Pid3, [Pid, Pid2, Pid3], - timeout_joining_gm_group_3), - passed = receive_birth(Pid, Pid3, timeout_waiting_for_birth_3_1), - passed = receive_birth(Pid2, Pid3, timeout_waiting_for_birth_3_2), - - unlink(Pid3), - exit(Pid3, kill), - - %% Have to do some broadcasts to ensure that all members - %% find out about the death. - passed = (test_broadcast_fun(fun gm:confirmed_broadcast/2))( - Pid, Pid2), - - passed = receive_death(Pid, Pid3, timeout_waiting_for_death_3_1), - passed = receive_death(Pid2, Pid3, timeout_waiting_for_death_3_2), - - passed - end). - -test_receive_in_order() -> - with_two_members( - fun (Pid, Pid2) -> - Numbers = lists:seq(1,1000), - [begin ok = gm:broadcast(Pid, N), ok = gm:broadcast(Pid2, N) end - || N <- Numbers], - passed = receive_numbers( - Pid, Pid, {timeout_for_msgs, Pid, Pid}, Numbers), - passed = receive_numbers( - Pid, Pid2, {timeout_for_msgs, Pid, Pid2}, Numbers), - passed = receive_numbers( - Pid2, Pid, {timeout_for_msgs, Pid2, Pid}, Numbers), - passed = receive_numbers( - Pid2, Pid2, {timeout_for_msgs, Pid2, Pid2}, Numbers), - passed - end). - -test_broadcast(Fun) -> - with_two_members(test_broadcast_fun(Fun)). - -test_broadcast_fun(Fun) -> - fun (Pid, Pid2) -> - ok = Fun(Pid, magic_message), - passed = receive_or_throw({msg, Pid, Pid, magic_message}, - timeout_waiting_for_msg), - passed = receive_or_throw({msg, Pid2, Pid, magic_message}, - timeout_waiting_for_msg) - end. - -with_two_members(Fun) -> - ok = gm:create_tables(), - - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), - passed = receive_joined(Pid, [Pid], timeout_joining_gm_group_1), - - {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), - passed = receive_joined(Pid2, [Pid, Pid2], timeout_joining_gm_group_2), - passed = receive_birth(Pid, Pid2, timeout_waiting_for_birth_2), - - passed = Fun(Pid, Pid2), - - ok = gm:leave(Pid), - passed = receive_death(Pid2, Pid, timeout_waiting_for_death_1), - passed = - receive_termination(Pid, normal, timeout_waiting_for_termination_1), - - ok = gm:leave(Pid2), - passed = - receive_termination(Pid2, normal, timeout_waiting_for_termination_2), - - receive X -> throw({unexpected_message, X}) - after 0 -> passed - end. - -receive_or_throw(Pattern, Error) -> - ?RECEIVE_OR_THROW(Pattern, true, Error). - -receive_birth(From, Born, Error) -> - ?RECEIVE_OR_THROW({members_changed, From, Birth, Death}, - ([Born] == Birth) andalso ([] == Death), - Error). - -receive_death(From, Died, Error) -> - ?RECEIVE_OR_THROW({members_changed, From, Birth, Death}, - ([] == Birth) andalso ([Died] == Death), - Error). - -receive_joined(From, Members, Error) -> - ?RECEIVE_OR_THROW({joined, From, Members1}, - lists:usort(Members) == lists:usort(Members1), - Error). - -receive_termination(From, Reason, Error) -> - ?RECEIVE_OR_THROW({termination, From, Reason1}, - Reason == Reason1, - Error). - -receive_numbers(_Pid, _Sender, _Error, []) -> - passed; -receive_numbers(Pid, Sender, Error, [N | Numbers]) -> - ?RECEIVE_OR_THROW({msg, Pid, Sender, M}, - M == N, - Error), - receive_numbers(Pid, Sender, Error, Numbers). diff --git a/test/src/mirrored_supervisor_tests.erl b/test/src/mirrored_supervisor_tests.erl deleted file mode 100644 index 34411c2c62..0000000000 --- a/test/src/mirrored_supervisor_tests.erl +++ /dev/null @@ -1,307 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(mirrored_supervisor_tests). - --export([all_tests/0]). - --export([init/1]). - --behaviour(mirrored_supervisor). - --define(MS, mirrored_supervisor). --define(SERVER, mirrored_supervisor_tests_gs). - -%% --------------------------------------------------------------------------- -%% Functional tests -%% --------------------------------------------------------------------------- - -all_tests() -> - passed = test_migrate(), - passed = test_migrate_twice(), - passed = test_already_there(), - passed = test_delete_restart(), - passed = test_which_children(), - passed = test_large_group(), - passed = test_childspecs_at_init(), - passed = test_anonymous_supervisors(), - passed = test_no_migration_on_shutdown(), - passed = test_start_idempotence(), - passed = test_unsupported(), - passed = test_ignore(), - passed = test_startup_failure(), - passed. - -%% Simplest test -test_migrate() -> - with_sups(fun([A, _]) -> - ?MS:start_child(a, childspec(worker)), - Pid1 = pid_of(worker), - kill_registered(A, Pid1), - Pid2 = pid_of(worker), - false = (Pid1 =:= Pid2) - end, [a, b]). - -%% Is migration transitive? -test_migrate_twice() -> - with_sups(fun([A, B]) -> - ?MS:start_child(a, childspec(worker)), - Pid1 = pid_of(worker), - kill_registered(A, Pid1), - {ok, C} = start_sup(c), - Pid2 = pid_of(worker), - kill_registered(B, Pid2), - Pid3 = pid_of(worker), - false = (Pid1 =:= Pid3), - kill(C) - end, [a, b]). - -%% Can't start the same child twice -test_already_there() -> - with_sups(fun([_, _]) -> - S = childspec(worker), - {ok, Pid} = ?MS:start_child(a, S), - {error, {already_started, Pid}} = ?MS:start_child(b, S) - end, [a, b]). - -%% Deleting and restarting should work as per a normal supervisor -test_delete_restart() -> - with_sups(fun([_, _]) -> - S = childspec(worker), - {ok, Pid1} = ?MS:start_child(a, S), - {error, running} = ?MS:delete_child(a, worker), - ok = ?MS:terminate_child(a, worker), - ok = ?MS:delete_child(a, worker), - {ok, Pid2} = ?MS:start_child(b, S), - false = (Pid1 =:= Pid2), - ok = ?MS:terminate_child(b, worker), - {ok, Pid3} = ?MS:restart_child(b, worker), - Pid3 = pid_of(worker), - false = (Pid2 =:= Pid3), - %% Not the same supervisor as the worker is on - ok = ?MS:terminate_child(a, worker), - ok = ?MS:delete_child(a, worker), - {ok, Pid4} = ?MS:start_child(a, S), - false = (Pid3 =:= Pid4) - end, [a, b]). - -test_which_children() -> - with_sups( - fun([A, B] = Both) -> - ?MS:start_child(A, childspec(worker)), - assert_wc(Both, fun ([C]) -> true = is_pid(wc_pid(C)) end), - ok = ?MS:terminate_child(a, worker), - assert_wc(Both, fun ([C]) -> undefined = wc_pid(C) end), - {ok, _} = ?MS:restart_child(a, worker), - assert_wc(Both, fun ([C]) -> true = is_pid(wc_pid(C)) end), - ?MS:start_child(B, childspec(worker2)), - assert_wc(Both, fun (C) -> 2 = length(C) end) - end, [a, b]). - -assert_wc(Sups, Fun) -> - [Fun(?MS:which_children(Sup)) || Sup <- Sups]. - -wc_pid(Child) -> - {worker, Pid, worker, [mirrored_supervisor_tests]} = Child, - Pid. - -%% Not all the members of the group should actually do the failover -test_large_group() -> - with_sups(fun([A, _, _, _]) -> - ?MS:start_child(a, childspec(worker)), - Pid1 = pid_of(worker), - kill_registered(A, Pid1), - Pid2 = pid_of(worker), - false = (Pid1 =:= Pid2) - end, [a, b, c, d]). - -%% Do childspecs work when returned from init? -test_childspecs_at_init() -> - S = childspec(worker), - with_sups(fun([A, _]) -> - Pid1 = pid_of(worker), - kill_registered(A, Pid1), - Pid2 = pid_of(worker), - false = (Pid1 =:= Pid2) - end, [{a, [S]}, {b, [S]}]). - -test_anonymous_supervisors() -> - with_sups(fun([A, _B]) -> - ?MS:start_child(A, childspec(worker)), - Pid1 = pid_of(worker), - kill_registered(A, Pid1), - Pid2 = pid_of(worker), - false = (Pid1 =:= Pid2) - end, [anon, anon]). - -%% When a mirrored_supervisor terminates, we should not migrate, but -%% the whole supervisor group should shut down. To test this we set up -%% a situation where the gen_server will only fail if it's running -%% under the supervisor called 'evil'. It should not migrate to -%% 'good' and survive, rather the whole group should go away. -test_no_migration_on_shutdown() -> - with_sups(fun([Evil, _]) -> - ?MS:start_child(Evil, childspec(worker)), - try - call(worker, ping, 1000, 100), - exit(worker_should_not_have_migrated) - catch exit:{timeout_waiting_for_server, _, _} -> - ok - end - end, [evil, good]). - -test_start_idempotence() -> - with_sups(fun([_]) -> - CS = childspec(worker), - {ok, Pid} = ?MS:start_child(a, CS), - {error, {already_started, Pid}} = ?MS:start_child(a, CS), - ?MS:terminate_child(a, worker), - {error, already_present} = ?MS:start_child(a, CS) - end, [a]). - -test_unsupported() -> - try - ?MS:start_link({global, foo}, get_group(group), fun tx_fun/1, ?MODULE, - {one_for_one, []}), - exit(no_global) - catch error:badarg -> - ok - end, - try - ?MS:start_link({local, foo}, get_group(group), fun tx_fun/1, ?MODULE, - {simple_one_for_one, []}), - exit(no_sofo) - catch error:badarg -> - ok - end, - passed. - -%% Just test we don't blow up -test_ignore() -> - ?MS:start_link({local, foo}, get_group(group), fun tx_fun/1, ?MODULE, - {fake_strategy_for_ignore, []}), - passed. - -test_startup_failure() -> - [test_startup_failure(F) || F <- [want_error, want_exit]], - passed. - -test_startup_failure(Fail) -> - process_flag(trap_exit, true), - ?MS:start_link(get_group(group), fun tx_fun/1, ?MODULE, - {one_for_one, [childspec(Fail)]}), - receive - {'EXIT', _, shutdown} -> - ok - after 1000 -> - exit({did_not_exit, Fail}) - end, - process_flag(trap_exit, false). - -%% --------------------------------------------------------------------------- - -with_sups(Fun, Sups) -> - inc_group(), - Pids = [begin {ok, Pid} = start_sup(Sup), Pid end || Sup <- Sups], - Fun(Pids), - [kill(Pid) || Pid <- Pids, is_process_alive(Pid)], - timer:sleep(500), - passed. - -start_sup(Spec) -> - start_sup(Spec, group). - -start_sup({Name, ChildSpecs}, Group) -> - {ok, Pid} = start_sup0(Name, get_group(Group), ChildSpecs), - %% We are not a supervisor, when we kill the supervisor we do not - %% want to die! - unlink(Pid), - {ok, Pid}; - -start_sup(Name, Group) -> - start_sup({Name, []}, Group). - -start_sup0(anon, Group, ChildSpecs) -> - ?MS:start_link(Group, fun tx_fun/1, ?MODULE, - {one_for_one, ChildSpecs}); - -start_sup0(Name, Group, ChildSpecs) -> - ?MS:start_link({local, Name}, Group, fun tx_fun/1, ?MODULE, - {one_for_one, ChildSpecs}). - -childspec(Id) -> - {Id,{?SERVER, start_link, [Id]}, transient, 16#ffffffff, worker, [?MODULE]}. - -pid_of(Id) -> - {received, Pid, ping} = call(Id, ping), - Pid. - -tx_fun(Fun) -> - case mnesia:sync_transaction(Fun) of - {atomic, Result} -> Result; - {aborted, Reason} -> throw({error, Reason}) - end. - -inc_group() -> - Count = case get(counter) of - undefined -> 0; - C -> C - end + 1, - put(counter, Count). - -get_group(Group) -> - {Group, get(counter)}. - -call(Id, Msg) -> call(Id, Msg, 10*1000, 100). - -call(Id, Msg, 0, _Decr) -> - exit({timeout_waiting_for_server, {Id, Msg}, erlang:get_stacktrace()}); - -call(Id, Msg, MaxDelay, Decr) -> - try - gen_server:call(Id, Msg, infinity) - catch exit:_ -> timer:sleep(Decr), - call(Id, Msg, MaxDelay - Decr, Decr) - end. - -kill(Pid) -> kill(Pid, []). -kill(Pid, Wait) when is_pid(Wait) -> kill(Pid, [Wait]); -kill(Pid, Waits) -> - erlang:monitor(process, Pid), - [erlang:monitor(process, P) || P <- Waits], - exit(Pid, bang), - kill_wait(Pid), - [kill_wait(P) || P <- Waits]. - -kill_registered(Pid, Child) -> - {registered_name, Name} = erlang:process_info(Child, registered_name), - kill(Pid, Child), - false = (Child =:= whereis(Name)), - ok. - -kill_wait(Pid) -> - receive - {'DOWN', _Ref, process, Pid, _Reason} -> - ok - end. - -%% --------------------------------------------------------------------------- - -init({fake_strategy_for_ignore, _ChildSpecs}) -> - ignore; - -init({Strategy, ChildSpecs}) -> - {ok, {{Strategy, 0, 1}, ChildSpecs}}. diff --git a/test/src/mirrored_supervisor_tests_gs.erl b/test/src/mirrored_supervisor_tests_gs.erl deleted file mode 100644 index beaf49a44d..0000000000 --- a/test/src/mirrored_supervisor_tests_gs.erl +++ /dev/null @@ -1,66 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(mirrored_supervisor_tests_gs). - -%% Dumb gen_server we can supervise - --export([start_link/1]). - --export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3, - handle_cast/2]). - --behaviour(gen_server). - --define(MS, mirrored_supervisor). - -start_link(want_error) -> - {error, foo}; - -start_link(want_exit) -> - exit(foo); - -start_link(Id) -> - gen_server:start_link({local, Id}, ?MODULE, [], []). - -%% --------------------------------------------------------------------------- - -init([]) -> - {ok, state}. - -handle_call(Msg, _From, State) -> - die_if_my_supervisor_is_evil(), - {reply, {received, self(), Msg}, State}. - -handle_cast(_Msg, State) -> - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -die_if_my_supervisor_is_evil() -> - try lists:keysearch(self(), 2, ?MS:which_children(evil)) of - false -> ok; - _ -> exit(doooom) - catch - exit:{noproc, _} -> ok - end. diff --git a/test/src/on_disk_store_tunable_parameter_validation_test.erl b/test/src/on_disk_store_tunable_parameter_validation_test.erl deleted file mode 100644 index 9db5425e6d..0000000000 --- a/test/src/on_disk_store_tunable_parameter_validation_test.erl +++ /dev/null @@ -1,47 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(on_disk_store_tunable_parameter_validation_test). - --include("rabbit.hrl"). - --export([test_msg_store_parameter_validation/0]). - --define(T(Fun, Args), (catch apply(rabbit, Fun, Args))). - -test_msg_store_parameter_validation() -> - %% make sure it works with default values - ok = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [?CREDIT_DISC_BOUND, ?IO_BATCH_SIZE]), - - %% IO_BATCH_SIZE must be greater than CREDIT_DISC_BOUND initial credit - ok = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, 3000]), - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, 1500]), - - %% All values must be integers - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, "1500"]), - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{"2000", 500}, abc]), - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, "500"}, 2048]), - - %% CREDIT_DISC_BOUND must be a tuple - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [[2000, 500], 1500]), - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [2000, 1500]), - - %% config values can't be smaller than default values - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{1999, 500}, 2048]), - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 499}, 2048]), - {error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, 2047]), - - passed. diff --git a/test/src/password_hashing_tests.erl b/test/src/password_hashing_tests.erl deleted file mode 100644 index 030ff89058..0000000000 --- a/test/src/password_hashing_tests.erl +++ /dev/null @@ -1,54 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(password_hashing_tests). - --include("rabbit.hrl"). - --export([test_password_hashing/0]). - -test_password_hashing() -> - rabbit_password_hashing_sha256 = rabbit_password:hashing_mod(), - application:set_env(rabbit, password_hashing_module, rabbit_password_hashing_md5), - rabbit_password_hashing_md5 = rabbit_password:hashing_mod(), - application:set_env(rabbit, password_hashing_module, rabbit_password_hashing_sha256), - rabbit_password_hashing_sha256 = rabbit_password:hashing_mod(), - - rabbit_password_hashing_sha256 = rabbit_password:hashing_mod(rabbit_password_hashing_sha256), - rabbit_password_hashing_md5 = rabbit_password:hashing_mod(rabbit_password_hashing_md5), - rabbit_password_hashing_md5 = rabbit_password:hashing_mod(undefined), - - rabbit_password_hashing_md5 = - rabbit_auth_backend_internal:hashing_module_for_user( - #internal_user{}), - rabbit_password_hashing_md5 = - rabbit_auth_backend_internal:hashing_module_for_user( - #internal_user{ - hashing_algorithm = undefined - }), - rabbit_password_hashing_md5 = - rabbit_auth_backend_internal:hashing_module_for_user( - #internal_user{ - hashing_algorithm = rabbit_password_hashing_md5 - }), - - rabbit_password_hashing_sha256 = - rabbit_auth_backend_internal:hashing_module_for_user( - #internal_user{ - hashing_algorithm = rabbit_password_hashing_sha256 - }), - - passed. diff --git a/test/src/rabbit_backing_queue_qc.erl b/test/src/rabbit_backing_queue_qc.erl deleted file mode 100644 index 1469f61162..0000000000 --- a/test/src/rabbit_backing_queue_qc.erl +++ /dev/null @@ -1,473 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_backing_queue_qc). --ifdef(use_proper_qc). --include("rabbit.hrl"). --include("rabbit_framing.hrl"). --include_lib("proper/include/proper.hrl"). - --behaviour(proper_statem). - --define(BQMOD, rabbit_variable_queue). --define(QUEUE_MAXLEN, 10000). --define(TIMEOUT_LIMIT, 100). - --define(RECORD_INDEX(Key, Record), - proplists:get_value( - Key, lists:zip(record_info(fields, Record), - lists:seq(2, record_info(size, Record))))). - --export([initial_state/0, command/1, precondition/2, postcondition/3, - next_state/3]). - --export([prop_backing_queue_test/0, publish_multiple/1, - timeout/2, bump_credit/1]). - --record(state, {bqstate, - len, %% int - next_seq_id, %% int - messages, %% gb_trees of seqid => {msg_props, basic_msg} - acks, %% [{acktag, {seqid, {msg_props, basic_msg}}}] - confirms, %% set of msgid - publishing}).%% int - -%% Initialise model - -initial_state() -> - #state{bqstate = qc_variable_queue_init(qc_test_queue()), - len = 0, - next_seq_id = 0, - messages = gb_trees:empty(), - acks = [], - confirms = gb_sets:new(), - publishing = 0}. - -%% Property - -prop_backing_queue_test() -> - ?FORALL(Cmds, commands(?MODULE, initial_state()), - backing_queue_test(Cmds)). - -backing_queue_test(Cmds) -> - {ok, FileSizeLimit} = - application:get_env(rabbit, msg_store_file_size_limit), - application:set_env(rabbit, msg_store_file_size_limit, 512, - infinity), - {ok, MaxJournal} = - application:get_env(rabbit, queue_index_max_journal_entries), - application:set_env(rabbit, queue_index_max_journal_entries, 128, - infinity), - - {_H, #state{bqstate = BQ}, Res} = run_commands(?MODULE, Cmds), - - application:set_env(rabbit, msg_store_file_size_limit, - FileSizeLimit, infinity), - application:set_env(rabbit, queue_index_max_journal_entries, - MaxJournal, infinity), - - ?BQMOD:delete_and_terminate(shutdown, BQ), - ?WHENFAIL( - io:format("Result: ~p~n", [Res]), - aggregate(command_names(Cmds), Res =:= ok)). - -%% Commands - -%% Command frequencies are tuned so that queues are normally -%% reasonably short, but they may sometimes exceed -%% ?QUEUE_MAXLEN. Publish-multiple and purging cause extreme queue -%% lengths, so these have lower probabilities. Fetches/drops are -%% sufficiently frequent so that commands that need acktags get decent -%% coverage. - -command(S) -> - frequency([{10, qc_publish(S)}, - {1, qc_publish_delivered(S)}, - {1, qc_publish_multiple(S)}, %% very slow - {9, qc_fetch(S)}, %% needed for ack and requeue - {6, qc_drop(S)}, %% - {15, qc_ack(S)}, - {15, qc_requeue(S)}, - {3, qc_set_ram_duration_target(S)}, - {1, qc_ram_duration(S)}, - {1, qc_drain_confirmed(S)}, - {1, qc_dropwhile(S)}, - {1, qc_is_empty(S)}, - {1, qc_timeout(S)}, - {1, qc_bump_credit(S)}, - {1, qc_purge(S)}, - {1, qc_fold(S)}]). - -qc_publish(#state{bqstate = BQ}) -> - {call, ?BQMOD, publish, - [qc_message(), - #message_properties{needs_confirming = frequency([{1, true}, - {20, false}]), - expiry = oneof([undefined | lists:seq(1, 10)]), - size = 10}, - false, self(), noflow, BQ]}. - -qc_publish_multiple(#state{}) -> - {call, ?MODULE, publish_multiple, [resize(?QUEUE_MAXLEN, pos_integer())]}. - -qc_publish_delivered(#state{bqstate = BQ}) -> - {call, ?BQMOD, publish_delivered, - [qc_message(), #message_properties{size = 10}, self(), noflow, BQ]}. - -qc_fetch(#state{bqstate = BQ}) -> - {call, ?BQMOD, fetch, [boolean(), BQ]}. - -qc_drop(#state{bqstate = BQ}) -> - {call, ?BQMOD, drop, [boolean(), BQ]}. - -qc_ack(#state{bqstate = BQ, acks = Acks}) -> - {call, ?BQMOD, ack, [rand_choice(proplists:get_keys(Acks)), BQ]}. - -qc_requeue(#state{bqstate = BQ, acks = Acks}) -> - {call, ?BQMOD, requeue, [rand_choice(proplists:get_keys(Acks)), BQ]}. - -qc_set_ram_duration_target(#state{bqstate = BQ}) -> - {call, ?BQMOD, set_ram_duration_target, - [oneof([0, 1, 2, resize(1000, pos_integer()), infinity]), BQ]}. - -qc_ram_duration(#state{bqstate = BQ}) -> - {call, ?BQMOD, ram_duration, [BQ]}. - -qc_drain_confirmed(#state{bqstate = BQ}) -> - {call, ?BQMOD, drain_confirmed, [BQ]}. - -qc_dropwhile(#state{bqstate = BQ}) -> - {call, ?BQMOD, dropwhile, [fun dropfun/1, BQ]}. - -qc_is_empty(#state{bqstate = BQ}) -> - {call, ?BQMOD, is_empty, [BQ]}. - -qc_timeout(#state{bqstate = BQ}) -> - {call, ?MODULE, timeout, [BQ, ?TIMEOUT_LIMIT]}. - -qc_bump_credit(#state{bqstate = BQ}) -> - {call, ?MODULE, bump_credit, [BQ]}. - -qc_purge(#state{bqstate = BQ}) -> - {call, ?BQMOD, purge, [BQ]}. - -qc_fold(#state{bqstate = BQ}) -> - {call, ?BQMOD, fold, [makefoldfun(pos_integer()), foldacc(), BQ]}. - -%% Preconditions - -%% Create long queues by only allowing publishing -precondition(#state{publishing = Count}, {call, _Mod, Fun, _Arg}) - when Count > 0, Fun /= publish -> - false; -precondition(#state{acks = Acks}, {call, ?BQMOD, Fun, _Arg}) - when Fun =:= ack; Fun =:= requeue -> - length(Acks) > 0; -precondition(#state{messages = Messages}, - {call, ?BQMOD, publish_delivered, _Arg}) -> - gb_trees:is_empty(Messages); -precondition(_S, {call, ?BQMOD, _Fun, _Arg}) -> - true; -precondition(_S, {call, ?MODULE, timeout, _Arg}) -> - true; -precondition(_S, {call, ?MODULE, bump_credit, _Arg}) -> - true; -precondition(#state{len = Len}, {call, ?MODULE, publish_multiple, _Arg}) -> - Len < ?QUEUE_MAXLEN. - -%% Model updates - -next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Del, _Pid, _Flow, _BQ]}) -> - #state{len = Len, - messages = Messages, - confirms = Confirms, - publishing = PublishCount, - next_seq_id = NextSeq} = S, - MsgId = {call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]}, - NeedsConfirm = - {call, erlang, element, - [?RECORD_INDEX(needs_confirming, message_properties), MsgProps]}, - S#state{bqstate = BQ, - len = Len + 1, - next_seq_id = NextSeq + 1, - messages = gb_trees:insert(NextSeq, {MsgProps, Msg}, Messages), - publishing = {call, erlang, max, [0, {call, erlang, '-', - [PublishCount, 1]}]}, - confirms = case eval(NeedsConfirm) of - true -> gb_sets:add(MsgId, Confirms); - _ -> Confirms - end}; - -next_state(S, _BQ, {call, ?MODULE, publish_multiple, [PublishCount]}) -> - S#state{publishing = PublishCount}; - -next_state(S, Res, - {call, ?BQMOD, publish_delivered, - [Msg, MsgProps, _Pid, _Flow, _BQ]}) -> - #state{confirms = Confirms, acks = Acks, next_seq_id = NextSeq} = S, - AckTag = {call, erlang, element, [1, Res]}, - BQ1 = {call, erlang, element, [2, Res]}, - MsgId = {call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]}, - NeedsConfirm = - {call, erlang, element, - [?RECORD_INDEX(needs_confirming, message_properties), MsgProps]}, - S#state{bqstate = BQ1, - next_seq_id = NextSeq + 1, - confirms = case eval(NeedsConfirm) of - true -> gb_sets:add(MsgId, Confirms); - _ -> Confirms - end, - acks = [{AckTag, {NextSeq, {MsgProps, Msg}}}|Acks] - }; - -next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _BQ]}) -> - next_state_fetch_and_drop(S, Res, AckReq, 3); - -next_state(S, Res, {call, ?BQMOD, drop, [AckReq, _BQ]}) -> - next_state_fetch_and_drop(S, Res, AckReq, 2); - -next_state(S, Res, {call, ?BQMOD, ack, [AcksArg, _BQ]}) -> - #state{acks = AcksState} = S, - BQ1 = {call, erlang, element, [2, Res]}, - S#state{bqstate = BQ1, - acks = lists:foldl(fun proplists:delete/2, AcksState, AcksArg)}; - -next_state(S, Res, {call, ?BQMOD, requeue, [AcksArg, _V]}) -> - #state{messages = Messages, acks = AcksState} = S, - BQ1 = {call, erlang, element, [2, Res]}, - Messages1 = lists:foldl(fun (AckTag, Msgs) -> - {SeqId, MsgPropsMsg} = - proplists:get_value(AckTag, AcksState), - gb_trees:insert(SeqId, MsgPropsMsg, Msgs) - end, Messages, AcksArg), - S#state{bqstate = BQ1, - len = gb_trees:size(Messages1), - messages = Messages1, - acks = lists:foldl(fun proplists:delete/2, AcksState, AcksArg)}; - -next_state(S, BQ, {call, ?BQMOD, set_ram_duration_target, _Args}) -> - S#state{bqstate = BQ}; - -next_state(S, Res, {call, ?BQMOD, ram_duration, _Args}) -> - BQ1 = {call, erlang, element, [2, Res]}, - S#state{bqstate = BQ1}; - -next_state(S, Res, {call, ?BQMOD, drain_confirmed, _Args}) -> - BQ1 = {call, erlang, element, [2, Res]}, - S#state{bqstate = BQ1}; - -next_state(S, Res, {call, ?BQMOD, dropwhile, _Args}) -> - BQ = {call, erlang, element, [2, Res]}, - #state{messages = Messages} = S, - Msgs1 = drop_messages(Messages), - S#state{bqstate = BQ, len = gb_trees:size(Msgs1), messages = Msgs1}; - -next_state(S, _Res, {call, ?BQMOD, is_empty, _Args}) -> - S; - -next_state(S, BQ, {call, ?MODULE, timeout, _Args}) -> - S#state{bqstate = BQ}; -next_state(S, BQ, {call, ?MODULE, bump_credit, _Args}) -> - S#state{bqstate = BQ}; - -next_state(S, Res, {call, ?BQMOD, purge, _Args}) -> - BQ1 = {call, erlang, element, [2, Res]}, - S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()}; - -next_state(S, Res, {call, ?BQMOD, fold, _Args}) -> - BQ1 = {call, erlang, element, [2, Res]}, - S#state{bqstate = BQ1}. - -%% Postconditions - -postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) -> - #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, - case Res of - {{MsgFetched, _IsDelivered, AckTag}, _BQ} -> - {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), - MsgFetched =:= Msg andalso - not proplists:is_defined(AckTag, Acks) andalso - not gb_sets:is_element(AckTag, Confrms) andalso - Len =/= 0; - {empty, _BQ} -> - Len =:= 0 - end; - -postcondition(S, {call, ?BQMOD, drop, _Args}, Res) -> - #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, - case Res of - {{MsgIdFetched, AckTag}, _BQ} -> - {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), - MsgId = eval({call, erlang, element, - [?RECORD_INDEX(id, basic_message), Msg]}), - MsgIdFetched =:= MsgId andalso - not proplists:is_defined(AckTag, Acks) andalso - not gb_sets:is_element(AckTag, Confrms) andalso - Len =/= 0; - {empty, _BQ} -> - Len =:= 0 - end; - -postcondition(S, {call, ?BQMOD, publish_delivered, _Args}, {AckTag, _BQ}) -> - #state{acks = Acks, confirms = Confrms} = S, - not proplists:is_defined(AckTag, Acks) andalso - not gb_sets:is_element(AckTag, Confrms); - -postcondition(#state{len = Len}, {call, ?BQMOD, purge, _Args}, Res) -> - {PurgeCount, _BQ} = Res, - Len =:= PurgeCount; - -postcondition(#state{len = Len}, {call, ?BQMOD, is_empty, _Args}, Res) -> - (Len =:= 0) =:= Res; - -postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> - #state{confirms = Confirms} = S, - {ReportedConfirmed, _BQ} = Res, - lists:all(fun (M) -> gb_sets:is_element(M, Confirms) end, - ReportedConfirmed); - -postcondition(S, {call, ?BQMOD, fold, [FoldFun, Acc0, _BQ0]}, {Res, _BQ1}) -> - #state{messages = Messages} = S, - {_, Model} = lists:foldl(fun ({_SeqId, {_MsgProps, _Msg}}, {stop, Acc}) -> - {stop, Acc}; - ({_SeqId, {MsgProps, Msg}}, {cont, Acc}) -> - FoldFun(Msg, MsgProps, false, Acc) - end, {cont, Acc0}, gb_trees:to_list(Messages)), - true = Model =:= Res; - -postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> - ?BQMOD:len(BQ) =:= Len. - -%% Helpers - -publish_multiple(_C) -> - ok. - -timeout(BQ, 0) -> - BQ; -timeout(BQ, AtMost) -> - case ?BQMOD:needs_timeout(BQ) of - false -> BQ; - _ -> timeout(?BQMOD:timeout(BQ), AtMost - 1) - end. - -bump_credit(BQ) -> - case credit_flow:blocked() of - false -> BQ; - true -> receive - {bump_credit, Msg} -> - credit_flow:handle_bump_msg(Msg), - ?BQMOD:resume(BQ) - end - end. - -qc_message_payload() -> ?SIZED(Size, resize(Size * Size, binary())). - -qc_routing_key() -> noshrink(binary(10)). - -qc_delivery_mode() -> oneof([1, 2]). - -qc_message() -> qc_message(qc_delivery_mode()). - -qc_message(DeliveryMode) -> - {call, rabbit_basic, message, [qc_default_exchange(), - qc_routing_key(), - #'P_basic'{delivery_mode = DeliveryMode}, - qc_message_payload()]}. - -qc_default_exchange() -> - {call, rabbit_misc, r, [<<>>, exchange, <<>>]}. - -qc_variable_queue_init(Q) -> - {call, ?BQMOD, init, - [Q, new, function(2, {ok, []})]}. - -qc_test_q() -> {call, rabbit_misc, r, [<<"/">>, queue, noshrink(binary(16))]}. - -qc_test_queue() -> qc_test_queue(boolean()). - -qc_test_queue(Durable) -> - #amqqueue{name = qc_test_q(), - durable = Durable, - auto_delete = false, - arguments = [], - pid = self()}. - -rand_choice([]) -> []; -rand_choice(List) -> rand_choice(List, [], random:uniform(length(List))). - -rand_choice(_List, Selection, 0) -> - Selection; -rand_choice(List, Selection, N) -> - Picked = lists:nth(random:uniform(length(List)), List), - rand_choice(List -- [Picked], [Picked | Selection], - N - 1). - -makefoldfun(Size) -> - fun (Msg, _MsgProps, Unacked, Acc) -> - case {length(Acc) > Size, Unacked} of - {false, false} -> {cont, [Msg | Acc]}; - {false, true} -> {cont, Acc}; - {true, _} -> {stop, Acc} - end - end. -foldacc() -> []. - -dropfun(Props) -> - Expiry = eval({call, erlang, element, - [?RECORD_INDEX(expiry, message_properties), Props]}), - Expiry =/= 1. - -drop_messages(Messages) -> - case gb_trees:is_empty(Messages) of - true -> - Messages; - false -> {_Seq, MsgProps_Msg, M2} = gb_trees:take_smallest(Messages), - MsgProps = {call, erlang, element, [1, MsgProps_Msg]}, - case dropfun(MsgProps) of - true -> drop_messages(M2); - false -> Messages - end - end. - -next_state_fetch_and_drop(S, Res, AckReq, AckTagIdx) -> - #state{len = Len, messages = Messages, acks = Acks} = S, - ResultInfo = {call, erlang, element, [1, Res]}, - BQ1 = {call, erlang, element, [2, Res]}, - AckTag = {call, erlang, element, [AckTagIdx, ResultInfo]}, - S1 = S#state{bqstate = BQ1}, - case gb_trees:is_empty(Messages) of - true -> S1; - false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages), - S2 = S1#state{len = Len - 1, messages = M2}, - case AckReq of - true -> - S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]}; - false -> - S2 - end - end. - --else. - --export([prop_disabled/0]). - -prop_disabled() -> - exit({compiled_without_proper, - "PropEr was not present during compilation of the test module. " - "Hence all tests are disabled."}). - --endif. diff --git a/test/src/rabbit_runtime_parameters_test.erl b/test/src/rabbit_runtime_parameters_test.erl deleted file mode 100644 index d88975b61e..0000000000 --- a/test/src/rabbit_runtime_parameters_test.erl +++ /dev/null @@ -1,72 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_runtime_parameters_test). --behaviour(rabbit_runtime_parameter). --behaviour(rabbit_policy_validator). - --include("rabbit.hrl"). - --export([validate/5, notify/4, notify_clear/3]). --export([register/0, unregister/0]). --export([validate_policy/1]). --export([register_policy_validator/0, unregister_policy_validator/0]). - -%---------------------------------------------------------------------------- - -register() -> - rabbit_registry:register(runtime_parameter, <<"test">>, ?MODULE). - -unregister() -> - rabbit_registry:unregister(runtime_parameter, <<"test">>). - -validate(_, <<"test">>, <<"good">>, _Term, _User) -> ok; -validate(_, <<"test">>, <<"maybe">>, <<"good">>, _User) -> ok; -validate(_, <<"test">>, <<"admin">>, _Term, none) -> ok; -validate(_, <<"test">>, <<"admin">>, _Term, User) -> - case lists:member(administrator, User#user.tags) of - true -> ok; - false -> {error, "meh", []} - end; -validate(_, <<"test">>, _, _, _) -> {error, "meh", []}. - -notify(_, _, _, _) -> ok. -notify_clear(_, _, _) -> ok. - -%---------------------------------------------------------------------------- - -register_policy_validator() -> - rabbit_registry:register(policy_validator, <<"testeven">>, ?MODULE), - rabbit_registry:register(policy_validator, <<"testpos">>, ?MODULE). - -unregister_policy_validator() -> - rabbit_registry:unregister(policy_validator, <<"testeven">>), - rabbit_registry:unregister(policy_validator, <<"testpos">>). - -validate_policy([{<<"testeven">>, Terms}]) when is_list(Terms) -> - case length(Terms) rem 2 =:= 0 of - true -> ok; - false -> {error, "meh", []} - end; - -validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) -> - case lists:all(fun (N) -> is_integer(N) andalso N > 0 end, Terms) of - true -> ok; - false -> {error, "meh", []} - end; - -validate_policy(_) -> - {error, "meh", []}. diff --git a/test/src/rabbit_tests.erl b/test/src/rabbit_tests.erl deleted file mode 100644 index 44bf3da5f2..0000000000 --- a/test/src/rabbit_tests.erl +++ /dev/null @@ -1,3232 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% --module(rabbit_tests). - --compile([export_all]). - --export([all_tests/0]). - --import(rabbit_misc, [pget/2]). - --include("rabbit.hrl"). --include("rabbit_framing.hrl"). --include_lib("kernel/include/file.hrl"). - --define(PERSISTENT_MSG_STORE, msg_store_persistent). --define(TRANSIENT_MSG_STORE, msg_store_transient). --define(CLEANUP_QUEUE_NAME, <<"cleanup-queue">>). --define(TIMEOUT, 30000). - -all_tests() -> - try - all_tests0() - catch - Type:Error -> - rabbit_misc:format( - "Tests failed~nError: {~p, ~p}~nStack trace:~n~p~n", - [Type, Error, erlang:get_stacktrace()]) - end. - -all_tests0() -> - ok = setup_cluster(), - ok = truncate:test(), - ok = supervisor2_tests:test_all(), - passed = gm_tests:all_tests(), - passed = mirrored_supervisor_tests:all_tests(), - application:set_env(rabbit, file_handles_high_watermark, 10), - ok = file_handle_cache:set_limit(10), - passed = test_version_equivalance(), - passed = test_file_handle_cache(), - passed = test_backing_queue(), - passed = test_rabbit_basic_header_handling(), - passed = test_priority_queue(), - passed = test_pg_local(), - passed = test_unfold(), - passed = test_pmerge(), - passed = test_plmerge(), - passed = test_supervisor_delayed_restart(), - passed = test_table_codec(), - passed = test_content_framing(), - passed = test_content_transcoding(), - passed = test_topic_matching(), - passed = test_log_management(), - passed = test_app_management(), - passed = test_log_management_during_startup(), - passed = test_ch_statistics(), - passed = test_head_message_timestamp_statistic(), - passed = test_arguments_parser(), - passed = test_dynamic_mirroring(), - passed = test_user_management(), - passed = test_runtime_parameters(), - passed = test_policy_validation(), - passed = test_policy_opts_validation(), - passed = test_ha_policy_validation(), - passed = test_queue_master_location_policy_validation(), - passed = test_server_status(), - passed = test_amqp_connection_refusal(), - passed = test_confirms(), - passed = test_with_state(), - passed = test_mcall(), - passed = - do_if_secondary_node( - fun run_cluster_dependent_tests/1, - fun (SecondaryNode) -> - io:format("Skipping cluster dependent tests with node ~p~n", - [SecondaryNode]), - passed - end), - passed = test_configurable_server_properties(), - passed = vm_memory_monitor_tests:all_tests(), - passed = credit_flow_test:test_credit_flow_settings(), - passed = test_memory_high_watermark(), - passed = on_disk_store_tunable_parameter_validation_test:test_msg_store_parameter_validation(), - passed = password_hashing_tests:test_password_hashing(), - passed = credit_flow_test:test_credit_flow_settings(), - - passed. - -do_if_secondary_node(Up, Down) -> - SecondaryNode = rabbit_nodes:make("hare"), - - case net_adm:ping(SecondaryNode) of - pong -> Up(SecondaryNode); - pang -> Down(SecondaryNode) - end. - -setup_cluster() -> - do_if_secondary_node( - fun (SecondaryNode) -> - ok = control_action(stop_app, []), - ok = control_action(join_cluster, - [atom_to_list(SecondaryNode)]), - ok = control_action(start_app, []), - ok = control_action(start_app, SecondaryNode, [], []) - end, - fun (_) -> ok end). - -maybe_run_cluster_dependent_tests() -> - do_if_secondary_node( - fun (SecondaryNode) -> - passed = run_cluster_dependent_tests(SecondaryNode) - end, - fun (SecondaryNode) -> - io:format("Skipping cluster dependent tests with node ~p~n", - [SecondaryNode]) - end). - -run_cluster_dependent_tests(SecondaryNode) -> - io:format("Running cluster dependent tests with node ~p~n", [SecondaryNode]), - passed = test_delegates_async(SecondaryNode), - passed = test_delegates_sync(SecondaryNode), - passed = test_queue_cleanup(SecondaryNode), - passed = test_declare_on_dead_queue(SecondaryNode), - passed = test_refresh_events(SecondaryNode), - - %% we now run the tests remotely, so that code coverage on the - %% local node picks up more of the delegate - Node = node(), - Self = self(), - Remote = spawn(SecondaryNode, - fun () -> Rs = [ test_delegates_async(Node), - test_delegates_sync(Node), - test_queue_cleanup(Node), - test_declare_on_dead_queue(Node), - test_refresh_events(Node) ], - Self ! {self(), Rs} - end), - receive - {Remote, Result} -> - Result = lists:duplicate(length(Result), passed) - after 30000 -> - throw(timeout) - end, - - passed. - -test_version_equivalance() -> - true = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.0"), - true = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.1"), - true = rabbit_misc:version_minor_equivalent("%%VSN%%", "%%VSN%%"), - false = rabbit_misc:version_minor_equivalent("3.0.0", "3.1.0"), - false = rabbit_misc:version_minor_equivalent("3.0.0", "3.0"), - false = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.0.1"), - false = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.foo"), - passed. - -test_rabbit_basic_header_handling() -> - passed = write_table_with_invalid_existing_type_test(), - passed = invalid_existing_headers_test(), - passed = disparate_invalid_header_entries_accumulate_separately_test(), - passed = corrupt_or_invalid_headers_are_overwritten_test(), - passed = invalid_same_header_entry_accumulation_test(), - passed. - --define(XDEATH_TABLE, - [{<<"reason">>, longstr, <<"blah">>}, - {<<"queue">>, longstr, <<"foo.bar.baz">>}, - {<<"exchange">>, longstr, <<"my-exchange">>}, - {<<"routing-keys">>, array, []}]). - --define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). - --define(BAD_HEADER(K), {<<K>>, longstr, <<"bad ", K>>}). --define(BAD_HEADER2(K, Suf), {<<K>>, longstr, <<"bad ", K, Suf>>}). --define(FOUND_BAD_HEADER(K), {<<K>>, array, [{longstr, <<"bad ", K>>}]}). - -write_table_with_invalid_existing_type_test() -> - prepend_check(<<"header1">>, ?XDEATH_TABLE, [?BAD_HEADER("header1")]), - passed. - -invalid_existing_headers_test() -> - Headers = - prepend_check(<<"header2">>, ?ROUTE_TABLE, [?BAD_HEADER("header2")]), - {array, [{table, ?ROUTE_TABLE}]} = - rabbit_misc:table_lookup(Headers, <<"header2">>), - passed. - -disparate_invalid_header_entries_accumulate_separately_test() -> - BadHeaders = [?BAD_HEADER("header2")], - Headers = prepend_check(<<"header2">>, ?ROUTE_TABLE, BadHeaders), - Headers2 = prepend_check(<<"header1">>, ?XDEATH_TABLE, - [?BAD_HEADER("header1") | Headers]), - {table, [?FOUND_BAD_HEADER("header1"), - ?FOUND_BAD_HEADER("header2")]} = - rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), - passed. - -corrupt_or_invalid_headers_are_overwritten_test() -> - Headers0 = [?BAD_HEADER("header1"), - ?BAD_HEADER("x-invalid-headers")], - Headers1 = prepend_check(<<"header1">>, ?XDEATH_TABLE, Headers0), - {table,[?FOUND_BAD_HEADER("header1"), - ?FOUND_BAD_HEADER("x-invalid-headers")]} = - rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), - passed. - -invalid_same_header_entry_accumulation_test() -> - BadHeader1 = ?BAD_HEADER2("header1", "a"), - Headers = prepend_check(<<"header1">>, ?ROUTE_TABLE, [BadHeader1]), - Headers2 = prepend_check(<<"header1">>, ?ROUTE_TABLE, - [?BAD_HEADER2("header1", "b") | Headers]), - {table, InvalidHeaders} = - rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), - {array, [{longstr,<<"bad header1b">>}, - {longstr,<<"bad header1a">>}]} = - rabbit_misc:table_lookup(InvalidHeaders, <<"header1">>), - passed. - -prepend_check(HeaderKey, HeaderTable, Headers) -> - Headers1 = rabbit_basic:prepend_table_header( - HeaderKey, HeaderTable, Headers), - {table, Invalid} = - rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), - {Type, Value} = rabbit_misc:table_lookup(Headers, HeaderKey), - {array, [{Type, Value} | _]} = - rabbit_misc:table_lookup(Invalid, HeaderKey), - Headers1. - -test_priority_queue() -> - - false = priority_queue:is_queue(not_a_queue), - - %% empty Q - Q = priority_queue:new(), - {true, true, 0, [], []} = test_priority_queue(Q), - - %% 1-4 element no-priority Q - true = lists:all(fun (X) -> X =:= passed end, - lists:map(fun test_simple_n_element_queue/1, - lists:seq(1, 4))), - - %% 1-element priority Q - Q1 = priority_queue:in(foo, 1, priority_queue:new()), - {true, false, 1, [{1, foo}], [foo]} = - test_priority_queue(Q1), - - %% 2-element same-priority Q - Q2 = priority_queue:in(bar, 1, Q1), - {true, false, 2, [{1, foo}, {1, bar}], [foo, bar]} = - test_priority_queue(Q2), - - %% 2-element different-priority Q - Q3 = priority_queue:in(bar, 2, Q1), - {true, false, 2, [{2, bar}, {1, foo}], [bar, foo]} = - test_priority_queue(Q3), - - %% 1-element negative priority Q - Q4 = priority_queue:in(foo, -1, priority_queue:new()), - {true, false, 1, [{-1, foo}], [foo]} = test_priority_queue(Q4), - - %% merge 2 * 1-element no-priority Qs - Q5 = priority_queue:join(priority_queue:in(foo, Q), - priority_queue:in(bar, Q)), - {true, false, 2, [{0, foo}, {0, bar}], [foo, bar]} = - test_priority_queue(Q5), - - %% merge 1-element no-priority Q with 1-element priority Q - Q6 = priority_queue:join(priority_queue:in(foo, Q), - priority_queue:in(bar, 1, Q)), - {true, false, 2, [{1, bar}, {0, foo}], [bar, foo]} = - test_priority_queue(Q6), - - %% merge 1-element priority Q with 1-element no-priority Q - Q7 = priority_queue:join(priority_queue:in(foo, 1, Q), - priority_queue:in(bar, Q)), - {true, false, 2, [{1, foo}, {0, bar}], [foo, bar]} = - test_priority_queue(Q7), - - %% merge 2 * 1-element same-priority Qs - Q8 = priority_queue:join(priority_queue:in(foo, 1, Q), - priority_queue:in(bar, 1, Q)), - {true, false, 2, [{1, foo}, {1, bar}], [foo, bar]} = - test_priority_queue(Q8), - - %% merge 2 * 1-element different-priority Qs - Q9 = priority_queue:join(priority_queue:in(foo, 1, Q), - priority_queue:in(bar, 2, Q)), - {true, false, 2, [{2, bar}, {1, foo}], [bar, foo]} = - test_priority_queue(Q9), - - %% merge 2 * 1-element different-priority Qs (other way around) - Q10 = priority_queue:join(priority_queue:in(bar, 2, Q), - priority_queue:in(foo, 1, Q)), - {true, false, 2, [{2, bar}, {1, foo}], [bar, foo]} = - test_priority_queue(Q10), - - %% merge 2 * 2-element multi-different-priority Qs - Q11 = priority_queue:join(Q6, Q5), - {true, false, 4, [{1, bar}, {0, foo}, {0, foo}, {0, bar}], - [bar, foo, foo, bar]} = test_priority_queue(Q11), - - %% and the other way around - Q12 = priority_queue:join(Q5, Q6), - {true, false, 4, [{1, bar}, {0, foo}, {0, bar}, {0, foo}], - [bar, foo, bar, foo]} = test_priority_queue(Q12), - - %% merge with negative priorities - Q13 = priority_queue:join(Q4, Q5), - {true, false, 3, [{0, foo}, {0, bar}, {-1, foo}], [foo, bar, foo]} = - test_priority_queue(Q13), - - %% and the other way around - Q14 = priority_queue:join(Q5, Q4), - {true, false, 3, [{0, foo}, {0, bar}, {-1, foo}], [foo, bar, foo]} = - test_priority_queue(Q14), - - %% joins with empty queues: - Q1 = priority_queue:join(Q, Q1), - Q1 = priority_queue:join(Q1, Q), - - %% insert with priority into non-empty zero-priority queue - Q15 = priority_queue:in(baz, 1, Q5), - {true, false, 3, [{1, baz}, {0, foo}, {0, bar}], [baz, foo, bar]} = - test_priority_queue(Q15), - - %% 1-element infinity priority Q - Q16 = priority_queue:in(foo, infinity, Q), - {true, false, 1, [{infinity, foo}], [foo]} = test_priority_queue(Q16), - - %% add infinity to 0-priority Q - Q17 = priority_queue:in(foo, infinity, priority_queue:in(bar, Q)), - {true, false, 2, [{infinity, foo}, {0, bar}], [foo, bar]} = - test_priority_queue(Q17), - - %% and the other way around - Q18 = priority_queue:in(bar, priority_queue:in(foo, infinity, Q)), - {true, false, 2, [{infinity, foo}, {0, bar}], [foo, bar]} = - test_priority_queue(Q18), - - %% add infinity to mixed-priority Q - Q19 = priority_queue:in(qux, infinity, Q3), - {true, false, 3, [{infinity, qux}, {2, bar}, {1, foo}], [qux, bar, foo]} = - test_priority_queue(Q19), - - %% merge the above with a negative priority Q - Q20 = priority_queue:join(Q19, Q4), - {true, false, 4, [{infinity, qux}, {2, bar}, {1, foo}, {-1, foo}], - [qux, bar, foo, foo]} = test_priority_queue(Q20), - - %% merge two infinity priority queues - Q21 = priority_queue:join(priority_queue:in(foo, infinity, Q), - priority_queue:in(bar, infinity, Q)), - {true, false, 2, [{infinity, foo}, {infinity, bar}], [foo, bar]} = - test_priority_queue(Q21), - - %% merge two mixed priority with infinity queues - Q22 = priority_queue:join(Q18, Q20), - {true, false, 6, [{infinity, foo}, {infinity, qux}, {2, bar}, {1, foo}, - {0, bar}, {-1, foo}], [foo, qux, bar, foo, bar, foo]} = - test_priority_queue(Q22), - - passed. - -priority_queue_in_all(Q, L) -> - lists:foldl(fun (X, Acc) -> priority_queue:in(X, Acc) end, Q, L). - -priority_queue_out_all(Q) -> - case priority_queue:out(Q) of - {empty, _} -> []; - {{value, V}, Q1} -> [V | priority_queue_out_all(Q1)] - end. - -test_priority_queue(Q) -> - {priority_queue:is_queue(Q), - priority_queue:is_empty(Q), - priority_queue:len(Q), - priority_queue:to_list(Q), - priority_queue_out_all(Q)}. - -test_simple_n_element_queue(N) -> - Items = lists:seq(1, N), - Q = priority_queue_in_all(priority_queue:new(), Items), - ToListRes = [{0, X} || X <- Items], - {true, false, N, ToListRes, Items} = test_priority_queue(Q), - passed. - -test_pg_local() -> - [P, Q] = [spawn(fun () -> receive X -> X end end) || _ <- [x, x]], - check_pg_local(ok, [], []), - check_pg_local(pg_local:join(a, P), [P], []), - check_pg_local(pg_local:join(b, P), [P], [P]), - check_pg_local(pg_local:join(a, P), [P, P], [P]), - check_pg_local(pg_local:join(a, Q), [P, P, Q], [P]), - check_pg_local(pg_local:join(b, Q), [P, P, Q], [P, Q]), - check_pg_local(pg_local:join(b, Q), [P, P, Q], [P, Q, Q]), - check_pg_local(pg_local:leave(a, P), [P, Q], [P, Q, Q]), - check_pg_local(pg_local:leave(b, P), [P, Q], [Q, Q]), - check_pg_local(pg_local:leave(a, P), [Q], [Q, Q]), - check_pg_local(pg_local:leave(a, P), [Q], [Q, Q]), - [begin X ! done, - Ref = erlang:monitor(process, X), - receive {'DOWN', Ref, process, X, _Info} -> ok end - end || X <- [P, Q]], - check_pg_local(ok, [], []), - passed. - -check_pg_local(ok, APids, BPids) -> - ok = pg_local:sync(), - [true, true] = [lists:sort(Pids) == lists:sort(pg_local:get_members(Key)) || - {Key, Pids} <- [{a, APids}, {b, BPids}]]. - -test_unfold() -> - {[], test} = rabbit_misc:unfold(fun (_V) -> false end, test), - List = lists:seq(2,20,2), - {List, 0} = rabbit_misc:unfold(fun (0) -> false; - (N) -> {true, N*2, N-1} - end, 10), - passed. - -test_pmerge() -> - P = [{a, 1}, {b, 2}], - P = rabbit_misc:pmerge(a, 3, P), - [{c, 3} | P] = rabbit_misc:pmerge(c, 3, P), - passed. - -test_plmerge() -> - P1 = [{a, 1}, {b, 2}, {c, 3}], - P2 = [{a, 2}, {d, 4}], - [{a, 1}, {b, 2}, {c, 3}, {d, 4}] = rabbit_misc:plmerge(P1, P2), - passed. - -test_table_codec() -> - %% FIXME this does not test inexact numbers (double and float) yet, - %% because they won't pass the equality assertions - Table = [{<<"longstr">>, longstr, <<"Here is a long string">>}, - {<<"signedint">>, signedint, 12345}, - {<<"decimal">>, decimal, {3, 123456}}, - {<<"timestamp">>, timestamp, 109876543209876}, - {<<"table">>, table, [{<<"one">>, signedint, 54321}, - {<<"two">>, longstr, - <<"A long string">>}]}, - {<<"byte">>, byte, -128}, - {<<"long">>, long, 1234567890}, - {<<"short">>, short, 655}, - {<<"bool">>, bool, true}, - {<<"binary">>, binary, <<"a binary string">>}, - {<<"void">>, void, undefined}, - {<<"array">>, array, [{signedint, 54321}, - {longstr, <<"A long string">>}]} - ], - Binary = << - 7,"longstr", "S", 21:32, "Here is a long string", - 9,"signedint", "I", 12345:32/signed, - 7,"decimal", "D", 3, 123456:32, - 9,"timestamp", "T", 109876543209876:64, - 5,"table", "F", 31:32, % length of table - 3,"one", "I", 54321:32, - 3,"two", "S", 13:32, "A long string", - 4,"byte", "b", -128:8/signed, - 4,"long", "l", 1234567890:64, - 5,"short", "s", 655:16, - 4,"bool", "t", 1, - 6,"binary", "x", 15:32, "a binary string", - 4,"void", "V", - 5,"array", "A", 23:32, - "I", 54321:32, - "S", 13:32, "A long string" - >>, - Binary = rabbit_binary_generator:generate_table(Table), - Table = rabbit_binary_parser:parse_table(Binary), - passed. - -%% Test that content frames don't exceed frame-max -test_content_framing(FrameMax, BodyBin) -> - [Header | Frames] = - rabbit_binary_generator:build_simple_content_frames( - 1, - rabbit_binary_generator:ensure_content_encoded( - rabbit_basic:build_content(#'P_basic'{}, BodyBin), - rabbit_framing_amqp_0_9_1), - FrameMax, - rabbit_framing_amqp_0_9_1), - %% header is formatted correctly and the size is the total of the - %% fragments - <<_FrameHeader:7/binary, _ClassAndWeight:4/binary, - BodySize:64/unsigned, _Rest/binary>> = list_to_binary(Header), - BodySize = size(BodyBin), - true = lists:all( - fun (ContentFrame) -> - FrameBinary = list_to_binary(ContentFrame), - %% assert - <<_TypeAndChannel:3/binary, - Size:32/unsigned, _Payload:Size/binary, 16#CE>> = - FrameBinary, - size(FrameBinary) =< FrameMax - end, Frames), - passed. - -test_content_framing() -> - %% no content - passed = test_content_framing(4096, <<>>), - %% easily fit in one frame - passed = test_content_framing(4096, <<"Easy">>), - %% exactly one frame (empty frame = 8 bytes) - passed = test_content_framing(11, <<"One">>), - %% more than one frame - passed = test_content_framing(11, <<"More than one frame">>), - passed. - -test_content_transcoding() -> - %% there are no guarantees provided by 'clear' - it's just a hint - ClearDecoded = fun rabbit_binary_parser:clear_decoded_content/1, - ClearEncoded = fun rabbit_binary_generator:clear_encoded_content/1, - EnsureDecoded = - fun (C0) -> - C1 = rabbit_binary_parser:ensure_content_decoded(C0), - true = C1#content.properties =/= none, - C1 - end, - EnsureEncoded = - fun (Protocol) -> - fun (C0) -> - C1 = rabbit_binary_generator:ensure_content_encoded( - C0, Protocol), - true = C1#content.properties_bin =/= none, - C1 - end - end, - %% Beyond the assertions in Ensure*, the only testable guarantee - %% is that the operations should never fail. - %% - %% If we were using quickcheck we'd simply stuff all the above - %% into a generator for sequences of operations. In the absence of - %% quickcheck we pick particularly interesting sequences that: - %% - %% - execute every op twice since they are idempotent - %% - invoke clear_decoded, clear_encoded, decode and transcode - %% with one or both of decoded and encoded content present - [begin - sequence_with_content([Op]), - sequence_with_content([ClearEncoded, Op]), - sequence_with_content([ClearDecoded, Op]) - end || Op <- [ClearDecoded, ClearEncoded, EnsureDecoded, - EnsureEncoded(rabbit_framing_amqp_0_9_1), - EnsureEncoded(rabbit_framing_amqp_0_8)]], - passed. - -sequence_with_content(Sequence) -> - lists:foldl(fun (F, V) -> F(F(V)) end, - rabbit_binary_generator:ensure_content_encoded( - rabbit_basic:build_content(#'P_basic'{}, <<>>), - rabbit_framing_amqp_0_9_1), - Sequence). - -test_topic_matching() -> - XName = #resource{virtual_host = <<"/">>, - kind = exchange, - name = <<"test_exchange">>}, - X0 = #exchange{name = XName, type = topic, durable = false, - auto_delete = false, arguments = []}, - X = rabbit_exchange_decorator:set(X0), - %% create - rabbit_exchange_type_topic:validate(X), - exchange_op_callback(X, create, []), - - %% add some bindings - Bindings = [#binding{source = XName, - key = list_to_binary(Key), - destination = #resource{virtual_host = <<"/">>, - kind = queue, - name = list_to_binary(Q)}, - args = Args} || - {Key, Q, Args} <- [{"a.b.c", "t1", []}, - {"a.*.c", "t2", []}, - {"a.#.b", "t3", []}, - {"a.b.b.c", "t4", []}, - {"#", "t5", []}, - {"#.#", "t6", []}, - {"#.b", "t7", []}, - {"*.*", "t8", []}, - {"a.*", "t9", []}, - {"*.b.c", "t10", []}, - {"a.#", "t11", []}, - {"a.#.#", "t12", []}, - {"b.b.c", "t13", []}, - {"a.b.b", "t14", []}, - {"a.b", "t15", []}, - {"b.c", "t16", []}, - {"", "t17", []}, - {"*.*.*", "t18", []}, - {"vodka.martini", "t19", []}, - {"a.b.c", "t20", []}, - {"*.#", "t21", []}, - {"#.*.#", "t22", []}, - {"*.#.#", "t23", []}, - {"#.#.#", "t24", []}, - {"*", "t25", []}, - {"#.b.#", "t26", []}, - {"args-test", "t27", - [{<<"foo">>, longstr, <<"bar">>}]}, - {"args-test", "t27", %% Note aliasing - [{<<"foo">>, longstr, <<"baz">>}]}]], - lists:foreach(fun (B) -> exchange_op_callback(X, add_binding, [B]) end, - Bindings), - - %% test some matches - test_topic_expect_match( - X, [{"a.b.c", ["t1", "t2", "t5", "t6", "t10", "t11", "t12", - "t18", "t20", "t21", "t22", "t23", "t24", - "t26"]}, - {"a.b", ["t3", "t5", "t6", "t7", "t8", "t9", "t11", - "t12", "t15", "t21", "t22", "t23", "t24", - "t26"]}, - {"a.b.b", ["t3", "t5", "t6", "t7", "t11", "t12", "t14", - "t18", "t21", "t22", "t23", "t24", "t26"]}, - {"", ["t5", "t6", "t17", "t24"]}, - {"b.c.c", ["t5", "t6", "t18", "t21", "t22", "t23", - "t24", "t26"]}, - {"a.a.a.a.a", ["t5", "t6", "t11", "t12", "t21", "t22", - "t23", "t24"]}, - {"vodka.gin", ["t5", "t6", "t8", "t21", "t22", "t23", - "t24"]}, - {"vodka.martini", ["t5", "t6", "t8", "t19", "t21", "t22", "t23", - "t24"]}, - {"b.b.c", ["t5", "t6", "t10", "t13", "t18", "t21", - "t22", "t23", "t24", "t26"]}, - {"nothing.here.at.all", ["t5", "t6", "t21", "t22", "t23", "t24"]}, - {"oneword", ["t5", "t6", "t21", "t22", "t23", "t24", - "t25"]}, - {"args-test", ["t5", "t6", "t21", "t22", "t23", "t24", - "t25", "t27"]}]), - %% remove some bindings - RemovedBindings = [lists:nth(1, Bindings), lists:nth(5, Bindings), - lists:nth(11, Bindings), lists:nth(19, Bindings), - lists:nth(21, Bindings), lists:nth(28, Bindings)], - exchange_op_callback(X, remove_bindings, [RemovedBindings]), - RemainingBindings = ordsets:to_list( - ordsets:subtract(ordsets:from_list(Bindings), - ordsets:from_list(RemovedBindings))), - - %% test some matches - test_topic_expect_match( - X, - [{"a.b.c", ["t2", "t6", "t10", "t12", "t18", "t20", "t22", - "t23", "t24", "t26"]}, - {"a.b", ["t3", "t6", "t7", "t8", "t9", "t12", "t15", - "t22", "t23", "t24", "t26"]}, - {"a.b.b", ["t3", "t6", "t7", "t12", "t14", "t18", "t22", - "t23", "t24", "t26"]}, - {"", ["t6", "t17", "t24"]}, - {"b.c.c", ["t6", "t18", "t22", "t23", "t24", "t26"]}, - {"a.a.a.a.a", ["t6", "t12", "t22", "t23", "t24"]}, - {"vodka.gin", ["t6", "t8", "t22", "t23", "t24"]}, - {"vodka.martini", ["t6", "t8", "t22", "t23", "t24"]}, - {"b.b.c", ["t6", "t10", "t13", "t18", "t22", "t23", - "t24", "t26"]}, - {"nothing.here.at.all", ["t6", "t22", "t23", "t24"]}, - {"oneword", ["t6", "t22", "t23", "t24", "t25"]}, - {"args-test", ["t6", "t22", "t23", "t24", "t25", "t27"]}]), - - %% remove the entire exchange - exchange_op_callback(X, delete, [RemainingBindings]), - %% none should match now - test_topic_expect_match(X, [{"a.b.c", []}, {"b.b.c", []}, {"", []}]), - passed. - -exchange_op_callback(X, Fun, Args) -> - rabbit_misc:execute_mnesia_transaction( - fun () -> rabbit_exchange:callback(X, Fun, transaction, [X] ++ Args) end), - rabbit_exchange:callback(X, Fun, none, [X] ++ Args). - -test_topic_expect_match(X, List) -> - lists:foreach( - fun ({Key, Expected}) -> - BinKey = list_to_binary(Key), - Message = rabbit_basic:message(X#exchange.name, BinKey, - #'P_basic'{}, <<>>), - Res = rabbit_exchange_type_topic:route( - X, #delivery{mandatory = false, - sender = self(), - message = Message}), - ExpectedRes = lists:map( - fun (Q) -> #resource{virtual_host = <<"/">>, - kind = queue, - name = list_to_binary(Q)} - end, Expected), - true = (lists:usort(ExpectedRes) =:= lists:usort(Res)) - end, List). - -test_app_management() -> - control_action(wait, [rabbit_mnesia:dir() ++ ".pid"]), - %% Starting, stopping and diagnostics. Note that we don't try - %% 'report' when the rabbit app is stopped and that we enable - %% tracing for the duration of this function. - ok = control_action(trace_on, []), - ok = control_action(stop_app, []), - ok = control_action(stop_app, []), - ok = control_action(status, []), - ok = control_action(cluster_status, []), - ok = control_action(environment, []), - ok = control_action(start_app, []), - ok = control_action(start_app, []), - ok = control_action(status, []), - ok = control_action(report, []), - ok = control_action(cluster_status, []), - ok = control_action(environment, []), - ok = control_action(trace_off, []), - passed. - -test_log_management() -> - MainLog = rabbit:log_location(kernel), - SaslLog = rabbit:log_location(sasl), - Suffix = ".1", - - %% prepare basic logs - file:delete([MainLog, Suffix]), - file:delete([SaslLog, Suffix]), - - %% simple logs reopening - ok = control_action(rotate_logs, []), - [true, true] = empty_files([MainLog, SaslLog]), - ok = test_logs_working(MainLog, SaslLog), - - %% simple log rotation - ok = control_action(rotate_logs, [Suffix]), - [true, true] = non_empty_files([[MainLog, Suffix], [SaslLog, Suffix]]), - [true, true] = empty_files([MainLog, SaslLog]), - ok = test_logs_working(MainLog, SaslLog), - - %% reopening logs with log rotation performed first - ok = clean_logs([MainLog, SaslLog], Suffix), - ok = control_action(rotate_logs, []), - ok = file:rename(MainLog, [MainLog, Suffix]), - ok = file:rename(SaslLog, [SaslLog, Suffix]), - ok = test_logs_working([MainLog, Suffix], [SaslLog, Suffix]), - ok = control_action(rotate_logs, []), - ok = test_logs_working(MainLog, SaslLog), - - %% log rotation on empty files (the main log will have a ctl action logged) - ok = clean_logs([MainLog, SaslLog], Suffix), - ok = control_action(rotate_logs, []), - ok = control_action(rotate_logs, [Suffix]), - [false, true] = empty_files([[MainLog, Suffix], [SaslLog, Suffix]]), - - %% logs with suffix are not writable - ok = control_action(rotate_logs, [Suffix]), - ok = make_files_non_writable([[MainLog, Suffix], [SaslLog, Suffix]]), - ok = control_action(rotate_logs, [Suffix]), - ok = test_logs_working(MainLog, SaslLog), - - %% rotate when original log files are not writable - ok = make_files_non_writable([MainLog, SaslLog]), - ok = control_action(rotate_logs, []), - - %% logging directed to tty (first, remove handlers) - ok = delete_log_handlers([rabbit_sasl_report_file_h, - rabbit_error_logger_file_h]), - ok = clean_logs([MainLog, SaslLog], Suffix), - ok = application:set_env(rabbit, sasl_error_logger, tty), - ok = application:set_env(rabbit, error_logger, tty), - ok = control_action(rotate_logs, []), - [{error, enoent}, {error, enoent}] = empty_files([MainLog, SaslLog]), - - %% rotate logs when logging is turned off - ok = application:set_env(rabbit, sasl_error_logger, false), - ok = application:set_env(rabbit, error_logger, silent), - ok = control_action(rotate_logs, []), - [{error, enoent}, {error, enoent}] = empty_files([MainLog, SaslLog]), - - %% cleanup - ok = application:set_env(rabbit, sasl_error_logger, {file, SaslLog}), - ok = application:set_env(rabbit, error_logger, {file, MainLog}), - ok = add_log_handlers([{rabbit_error_logger_file_h, MainLog}, - {rabbit_sasl_report_file_h, SaslLog}]), - passed. - -test_log_management_during_startup() -> - MainLog = rabbit:log_location(kernel), - SaslLog = rabbit:log_location(sasl), - - %% start application with simple tty logging - ok = control_action(stop_app, []), - ok = application:set_env(rabbit, error_logger, tty), - ok = application:set_env(rabbit, sasl_error_logger, tty), - ok = add_log_handlers([{error_logger_tty_h, []}, - {sasl_report_tty_h, []}]), - ok = control_action(start_app, []), - - %% start application with tty logging and - %% proper handlers not installed - ok = control_action(stop_app, []), - ok = error_logger:tty(false), - ok = delete_log_handlers([sasl_report_tty_h]), - ok = case catch control_action(start_app, []) of - ok -> exit({got_success_but_expected_failure, - log_rotation_tty_no_handlers_test}); - {badrpc, {'EXIT', {error, - {cannot_log_to_tty, _, not_installed}}}} -> ok - end, - - %% fix sasl logging - ok = application:set_env(rabbit, sasl_error_logger, {file, SaslLog}), - - %% start application with logging to non-existing directory - TmpLog = "/tmp/rabbit-tests/test.log", - delete_file(TmpLog), - ok = control_action(stop_app, []), - ok = application:set_env(rabbit, error_logger, {file, TmpLog}), - - ok = delete_log_handlers([rabbit_error_logger_file_h]), - ok = add_log_handlers([{error_logger_file_h, MainLog}]), - ok = control_action(start_app, []), - - %% start application with logging to directory with no - %% write permissions - ok = control_action(stop_app, []), - TmpDir = "/tmp/rabbit-tests", - ok = set_permissions(TmpDir, 8#00400), - ok = delete_log_handlers([rabbit_error_logger_file_h]), - ok = add_log_handlers([{error_logger_file_h, MainLog}]), - ok = case control_action(start_app, []) of - ok -> exit({got_success_but_expected_failure, - log_rotation_no_write_permission_dir_test}); - {badrpc, {'EXIT', - {error, {cannot_log_to_file, _, _}}}} -> ok - end, - - %% start application with logging to a subdirectory which - %% parent directory has no write permissions - ok = control_action(stop_app, []), - TmpTestDir = "/tmp/rabbit-tests/no-permission/test/log", - ok = application:set_env(rabbit, error_logger, {file, TmpTestDir}), - ok = add_log_handlers([{error_logger_file_h, MainLog}]), - ok = case control_action(start_app, []) of - ok -> exit({got_success_but_expected_failure, - log_rotatation_parent_dirs_test}); - {badrpc, - {'EXIT', - {error, {cannot_log_to_file, _, - {error, - {cannot_create_parent_dirs, _, eacces}}}}}} -> ok - end, - ok = set_permissions(TmpDir, 8#00700), - ok = set_permissions(TmpLog, 8#00600), - ok = delete_file(TmpLog), - ok = file:del_dir(TmpDir), - - %% start application with standard error_logger_file_h - %% handler not installed - ok = control_action(stop_app, []), - ok = application:set_env(rabbit, error_logger, {file, MainLog}), - ok = control_action(start_app, []), - - %% start application with standard sasl handler not installed - %% and rabbit main log handler installed correctly - ok = control_action(stop_app, []), - ok = delete_log_handlers([rabbit_sasl_report_file_h]), - ok = control_action(start_app, []), - passed. - -test_arguments_parser() -> - GlobalOpts1 = [{"-f1", flag}, {"-o1", {option, "foo"}}], - Commands1 = [command1, {command2, [{"-f2", flag}, {"-o2", {option, "bar"}}]}], - - GetOptions = - fun (Args) -> - rabbit_cli:parse_arguments(Commands1, GlobalOpts1, "-n", Args) - end, - - check_parse_arguments(no_command, GetOptions, []), - check_parse_arguments(no_command, GetOptions, ["foo", "bar"]), - check_parse_arguments( - {ok, {command1, [{"-f1", false}, {"-o1", "foo"}], []}}, - GetOptions, ["command1"]), - check_parse_arguments( - {ok, {command1, [{"-f1", false}, {"-o1", "blah"}], []}}, - GetOptions, ["command1", "-o1", "blah"]), - check_parse_arguments( - {ok, {command1, [{"-f1", true}, {"-o1", "foo"}], []}}, - GetOptions, ["command1", "-f1"]), - check_parse_arguments( - {ok, {command1, [{"-f1", false}, {"-o1", "blah"}], []}}, - GetOptions, ["-o1", "blah", "command1"]), - check_parse_arguments( - {ok, {command1, [{"-f1", false}, {"-o1", "blah"}], ["quux"]}}, - GetOptions, ["-o1", "blah", "command1", "quux"]), - check_parse_arguments( - {ok, {command1, [{"-f1", true}, {"-o1", "blah"}], ["quux", "baz"]}}, - GetOptions, ["command1", "quux", "-f1", "-o1", "blah", "baz"]), - %% For duplicate flags, the last one counts - check_parse_arguments( - {ok, {command1, [{"-f1", false}, {"-o1", "second"}], []}}, - GetOptions, ["-o1", "first", "command1", "-o1", "second"]), - %% If the flag "eats" the command, the command won't be recognised - check_parse_arguments(no_command, GetOptions, - ["-o1", "command1", "quux"]), - %% If a flag eats another flag, the eaten flag won't be recognised - check_parse_arguments( - {ok, {command1, [{"-f1", false}, {"-o1", "-f1"}], []}}, - GetOptions, ["command1", "-o1", "-f1"]), - - %% Now for some command-specific flags... - check_parse_arguments( - {ok, {command2, [{"-f1", false}, {"-f2", false}, - {"-o1", "foo"}, {"-o2", "bar"}], []}}, - GetOptions, ["command2"]), - - check_parse_arguments( - {ok, {command2, [{"-f1", false}, {"-f2", true}, - {"-o1", "baz"}, {"-o2", "bar"}], ["quux", "foo"]}}, - GetOptions, ["-f2", "command2", "quux", "-o1", "baz", "foo"]), - - passed. - -test_dynamic_mirroring() -> - %% Just unit tests of the node selection logic, see multi node - %% tests for the rest... - Test = fun ({NewM, NewSs, ExtraSs}, Policy, Params, - {MNode, SNodes, SSNodes}, All) -> - {ok, M} = rabbit_mirror_queue_misc:module(Policy), - {NewM, NewSs0} = M:suggested_queue_nodes( - Params, MNode, SNodes, SSNodes, All), - NewSs1 = lists:sort(NewSs0), - case dm_list_match(NewSs, NewSs1, ExtraSs) of - ok -> ok; - error -> exit({no_match, NewSs, NewSs1, ExtraSs}) - end - end, - - Test({a,[b,c],0},<<"all">>,'_',{a,[], []}, [a,b,c]), - Test({a,[b,c],0},<<"all">>,'_',{a,[b,c],[b,c]},[a,b,c]), - Test({a,[b,c],0},<<"all">>,'_',{a,[d], [d]}, [a,b,c]), - - N = fun (Atoms) -> [list_to_binary(atom_to_list(A)) || A <- Atoms] end, - - %% Add a node - Test({a,[b,c],0},<<"nodes">>,N([a,b,c]),{a,[b],[b]},[a,b,c,d]), - Test({b,[a,c],0},<<"nodes">>,N([a,b,c]),{b,[a],[a]},[a,b,c,d]), - %% Add two nodes and drop one - Test({a,[b,c],0},<<"nodes">>,N([a,b,c]),{a,[d],[d]},[a,b,c,d]), - %% Don't try to include nodes that are not running - Test({a,[b], 0},<<"nodes">>,N([a,b,f]),{a,[b],[b]},[a,b,c,d]), - %% If we can't find any of the nodes listed then just keep the master - Test({a,[], 0},<<"nodes">>,N([f,g,h]),{a,[b],[b]},[a,b,c,d]), - %% And once that's happened, still keep the master even when not listed, - %% if nothing is synced - Test({a,[b,c],0},<<"nodes">>,N([b,c]), {a,[], []}, [a,b,c,d]), - Test({a,[b,c],0},<<"nodes">>,N([b,c]), {a,[b],[]}, [a,b,c,d]), - %% But if something is synced we can lose the master - but make - %% sure we pick the new master from the nodes which are synced! - Test({b,[c], 0},<<"nodes">>,N([b,c]), {a,[b],[b]},[a,b,c,d]), - Test({b,[c], 0},<<"nodes">>,N([c,b]), {a,[b],[b]},[a,b,c,d]), - - Test({a,[], 1},<<"exactly">>,2,{a,[], []}, [a,b,c,d]), - Test({a,[], 2},<<"exactly">>,3,{a,[], []}, [a,b,c,d]), - Test({a,[c], 0},<<"exactly">>,2,{a,[c], [c]}, [a,b,c,d]), - Test({a,[c], 1},<<"exactly">>,3,{a,[c], [c]}, [a,b,c,d]), - Test({a,[c], 0},<<"exactly">>,2,{a,[c,d],[c,d]},[a,b,c,d]), - Test({a,[c,d],0},<<"exactly">>,3,{a,[c,d],[c,d]},[a,b,c,d]), - - passed. - -%% Does the first list match the second where the second is required -%% to have exactly Extra superfluous items? -dm_list_match([], [], 0) -> ok; -dm_list_match(_, [], _Extra) -> error; -dm_list_match([H|T1], [H |T2], Extra) -> dm_list_match(T1, T2, Extra); -dm_list_match(L1, [_H|T2], Extra) -> dm_list_match(L1, T2, Extra - 1). - -test_user_management() -> - - %% lots if stuff that should fail - {error, {no_such_user, _}} = - control_action(delete_user, ["foo"]), - {error, {no_such_user, _}} = - control_action(change_password, ["foo", "baz"]), - {error, {no_such_vhost, _}} = - control_action(delete_vhost, ["/testhost"]), - {error, {no_such_user, _}} = - control_action(set_permissions, ["foo", ".*", ".*", ".*"]), - {error, {no_such_user, _}} = - control_action(clear_permissions, ["foo"]), - {error, {no_such_user, _}} = - control_action(list_user_permissions, ["foo"]), - {error, {no_such_vhost, _}} = - control_action(list_permissions, [], [{"-p", "/testhost"}]), - {error, {invalid_regexp, _, _}} = - control_action(set_permissions, ["guest", "+foo", ".*", ".*"]), - {error, {no_such_user, _}} = - control_action(set_user_tags, ["foo", "bar"]), - - %% user creation - ok = control_action(add_user, ["foo", "bar"]), - {error, {user_already_exists, _}} = - control_action(add_user, ["foo", "bar"]), - ok = control_action(clear_password, ["foo"]), - ok = control_action(change_password, ["foo", "baz"]), - - TestTags = fun (Tags) -> - Args = ["foo" | [atom_to_list(T) || T <- Tags]], - ok = control_action(set_user_tags, Args), - {ok, #internal_user{tags = Tags}} = - rabbit_auth_backend_internal:lookup_user(<<"foo">>), - ok = control_action(list_users, []) - end, - TestTags([foo, bar, baz]), - TestTags([administrator]), - TestTags([]), - - %% vhost creation - ok = control_action(add_vhost, ["/testhost"]), - {error, {vhost_already_exists, _}} = - control_action(add_vhost, ["/testhost"]), - ok = control_action(list_vhosts, []), - - %% user/vhost mapping - ok = control_action(set_permissions, ["foo", ".*", ".*", ".*"], - [{"-p", "/testhost"}]), - ok = control_action(set_permissions, ["foo", ".*", ".*", ".*"], - [{"-p", "/testhost"}]), - ok = control_action(set_permissions, ["foo", ".*", ".*", ".*"], - [{"-p", "/testhost"}]), - ok = control_action(list_permissions, [], [{"-p", "/testhost"}]), - ok = control_action(list_permissions, [], [{"-p", "/testhost"}]), - ok = control_action(list_user_permissions, ["foo"]), - - %% user/vhost unmapping - ok = control_action(clear_permissions, ["foo"], [{"-p", "/testhost"}]), - ok = control_action(clear_permissions, ["foo"], [{"-p", "/testhost"}]), - - %% vhost deletion - ok = control_action(delete_vhost, ["/testhost"]), - {error, {no_such_vhost, _}} = - control_action(delete_vhost, ["/testhost"]), - - %% deleting a populated vhost - ok = control_action(add_vhost, ["/testhost"]), - ok = control_action(set_permissions, ["foo", ".*", ".*", ".*"], - [{"-p", "/testhost"}]), - {new, _} = rabbit_amqqueue:declare( - rabbit_misc:r(<<"/testhost">>, queue, <<"test">>), - true, false, [], none), - ok = control_action(delete_vhost, ["/testhost"]), - - %% user deletion - ok = control_action(delete_user, ["foo"]), - {error, {no_such_user, _}} = - control_action(delete_user, ["foo"]), - - passed. - -test_runtime_parameters() -> - rabbit_runtime_parameters_test:register(), - Good = fun(L) -> ok = control_action(set_parameter, L) end, - Bad = fun(L) -> {error_string, _} = control_action(set_parameter, L) end, - - %% Acceptable for bijection - Good(["test", "good", "\"ignore\""]), - Good(["test", "good", "123"]), - Good(["test", "good", "true"]), - Good(["test", "good", "false"]), - Good(["test", "good", "null"]), - Good(["test", "good", "{\"key\": \"value\"}"]), - - %% Invalid json - Bad(["test", "good", "atom"]), - Bad(["test", "good", "{\"foo\": \"bar\""]), - Bad(["test", "good", "{foo: \"bar\"}"]), - - %% Test actual validation hook - Good(["test", "maybe", "\"good\""]), - Bad(["test", "maybe", "\"bad\""]), - Good(["test", "admin", "\"ignore\""]), %% ctl means 'user' -> none - - ok = control_action(list_parameters, []), - - ok = control_action(clear_parameter, ["test", "good"]), - ok = control_action(clear_parameter, ["test", "maybe"]), - ok = control_action(clear_parameter, ["test", "admin"]), - {error_string, _} = - control_action(clear_parameter, ["test", "neverexisted"]), - - %% We can delete for a component that no longer exists - Good(["test", "good", "\"ignore\""]), - rabbit_runtime_parameters_test:unregister(), - ok = control_action(clear_parameter, ["test", "good"]), - passed. - -test_policy_validation() -> - rabbit_runtime_parameters_test:register_policy_validator(), - SetPol = fun (Key, Val) -> - control_action_opts( - ["set_policy", "name", ".*", - rabbit_misc:format("{\"~s\":~p}", [Key, Val])]) - end, - - ok = SetPol("testeven", []), - ok = SetPol("testeven", [1, 2]), - ok = SetPol("testeven", [1, 2, 3, 4]), - ok = SetPol("testpos", [2, 5, 5678]), - - error = SetPol("testpos", [-1, 0, 1]), - error = SetPol("testeven", [ 1, 2, 3]), - - ok = control_action(clear_policy, ["name"]), - rabbit_runtime_parameters_test:unregister_policy_validator(), - passed. - -test_policy_opts_validation() -> - Set = fun (Extra) -> control_action_opts( - ["set_policy", "name", ".*", "{\"ha-mode\":\"all\"}" - | Extra]) end, - OK = fun (Extra) -> ok = Set(Extra) end, - Fail = fun (Extra) -> error = Set(Extra) end, - - OK ([]), - - OK (["--priority", "0"]), - OK (["--priority", "3"]), - Fail(["--priority", "banana"]), - Fail(["--priority"]), - - OK (["--apply-to", "all"]), - OK (["--apply-to", "queues"]), - Fail(["--apply-to", "bananas"]), - Fail(["--apply-to"]), - - OK (["--priority", "3", "--apply-to", "queues"]), - Fail(["--priority", "banana", "--apply-to", "queues"]), - Fail(["--priority", "3", "--apply-to", "bananas"]), - - Fail(["--offline"]), - - ok = control_action(clear_policy, ["name"]), - passed. - -test_ha_policy_validation() -> - Set = fun (JSON) -> control_action_opts( - ["set_policy", "name", ".*", JSON]) end, - OK = fun (JSON) -> ok = Set(JSON) end, - Fail = fun (JSON) -> error = Set(JSON) end, - - OK ("{\"ha-mode\":\"all\"}"), - Fail("{\"ha-mode\":\"made_up\"}"), - - Fail("{\"ha-mode\":\"nodes\"}"), - Fail("{\"ha-mode\":\"nodes\",\"ha-params\":2}"), - Fail("{\"ha-mode\":\"nodes\",\"ha-params\":[\"a\",2]}"), - OK ("{\"ha-mode\":\"nodes\",\"ha-params\":[\"a\",\"b\"]}"), - Fail("{\"ha-params\":[\"a\",\"b\"]}"), - - Fail("{\"ha-mode\":\"exactly\"}"), - Fail("{\"ha-mode\":\"exactly\",\"ha-params\":[\"a\",\"b\"]}"), - OK ("{\"ha-mode\":\"exactly\",\"ha-params\":2}"), - Fail("{\"ha-params\":2}"), - - OK ("{\"ha-mode\":\"all\",\"ha-sync-mode\":\"manual\"}"), - OK ("{\"ha-mode\":\"all\",\"ha-sync-mode\":\"automatic\"}"), - Fail("{\"ha-mode\":\"all\",\"ha-sync-mode\":\"made_up\"}"), - Fail("{\"ha-sync-mode\":\"manual\"}"), - Fail("{\"ha-sync-mode\":\"automatic\"}"), - - ok = control_action(clear_policy, ["name"]), - passed. - -test_queue_master_location_policy_validation() -> - Set = fun (JSON) -> - control_action_opts( ["set_policy", "name", ".*", JSON] ) - end, - OK = fun (JSON) -> ok = Set(JSON) end, - Fail = fun (JSON) -> error = Set(JSON) end, - - OK ("{\"x-queue-master-locator\":\"min-masters\"}"), - OK ("{\"x-queue-master-locator\":\"client-local\"}"), - OK ("{\"x-queue-master-locator\":\"random\"}"), - Fail("{\"x-queue-master-locator\":\"made_up\"}"), - - ok = control_action(clear_policy, ["name"]), - passed. - -test_server_status() -> - %% create a few things so there is some useful information to list - {_Writer, Limiter, Ch} = test_channel(), - [Q, Q2] = [Queue || {Name, Owner} <- [{<<"foo">>, none}, {<<"bar">>, self()}], - {new, Queue = #amqqueue{}} <- - [rabbit_amqqueue:declare( - rabbit_misc:r(<<"/">>, queue, Name), - false, false, [], Owner)]], - ok = rabbit_amqqueue:basic_consume( - Q, true, Ch, Limiter, false, 0, <<"ctag">>, true, [], undefined), - - %% list queues - ok = info_action(list_queues, rabbit_amqqueue:info_keys(), true), - - %% as we have no way to collect output of info_action/3 call, the only way - %% we can test individual queueinfoitems is by directly calling - %% rabbit_amqqueue:info/2 - [{exclusive, false}] = rabbit_amqqueue:info(Q, [exclusive]), - [{exclusive, true}] = rabbit_amqqueue:info(Q2, [exclusive]), - - %% list exchanges - ok = info_action(list_exchanges, rabbit_exchange:info_keys(), true), - - %% list bindings - ok = info_action(list_bindings, rabbit_binding:info_keys(), true), - %% misc binding listing APIs - [_|_] = rabbit_binding:list_for_source( - rabbit_misc:r(<<"/">>, exchange, <<"">>)), - [_] = rabbit_binding:list_for_destination( - rabbit_misc:r(<<"/">>, queue, <<"foo">>)), - [_] = rabbit_binding:list_for_source_and_destination( - rabbit_misc:r(<<"/">>, exchange, <<"">>), - rabbit_misc:r(<<"/">>, queue, <<"foo">>)), - - %% list connections - {H, P} = find_listener(), - {ok, C} = gen_tcp:connect(H, P, []), - gen_tcp:send(C, <<"AMQP", 0, 0, 9, 1>>), - timer:sleep(100), - ok = info_action(list_connections, - rabbit_networking:connection_info_keys(), false), - %% close_connection - [ConnPid] = rabbit_networking:connections(), - ok = control_action(close_connection, [rabbit_misc:pid_to_string(ConnPid), - "go away"]), - - %% list channels - ok = info_action(list_channels, rabbit_channel:info_keys(), false), - - %% list consumers - ok = control_action(list_consumers, []), - - %% set vm memory high watermark - HWM = vm_memory_monitor:get_vm_memory_high_watermark(), - ok = control_action(set_vm_memory_high_watermark, ["1"]), - ok = control_action(set_vm_memory_high_watermark, ["1.0"]), - %% this will trigger an alarm - ok = control_action(set_vm_memory_high_watermark, ["0.0"]), - %% reset - ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), - - %% eval - {error_string, _} = control_action(eval, ["\""]), - {error_string, _} = control_action(eval, ["a("]), - ok = control_action(eval, ["a."]), - - %% cleanup - [{ok, _} = rabbit_amqqueue:delete(QR, false, false) || QR <- [Q, Q2]], - - unlink(Ch), - ok = rabbit_channel:shutdown(Ch), - - passed. - -test_amqp_connection_refusal() -> - [passed = test_amqp_connection_refusal(V) || - V <- [<<"AMQP",9,9,9,9>>, <<"AMQP",0,1,0,0>>, <<"XXXX",0,0,9,1>>]], - passed. - -test_amqp_connection_refusal(Header) -> - {H, P} = find_listener(), - {ok, C} = gen_tcp:connect(H, P, [binary, {active, false}]), - ok = gen_tcp:send(C, Header), - {ok, <<"AMQP",0,0,9,1>>} = gen_tcp:recv(C, 8, 100), - ok = gen_tcp:close(C), - passed. - -find_listener() -> - [#listener{host = H, port = P} | _] = - [L || L = #listener{node = N, protocol = amqp} - <- rabbit_networking:active_listeners(), - N =:= node()], - {H, P}. - -test_writer(Pid) -> - receive - {'$gen_call', From, flush} -> gen_server:reply(From, ok), - test_writer(Pid); - {send_command, Method} -> Pid ! Method, - test_writer(Pid); - shutdown -> ok - end. - -test_channel() -> - Me = self(), - Writer = spawn(fun () -> test_writer(Me) end), - {ok, Limiter} = rabbit_limiter:start_link(no_id), - {ok, Ch} = rabbit_channel:start_link( - 1, Me, Writer, Me, "", rabbit_framing_amqp_0_9_1, - user(<<"guest">>), <<"/">>, [], Me, Limiter), - {Writer, Limiter, Ch}. - -test_spawn() -> - {Writer, _Limiter, Ch} = test_channel(), - ok = rabbit_channel:do(Ch, #'channel.open'{}), - receive #'channel.open_ok'{} -> ok - after ?TIMEOUT -> throw(failed_to_receive_channel_open_ok) - end, - {Writer, Ch}. - -test_spawn(Node) -> - rpc:call(Node, ?MODULE, test_spawn_remote, []). - -%% Spawn an arbitrary long lived process, so we don't end up linking -%% the channel to the short-lived process (RPC, here) spun up by the -%% RPC server. -test_spawn_remote() -> - RPC = self(), - spawn(fun () -> - {Writer, Ch} = test_spawn(), - RPC ! {Writer, Ch}, - link(Ch), - receive - _ -> ok - end - end), - receive Res -> Res - after ?TIMEOUT -> throw(failed_to_receive_result) - end. - -user(Username) -> - #user{username = Username, - tags = [administrator], - authz_backends = [{rabbit_auth_backend_internal, none}]}. - -test_confirms() -> - {_Writer, Ch} = test_spawn(), - DeclareBindDurableQueue = - fun() -> - rabbit_channel:do(Ch, #'queue.declare'{durable = true}), - receive #'queue.declare_ok'{queue = Q0} -> - rabbit_channel:do(Ch, #'queue.bind'{ - queue = Q0, - exchange = <<"amq.direct">>, - routing_key = "magic" }), - receive #'queue.bind_ok'{} -> Q0 - after ?TIMEOUT -> throw(failed_to_bind_queue) - end - after ?TIMEOUT -> throw(failed_to_declare_queue) - end - end, - %% Declare and bind two queues - QName1 = DeclareBindDurableQueue(), - QName2 = DeclareBindDurableQueue(), - %% Get the first one's pid (we'll crash it later) - {ok, Q1} = rabbit_amqqueue:lookup(rabbit_misc:r(<<"/">>, queue, QName1)), - QPid1 = Q1#amqqueue.pid, - %% Enable confirms - rabbit_channel:do(Ch, #'confirm.select'{}), - receive - #'confirm.select_ok'{} -> ok - after ?TIMEOUT -> throw(failed_to_enable_confirms) - end, - %% Publish a message - rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"amq.direct">>, - routing_key = "magic" - }, - rabbit_basic:build_content( - #'P_basic'{delivery_mode = 2}, <<"">>)), - %% We must not kill the queue before the channel has processed the - %% 'publish'. - ok = rabbit_channel:flush(Ch), - %% Crash the queue - QPid1 ! boom, - %% Wait for a nack - receive - #'basic.nack'{} -> ok; - #'basic.ack'{} -> throw(received_ack_instead_of_nack) - after ?TIMEOUT-> throw(did_not_receive_nack) - end, - receive - #'basic.ack'{} -> throw(received_ack_when_none_expected) - after 1000 -> ok - end, - %% Cleanup - rabbit_channel:do(Ch, #'queue.delete'{queue = QName2}), - receive - #'queue.delete_ok'{} -> ok - after ?TIMEOUT -> throw(failed_to_cleanup_queue) - end, - unlink(Ch), - ok = rabbit_channel:shutdown(Ch), - - passed. - -test_with_state() -> - fhc_state = gen_server2:with_state(file_handle_cache, - fun (S) -> element(1, S) end), - passed. - -test_mcall() -> - P1 = spawn(fun gs2_test_listener/0), - register(foo, P1), - global:register_name(gfoo, P1), - - P2 = spawn(fun() -> exit(bang) end), - %% ensure P2 is dead (ignore the race setting up the monitor) - await_exit(P2), - - P3 = spawn(fun gs2_test_crasher/0), - - %% since P2 crashes almost immediately and P3 after receiving its first - %% message, we have to spawn a few more processes to handle the additional - %% cases we're interested in here - register(baz, spawn(fun gs2_test_crasher/0)), - register(bog, spawn(fun gs2_test_crasher/0)), - global:register_name(gbaz, spawn(fun gs2_test_crasher/0)), - - NoNode = rabbit_nodes:make("nonode"), - - Targets = - %% pids - [P1, P2, P3] - ++ - %% registered names - [foo, bar, baz] - ++ - %% {Name, Node} pairs - [{foo, node()}, {bar, node()}, {bog, node()}, {foo, NoNode}] - ++ - %% {global, Name} - [{global, gfoo}, {global, gbar}, {global, gbaz}], - - GoodResults = [{D, goodbye} || D <- [P1, foo, - {foo, node()}, - {global, gfoo}]], - - BadResults = [{P2, noproc}, % died before use - {P3, boom}, % died on first use - {bar, noproc}, % never registered - {baz, boom}, % died on first use - {{bar, node()}, noproc}, % never registered - {{bog, node()}, boom}, % died on first use - {{foo, NoNode}, nodedown}, % invalid node - {{global, gbar}, noproc}, % never registered globally - {{global, gbaz}, boom}], % died on first use - - {Replies, Errors} = gen_server2:mcall([{T, hello} || T <- Targets]), - true = lists:sort(Replies) == lists:sort(GoodResults), - true = lists:sort(Errors) == lists:sort(BadResults), - - %% cleanup (ignore the race setting up the monitor) - P1 ! stop, - await_exit(P1), - passed. - -await_exit(Pid) -> - MRef = erlang:monitor(process, Pid), - receive - {'DOWN', MRef, _, _, _} -> ok - end. - -gs2_test_crasher() -> - receive - {'$gen_call', _From, hello} -> exit(boom) - end. - -gs2_test_listener() -> - receive - {'$gen_call', From, hello} -> - gen_server2:reply(From, goodbye), - gs2_test_listener(); - stop -> - ok - end. - -test_statistics_event_receiver(Pid) -> - receive - Foo -> Pid ! Foo, test_statistics_event_receiver(Pid) - end. - -test_ch_statistics_receive_event(Ch, Matcher) -> - rabbit_channel:flush(Ch), - Ch ! emit_stats, - test_ch_statistics_receive_event1(Ch, Matcher). - -test_ch_statistics_receive_event1(Ch, Matcher) -> - receive #event{type = channel_stats, props = Props} -> - case Matcher(Props) of - true -> Props; - _ -> test_ch_statistics_receive_event1(Ch, Matcher) - end - after ?TIMEOUT -> throw(failed_to_receive_event) - end. - -test_ch_statistics() -> - application:set_env(rabbit, collect_statistics, fine), - - %% ATM this just tests the queue / exchange stats in channels. That's - %% by far the most complex code though. - - %% Set up a channel and queue - {_Writer, Ch} = test_spawn(), - rabbit_channel:do(Ch, #'queue.declare'{}), - QName = receive #'queue.declare_ok'{queue = Q0} -> Q0 - after ?TIMEOUT -> throw(failed_to_receive_queue_declare_ok) - end, - QRes = rabbit_misc:r(<<"/">>, queue, QName), - X = rabbit_misc:r(<<"/">>, exchange, <<"">>), - - rabbit_tests_event_receiver:start(self(), [node()], [channel_stats]), - - %% Check stats empty - Event = test_ch_statistics_receive_event(Ch, fun (_) -> true end), - [] = proplists:get_value(channel_queue_stats, Event), - [] = proplists:get_value(channel_exchange_stats, Event), - [] = proplists:get_value(channel_queue_exchange_stats, Event), - - %% Publish and get a message - rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"">>, - routing_key = QName}, - rabbit_basic:build_content(#'P_basic'{}, <<"">>)), - rabbit_channel:do(Ch, #'basic.get'{queue = QName}), - - %% Check the stats reflect that - Event2 = test_ch_statistics_receive_event( - Ch, - fun (E) -> - length(proplists:get_value( - channel_queue_exchange_stats, E)) > 0 - end), - [{QRes, [{get,1}]}] = proplists:get_value(channel_queue_stats, Event2), - [{X,[{publish,1}]}] = proplists:get_value(channel_exchange_stats, Event2), - [{{QRes,X},[{publish,1}]}] = - proplists:get_value(channel_queue_exchange_stats, Event2), - - %% Check the stats remove stuff on queue deletion - rabbit_channel:do(Ch, #'queue.delete'{queue = QName}), - Event3 = test_ch_statistics_receive_event( - Ch, - fun (E) -> - length(proplists:get_value( - channel_queue_exchange_stats, E)) == 0 - end), - - [] = proplists:get_value(channel_queue_stats, Event3), - [{X,[{publish,1}]}] = proplists:get_value(channel_exchange_stats, Event3), - [] = proplists:get_value(channel_queue_exchange_stats, Event3), - - rabbit_channel:shutdown(Ch), - rabbit_tests_event_receiver:stop(), - passed. - -test_queue_statistics_receive_event(Q, Matcher) -> - %% Q ! emit_stats, - test_queue_statistics_receive_event1(Q, Matcher). - -test_queue_statistics_receive_event1(Q, Matcher) -> - receive #event{type = queue_stats, props = Props} -> - case Matcher(Props) of - true -> Props; - _ -> test_queue_statistics_receive_event1(Q, Matcher) - end - after ?TIMEOUT -> throw(failed_to_receive_event) - end. - -test_head_message_timestamp_statistic() -> - %% Can't find a way to receive the ack here so can't test pending acks status - - application:set_env(rabbit, collect_statistics, fine), - - %% Set up a channel and queue - {_Writer, Ch} = test_spawn(), - rabbit_channel:do(Ch, #'queue.declare'{}), - QName = receive #'queue.declare_ok'{queue = Q0} -> Q0 - after ?TIMEOUT -> throw(failed_to_receive_queue_declare_ok) - end, - QRes = rabbit_misc:r(<<"/">>, queue, QName), - - {ok, Q1} = rabbit_amqqueue:lookup(QRes), - QPid = Q1#amqqueue.pid, - - %% Set up event receiver for queue - rabbit_tests_event_receiver:start(self(), [node()], [queue_stats]), - - %% Check timestamp is empty when queue is empty - Event1 = test_queue_statistics_receive_event(QPid, fun (E) -> proplists:get_value(name, E) == QRes end), - '' = proplists:get_value(head_message_timestamp, Event1), - - %% Publish two messages and check timestamp is that of first message - rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"">>, - routing_key = QName}, - rabbit_basic:build_content(#'P_basic'{timestamp = 1}, <<"">>)), - rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"">>, - routing_key = QName}, - rabbit_basic:build_content(#'P_basic'{timestamp = 2}, <<"">>)), - Event2 = test_queue_statistics_receive_event(QPid, fun (E) -> proplists:get_value(name, E) == QRes end), - 1 = proplists:get_value(head_message_timestamp, Event2), - - %% Get first message and check timestamp is that of second message - rabbit_channel:do(Ch, #'basic.get'{queue = QName, no_ack = true}), - Event3 = test_queue_statistics_receive_event(QPid, fun (E) -> proplists:get_value(name, E) == QRes end), - 2 = proplists:get_value(head_message_timestamp, Event3), - - %% Get second message and check timestamp is empty again - rabbit_channel:do(Ch, #'basic.get'{queue = QName, no_ack = true}), - Event4 = test_queue_statistics_receive_event(QPid, fun (E) -> proplists:get_value(name, E) == QRes end), - '' = proplists:get_value(head_message_timestamp, Event4), - - %% Teardown - rabbit_channel:do(Ch, #'queue.delete'{queue = QName}), - rabbit_channel:shutdown(Ch), - rabbit_tests_event_receiver:stop(), - - passed. - -test_refresh_events(SecondaryNode) -> - rabbit_tests_event_receiver:start(self(), [node(), SecondaryNode], - [channel_created, queue_created]), - - {_Writer, Ch} = test_spawn(), - expect_events(pid, Ch, channel_created), - rabbit_channel:shutdown(Ch), - - {_Writer2, Ch2} = test_spawn(SecondaryNode), - expect_events(pid, Ch2, channel_created), - rabbit_channel:shutdown(Ch2), - - {new, #amqqueue{name = QName} = Q} = - rabbit_amqqueue:declare(test_queue(), false, false, [], none), - expect_events(name, QName, queue_created), - rabbit_amqqueue:delete(Q, false, false), - - rabbit_tests_event_receiver:stop(), - passed. - -expect_events(Tag, Key, Type) -> - expect_event(Tag, Key, Type), - rabbit:force_event_refresh(make_ref()), - expect_event(Tag, Key, Type). - -expect_event(Tag, Key, Type) -> - receive #event{type = Type, props = Props} -> - case pget(Tag, Props) of - Key -> ok; - _ -> expect_event(Tag, Key, Type) - end - after ?TIMEOUT -> throw({failed_to_receive_event, Type}) - end. - -test_delegates_async(SecondaryNode) -> - Self = self(), - Sender = fun (Pid) -> Pid ! {invoked, Self} end, - - Responder = make_responder(fun ({invoked, Pid}) -> Pid ! response end), - - ok = delegate:invoke_no_result(spawn(Responder), Sender), - ok = delegate:invoke_no_result(spawn(SecondaryNode, Responder), Sender), - await_response(2), - - LocalPids = spawn_responders(node(), Responder, 10), - RemotePids = spawn_responders(SecondaryNode, Responder, 10), - ok = delegate:invoke_no_result(LocalPids ++ RemotePids, Sender), - await_response(20), - - passed. - -make_responder(FMsg) -> make_responder(FMsg, timeout). -make_responder(FMsg, Throw) -> - fun () -> - receive Msg -> FMsg(Msg) - after ?TIMEOUT -> throw(Throw) - end - end. - -spawn_responders(Node, Responder, Count) -> - [spawn(Node, Responder) || _ <- lists:seq(1, Count)]. - -await_response(0) -> - ok; -await_response(Count) -> - receive - response -> ok, - await_response(Count - 1) - after ?TIMEOUT -> throw(timeout) - end. - -must_exit(Fun) -> - try - Fun(), - throw(exit_not_thrown) - catch - exit:_ -> ok - end. - -test_delegates_sync(SecondaryNode) -> - Sender = fun (Pid) -> gen_server:call(Pid, invoked, infinity) end, - BadSender = fun (_Pid) -> exit(exception) end, - - Responder = make_responder(fun ({'$gen_call', From, invoked}) -> - gen_server:reply(From, response) - end), - - BadResponder = make_responder(fun ({'$gen_call', From, invoked}) -> - gen_server:reply(From, response) - end, bad_responder_died), - - response = delegate:invoke(spawn(Responder), Sender), - response = delegate:invoke(spawn(SecondaryNode, Responder), Sender), - - must_exit(fun () -> delegate:invoke(spawn(BadResponder), BadSender) end), - must_exit(fun () -> - delegate:invoke(spawn(SecondaryNode, BadResponder), BadSender) end), - - LocalGoodPids = spawn_responders(node(), Responder, 2), - RemoteGoodPids = spawn_responders(SecondaryNode, Responder, 2), - LocalBadPids = spawn_responders(node(), BadResponder, 2), - RemoteBadPids = spawn_responders(SecondaryNode, BadResponder, 2), - - {GoodRes, []} = delegate:invoke(LocalGoodPids ++ RemoteGoodPids, Sender), - true = lists:all(fun ({_, response}) -> true end, GoodRes), - GoodResPids = [Pid || {Pid, _} <- GoodRes], - - Good = lists:usort(LocalGoodPids ++ RemoteGoodPids), - Good = lists:usort(GoodResPids), - - {[], BadRes} = delegate:invoke(LocalBadPids ++ RemoteBadPids, BadSender), - true = lists:all(fun ({_, {exit, exception, _}}) -> true end, BadRes), - BadResPids = [Pid || {Pid, _} <- BadRes], - - Bad = lists:usort(LocalBadPids ++ RemoteBadPids), - Bad = lists:usort(BadResPids), - - MagicalPids = [rabbit_misc:string_to_pid(Str) || - Str <- ["<nonode@nohost.0.1.0>", "<nonode@nohost.0.2.0>"]], - {[], BadNodes} = delegate:invoke(MagicalPids, Sender), - true = lists:all( - fun ({_, {exit, {nodedown, nonode@nohost}, _Stack}}) -> true end, - BadNodes), - BadNodesPids = [Pid || {Pid, _} <- BadNodes], - - Magical = lists:usort(MagicalPids), - Magical = lists:usort(BadNodesPids), - - passed. - -test_queue_cleanup(_SecondaryNode) -> - {_Writer, Ch} = test_spawn(), - rabbit_channel:do(Ch, #'queue.declare'{ queue = ?CLEANUP_QUEUE_NAME }), - receive #'queue.declare_ok'{queue = ?CLEANUP_QUEUE_NAME} -> - ok - after ?TIMEOUT -> throw(failed_to_receive_queue_declare_ok) - end, - rabbit_channel:shutdown(Ch), - rabbit:stop(), - rabbit:start(), - {_Writer2, Ch2} = test_spawn(), - rabbit_channel:do(Ch2, #'queue.declare'{ passive = true, - queue = ?CLEANUP_QUEUE_NAME }), - receive - #'channel.close'{reply_code = ?NOT_FOUND} -> - ok - after ?TIMEOUT -> throw(failed_to_receive_channel_exit) - end, - rabbit_channel:shutdown(Ch2), - passed. - -test_declare_on_dead_queue(SecondaryNode) -> - QueueName = rabbit_misc:r(<<"/">>, queue, ?CLEANUP_QUEUE_NAME), - Self = self(), - Pid = spawn(SecondaryNode, - fun () -> - {new, #amqqueue{name = QueueName, pid = QPid}} = - rabbit_amqqueue:declare(QueueName, false, false, [], - none), - exit(QPid, kill), - Self ! {self(), killed, QPid} - end), - receive - {Pid, killed, OldPid} -> - Q = dead_queue_loop(QueueName, OldPid), - {ok, 0} = rabbit_amqqueue:delete(Q, false, false), - passed - after ?TIMEOUT -> throw(failed_to_create_and_kill_queue) - end. - -dead_queue_loop(QueueName, OldPid) -> - {existing, Q} = rabbit_amqqueue:declare(QueueName, false, false, [], none), - case Q#amqqueue.pid of - OldPid -> timer:sleep(25), - dead_queue_loop(QueueName, OldPid); - _ -> true = rabbit_misc:is_process_alive(Q#amqqueue.pid), - Q - end. - -%%--------------------------------------------------------------------- - -control_action(Command, Args) -> - control_action(Command, node(), Args, default_options()). - -control_action(Command, Args, NewOpts) -> - control_action(Command, node(), Args, - expand_options(default_options(), NewOpts)). - -control_action(Command, Node, Args, Opts) -> - case catch rabbit_control_main:action( - Command, Node, Args, Opts, - fun (Format, Args1) -> - io:format(Format ++ " ...~n", Args1) - end) of - ok -> - io:format("done.~n"), - ok; - Other -> - io:format("failed.~n"), - Other - end. - -control_action_opts(Raw) -> - NodeStr = atom_to_list(node()), - case rabbit_control_main:parse_arguments(Raw, NodeStr) of - {ok, {Cmd, Opts, Args}} -> - case control_action(Cmd, node(), Args, Opts) of - ok -> ok; - _ -> error - end; - _ -> - error - end. - -info_action(Command, Args, CheckVHost) -> - ok = control_action(Command, []), - if CheckVHost -> ok = control_action(Command, [], ["-p", "/"]); - true -> ok - end, - ok = control_action(Command, lists:map(fun atom_to_list/1, Args)), - {bad_argument, dummy} = control_action(Command, ["dummy"]), - ok. - -default_options() -> [{"-p", "/"}, {"-q", "false"}]. - -expand_options(As, Bs) -> - lists:foldl(fun({K, _}=A, R) -> - case proplists:is_defined(K, R) of - true -> R; - false -> [A | R] - end - end, Bs, As). - -check_parse_arguments(ExpRes, Fun, As) -> - SortRes = - fun (no_command) -> no_command; - ({ok, {C, KVs, As1}}) -> {ok, {C, lists:sort(KVs), As1}} - end, - - true = SortRes(ExpRes) =:= SortRes(Fun(As)). - -empty_files(Files) -> - [case file:read_file_info(File) of - {ok, FInfo} -> FInfo#file_info.size == 0; - Error -> Error - end || File <- Files]. - -non_empty_files(Files) -> - [case EmptyFile of - {error, Reason} -> {error, Reason}; - _ -> not(EmptyFile) - end || EmptyFile <- empty_files(Files)]. - -test_logs_working(MainLogFile, SaslLogFile) -> - ok = rabbit_log:error("foo bar"), - ok = error_logger:error_report(crash_report, [foo, bar]), - %% give the error loggers some time to catch up - timer:sleep(100), - [true, true] = non_empty_files([MainLogFile, SaslLogFile]), - ok. - -set_permissions(Path, Mode) -> - case file:read_file_info(Path) of - {ok, FInfo} -> file:write_file_info( - Path, - FInfo#file_info{mode=Mode}); - Error -> Error - end. - -clean_logs(Files, Suffix) -> - [begin - ok = delete_file(File), - ok = delete_file([File, Suffix]) - end || File <- Files], - ok. - -assert_ram_node() -> - case rabbit_mnesia:node_type() of - disc -> exit('not_ram_node'); - ram -> ok - end. - -assert_disc_node() -> - case rabbit_mnesia:node_type() of - disc -> ok; - ram -> exit('not_disc_node') - end. - -delete_file(File) -> - case file:delete(File) of - ok -> ok; - {error, enoent} -> ok; - Error -> Error - end. - -make_files_non_writable(Files) -> - [ok = file:write_file_info(File, #file_info{mode=0}) || - File <- Files], - ok. - -add_log_handlers(Handlers) -> - [ok = error_logger:add_report_handler(Handler, Args) || - {Handler, Args} <- Handlers], - ok. - -%% sasl_report_file_h returns [] during terminate -%% see: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/error_logger_file_h.erl#L98 -%% -%% error_logger_file_h returns ok since OTP 18.1 -%% see: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/error_logger_file_h.erl#L98 -delete_log_handlers(Handlers) -> - [ok_or_empty_list(error_logger:delete_report_handler(Handler)) - || Handler <- Handlers], - ok. - -ok_or_empty_list([]) -> - []; -ok_or_empty_list(ok) -> - ok. - -test_supervisor_delayed_restart() -> - test_sup:test_supervisor_delayed_restart(). - -test_file_handle_cache() -> - %% test copying when there is just one spare handle - Limit = file_handle_cache:get_limit(), - ok = file_handle_cache:set_limit(5), %% 1 or 2 sockets, 2 msg_stores - TmpDir = filename:join(rabbit_mnesia:dir(), "tmp"), - ok = filelib:ensure_dir(filename:join(TmpDir, "nothing")), - [Src1, Dst1, Src2, Dst2] = Files = - [filename:join(TmpDir, Str) || Str <- ["file1", "file2", "file3", "file4"]], - Content = <<"foo">>, - CopyFun = fun (Src, Dst) -> - {ok, Hdl} = prim_file:open(Src, [binary, write]), - ok = prim_file:write(Hdl, Content), - ok = prim_file:sync(Hdl), - prim_file:close(Hdl), - - {ok, SrcHdl} = file_handle_cache:open(Src, [read], []), - {ok, DstHdl} = file_handle_cache:open(Dst, [write], []), - Size = size(Content), - {ok, Size} = file_handle_cache:copy(SrcHdl, DstHdl, Size), - ok = file_handle_cache:delete(SrcHdl), - ok = file_handle_cache:delete(DstHdl) - end, - Pid = spawn(fun () -> {ok, Hdl} = file_handle_cache:open( - filename:join(TmpDir, "file5"), - [write], []), - receive {next, Pid1} -> Pid1 ! {next, self()} end, - file_handle_cache:delete(Hdl), - %% This will block and never return, so we - %% exercise the fhc tidying up the pending - %% queue on the death of a process. - ok = CopyFun(Src1, Dst1) - end), - ok = CopyFun(Src1, Dst1), - ok = file_handle_cache:set_limit(2), - Pid ! {next, self()}, - receive {next, Pid} -> ok end, - timer:sleep(100), - Pid1 = spawn(fun () -> CopyFun(Src2, Dst2) end), - timer:sleep(100), - erlang:monitor(process, Pid), - erlang:monitor(process, Pid1), - exit(Pid, kill), - exit(Pid1, kill), - receive {'DOWN', _MRef, process, Pid, _Reason} -> ok end, - receive {'DOWN', _MRef1, process, Pid1, _Reason1} -> ok end, - [file:delete(File) || File <- Files], - ok = file_handle_cache:set_limit(Limit), - passed. - -test_backing_queue() -> - case application:get_env(rabbit, backing_queue_module) of - {ok, rabbit_priority_queue} -> - {ok, FileSizeLimit} = - application:get_env(rabbit, msg_store_file_size_limit), - application:set_env(rabbit, msg_store_file_size_limit, 512), - {ok, MaxJournal} = - application:get_env(rabbit, queue_index_max_journal_entries), - application:set_env(rabbit, queue_index_max_journal_entries, 128), - passed = test_msg_store(), - application:set_env(rabbit, msg_store_file_size_limit, - FileSizeLimit), - [begin - application:set_env( - rabbit, queue_index_embed_msgs_below, Bytes), - passed = test_queue_index(), - passed = test_queue_index_props(), - passed = test_variable_queue(), - passed = test_variable_queue_delete_msg_store_files_callback(), - passed = test_queue_recover() - end || Bytes <- [0, 1024]], - application:set_env(rabbit, queue_index_max_journal_entries, - MaxJournal), - %% We will have restarted the message store, and thus changed - %% the order of the children of rabbit_sup. This will cause - %% problems if there are subsequent failures - see bug 24262. - ok = restart_app(), - passed; - _ -> - passed - end. - -restart_msg_store_empty() -> - ok = rabbit_variable_queue:stop_msg_store(), - ok = rabbit_variable_queue:start_msg_store( - undefined, {fun (ok) -> finished end, ok}). - -msg_id_bin(X) -> - erlang:md5(term_to_binary(X)). - -msg_store_client_init(MsgStore, Ref) -> - rabbit_msg_store:client_init(MsgStore, Ref, undefined, undefined). - -on_disk_capture() -> - receive - {await, MsgIds, Pid} -> on_disk_capture([], MsgIds, Pid); - stop -> done - end. - -on_disk_capture([_|_], _Awaiting, Pid) -> - Pid ! {self(), surplus}; -on_disk_capture(OnDisk, Awaiting, Pid) -> - receive - {on_disk, MsgIdsS} -> - MsgIds = gb_sets:to_list(MsgIdsS), - on_disk_capture(OnDisk ++ (MsgIds -- Awaiting), Awaiting -- MsgIds, - Pid); - stop -> - done - after (case Awaiting of [] -> 200; _ -> ?TIMEOUT end) -> - case Awaiting of - [] -> Pid ! {self(), arrived}, on_disk_capture(); - _ -> Pid ! {self(), timeout} - end - end. - -on_disk_await(Pid, MsgIds) when is_list(MsgIds) -> - Pid ! {await, MsgIds, self()}, - receive - {Pid, arrived} -> ok; - {Pid, Error} -> Error - end. - -on_disk_stop(Pid) -> - MRef = erlang:monitor(process, Pid), - Pid ! stop, - receive {'DOWN', MRef, process, Pid, _Reason} -> - ok - end. - -msg_store_client_init_capture(MsgStore, Ref) -> - Pid = spawn(fun on_disk_capture/0), - {Pid, rabbit_msg_store:client_init( - MsgStore, Ref, fun (MsgIds, _ActionTaken) -> - Pid ! {on_disk, MsgIds} - end, undefined)}. - -msg_store_contains(Atom, MsgIds, MSCState) -> - Atom = lists:foldl( - fun (MsgId, Atom1) when Atom1 =:= Atom -> - rabbit_msg_store:contains(MsgId, MSCState) end, - Atom, MsgIds). - -msg_store_read(MsgIds, MSCState) -> - lists:foldl(fun (MsgId, MSCStateM) -> - {{ok, MsgId}, MSCStateN} = rabbit_msg_store:read( - MsgId, MSCStateM), - MSCStateN - end, MSCState, MsgIds). - -msg_store_write(MsgIds, MSCState) -> - ok = lists:foldl(fun (MsgId, ok) -> - rabbit_msg_store:write(MsgId, MsgId, MSCState) - end, ok, MsgIds). - -msg_store_write_flow(MsgIds, MSCState) -> - ok = lists:foldl(fun (MsgId, ok) -> - rabbit_msg_store:write_flow(MsgId, MsgId, MSCState) - end, ok, MsgIds). - -msg_store_remove(MsgIds, MSCState) -> - rabbit_msg_store:remove(MsgIds, MSCState). - -msg_store_remove(MsgStore, Ref, MsgIds) -> - with_msg_store_client(MsgStore, Ref, - fun (MSCStateM) -> - ok = msg_store_remove(MsgIds, MSCStateM), - MSCStateM - end). - -with_msg_store_client(MsgStore, Ref, Fun) -> - rabbit_msg_store:client_terminate( - Fun(msg_store_client_init(MsgStore, Ref))). - -foreach_with_msg_store_client(MsgStore, Ref, Fun, L) -> - rabbit_msg_store:client_terminate( - lists:foldl(fun (MsgId, MSCState) -> Fun(MsgId, MSCState) end, - msg_store_client_init(MsgStore, Ref), L)). - -test_msg_store() -> - restart_msg_store_empty(), - MsgIds = [msg_id_bin(M) || M <- lists:seq(1,100)], - {MsgIds1stHalf, MsgIds2ndHalf} = lists:split(length(MsgIds) div 2, MsgIds), - Ref = rabbit_guid:gen(), - {Cap, MSCState} = msg_store_client_init_capture( - ?PERSISTENT_MSG_STORE, Ref), - Ref2 = rabbit_guid:gen(), - {Cap2, MSC2State} = msg_store_client_init_capture( - ?PERSISTENT_MSG_STORE, Ref2), - %% check we don't contain any of the msgs we're about to publish - false = msg_store_contains(false, MsgIds, MSCState), - %% test confirm logic - passed = test_msg_store_confirms([hd(MsgIds)], Cap, MSCState), - %% check we don't contain any of the msgs we're about to publish - false = msg_store_contains(false, MsgIds, MSCState), - %% publish the first half - ok = msg_store_write(MsgIds1stHalf, MSCState), - %% sync on the first half - ok = on_disk_await(Cap, MsgIds1stHalf), - %% publish the second half - ok = msg_store_write(MsgIds2ndHalf, MSCState), - %% check they're all in there - true = msg_store_contains(true, MsgIds, MSCState), - %% publish the latter half twice so we hit the caching and ref - %% count code. We need to do this through a 2nd client since a - %% single client is not supposed to write the same message more - %% than once without first removing it. - ok = msg_store_write(MsgIds2ndHalf, MSC2State), - %% check they're still all in there - true = msg_store_contains(true, MsgIds, MSCState), - %% sync on the 2nd half - ok = on_disk_await(Cap2, MsgIds2ndHalf), - %% cleanup - ok = on_disk_stop(Cap2), - ok = rabbit_msg_store:client_delete_and_terminate(MSC2State), - ok = on_disk_stop(Cap), - %% read them all - MSCState1 = msg_store_read(MsgIds, MSCState), - %% read them all again - this will hit the cache, not disk - MSCState2 = msg_store_read(MsgIds, MSCState1), - %% remove them all - ok = msg_store_remove(MsgIds, MSCState2), - %% check first half doesn't exist - false = msg_store_contains(false, MsgIds1stHalf, MSCState2), - %% check second half does exist - true = msg_store_contains(true, MsgIds2ndHalf, MSCState2), - %% read the second half again - MSCState3 = msg_store_read(MsgIds2ndHalf, MSCState2), - %% read the second half again, just for fun (aka code coverage) - MSCState4 = msg_store_read(MsgIds2ndHalf, MSCState3), - ok = rabbit_msg_store:client_terminate(MSCState4), - %% stop and restart, preserving every other msg in 2nd half - ok = rabbit_variable_queue:stop_msg_store(), - ok = rabbit_variable_queue:start_msg_store( - [], {fun ([]) -> finished; - ([MsgId|MsgIdsTail]) - when length(MsgIdsTail) rem 2 == 0 -> - {MsgId, 1, MsgIdsTail}; - ([MsgId|MsgIdsTail]) -> - {MsgId, 0, MsgIdsTail} - end, MsgIds2ndHalf}), - MSCState5 = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), - %% check we have the right msgs left - lists:foldl( - fun (MsgId, Bool) -> - not(Bool = rabbit_msg_store:contains(MsgId, MSCState5)) - end, false, MsgIds2ndHalf), - ok = rabbit_msg_store:client_terminate(MSCState5), - %% restart empty - restart_msg_store_empty(), - MSCState6 = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), - %% check we don't contain any of the msgs - false = msg_store_contains(false, MsgIds, MSCState6), - %% publish the first half again - ok = msg_store_write(MsgIds1stHalf, MSCState6), - %% this should force some sort of sync internally otherwise misread - ok = rabbit_msg_store:client_terminate( - msg_store_read(MsgIds1stHalf, MSCState6)), - MSCState7 = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), - ok = msg_store_remove(MsgIds1stHalf, MSCState7), - ok = rabbit_msg_store:client_terminate(MSCState7), - %% restart empty - restart_msg_store_empty(), %% now safe to reuse msg_ids - %% push a lot of msgs in... at least 100 files worth - {ok, FileSize} = application:get_env(rabbit, msg_store_file_size_limit), - PayloadSizeBits = 65536, - BigCount = trunc(100 * FileSize / (PayloadSizeBits div 8)), - MsgIdsBig = [msg_id_bin(X) || X <- lists:seq(1, BigCount)], - Payload = << 0:PayloadSizeBits >>, - ok = with_msg_store_client( - ?PERSISTENT_MSG_STORE, Ref, - fun (MSCStateM) -> - [ok = rabbit_msg_store:write(MsgId, Payload, MSCStateM) || - MsgId <- MsgIdsBig], - MSCStateM - end), - %% now read them to ensure we hit the fast client-side reading - ok = foreach_with_msg_store_client( - ?PERSISTENT_MSG_STORE, Ref, - fun (MsgId, MSCStateM) -> - {{ok, Payload}, MSCStateN} = rabbit_msg_store:read( - MsgId, MSCStateM), - MSCStateN - end, MsgIdsBig), - %% .., then 3s by 1... - ok = msg_store_remove(?PERSISTENT_MSG_STORE, Ref, - [msg_id_bin(X) || X <- lists:seq(BigCount, 1, -3)]), - %% .., then remove 3s by 2, from the young end first. This hits - %% GC (under 50% good data left, but no empty files. Must GC). - ok = msg_store_remove(?PERSISTENT_MSG_STORE, Ref, - [msg_id_bin(X) || X <- lists:seq(BigCount-1, 1, -3)]), - %% .., then remove 3s by 3, from the young end first. This hits - %% GC... - ok = msg_store_remove(?PERSISTENT_MSG_STORE, Ref, - [msg_id_bin(X) || X <- lists:seq(BigCount-2, 1, -3)]), - %% ensure empty - ok = with_msg_store_client( - ?PERSISTENT_MSG_STORE, Ref, - fun (MSCStateM) -> - false = msg_store_contains(false, MsgIdsBig, MSCStateM), - MSCStateM - end), - %% - passed = test_msg_store_client_delete_and_terminate(), - %% restart empty - restart_msg_store_empty(), - passed. - -test_msg_store_confirms(MsgIds, Cap, MSCState) -> - %% write -> confirmed - ok = msg_store_write(MsgIds, MSCState), - ok = on_disk_await(Cap, MsgIds), - %% remove -> _ - ok = msg_store_remove(MsgIds, MSCState), - ok = on_disk_await(Cap, []), - %% write, remove -> confirmed - ok = msg_store_write(MsgIds, MSCState), - ok = msg_store_remove(MsgIds, MSCState), - ok = on_disk_await(Cap, MsgIds), - %% write, remove, write -> confirmed, confirmed - ok = msg_store_write(MsgIds, MSCState), - ok = msg_store_remove(MsgIds, MSCState), - ok = msg_store_write(MsgIds, MSCState), - ok = on_disk_await(Cap, MsgIds ++ MsgIds), - %% remove, write -> confirmed - ok = msg_store_remove(MsgIds, MSCState), - ok = msg_store_write(MsgIds, MSCState), - ok = on_disk_await(Cap, MsgIds), - %% remove, write, remove -> confirmed - ok = msg_store_remove(MsgIds, MSCState), - ok = msg_store_write(MsgIds, MSCState), - ok = msg_store_remove(MsgIds, MSCState), - ok = on_disk_await(Cap, MsgIds), - %% confirmation on timer-based sync - passed = test_msg_store_confirm_timer(), - passed. - -test_msg_store_confirm_timer() -> - Ref = rabbit_guid:gen(), - MsgId = msg_id_bin(1), - Self = self(), - MSCState = rabbit_msg_store:client_init( - ?PERSISTENT_MSG_STORE, Ref, - fun (MsgIds, _ActionTaken) -> - case gb_sets:is_member(MsgId, MsgIds) of - true -> Self ! on_disk; - false -> ok - end - end, undefined), - ok = msg_store_write([MsgId], MSCState), - ok = msg_store_keep_busy_until_confirm([msg_id_bin(2)], MSCState, false), - ok = msg_store_remove([MsgId], MSCState), - ok = rabbit_msg_store:client_delete_and_terminate(MSCState), - passed. - -msg_store_keep_busy_until_confirm(MsgIds, MSCState, Blocked) -> - After = case Blocked of - false -> 0; - true -> ?MAX_WAIT - end, - Recurse = fun () -> msg_store_keep_busy_until_confirm( - MsgIds, MSCState, credit_flow:blocked()) end, - receive - on_disk -> ok; - {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - Recurse() - after After -> - ok = msg_store_write_flow(MsgIds, MSCState), - ok = msg_store_remove(MsgIds, MSCState), - Recurse() - end. - -test_msg_store_client_delete_and_terminate() -> - restart_msg_store_empty(), - MsgIds = [msg_id_bin(M) || M <- lists:seq(1, 10)], - Ref = rabbit_guid:gen(), - MSCState = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), - ok = msg_store_write(MsgIds, MSCState), - %% test the 'dying client' fast path for writes - ok = rabbit_msg_store:client_delete_and_terminate(MSCState), - passed. - -queue_name(Name) -> - rabbit_misc:r(<<"/">>, queue, Name). - -test_queue() -> - queue_name(<<"test">>). - -init_test_queue() -> - TestQueue = test_queue(), - PRef = rabbit_guid:gen(), - PersistentClient = msg_store_client_init(?PERSISTENT_MSG_STORE, PRef), - Res = rabbit_queue_index:recover( - TestQueue, [], false, - fun (MsgId) -> - rabbit_msg_store:contains(MsgId, PersistentClient) - end, - fun nop/1, fun nop/1), - ok = rabbit_msg_store:client_delete_and_terminate(PersistentClient), - Res. - -restart_test_queue(Qi) -> - _ = rabbit_queue_index:terminate([], Qi), - ok = rabbit_variable_queue:stop(), - {ok, _} = rabbit_variable_queue:start([test_queue()]), - init_test_queue(). - -empty_test_queue() -> - ok = rabbit_variable_queue:stop(), - {ok, _} = rabbit_variable_queue:start([]), - {0, 0, Qi} = init_test_queue(), - _ = rabbit_queue_index:delete_and_terminate(Qi), - ok. - -with_empty_test_queue(Fun) -> - ok = empty_test_queue(), - {0, 0, Qi} = init_test_queue(), - rabbit_queue_index:delete_and_terminate(Fun(Qi)). - -restart_app() -> - rabbit:stop(), - rabbit:start(). - -queue_index_publish(SeqIds, Persistent, Qi) -> - Ref = rabbit_guid:gen(), - MsgStore = case Persistent of - true -> ?PERSISTENT_MSG_STORE; - false -> ?TRANSIENT_MSG_STORE - end, - MSCState = msg_store_client_init(MsgStore, Ref), - {A, B = [{_SeqId, LastMsgIdWritten} | _]} = - lists:foldl( - fun (SeqId, {QiN, SeqIdsMsgIdsAcc}) -> - MsgId = rabbit_guid:gen(), - QiM = rabbit_queue_index:publish( - MsgId, SeqId, #message_properties{size = 10}, - Persistent, infinity, QiN), - ok = rabbit_msg_store:write(MsgId, MsgId, MSCState), - {QiM, [{SeqId, MsgId} | SeqIdsMsgIdsAcc]} - end, {Qi, []}, SeqIds), - %% do this just to force all of the publishes through to the msg_store: - true = rabbit_msg_store:contains(LastMsgIdWritten, MSCState), - ok = rabbit_msg_store:client_delete_and_terminate(MSCState), - {A, B}. - -verify_read_with_published(_Delivered, _Persistent, [], _) -> - ok; -verify_read_with_published(Delivered, Persistent, - [{MsgId, SeqId, _Props, Persistent, Delivered}|Read], - [{SeqId, MsgId}|Published]) -> - verify_read_with_published(Delivered, Persistent, Read, Published); -verify_read_with_published(_Delivered, _Persistent, _Read, _Published) -> - ko. - -test_queue_index_props() -> - with_empty_test_queue( - fun(Qi0) -> - MsgId = rabbit_guid:gen(), - Props = #message_properties{expiry=12345, size = 10}, - Qi1 = rabbit_queue_index:publish( - MsgId, 1, Props, true, infinity, Qi0), - {[{MsgId, 1, Props, _, _}], Qi2} = - rabbit_queue_index:read(1, 2, Qi1), - Qi2 - end), - - ok = rabbit_variable_queue:stop(), - {ok, _} = rabbit_variable_queue:start([]), - - passed. - -test_queue_index() -> - SegmentSize = rabbit_queue_index:next_segment_boundary(0), - TwoSegs = SegmentSize + SegmentSize, - MostOfASegment = trunc(SegmentSize*0.75), - SeqIdsA = lists:seq(0, MostOfASegment-1), - SeqIdsB = lists:seq(MostOfASegment, 2*MostOfASegment), - SeqIdsC = lists:seq(0, trunc(SegmentSize/2)), - SeqIdsD = lists:seq(0, SegmentSize*4), - - with_empty_test_queue( - fun (Qi0) -> - {0, 0, Qi1} = rabbit_queue_index:bounds(Qi0), - {Qi2, SeqIdsMsgIdsA} = queue_index_publish(SeqIdsA, false, Qi1), - {0, SegmentSize, Qi3} = rabbit_queue_index:bounds(Qi2), - {ReadA, Qi4} = rabbit_queue_index:read(0, SegmentSize, Qi3), - ok = verify_read_with_published(false, false, ReadA, - lists:reverse(SeqIdsMsgIdsA)), - %% should get length back as 0, as all the msgs were transient - {0, 0, Qi6} = restart_test_queue(Qi4), - {0, 0, Qi7} = rabbit_queue_index:bounds(Qi6), - {Qi8, SeqIdsMsgIdsB} = queue_index_publish(SeqIdsB, true, Qi7), - {0, TwoSegs, Qi9} = rabbit_queue_index:bounds(Qi8), - {ReadB, Qi10} = rabbit_queue_index:read(0, SegmentSize, Qi9), - ok = verify_read_with_published(false, true, ReadB, - lists:reverse(SeqIdsMsgIdsB)), - %% should get length back as MostOfASegment - LenB = length(SeqIdsB), - BytesB = LenB * 10, - {LenB, BytesB, Qi12} = restart_test_queue(Qi10), - {0, TwoSegs, Qi13} = rabbit_queue_index:bounds(Qi12), - Qi14 = rabbit_queue_index:deliver(SeqIdsB, Qi13), - {ReadC, Qi15} = rabbit_queue_index:read(0, SegmentSize, Qi14), - ok = verify_read_with_published(true, true, ReadC, - lists:reverse(SeqIdsMsgIdsB)), - Qi16 = rabbit_queue_index:ack(SeqIdsB, Qi15), - Qi17 = rabbit_queue_index:flush(Qi16), - %% Everything will have gone now because #pubs == #acks - {0, 0, Qi18} = rabbit_queue_index:bounds(Qi17), - %% should get length back as 0 because all persistent - %% msgs have been acked - {0, 0, Qi19} = restart_test_queue(Qi18), - Qi19 - end), - - %% These next bits are just to hit the auto deletion of segment files. - %% First, partials: - %% a) partial pub+del+ack, then move to new segment - with_empty_test_queue( - fun (Qi0) -> - {Qi1, _SeqIdsMsgIdsC} = queue_index_publish(SeqIdsC, - false, Qi0), - Qi2 = rabbit_queue_index:deliver(SeqIdsC, Qi1), - Qi3 = rabbit_queue_index:ack(SeqIdsC, Qi2), - Qi4 = rabbit_queue_index:flush(Qi3), - {Qi5, _SeqIdsMsgIdsC1} = queue_index_publish([SegmentSize], - false, Qi4), - Qi5 - end), - - %% b) partial pub+del, then move to new segment, then ack all in old segment - with_empty_test_queue( - fun (Qi0) -> - {Qi1, _SeqIdsMsgIdsC2} = queue_index_publish(SeqIdsC, - false, Qi0), - Qi2 = rabbit_queue_index:deliver(SeqIdsC, Qi1), - {Qi3, _SeqIdsMsgIdsC3} = queue_index_publish([SegmentSize], - false, Qi2), - Qi4 = rabbit_queue_index:ack(SeqIdsC, Qi3), - rabbit_queue_index:flush(Qi4) - end), - - %% c) just fill up several segments of all pubs, then +dels, then +acks - with_empty_test_queue( - fun (Qi0) -> - {Qi1, _SeqIdsMsgIdsD} = queue_index_publish(SeqIdsD, - false, Qi0), - Qi2 = rabbit_queue_index:deliver(SeqIdsD, Qi1), - Qi3 = rabbit_queue_index:ack(SeqIdsD, Qi2), - rabbit_queue_index:flush(Qi3) - end), - - %% d) get messages in all states to a segment, then flush, then do - %% the same again, don't flush and read. This will hit all - %% possibilities in combining the segment with the journal. - with_empty_test_queue( - fun (Qi0) -> - {Qi1, [Seven,Five,Four|_]} = queue_index_publish([0,1,2,4,5,7], - false, Qi0), - Qi2 = rabbit_queue_index:deliver([0,1,4], Qi1), - Qi3 = rabbit_queue_index:ack([0], Qi2), - Qi4 = rabbit_queue_index:flush(Qi3), - {Qi5, [Eight,Six|_]} = queue_index_publish([3,6,8], false, Qi4), - Qi6 = rabbit_queue_index:deliver([2,3,5,6], Qi5), - Qi7 = rabbit_queue_index:ack([1,2,3], Qi6), - {[], Qi8} = rabbit_queue_index:read(0, 4, Qi7), - {ReadD, Qi9} = rabbit_queue_index:read(4, 7, Qi8), - ok = verify_read_with_published(true, false, ReadD, - [Four, Five, Six]), - {ReadE, Qi10} = rabbit_queue_index:read(7, 9, Qi9), - ok = verify_read_with_published(false, false, ReadE, - [Seven, Eight]), - Qi10 - end), - - %% e) as for (d), but use terminate instead of read, which will - %% exercise journal_minus_segment, not segment_plus_journal. - with_empty_test_queue( - fun (Qi0) -> - {Qi1, _SeqIdsMsgIdsE} = queue_index_publish([0,1,2,4,5,7], - true, Qi0), - Qi2 = rabbit_queue_index:deliver([0,1,4], Qi1), - Qi3 = rabbit_queue_index:ack([0], Qi2), - {5, 50, Qi4} = restart_test_queue(Qi3), - {Qi5, _SeqIdsMsgIdsF} = queue_index_publish([3,6,8], true, Qi4), - Qi6 = rabbit_queue_index:deliver([2,3,5,6], Qi5), - Qi7 = rabbit_queue_index:ack([1,2,3], Qi6), - {5, 50, Qi8} = restart_test_queue(Qi7), - Qi8 - end), - - ok = rabbit_variable_queue:stop(), - {ok, _} = rabbit_variable_queue:start([]), - - passed. - -variable_queue_init(Q, Recover) -> - rabbit_variable_queue:init( - Q, case Recover of - true -> non_clean_shutdown; - false -> new - end, fun nop/2, fun nop/2, fun nop/1, fun nop/1). - -variable_queue_publish(IsPersistent, Count, VQ) -> - variable_queue_publish(IsPersistent, Count, fun (_N, P) -> P end, VQ). - -variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> - variable_queue_publish(IsPersistent, 1, Count, PropFun, - fun (_N) -> <<>> end, VQ). - -variable_queue_publish(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> - variable_queue_wait_for_shuffling_end( - lists:foldl( - fun (N, VQN) -> - rabbit_variable_queue:publish( - rabbit_basic:message( - rabbit_misc:r(<<>>, exchange, <<>>), - <<>>, #'P_basic'{delivery_mode = case IsPersistent of - true -> 2; - false -> 1 - end}, - PayloadFun(N)), - PropFun(N, #message_properties{size = 10}), - false, self(), noflow, VQN) - end, VQ, lists:seq(Start, Start + Count - 1))). - -variable_queue_batch_publish(IsPersistent, Count, VQ) -> - variable_queue_batch_publish(IsPersistent, Count, fun (_N, P) -> P end, VQ). - -variable_queue_batch_publish(IsPersistent, Count, PropFun, VQ) -> - variable_queue_batch_publish(IsPersistent, 1, Count, PropFun, - fun (_N) -> <<>> end, VQ). - -variable_queue_batch_publish(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> - variable_queue_batch_publish0(IsPersistent, Start, Count, PropFun, - PayloadFun, fun make_publish/4, - fun rabbit_variable_queue:batch_publish/4, - VQ). - -variable_queue_batch_publish_delivered(IsPersistent, Count, VQ) -> - variable_queue_batch_publish_delivered(IsPersistent, Count, fun (_N, P) -> P end, VQ). - -variable_queue_batch_publish_delivered(IsPersistent, Count, PropFun, VQ) -> - variable_queue_batch_publish_delivered(IsPersistent, 1, Count, PropFun, - fun (_N) -> <<>> end, VQ). - -variable_queue_batch_publish_delivered(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> - variable_queue_batch_publish0(IsPersistent, Start, Count, PropFun, - PayloadFun, fun make_publish_delivered/4, - fun rabbit_variable_queue:batch_publish_delivered/4, - VQ). - -variable_queue_batch_publish0(IsPersistent, Start, Count, PropFun, PayloadFun, - MakePubFun, PubFun, VQ) -> - Publishes = - [MakePubFun(IsPersistent, PayloadFun, PropFun, N) - || N <- lists:seq(Start, Start + Count - 1)], - Res = PubFun(Publishes, self(), noflow, VQ), - VQ1 = pub_res(Res), - variable_queue_wait_for_shuffling_end(VQ1). - -pub_res({_, VQS}) -> - VQS; -pub_res(VQS) -> - VQS. - -make_publish(IsPersistent, PayloadFun, PropFun, N) -> - {rabbit_basic:message( - rabbit_misc:r(<<>>, exchange, <<>>), - <<>>, #'P_basic'{delivery_mode = case IsPersistent of - true -> 2; - false -> 1 - end}, - PayloadFun(N)), - PropFun(N, #message_properties{size = 10}), - false}. - -make_publish_delivered(IsPersistent, PayloadFun, PropFun, N) -> - {rabbit_basic:message( - rabbit_misc:r(<<>>, exchange, <<>>), - <<>>, #'P_basic'{delivery_mode = case IsPersistent of - true -> 2; - false -> 1 - end}, - PayloadFun(N)), - PropFun(N, #message_properties{size = 10})}. - -variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> - lists:foldl(fun (N, {VQN, AckTagsAcc}) -> - Rem = Len - N, - {{#basic_message { is_persistent = IsPersistent }, - IsDelivered, AckTagN}, VQM} = - rabbit_variable_queue:fetch(true, VQN), - Rem = rabbit_variable_queue:len(VQM), - {VQM, [AckTagN | AckTagsAcc]} - end, {VQ, []}, lists:seq(1, Count)). - -variable_queue_set_ram_duration_target(Duration, VQ) -> - variable_queue_wait_for_shuffling_end( - rabbit_variable_queue:set_ram_duration_target(Duration, VQ)). - -assert_prop(List, Prop, Value) -> - case proplists:get_value(Prop, List)of - Value -> ok; - _ -> {exit, Prop, exp, Value, List} - end. - -assert_props(List, PropVals) -> - [assert_prop(List, Prop, Value) || {Prop, Value} <- PropVals]. - -test_amqqueue(Durable) -> - (rabbit_amqqueue:pseudo_queue(test_queue(), self())) - #amqqueue { durable = Durable }. - -with_fresh_variable_queue(Fun) -> - Ref = make_ref(), - Me = self(), - %% Run in a separate process since rabbit_msg_store will send - %% bump_credit messages and we want to ignore them - spawn_link(fun() -> - ok = empty_test_queue(), - VQ = variable_queue_init(test_amqqueue(true), false), - S0 = variable_queue_status(VQ), - assert_props(S0, [{q1, 0}, {q2, 0}, - {delta, - {delta, undefined, 0, undefined}}, - {q3, 0}, {q4, 0}, - {len, 0}]), - try - _ = rabbit_variable_queue:delete_and_terminate( - shutdown, Fun(VQ)), - Me ! Ref - catch - Type:Error -> - Me ! {Ref, Type, Error, erlang:get_stacktrace()} - end - end), - receive - Ref -> ok; - {Ref, Type, Error, ST} -> exit({Type, Error, ST}) - end, - passed. - -publish_and_confirm(Q, Payload, Count) -> - Seqs = lists:seq(1, Count), - [begin - Msg = rabbit_basic:message(rabbit_misc:r(<<>>, exchange, <<>>), - <<>>, #'P_basic'{delivery_mode = 2}, - Payload), - Delivery = #delivery{mandatory = false, sender = self(), - confirm = true, message = Msg, msg_seq_no = Seq, - flow = noflow}, - _QPids = rabbit_amqqueue:deliver([Q], Delivery) - end || Seq <- Seqs], - wait_for_confirms(gb_sets:from_list(Seqs)). - -wait_for_confirms(Unconfirmed) -> - case gb_sets:is_empty(Unconfirmed) of - true -> ok; - false -> receive {'$gen_cast', {confirm, Confirmed, _}} -> - wait_for_confirms( - rabbit_misc:gb_sets_difference( - Unconfirmed, gb_sets:from_list(Confirmed))) - after ?TIMEOUT -> exit(timeout_waiting_for_confirm) - end - end. - -test_variable_queue() -> - [passed = with_fresh_variable_queue(F) || - F <- [fun test_variable_queue_dynamic_duration_change/1, - fun test_variable_queue_partial_segments_delta_thing/1, - fun test_variable_queue_all_the_bits_not_covered_elsewhere1/1, - fun test_variable_queue_all_the_bits_not_covered_elsewhere2/1, - fun test_drop/1, - fun test_variable_queue_fold_msg_on_disk/1, - fun test_dropfetchwhile/1, - fun test_dropwhile_varying_ram_duration/1, - fun test_fetchwhile_varying_ram_duration/1, - fun test_variable_queue_ack_limiting/1, - fun test_variable_queue_purge/1, - fun test_variable_queue_requeue/1, - fun test_variable_queue_requeue_ram_beta/1, - fun test_variable_queue_fold/1, - fun test_variable_queue_batch_publish/1, - fun test_variable_queue_batch_publish_delivered/1]], - passed. - -test_variable_queue_batch_publish(VQ) -> - Count = 10, - VQ1 = variable_queue_batch_publish(true, Count, VQ), - Count = rabbit_variable_queue:len(VQ1), - VQ1. - -test_variable_queue_batch_publish_delivered(VQ) -> - Count = 10, - VQ1 = variable_queue_batch_publish_delivered(true, Count, VQ), - Count = rabbit_variable_queue:depth(VQ1), - VQ1. - -test_variable_queue_fold(VQ0) -> - {PendingMsgs, RequeuedMsgs, FreshMsgs, VQ1} = - variable_queue_with_holes(VQ0), - Count = rabbit_variable_queue:depth(VQ1), - Msgs = lists:sort(PendingMsgs ++ RequeuedMsgs ++ FreshMsgs), - lists:foldl(fun (Cut, VQ2) -> - test_variable_queue_fold(Cut, Msgs, PendingMsgs, VQ2) - end, VQ1, [0, 1, 2, Count div 2, - Count - 1, Count, Count + 1, Count * 2]). - -test_variable_queue_fold(Cut, Msgs, PendingMsgs, VQ0) -> - {Acc, VQ1} = rabbit_variable_queue:fold( - fun (M, _, Pending, A) -> - MInt = msg2int(M), - Pending = lists:member(MInt, PendingMsgs), %% assert - case MInt =< Cut of - true -> {cont, [MInt | A]}; - false -> {stop, A} - end - end, [], VQ0), - Expected = lists:takewhile(fun (I) -> I =< Cut end, Msgs), - Expected = lists:reverse(Acc), %% assertion - VQ1. - -msg2int(#basic_message{content = #content{ payload_fragments_rev = P}}) -> - binary_to_term(list_to_binary(lists:reverse(P))). - -ack_subset(AckSeqs, Interval, Rem) -> - lists:filter(fun ({_Ack, N}) -> (N + Rem) rem Interval == 0 end, AckSeqs). - -requeue_one_by_one(Acks, VQ) -> - lists:foldl(fun (AckTag, VQN) -> - {_MsgId, VQM} = rabbit_variable_queue:requeue( - [AckTag], VQN), - VQM - end, VQ, Acks). - -%% Create a vq with messages in q1, delta, and q3, and holes (in the -%% form of pending acks) in the latter two. -variable_queue_with_holes(VQ0) -> - Interval = 2048, %% should match vq:IO_BATCH_SIZE - Count = rabbit_queue_index:next_segment_boundary(0)*2 + 2 * Interval, - Seq = lists:seq(1, Count), - VQ1 = variable_queue_set_ram_duration_target(0, VQ0), - VQ2 = variable_queue_publish( - false, 1, Count, - fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - {VQ3, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ2), - Acks = lists:reverse(AcksR), - AckSeqs = lists:zip(Acks, Seq), - [{Subset1, _Seq1}, {Subset2, _Seq2}, {Subset3, Seq3}] = - [lists:unzip(ack_subset(AckSeqs, Interval, I)) || I <- [0, 1, 2]], - %% we requeue in three phases in order to exercise requeuing logic - %% in various vq states - {_MsgIds, VQ4} = rabbit_variable_queue:requeue( - Acks -- (Subset1 ++ Subset2 ++ Subset3), VQ3), - VQ5 = requeue_one_by_one(Subset1, VQ4), - %% by now we have some messages (and holes) in delta - VQ6 = requeue_one_by_one(Subset2, VQ5), - VQ7 = variable_queue_set_ram_duration_target(infinity, VQ6), - %% add the q1 tail - VQ8 = variable_queue_publish( - true, Count + 1, Interval, - fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ7), - %% assertions - [false = case V of - {delta, _, 0, _} -> true; - 0 -> true; - _ -> false - end || {K, V} <- variable_queue_status(VQ8), - lists:member(K, [q1, delta, q3])], - Depth = Count + Interval, - Depth = rabbit_variable_queue:depth(VQ8), - Len = Depth - length(Subset3), - Len = rabbit_variable_queue:len(VQ8), - {Seq3, Seq -- Seq3, lists:seq(Count + 1, Count + Interval), VQ8}. - -test_variable_queue_requeue(VQ0) -> - {_PendingMsgs, RequeuedMsgs, FreshMsgs, VQ1} = - variable_queue_with_holes(VQ0), - Msgs = - lists:zip(RequeuedMsgs, - lists:duplicate(length(RequeuedMsgs), true)) ++ - lists:zip(FreshMsgs, - lists:duplicate(length(FreshMsgs), false)), - VQ2 = lists:foldl(fun ({I, Requeued}, VQa) -> - {{M, MRequeued, _}, VQb} = - rabbit_variable_queue:fetch(true, VQa), - Requeued = MRequeued, %% assertion - I = msg2int(M), %% assertion - VQb - end, VQ1, Msgs), - {empty, VQ3} = rabbit_variable_queue:fetch(true, VQ2), - VQ3. - -%% requeue from ram_pending_ack into q3, move to delta and then empty queue -test_variable_queue_requeue_ram_beta(VQ0) -> - Count = rabbit_queue_index:next_segment_boundary(0)*2 + 2, - VQ1 = variable_queue_publish(false, Count, VQ0), - {VQ2, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ1), - {Back, Front} = lists:split(Count div 2, AcksR), - {_, VQ3} = rabbit_variable_queue:requeue(erlang:tl(Back), VQ2), - VQ4 = variable_queue_set_ram_duration_target(0, VQ3), - {_, VQ5} = rabbit_variable_queue:requeue([erlang:hd(Back)], VQ4), - VQ6 = requeue_one_by_one(Front, VQ5), - {VQ7, AcksAll} = variable_queue_fetch(Count, false, true, Count, VQ6), - {_, VQ8} = rabbit_variable_queue:ack(AcksAll, VQ7), - VQ8. - -test_variable_queue_purge(VQ0) -> - LenDepth = fun (VQ) -> - {rabbit_variable_queue:len(VQ), - rabbit_variable_queue:depth(VQ)} - end, - VQ1 = variable_queue_publish(false, 10, VQ0), - {VQ2, Acks} = variable_queue_fetch(6, false, false, 10, VQ1), - {4, VQ3} = rabbit_variable_queue:purge(VQ2), - {0, 6} = LenDepth(VQ3), - {_, VQ4} = rabbit_variable_queue:requeue(lists:sublist(Acks, 2), VQ3), - {2, 6} = LenDepth(VQ4), - VQ5 = rabbit_variable_queue:purge_acks(VQ4), - {2, 2} = LenDepth(VQ5), - VQ5. - -test_variable_queue_ack_limiting(VQ0) -> - %% start by sending in a bunch of messages - Len = 1024, - VQ1 = variable_queue_publish(false, Len, VQ0), - - %% squeeze and relax queue - Churn = Len div 32, - VQ2 = publish_fetch_and_ack(Churn, Len, VQ1), - - %% update stats for duration - {_Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), - - %% fetch half the messages - {VQ4, _AckTags} = variable_queue_fetch(Len div 2, false, false, Len, VQ3), - - VQ5 = check_variable_queue_status( - VQ4, [{len, Len div 2}, - {messages_unacknowledged_ram, Len div 2}, - {messages_ready_ram, Len div 2}, - {messages_ram, Len}]), - - %% ensure all acks go to disk on 0 duration target - VQ6 = check_variable_queue_status( - variable_queue_set_ram_duration_target(0, VQ5), - [{len, Len div 2}, - {target_ram_count, 0}, - {messages_unacknowledged_ram, 0}, - {messages_ready_ram, 0}, - {messages_ram, 0}]), - - VQ6. - -test_drop(VQ0) -> - %% start by sending a messages - VQ1 = variable_queue_publish(false, 1, VQ0), - %% drop message with AckRequired = true - {{MsgId, AckTag}, VQ2} = rabbit_variable_queue:drop(true, VQ1), - true = rabbit_variable_queue:is_empty(VQ2), - true = AckTag =/= undefinded, - %% drop again -> empty - {empty, VQ3} = rabbit_variable_queue:drop(false, VQ2), - %% requeue - {[MsgId], VQ4} = rabbit_variable_queue:requeue([AckTag], VQ3), - %% drop message with AckRequired = false - {{MsgId, undefined}, VQ5} = rabbit_variable_queue:drop(false, VQ4), - true = rabbit_variable_queue:is_empty(VQ5), - VQ5. - -test_dropfetchwhile(VQ0) -> - Count = 10, - - %% add messages with sequential expiry - VQ1 = variable_queue_publish( - false, 1, Count, - fun (N, Props) -> Props#message_properties{expiry = N} end, - fun erlang:term_to_binary/1, VQ0), - - %% fetch the first 5 messages - {#message_properties{expiry = 6}, {Msgs, AckTags}, VQ2} = - rabbit_variable_queue:fetchwhile( - fun (#message_properties{expiry = Expiry}) -> Expiry =< 5 end, - fun (Msg, AckTag, {MsgAcc, AckAcc}) -> - {[Msg | MsgAcc], [AckTag | AckAcc]} - end, {[], []}, VQ1), - true = lists:seq(1, 5) == [msg2int(M) || M <- lists:reverse(Msgs)], - - %% requeue them - {_MsgIds, VQ3} = rabbit_variable_queue:requeue(AckTags, VQ2), - - %% drop the first 5 messages - {#message_properties{expiry = 6}, VQ4} = - rabbit_variable_queue:dropwhile( - fun (#message_properties {expiry = Expiry}) -> Expiry =< 5 end, VQ3), - - %% fetch 5 - VQ5 = lists:foldl(fun (N, VQN) -> - {{Msg, _, _}, VQM} = - rabbit_variable_queue:fetch(false, VQN), - true = msg2int(Msg) == N, - VQM - end, VQ4, lists:seq(6, Count)), - - %% should be empty now - true = rabbit_variable_queue:is_empty(VQ5), - - VQ5. - -test_dropwhile_varying_ram_duration(VQ0) -> - test_dropfetchwhile_varying_ram_duration( - fun (VQ1) -> - {_, VQ2} = rabbit_variable_queue:dropwhile( - fun (_) -> false end, VQ1), - VQ2 - end, VQ0). - -test_fetchwhile_varying_ram_duration(VQ0) -> - test_dropfetchwhile_varying_ram_duration( - fun (VQ1) -> - {_, ok, VQ2} = rabbit_variable_queue:fetchwhile( - fun (_) -> false end, - fun (_, _, A) -> A end, - ok, VQ1), - VQ2 - end, VQ0). - -test_dropfetchwhile_varying_ram_duration(Fun, VQ0) -> - VQ1 = variable_queue_publish(false, 1, VQ0), - VQ2 = variable_queue_set_ram_duration_target(0, VQ1), - VQ3 = Fun(VQ2), - VQ4 = variable_queue_set_ram_duration_target(infinity, VQ3), - VQ5 = variable_queue_publish(false, 1, VQ4), - VQ6 = Fun(VQ5), - VQ6. - -test_variable_queue_dynamic_duration_change(VQ0) -> - SegmentSize = rabbit_queue_index:next_segment_boundary(0), - - %% start by sending in a couple of segments worth - Len = 2*SegmentSize, - VQ1 = variable_queue_publish(false, Len, VQ0), - %% squeeze and relax queue - Churn = Len div 32, - VQ2 = publish_fetch_and_ack(Churn, Len, VQ1), - - {Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), - VQ7 = lists:foldl( - fun (Duration1, VQ4) -> - {_Duration, VQ5} = rabbit_variable_queue:ram_duration(VQ4), - VQ6 = variable_queue_set_ram_duration_target( - Duration1, VQ5), - publish_fetch_and_ack(Churn, Len, VQ6) - end, VQ3, [Duration / 4, 0, Duration / 4, infinity]), - - %% drain - {VQ8, AckTags} = variable_queue_fetch(Len, false, false, Len, VQ7), - {_Guids, VQ9} = rabbit_variable_queue:ack(AckTags, VQ8), - {empty, VQ10} = rabbit_variable_queue:fetch(true, VQ9), - - VQ10. - -publish_fetch_and_ack(0, _Len, VQ0) -> - VQ0; -publish_fetch_and_ack(N, Len, VQ0) -> - VQ1 = variable_queue_publish(false, 1, VQ0), - {{_Msg, false, AckTag}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), - Len = rabbit_variable_queue:len(VQ2), - {_Guids, VQ3} = rabbit_variable_queue:ack([AckTag], VQ2), - publish_fetch_and_ack(N-1, Len, VQ3). - -test_variable_queue_partial_segments_delta_thing(VQ0) -> - SegmentSize = rabbit_queue_index:next_segment_boundary(0), - HalfSegment = SegmentSize div 2, - OneAndAHalfSegment = SegmentSize + HalfSegment, - VQ1 = variable_queue_publish(true, OneAndAHalfSegment, VQ0), - {_Duration, VQ2} = rabbit_variable_queue:ram_duration(VQ1), - VQ3 = check_variable_queue_status( - variable_queue_set_ram_duration_target(0, VQ2), - %% one segment in q3, and half a segment in delta - [{delta, {delta, SegmentSize, HalfSegment, OneAndAHalfSegment}}, - {q3, SegmentSize}, - {len, SegmentSize + HalfSegment}]), - VQ4 = variable_queue_set_ram_duration_target(infinity, VQ3), - VQ5 = check_variable_queue_status( - variable_queue_publish(true, 1, VQ4), - %% one alpha, but it's in the same segment as the deltas - [{q1, 1}, - {delta, {delta, SegmentSize, HalfSegment, OneAndAHalfSegment}}, - {q3, SegmentSize}, - {len, SegmentSize + HalfSegment + 1}]), - {VQ6, AckTags} = variable_queue_fetch(SegmentSize, true, false, - SegmentSize + HalfSegment + 1, VQ5), - VQ7 = check_variable_queue_status( - VQ6, - %% the half segment should now be in q3 - [{q1, 1}, - {delta, {delta, undefined, 0, undefined}}, - {q3, HalfSegment}, - {len, HalfSegment + 1}]), - {VQ8, AckTags1} = variable_queue_fetch(HalfSegment + 1, true, false, - HalfSegment + 1, VQ7), - {_Guids, VQ9} = rabbit_variable_queue:ack(AckTags ++ AckTags1, VQ8), - %% should be empty now - {empty, VQ10} = rabbit_variable_queue:fetch(true, VQ9), - VQ10. - -check_variable_queue_status(VQ0, Props) -> - VQ1 = variable_queue_wait_for_shuffling_end(VQ0), - S = variable_queue_status(VQ1), - assert_props(S, Props), - VQ1. - -variable_queue_status(VQ) -> - Keys = rabbit_backing_queue:info_keys() -- [backing_queue_status], - [{K, rabbit_variable_queue:info(K, VQ)} || K <- Keys] ++ - rabbit_variable_queue:info(backing_queue_status, VQ). - -variable_queue_wait_for_shuffling_end(VQ) -> - case credit_flow:blocked() of - false -> VQ; - true -> receive - {bump_credit, Msg} -> - credit_flow:handle_bump_msg(Msg), - variable_queue_wait_for_shuffling_end( - rabbit_variable_queue:resume(VQ)) - end - end. - -test_variable_queue_all_the_bits_not_covered_elsewhere1(VQ0) -> - Count = 2 * rabbit_queue_index:next_segment_boundary(0), - VQ1 = variable_queue_publish(true, Count, VQ0), - VQ2 = variable_queue_publish(false, Count, VQ1), - VQ3 = variable_queue_set_ram_duration_target(0, VQ2), - {VQ4, _AckTags} = variable_queue_fetch(Count, true, false, - Count + Count, VQ3), - {VQ5, _AckTags1} = variable_queue_fetch(Count, false, false, - Count, VQ4), - _VQ6 = rabbit_variable_queue:terminate(shutdown, VQ5), - VQ7 = variable_queue_init(test_amqqueue(true), true), - {{_Msg1, true, _AckTag1}, VQ8} = rabbit_variable_queue:fetch(true, VQ7), - Count1 = rabbit_variable_queue:len(VQ8), - VQ9 = variable_queue_publish(false, 1, VQ8), - VQ10 = variable_queue_set_ram_duration_target(0, VQ9), - {VQ11, _AckTags2} = variable_queue_fetch(Count1, true, true, Count, VQ10), - {VQ12, _AckTags3} = variable_queue_fetch(1, false, false, 1, VQ11), - VQ12. - -test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> - VQ1 = variable_queue_set_ram_duration_target(0, VQ0), - VQ2 = variable_queue_publish(false, 4, VQ1), - {VQ3, AckTags} = variable_queue_fetch(2, false, false, 4, VQ2), - {_Guids, VQ4} = - rabbit_variable_queue:requeue(AckTags, VQ3), - VQ5 = rabbit_variable_queue:timeout(VQ4), - _VQ6 = rabbit_variable_queue:terminate(shutdown, VQ5), - VQ7 = variable_queue_init(test_amqqueue(true), true), - {empty, VQ8} = rabbit_variable_queue:fetch(false, VQ7), - VQ8. - -test_variable_queue_fold_msg_on_disk(VQ0) -> - VQ1 = variable_queue_publish(true, 1, VQ0), - {VQ2, AckTags} = variable_queue_fetch(1, true, false, 1, VQ1), - {ok, VQ3} = rabbit_variable_queue:ackfold(fun (_M, _A, ok) -> ok end, - ok, VQ2, AckTags), - VQ3. - -test_queue_recover() -> - Count = 2 * rabbit_queue_index:next_segment_boundary(0), - {new, #amqqueue { pid = QPid, name = QName } = Q} = - rabbit_amqqueue:declare(test_queue(), true, false, [], none), - publish_and_confirm(Q, <<>>, Count), - - [{_, SupPid, _, _}] = supervisor:which_children(rabbit_amqqueue_sup_sup), - exit(SupPid, kill), - exit(QPid, kill), - MRef = erlang:monitor(process, QPid), - receive {'DOWN', MRef, process, QPid, _Info} -> ok - after 10000 -> exit(timeout_waiting_for_queue_death) - end, - rabbit_amqqueue:stop(), - rabbit_amqqueue:start(rabbit_amqqueue:recover()), - {ok, Limiter} = rabbit_limiter:start_link(no_id), - rabbit_amqqueue:with_or_die( - QName, - fun (Q1 = #amqqueue { pid = QPid1 }) -> - CountMinusOne = Count - 1, - {ok, CountMinusOne, {QName, QPid1, _AckTag, true, _Msg}} = - rabbit_amqqueue:basic_get(Q1, self(), false, Limiter), - exit(QPid1, shutdown), - VQ1 = variable_queue_init(Q, true), - {{_Msg1, true, _AckTag1}, VQ2} = - rabbit_variable_queue:fetch(true, VQ1), - CountMinusOne = rabbit_variable_queue:len(VQ2), - _VQ3 = rabbit_variable_queue:delete_and_terminate(shutdown, VQ2), - rabbit_amqqueue:internal_delete(QName) - end), - passed. - -test_variable_queue_delete_msg_store_files_callback() -> - ok = restart_msg_store_empty(), - {new, #amqqueue { pid = QPid, name = QName } = Q} = - rabbit_amqqueue:declare(test_queue(), true, false, [], none), - Payload = <<0:8388608>>, %% 1MB - Count = 30, - publish_and_confirm(Q, Payload, Count), - - rabbit_amqqueue:set_ram_duration_target(QPid, 0), - - {ok, Limiter} = rabbit_limiter:start_link(no_id), - - CountMinusOne = Count - 1, - {ok, CountMinusOne, {QName, QPid, _AckTag, false, _Msg}} = - rabbit_amqqueue:basic_get(Q, self(), true, Limiter), - {ok, CountMinusOne} = rabbit_amqqueue:purge(Q), - - %% give the queue a second to receive the close_fds callback msg - timer:sleep(1000), - - rabbit_amqqueue:delete(Q, false, false), - passed. - -test_configurable_server_properties() -> - %% List of the names of the built-in properties do we expect to find - BuiltInPropNames = [<<"product">>, <<"version">>, <<"platform">>, - <<"copyright">>, <<"information">>], - - Protocol = rabbit_framing_amqp_0_9_1, - - %% Verify that the built-in properties are initially present - ActualPropNames = [Key || {Key, longstr, _} <- - rabbit_reader:server_properties(Protocol)], - true = lists:all(fun (X) -> lists:member(X, ActualPropNames) end, - BuiltInPropNames), - - %% Get the initial server properties configured in the environment - {ok, ServerProperties} = application:get_env(rabbit, server_properties), - - %% Helper functions - ConsProp = fun (X) -> application:set_env(rabbit, - server_properties, - [X | ServerProperties]) end, - IsPropPresent = - fun (X) -> - lists:member(X, rabbit_reader:server_properties(Protocol)) - end, - - %% Add a wholly new property of the simplified {KeyAtom, StringValue} form - NewSimplifiedProperty = {NewHareKey, NewHareVal} = {hare, "soup"}, - ConsProp(NewSimplifiedProperty), - %% Do we find hare soup, appropriately formatted in the generated properties? - ExpectedHareImage = {list_to_binary(atom_to_list(NewHareKey)), - longstr, - list_to_binary(NewHareVal)}, - true = IsPropPresent(ExpectedHareImage), - - %% Add a wholly new property of the {BinaryKey, Type, Value} form - %% and check for it - NewProperty = {<<"new-bin-key">>, signedint, -1}, - ConsProp(NewProperty), - %% Do we find the new property? - true = IsPropPresent(NewProperty), - - %% Add a property that clobbers a built-in, and verify correct clobbering - {NewVerKey, NewVerVal} = NewVersion = {version, "X.Y.Z."}, - {BinNewVerKey, BinNewVerVal} = {list_to_binary(atom_to_list(NewVerKey)), - list_to_binary(NewVerVal)}, - ConsProp(NewVersion), - ClobberedServerProps = rabbit_reader:server_properties(Protocol), - %% Is the clobbering insert present? - true = IsPropPresent({BinNewVerKey, longstr, BinNewVerVal}), - %% Is the clobbering insert the only thing with the clobbering key? - [{BinNewVerKey, longstr, BinNewVerVal}] = - [E || {K, longstr, _V} = E <- ClobberedServerProps, K =:= BinNewVerKey], - - application:set_env(rabbit, server_properties, ServerProperties), - passed. - -nop(_) -> ok. -nop(_, _) -> ok. - -test_memory_high_watermark() -> - %% set vm memory high watermark - HWM = vm_memory_monitor:get_vm_memory_high_watermark(), - %% this will trigger an alarm (memory unit is MB) - ok = control_action(set_vm_memory_high_watermark, ["absolute", "2"]), - [{{resource_limit,memory,_},[]}] = rabbit_alarm:get_alarms(), - %% reset - ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), - - passed. diff --git a/test/src/rabbit_tests_event_receiver.erl b/test/src/rabbit_tests_event_receiver.erl deleted file mode 100644 index 610496b60c..0000000000 --- a/test/src/rabbit_tests_event_receiver.erl +++ /dev/null @@ -1,58 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_tests_event_receiver). - --export([start/3, stop/0]). - --export([init/1, handle_call/2, handle_event/2, handle_info/2, - terminate/2, code_change/3]). - --include("rabbit.hrl"). - -start(Pid, Nodes, Types) -> - Oks = [ok || _ <- Nodes], - {Oks, _} = rpc:multicall(Nodes, gen_event, add_handler, - [rabbit_event, ?MODULE, [Pid, Types]]). - -stop() -> - gen_event:delete_handler(rabbit_event, ?MODULE, []). - -%%---------------------------------------------------------------------------- - -init([Pid, Types]) -> - {ok, {Pid, Types}}. - -handle_call(_Request, State) -> - {ok, not_understood, State}. - -handle_event(Event = #event{type = Type}, State = {Pid, Types}) -> - case lists:member(Type, Types) of - true -> Pid ! Event; - false -> ok - end, - {ok, State}. - -handle_info(_Info, State) -> - {ok, State}. - -terminate(_Arg, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%---------------------------------------------------------------------------- diff --git a/test/src/supervisor2_tests.erl b/test/src/supervisor2_tests.erl deleted file mode 100644 index 199c66eca0..0000000000 --- a/test/src/supervisor2_tests.erl +++ /dev/null @@ -1,75 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(supervisor2_tests). --behaviour(supervisor2). - --export([test_all/0, start_link/0]). --export([init/1]). - -test_all() -> - ok = check_shutdown(stop, 200, 200, 2000), - ok = check_shutdown(ignored, 1, 2, 2000). - -check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) -> - {ok, Sup} = supervisor2:start_link(?MODULE, [SupTimeout]), - Res = lists:foldl( - fun (I, ok) -> - TestSupPid = erlang:whereis(?MODULE), - ChildPids = - [begin - {ok, ChildPid} = - supervisor2:start_child(TestSupPid, []), - ChildPid - end || _ <- lists:seq(1, ChildCount)], - MRef = erlang:monitor(process, TestSupPid), - [P ! SigStop || P <- ChildPids], - ok = supervisor2:terminate_child(Sup, test_sup), - {ok, _} = supervisor2:restart_child(Sup, test_sup), - receive - {'DOWN', MRef, process, TestSupPid, shutdown} -> - ok; - {'DOWN', MRef, process, TestSupPid, Reason} -> - {error, {I, Reason}} - end; - (_, R) -> - R - end, ok, lists:seq(1, Iterations)), - unlink(Sup), - MSupRef = erlang:monitor(process, Sup), - exit(Sup, shutdown), - receive - {'DOWN', MSupRef, process, Sup, _Reason} -> - ok - end, - Res. - -start_link() -> - Pid = spawn_link(fun () -> - process_flag(trap_exit, true), - receive stop -> ok end - end), - {ok, Pid}. - -init([Timeout]) -> - {ok, {{one_for_one, 0, 1}, - [{test_sup, {supervisor2, start_link, - [{local, ?MODULE}, ?MODULE, []]}, - transient, Timeout, supervisor, [?MODULE]}]}}; -init([]) -> - {ok, {{simple_one_for_one, 0, 1}, - [{test_worker, {?MODULE, start_link, []}, - temporary, 1000, worker, [?MODULE]}]}}. diff --git a/test/src/test_sup.erl b/test/src/test_sup.erl deleted file mode 100644 index 84d14f725d..0000000000 --- a/test/src/test_sup.erl +++ /dev/null @@ -1,93 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(test_sup). - --behaviour(supervisor2). - --export([test_supervisor_delayed_restart/0, - init/1, start_child/0]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(test_supervisor_delayed_restart/0 :: () -> 'passed'). - --endif. - -%%---------------------------------------------------------------------------- -%% Public API -%%---------------------------------------------------------------------------- - -test_supervisor_delayed_restart() -> - passed = with_sup(simple_one_for_one, - fun (SupPid) -> - {ok, _ChildPid} = - supervisor2:start_child(SupPid, []), - test_supervisor_delayed_restart(SupPid) - end), - passed = with_sup(one_for_one, fun test_supervisor_delayed_restart/1). - -test_supervisor_delayed_restart(SupPid) -> - ok = ping_child(SupPid), - ok = exit_child(SupPid), - timer:sleep(100), - ok = ping_child(SupPid), - ok = exit_child(SupPid), - timer:sleep(100), - timeout = ping_child(SupPid), - timer:sleep(1010), - ok = ping_child(SupPid), - passed. - -with_sup(RestartStrategy, Fun) -> - {ok, SupPid} = supervisor2:start_link(?MODULE, [RestartStrategy]), - Res = Fun(SupPid), - unlink(SupPid), - exit(SupPid, shutdown), - Res. - -init([RestartStrategy]) -> - {ok, {{RestartStrategy, 1, 1}, - [{test, {test_sup, start_child, []}, {permanent, 1}, - 16#ffffffff, worker, [test_sup]}]}}. - -start_child() -> - {ok, proc_lib:spawn_link(fun run_child/0)}. - -ping_child(SupPid) -> - Ref = make_ref(), - with_child_pid(SupPid, fun(ChildPid) -> ChildPid ! {ping, Ref, self()} end), - receive {pong, Ref} -> ok - after 1000 -> timeout - end. - -exit_child(SupPid) -> - with_child_pid(SupPid, fun(ChildPid) -> exit(ChildPid, abnormal) end), - ok. - -with_child_pid(SupPid, Fun) -> - case supervisor2:which_children(SupPid) of - [{_Id, undefined, worker, [test_sup]}] -> ok; - [{_Id, ChildPid, worker, [test_sup]}] -> Fun(ChildPid); - [] -> ok - end. - -run_child() -> - receive {ping, Ref, Pid} -> Pid ! {pong, Ref}, - run_child() - end. diff --git a/test/src/vm_memory_monitor_tests.erl b/test/src/vm_memory_monitor_tests.erl deleted file mode 100644 index 61d62f862d..0000000000 --- a/test/src/vm_memory_monitor_tests.erl +++ /dev/null @@ -1,35 +0,0 @@ -%% 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 GoPivotal, Inc. -%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -%% - --module(vm_memory_monitor_tests). - --export([all_tests/0]). - -%% --------------------------------------------------------------------------- -%% Tests -%% --------------------------------------------------------------------------- - -all_tests() -> - lists:foreach(fun ({S, {K, V}}) -> - {K, V} = vm_memory_monitor:parse_line_linux(S) - end, - [{"MemTotal: 0 kB", {'MemTotal', 0}}, - {"MemTotal: 502968 kB ", {'MemTotal', 515039232}}, - {"MemFree: 178232 kB", {'MemFree', 182509568}}, - {"MemTotal: 50296888", {'MemTotal', 50296888}}, - {"MemTotal 502968 kB", {'MemTotal', 515039232}}, - {"MemTotal 50296866 ", {'MemTotal', 50296866}}]), - passed. diff --git a/version.mk b/version.mk deleted file mode 100644 index 5683af4a2f..0000000000 --- a/version.mk +++ /dev/null @@ -1 +0,0 @@ -VERSION?=0.0.0 |
