aboutsummaryrefslogblamecommitdiffstats
path: root/lib/diameter/src/app/diameter_lib.erl
blob: b5c0e1bf6a9edb174340c0c2d905e02d111a4a52 (plain) (tree)









































































































































































































































































                                                                               
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

-module(diameter_lib).

-export([report/2, info_report/2,
         error_report/2,
         warning_report/2,
         now_diff/1,
         time/1,
         eval/1,
         ip4address/1,
         ip6address/1,
         ipaddr/1,
         spawn_opts/2,
         wait/1,
         fold_tuple/3]).

-include("diameter_internal.hrl").

%% ---------------------------------------------------------------------------
%% # info_report(Reason, MFA)
%%
%% Input: Reason = Arbitrary term indicating the reason for the report.
%%        MFA    = {Module, Function, Args} to report.
%%
%% Output:  true
%% ---------------------------------------------------------------------------

report(Reason, MFA) ->
    info_report(Reason, MFA).

info_report(Reason, {M,F,A}) ->
    error_logger:info_report("   Reason: ~p~n"
			     "      Pid: ~p~n"
			     "     Node: ~p~n"
			     "   Module: ~p~n"
			     " Function: ~p~n"
			     "Arguments: ~p~n",
			     [Reason, self(), node(), M, F, A]).

%%% ---------------------------------------------------------------------------
%%% # error_report(Reason, MFA)
%%% # warning_report(Reason, MFA)
%%%
%%% Output:  false
%%% ---------------------------------------------------------------------------

error_report(Reason, MFA) ->
    report(fun error_logger:error_report/1, Reason, MFA).

warning_report(Reason, MFA) ->
    report(fun error_logger:warning_report/1, Reason, MFA).

report(Fun, Reason, MFA) ->
    Fun([{reason, Reason}, {who, self()}, {where, node()}, {what, MFA}]),
    false.

%%% ---------------------------------------------------------------------------
%%% # now_diff(Time)
%%%
%%% Description: Return timer:now_diff(now(), Time) as an {H, M, S, MicroS}
%%%              tuple instead of as integer microseconds.
%%% ---------------------------------------------------------------------------

now_diff({_,_,_} = Time) ->
    time(timer:now_diff(erlang:now(), Time)).

%%% ---------------------------------------------------------------------------
%%% # time(Time)
%%%
%%% Input:  Time = {MegaSec, Sec, MicroSec}
%%%              | MicroSec
%%%
%%% Output: {H, M, S, MicroS}
%%% ---------------------------------------------------------------------------

time({_,_,_} = Time) ->  %% time of day
    %% 24 hours = 24*60*60*1000000 = 86400000000 microsec
    time(timer:now_diff(Time, {0,0,0}) rem 86400000000);

time(Micro) ->  %% elapsed time
    Seconds = Micro div 1000000,
    H = Seconds div 3600,
    M = (Seconds rem 3600) div 60,
    S = Seconds rem 60,
    {H, M, S, Micro rem 1000000}.

%%% ---------------------------------------------------------------------------
%%% # eval(Func)
%%% ---------------------------------------------------------------------------

eval({M,F,A}) ->
    apply(M,F,A);

eval([{M,F,A} | X]) ->
    apply(M, F, X ++ A);

eval([[F|A] | X]) ->
    eval([F | X ++ A]);

eval([F|A]) ->
    apply(F,A);

eval({F}) ->
    eval(F);

eval(F) ->
    F().

%%% ---------------------------------------------------------------------------
%%% # ip4address(Addr)
%%%
%%% Input:  string()   (eg. "10.0.0.1")
%%%         | list of integer()
%%%         | tuple of integer()
%%%
%%% Output: {_,_,_,_} of integer
%%%
%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
%%% ---------------------------------------------------------------------------

ip4address([_,_,_,_] = Addr) -> %% Length 4 string can't be an address.
    ipaddr(list_to_tuple(Addr));

%% Be brutal.
ip4address(Addr) ->
    try
        {_,_,_,_} = ipaddr(Addr)
    catch
        error: _ ->
            erlang:error({invalid_address, Addr, ?STACK})
    end.

%%% ---------------------------------------------------------------------------
%%% # ip6address(Addr)
%%%
%%% Input:  string()   (eg. "1080::8:800:200C:417A")
%%%         | list of integer()
%%%         | tuple of integer()
%%%
%%% Output: {_,_,_,_,_,_,_,_} of integer
%%%
%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
%%% ---------------------------------------------------------------------------

ip6address([_,_,_,_,_,_,_,_] = Addr) -> %% Length 8 string can't be an address.
    ipaddr(list_to_tuple(Addr));

%% Be brutal.
ip6address(Addr) ->
    try
        {_,_,_,_,_,_,_,_} = ipaddr(Addr)
    catch
        error: _ ->
            erlang:error({invalid_address, Addr, ?STACK})
    end.

%%% ---------------------------------------------------------------------------
%%% # ipaddr(Addr)
%%%
%%% Input:  string() | tuple of integer()
%%%
%%% Output: {_,_,_,_} | {_,_,_,_,_,_,_,_}
%%%
%%% Exceptions: error: {invalid_address, erlang:get_stacktrace()}
%%% ---------------------------------------------------------------------------

-spec ipaddr(string() | tuple())
   -> inet:ip_address().

%% Don't convert lists of integers since a length 8 list like
%% [$1,$0,$.,$0,$.,$0,$.,$1] is ambiguous: is it "10.0.0.1" or
%% "49:48:46:48:46:48:46:49"?
%%
%% RFC 2373 defines the format parsed for v6 addresses.

%% Be brutal.
ipaddr(Addr) ->
    try
        ip(Addr)
    catch
        error: _ ->
            erlang:error({invalid_address, ?STACK})
    end.

%% Already a tuple: ensure non-negative integers of the right size.
ip(T)
  when size(T) == 4;
       size(T) == 8 ->
    Bs = 2*size(T),
    [] = lists:filter(fun(N) when 0 =< N -> 0 < N bsr Bs end,
                      tuple_to_list(T)),
    T;

%% Or not: convert from '.'/':'-separated decimal/hex.
ip(Addr) ->
    {ok, A} = inet_parse:address(Addr),  %% documented in inet(3)
    A.

%%% ---------------------------------------------------------------------------
%%% # spawn_opts(Type, Opts)
%%% ---------------------------------------------------------------------------

%% TODO: config variables.

spawn_opts(server, Opts) ->
    opts(75000, Opts);
spawn_opts(worker, Opts) ->
    opts(5000, Opts).

opts(HeapSize, Opts) ->
    [{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)].

%%% ---------------------------------------------------------------------------
%%% # wait(MRefs)
%%% ---------------------------------------------------------------------------

wait(L) ->
    w([erlang:monitor(process, P) || P <- L]).

w([]) ->
    ok;
w(L) ->
    receive
        {'DOWN', MRef, process, _, _} ->
            w(lists:delete(MRef, L))
    end.

%%% ---------------------------------------------------------------------------
%%% # fold_tuple(N, T0, T)
%%% ---------------------------------------------------------------------------

%% Replace fields in T0 by those of T starting at index N, unless the
%% new value is 'undefined'.
%%
%% eg. fold_tuple(2, Hdr, #diameter_header{end_to_end_id = 42})

fold_tuple(_, T, undefined) ->
    T;

fold_tuple(N, T0, T) ->
    element(2, lists:foldl(fun(X, {M,_} = A) -> {M+1, ft(X, A)} end,
                           {N, T0},
                           lists:nthtail(N-1, tuple_to_list(T)))).

ft(undefined, T) ->
    T;
ft(X, {N, T}) ->
    setelement(N, T, X).