浏览 3940 次
锁定老帖子 主题:读ejabberdctl学先进科技
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-04-02
最完美的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. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-04-09
呵呵,真不错。
可以做为项目模板使用。 哦。刚才看到 erl -remsh, 试用了一下,还是很神奇的。 出了CTRL + G 连接远程的node,这个也不错。。 赞! |
|
返回顶楼 | |