2008-12-23 Badlop <badlop@process-one.net>
+ * src/mod_last.erl: Implement workaround for uptime statistic in
+ 32 bit machines, so it can show uptime greater than 50
+ days (EJAB-610)
+ * src/mod_last_odbc.erl: Likewise
+ * src/ejabberd_config.erl: Store start time in local_config table
+
* src/cyrsasl_digest.erl: Check digest-uri in SASL digest
authentication (thanks to Paul Guyot)(EJAB-569)
{attributes, record_info(fields, local_config)}]),
mnesia:add_table_copy(local_config, node(), ram_copies),
Config = get_ejabberd_config_path(),
- load_file(Config).
+ load_file(Config),
+ %% This start time is used by mod_last:
+ add_local_option(node_start, now()),
+ ok.
%% @doc Get the filename of the ejabberd configuration file.
%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg".
%% @doc Load the ejabberd configuration file.
%% It also includes additional configuration files and replaces macros.
-%% @spec (File::string()) -> [term()]
+%% @spec (File::string()) -> ok
load_file(File) ->
Terms = get_plain_terms_file(File),
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST).
+%%%
+%%% Uptime of ejabberd node
+%%%
+
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
- Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
+ Sec = get_node_uptime(),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_LAST},
[]}]}
end.
+%% @spec () -> integer()
+%% @doc Get the uptime of the ejabberd node, expressed in seconds.
+%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
+get_node_uptime() ->
+ case ejabberd_config:get_local_option(node_start) of
+ {_, _, _} = StartNow ->
+ now_to_seconds(now()) - now_to_seconds(StartNow);
+ _undefined ->
+ trunc(element(1, erlang:statistics(wall_clock))/1000)
+ end.
+
+now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
+ MegaSecs * 1000000 + Secs.
+
+
+%%%
+%%% Serve queries about user last online
+%%%
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
[] ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
[#last_activity{timestamp = TimeStamp, status = Status}] ->
- {MegaSecs, Secs, _MicroSecs} = now(),
- TimeStamp2 = MegaSecs * 1000000 + Secs,
+ TimeStamp2 = now_to_seconds(now()),
Sec = TimeStamp2 - TimeStamp,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
on_presence_update(User, Server, _Resource, Status) ->
- {MegaSecs, Secs, _MicroSecs} = now(),
- TimeStamp = MegaSecs * 1000000 + Secs,
+ TimeStamp = now_to_seconds(now()),
store_last_info(User, Server, TimeStamp, Status).
store_last_info(User, Server, TimeStamp, Status) ->
status = Status})
end,
mnesia:transaction(F).
-
-%% Returns: {ok, Timestamp, Status} | not_found
+
+%% @spec (LUser::string(), LServer::string() ->
+%% {ok, Timestamp::integer(), Status::string()} | not_found
get_last_info(LUser, LServer) ->
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
{'EXIT', _Reason} ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST).
+%%%
+%%% Uptime of ejabberd node
+%%%
+
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
- Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
+ Sec = get_node_uptime(),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_LAST},
[]}]}
end.
+%% @spec () -> integer()
+%% @doc Get the uptime of the ejabberd node, expressed in seconds.
+%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
+get_node_uptime() ->
+ case ejabberd_config:get_local_option(node_start) of
+ {_, _, _} = StartNow ->
+ now_to_seconds(now()) - now_to_seconds(StartNow);
+ _undefined ->
+ trunc(element(1, erlang:statistics(wall_clock))/1000)
+ end.
+
+now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
+ MegaSecs * 1000000 + Secs.
+
+
+%%%
+%%% Serve queries about user last online
+%%%
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
{selected, ["seconds","state"], [{STimeStamp, Status}]} ->
case catch list_to_integer(STimeStamp) of
TimeStamp when is_integer(TimeStamp) ->
- {MegaSecs, Secs, _MicroSecs} = now(),
- TimeStamp2 = MegaSecs * 1000000 + Secs,
+ TimeStamp2 = now_to_seconds(now()),
Sec = TimeStamp2 - TimeStamp,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
end.
on_presence_update(User, Server, _Resource, Status) ->
- {MegaSecs, Secs, _MicroSecs} = now(),
- TimeStamp = MegaSecs * 1000000 + Secs,
+ TimeStamp = now_to_seconds(now()),
store_last_info(User, Server, TimeStamp, Status).
store_last_info(User, Server, TimeStamp, Status) ->
State = ejabberd_odbc:escape(Status),
odbc_queries:set_last_t(LServer, Username, Seconds, State).
-%% Returns: {ok, Timestamp, Status} | not_found
+%% @spec (LUser::string(), LServer::string() ->
+%% {ok, Timestamp::integer(), Status::string()} | not_found
get_last_info(LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_last(LServer, Username) of