- 浏览: 982066 次
- 性别:
- 来自: 广州
最新评论
-
qingchuwudi:
有用,非常感谢!
erlang进程的优先级 -
zfjdiamond:
你好 这条命令 在那里输入??
你们有yum 我有LuaRocks -
simsunny22:
这个是在linux下运行的吧,在window下怎么运行escr ...
escript的高级特性 -
mozhenghua:
http://www.erlang.org/doc/apps/ ...
mnesia 分布协调的几个细节 -
fxltsbl:
A new record of 108000 HTTP req ...
Haproxy 1.4-dev2: barrier of 100k HTTP req/s crossed
最完美的shell和erlang控制的结合,比其他系统要优雅不知道多少,仔细研究你会有很大的收获的,至少可以列出10个卖点.
cat ejabberdctl.template
#!/bin/sh
# define default configuration
POLL=true
SMP=auto
ERL_MAX_PORTS=32000
ERL_PROCESSES=250000
ERL_MAX_ETS_TABLES=1400
# define default environment variables
NODE=ejabberd
HOST=localhost
ERLANG_NODE=$NODE@$HOST
ERL=@erl@
ROOTDIR=@rootdir@
EJABBERD_CONFIG_PATH=$ROOTDIR/etc/ejabberd/ejabberd.cfg
LOGS_DIR=$ROOTDIR/var/log/ejabberd/
EJABBERD_DB=$ROOTDIR/var/lib/ejabberd/db/$NODE
# read custom configuration
CONFIG=$ROOTDIR/etc/ejabberd/ejabberdctl.cfg
[ -f "$CONFIG" ] && . "$CONFIG"
# parse command line parameters
ARGS=
while [ $# -ne 0 ] ; do
PARAM=$1
shift
case $PARAM in
--) break ;;
--node) ERLANG_NODE=$1; shift ;;
--config) EJABBERD_CONFIG_PATH=$1 ; shift ;;
--ctl-config) CONFIG=$1 ; shift ;;
--logs) LOGS_DIR=$1 ; shift ;;
--spool) EJABBERD_DB=$1 ; shift ;;
*) ARGS="$ARGS $PARAM" ;;
esac
done
NAME=-name
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES"
# define additional environment variables
EJABBERD_EBIN=$ROOTDIR/var/lib/ejabberd/ebin
EJABBERD_MSGS_PATH=$ROOTDIR/var/lib/ejabberd/priv/msgs
EJABBERD_SO_PATH=$ROOTDIR/var/lib/ejabberd/priv/lib
EJABBERD_BIN_PATH=$ROOTDIR/var/lib/ejabberd/priv/bin
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
SASL_LOG_PATH=$LOGS_DIR/sasl.log
DATETIME=`date "+%Y%m%d-%H%M%S"`
ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump
ERL_INETRC=$ROOTDIR/etc/ejabberd/inetrc
HOME=$ROOTDIR/var/lib/ejabberd
# export global variables
export EJABBERD_CONFIG_PATH
export EJABBERD_MSGS_PATH
export EJABBERD_LOG_PATH
export EJABBERD_SO_PATH
export EJABBERD_BIN_PATH
export ERL_CRASH_DUMP
export ERL_INETRC
export ERL_MAX_PORTS
export ERL_MAX_ETS_TABLES
export HOME
[ -d $EJABBERD_DB ] || mkdir -p $EJABBERD_DB
[ -d $LOGS_DIR ] || mkdir -p $LOGS_DIR
# Compatibility in ZSH
#setopt shwordsplit 2>/dev/null
# start server
start ()
{
$ERL \
$NAME $ERLANG_NODE \
-noinput -detached \
-pa $EJABBERD_EBIN \
-mnesia dir "\"$EJABBERD_DB\"" \
-s ejabberd \
-sasl sasl_error_logger \{file,\"$SASL_LOG_PATH\"\} \
$ERLANG_OPTS $ARGS "$@"
}
# attach to server
debug ()
{
echo "--------------------------------------------------------------------"
echo ""
echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell"
echo "to an already running ejabberd node."
echo "If an ERROR is printed, it means the connection was not succesfull."
echo "You can interact with the ejabberd node if you know how to use it."
echo "Please be extremely cautious with your actions,"
echo "and exit immediately if you are not completely sure."
echo ""
echo "To detach this shell from ejabberd, press:"
echo " control+c, control+c"
echo ""
echo "--------------------------------------------------------------------"
echo "Press any key to continue"
read foo
echo ""
$ERL \
$NAME ${NODE}debug \
-remsh $ERLANG_NODE \
$ERLANG_OPTS $ARGS "$@"
}
# start interactive server
live ()
{
echo "--------------------------------------------------------------------"
echo ""
echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode."
echo "All log messages will be shown in the command shell."
echo "You can interact with the ejabberd node if you know how to use it."
echo "Please be extremely cautious with your actions,"
echo "and exit immediately if you are not completely sure."
echo ""
echo "To exit this LIVE mode and stop ejabberd, press:"
echo " q(). and press the Enter key"
echo ""
echo "--------------------------------------------------------------------"
echo "Press any key to continue"
read foo
echo ""
$ERL \
$NAME $ERLANG_NODE \
-pa $EJABBERD_EBIN \
-mnesia dir "\"$EJABBERD_DB\"" \
-s ejabberd \
$ERLANG_OPTS $ARGS "$@"
}
# common control function
ctl ()
{
$ERL \
$NAME ejabberdctl \
-noinput \
-pa $EJABBERD_EBIN \
-s ejabberd_ctl -extra $ERLANG_NODE $@
result=$?
case $result in
0) :;;
*)
echo ""
echo "Commands to start an ejabberd node:"
echo " start Start an ejabberd node in server mode"
echo " debug Attach an interactive Erlang shell to a running ejabberd node"
echo " live Start an ejabberd node in live (interactive) mode"
echo ""
echo "Optional parameters when starting an ejabberd node:"
echo " --config file Config file of ejabberd: $EJABBERD_CONFIG_PATH"
echo " --ctl-config file Config file of ejabberdctl: $CONFIG"
echo " --logs dir Directory for logs: $LOGS_DIR"
echo " --spool dir Database spool dir: $EJABBERD_DB"
echo " --node nodename ejabberd node name: $ERLANG_NODE"
echo "";;
esac
return $result
}
# display ctl usage
usage ()
{
ctl
exit
}
case $ARGS in
' start') start;;
' debug') debug;;
' live') live;;
*) ctl $ARGS;;
esac
cat ejabberdctl.cfg.example
#
# In this file you can configure options that are passed by ejabberdctl
# to the erlang runtime system when starting ejabberd
#
# POLL: Kernel polling ([true|false])
#
# The kernel polling option requires support in the kernel.
# Additionaly, you need to enable this feature while compiling Erlang.
#
# Default: true
#
#POLL=true
# SMP: SMP support ([enable|auto|disable])
#
# Explanation in Erlang/OTP documentation:
# enable: starts the Erlang runtime system with SMP support enabled.
# This may fail if no runtime system with SMP support is available.
# auto: starts the Erlang runtime system with SMP support enabled if it
# is available and more than one logical processor are detected.
# disable: starts a runtime system without SMP support.
#
# Default: auto
#
#SMP=auto
# ERL_MAX_PORTS: Maximum number of simultaneously open Erlang ports
#
# ejabberd consumes two or three ports for every connection, either
# from a client or from another Jabber server. So take this into
# account when setting this limit.
#
# Default: 32000
# Maximum: 268435456
#
#ERL_MAX_PORTS=32000
# PROCESSES: Maximum number of Erlang processes
#
# Erlang consumes a lot of lightweight processes. If there is a lot of activity
# on ejabberd so that the maximum number of proccesses is reached, people will
# experiment greater latency times. As these processes are implemented in
# Erlang, and therefore not related to the operating system processes, you do
# not have to worry about allowing a huge number of them.
#
# Default: 250000
# Maximum: 268435456
#
#PROCESSES=250000
# ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables
#
# The number of concurrent ETS and Mnesia tables is limited. When the limit is
# reached, errors will appear in the logs:
# ** Too many db tables **
# You can safely increase this limit when starting ejabberd. It impacts memory
# consumption but the difference will be quite small.
#
# Default: 1400
#
#ERL_MAX_ETS_TABLES=1400
# The next variable allows to explicitly specify erlang node for ejabberd
# It can be given in different formats:
# ERLANG_NODE=ejabberd
# Lets erlang add hostname to the node (ejabberd uses short name in this case)
# ERLANG_NODE=ejabberd@hostname
# Erlang uses node name as is (so make sure that hostname is a real
# machine hostname or you'll not be able to control ejabberd)
# ERLANG_NODE=ejabberd@hostname.domainname
# The same as previous, but erlang will use long hostname
# (see erl (1) manual for details)
#
# Default: ejabberd
#
#ERLANG_NODE=ejabberd
cat ejabberd_ctl.erl
%%%----------------------------------------------------------------------
%%% File : ejabberd_ctl.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Ejabberd admin tool
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_ctl).
-author('alexey@process-one.net').
-export([start/0,
init/0,
process/1,
dump_to_textfile/1,
register_commands/3,
register_commands/4,
unregister_commands/3,
unregister_commands/4]).
-include("ejabberd_ctl.hrl").
-include("ejabberd.hrl").
start() ->
case init:get_plain_arguments() of
[SNode | Args] ->
SNode1 = case string:tokens(SNode, "@") of
[_Node, _Server] ->
SNode;
_ ->
case net_kernel:longnames() of
true ->
SNode ++ "@" ++ inet_db:gethostname() ++
"." ++ inet_db:res_option(domain);
false ->
SNode ++ "@" ++ inet_db:gethostname();
_ ->
SNode
end
end,
Node = list_to_atom(SNode1),
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
{badrpc, Reason} ->
?PRINT("RPC failed on the node ~p: ~p~n",
[Node, Reason]),
?STATUS_BADRPC;
S ->
S
end,
halt(Status);
_ ->
print_usage(),
halt(?STATUS_USAGE)
end.
init() ->
ets:new(ejabberd_ctl_cmds, [named_table, set, public]),
ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]).
process(["status"]) ->
{InternalStatus, ProvidedStatus} = init:get_status(),
?PRINT("Node ~p is ~p. Status: ~p~n",
[node(), InternalStatus, ProvidedStatus]),
case lists:keysearch(ejabberd, 1, application:which_applications()) of
false ->
?PRINT("ejabberd is not running~n", []),
?STATUS_ERROR;
{value,_Version} ->
?PRINT("ejabberd is running~n", []),
?STATUS_SUCCESS
end;
process(["stop"]) ->
init:stop(),
?STATUS_SUCCESS;
process(["restart"]) ->
init:restart(),
?STATUS_SUCCESS;
process(["reopen-log"]) ->
ejabberd_logger_h:reopen_log(),
case application:get_env(sasl,sasl_error_logger) of
{ok, {file, SASLfile}} ->
error_logger:delete_report_handler(sasl_report_file_h),
ejabberd_logger_h:rotate_log(SASLfile),
error_logger:add_report_handler(sasl_report_file_h,
{SASLfile, get_sasl_error_logger_type()});
_ -> false
end,
?STATUS_SUCCESS;
process(["register", User, Server, Password]) ->
case ejabberd_auth:try_register(User, Server, Password) of
{atomic, ok} ->
?STATUS_SUCCESS;
{atomic, exists} ->
?PRINT("User ~p already registered at node ~p~n",
[User ++ "@" ++ Server, node()]),
?STATUS_ERROR;
{error, Reason} ->
?PRINT("Can't register user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR
end;
process(["unregister", User, Server]) ->
case ejabberd_auth:remove_user(User, Server) of
{error, Reason} ->
?PRINT("Can't unregister user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR;
_ ->
?STATUS_SUCCESS
end;
process(["backup", Path]) ->
case mnesia:backup(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't store backup in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["dump", Path]) ->
case dump_to_textfile(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't store dump in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["load", Path]) ->
case mnesia:load_textfile(Path) of
{atomic, ok} ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't load dump in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["restore", Path]) ->
case ejabberd_admin:restore(Path) of
{atomic, _} ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't restore backup from ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR;
{aborted,{no_exists,Table}} ->
?PRINT("Can't restore backup from ~p at node ~p: Table ~p does not exist.~n",
[filename:absname(Path), node(), Table]),
?STATUS_ERROR;
{aborted,enoent} ->
?PRINT("Can't restore backup from ~p at node ~p: File not found.~n",
[filename:absname(Path), node()]),
?STATUS_ERROR
end;
process(["install-fallback", Path]) ->
case mnesia:install_fallback(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't install fallback from ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["import-file", Path]) ->
case jd2ejd:import_file(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["import-dir", Path]) ->
case jd2ejd:import_dir(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["delete-expired-messages"]) ->
mod_offline:remove_expired_messages(),
?STATUS_SUCCESS;
process(["mnesia"]) ->
?PRINT("~p~n", [mnesia:system_info(all)]),
?STATUS_SUCCESS;
process(["mnesia", "info"]) ->
mnesia:info(),
?STATUS_SUCCESS;
process(["mnesia", Arg]) when is_list(Arg) ->
case catch mnesia:system_info(list_to_atom(Arg)) of
{'EXIT', Error} -> ?PRINT("Error: ~p~n", [Error]);
Return -> ?PRINT("~p~n", [Return])
end,
?STATUS_SUCCESS;
process(["delete-old-messages", Days]) ->
case catch list_to_integer(Days) of
{'EXIT',{Reason, _Stack}} ->
?PRINT("Can't delete old messages (~p). Please pass an integer as parameter.~n",
[Reason]),
?STATUS_ERROR;
Integer when Integer >= 0 ->
{atomic, _} = mod_offline:remove_old_messages(Integer),
?PRINT("Removed messages older than ~s days~n", [Days]),
?STATUS_SUCCESS;
_Integer ->
?PRINT("Can't delete old messages. Please pass a positive integer as parameter.~n", []),
?STATUS_ERROR
end;
process(["vhost", H | Args]) ->
case jlib:nameprep(H) of
false ->
?PRINT("Bad hostname: ~p~n", [H]),
?STATUS_ERROR;
Host ->
case ejabberd_hooks:run_fold(
ejabberd_ctl_process, Host, false, [Host, Args]) of
false ->
print_vhost_usage(Host),
?STATUS_USAGE;
Status ->
Status
end
end;
process(Args) ->
case ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
false ->
print_usage(),
?STATUS_USAGE;
Status ->
Status
end.
print_usage() ->
CmdDescs =
[{"status", "get ejabberd status"},
{"stop", "stop ejabberd"},
{"restart", "restart ejabberd"},
{"reopen-log", "reopen log file"},
{"register user server password", "register a user"},
{"unregister user server", "unregister a user"},
{"backup file", "store a database backup to file"},
{"restore file", "restore a database backup from file"},
{"install-fallback file", "install a database fallback from file"},
{"dump file", "dump a database to a text file"},
{"load file", "restore a database from a text file"},
{"import-file file", "import user data from jabberd 1.4 spool file"},
{"import-dir dir", "import user data from jabberd 1.4 spool directory"},
{"delete-expired-messages", "delete expired offline messages from database"},
{"delete-old-messages n", "delete offline messages older than n days from database"},
{"mnesia [info]", "show information of Mnesia system"},
{"vhost host ...", "execute host-specific commands"}] ++
ets:tab2list(ejabberd_ctl_cmds),
MaxCmdLen =
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs)),
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
?PRINT(
"Usage: ejabberdctl [--node nodename] command [options]~n"
"~n"
"Available commands in this ejabberd node:~n"
++ FmtCmdDescs ++
"~n"
"Examples:~n"
" ejabberdctl restart~n"
" ejabberdctl --node ejabberd@host restart~n"
" ejabberdctl vhost jabber.example.org ...~n",
[]).
print_vhost_usage(Host) ->
CmdDescs =
ets:select(ejabberd_ctl_host_cmds,
[{{{Host, '$1'}, '$2'}, [], [{{'$1', '$2'}}]}]),
MaxCmdLen =
if
CmdDescs == [] ->
0;
true ->
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs))
end,
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
?PRINT(
"Usage: ejabberdctl [--node nodename] vhost hostname command [options]~n"
"~n"
"Available commands in this ejabberd node and this vhost:~n"
++ FmtCmdDescs ++
"~n"
"Examples:~n"
" ejabberdctl vhost "++Host++" registered-users~n",
[]).
register_commands(CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_cmds, CmdDescs),
ejabberd_hooks:add(ejabberd_ctl_process,
Module, Function, 50),
ok.
register_commands(Host, CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_host_cmds,
[{{Host, Cmd}, Desc} || {Cmd, Desc} <- CmdDescs]),
ejabberd_hooks:add(ejabberd_ctl_process, Host,
Module, Function, 50),
ok.
unregister_commands(CmdDescs, Module, Function) ->
lists:foreach(fun(CmdDesc) ->
ets:delete_object(ejabberd_ctl_cmds, CmdDesc)
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
unregister_commands(Host, CmdDescs, Module, Function) ->
lists:foreach(fun({Cmd, Desc}) ->
ets:delete_object(ejabberd_ctl_host_cmds,
{{Host, Cmd}, Desc})
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
dump_to_textfile(File) ->
dump_to_textfile(mnesia:system_info(is_running), file:open(File, write)).
dump_to_textfile(yes, {ok, F}) ->
Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)),
Tabs = lists:filter(
fun(T) ->
case mnesia:table_info(T, storage_type) of
disc_copies -> true;
disc_only_copies -> true;
_ -> false
end
end, Tabs1),
Defs = lists:map(
fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)},
{attributes, mnesia:table_info(T, attributes)}]}
end,
Tabs),
io:format(F, "~p.~n", [{tables, Defs}]),
lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
file:close(F);
dump_to_textfile(_, {ok, F}) ->
file:close(F),
{error, mnesia_not_running};
dump_to_textfile(_, {error, Reason}) ->
{error, Reason}.
dump_tab(F, T) ->
W = mnesia:table_info(T, wild_pattern),
{atomic,All} = mnesia:transaction(
fun() -> mnesia:match_object(T, W, read) end),
lists:foreach(
fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
%% Function copied from Erlang/OTP lib/sasl/src/sasl.erl which doesn't export it
get_sasl_error_logger_type () ->
case application:get_env (sasl, errlog_type) of
{ok, error} -> error;
{ok, progress} -> progress;
{ok, all} -> all;
{ok, Bad} -> exit ({bad_config, {sasl, {errlog_type, Bad}}});
_ -> all
end.
cat ejabberdctl.template
#!/bin/sh
# define default configuration
POLL=true
SMP=auto
ERL_MAX_PORTS=32000
ERL_PROCESSES=250000
ERL_MAX_ETS_TABLES=1400
# define default environment variables
NODE=ejabberd
HOST=localhost
ERLANG_NODE=$NODE@$HOST
ERL=@erl@
ROOTDIR=@rootdir@
EJABBERD_CONFIG_PATH=$ROOTDIR/etc/ejabberd/ejabberd.cfg
LOGS_DIR=$ROOTDIR/var/log/ejabberd/
EJABBERD_DB=$ROOTDIR/var/lib/ejabberd/db/$NODE
# read custom configuration
CONFIG=$ROOTDIR/etc/ejabberd/ejabberdctl.cfg
[ -f "$CONFIG" ] && . "$CONFIG"
# parse command line parameters
ARGS=
while [ $# -ne 0 ] ; do
PARAM=$1
shift
case $PARAM in
--) break ;;
--node) ERLANG_NODE=$1; shift ;;
--config) EJABBERD_CONFIG_PATH=$1 ; shift ;;
--ctl-config) CONFIG=$1 ; shift ;;
--logs) LOGS_DIR=$1 ; shift ;;
--spool) EJABBERD_DB=$1 ; shift ;;
*) ARGS="$ARGS $PARAM" ;;
esac
done
NAME=-name
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES"
# define additional environment variables
EJABBERD_EBIN=$ROOTDIR/var/lib/ejabberd/ebin
EJABBERD_MSGS_PATH=$ROOTDIR/var/lib/ejabberd/priv/msgs
EJABBERD_SO_PATH=$ROOTDIR/var/lib/ejabberd/priv/lib
EJABBERD_BIN_PATH=$ROOTDIR/var/lib/ejabberd/priv/bin
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
SASL_LOG_PATH=$LOGS_DIR/sasl.log
DATETIME=`date "+%Y%m%d-%H%M%S"`
ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump
ERL_INETRC=$ROOTDIR/etc/ejabberd/inetrc
HOME=$ROOTDIR/var/lib/ejabberd
# export global variables
export EJABBERD_CONFIG_PATH
export EJABBERD_MSGS_PATH
export EJABBERD_LOG_PATH
export EJABBERD_SO_PATH
export EJABBERD_BIN_PATH
export ERL_CRASH_DUMP
export ERL_INETRC
export ERL_MAX_PORTS
export ERL_MAX_ETS_TABLES
export HOME
[ -d $EJABBERD_DB ] || mkdir -p $EJABBERD_DB
[ -d $LOGS_DIR ] || mkdir -p $LOGS_DIR
# Compatibility in ZSH
#setopt shwordsplit 2>/dev/null
# start server
start ()
{
$ERL \
$NAME $ERLANG_NODE \
-noinput -detached \
-pa $EJABBERD_EBIN \
-mnesia dir "\"$EJABBERD_DB\"" \
-s ejabberd \
-sasl sasl_error_logger \{file,\"$SASL_LOG_PATH\"\} \
$ERLANG_OPTS $ARGS "$@"
}
# attach to server
debug ()
{
echo "--------------------------------------------------------------------"
echo ""
echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell"
echo "to an already running ejabberd node."
echo "If an ERROR is printed, it means the connection was not succesfull."
echo "You can interact with the ejabberd node if you know how to use it."
echo "Please be extremely cautious with your actions,"
echo "and exit immediately if you are not completely sure."
echo ""
echo "To detach this shell from ejabberd, press:"
echo " control+c, control+c"
echo ""
echo "--------------------------------------------------------------------"
echo "Press any key to continue"
read foo
echo ""
$ERL \
$NAME ${NODE}debug \
-remsh $ERLANG_NODE \
$ERLANG_OPTS $ARGS "$@"
}
# start interactive server
live ()
{
echo "--------------------------------------------------------------------"
echo ""
echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode."
echo "All log messages will be shown in the command shell."
echo "You can interact with the ejabberd node if you know how to use it."
echo "Please be extremely cautious with your actions,"
echo "and exit immediately if you are not completely sure."
echo ""
echo "To exit this LIVE mode and stop ejabberd, press:"
echo " q(). and press the Enter key"
echo ""
echo "--------------------------------------------------------------------"
echo "Press any key to continue"
read foo
echo ""
$ERL \
$NAME $ERLANG_NODE \
-pa $EJABBERD_EBIN \
-mnesia dir "\"$EJABBERD_DB\"" \
-s ejabberd \
$ERLANG_OPTS $ARGS "$@"
}
# common control function
ctl ()
{
$ERL \
$NAME ejabberdctl \
-noinput \
-pa $EJABBERD_EBIN \
-s ejabberd_ctl -extra $ERLANG_NODE $@
result=$?
case $result in
0) :;;
*)
echo ""
echo "Commands to start an ejabberd node:"
echo " start Start an ejabberd node in server mode"
echo " debug Attach an interactive Erlang shell to a running ejabberd node"
echo " live Start an ejabberd node in live (interactive) mode"
echo ""
echo "Optional parameters when starting an ejabberd node:"
echo " --config file Config file of ejabberd: $EJABBERD_CONFIG_PATH"
echo " --ctl-config file Config file of ejabberdctl: $CONFIG"
echo " --logs dir Directory for logs: $LOGS_DIR"
echo " --spool dir Database spool dir: $EJABBERD_DB"
echo " --node nodename ejabberd node name: $ERLANG_NODE"
echo "";;
esac
return $result
}
# display ctl usage
usage ()
{
ctl
exit
}
case $ARGS in
' start') start;;
' debug') debug;;
' live') live;;
*) ctl $ARGS;;
esac
cat ejabberdctl.cfg.example
#
# In this file you can configure options that are passed by ejabberdctl
# to the erlang runtime system when starting ejabberd
#
# POLL: Kernel polling ([true|false])
#
# The kernel polling option requires support in the kernel.
# Additionaly, you need to enable this feature while compiling Erlang.
#
# Default: true
#
#POLL=true
# SMP: SMP support ([enable|auto|disable])
#
# Explanation in Erlang/OTP documentation:
# enable: starts the Erlang runtime system with SMP support enabled.
# This may fail if no runtime system with SMP support is available.
# auto: starts the Erlang runtime system with SMP support enabled if it
# is available and more than one logical processor are detected.
# disable: starts a runtime system without SMP support.
#
# Default: auto
#
#SMP=auto
# ERL_MAX_PORTS: Maximum number of simultaneously open Erlang ports
#
# ejabberd consumes two or three ports for every connection, either
# from a client or from another Jabber server. So take this into
# account when setting this limit.
#
# Default: 32000
# Maximum: 268435456
#
#ERL_MAX_PORTS=32000
# PROCESSES: Maximum number of Erlang processes
#
# Erlang consumes a lot of lightweight processes. If there is a lot of activity
# on ejabberd so that the maximum number of proccesses is reached, people will
# experiment greater latency times. As these processes are implemented in
# Erlang, and therefore not related to the operating system processes, you do
# not have to worry about allowing a huge number of them.
#
# Default: 250000
# Maximum: 268435456
#
#PROCESSES=250000
# ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables
#
# The number of concurrent ETS and Mnesia tables is limited. When the limit is
# reached, errors will appear in the logs:
# ** Too many db tables **
# You can safely increase this limit when starting ejabberd. It impacts memory
# consumption but the difference will be quite small.
#
# Default: 1400
#
#ERL_MAX_ETS_TABLES=1400
# The next variable allows to explicitly specify erlang node for ejabberd
# It can be given in different formats:
# ERLANG_NODE=ejabberd
# Lets erlang add hostname to the node (ejabberd uses short name in this case)
# ERLANG_NODE=ejabberd@hostname
# Erlang uses node name as is (so make sure that hostname is a real
# machine hostname or you'll not be able to control ejabberd)
# ERLANG_NODE=ejabberd@hostname.domainname
# The same as previous, but erlang will use long hostname
# (see erl (1) manual for details)
#
# Default: ejabberd
#
#ERLANG_NODE=ejabberd
cat ejabberd_ctl.erl
%%%----------------------------------------------------------------------
%%% File : ejabberd_ctl.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Ejabberd admin tool
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_ctl).
-author('alexey@process-one.net').
-export([start/0,
init/0,
process/1,
dump_to_textfile/1,
register_commands/3,
register_commands/4,
unregister_commands/3,
unregister_commands/4]).
-include("ejabberd_ctl.hrl").
-include("ejabberd.hrl").
start() ->
case init:get_plain_arguments() of
[SNode | Args] ->
SNode1 = case string:tokens(SNode, "@") of
[_Node, _Server] ->
SNode;
_ ->
case net_kernel:longnames() of
true ->
SNode ++ "@" ++ inet_db:gethostname() ++
"." ++ inet_db:res_option(domain);
false ->
SNode ++ "@" ++ inet_db:gethostname();
_ ->
SNode
end
end,
Node = list_to_atom(SNode1),
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
{badrpc, Reason} ->
?PRINT("RPC failed on the node ~p: ~p~n",
[Node, Reason]),
?STATUS_BADRPC;
S ->
S
end,
halt(Status);
_ ->
print_usage(),
halt(?STATUS_USAGE)
end.
init() ->
ets:new(ejabberd_ctl_cmds, [named_table, set, public]),
ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]).
process(["status"]) ->
{InternalStatus, ProvidedStatus} = init:get_status(),
?PRINT("Node ~p is ~p. Status: ~p~n",
[node(), InternalStatus, ProvidedStatus]),
case lists:keysearch(ejabberd, 1, application:which_applications()) of
false ->
?PRINT("ejabberd is not running~n", []),
?STATUS_ERROR;
{value,_Version} ->
?PRINT("ejabberd is running~n", []),
?STATUS_SUCCESS
end;
process(["stop"]) ->
init:stop(),
?STATUS_SUCCESS;
process(["restart"]) ->
init:restart(),
?STATUS_SUCCESS;
process(["reopen-log"]) ->
ejabberd_logger_h:reopen_log(),
case application:get_env(sasl,sasl_error_logger) of
{ok, {file, SASLfile}} ->
error_logger:delete_report_handler(sasl_report_file_h),
ejabberd_logger_h:rotate_log(SASLfile),
error_logger:add_report_handler(sasl_report_file_h,
{SASLfile, get_sasl_error_logger_type()});
_ -> false
end,
?STATUS_SUCCESS;
process(["register", User, Server, Password]) ->
case ejabberd_auth:try_register(User, Server, Password) of
{atomic, ok} ->
?STATUS_SUCCESS;
{atomic, exists} ->
?PRINT("User ~p already registered at node ~p~n",
[User ++ "@" ++ Server, node()]),
?STATUS_ERROR;
{error, Reason} ->
?PRINT("Can't register user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR
end;
process(["unregister", User, Server]) ->
case ejabberd_auth:remove_user(User, Server) of
{error, Reason} ->
?PRINT("Can't unregister user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR;
_ ->
?STATUS_SUCCESS
end;
process(["backup", Path]) ->
case mnesia:backup(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't store backup in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["dump", Path]) ->
case dump_to_textfile(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't store dump in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["load", Path]) ->
case mnesia:load_textfile(Path) of
{atomic, ok} ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't load dump in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["restore", Path]) ->
case ejabberd_admin:restore(Path) of
{atomic, _} ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't restore backup from ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR;
{aborted,{no_exists,Table}} ->
?PRINT("Can't restore backup from ~p at node ~p: Table ~p does not exist.~n",
[filename:absname(Path), node(), Table]),
?STATUS_ERROR;
{aborted,enoent} ->
?PRINT("Can't restore backup from ~p at node ~p: File not found.~n",
[filename:absname(Path), node()]),
?STATUS_ERROR
end;
process(["install-fallback", Path]) ->
case mnesia:install_fallback(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't install fallback from ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["import-file", Path]) ->
case jd2ejd:import_file(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["import-dir", Path]) ->
case jd2ejd:import_dir(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
?PRINT("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["delete-expired-messages"]) ->
mod_offline:remove_expired_messages(),
?STATUS_SUCCESS;
process(["mnesia"]) ->
?PRINT("~p~n", [mnesia:system_info(all)]),
?STATUS_SUCCESS;
process(["mnesia", "info"]) ->
mnesia:info(),
?STATUS_SUCCESS;
process(["mnesia", Arg]) when is_list(Arg) ->
case catch mnesia:system_info(list_to_atom(Arg)) of
{'EXIT', Error} -> ?PRINT("Error: ~p~n", [Error]);
Return -> ?PRINT("~p~n", [Return])
end,
?STATUS_SUCCESS;
process(["delete-old-messages", Days]) ->
case catch list_to_integer(Days) of
{'EXIT',{Reason, _Stack}} ->
?PRINT("Can't delete old messages (~p). Please pass an integer as parameter.~n",
[Reason]),
?STATUS_ERROR;
Integer when Integer >= 0 ->
{atomic, _} = mod_offline:remove_old_messages(Integer),
?PRINT("Removed messages older than ~s days~n", [Days]),
?STATUS_SUCCESS;
_Integer ->
?PRINT("Can't delete old messages. Please pass a positive integer as parameter.~n", []),
?STATUS_ERROR
end;
process(["vhost", H | Args]) ->
case jlib:nameprep(H) of
false ->
?PRINT("Bad hostname: ~p~n", [H]),
?STATUS_ERROR;
Host ->
case ejabberd_hooks:run_fold(
ejabberd_ctl_process, Host, false, [Host, Args]) of
false ->
print_vhost_usage(Host),
?STATUS_USAGE;
Status ->
Status
end
end;
process(Args) ->
case ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
false ->
print_usage(),
?STATUS_USAGE;
Status ->
Status
end.
print_usage() ->
CmdDescs =
[{"status", "get ejabberd status"},
{"stop", "stop ejabberd"},
{"restart", "restart ejabberd"},
{"reopen-log", "reopen log file"},
{"register user server password", "register a user"},
{"unregister user server", "unregister a user"},
{"backup file", "store a database backup to file"},
{"restore file", "restore a database backup from file"},
{"install-fallback file", "install a database fallback from file"},
{"dump file", "dump a database to a text file"},
{"load file", "restore a database from a text file"},
{"import-file file", "import user data from jabberd 1.4 spool file"},
{"import-dir dir", "import user data from jabberd 1.4 spool directory"},
{"delete-expired-messages", "delete expired offline messages from database"},
{"delete-old-messages n", "delete offline messages older than n days from database"},
{"mnesia [info]", "show information of Mnesia system"},
{"vhost host ...", "execute host-specific commands"}] ++
ets:tab2list(ejabberd_ctl_cmds),
MaxCmdLen =
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs)),
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
?PRINT(
"Usage: ejabberdctl [--node nodename] command [options]~n"
"~n"
"Available commands in this ejabberd node:~n"
++ FmtCmdDescs ++
"~n"
"Examples:~n"
" ejabberdctl restart~n"
" ejabberdctl --node ejabberd@host restart~n"
" ejabberdctl vhost jabber.example.org ...~n",
[]).
print_vhost_usage(Host) ->
CmdDescs =
ets:select(ejabberd_ctl_host_cmds,
[{{{Host, '$1'}, '$2'}, [], [{{'$1', '$2'}}]}]),
MaxCmdLen =
if
CmdDescs == [] ->
0;
true ->
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs))
end,
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
?PRINT(
"Usage: ejabberdctl [--node nodename] vhost hostname command [options]~n"
"~n"
"Available commands in this ejabberd node and this vhost:~n"
++ FmtCmdDescs ++
"~n"
"Examples:~n"
" ejabberdctl vhost "++Host++" registered-users~n",
[]).
register_commands(CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_cmds, CmdDescs),
ejabberd_hooks:add(ejabberd_ctl_process,
Module, Function, 50),
ok.
register_commands(Host, CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_host_cmds,
[{{Host, Cmd}, Desc} || {Cmd, Desc} <- CmdDescs]),
ejabberd_hooks:add(ejabberd_ctl_process, Host,
Module, Function, 50),
ok.
unregister_commands(CmdDescs, Module, Function) ->
lists:foreach(fun(CmdDesc) ->
ets:delete_object(ejabberd_ctl_cmds, CmdDesc)
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
unregister_commands(Host, CmdDescs, Module, Function) ->
lists:foreach(fun({Cmd, Desc}) ->
ets:delete_object(ejabberd_ctl_host_cmds,
{{Host, Cmd}, Desc})
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
dump_to_textfile(File) ->
dump_to_textfile(mnesia:system_info(is_running), file:open(File, write)).
dump_to_textfile(yes, {ok, F}) ->
Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)),
Tabs = lists:filter(
fun(T) ->
case mnesia:table_info(T, storage_type) of
disc_copies -> true;
disc_only_copies -> true;
_ -> false
end
end, Tabs1),
Defs = lists:map(
fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)},
{attributes, mnesia:table_info(T, attributes)}]}
end,
Tabs),
io:format(F, "~p.~n", [{tables, Defs}]),
lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
file:close(F);
dump_to_textfile(_, {ok, F}) ->
file:close(F),
{error, mnesia_not_running};
dump_to_textfile(_, {error, Reason}) ->
{error, Reason}.
dump_tab(F, T) ->
W = mnesia:table_info(T, wild_pattern),
{atomic,All} = mnesia:transaction(
fun() -> mnesia:match_object(T, W, read) end),
lists:foreach(
fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
%% Function copied from Erlang/OTP lib/sasl/src/sasl.erl which doesn't export it
get_sasl_error_logger_type () ->
case application:get_env (sasl, errlog_type) of
{ok, error} -> error;
{ok, progress} -> progress;
{ok, all} -> all;
{ok, Bad} -> exit ({bad_config, {sasl, {errlog_type, Bad}}});
_ -> all
end.
评论
1 楼
litaocheng
2009-04-09
呵呵,真不错。
可以做为项目模板使用。
哦。刚才看到 erl -remsh, 试用了一下,还是很神奇的。
出了CTRL + G 连接远程的node,这个也不错。。
赞!
可以做为项目模板使用。
哦。刚才看到 erl -remsh, 试用了一下,还是很神奇的。
出了CTRL + G 连接远程的node,这个也不错。。
赞!
发表评论
-
OTP R14A今天发布了
2010-06-17 14:36 2676以下是这次发布的亮点,没有太大的性能改进, 主要是修理了很多B ... -
R14A实现了EEP31,添加了binary模块
2010-05-21 15:15 3030Erlang的binary数据结构非常强大,而且偏向底层,在作 ... -
如何查看节点的可用句柄数目和已用句柄数
2010-04-08 03:31 4813很多同学在使用erlang的过程中, 碰到了很奇怪的问题, 后 ... -
获取Erlang系统信息的代码片段
2010-04-06 21:49 3475从lib/megaco/src/tcp/megaco_tcp_ ... -
iolist跟list有什么区别?
2010-04-06 20:30 6529看到erlang-china.org上有个 ... -
erlang:send_after和erlang:start_timer的使用解释
2010-04-06 18:31 8386前段时间arksea 同学提出这个问题, 因为文档里面写的很不 ... -
Latest news from the Erlang/OTP team at Ericsson 2010
2010-04-05 19:23 2013参考Talk http://www.erlang-factor ... -
对try 异常 运行的疑问,为什么出现两种结果
2010-04-05 19:22 2841郎咸武<langxianzhe@163.com> ... -
Erlang ERTS Async基础设施
2010-03-19 00:03 2517其实Erts的Async做的很不错的, 相当的完备, 性能又高 ... -
CloudI 0.0.9 Released, A Cloud as an Interface
2010-03-09 22:32 2474基于Erlang的云平台 看了下代码 质量还是不错的 完成了不 ... -
Memory matters - even in Erlang (再次说明了了解内存如何工作的必要性)
2010-03-09 20:26 3439原文地址:http://www.lshift.net/blog ... -
Some simple examples of using Erlang’s XPath implementation
2010-03-08 23:30 2050原文地址 http://www.lshift.net/blog ... -
lcnt 环境搭建
2010-02-26 16:19 2613抄书:otp_doc_html_R13B04/lib/tool ... -
Erlang强大的代码重构工具 tidier
2010-02-25 16:22 2485Jan 29, 2010 We are very happy ... -
[Feb 24 2010] Erlang/OTP R13B04 has been released
2010-02-25 00:31 1387Erlang/OTP R13B04 has been rele ... -
R13B04 Installation
2010-01-28 10:28 1389R13B04后erlang的源码编译为了考虑移植性,就改变了编 ... -
Running tests
2010-01-19 14:51 1485R13B03以后 OTP的模块加入了大量的测试模块,这些模块都 ... -
R13B04在细化Binary heap
2010-01-14 15:11 1508从github otp的更新日志可以清楚的看到otp R13B ... -
R13B03 binary vheap有助减少binary内存压力
2009-11-29 16:07 1667R13B03 binary vheap有助减少binary内存 ... -
erl_nif 扩展erlang的另外一种方法
2009-11-26 01:02 3218我们知道扩展erl有2种方法, driver和port. 这2 ...
相关推荐
2. ejabberdctl help:用户可以通过该命令来查看Ejabberdctl的帮助信息。 3. ejabberdctl命令:用户可以通过该命令来执行各种管理任务,例如启动、停止、重启Ejabberd服务等。 管理控制台 Ejabberd提供了一个基于...
Ejabberd 安装配置 Ejabberd 是一个基于 XMPP 协议的 instant messaging 服务器,...Ejabberd 安装配置需要按照安装指南进行安装,配置环境变量,启动 Ejabberd 服务,并使用 ejabberdctl 命令进行管理和状态查看。
如果尚未执行此操作,请运行ejabberdctl modules_update_specs以检索可用模块的列表。 运行ejabberdctl module_install 以获取源代码,并将beam文件编译并安装到ejabberd的模块搜索路径中。 此路径是~/.ejabberd-...
- **重启服务器**:使用`ejabberdctl restart`命令重启服务器。 #### 七、网关集成 为了实现与其他即时通讯服务的互通,可以安装和配置相应的网关。例如,与MSN或AIM等服务的集成通常需要额外安装相应的网关软件。 ...
ejabberdctl module_install ejabberd_offline_my 配置 如何启用 最简单的方法是在ejabberd.yml模块设置下启用mod_offline_my并设置某些配置选项。 配置选项 mod_offline_my需要一些参数才能正常运行。 应在ejabberd...