/[packages]/cauldron/ejabberd/current/SOURCES/mod_ctlextra.erl
ViewVC logotype

Contents of /cauldron/ejabberd/current/SOURCES/mod_ctlextra.erl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 233035 - (show annotations) (download)
Mon Apr 23 18:15:51 2012 UTC (12 years ago) by mitya
File size: 21970 byte(s)
Import ejabberd 2.1.10

1 %%%----------------------------------------------------------------------
2 %%% File : mod_ctlextra.erl
3 %%% Author : Badlop <badlop@ono.com>
4 %%% Purpose : Adds more commands to ejabberd_ctl
5 %%% Created : 30 Nov 2006 by Badlop <badlop@ono.com>
6 %%% Id : $Id: mod_ctlextra.erl 483 2008-01-20 12:37:04Z badlop $
7 %%%----------------------------------------------------------------------
8
9 -module(mod_ctlextra).
10 -author('badlop@ono.com').
11
12 -behaviour(gen_mod).
13
14 -export([start/2,
15 stop/1,
16 ctl_process/2,
17 ctl_process/3
18 ]).
19
20 -include("ejabberd.hrl").
21 -include("ejabberd_ctl.hrl").
22 -include("jlib.hrl").
23 -include("mod_roster.hrl").
24
25 %% Copied from ejabberd_sm.erl
26 -record(session, {sid, usr, us, priority, info}).
27
28
29 %%-------------
30 %% gen_mod
31 %%-------------
32
33 start(Host, _Opts) ->
34 ejabberd_ctl:register_commands(commands_global(), ?MODULE, ctl_process),
35 ejabberd_ctl:register_commands(Host, commands_host(), ?MODULE, ctl_process).
36
37 stop(Host) ->
38 ejabberd_ctl:unregister_commands(commands_global(), ?MODULE, ctl_process),
39 ejabberd_ctl:unregister_commands(Host, commands_host(), ?MODULE, ctl_process).
40
41 commands_global() ->
42 [
43 {"compile file", "recompile and reload file"},
44 {"load-config file", "load config from file"},
45 {"remove-node nodename", "remove an ejabberd node from the database"},
46
47 %% ejabberd_auth
48 {"delete-older-users days", "delete users that have not logged in the last 'days'"},
49 {"set-password user server password", "set password to user@server"},
50
51 %% ejd2odbc
52 {"export2odbc server output", "export Mnesia tables on server to files on output directory"},
53
54 %% mod_offline
55 {"delete-older-messages days", "delete offline messages older than 'days'"},
56
57 %% mod_shared_roster
58 {"srg-create group host name description display", "create the group with options"},
59 {"srg-delete group host", "delete the group"},
60 {"srg-user-add user server group host", "add user@server to group on host"},
61 {"srg-user-del user server group host", "delete user@server from group on host"},
62
63 %% mod_vcard
64 {"vcard-get user host data [data2]", "get data from the vCard of the user"},
65 {"vcard-set user host data [data2] content", "set data to content on the vCard"},
66
67 %% mod_announce
68 %% announce_send_online host message
69 %% announce_send_all host, message
70
71 %% mod_roster
72 {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1's roster"},
73 %%{"", "subs= none, from, to or both"},
74 %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
75 %%{"", "will add mike@server.com to peter@localhost roster"},
76 {"rem-rosteritem user1 server1 user2 server2", "Remove user2@server2 from user1@server1's roster"},
77 {"pushroster file user server", "push template roster in file to user@server"},
78 {"pushroster-all file", "push template roster in file to all those users"},
79 {"push-alltoall server group", "adds all the users to all the users in Group"},
80
81 {"status-list status", "list the logged users with status"},
82 {"status-num status", "number of logged users with status"},
83
84 {"stats registeredusers", "number of registered users"},
85 {"stats onlineusers", "number of logged users"},
86 {"stats uptime-seconds", "uptime of ejabberd node in seconds"},
87
88 %% misc
89 {"get-cookie", "get the Erlang cookie of this node"},
90 {"killsession user server resource", "kill a user session"}
91 ].
92
93 commands_host() ->
94 [
95 %% mod_last
96 {"num-active-users days", "number of users active in the last 'days'"},
97 {"status-list status", "list the logged users with status"},
98 {"status-num status", "number of logged users with status"},
99 {"stats registeredusers", "number of registered users"},
100 {"stats onlineusers", "number of logged users"}
101 ].
102
103
104 %%-------------
105 %% Commands global
106 %%-------------
107
108 ctl_process(_Val, ["blo"]) ->
109 FResources = "eeeaaa aaa",
110 io:format("~s", [FResources]),
111 ?STATUS_SUCCESS;
112
113 ctl_process(_Val, ["delete-older-messages", Days]) ->
114 mod_offline:remove_old_messages(list_to_integer(Days)),
115 ?STATUS_SUCCESS;
116
117 ctl_process(_Val, ["delete-older-users", Days]) ->
118 {removed, N, UR} = delete_older_users(list_to_integer(Days)),
119 io:format("Deleted ~p users: ~p~n", [N, UR]),
120 ?STATUS_SUCCESS;
121
122 ctl_process(_Val, ["export2odbc", Server, Output]) ->
123 Tables = [
124 {export_last, last},
125 {export_offline, offline},
126 {export_passwd, passwd},
127 {export_private_storage, private_storage},
128 {export_roster, roster},
129 {export_vcard, vcard},
130 {export_vcard_search, vcard_search}],
131 Export = fun({TableFun, Table}) ->
132 Filename = filename:join([Output, atom_to_list(Table)++".txt"]),
133 io:format("Trying to export Mnesia table '~p' on server '~s' to file '~s'~n", [Table, Server, Filename]),
134 catch ejd2odbc:TableFun(Server, Filename)
135 end,
136 lists:foreach(Export, Tables),
137 ?STATUS_SUCCESS;
138
139 ctl_process(_Val, ["set-password", User, Server, Password]) ->
140 ejabberd_auth:set_password(User, Server, Password),
141 ?STATUS_SUCCESS;
142
143 ctl_process(_Val, ["vcard-get", User, Server, Data]) ->
144 {ok, Res} = vcard_get(User, Server, [Data]),
145 io:format("~s~n", [Res]),
146 ?STATUS_SUCCESS;
147
148 ctl_process(_Val, ["vcard-get", User, Server, Data1, Data2]) ->
149 {ok, Res} = vcard_get(User, Server, [Data1, Data2]),
150 io:format("~s~n", [Res]),
151 ?STATUS_SUCCESS;
152
153 ctl_process(_Val, ["vcard-set", User, Server, Data1, Content]) ->
154 {ok, Res} = vcard_set(User, Server, [Data1], Content),
155 io:format("~s~n", [Res]),
156 ?STATUS_SUCCESS;
157
158 ctl_process(_Val, ["vcard-set", User, Server, Data1, Data2, Content]) ->
159 {ok, Res} = vcard_set(User, Server, [Data1, Data2], Content),
160 io:format("~s~n", [Res]),
161 ?STATUS_SUCCESS;
162
163 ctl_process(_Val, ["compile", Module]) ->
164 compile:file(Module),
165 ?STATUS_SUCCESS;
166
167 ctl_process(_Val, ["remove-node", Node]) ->
168 mnesia:del_table_copy(schema, list_to_atom(Node)),
169 ?STATUS_SUCCESS;
170
171 ctl_process(_Val, ["srg-create" | Parameters]) ->
172 [Group, Host, Name, Description, Display] = group_parameters(Parameters, "'"),
173 Opts = [{name, Name}, {displayed_groups, [Display]}, {description, Description}],
174 {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
175 ?STATUS_SUCCESS;
176
177 ctl_process(_Val, ["srg-delete", Group, Host]) ->
178 {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
179 ?STATUS_SUCCESS;
180
181 ctl_process(_Val, ["srg-user-add", User, Server, Group, Host]) ->
182 {atomic, ok} = mod_shared_roster:add_user_to_group(Host, {User, Server}, Group),
183 ?STATUS_SUCCESS;
184
185 ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) ->
186 {atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group),
187 ?STATUS_SUCCESS;
188
189 ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) ->
190 case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of
191 {atomic, ok} ->
192 ?STATUS_SUCCESS;
193 {error, Reason} ->
194 io:format("Can't add ~p@~p to ~p@~p: ~p~n",
195 [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
196 ?STATUS_ERROR;
197 {badrpc, Reason} ->
198 io:format("Can't add roster item to user ~p: ~p~n",
199 [LocalUser, Reason]),
200 ?STATUS_BADRPC
201 end;
202
203 ctl_process(_Val, ["rem-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer]) ->
204 case rem_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer) of
205 {atomic, ok} ->
206 ?STATUS_SUCCESS;
207 {error, Reason} ->
208 io:format("Can't remove ~p@~p to ~p@~p: ~p~n",
209 [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
210 ?STATUS_ERROR;
211 {badrpc, Reason} ->
212 io:format("Can't remove roster item to user ~p: ~p~n",
213 [LocalUser, Reason]),
214 ?STATUS_BADRPC
215 end;
216
217 ctl_process(_Val, ["pushroster", File, User, Server]) ->
218 case pushroster(File, User, Server) of
219 ok ->
220 ?STATUS_SUCCESS;
221 {error, Reason} ->
222 io:format("Can't push roster ~p to ~p@~p: ~p~n",
223 [File, User, Server, Reason]),
224 ?STATUS_ERROR;
225 {badrpc, Reason} ->
226 io:format("Can't push roster ~p: ~p~n",
227 [File, Reason]),
228 ?STATUS_BADRPC
229 end;
230
231 ctl_process(_Val, ["pushroster-all", File]) ->
232 case pushroster_all([File]) of
233 ok ->
234 ?STATUS_SUCCESS;
235 {error, Reason} ->
236 io:format("Can't push roster ~p: ~p~n",
237 [File, Reason]),
238 ?STATUS_ERROR;
239 {badrpc, Reason} ->
240 io:format("Can't push roster ~p: ~p~n",
241 [File, Reason]),
242 ?STATUS_BADRPC
243 end;
244
245 ctl_process(_Val, ["push-alltoall", Server, Group]) ->
246 case push_alltoall(Server, Group) of
247 ok ->
248 ?STATUS_SUCCESS;
249 {error, Reason} ->
250 io:format("Can't push all to all: ~p~n",
251 [Reason]),
252 ?STATUS_ERROR;
253 {badrpc, Reason} ->
254 io:format("Can't push all to all: ~p~n",
255 [Reason]),
256 ?STATUS_BADRPC
257 end;
258
259 ctl_process(_Val, ["load-config", Path]) ->
260 case ejabberd_config:load_file(Path) of
261 {atomic, ok} ->
262 ?STATUS_SUCCESS;
263 {error, Reason} ->
264 io:format("Can't load config file ~p: ~p~n",
265 [filename:absname(Path), Reason]),
266 ?STATUS_ERROR;
267 {badrpc, Reason} ->
268 io:format("Can't load config file ~p: ~p~n",
269 [filename:absname(Path), Reason]),
270 ?STATUS_BADRPC
271 end;
272
273 ctl_process(_Val, ["stats", Stat]) ->
274 Res = case Stat of
275 "uptime-seconds" -> uptime_seconds();
276 "registeredusers" -> mnesia:table_info(passwd, size);
277 "onlineusers" -> mnesia:table_info(session, size)
278 end,
279 io:format("~p~n", [Res]),
280 ?STATUS_SUCCESS;
281
282 ctl_process(_Val, ["status-num", Status_required]) ->
283 ctl_process(_Val, "all", ["status-num", Status_required]);
284
285 ctl_process(_Val, ["status-list", Status_required]) ->
286 ctl_process(_Val, "all", ["status-list", Status_required]);
287
288 ctl_process(_Val, ["get-cookie"]) ->
289 io:format("~s~n", [atom_to_list(erlang:get_cookie())]),
290 ?STATUS_SUCCESS;
291
292 ctl_process(_Val, ["killsession", User, Server, Resource]) ->
293 ejabberd_router:route(
294 jlib:make_jid("", "", ""),
295 jlib:make_jid(User, Server, Resource),
296 {xmlelement, "broadcast", [], [{exit, "killed"}]}),
297 ?STATUS_SUCCESS;
298
299 ctl_process(Val, _Args) ->
300 Val.
301
302
303 %%-------------
304 %% Commands vhost
305 %%-------------
306
307 ctl_process(_Val, Host, ["num-active-users", Days]) ->
308 Number = num_active_users(Host, list_to_integer(Days)),
309 io:format("~p~n", [Number]),
310 ?STATUS_SUCCESS;
311
312 ctl_process(_Val, Host, ["stats", Stat]) ->
313 Res = case Stat of
314 "registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
315 "onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
316 end,
317 io:format("~p~n", [Res]),
318 ?STATUS_SUCCESS;
319
320 ctl_process(_Val, Host, ["status-num", Status_required]) ->
321 Num = length(get_status_list(Host, Status_required)),
322 io:format("~p~n", [Num]),
323 ?STATUS_SUCCESS;
324
325 ctl_process(_Val, Host, ["status-list", Status_required]) ->
326 Res = get_status_list(Host, Status_required),
327 [ io:format("~s@~s ~s ~p \"~s\"~n", [U, S, R, P, St]) || {U, S, R, P, St} <- Res],
328 ?STATUS_SUCCESS;
329
330 ctl_process(Val, _Host, _Args) ->
331 Val.
332
333
334 %%-------------
335 %% Utils
336 %%-------------
337
338 uptime_seconds() ->
339 trunc(element(1, erlang:statistics(wall_clock))/1000).
340
341 get_status_list(Host, Status_required) ->
342 %% Get list of all logged users
343 Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
344 %% Reformat the list
345 Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
346 Fhost = case Host of
347 "all" ->
348 %% All hosts are requested, so dont filter at all
349 fun(_, _) -> true end;
350 _ ->
351 %% Filter the list, only Host is interesting
352 fun(A, B) -> A == B end
353 end,
354 Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
355 %% For each Pid, get its presence
356 Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
357 %% Filter by status
358 Fstatus = case Status_required of
359 "all" ->
360 fun(_, _) -> true end;
361 _ ->
362 fun(A, B) -> A == B end
363 end,
364 [{User, Server, Resource, Priority, stringize(Status_text)}
365 || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
366 apply(Fstatus, [Status, Status_required])].
367
368 %% Make string more print-friendly
369 stringize(String) ->
370 %% Replace newline characters with other code
371 element(2, regexp:gsub(String, "\n", "\\n")).
372
373 add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) ->
374 subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs),
375 route_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription),
376 {atomic, ok}.
377
378 subscribe(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription, Xattrs) ->
379 R = #roster{usj = {LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}},
380 us = {LocalUser,LocalServer},
381 jid = {RemoteUser,RemoteServer,[]},
382 name = Nick,
383 subscription = Subscription, % none, to=you see him, from=he sees you, both
384 ask = none, % out=send request, in=somebody requests you, none
385 groups = [Group],
386 askmessage = Xattrs, % example: [{"category","conference"}]
387 xs = []},
388 mnesia:transaction(fun() -> mnesia:write(R) end).
389
390 rem_rosteritem(LU, LS, RU, RS) ->
391 unsubscribe(LU, LS, RU, RS),
392 route_rosteritem(LU, LS, RU, RS, "", "", "remove"),
393 {atomic, ok}.
394
395 unsubscribe(LocalUser, LocalServer, RemoteUser, RemoteServer) ->
396 Key = {{LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}},
397 {LocalUser,LocalServer}},
398 mnesia:transaction(fun() -> mnesia:delete(roster, Key, write) end).
399
400 route_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription) ->
401 LJID = jlib:make_jid(LocalUser, LocalServer, ""),
402 RJID = jlib:make_jid(RemoteUser, RemoteServer, ""),
403 ToS = jlib:jid_to_string(LJID),
404 ItemJIDS = jlib:jid_to_string(RJID),
405 GroupXML = {xmlelement, "group", [], [{xmlcdata, Group}]},
406 Item = {xmlelement, "item",
407 [{"jid", ItemJIDS},
408 {"name", Nick},
409 {"subscription", Subscription}],
410 [GroupXML]},
411 Query = {xmlelement, "query", [{"xmlns", ?NS_ROSTER}], [Item]},
412 Packet = {xmlelement, "iq", [{"type", "set"}, {"to", ToS}], [Query]},
413 ejabberd_router:route(LJID, LJID, Packet).
414
415 pushroster(File, User, Server) ->
416 {ok, [Roster]} = file:consult(File),
417 subscribe_roster({User, Server, "", User}, Roster).
418
419 pushroster_all(File) ->
420 {ok, [Roster]} = file:consult(File),
421 subscribe_all(Roster).
422
423 subscribe_all(Roster) ->
424 subscribe_all(Roster, Roster).
425 subscribe_all([], _) ->
426 ok;
427 subscribe_all([User1 | Users], Roster) ->
428 subscribe_roster(User1, Roster),
429 subscribe_all(Users, Roster).
430
431 subscribe_roster(_, []) ->
432 ok;
433 %% Do not subscribe a user to itself
434 subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
435 subscribe_roster({Name, Server, Group, Nick}, Roster);
436 %% Subscribe Name2 to Name1
437 subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
438 subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
439 subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
440
441 push_alltoall(S, G) ->
442 Users = ejabberd_auth:get_vh_registered_users(S),
443 Users2 = build_list_users(G, Users, []),
444 subscribe_all(Users2).
445
446 build_list_users(_Group, [], Res) ->
447 Res;
448 build_list_users(Group, [{User, Server}|Users], Res) ->
449 build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
450
451 vcard_get(User, Server, Data) ->
452 [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
453 JID = jlib:make_jid(User, Server, ""),
454 IQ = #iq{type = get, xmlns = ?NS_VCARD},
455 IQr = Module:Function(JID, JID, IQ),
456 Res = case IQr#iq.sub_el of
457 [A1] ->
458 case vcard_get(Data, A1) of
459 false -> no_value;
460 Elem -> xml:get_tag_cdata(Elem)
461 end;
462 [] ->
463 no_vcard
464 end,
465 {ok, Res}.
466
467 vcard_get([Data1, Data2], A1) ->
468 case xml:get_subtag(A1, Data1) of
469 false -> false;
470 A2 -> vcard_get([Data2], A2)
471 end;
472
473 vcard_get([Data], A1) ->
474 xml:get_subtag(A1, Data).
475
476 vcard_set(User, Server, Data, Content) ->
477 [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
478 JID = jlib:make_jid(User, Server, ""),
479 IQ = #iq{type = get, xmlns = ?NS_VCARD},
480 IQr = Module:Function(JID, JID, IQ),
481
482 %% Get old vcard
483 A4 = case IQr#iq.sub_el of
484 [A1] ->
485 {_, _, _, A2} = A1,
486 update_vcard_els(Data, Content, A2);
487 [] ->
488 update_vcard_els(Data, Content, [])
489 end,
490
491 %% Build new vcard
492 SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
493 IQ2 = #iq{type=set, sub_el = SubEl},
494
495 Module:Function(JID, JID, IQ2),
496 {ok, "done"}.
497
498 update_vcard_els(Data, Content, Els1) ->
499 Els2 = lists:keysort(2, Els1),
500 [Data1 | Data2] = Data,
501 NewEl = case Data2 of
502 [] ->
503 {xmlelement, Data1, [], [{xmlcdata,Content}]};
504 [D2] ->
505 OldEl = case lists:keysearch(Data1, 2, Els2) of
506 {value, A} -> A;
507 false -> {xmlelement, Data1, [], []}
508 end,
509 {xmlelement, _, _, ContentOld1} = OldEl,
510 Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]}],
511 ContentOld2 = lists:keysort(2, ContentOld1),
512 ContentOld3 = lists:keydelete(D2, 2, ContentOld2),
513 ContentNew = lists:keymerge(2, Content2, ContentOld3),
514 {xmlelement, Data1, [], ContentNew}
515 end,
516 Els3 = lists:keydelete(Data1, 2, Els2),
517 lists:keymerge(2, [NewEl], Els3).
518
519 -record(last_activity, {us, timestamp, status}).
520
521 delete_older_users(Days) ->
522 %% Convert older time
523 SecOlder = Days*24*60*60,
524
525 %% Get current time
526 {MegaSecs, Secs, _MicroSecs} = now(),
527 TimeStamp_now = MegaSecs * 1000000 + Secs,
528
529 %% Get the list of registered users
530 Users = ejabberd_auth:dirty_get_registered_users(),
531
532 %% For a user, remove if required and answer true
533 F = fun({LUser, LServer}) ->
534 %% Check if the user is logged
535 case ejabberd_sm:get_user_resources(LUser, LServer) of
536 %% If it isnt
537 [] ->
538 %% Look for his last_activity
539 case mnesia:dirty_read(last_activity, {LUser, LServer}) of
540 %% If it is
541 %% existent:
542 [#last_activity{timestamp = TimeStamp}] ->
543 %% get his age
544 Sec = TimeStamp_now - TimeStamp,
545 %% If he is
546 if
547 %% younger than SecOlder:
548 Sec < SecOlder ->
549 %% do nothing
550 false;
551 %% older:
552 true ->
553 %% remove the user
554 ejabberd_auth:remove_user(LUser, LServer),
555 true
556 end;
557 %% nonexistent:
558 [] ->
559 %% remove the user
560 ejabberd_auth:remove_user(LUser, LServer),
561 true
562 end;
563 %% Else
564 _ ->
565 %% do nothing
566 false
567 end
568 end,
569 %% Apply the function to every user in the list
570 Users_removed = lists:filter(F, Users),
571 {removed, length(Users_removed), Users_removed}.
572
573 num_active_users(Host, Days) ->
574 list_last_activity(Host, true, Days).
575
576 %% Code based on ejabberd/src/web/ejabberd_web_admin.erl
577 list_last_activity(Host, Integral, Days) ->
578 {MegaSecs, Secs, _MicroSecs} = now(),
579 TimeStamp = MegaSecs * 1000000 + Secs,
580 TS = TimeStamp - Days * 86400,
581 case catch mnesia:dirty_select(
582 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
583 [{'>', '$1', TS}],
584 [{'trunc', {'/',
585 {'-', TimeStamp, '$1'},
586 86400}}]}]) of
587 {'EXIT', _Reason} ->
588 [];
589 Vals ->
590 Hist = histogram(Vals, Integral),
591 if
592 Hist == [] ->
593 0;
594 true ->
595 Left = if
596 Days == infinity ->
597 0;
598 true ->
599 Days - length(Hist)
600 end,
601 Tail = if
602 Integral ->
603 lists:duplicate(Left, lists:last(Hist));
604 true ->
605 lists:duplicate(Left, 0)
606 end,
607 lists:nth(Days, Hist ++ Tail)
608 end
609 end.
610 histogram(Values, Integral) ->
611 histogram(lists:sort(Values), Integral, 0, 0, []).
612 histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
613 histogram(T, Integral, Current, Count + 1, Hist);
614 histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
615 if
616 Integral ->
617 histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
618 true ->
619 histogram(Values, Integral, Current + 1, 0, [Count | Hist])
620 end;
621 histogram([], _Integral, _Current, Count, Hist) ->
622 if
623 Count > 0 ->
624 lists:reverse([Count | Hist]);
625 true ->
626 lists:reverse(Hist)
627 end.
628
629 group_parameters(Ps, [Char]) ->
630 {none, Grouped_Ps} = lists:foldl(
631 fun(P, {State, Res}) ->
632 case State of
633 none ->
634 case P of
635 [Char | PTail]->
636 {building, [PTail | Res]};
637 _ ->
638 {none, [P | Res]}
639 end;
640 building ->
641 [ResHead | ResTail] = Res,
642 case lists:last(P) of
643 Char ->
644 P2 = lists:sublist(P, length(P)-1),
645 {none, [ResHead ++ " " ++ P2 | ResTail]};
646 _ ->
647 {building, [ResHead ++ " " ++ P | ResTail]}
648 end
649 end
650 end,
651 {none, []},
652 Ps),
653 lists:reverse(Grouped_Ps).

  ViewVC Help
Powered by ViewVC 1.1.30