Add Elixir support to ejabberd

This commit is contained in:
Mickaël Rémond 2015-01-29 18:43:47 +01:00 committed by Mickaël Rémond
parent cf929e730f
commit 01e1f677c7
11 changed files with 202 additions and 55 deletions

3
.gitignore vendored
View File

@ -35,3 +35,6 @@
/src/ejabberd.app.src /src/ejabberd.app.src
/src/eldap_filter_yecc.erl /src/eldap_filter_yecc.erl
/vars.config /vars.config
/dialyzer/
/test/*.beam
/logs/

View File

@ -11,6 +11,9 @@ DESTDIR =
# /etc/ejabberd/ # /etc/ejabberd/
ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd
# /bin/
BINDIR = $(DESTDIR)@bindir@
# /sbin/ # /sbin/
SBINDIR = $(DESTDIR)@sbindir@ SBINDIR = $(DESTDIR)@sbindir@
@ -118,6 +121,7 @@ install: all
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml || $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml
$(SED) -e "s*{{rootdir}}*@prefix@*" \ $(SED) -e "s*{{rootdir}}*@prefix@*" \
-e "s*{{installuser}}*@INSTALLUSER@*" \ -e "s*{{installuser}}*@INSTALLUSER@*" \
-e "s*{{bindir}}*@bindir@*" \
-e "s*{{libdir}}*@libdir@*" \ -e "s*{{libdir}}*@libdir@*" \
-e "s*{{sysconfdir}}*@sysconfdir@*" \ -e "s*{{sysconfdir}}*@sysconfdir@*" \
-e "s*{{localstatedir}}*@localstatedir@*" \ -e "s*{{localstatedir}}*@localstatedir@*" \
@ -132,6 +136,11 @@ install: all
# Administration script # Administration script
[ -d $(SBINDIR) ] || $(INSTALL) -d -m 755 $(SBINDIR) [ -d $(SBINDIR) ] || $(INSTALL) -d -m 755 $(SBINDIR)
$(INSTALL) -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl $(INSTALL) -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl
# Elixir binaries
[ -d $(BINDIR) ] || $(INSTALL) -d -m 755 $(BINDIR)
[ -f deps/elixir/bin/iex ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/iex $(BINDIR)/iex
[ -f deps/elixir/bin/elixir ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/elixir $(BINDIR)/elixir
[ -f deps/elixir/bin/mix ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/mix $(BINDIR)/mix
# #
# Init script # Init script
$(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*" \ $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*" \
@ -145,6 +154,9 @@ install: all
$(INSTALL) -m 644 ebin/*.beam $(BEAMDIR) $(INSTALL) -m 644 ebin/*.beam $(BEAMDIR)
$(INSTALL) -m 644 deps/*/ebin/*.app $(BEAMDIR) $(INSTALL) -m 644 deps/*/ebin/*.app $(BEAMDIR)
$(INSTALL) -m 644 deps/*/ebin/*.beam $(BEAMDIR) $(INSTALL) -m 644 deps/*/ebin/*.beam $(BEAMDIR)
# Install Elixir and Elixir dependancies
-$(INSTALL) -m 644 deps/*/lib/*/ebin/*.app $(BEAMDIR)
-$(INSTALL) -m 644 deps/*/lib/*/ebin/*.beam $(BEAMDIR)
rm -f $(BEAMDIR)/configure.beam rm -f $(BEAMDIR)/configure.beam
# #
# ejabberd header files # ejabberd header files
@ -201,6 +213,9 @@ uninstall: uninstall-binary
uninstall-binary: uninstall-binary:
rm -f $(SBINDIR)/ejabberdctl rm -f $(SBINDIR)/ejabberdctl
rm -f $(BINDIR)/iex
rm -f $(BINDIR)/elixir
rm -f $(BINDIR)/mix
rm -fr $(DOCDIR) rm -fr $(DOCDIR)
rm -f $(BEAMDIR)/*.beam rm -f $(BEAMDIR)/*.beam
rm -f $(BEAMDIR)/*.app rm -f $(BEAMDIR)/*.app

View File

@ -106,10 +106,10 @@ AC_ARG_ENABLE(mssql,
esac],[db_type=generic]) esac],[db_type=generic])
AC_ARG_ENABLE(all, AC_ARG_ENABLE(all,
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-riak --enable-json --enable-iconv --enable-debug --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])], [AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-riak --enable-json --enable-elixir --enable-iconv --enable-debug --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])],
[case "${enableval}" in [case "${enableval}" in
yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true riak=true json=true iconv=true debug=true lager=true tools=true ;; yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true riak=true json=true elixir=true iconv=true debug=true lager=true tools=true ;;
no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false riak=false json=false iconv=false debug=false lager=false tools=false ;; no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false riak=false json=false elixir=false iconv=false debug=false lager=false tools=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
esac],[]) esac],[])
@ -185,6 +185,14 @@ AC_ARG_ENABLE(json,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-json) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-json) ;;
esac],[if test "x$json" = "x"; then json=false; fi]) esac],[if test "x$json" = "x"; then json=false; fi])
AC_ARG_ENABLE(elixir,
[AC_HELP_STRING([--enable-elixir], [enable Elixir support (default: no)])],
[case "${enableval}" in
yes) elixir=true ;;
no) elixir=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-elixir) ;;
esac],[if test "x$elixir" = "x"; then elixir=false; fi])
AC_ARG_ENABLE(iconv, AC_ARG_ENABLE(iconv,
[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])], [AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])],
[case "${enableval}" in [case "${enableval}" in
@ -240,6 +248,7 @@ AC_SUBST(pam)
AC_SUBST(zlib) AC_SUBST(zlib)
AC_SUBST(riak) AC_SUBST(riak)
AC_SUBST(json) AC_SUBST(json)
AC_SUBST(elixir)
AC_SUBST(iconv) AC_SUBST(iconv)
AC_SUBST(debug) AC_SUBST(debug)
AC_SUBST(lager) AC_SUBST(lager)

View File

@ -12,6 +12,7 @@ ERLANG_NODE=ejabberd@localhost
# define default environment variables # define default environment variables
SCRIPT_DIR=`cd ${0%/*} && pwd` SCRIPT_DIR=`cd ${0%/*} && pwd`
ERL={{erl}} ERL={{erl}}
IEX={{bindir}}/iex
INSTALLUSER={{installuser}} INSTALLUSER={{installuser}}
# Compatibility in ZSH # Compatibility in ZSH
@ -128,6 +129,7 @@ if [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] ; then
else else
NAME="-name" NAME="-name"
fi fi
IEXNAME="-$NAME"
# define ejabberd environment parameters # define ejabberd environment parameters
if [ "$EJABBERD_CONFIG_PATH" != "${EJABBERD_CONFIG_PATH%.yml}" ] ; then if [ "$EJABBERD_CONFIG_PATH" != "${EJABBERD_CONFIG_PATH%.yml}" ] ; then
@ -182,6 +184,67 @@ start()
# attach to server # attach to server
debug() debug()
{
debugwarning
TTY=`tty | sed -e 's/.*\///g'`
$EXEC_CMD "$ERL \
$NAME debug-${TTY}-${ERLANG_NODE} \
-remsh $ERLANG_NODE \
-hidden \
$KERNEL_OPTS \
$ERLANG_OPTS $ARGS \"$@\""
}
# attach to server using Elixir
iexdebug()
{
debugwarning
TTY=`tty | sed -e 's/.*\///g'`
# Elixir shell is hidden as default
$EXEC_CMD "$IEX \
$IEXNAME debug-${TTY}-${ERLANG_NODE} \
--remsh $ERLANG_NODE \
--erl \"$KERNEL_OPTS\" \
--erl \"$ERLANG_OPTS\" --erl \"$ARGS\" --erl \"$@\""
}
# start interactive server
live()
{
livewarning
$EXEC_CMD "$ERL \
$NAME $ERLANG_NODE \
-pa $EJABBERD_EBIN_PATH \
-mnesia dir \"\\\"$SPOOL_DIR\\\"\" \
$KERNEL_OPTS \
$EJABBERD_OPTS \
-s ejabberd \
$ERLANG_OPTS $ARGS \"$@\""
}
# start interactive server with Elixir
iexlive()
{
livewarning
$EXEC_CMD "$IEX \
$IEXNAME $ERLANG_NODE \
-pa $EJABBERD_EBIN_PATH \
--erl \"-mnesia dir \\\"$SPOOL_DIR\\\"\" \
--erl \"$KERNEL_OPTS\" \
--erl \"$EJABBERD_OPTS\" \
--app ejabberd \
--erl \"$ERLANG_OPTS\" --erl $ARGS --erl \"$@\""
}
etop()
{
$EXEC_CMD "$ERL \
$NAME debug-${TTY}-${ERLANG_NODE} \
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
}
# TODO: refactor debug warning and livewarning
debugwarning()
{ {
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
echo "--------------------------------------------------------------------" echo "--------------------------------------------------------------------"
@ -199,21 +262,13 @@ debug()
echo "--------------------------------------------------------------------" echo "--------------------------------------------------------------------"
echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:"
echo " EJABBERD_BYPASS_WARNINGS=true" echo " EJABBERD_BYPASS_WARNINGS=true"
echo "Press any key to continue" echo "Press return to continue"
read foo read foo
echo "" echo ""
fi fi
TTY=`tty | sed -e 's/.*\///g'`
$EXEC_CMD "$ERL \
$NAME debug-${TTY}-${ERLANG_NODE} \
-remsh $ERLANG_NODE \
-hidden \
$KERNEL_OPTS \
$ERLANG_OPTS $ARGS \"$@\""
} }
# start interactive server livewarning()
live()
{ {
check_start check_start
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
@ -231,34 +286,22 @@ live()
echo "--------------------------------------------------------------------" echo "--------------------------------------------------------------------"
echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:"
echo " EJABBERD_BYPASS_WARNINGS=true" echo " EJABBERD_BYPASS_WARNINGS=true"
echo "Press any key to continue" echo "Press return to continue"
read foo read foo
echo "" echo ""
fi fi
$EXEC_CMD "$ERL \
$NAME $ERLANG_NODE \
-pa $EJABBERD_EBIN_PATH \
-mnesia dir \"\\\"$SPOOL_DIR\\\"\" \
$KERNEL_OPTS \
$EJABBERD_OPTS \
-s ejabberd \
$ERLANG_OPTS $ARGS \"$@\""
}
etop()
{
$EXEC_CMD "$ERL \
$NAME debug-${TTY}-${ERLANG_NODE} \
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
} }
# TODO: Make iex command display only if ejabberd Elixir support has been enabled
help() help()
{ {
echo "" echo ""
echo "Commands to start an ejabberd node:" echo "Commands to start an ejabberd node:"
echo " start Start an ejabberd node in server mode" echo " start Start an ejabberd node in server mode"
echo " debug Attach an interactive Erlang shell to a running ejabberd node" echo " debug Attach an interactive Erlang shell to a running ejabberd node"
echo " live Start an ejabberd node in live (interactive) mode" echo " iexdebug Attach an interactive Elixir shell to a running ejabberd node"
echo " live Start an ejabberd node in live (interactive) mode"
echo " iexlive Start an ejabberd node in live (interactive) mode, within an Elixir shell"
echo "" echo ""
echo "Optional parameters when starting an ejabberd node:" echo "Optional parameters when starting an ejabberd node:"
echo " --config-dir dir Config ejabberd: $ETC_DIR" echo " --config-dir dir Config ejabberd: $ETC_DIR"
@ -409,7 +452,9 @@ wait_for_status()
case $ARGS in case $ARGS in
' start') start;; ' start') start;;
' debug') debug;; ' debug') debug;;
' iexdebug') iexdebug;;
' live') live;; ' live') live;;
' iexlive') iexlive;;
' etop') etop;; ' etop') etop;;
' started') wait_for_status 0 30 2;; # wait 30x2s before timeout ' started') wait_for_status 0 30 2;; # wait 30x2s before timeout
' stopped') wait_for_status 3 15 2 && stop_epmd;; # wait 15x2s before timeout ' stopped') wait_for_status 3 15 2 && stop_epmd;; # wait 15x2s before timeout

13
lib/Ejabberd/hooks.ex Normal file
View File

@ -0,0 +1,13 @@
defmodule Ejabberd.Hooks do
# Generic hook setting features
def add(hook_name, host, module, function, priority) do
:ejabberd_hooks.add(hook_name, host, module, function, priority)
end
# Should be named 'removed'
def delete(hook_name, host, module, function, priority) do
:ejabberd_hooks.delete(hook_name, host, module, function, priority)
end
end

9
lib/Ejabberd/logger.ex Normal file
View File

@ -0,0 +1,9 @@
defmodule Ejabberd.Logger do
def critical(message, args \\ []), do: :lager.log(:critical, [], message, args)
def error(message, args \\ []), do: :lager.log(:error, [], message, args)
def warning(message, args \\ []), do: :lager.log(:warning, [], message, args)
def info(message, args \\ []), do: :lager.log(:info, [], message, args)
def debug(message, args \\ []), do: :lager.log(:debug, [], message, args)
end

2
lib/ejabber.ex Normal file
View File

@ -0,0 +1,2 @@
defmodule Ejabberd do
end

21
lib/mod_presence_demo.ex Normal file
View File

@ -0,0 +1,21 @@
defmodule ModPresenceDemo do
import Ejabberd.Logger # this allow using info, error, etc for logging
@behaviour :gen_mod
def start(host, _opts) do
info('Starting ejabberd module Presence Demo')
Ejabberd.Hooks.add(:set_presence_hook, host, __ENV__.module, :on_presence, 50)
:ok
end
def stop(host) do
info('Stopping ejabberd module Presence Demo')
Ejabberd.Hooks.delete(:set_presence_hook, host, __ENV__.module, :on_presence, 50)
:ok
end
def on_presence(user, _server, _resource, _packet) do
info('Receive presence for #{user}')
:none
end
end

View File

@ -95,6 +95,9 @@ CfgDeps = lists:flatmap(
{tag, "1.4.2"}}}]; {tag, "1.4.2"}}}];
({json, true}) -> ({json, true}) ->
[{jiffy, ".*", {git, "git://github.com/davisp/jiffy"}}]; [{jiffy, ".*", {git, "git://github.com/davisp/jiffy"}}];
({elixir, true}) ->
[{rebar_elixir_plugin, ".*", {git, "git://github.com/yrashk/rebar_elixir_plugin"}},
{elixir, "1.1.*", {git, "git://github.com/elixir-lang/elixir"}}];
({iconv, true}) -> ({iconv, true}) ->
[{p1_iconv, ".*", {git, "git://github.com/processone/eiconv"}}]; [{p1_iconv, ".*", {git, "git://github.com/processone/eiconv"}}];
({lager, true}) -> ({lager, true}) ->
@ -142,6 +145,13 @@ CfgXrefs = lists:flatmap(
[] []
end, Cfg), end, Cfg),
ElixirConfig = case lists:keysearch(elixir, 1, Cfg) of
{value, {elixir, true}} ->
[{plugins, [rebar_elixir_compiler, rebar_exunit] },
{lib_dirs, ["deps/elixir/lib"]}];
_ ->
[]
end,
{ok, Cwd} = file:get_cwd(), {ok, Cwd} = file:get_cwd(),
@ -157,7 +167,7 @@ Config = [{erl_opts, Macros ++ HiPE ++ DebugInfo ++
[{"(XC - UC) || (XU - X - B - " [{"(XC - UC) || (XU - X - B - "
++ string:join(CfgXrefs, " - ") ++ ")", []}]}, ++ string:join(CfgXrefs, " - ") ++ ")", []}]},
{post_hooks, PostHooks ++ CfgPostHooks}, {post_hooks, PostHooks ++ CfgPostHooks},
{deps, Deps ++ CfgDeps}], {deps, Deps ++ CfgDeps}] ++ ElixirConfig,
%%io:format("ejabberd configuration:~n ~p~n", [Config]), %%io:format("ejabberd configuration:~n ~p~n", [Config]),
Config. Config.

View File

@ -707,26 +707,40 @@ replace_module(mod_roster_odbc) -> {mod_roster, odbc};
replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, odbc}; replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, odbc};
replace_module(mod_vcard_odbc) -> {mod_vcard, odbc}; replace_module(mod_vcard_odbc) -> {mod_vcard, odbc};
replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, odbc}; replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, odbc};
replace_module(Module) -> Module. replace_module(Module) ->
case is_elixir_module(Module) of
true -> expand_elixir_module(Module);
false -> Module
end.
replace_modules(Modules) -> replace_modules(Modules) -> lists:map( fun({Module, Opts}) -> case
lists:map( replace_module(Module) of {NewModule, DBType} ->
fun({Module, Opts}) -> emit_deprecation_warning(Module, NewModule, DBType), NewOpts =
case replace_module(Module) of [{db_type, DBType} | lists:keydelete(db_type, 1, Opts)],
{NewModule, DBType} -> {NewModule, transform_module_options(Module, NewOpts)}; NewModule
emit_deprecation_warning(Module, NewModule, DBType), -> if Module /= NewModule -> emit_deprecation_warning(Module,
NewOpts = [{db_type, DBType} | NewModule); true -> ok end, {NewModule,
lists:keydelete(db_type, 1, Opts)], transform_module_options(Module, Opts)} end end, Modules).
{NewModule, transform_module_options(Module, NewOpts)};
NewModule -> %% Elixir module naming
if Module /= NewModule -> %% ====================
emit_deprecation_warning(Module, NewModule);
true -> %% If module name start with uppercase letter, this is an Elixir module:
ok is_elixir_module(Module) ->
end, case atom_to_list(Module) of
{NewModule, transform_module_options(Module, Opts)} [H|_] when H >= 65, H =< 90 -> true;
end _ ->false
end, Modules). end.
%% We assume we know this is an elixir module
expand_elixir_module(Module) ->
case atom_to_list(Module) of
%% Module name already specified as an Elixir from Erlang module name
"Elixir." ++ _ -> Module;
%% if start with uppercase letter, this is an Elixir module: Append 'Elixir.' to module name.
ModuleString ->
list_to_atom("Elixir." ++ ModuleString)
end.
strings_to_binary([]) -> strings_to_binary([]) ->
[]; [];
@ -1009,5 +1023,10 @@ emit_deprecation_warning(Module, NewModule, DBType) ->
" instead", [Module, NewModule, DBType]). " instead", [Module, NewModule, DBType]).
emit_deprecation_warning(Module, NewModule) -> emit_deprecation_warning(Module, NewModule) ->
?WARNING_MSG("Module ~s is deprecated, use ~s instead", case is_elixir_module(NewModule) of
[Module, NewModule]). %% Do not emit deprecation warning for Elixir
true -> ok;
false ->
?WARNING_MSG("Module ~s is deprecated, use ~s instead",
[Module, NewModule])
end.

View File

@ -26,6 +26,7 @@
{zlib, @zlib@}. {zlib, @zlib@}.
{riak, @riak@}. {riak, @riak@}.
{json, @json@}. {json, @json@}.
{elixir, @elixir@}.
{lager, @lager@}. {lager, @lager@}.
{iconv, @iconv@}. {iconv, @iconv@}.