From c0272ae7669b548a3cd568229748377feba71e57 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sun, 31 Jul 2016 14:51:16 +0300 Subject: [PATCH] Rewrite mod_stats to use XML generator --- include/xmpp_codec.hrl | 14 +++-- src/mod_stats.erl | 104 ++++++++++++---------------------- src/xmpp_codec.erl | 123 ++++++++++++++++++++++++----------------- tools/xmpp_codec.spec | 17 +++--- 4 files changed, 126 insertions(+), 132 deletions(-) diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl index acbc81e4a..e3eb31285 100644 --- a/include/xmpp_codec.hrl +++ b/include/xmpp_codec.hrl @@ -227,10 +227,14 @@ nick :: binary()}). -type muc_actor() :: #muc_actor{}. +-record(stat_error, {code :: integer(), + reason = <<>> :: binary()}). +-type stat_error() :: #stat_error{}. + -record(stat, {name :: binary(), - units :: binary(), - value :: binary(), - error = [] :: [{integer(),'undefined' | binary()}]}). + units = <<>> :: binary(), + value = <<>> :: binary(), + error :: #stat_error{}}). -type stat() :: #stat{}. -record(addresses, {list = [] :: [#address{}]}). @@ -367,7 +371,8 @@ -record(stream_features, {sub_els = [] :: [any()]}). -type stream_features() :: #stream_features{}. --record(stats, {stat = [] :: [#stat{}]}). +-record(stats, {list = [] :: [#stat{}], + node = <<>> :: binary()}). -type stats() :: #stats{}. -record(pubsub_items, {node :: binary(), @@ -980,6 +985,7 @@ feature_register() | register() | sm_r() | + stat_error() | error() | stream_error() | muc_user() | diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 99059839a..c4b8ddb15 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -31,65 +31,39 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/3, - mod_opt_type/1, depends/2]). +-export([start/2, stop/1, process_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, one_queue), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_STATS, ?MODULE, process_local_iq, IQDisc). + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_STATS, + ?MODULE, process_iq, IQDisc). stop(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_STATS). + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_STATS). depends(_Host, _Opts) -> []. -process_local_iq(_From, To, - #iq{id = _ID, type = Type, xmlns = XMLNS, - sub_el = SubEl, lang = Lang} = - IQ) -> - case Type of - set -> - Txt = <<"Value 'set' of 'type' attribute is not allowed">>, - IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]}; - get -> - #xmlel{children = Els} = SubEl, - Node = str:tokens(fxml:get_tag_attr_s(<<"node">>, SubEl), - <<"/">>), - Names = get_names(Els, []), - case get_local_stats(To#jid.server, Node, Names, Lang) of - {result, Res} -> - IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, XMLNS}], - children = Res}]}; - {error, Error} -> - IQ#iq{type = error, sub_el = [SubEl, Error]} - end +process_iq(#iq{type = set, lang = Lang} = IQ) -> + Txt = <<"Value 'set' of 'type' attribute is not allowed">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_iq(#iq{type = get, to = To, lang = Lang, + sub_els = [#stats{} = Stats]} = IQ) -> + Node = str:tokens(Stats#stats.node, <<"/">>), + Names = [Name || #stat{name = Name} <- Stats#stats.list], + case get_local_stats(To#jid.server, Node, Names, Lang) of + {result, List} -> + xmpp:make_iq_result(IQ, Stats#stats{list = List}); + {error, Error} -> + xmpp:make_error(IQ, Error) end. -get_names([], Res) -> Res; -get_names([#xmlel{name = <<"stat">>, attrs = Attrs} - | Els], - Res) -> - Name = fxml:get_attr_s(<<"name">>, Attrs), - case Name of - <<"">> -> get_names(Els, Res); - _ -> get_names(Els, [Name | Res]) - end; -get_names([_ | Els], Res) -> get_names(Els, Res). - --define(STAT(Name), - #xmlel{name = <<"stat">>, attrs = [{<<"name">>, Name}], - children = []}). +-define(STAT(Name), #stat{name = Name}). get_local_stats(_Server, [], [], _Lang) -> {result, @@ -115,7 +89,7 @@ get_local_stats(_Server, [<<"running nodes">>, ENode], case search_running_node(ENode) of false -> Txt = <<"No running node found">>, - {error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)}; + {error, xmpp:err_item_not_found(Txt, Lang)}; Node -> {result, lists:map(fun (Name) -> get_node_stat(Node, Name) end, @@ -123,27 +97,19 @@ get_local_stats(_Server, [<<"running nodes">>, ENode], end; get_local_stats(_Server, _, _, Lang) -> Txt = <<"No statistics found for this item">>, - {error, ?ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Txt)}. + {error, xmpp:err_feature_not_implemented(Txt, Lang)}. --define(STATVAL(Val, Unit), - #xmlel{name = <<"stat">>, - attrs = - [{<<"name">>, Name}, {<<"units">>, Unit}, - {<<"value">>, Val}], - children = []}). +-define(STATVAL(Val, Unit), #stat{name = Name, units = Unit, value = Val}). -define(STATERR(Code, Desc), - #xmlel{name = <<"stat">>, attrs = [{<<"name">>, Name}], - children = - [#xmlel{name = <<"error">>, - attrs = [{<<"code">>, Code}], - children = [{xmlcdata, Desc}]}]}). + #stat{name = Name, + error = #stat_error{code = Code, reason = Desc}}). get_local_stat(Server, [], Name) when Name == <<"users/online">> -> case catch ejabberd_sm:get_vh_session_list(Server) of {'EXIT', _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Users -> ?STATVAL((iolist_to_binary(integer_to_list(length(Users)))), <<"users">>) @@ -154,7 +120,7 @@ get_local_stat(Server, [], Name) ejabberd_auth:get_vh_registered_users_number(Server) of {'EXIT', _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); NUsers -> ?STATVAL((iolist_to_binary(integer_to_list(NUsers))), <<"users">>) @@ -163,7 +129,7 @@ get_local_stat(_Server, [], Name) when Name == <<"users/all-hosts/online">> -> case catch mnesia:table_info(session, size) of {'EXIT', _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Users -> ?STATVAL((iolist_to_binary(integer_to_list(Users))), <<"users">>) @@ -178,7 +144,7 @@ get_local_stat(_Server, [], Name) ?STATVAL((iolist_to_binary(integer_to_list(NumUsers))), <<"users">>); get_local_stat(_Server, _, Name) -> - ?STATERR(<<"404">>, <<"Not Found">>). + ?STATERR(404, <<"Not Found">>). get_node_stat(Node, Name) when Name == <<"time/uptime">> -> @@ -186,7 +152,7 @@ get_node_stat(Node, Name) [wall_clock]) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); CPUTime -> ?STATVAL(list_to_binary( io_lib:format("~.3f", @@ -198,7 +164,7 @@ get_node_stat(Node, Name) case catch ejabberd_cluster:call(Node, erlang, statistics, [runtime]) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); RunTime -> ?STATVAL(list_to_binary( io_lib:format("~.3f", @@ -211,7 +177,7 @@ get_node_stat(Node, Name) dirty_get_my_sessions_list, []) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Users -> ?STATVAL((iolist_to_binary(integer_to_list(length(Users)))), <<"users">>) @@ -222,7 +188,7 @@ get_node_stat(Node, Name) [transaction_commits]) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((iolist_to_binary(integer_to_list(Transactions))), <<"transactions">>) @@ -233,7 +199,7 @@ get_node_stat(Node, Name) [transaction_failures]) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((iolist_to_binary(integer_to_list(Transactions))), <<"transactions">>) @@ -244,7 +210,7 @@ get_node_stat(Node, Name) [transaction_restarts]) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((iolist_to_binary(integer_to_list(Transactions))), <<"transactions">>) @@ -255,13 +221,13 @@ get_node_stat(Node, Name) [transaction_log_writes]) of {badrpc, _Reason} -> - ?STATERR(<<"500">>, <<"Internal Server Error">>); + ?STATERR(500, <<"Internal Server Error">>); Transactions -> ?STATVAL((iolist_to_binary(integer_to_list(Transactions))), <<"transactions">>) end; get_node_stat(_, Name) -> - ?STATERR(<<"404">>, <<"Not Found">>). + ?STATERR(404, <<"Not Found">>). search_running_node(SNode) -> search_running_node(SNode, diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl index 6fddf97ed..e0a5d47c9 100644 --- a/src/xmpp_codec.erl +++ b/src/xmpp_codec.erl @@ -2222,11 +2222,15 @@ encode({bookmark_url, _, _} = Url) -> encode({bookmark_storage, _, _} = Storage) -> encode_bookmarks_storage(Storage, [{<<"xmlns">>, <<"storage:bookmarks">>}]); +encode({stat_error, _, _} = Error) -> + encode_stat_error(Error, + [{<<"xmlns">>, + <<"http://jabber.org/protocol/stats">>}]); encode({stat, _, _, _, _} = Stat) -> encode_stat(Stat, [{<<"xmlns">>, <<"http://jabber.org/protocol/stats">>}]); -encode({stats, _} = Query) -> +encode({stats, _, _} = Query) -> encode_stats(Query, [{<<"xmlns">>, <<"http://jabber.org/protocol/stats">>}]); @@ -2734,8 +2738,9 @@ get_name({bookmark_conference, _, _, _, _, _}) -> <<"conference">>; get_name({bookmark_url, _, _}) -> <<"url">>; get_name({bookmark_storage, _, _}) -> <<"storage">>; +get_name({stat_error, _, _}) -> <<"error">>; get_name({stat, _, _, _, _}) -> <<"stat">>; -get_name({stats, _}) -> <<"query">>; +get_name({stats, _, _}) -> <<"query">>; get_name({iq, _, _, _, _, _, _}) -> <<"iq">>; get_name({message, _, _, _, _, _, _, _, _, _}) -> <<"message">>; @@ -2939,9 +2944,11 @@ get_ns({bookmark_conference, _, _, _, _, _}) -> get_ns({bookmark_url, _, _}) -> <<"storage:bookmarks">>; get_ns({bookmark_storage, _, _}) -> <<"storage:bookmarks">>; +get_ns({stat_error, _, _}) -> + <<"http://jabber.org/protocol/stats">>; get_ns({stat, _, _, _, _}) -> <<"http://jabber.org/protocol/stats">>; -get_ns({stats, _}) -> +get_ns({stats, _, _}) -> <<"http://jabber.org/protocol/stats">>; get_ns({iq, _, _, _, _, _, _}) -> <<"jabber:client">>; get_ns({message, _, _, _, _, _, _, _, _, _}) -> @@ -3254,8 +3261,9 @@ pp(bookmark_conference, 5) -> [name, jid, autojoin, nick, password]; pp(bookmark_url, 2) -> [name, url]; pp(bookmark_storage, 2) -> [conference, url]; +pp(stat_error, 2) -> [code, reason]; pp(stat, 4) -> [name, units, value, error]; -pp(stats, 1) -> [stat]; +pp(stats, 2) -> [list, node]; pp(iq, 6) -> [id, type, lang, from, to, sub_els]; pp(message, 9) -> [id, type, lang, from, to, subject, body, thread, @@ -25097,53 +25105,70 @@ encode_iq_attr_to(_val, _acc) -> decode_stats(__TopXMLNS, __IgnoreEls, {xmlel, <<"query">>, _attrs, _els}) -> - Stat = decode_stats_els(__TopXMLNS, __IgnoreEls, _els, + List = decode_stats_els(__TopXMLNS, __IgnoreEls, _els, []), - {stats, Stat}. + Node = decode_stats_attrs(__TopXMLNS, _attrs, + undefined), + {stats, List, Node}. -decode_stats_els(__TopXMLNS, __IgnoreEls, [], Stat) -> - lists:reverse(Stat); +decode_stats_els(__TopXMLNS, __IgnoreEls, [], List) -> + lists:reverse(List); decode_stats_els(__TopXMLNS, __IgnoreEls, - [{xmlel, <<"stat">>, _attrs, _} = _el | _els], Stat) -> + [{xmlel, <<"stat">>, _attrs, _} = _el | _els], List) -> case get_attr(<<"xmlns">>, _attrs) of <<"">> when __TopXMLNS == <<"http://jabber.org/protocol/stats">> -> decode_stats_els(__TopXMLNS, __IgnoreEls, _els, - [decode_stat(__TopXMLNS, __IgnoreEls, _el) | Stat]); + [decode_stat(__TopXMLNS, __IgnoreEls, _el) | List]); <<"http://jabber.org/protocol/stats">> -> decode_stats_els(__TopXMLNS, __IgnoreEls, _els, [decode_stat(<<"http://jabber.org/protocol/stats">>, __IgnoreEls, _el) - | Stat]); + | List]); _ -> - decode_stats_els(__TopXMLNS, __IgnoreEls, _els, Stat) + decode_stats_els(__TopXMLNS, __IgnoreEls, _els, List) end; decode_stats_els(__TopXMLNS, __IgnoreEls, [_ | _els], - Stat) -> - decode_stats_els(__TopXMLNS, __IgnoreEls, _els, Stat). + List) -> + decode_stats_els(__TopXMLNS, __IgnoreEls, _els, List). -encode_stats({stats, Stat}, _xmlns_attrs) -> - _els = lists:reverse('encode_stats_$stat'(Stat, [])), - _attrs = _xmlns_attrs, +decode_stats_attrs(__TopXMLNS, + [{<<"node">>, _val} | _attrs], _Node) -> + decode_stats_attrs(__TopXMLNS, _attrs, _val); +decode_stats_attrs(__TopXMLNS, [_ | _attrs], Node) -> + decode_stats_attrs(__TopXMLNS, _attrs, Node); +decode_stats_attrs(__TopXMLNS, [], Node) -> + decode_stats_attr_node(__TopXMLNS, Node). + +encode_stats({stats, List, Node}, _xmlns_attrs) -> + _els = lists:reverse('encode_stats_$list'(List, [])), + _attrs = encode_stats_attr_node(Node, _xmlns_attrs), {xmlel, <<"query">>, _attrs, _els}. -'encode_stats_$stat'([], _acc) -> _acc; -'encode_stats_$stat'([Stat | _els], _acc) -> - 'encode_stats_$stat'(_els, - [encode_stat(Stat, []) | _acc]). +'encode_stats_$list'([], _acc) -> _acc; +'encode_stats_$list'([List | _els], _acc) -> + 'encode_stats_$list'(_els, + [encode_stat(List, []) | _acc]). + +decode_stats_attr_node(__TopXMLNS, undefined) -> <<>>; +decode_stats_attr_node(__TopXMLNS, _val) -> _val. + +encode_stats_attr_node(<<>>, _acc) -> _acc; +encode_stats_attr_node(_val, _acc) -> + [{<<"node">>, _val} | _acc]. decode_stat(__TopXMLNS, __IgnoreEls, {xmlel, <<"stat">>, _attrs, _els}) -> Error = decode_stat_els(__TopXMLNS, __IgnoreEls, _els, - []), + undefined), {Name, Units, Value} = decode_stat_attrs(__TopXMLNS, _attrs, undefined, undefined, undefined), {stat, Name, Units, Value, Error}. decode_stat_els(__TopXMLNS, __IgnoreEls, [], Error) -> - lists:reverse(Error); + Error; decode_stat_els(__TopXMLNS, __IgnoreEls, [{xmlel, <<"error">>, _attrs, _} = _el | _els], Error) -> @@ -25152,13 +25177,11 @@ decode_stat_els(__TopXMLNS, __IgnoreEls, when __TopXMLNS == <<"http://jabber.org/protocol/stats">> -> decode_stat_els(__TopXMLNS, __IgnoreEls, _els, - [decode_stat_error(__TopXMLNS, __IgnoreEls, _el) - | Error]); + decode_stat_error(__TopXMLNS, __IgnoreEls, _el)); <<"http://jabber.org/protocol/stats">> -> decode_stat_els(__TopXMLNS, __IgnoreEls, _els, - [decode_stat_error(<<"http://jabber.org/protocol/stats">>, - __IgnoreEls, _el) - | Error]); + decode_stat_error(<<"http://jabber.org/protocol/stats">>, + __IgnoreEls, _el)); _ -> decode_stat_els(__TopXMLNS, __IgnoreEls, _els, Error) end; @@ -25196,10 +25219,9 @@ encode_stat({stat, Name, Units, Value, Error}, _xmlns_attrs))), {xmlel, <<"stat">>, _attrs, _els}. -'encode_stat_$error'([], _acc) -> _acc; -'encode_stat_$error'([Error | _els], _acc) -> - 'encode_stat_$error'(_els, - [encode_stat_error(Error, []) | _acc]). +'encode_stat_$error'(undefined, _acc) -> _acc; +'encode_stat_$error'(Error, _acc) -> + [encode_stat_error(Error, []) | _acc]. decode_stat_attr_name(__TopXMLNS, undefined) -> erlang:error({xmpp_codec, @@ -25209,41 +25231,39 @@ decode_stat_attr_name(__TopXMLNS, _val) -> _val. encode_stat_attr_name(_val, _acc) -> [{<<"name">>, _val} | _acc]. -decode_stat_attr_units(__TopXMLNS, undefined) -> - undefined; +decode_stat_attr_units(__TopXMLNS, undefined) -> <<>>; decode_stat_attr_units(__TopXMLNS, _val) -> _val. -encode_stat_attr_units(undefined, _acc) -> _acc; +encode_stat_attr_units(<<>>, _acc) -> _acc; encode_stat_attr_units(_val, _acc) -> [{<<"units">>, _val} | _acc]. -decode_stat_attr_value(__TopXMLNS, undefined) -> - undefined; +decode_stat_attr_value(__TopXMLNS, undefined) -> <<>>; decode_stat_attr_value(__TopXMLNS, _val) -> _val. -encode_stat_attr_value(undefined, _acc) -> _acc; +encode_stat_attr_value(<<>>, _acc) -> _acc; encode_stat_attr_value(_val, _acc) -> [{<<"value">>, _val} | _acc]. decode_stat_error(__TopXMLNS, __IgnoreEls, {xmlel, <<"error">>, _attrs, _els}) -> - Cdata = decode_stat_error_els(__TopXMLNS, __IgnoreEls, - _els, <<>>), + Reason = decode_stat_error_els(__TopXMLNS, __IgnoreEls, + _els, <<>>), Code = decode_stat_error_attrs(__TopXMLNS, _attrs, undefined), - {Code, Cdata}. + {stat_error, Code, Reason}. decode_stat_error_els(__TopXMLNS, __IgnoreEls, [], - Cdata) -> - decode_stat_error_cdata(__TopXMLNS, Cdata); + Reason) -> + decode_stat_error_cdata(__TopXMLNS, Reason); decode_stat_error_els(__TopXMLNS, __IgnoreEls, - [{xmlcdata, _data} | _els], Cdata) -> + [{xmlcdata, _data} | _els], Reason) -> decode_stat_error_els(__TopXMLNS, __IgnoreEls, _els, - <>); + <>); decode_stat_error_els(__TopXMLNS, __IgnoreEls, - [_ | _els], Cdata) -> + [_ | _els], Reason) -> decode_stat_error_els(__TopXMLNS, __IgnoreEls, _els, - Cdata). + Reason). decode_stat_error_attrs(__TopXMLNS, [{<<"code">>, _val} | _attrs], _Code) -> @@ -25254,8 +25274,9 @@ decode_stat_error_attrs(__TopXMLNS, [_ | _attrs], decode_stat_error_attrs(__TopXMLNS, [], Code) -> decode_stat_error_attr_code(__TopXMLNS, Code). -encode_stat_error({Code, Cdata}, _xmlns_attrs) -> - _els = encode_stat_error_cdata(Cdata, []), +encode_stat_error({stat_error, Code, Reason}, + _xmlns_attrs) -> + _els = encode_stat_error_cdata(Reason, []), _attrs = encode_stat_error_attr_code(Code, _xmlns_attrs), {xmlel, <<"error">>, _attrs, _els}. @@ -25274,10 +25295,10 @@ decode_stat_error_attr_code(__TopXMLNS, _val) -> encode_stat_error_attr_code(_val, _acc) -> [{<<"code">>, enc_int(_val)} | _acc]. -decode_stat_error_cdata(__TopXMLNS, <<>>) -> undefined; +decode_stat_error_cdata(__TopXMLNS, <<>>) -> <<>>; decode_stat_error_cdata(__TopXMLNS, _val) -> _val. -encode_stat_error_cdata(undefined, _acc) -> _acc; +encode_stat_error_cdata(<<>>, _acc) -> _acc; encode_stat_error_cdata(_val, _acc) -> [{xmlcdata, _val} | _acc]. diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index 964b631c2..a50b58b59 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -289,7 +289,8 @@ -xml(stat_error, #elem{name = <<"error">>, xmlns = <<"http://jabber.org/protocol/stats">>, - result = {'$code', '$cdata'}, + result = {stat_error, '$code', '$reason'}, + cdata = #cdata{default = <<"">>, label = '$reason'}, attrs = [#attr{name = <<"code">>, required = true, enc = {enc_int, []}, @@ -301,17 +302,17 @@ result = {stat, '$name', '$units', '$value', '$error'}, attrs = [#attr{name = <<"name">>, required = true}, - #attr{name = <<"units">>}, - #attr{name = <<"value">>}], - refs = [#ref{name = stat_error, - label = '$error'}]}). + #attr{name = <<"units">>, default = <<"">>}, + #attr{name = <<"value">>, default = <<"">>}], + refs = [#ref{name = stat_error, label = '$error', + min = 0, max = 1}]}). -xml(stats, #elem{name = <<"query">>, xmlns = <<"http://jabber.org/protocol/stats">>, - result = {stats, '$stat'}, - refs = [#ref{name = stat, - label = '$stat'}]}). + result = {stats, '$list', '$node'}, + attrs = [#attr{name = <<"node">>, default = <<"">>}], + refs = [#ref{name = stat, label = '$list'}]}). -xml(iq, #elem{name = <<"iq">>,