1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%----------------------------------------------------------------------
23%% Purpose: Lightweight test server
24%%----------------------------------------------------------------------
25%%
26
27-module(megaco_test_lib).
28
29%% -compile(export_all).
30
31-compile({no_auto_import, [error/3]}).
32
33-export([
34         proxy_call/3,
35         log/4,
36         error/3,
37
38         sleep/1,
39         hours/1, minutes/1, seconds/1,
40         formated_timestamp/0, format_timestamp/1,
41
42         skip/3,
43         non_pc_tc_maybe_skip/4,
44         os_based_skip/1,
45
46         flush/0,
47         still_alive/1,
48
49         display_alloc_info/0,
50         display_system_info/1, display_system_info/2, display_system_info/3,
51
52         try_tc/6,
53
54         prepare_test_case/5,
55
56         proxy_start/1, proxy_start/2,
57
58         mk_nodes/1,
59         start_nodes/3, start_nodes/4,
60         start_node/3,  start_node/4,
61
62         stop_nodes/3,
63         stop_node/3,
64
65         is_socket_backend/1,
66         inet_backend_opts/1,
67         explicit_inet_backend/0, test_inet_backends/0,
68         open/3,
69         listen/3, connect/3
70
71        ]).
72-export([init_per_suite/1,    end_per_suite/1,
73         init_per_testcase/2, end_per_testcase/2]).
74
75-export([proxy_init/2]).
76
77-include("megaco_test_lib.hrl").
78
79-record('REASON', {mod, line, desc}).
80
81
82%% ----------------------------------------------------------------
83%% Proxy Call
84%% This is used when we need to assign a timeout to a call, but the
85%% call itself does not provide such an argument.
86%%
87%% This has nothing to to with the proxy_start and proxy_init
88%% functions below.
89
90proxy_call(F, Timeout, Default)
91  when is_function(F, 0) andalso
92       is_integer(Timeout) andalso (Timeout > 0) andalso
93       is_function(Default, 0) ->
94    {P, M} = erlang:spawn_monitor(fun() -> exit(F()) end),
95    receive
96        {'DOWN', M, process, P, Reply} ->
97            Reply
98    after Timeout ->
99            erlang:demonitor(M, [flush]),
100            exit(P, kill),
101            Default()
102    end;
103proxy_call(F, Timeout, Default) ->
104    proxy_call(F, Timeout, fun() -> Default end).
105
106
107%% ----------------------------------------------------------------
108%% Time related function
109%%
110
111sleep(infinity) ->
112    receive
113    after infinity ->
114            ok
115    end;
116sleep(MSecs) ->
117    receive
118    after trunc(MSecs) ->
119            ok
120    end,
121    ok.
122
123
124hours(N)   -> trunc(N * 1000 * 60 * 60).
125minutes(N) -> trunc(N * 1000 * 60).
126seconds(N) -> trunc(N * 1000).
127
128
129formated_timestamp() ->
130    format_timestamp(os:timestamp()).
131
132format_timestamp(TS) ->
133    megaco:format_timestamp(TS).
134
135
136%% ----------------------------------------------------------------
137%% Conditional skip of testcases
138%%
139
140non_pc_tc_maybe_skip(Config, Condition, File, Line)
141  when is_list(Config) andalso is_function(Condition) ->
142    %% Check if we shall skip the skip
143    case os:getenv("TS_OS_BASED_SKIP") of
144	"false" ->
145	    ok;
146	_ ->
147	    case lists:keysearch(ts, 1, Config) of
148		{value, {ts, megaco}} ->
149		    %% Always run the testcase if we are using our own
150		    %% test-server...
151		    ok;
152		_ ->
153		    case (catch Condition()) of
154			true ->
155			    skip(non_pc_testcase, File, Line);
156			_ ->
157			    ok
158		    end
159	    end
160    end.
161
162
163%% The type and spec'ing is just to increase readability
164-type os_family()  :: win32 | unix.
165-type os_name()    :: atom().
166-type os_version() :: string() | {non_neg_integer(),
167                                  non_neg_integer(),
168                                  non_neg_integer()}.
169-type os_skip_check() :: fun(() -> boolean()) |
170                            fun((os_version()) -> boolean()).
171-type skippable() :: any | [os_family() |
172                            {os_family(), os_name() |
173                                          [os_name() | {os_name(),
174                                                        os_skip_check()}]}].
175
176-spec os_based_skip(skippable()) -> boolean().
177
178os_based_skip(any) ->
179    true;
180os_based_skip(Skippable) when is_list(Skippable) ->
181    os_base_skip(Skippable, os:type());
182os_based_skip(_Crap) ->
183    false.
184
185os_base_skip(Skippable, {OsFam, OsName}) ->
186    os_base_skip(Skippable, OsFam, OsName);
187os_base_skip(Skippable, OsFam) ->
188    os_base_skip(Skippable, OsFam, undefined).
189
190os_base_skip(Skippable, OsFam, OsName) ->
191    %% Check if the entire family is to be skipped
192    %% Example: [win32, unix]
193    case lists:member(OsFam, Skippable) of
194        true ->
195            true;
196        false ->
197            %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}]
198            case lists:keysearch(OsFam, 1, Skippable) of
199                {value, {OsFam, OsName}} ->
200                    true;
201		{value, {OsFam, Check}} when is_function(Check, 0) ->
202		    Check();
203		{value, {OsFam, Check}} when is_function(Check, 1) ->
204		    Check(os:version());
205                {value, {OsFam, OsNames}} when is_list(OsNames) ->
206                    %% OsNames is a list of:
207                    %%    [atom()|{atom(), function/0 | function/1}]
208                    case lists:member(OsName, OsNames) of
209                        true ->
210                            true;
211                        false ->
212                            os_based_skip_check(OsName, OsNames)
213                    end;
214                _ ->
215                    false
216            end
217    end.
218
219
220
221%% Performs a check via a provided fun with arity 0 or 1.
222%% The argument is the result of os:version().
223os_based_skip_check(OsName, OsNames) ->
224    case lists:keysearch(OsName, 1, OsNames) of
225        {value, {OsName, Check}} when is_function(Check, 0) ->
226            Check();
227        {value, {OsName, Check}} when is_function(Check, 1) ->
228            Check(os:version());
229        _ ->
230            false
231    end.
232
233
234
235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236%% Evaluates a test case or test suite
237%% Returns a list of failing test cases:
238%%
239%%     {Mod, Fun, ExpectedRes, ActualRes}
240%%----------------------------------------------------------------------
241
242display_alloc_info() ->
243    io:format("Allocator memory information:~n", []),
244    AllocInfo = alloc_info(),
245    display_alloc_info(AllocInfo).
246
247display_alloc_info([]) ->
248    ok;
249display_alloc_info([{Alloc, Mem}|AllocInfo]) ->
250    io:format("  ~15w: ~10w~n", [Alloc, Mem]),
251    display_alloc_info(AllocInfo).
252
253alloc_info() ->
254    case erlang:system_info(allocator) of
255	{_Allocator, _Version, Features, _Settings} ->
256	    alloc_info(Features);
257	_ ->
258	    []
259    end.
260
261alloc_info(Allocators) ->
262    Allocs = [temp_alloc, sl_alloc, std_alloc, ll_alloc, eheap_alloc,
263	      ets_alloc, binary_alloc, driver_alloc],
264    alloc_info(Allocators, Allocs, []).
265
266alloc_info([], _, Acc) ->
267    lists:reverse(Acc);
268alloc_info([Allocator | Allocators], Allocs, Acc) ->
269    case lists:member(Allocator, Allocs) of
270	true ->
271	    Instances0 = erlang:system_info({allocator, Allocator}),
272	    Instances =
273		if
274		    is_list(Instances0) ->
275			[Instance || Instance <- Instances0,
276				     element(1, Instance) =:= instance];
277		    true ->
278			[]
279		end,
280	    AllocatorMem = alloc_mem_info(Instances),
281	    alloc_info(Allocators, Allocs, [{Allocator, AllocatorMem} | Acc]);
282
283	false ->
284	    alloc_info(Allocators, Allocs, Acc)
285    end.
286
287alloc_mem_info(Instances) ->
288    alloc_mem_info(Instances, []).
289
290alloc_mem_info([], Acc) ->
291    lists:sum([Mem || {instance, _, Mem} <- Acc]);
292alloc_mem_info([{instance, N, Info}|Instances], Acc) ->
293    InstanceMemInfo = alloc_instance_mem_info(Info),
294    alloc_mem_info(Instances, [{instance, N, InstanceMemInfo} | Acc]).
295
296alloc_instance_mem_info(InstanceInfo) ->
297    MBCS = alloc_instance_mem_info(mbcs, InstanceInfo),
298    SBCS = alloc_instance_mem_info(sbcs, InstanceInfo),
299    MBCS + SBCS.
300
301alloc_instance_mem_info(Key, InstanceInfo) ->
302    case lists:keysearch(Key, 1, InstanceInfo) of
303	{value, {Key, Info}} ->
304	    case lists:keysearch(blocks_size, 1, Info) of
305		{value, {blocks_size, Mem, _, _}} ->
306		    Mem;
307		_ ->
308		    0
309	    end;
310	_ ->
311	    0
312    end.
313
314
315display_system_info(WhenStr) ->
316    display_system_info(WhenStr, undefined, undefined).
317
318display_system_info(WhenStr, undefined, undefined) ->
319    display_system_info(WhenStr, "");
320display_system_info(WhenStr, Mod, Func) ->
321    ModFuncStr = lists:flatten(io_lib:format(" ~w:~w", [Mod, Func])),
322    display_system_info(WhenStr, ModFuncStr).
323
324display_system_info(WhenStr, ModFuncStr) ->
325    Fun = fun(F) -> case (catch F()) of
326			{'EXIT', _} ->
327			    undefined;
328			Res ->
329			    Res
330		    end
331	  end,
332    ProcCount    = Fun(fun() -> erlang:system_info(process_count) end),
333    ProcLimit    = Fun(fun() -> erlang:system_info(process_limit) end),
334    ProcMemAlloc = Fun(fun() -> erlang:memory(processes) end),
335    ProcMemUsed  = Fun(fun() -> erlang:memory(processes_used) end),
336    ProcMemBin   = Fun(fun() -> erlang:memory(binary) end),
337    ProcMemTot   = Fun(fun() -> erlang:memory(total) end),
338    %% error_logger:info_msg(
339    io:format("~n"
340	      "~n*********************************************"
341	      "~n"
342	      "System info ~s~s => "
343	      "~n   Process count:        ~w"
344              "~n   Process limit:        ~w"
345              "~n   Process memory alloc: ~w"
346              "~n   Process memory used:  ~w"
347              "~n   Memory for binaries:  ~w"
348              "~n   Memory total:         ~w"
349	      "~n"
350	      "~n*********************************************"
351	      "~n"
352	      "~n", [WhenStr, ModFuncStr,
353		     ProcCount, ProcLimit, ProcMemAlloc, ProcMemUsed,
354		     ProcMemBin, ProcMemTot]),
355    ok.
356
357
358
359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360%% Verify that the actual result of a test case matches the exected one
361%% Returns the actual result
362%% Stores the result in the process dictionary if mismatch
363
364error(Actual, Mod, Line) ->
365    global:send(megaco_global_logger, {failed, Mod, Line}),
366    log("<ERROR> Bad result: ~p~n", [Actual], Mod, Line),
367    Label = lists:concat([Mod, "(", Line, ") unexpected result"]),
368    megaco:report_event(60, Mod, Mod, Label,
369			[{line, Mod, Line}, {error, Actual}]),
370    case global:whereis_name(megaco_test_case_sup) of
371	undefined ->
372	    ignore;
373	Pid ->
374	    Fail = #'REASON'{mod = Mod, line = Line, desc = Actual},
375	    Pid ! {fail, self(), Fail}
376    end,
377    Actual.
378
379log(Format, Args, Mod, Line) ->
380    case global:whereis_name(megaco_global_logger) of
381	undefined ->
382	    io:format(user, "~p~p(~p): " ++ Format,
383		      [self(), Mod, Line] ++ Args);
384	Pid ->
385	    io:format(Pid, "~p~p(~p): " ++ Format,
386		      [self(), Mod, Line] ++ Args)
387    end.
388
389skip(Reason) ->
390    exit({skip, Reason}).
391
392skip(Actual, File, Line) ->
393    log("Skipping test case: ~p~n", [Actual], File, Line),
394    String = f("~p(~p): ~p~n", [File, Line, Actual]),
395    skip(String).
396
397fatal_skip(Actual, File, Line) ->
398    error(Actual, File, Line),
399    skip({fatal, Actual, File, Line}).
400
401
402
403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404%% Flush the message queue and return its messages
405
406flush() ->
407    receive
408	Msg ->
409	    [Msg | flush()]
410    after 1000 ->
411	    []
412    end.
413
414
415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416%% Check if process is alive and kicking
417
418still_alive(Pid) ->
419    erlang:is_process_alive(Pid).
420
421
422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423%% The proxy process
424
425proxy_start(ProxyId) ->
426    spawn_link(?MODULE, proxy_init, [ProxyId, self()]).
427
428proxy_start(Node, ProxyId) ->
429    spawn_link(Node, ?MODULE, proxy_init, [ProxyId, self()]).
430
431proxy_init(ProxyId, Controller) ->
432    process_flag(trap_exit, true),
433    IdStr = proxyid2string(ProxyId),
434    put(id, IdStr),
435    ?LOG("[~s] proxy started by ~p~n", [IdStr, Controller]),
436    proxy_loop(ProxyId, Controller).
437
438proxy_loop(OwnId, Controller) ->
439    receive
440	{'EXIT', Controller, Reason} ->
441	    pprint("proxy_loop -> received exit from controller"
442                   "~n   Reason: ~p", [Reason]),
443	    exit(Reason);
444	{stop, Controller, Reason} ->
445	    p("proxy_loop -> received stop from controller"
446	      "~n   Reason: ~p"
447	      "~n", [Reason]),
448	    exit(Reason);
449
450	{apply, Fun} ->
451            pprint("proxy_loop -> received apply request"),
452	    Res = Fun(),
453            pprint("proxy_loop -> apply result: "
454                   "~n   ~p", [Res]),
455	    Controller ! {res, OwnId, Res},
456	    proxy_loop(OwnId, Controller);
457	OtherMsg ->
458	    pprint("proxy_loop -> received unknown message: "
459                   "~n  ~p", [OtherMsg]),
460	    Controller ! {msg, OwnId, OtherMsg},
461	    proxy_loop(OwnId, Controller)
462    end.
463
464proxyid2string(Id) when is_list(Id) ->
465    Id;
466proxyid2string(Id) when is_atom(Id) ->
467    atom_to_list(Id);
468proxyid2string(Id) ->
469    f("~p", [Id]).
470
471pprint(F) ->
472    pprint(F, []).
473
474pprint(F, A) ->
475    io:format("[~s] ~p ~s " ++ F ++ "~n",
476              [get(id), self(), formated_timestamp() | A]).
477
478
479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480%% Test server callbacks
481
482init_per_suite(Config) ->
483
484    p("megaco environment: "
485      "~n   (megaco) app:  ~p"
486      "~n   (all)    init: ~p"
487      "~n   (megaco) init: ~p",
488      [application:get_all_env(megaco),
489       init:get_arguments(),
490       case init:get_argument(megaco) of
491           {ok, Args} -> Args;
492           error -> undefined
493       end]),
494
495    ct:timetrap(minutes(3)),
496
497    try analyze_and_print_host_info() of
498        {Factor, HostInfo} when is_integer(Factor) ->
499            try maybe_skip(HostInfo) of
500                true ->
501                    {skip, "Unstable host and/or os (or combo thererof)"};
502                false ->
503                    maybe_start_global_sys_monitor(Config),
504                    [{megaco_factor, Factor} | Config]
505            catch
506                throw:{skip, _} = SKIP ->
507                    SKIP
508            end
509    catch
510        throw:{skip, _} = SKIP ->
511            SKIP
512    end.
513
514maybe_skip(_HostInfo) ->
515
516    %% We have some crap machines that causes random test case failures
517    %% for no obvious reason. So, attempt to identify those without actually
518    %% checking for the host name...
519
520    LinuxVersionVerify =
521        fun(V) when (V > {3,6,11}) ->
522                false; % OK - No skip
523           (V) when (V =:= {3,6,11}) ->
524                case string:trim(os:cmd("cat /etc/issue")) of
525                    "Fedora release 16 " ++ _ -> % Stone age Fedora => Skip
526                        true;
527                    _ ->
528                        false
529                end;
530           (V) when (V =:= {3,4,20}) ->
531                case string:trim(os:cmd("cat /etc/issue")) of
532                    "Wind River Linux 5.0.1.0" ++ _ -> % *Old* Wind River => skip
533                        true;
534                    _ ->
535                        false
536                end;
537           (V) when (V =:= {2,6,32}) ->
538                case string:trim(os:cmd("cat /etc/issue")) of
539                    "Debian GNU/Linux 6.0 " ++ _ -> % Stone age Debian => Skip
540                        true;
541                    _ ->
542                        false
543                end;
544           (V) when (V =:= {2,6,16}) ->
545                case string:trim(os:cmd("cat /etc/issue")) of
546                    %% Stone age SLES => Skip
547                    %% We have atleast one VM that has this version,
548                    %% and it causes randome timeout glitches...
549                    "Welcome to SUSE Linux Enterprise Server 10 SP1 " ++ _ ->
550                        true;
551                    _ ->
552                        false
553                end;
554           (V) when (V =:= {2,6,10}) ->
555                case string:trim(os:cmd("cat /etc/issue")) of
556                    "MontaVista" ++ _ -> % Stone age MontaVista => Skip
557                        %% The real problem is that the machine is *very* slow
558                        true;
559                    _ ->
560                        false
561                end;
562           (V) when (V > {2,6,24}) ->
563                false; % OK - No skip
564           (_) ->
565                %% We are specifically checking for
566                %% a *really* old gento...
567                case string:find(string:strip(os:cmd("uname -a")), "gentoo") of
568                    nomatch ->
569                        false;
570                    _ -> % Stone age gentoo => Skip
571                        true
572                end
573        end,
574    DarwinVersionVerify =
575        fun(V) when (V > {9, 8, 0}) ->
576                %% This version is OK: No Skip
577                false;
578           (_V) ->
579                %% This version is *not* ok: Skip
580                true
581        end,
582    SkipWindowsOnVirtual =
583        %% fun() ->
584        %%         SysMan = win_sys_info_lookup(system_manufacturer, HostInfo),
585        %%         case string:to_lower(SysMan) of
586        %%             "vmware" ++ _ ->
587        %%                 true;
588        %%             _ ->
589        %%                 false
590        %%         end
591        %% end,
592        fun() ->
593                %% The host has been replaced and the VM has been reinstalled
594                %% so for now we give it a chance...
595                false
596        end,
597    COND = [
598            {unix, [{linux,  LinuxVersionVerify},
599		    {darwin, DarwinVersionVerify}]},
600            {win32, SkipWindowsOnVirtual}
601           ],
602    os_based_skip(COND).
603
604%% We start the global system monitor unless explicitly disabled
605maybe_start_global_sys_monitor(Config) ->
606    case lists:keysearch(sysmon, 1, Config) of
607        {value, {sysmon, false}} ->
608            ok;
609        _ ->
610            megaco_test_global_sys_monitor:start()
611    end.
612
613end_per_suite(Config) when is_list(Config) ->
614
615    case lists:keysearch(sysmon, 1, Config) of
616        {value, {sysmon, false}} ->
617            ok;
618        _ ->
619            megaco_test_global_sys_monitor:stop()
620    end,
621
622    Config.
623
624
625init_per_testcase(_Case, Config) ->
626    Pid = group_leader(),
627    Name = megaco_global_logger,
628    case global:whereis_name(Name) of
629	undefined ->
630	    global:register_name(megaco_global_logger, Pid);
631	Pid ->
632	    io:format("~w:init_per_testcase -> "
633		      "already registered to ~p~n", [?MODULE, Pid]),
634	    ok;
635	OtherPid when is_pid(OtherPid) ->
636	    io:format("~w:init_per_testcase -> "
637		      "already registered to other ~p (~p)~n",
638		      [?MODULE, OtherPid, Pid]),
639	    exit({already_registered, {megaco_global_logger, OtherPid, Pid}})
640    end,
641    set_kill_timer(Config).
642
643end_per_testcase(_Case, Config) ->
644    Name = megaco_global_logger,
645    case global:whereis_name(Name) of
646	undefined ->
647	    io:format("~w:end_per_testcase -> already un-registered~n",
648		      [?MODULE]),
649	    ok;
650	Pid when is_pid(Pid) ->
651	    global:unregister_name(megaco_global_logger),
652	    ok
653    end,
654    reset_kill_timer(Config).
655
656
657%% This function prints various host info, which might be usefull
658%% when analyzing the test suite (results).
659%% It also returns a "factor" that can be used when deciding
660%% the load for some test cases. Such as run time or number of
661%% iteraions. This only works for some OSes.
662%%
663analyze_and_print_host_info() ->
664    {OsFam, OsName} = os:type(),
665    Version         =
666        case os:version() of
667            {Maj, Min, Rel} ->
668                f("~w.~w.~w", [Maj, Min, Rel]);
669            VStr ->
670                VStr
671        end,
672    case {OsFam, OsName} of
673        {unix, linux} ->
674            analyze_and_print_linux_host_info(Version);
675        {unix, openbsd} ->
676            analyze_and_print_openbsd_host_info(Version);
677        {unix, freebsd} ->
678            analyze_and_print_freebsd_host_info(Version);
679        {unix, netbsd} ->
680            analyze_and_print_netbsd_host_info(Version);
681        {unix, darwin} ->
682            analyze_and_print_darwin_host_info(Version);
683        {unix, sunos} ->
684            analyze_and_print_solaris_host_info(Version);
685        {win32, nt} ->
686            analyze_and_print_win_host_info(Version);
687        _ ->
688            io:format("OS Family: ~p"
689                      "~n   OS Type:               ~p"
690                      "~n   Version:               ~p"
691                      "~n   Num Online Schedulers: ~s"
692                      "~n", [OsFam, OsName, Version, str_num_schedulers()]),
693            {num_schedulers_to_factor(), []}
694    end.
695
696str_num_schedulers() ->
697    try erlang:system_info(schedulers_online) of
698        N -> f("~w", [N])
699    catch
700        _:_:_ -> "-"
701    end.
702
703num_schedulers_to_factor() ->
704    try erlang:system_info(schedulers_online) of
705        1 ->
706            10;
707        2 ->
708            5;
709        N when (N =< 6) ->
710            2;
711        _ ->
712            1
713    catch
714        _:_:_ ->
715            10
716    end.
717
718
719
720linux_which_distro(Version) ->
721    case file:read_file_info("/etc/issue") of
722        {ok, _} ->
723            case [string:trim(S) ||
724                     S <- string:tokens(os:cmd("cat /etc/issue"), [$\n])] of
725                [DistroStr|_] ->
726                    io:format("Linux: ~s"
727                              "~n   ~s"
728                              "~n",
729                              [Version, DistroStr]),
730                    case DistroStr of
731                        "Wind River Linux" ++ _ ->
732                            wind_river;
733                        "MontaVista" ++ _ ->
734                            montavista;
735                        "Yellow Dog" ++ _ ->
736                            yellow_dog;
737                        _ ->
738                            other
739                    end;
740                X ->
741                    io:format("Linux: ~s"
742                              "~n   ~p"
743                              "~n",
744                              [Version, X]),
745                    other
746            end;
747        _ ->
748            io:format("Linux: ~s"
749                      "~n", [Version]),
750            other
751    end.
752
753
754analyze_and_print_linux_host_info(Version) ->
755    Distro = linux_which_distro(Version),
756    Factor =
757        case (catch linux_which_cpuinfo(Distro)) of
758            {ok, {CPU, BogoMIPS}} ->
759                io:format("CPU: "
760                          "~n   Model:                 ~s"
761                          "~n   BogoMIPS:              ~w"
762                          "~n   Num Online Schedulers: ~s"
763                          "~n", [CPU, BogoMIPS, str_num_schedulers()]),
764                if
765                    (BogoMIPS > 20000) ->
766                        1;
767                    (BogoMIPS > 10000) ->
768                        2;
769                    (BogoMIPS > 5000) ->
770                        3;
771                    (BogoMIPS > 2000) ->
772                        5;
773                    (BogoMIPS > 1000) ->
774                        8;
775                    true ->
776                        10
777                end;
778            {ok, CPU} ->
779                io:format("CPU: "
780                          "~n   Model:                 ~s"
781                          "~n   Num Online Schedulers: ~s"
782                          "~n", [CPU, str_num_schedulers()]),
783                num_schedulers_to_factor();
784            _ ->
785                5
786        end,
787    %% Check if we need to adjust the factor because of the memory
788    try linux_which_meminfo() of
789        AddFactor ->
790            {Factor + AddFactor, []}
791    catch
792        _:_:_ ->
793            {Factor, []}
794    end.
795
796
797linux_cpuinfo_lookup(Key) when is_list(Key) ->
798    linux_info_lookup(Key, "/proc/cpuinfo").
799
800linux_cpuinfo_cpu() ->
801    case linux_cpuinfo_lookup("cpu") of
802        [Model] ->
803            Model;
804        _ ->
805            "-"
806    end.
807
808linux_cpuinfo_motherboard() ->
809    case linux_cpuinfo_lookup("motherboard") of
810        [MB] ->
811            MB;
812        _ ->
813            "-"
814    end.
815
816linux_cpuinfo_bogomips() ->
817    case linux_cpuinfo_lookup("bogomips") of
818        BMips when is_list(BMips) ->
819            try lists:sum([bogomips_to_int(BM) || BM <- BMips])
820            catch
821                _:_:_ ->
822                    "-"
823            end;
824        _ ->
825            "-"
826    end.
827
828linux_cpuinfo_total_bogomips() ->
829    case linux_cpuinfo_lookup("total bogomips") of
830        [TBM] ->
831            try bogomips_to_int(TBM)
832            catch
833                _:_:_ ->
834                    "-"
835            end;
836        _ ->
837            "-"
838    end.
839
840bogomips_to_int(BM) ->
841    try list_to_float(BM) of
842        F ->
843            floor(F)
844    catch
845        _:_:_ ->
846            try list_to_integer(BM) of
847                I ->
848                    I
849            catch
850                _:_:_ ->
851                    throw(noinfo)
852            end
853    end.
854
855linux_cpuinfo_model() ->
856    case linux_cpuinfo_lookup("model") of
857        [M] ->
858            M;
859        _ ->
860            "-"
861    end.
862
863linux_cpuinfo_platform() ->
864    case linux_cpuinfo_lookup("platform") of
865        [P] ->
866            P;
867        _ ->
868            "-"
869    end.
870
871linux_cpuinfo_model_name() ->
872    case linux_cpuinfo_lookup("model name") of
873        [P|_] ->
874            P;
875        _X ->
876            "-"
877    end.
878
879linux_cpuinfo_processor() ->
880    case linux_cpuinfo_lookup("Processor") of
881        [P] ->
882            P;
883        _ ->
884            "-"
885    end.
886
887linux_which_cpuinfo(montavista) ->
888    CPU =
889        case linux_cpuinfo_cpu() of
890            "-" ->
891                throw(noinfo);
892            Model ->
893                case linux_cpuinfo_motherboard() of
894                    "-" ->
895                        Model;
896                    MB ->
897                        Model ++ " (" ++ MB ++ ")"
898                end
899        end,
900    case linux_cpuinfo_bogomips() of
901        "-" ->
902            {ok, CPU};
903        BMips ->
904            {ok, {CPU, BMips}}
905    end;
906
907linux_which_cpuinfo(yellow_dog) ->
908    CPU =
909        case linux_cpuinfo_cpu() of
910            "-" ->
911                throw(noinfo);
912            Model ->
913                case linux_cpuinfo_motherboard() of
914                    "-" ->
915                        Model;
916                    MB ->
917                        Model ++ " (" ++ MB ++ ")"
918                end
919        end,
920    {ok, CPU};
921
922linux_which_cpuinfo(wind_river) ->
923    CPU =
924        case linux_cpuinfo_model() of
925            "-" ->
926                throw(noinfo);
927            Model ->
928                case linux_cpuinfo_platform() of
929                    "-" ->
930                        Model;
931                    Platform ->
932                        Model ++ " (" ++ Platform ++ ")"
933                end
934        end,
935    case linux_cpuinfo_total_bogomips() of
936        "-" ->
937            {ok, CPU};
938        BMips ->
939            {ok, {CPU, BMips}}
940    end;
941
942linux_which_cpuinfo(other) ->
943    %% Check for x86 (Intel or AMD)
944    CPU =
945        case linux_cpuinfo_model_name() of
946            "-" ->
947                %% ARM (at least some distros...)
948                case linux_cpuinfo_processor() of
949                    "-" ->
950                        %% Ok, we give up
951                        throw(noinfo);
952                    Proc ->
953                        Proc
954                end;
955            ModelName ->
956                ModelName
957        end,
958    case linux_cpuinfo_bogomips() of
959        "-" ->
960            {ok, CPU};
961        BMips ->
962            {ok, {CPU, BMips}}
963    end.
964
965linux_meminfo_lookup(Key) when is_list(Key) ->
966    linux_info_lookup(Key, "/proc/meminfo").
967
968linux_meminfo_memtotal() ->
969    case linux_meminfo_lookup("MemTotal") of
970        [X] ->
971            X;
972        _ ->
973            "-"
974    end.
975
976%% We *add* the value this return to the Factor.
977linux_which_meminfo() ->
978    case linux_meminfo_memtotal() of
979        "-" ->
980            0;
981        MemTotal ->
982            io:format("Memory:"
983                      "~n   ~s"
984                      "~n", [MemTotal]),
985            case string:tokens(MemTotal, [$ ]) of
986                [MemSzStr, MemUnit] ->
987                    MemSz2 = list_to_integer(MemSzStr),
988                    MemSz3 =
989                        case string:to_lower(MemUnit) of
990                            "kb" ->
991                                MemSz2;
992                            "mb" ->
993                                MemSz2*1024;
994                            "gb" ->
995                                MemSz2*1024*1024;
996                            _ ->
997                                throw(noinfo)
998                        end,
999                    if
1000                        (MemSz3 >= 8388608) ->
1001                            0;
1002                        (MemSz3 >= 4194304) ->
1003                            1;
1004                        (MemSz3 >= 2097152) ->
1005                            3;
1006                        true ->
1007                            5
1008                    end;
1009                _X ->
1010                    0
1011            end
1012    end.
1013
1014
1015%% Just to be clear: This is ***not*** scientific...
1016analyze_and_print_openbsd_host_info(Version) ->
1017    io:format("OpenBSD:"
1018              "~n   Version: ~p"
1019              "~n", [Version]),
1020    Extract =
1021        fun(Key) ->
1022                string:tokens(string:trim(os:cmd("sysctl " ++ Key)), [$=])
1023        end,
1024    try
1025        begin
1026            CPU =
1027                case Extract("hw.model") of
1028                    ["hw.model", Model] ->
1029                        string:trim(Model);
1030                    _ ->
1031                        "-"
1032                end,
1033            CPUSpeed =
1034                case Extract("hw.cpuspeed") of
1035                    ["hw.cpuspeed", Speed] ->
1036                        list_to_integer(Speed);
1037                    _ ->
1038                        -1
1039                end,
1040            NCPU =
1041                case Extract("hw.ncpufound") of
1042                    ["hw.ncpufound", N] ->
1043                        list_to_integer(N);
1044                    _ ->
1045                        -1
1046                end,
1047            Memory =
1048                case Extract("hw.physmem") of
1049                    ["hw.physmem", PhysMem] ->
1050                        list_to_integer(PhysMem) div 1024;
1051                    _ ->
1052                        -1
1053                end,
1054            io:format("CPU:"
1055                      "~n   Model: ~s"
1056                      "~n   Speed: ~w"
1057                      "~n   N:     ~w"
1058                      "~nMemory:"
1059                      "~n   ~w KB"
1060                      "~n", [CPU, CPUSpeed, NCPU, Memory]),
1061            CPUFactor =
1062                if
1063                    (CPUSpeed =:= -1) ->
1064                        1;
1065                    (CPUSpeed >= 2000) ->
1066                        if
1067                            (NCPU >= 4) ->
1068                                1;
1069                            (NCPU >= 2) ->
1070                                2;
1071                            true ->
1072                                3
1073                        end;
1074                    true ->
1075                        if
1076                            (NCPU >= 4) ->
1077                                2;
1078                            (NCPU >= 2) ->
1079                                3;
1080                            true ->
1081                                4
1082                        end
1083                end,
1084            MemAddFactor =
1085                if
1086                    (Memory =:= -1) ->
1087                        0;
1088                    (Memory >= 8388608) ->
1089                        0;
1090                    (Memory >= 4194304) ->
1091                        1;
1092                    (Memory >= 2097152) ->
1093                        2;
1094                    true ->
1095                        3
1096                end,
1097            {CPUFactor + MemAddFactor, []}
1098        end
1099    catch
1100        _:_:_ ->
1101            {5, []}
1102    end.
1103
1104
1105analyze_and_print_freebsd_host_info(Version) ->
1106    io:format("FreeBSD:"
1107              "~n   Version: ~p"
1108              "~n", [Version]),
1109    %% This test require that the program 'sysctl' is in the path.
1110    %% First test with 'which sysctl', if that does not work
1111    %% try with 'which /sbin/sysctl'. If that does not work either,
1112    %% we skip the test...
1113    try
1114        begin
1115            SysCtl =
1116                case string:trim(os:cmd("which sysctl")) of
1117                    [] ->
1118                        case string:trim(os:cmd("which /sbin/sysctl")) of
1119                            [] ->
1120                                throw(sysctl);
1121                            SC2 ->
1122                                SC2
1123                        end;
1124                    SC1 ->
1125                        SC1
1126                end,
1127            Extract =
1128                fun(Key) ->
1129                        string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)),
1130                                      [$:])
1131                end,
1132            CPU      = analyze_freebsd_cpu(Extract),
1133            CPUSpeed = analyze_freebsd_cpu_speed(Extract),
1134            NCPU     = analyze_freebsd_ncpu(Extract),
1135            Memory   = analyze_freebsd_memory(Extract),
1136            io:format("CPU:"
1137                      "~n   Model:          ~s"
1138                      "~n   Speed:          ~w"
1139                      "~n   N:              ~w"
1140                      "~n   Num Schedulers: ~w"
1141                      "~nMemory:"
1142                      "~n   ~w KB"
1143                      "~n",
1144                      [CPU, CPUSpeed, NCPU,
1145                       erlang:system_info(schedulers), Memory]),
1146            CPUFactor =
1147                if
1148                    (CPUSpeed =:= -1) ->
1149                        1;
1150                    (CPUSpeed >= 2000) ->
1151                        if
1152                            (NCPU >= 4) ->
1153                                1;
1154                            (NCPU >= 2) ->
1155                                2;
1156                            true ->
1157                                3
1158                        end;
1159                    true ->
1160                        if
1161                            (NCPU =:= -1) ->
1162                                1;
1163                            (NCPU >= 4) ->
1164                                2;
1165                            (NCPU >= 2) ->
1166                                3;
1167                            true ->
1168                                4
1169                        end
1170                end,
1171            MemAddFactor =
1172                if
1173                    (Memory =:= -1) ->
1174                        0;
1175                    (Memory >= 8388608) ->
1176                        0;
1177                    (Memory >= 4194304) ->
1178                        1;
1179                    (Memory >= 2097152) ->
1180                        2;
1181                    true ->
1182                        3
1183                end,
1184            {CPUFactor + MemAddFactor, []}
1185        end
1186    catch
1187        _:_:_ ->
1188            io:format("CPU:"
1189                      "~n   Num Schedulers: ~w"
1190                      "~n", [erlang:system_info(schedulers)]),
1191            Factor = case erlang:system_info(schedulers) of
1192                         1 ->
1193                             10;
1194                         2 ->
1195                             5;
1196                         _ ->
1197                             2
1198                     end,
1199            {Factor, []}
1200    end.
1201
1202analyze_freebsd_cpu(Extract) ->
1203    analyze_freebsd_item(Extract, "hw.model", fun(X) -> X end, "-").
1204
1205analyze_freebsd_cpu_speed(Extract) ->
1206    analyze_freebsd_item(Extract,
1207                         "hw.clockrate",
1208                         fun(X) -> list_to_integer(X) end,
1209                         -1).
1210
1211analyze_freebsd_ncpu(Extract) ->
1212    analyze_freebsd_item(Extract,
1213                         "hw.ncpu",
1214                         fun(X) -> list_to_integer(X) end,
1215                         -1).
1216
1217analyze_freebsd_memory(Extract) ->
1218    analyze_freebsd_item(Extract,
1219                         "hw.physmem",
1220                         fun(X) -> list_to_integer(X) div 1024 end,
1221                         -1).
1222
1223analyze_freebsd_item(Extract, Key, Process, Default) ->
1224    try
1225        begin
1226            case Extract(Key) of
1227                [Key, Model] ->
1228                    Process(string:trim(Model));
1229                _ ->
1230                    Default
1231            end
1232        end
1233    catch
1234        _:_:_ ->
1235            Default
1236    end.
1237
1238
1239analyze_and_print_netbsd_host_info(Version) ->
1240    io:format("NetBSD:"
1241              "~n   Version: ~p"
1242              "~n", [Version]),
1243    %% This test require that the program 'sysctl' is in the path.
1244    %% First test with 'which sysctl', if that does not work
1245    %% try with 'which /sbin/sysctl'. If that does not work either,
1246    %% we skip the test...
1247    try
1248        begin
1249            SysCtl =
1250                case string:trim(os:cmd("which sysctl")) of
1251                    [] ->
1252                        case string:trim(os:cmd("which /sbin/sysctl")) of
1253                            [] ->
1254                                throw(sysctl);
1255                            SC2 ->
1256                                SC2
1257                        end;
1258                    SC1 ->
1259                        SC1
1260                end,
1261            Extract =
1262                fun(Key) ->
1263                        [string:trim(S) ||
1264                            S <-
1265                                string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)),
1266                                              [$=])]
1267                end,
1268            CPU      = analyze_netbsd_cpu(Extract),
1269            Machine  = analyze_netbsd_machine(Extract),
1270            Arch     = analyze_netbsd_machine_arch(Extract),
1271            CPUSpeed = analyze_netbsd_cpu_speed(Extract),
1272            NCPU     = analyze_netbsd_ncpu(Extract),
1273            Memory   = analyze_netbsd_memory(Extract),
1274            io:format("CPU:"
1275                      "~n   Model:          ~s (~s, ~s)"
1276                      "~n   Speed:          ~w MHz"
1277                      "~n   N:              ~w"
1278                      "~n   Num Schedulers: ~w"
1279                      "~nMemory:"
1280                      "~n   ~w KB"
1281                      "~n",
1282                      [CPU, Machine, Arch, CPUSpeed, NCPU,
1283                       erlang:system_info(schedulers), Memory]),
1284            CPUFactor =
1285                if
1286                    (CPUSpeed =:= -1) ->
1287                        1;
1288                    (CPUSpeed >= 2000) ->
1289                        if
1290                            (NCPU >= 4) ->
1291                                1;
1292                            (NCPU >= 2) ->
1293                                2;
1294                            true ->
1295                                3
1296                        end;
1297                    true ->
1298                        if
1299                            (NCPU =:= -1) ->
1300                                1;
1301                            (NCPU >= 4) ->
1302                                2;
1303                            (NCPU >= 2) ->
1304                                3;
1305                            true ->
1306                                4
1307                        end
1308                end,
1309            MemAddFactor =
1310                if
1311                    (Memory =:= -1) ->
1312                        0;
1313                    (Memory >= 8388608) ->
1314                        0;
1315                    (Memory >= 4194304) ->
1316                        1;
1317                    (Memory >= 2097152) ->
1318                        2;
1319                    true ->
1320                        3
1321                end,
1322            {CPUFactor + MemAddFactor, []}
1323        end
1324    catch
1325        _:_:_ ->
1326            io:format("CPU:"
1327                      "~n   Num Schedulers: ~w"
1328                      "~n", [erlang:system_info(schedulers)]),
1329            Factor = case erlang:system_info(schedulers) of
1330                         1 ->
1331                             10;
1332                         2 ->
1333                             5;
1334                         _ ->
1335                             2
1336                     end,
1337            {Factor, []}
1338    end.
1339
1340analyze_netbsd_cpu(Extract) ->
1341    analyze_netbsd_item(Extract, "hw.model", fun(X) -> X end, "-").
1342
1343analyze_netbsd_machine(Extract) ->
1344    analyze_netbsd_item(Extract, "hw.machine", fun(X) -> X end, "-").
1345
1346analyze_netbsd_machine_arch(Extract) ->
1347    analyze_netbsd_item(Extract, "hw.machine_arch", fun(X) -> X end, "-").
1348
1349analyze_netbsd_cpu_speed(Extract) ->
1350    analyze_netbsd_item(Extract, "machdep.dmi.processor-frequency",
1351                        fun(X) -> case string:tokens(X, [$\ ]) of
1352                                      [MHz, "MHz"] ->
1353                                          list_to_integer(MHz);
1354                                      _ ->
1355                                          -1
1356                                  end
1357                        end, "-").
1358
1359analyze_netbsd_ncpu(Extract) ->
1360    analyze_netbsd_item(Extract,
1361                        "hw.ncpu",
1362                        fun(X) -> list_to_integer(X) end,
1363                        -1).
1364
1365analyze_netbsd_memory(Extract) ->
1366    analyze_netbsd_item(Extract,
1367                        "hw.physmem64",
1368                        fun(X) -> list_to_integer(X) div 1024 end,
1369                        -1).
1370
1371analyze_netbsd_item(Extract, Key, Process, Default) ->
1372    analyze_freebsd_item(Extract, Key, Process, Default).
1373
1374
1375
1376%% Model Identifier: Macmini7,1
1377%% Processor Name: Intel Core i5
1378%% Processor Speed: 2,6 GHz
1379%% Number of Processors: 1
1380%% Total Number of Cores: 2
1381%% L2 Cache (per Core): 256 KB
1382%% L3 Cache: 3 MB
1383%% Hyper-Threading Technology: Enabled
1384%% Memory: 16 GB
1385
1386analyze_and_print_darwin_host_info(Version) ->
1387    %% This stuff is for macOS.
1388    %% If we ever tested on a pure darwin machine,
1389    %% we need to find some other way to find some info...
1390    %% Also, I suppose its possible that we for some other
1391    %% reason *fail* to get the info...
1392    case analyze_darwin_software_info() of
1393        [] ->
1394            io:format("Darwin:"
1395                      "~n   Version:               ~s"
1396                      "~n   Num Online Schedulers: ~s"
1397                      "~n", [Version, str_num_schedulers()]),
1398            {num_schedulers_to_factor(), []};
1399        SwInfo when  is_list(SwInfo) ->
1400            SystemVersion = analyze_darwin_sw_system_version(SwInfo),
1401            KernelVersion = analyze_darwin_sw_kernel_version(SwInfo),
1402            HwInfo        = analyze_darwin_hardware_info(),
1403            ModelName     = analyze_darwin_hw_model_name(HwInfo),
1404            ModelId       = analyze_darwin_hw_model_identifier(HwInfo),
1405            ProcName      = analyze_darwin_hw_processor_name(HwInfo),
1406            ProcSpeed     = analyze_darwin_hw_processor_speed(HwInfo),
1407            NumProc       = analyze_darwin_hw_number_of_processors(HwInfo),
1408            NumCores      = analyze_darwin_hw_total_number_of_cores(HwInfo),
1409            Memory        = analyze_darwin_hw_memory(HwInfo),
1410            io:format("Darwin:"
1411                      "~n   System Version:        ~s"
1412                      "~n   Kernel Version:        ~s"
1413                      "~n   Model:                 ~s (~s)"
1414                      "~n   Processor:             ~s (~s, ~s, ~s)"
1415                      "~n   Memory:                ~s"
1416                      "~n   Num Online Schedulers: ~s"
1417                      "~n", [SystemVersion, KernelVersion,
1418                             ModelName, ModelId,
1419                             ProcName, ProcSpeed, NumProc, NumCores,
1420                             Memory,
1421                             str_num_schedulers()]),
1422            CPUFactor = analyze_darwin_cpu_to_factor(ProcName,
1423                                                     ProcSpeed,
1424                                                     NumProc,
1425                                                     NumCores),
1426            MemFactor = analyze_darwin_memory_to_factor(Memory),
1427            if (MemFactor =:= 1) ->
1428                    {CPUFactor, []};
1429               true ->
1430                    {CPUFactor + MemFactor, []}
1431            end
1432    end.
1433
1434analyze_darwin_sw_system_version(SwInfo) ->
1435    proplists:get_value("system version", SwInfo, "-").
1436
1437analyze_darwin_sw_kernel_version(SwInfo) ->
1438    proplists:get_value("kernel version", SwInfo, "-").
1439
1440analyze_darwin_software_info() ->
1441    analyze_darwin_system_profiler("SPSoftwareDataType").
1442
1443analyze_darwin_hw_model_name(HwInfo) ->
1444    proplists:get_value("model name", HwInfo, "-").
1445
1446analyze_darwin_hw_model_identifier(HwInfo) ->
1447    proplists:get_value("model identifier", HwInfo, "-").
1448
1449analyze_darwin_hw_processor_name(HwInfo) ->
1450    proplists:get_value("processor name", HwInfo, "-").
1451
1452analyze_darwin_hw_processor_speed(HwInfo) ->
1453    proplists:get_value("processor speed", HwInfo, "-").
1454
1455analyze_darwin_hw_number_of_processors(HwInfo) ->
1456    proplists:get_value("number of processors", HwInfo, "-").
1457
1458analyze_darwin_hw_total_number_of_cores(HwInfo) ->
1459    proplists:get_value("total number of cores", HwInfo, "-").
1460
1461analyze_darwin_hw_memory(HwInfo) ->
1462    proplists:get_value("memory", HwInfo, "-").
1463
1464analyze_darwin_hardware_info() ->
1465    analyze_darwin_system_profiler("SPHardwareDataType").
1466
1467%% This basically has the structure: "Key: Value"
1468%% But could also be (for example):
1469%%    "Something:" (which we ignore)
1470%%    "Key: Value1:Value2"
1471analyze_darwin_system_profiler(DataType) ->
1472    %% First, make sure the program actually exist:
1473    case os:cmd("which system_profiler") of
1474        [] ->
1475            [];
1476        _ ->
1477            D0 = os:cmd("system_profiler " ++ DataType),
1478            D1 = string:tokens(D0, [$\n]),
1479            D2 = [string:trim(S1) || S1 <- D1],
1480            D3 = [string:tokens(S2, [$:]) || S2 <- D2],
1481            analyze_darwin_system_profiler2(D3)
1482    end.
1483
1484analyze_darwin_system_profiler2(L) ->
1485    analyze_darwin_system_profiler2(L, []).
1486
1487analyze_darwin_system_profiler2([], Acc) ->
1488    [{string:to_lower(K), V} || {K, V} <- lists:reverse(Acc)];
1489analyze_darwin_system_profiler2([[_]|T], Acc) ->
1490    analyze_darwin_system_profiler2(T, Acc);
1491analyze_darwin_system_profiler2([[H1,H2]|T], Acc) ->
1492    analyze_darwin_system_profiler2(T, [{H1, string:trim(H2)}|Acc]);
1493analyze_darwin_system_profiler2([[H|TH0]|T], Acc) ->
1494    %% Some value parts has ':' in them, so put them together
1495    TH1 = colonize(TH0),
1496    analyze_darwin_system_profiler2(T, [{H, string:trim(TH1)}|Acc]).
1497
1498%% This is only called if the length is at least 2
1499colonize([L1, L2]) ->
1500    L1 ++ ":" ++ L2;
1501colonize([H|T]) ->
1502    H ++ ":" ++ colonize(T).
1503
1504
1505%% The memory looks like this "<size> <unit>". Example: "2 GB"
1506analyze_darwin_memory_to_factor(Mem) ->
1507    case [string:to_lower(S) || S <- string:tokens(Mem, [$\ ])] of
1508        [_SzStr, "tb"] ->
1509            1;
1510        [SzStr, "gb"] ->
1511            try list_to_integer(SzStr) of
1512                Sz when Sz < 2 ->
1513                    20;
1514                Sz when Sz < 4 ->
1515                    10;
1516                Sz when Sz < 8 ->
1517                    5;
1518                Sz when Sz < 16 ->
1519                    2;
1520                _ ->
1521                    1
1522            catch
1523                _:_:_ ->
1524                    20
1525            end;
1526        [_SzStr, "mb"] ->
1527            20;
1528        _ ->
1529            20
1530    end.
1531
1532
1533%% The speed is a string: "<speed> <unit>"
1534%% the speed may be a float, which we transforms into an integer of MHz.
1535%% To calculate a factor based on processor speed, number of procs
1536%% and number of cores is ... not an exact ... science ...
1537analyze_darwin_cpu_to_factor(_ProcName,
1538                             ProcSpeedStr, NumProcStr, NumCoresStr) ->
1539    Speed =
1540        case [string:to_lower(S) || S <- string:tokens(ProcSpeedStr, [$\ ])] of
1541            [SpeedStr, "mhz"] ->
1542                try list_to_integer(SpeedStr) of
1543                    SpeedI ->
1544                        SpeedI
1545                catch
1546                    _:_:_ ->
1547                        try list_to_float(SpeedStr) of
1548                            SpeedF ->
1549                                trunc(SpeedF)
1550                        catch
1551                            _:_:_ ->
1552                                -1
1553                        end
1554                end;
1555            [SpeedStr, "ghz"] ->
1556                try list_to_float(SpeedStr) of
1557                    SpeedF ->
1558                        trunc(1000*SpeedF)
1559                catch
1560                    _:_:_ ->
1561                        try list_to_integer(SpeedStr) of
1562                            SpeedI ->
1563                                1000*SpeedI
1564                        catch
1565                            _:_:_ ->
1566                                -1
1567                        end
1568                end;
1569            _ ->
1570                -1
1571        end,
1572    NumProc = try list_to_integer(NumProcStr) of
1573                  NumProcI ->
1574                      NumProcI
1575              catch
1576                  _:_:_ ->
1577                      1
1578              end,
1579    NumCores = try list_to_integer(NumCoresStr) of
1580                   NumCoresI ->
1581                       NumCoresI
1582               catch
1583                   _:_:_ ->
1584                       1
1585               end,
1586    if
1587        (Speed > 3000) ->
1588            if
1589                (NumProc =:= 1) ->
1590                    if
1591                        (NumCores < 2) ->
1592                            5;
1593                        (NumCores < 4) ->
1594                            3;
1595                        (NumCores < 6) ->
1596                            2;
1597                        true ->
1598                            1
1599                    end;
1600                true ->
1601                    if
1602                        (NumCores < 4) ->
1603                            2;
1604                        true ->
1605                            1
1606                    end
1607            end;
1608        (Speed > 2000) ->
1609            if
1610                (NumProc =:= 1) ->
1611                    if
1612                        (NumCores < 2) ->
1613                            8;
1614                        (NumCores < 4) ->
1615                            5;
1616                        (NumCores < 6) ->
1617                            3;
1618                        true ->
1619                            1
1620                    end;
1621                true ->
1622                    if
1623                        (NumCores < 4) ->
1624                            5;
1625                        (NumCores < 8) ->
1626                            2;
1627                        true ->
1628                            1
1629                    end
1630            end;
1631        true ->
1632            if
1633                (NumProc =:= 1) ->
1634                    if
1635                        (NumCores < 2) ->
1636                            10;
1637                        (NumCores < 4) ->
1638                            7;
1639                        (NumCores < 6) ->
1640                            5;
1641                        (NumCores < 8) ->
1642                            3;
1643                        true ->
1644                            1
1645                    end;
1646                true ->
1647                    if
1648                        (NumCores < 4) ->
1649                            8;
1650                        (NumCores < 8) ->
1651                            4;
1652                        true ->
1653                            1
1654                    end
1655            end
1656    end.
1657
1658
1659analyze_and_print_solaris_host_info(Version) ->
1660    Release =
1661        case file:read_file_info("/etc/release") of
1662            {ok, _} ->
1663                case [string:trim(S) || S <- string:tokens(os:cmd("cat /etc/release"), [$\n])] of
1664                    [Rel | _] ->
1665                        Rel;
1666                    _ ->
1667                        "-"
1668                end;
1669            _ ->
1670                "-"
1671        end,
1672    %% Display the firmware device tree root properties (prtconf -b)
1673    Props = [list_to_tuple([string:trim(PS) || PS <- Prop]) ||
1674                Prop <- [string:tokens(S, [$:]) ||
1675                            S <- string:tokens(os:cmd("prtconf -b"), [$\n])]],
1676    BannerName = case lists:keysearch("banner-name", 1, Props) of
1677                     {value, {_, BN}} ->
1678                         string:trim(BN);
1679                     _ ->
1680                         "-"
1681                 end,
1682    InstructionSet =
1683        case string:trim(os:cmd("isainfo -k")) of
1684            "Pseudo-terminal will not" ++ _ ->
1685                "-";
1686            IS ->
1687                IS
1688        end,
1689    PtrConf = [list_to_tuple([string:trim(S) || S <- Items]) || Items <- [string:tokens(S, [$:]) || S <- string:tokens(os:cmd("prtconf"), [$\n])], length(Items) > 1],
1690    SysConf =
1691        case lists:keysearch("System Configuration", 1, PtrConf) of
1692            {value, {_, SC}} ->
1693                SC;
1694            _ ->
1695                "-"
1696        end,
1697    NumPhysProc =
1698        begin
1699            NPPStr = string:trim(os:cmd("psrinfo -p")),
1700            try list_to_integer(NPPStr) of
1701                _ ->
1702                    NPPStr
1703            catch
1704                _:_:_ ->
1705                    "-"
1706            end
1707        end,
1708    NumProc = try integer_to_list(length(string:tokens(os:cmd("psrinfo"), [$\n]))) of
1709                  NPStr ->
1710                      NPStr
1711              catch
1712                  _:_:_ ->
1713                      "-"
1714              end,
1715    MemSz =
1716        case lists:keysearch("Memory size", 1, PtrConf) of
1717            {value, {_, MS}} ->
1718                MS;
1719            _ ->
1720                "-"
1721        end,
1722    io:format("Solaris: ~s"
1723              "~n   Release:               ~s"
1724              "~n   Banner Name:           ~s"
1725              "~n   Instruction Set:       ~s"
1726              "~n   CPUs:                  ~s (~s)"
1727              "~n   System Config:         ~s"
1728              "~n   Memory Size:           ~s"
1729              "~n   Num Online Schedulers: ~s"
1730              "~n~n", [Version, Release, BannerName, InstructionSet,
1731                       NumPhysProc, NumProc,
1732                       SysConf, MemSz,
1733                       str_num_schedulers()]),
1734    MemFactor =
1735        try string:tokens(MemSz, [$ ]) of
1736            [SzStr, "Mega" ++ _] ->
1737                try list_to_integer(SzStr) of
1738                    Sz when Sz > 8192 ->
1739                        0;
1740                    Sz when Sz > 4096 ->
1741                        1;
1742                    Sz when Sz > 2048 ->
1743                        2;
1744                    _ ->
1745                        5
1746                catch
1747                    _:_:_ ->
1748                        10
1749                end;
1750            [SzStr, "Giga" ++ _] ->
1751                try list_to_integer(SzStr) of
1752                    Sz when Sz > 8 ->
1753                        0;
1754                    Sz when Sz > 4 ->
1755                        1;
1756                    Sz when Sz > 2 ->
1757                        2;
1758                    _ ->
1759                        5
1760                catch
1761                    _:_:_ ->
1762                        10
1763                end;
1764            _ ->
1765                10
1766        catch
1767            _:_:_ ->
1768                10
1769        end,
1770    {try erlang:system_info(schedulers) of
1771         1 ->
1772             10;
1773         2 ->
1774             5;
1775         N when (N =< 6) ->
1776             2;
1777         _ ->
1778             1
1779     catch
1780         _:_:_ ->
1781             10
1782     end + MemFactor, []}.
1783
1784
1785analyze_and_print_win_host_info(Version) ->
1786    SysInfo    = which_win_system_info(),
1787    OsName     = win_sys_info_lookup(os_name,             SysInfo),
1788    OsVersion  = win_sys_info_lookup(os_version,          SysInfo),
1789    SysMan     = win_sys_info_lookup(system_manufacturer, SysInfo),
1790    SysMod     = win_sys_info_lookup(system_model,        SysInfo),
1791    NumProcs   = win_sys_info_lookup(num_processors,      SysInfo),
1792    TotPhysMem = win_sys_info_lookup(total_phys_memory,   SysInfo),
1793    io:format("Windows: ~s"
1794              "~n   OS Version:             ~s (~p)"
1795              "~n   System Manufacturer:    ~s"
1796              "~n   System Model:           ~s"
1797              "~n   Number of Processor(s): ~s"
1798              "~n   Total Physical Memory:  ~s"
1799              "~n   Num Online Schedulers:  ~s"
1800              "~n", [OsName, OsVersion, Version,
1801		     SysMan, SysMod, NumProcs, TotPhysMem,
1802		     str_num_schedulers()]),
1803    MemFactor =
1804        try
1805            begin
1806                [MStr, MUnit|_] =
1807                    string:tokens(lists:delete($,, TotPhysMem), [$\ ]),
1808                case string:to_lower(MUnit) of
1809                    "gb" ->
1810                        try list_to_integer(MStr) of
1811                            M when M > 8 ->
1812                                0;
1813                            M when M > 4 ->
1814                                1;
1815                            M when M > 2 ->
1816                                2;
1817                            _ ->
1818                                5
1819                        catch
1820                            _:_:_ ->
1821                                10
1822                        end;
1823                    "mb" ->
1824                        try list_to_integer(MStr) of
1825                            M when M > 8192 ->
1826                                0;
1827                            M when M > 4096 ->
1828                                1;
1829                            M when M > 2048 ->
1830                                2;
1831                            _ ->
1832                                5
1833                        catch
1834                            _:_:_ ->
1835                                10
1836                        end;
1837                    _ ->
1838                        10
1839                end
1840            end
1841        catch
1842            _:_:_ ->
1843                10
1844        end,
1845    CPUFactor =
1846        case erlang:system_info(schedulers) of
1847            1 ->
1848                10;
1849            2 ->
1850                5;
1851            _ ->
1852                2
1853        end,
1854    {CPUFactor + MemFactor, SysInfo}.
1855
1856win_sys_info_lookup(Key, SysInfo) ->
1857    win_sys_info_lookup(Key, SysInfo, "-").
1858
1859win_sys_info_lookup(Key, SysInfo, Def) ->
1860    case lists:keysearch(Key, 1, SysInfo) of
1861        {value, {Key, Value}} ->
1862            Value;
1863        false ->
1864            Def
1865    end.
1866
1867%% This function only extracts the prop we actually care about!
1868which_win_system_info() ->
1869    F = fun() ->
1870                try
1871                    begin
1872                        SysInfo = os:cmd("systeminfo"),
1873                        process_win_system_info(
1874                          string:tokens(SysInfo, [$\r, $\n]), [])
1875                    end
1876                catch
1877                    C:E:S ->
1878                        io:format("Failed get or process System info: "
1879                                  "   Error Class: ~p"
1880                                  "   Error:       ~p"
1881                                  "   Stack:       ~p"
1882                                  "~n", [C, E, S]),
1883                        []
1884                end
1885        end,
1886    proxy_call(F, minutes(1),
1887               fun() -> throw({skip, "System info timeout"}) end).
1888
1889process_win_system_info([], Acc) ->
1890    Acc;
1891process_win_system_info([H|T], Acc) ->
1892    case string:tokens(H, [$:]) of
1893        [Key, Value] ->
1894            case string:to_lower(Key) of
1895                "os name" ->
1896                    process_win_system_info(T,
1897                                            [{os_name, string:trim(Value)}|Acc]);
1898                "os version" ->
1899                    process_win_system_info(T,
1900                                            [{os_version, string:trim(Value)}|Acc]);
1901                "system manufacturer" ->
1902                    process_win_system_info(T,
1903                                            [{system_manufacturer, string:trim(Value)}|Acc]);
1904                "system model" ->
1905                    process_win_system_info(T,
1906                                            [{system_model, string:trim(Value)}|Acc]);
1907                "processor(s)" ->
1908                    [NumProcStr|_] = string:tokens(Value, [$\ ]),
1909                    T2 = lists:nthtail(list_to_integer(NumProcStr), T),
1910                    process_win_system_info(T2,
1911                                            [{num_processors, NumProcStr}|Acc]);
1912                "total physical memory" ->
1913                    process_win_system_info(T,
1914                                            [{total_phys_memory, string:trim(Value)}|Acc]);
1915                _ ->
1916                    process_win_system_info(T, Acc)
1917            end;
1918        _ ->
1919            process_win_system_info(T, Acc)
1920    end.
1921
1922
1923linux_info_lookup(Key, File) ->
1924    try [string:trim(S) || S <- string:tokens(os:cmd("grep " ++ "\"" ++ Key ++ "\"" ++ " " ++ File), [$:,$\n])] of
1925        Info ->
1926            linux_info_lookup_collect(Key, Info, [])
1927    catch
1928        _:_:_ ->
1929            "-"
1930    end.
1931
1932linux_info_lookup_collect(_Key, [], Values) ->
1933    lists:reverse(Values);
1934linux_info_lookup_collect(Key, [Key, Value|Rest], Values) ->
1935    linux_info_lookup_collect(Key, Rest, [Value|Values]);
1936linux_info_lookup_collect(_, _, Values) ->
1937    lists:reverse(Values).
1938
1939
1940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1941%% Set kill timer
1942
1943set_kill_timer(Config) ->
1944    case init:get_argument(megaco_test_timeout) of
1945	{ok, _} ->
1946	    Config;
1947	_ ->
1948	    Time =
1949		case lookup_config(tc_timeout, Config) of
1950		    [] ->
1951			timer:minutes(5);
1952		    ConfigTime when is_integer(ConfigTime) ->
1953			ConfigTime
1954		end,
1955	    Dog = test_server:timetrap(Time),
1956	    [{kill_timer, Dog}|Config]
1957
1958
1959    end.
1960
1961reset_kill_timer(Config) ->
1962    case lists:keysearch(kill_timer, 1, Config) of
1963	{value, {kill_timer, Dog}} ->
1964	    test_server:timetrap_cancel(Dog),
1965	    lists:keydelete(kill_timer, 1, Config);
1966	_ ->
1967	    Config
1968    end.
1969
1970
1971%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1972
1973try_tc(TCName, Name, Verbosity, Pre, Case, Post)
1974  when is_function(Pre, 0)  andalso
1975       is_function(Case, 1) andalso
1976       is_function(Post, 1) ->
1977    process_flag(trap_exit, true),
1978    put(verbosity, Verbosity),
1979    put(sname,     Name),
1980    put(tc,        TCName),
1981    p("try_tc -> starting: try pre"),
1982    try Pre() of
1983        State ->
1984            p("try_tc -> pre done: try test case"),
1985            try Case(State) of
1986                Res ->
1987                    p("try_tc -> test case done: try post"),
1988                    (catch Post(State)),
1989                    p("try_tc -> done"),
1990                    Res
1991            catch
1992                throw:{skip, _} = SKIP:_ ->
1993                    p("try_tc -> test case (throw) skip: try post"),
1994                    (catch Post(State)),
1995                    p("try_tc -> test case (throw) skip: done"),
1996                    SKIP;
1997                exit:{skip, _} = SKIP:_ ->
1998                    p("try_tc -> test case (exit) skip: try post"),
1999                    (catch Post(State)),
2000                    p("try_tc -> test case (exit) skip: done"),
2001                    SKIP;
2002                C:E:S ->
2003                    p("try_tc -> test case failed: try post"),
2004                    (catch Post(State)),
2005                    case megaco_test_global_sys_monitor:events() of
2006                        [] ->
2007                            p("try_tc -> test case failed: done"),
2008                            exit({case_catched, C, E, S});
2009                        SysEvs ->
2010                            p("try_tc -> test case failed with system event(s): "
2011                              "~n   ~p", [SysEvs]),
2012                            {skip, "TC failure with system events"}
2013                    end
2014            end
2015    catch
2016        throw:{skip, _} = SKIP:_ ->
2017            p("try_tc -> pre (throw) skip"),
2018            SKIP;
2019        exit:{skip, _} = SKIP:_ ->
2020            p("try_tc -> pre (exit) skip"),
2021            SKIP;
2022        C:E:S ->
2023            case megaco_test_global_sys_monitor:events() of
2024                [] ->
2025                    p("try_tc -> pre failed: done"),
2026                    exit({pre_catched, C, E, S});
2027                SysEvs ->
2028                    p("try_tc -> pre failed with system event(s): "
2029                      "~n   ~p", [SysEvs]),
2030                    {skip, "TC pre failure with system events"}
2031            end
2032    end.
2033
2034
2035
2036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2037
2038prepare_test_case(Actions, N, Config, File, Line) ->
2039    OrigNodes = lookup_config(nodes, Config),
2040    TestNodes = lookup_config(nodenames, Config), %% For testserver
2041    This      = node(),
2042    SomeNodes = OrigNodes ++ (TestNodes -- OrigNodes),
2043    AllNodes  = [This | (SomeNodes -- [This])],
2044    Nodes     = pick_n_nodes(N, AllNodes, File, Line),
2045    start_nodes(Nodes, File, Line),
2046    do_prepare_test_case(Actions, Nodes, Config, File, Line).
2047
2048do_prepare_test_case([init | Actions], Nodes, Config, File, Line) ->
2049    process_flag(trap_exit, true),
2050    megaco_test_lib:flush(),
2051    do_prepare_test_case(Actions, Nodes, Config, File, Line);
2052do_prepare_test_case([{stop_app, App} | Actions], Nodes, Config, File, Line) ->
2053    _Res = rpc:multicall(Nodes, application, stop, [App]),
2054    do_prepare_test_case(Actions, Nodes, Config, File, Line);
2055do_prepare_test_case([], Nodes, _Config, _File, _Line) ->
2056    Nodes.
2057
2058pick_n_nodes(all, AllNodes, _File, _Line) ->
2059    AllNodes;
2060pick_n_nodes(N, AllNodes, _File, _Line)
2061  when is_integer(N) andalso (length(AllNodes) >= N) ->
2062    AllNodes -- lists:nthtail(N, AllNodes);
2063pick_n_nodes(N, AllNodes, File, Line) ->
2064    fatal_skip({too_few_nodes, N, AllNodes}, File, Line).
2065
2066lookup_config(Key,Config) ->
2067    case lists:keysearch(Key, 1, Config) of
2068	{value,{Key,Val}} ->
2069	    Val;
2070	_ ->
2071	    []
2072    end.
2073
2074mk_nodes(N) when (N > 0) ->
2075    mk_nodes(N, []).
2076
2077mk_nodes(0, Nodes) ->
2078    Nodes;
2079mk_nodes(N, []) ->
2080    mk_nodes(N - 1, [node()]);
2081mk_nodes(N, Nodes) when N > 0 ->
2082    Head = hd(Nodes),
2083    [Name, Host] = node_to_name_and_host(Head),
2084    Nodes ++ [mk_node(I, Name, Host) || I <- lists:seq(1, N)].
2085
2086mk_node(N, Name, Host) ->
2087    list_to_atom(lists:concat([Name ++ integer_to_list(N) ++ "@" ++ Host])).
2088
2089%% Returns [Name, Host]
2090node_to_name_and_host(Node) ->
2091    string:tokens(atom_to_list(Node), [$@]).
2092
2093
2094start_nodes(Nodes, File, Line) when is_list(Nodes) ->
2095    start_nodes(Nodes, false, File, Line).
2096
2097start_nodes(Nodes, Force, File, Line)
2098  when is_list(Nodes) andalso is_boolean(Force) ->
2099    start_nodes(Nodes, Force, File, Line, []).
2100
2101start_nodes([], _Force, _File, _Line, _Started) ->
2102    ok;
2103start_nodes([Node|Nodes], Force, File, Line, Started) ->
2104    try start_node(Node, Force, true, File, Line) of
2105        ok ->
2106            start_nodes(Nodes, Force, File, Line, [Node|Started])
2107    catch
2108        exit:{skip, _} = SKIP:_ ->
2109            (catch stop_nodes(lists:reverse(Started), File, Line)),
2110            exit(SKIP);
2111        C:E:S ->
2112            (catch stop_nodes(lists:reverse(Started), File, Line)),
2113            erlang:raise(C, E, S)
2114    end.
2115
2116start_node(Node, File, Line) ->
2117    start_node(Node, false, false, File, Line).
2118
2119start_node(Node, Force, File, Line)
2120  when is_atom(Node) andalso is_boolean(Force) ->
2121    start_node(Node, Force, false, File, Line).
2122
2123start_node(Node, Force, Retry, File, Line) ->
2124    case net_adm:ping(Node) of
2125        %% Do not require a *new* node
2126	pong when (Force =:= false) ->
2127            p("node ~p already running", [Node]),
2128	    ok;
2129
2130        %% Do require a *new* node, so kill this one and try again
2131	pong when ((Force =:= true) andalso (Retry =:= true)) ->
2132            e("node ~p already running - kill and retry", [Node]),
2133            case stop_node(Node) of
2134                ok ->
2135                    start_node(Node, Force, false, File, Line);
2136                error ->
2137                    e("node ~p already running - failed kill (no retry)", [Node]),
2138                    fatal_skip({node_already_running, Node}, File, Line)
2139            end;
2140
2141        %% Do require a *new* node, but no retry so give up and fail
2142        pong when (Force =:= true) ->
2143            e("node ~p already running", [Node]),
2144            fatal_skip({node_already_running, Node}, File, Line);
2145
2146        % Not (yet) running
2147        pang ->
2148	    [Name, Host] = node_to_name_and_host(Node),
2149            Pa = filename:dirname(code:which(?MODULE)),
2150            Args = " -pa " ++ Pa ++
2151                " -s " ++ atom_to_list(megaco_test_sys_monitor) ++ " start" ++
2152                " -s global sync",
2153            p("try start node ~p", [Node]),
2154	    case slave:start_link(Host, Name, Args) of
2155		{ok, NewNode} when NewNode =:= Node ->
2156                    p("node ~p started - now set path, cwd and sync", [Node]),
2157		    Path = code:get_path(),
2158		    {ok, Cwd} = file:get_cwd(),
2159		    true = rpc:call(Node, code, set_path, [Path]),
2160		    ok = rpc:call(Node, file, set_cwd, [Cwd]),
2161		    true = rpc:call(Node, code, set_path, [Path]),
2162		    {_, []} = rpc:multicall(global, sync, []),
2163		    ok;
2164		Other ->
2165                    e("failed starting node ~p: ~p", [Node, Other]),
2166		    fatal_skip({cannot_start_node, Node, Other}, File, Line)
2167	    end
2168    end.
2169
2170
2171stop_nodes(Nodes, File, Line) when is_list(Nodes) ->
2172    stop_nodes(Nodes, [], File, Line).
2173
2174stop_nodes([], [], _File, _Line) ->
2175    ok;
2176stop_nodes([], StillRunning, File, Line) ->
2177    e("Failed stopping nodes: "
2178      "~n   ~p", [StillRunning]),
2179    fatal_skip({failed_stop_nodes, lists:reverse(StillRunning)}, File, Line);
2180stop_nodes([Node|Nodes], Acc, File, Line) ->
2181    case stop_node(Node) of
2182        ok ->
2183            stop_nodes(Nodes, Acc, File, Line);
2184        error ->
2185            stop_nodes(Nodes, [Node|Acc], File, Line)
2186    end.
2187
2188
2189stop_node(Node, File, Line) when is_atom(Node) ->
2190    p("try stop node ~p", [Node]),
2191    case stop_node(Node) of
2192        ok ->
2193            ok;
2194        error ->
2195            fatal_skip({failed_stop_node, Node}, File, Line)
2196    end.
2197
2198stop_node(Node) ->
2199    p("try stop node ~p", [Node]),
2200    erlang:monitor_node(Node, true),
2201    rpc:call(Node, erlang, halt, []),
2202    receive
2203        {nodedown, Node} ->
2204            ok
2205    after 10000 ->
2206            e("failed stop node ~p", [Node]),
2207            error
2208    end.
2209
2210
2211
2212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2213
2214f(F, A) ->
2215    lists:flatten(io_lib:format(F, A)).
2216
2217e(F, A) ->
2218    print("ERROR", F, A).
2219
2220p(F) ->
2221    p(F, []).
2222
2223p(F, A) ->
2224    print("INFO", F, A).
2225
2226print(Pre, F, A) ->
2227    io:format("*** [~s] [~s] ~p " ++ F ++ "~n", [?FTS(), Pre, self() | A]).
2228
2229
2230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2231
2232explicit_inet_backend() ->
2233    %% This is intentional!
2234    %% This is a kernel flag, which if set disables
2235    %% our own special handling of the inet_backend
2236    %% in our test suites.
2237    case application:get_all_env(kernel) of
2238        Env when is_list(Env) ->
2239            case lists:keysearch(inet_backend, 1, Env) of
2240                {value, {inet_backend, _}} ->
2241                    true;
2242                _ ->
2243                    false
2244            end;
2245        _ ->
2246            false
2247    end.
2248
2249test_inet_backends() ->
2250    case init:get_argument(megaco) of
2251        {ok, SnmpArgs} when is_list(SnmpArgs) ->
2252            test_inet_backends(SnmpArgs, atom_to_list(?FUNCTION_NAME));
2253        error ->
2254            false
2255    end.
2256
2257test_inet_backends([], _) ->
2258    false;
2259test_inet_backends([[Key, Val] | _], Key) ->
2260    case list_to_atom(string:to_lower(Val)) of
2261        Bool when is_boolean(Bool) ->
2262            Bool;
2263        _ ->
2264            false
2265    end;
2266test_inet_backends([_|Args], Key) ->
2267    test_inet_backends(Args, Key).
2268
2269
2270inet_backend_opts(Config) when is_list(Config) ->
2271    case lists:keysearch(socket_create_opts, 1, Config) of
2272        {value, {socket_create_opts, InetBackendOpts}} ->
2273            InetBackendOpts;
2274        false ->
2275            []
2276    end.
2277
2278is_socket_backend(Config) when is_list(Config) ->
2279    case lists:keysearch(socket_create_opts, 1, Config) of
2280        {value, {socket_create_opts, [{inet_backend, socket}]}} ->
2281            true;
2282        _ ->
2283            false
2284    end.
2285
2286
2287open(Config, Pid, Opts)
2288  when is_list(Config) andalso is_pid(Pid) andalso is_list(Opts) ->
2289    InetBackendOpts = inet_backend_opts(Config),
2290    megaco_udp:open(Pid, InetBackendOpts ++ Opts).
2291
2292listen(Config, Pid, Opts)
2293  when is_list(Config) andalso is_pid(Pid) andalso is_list(Opts) ->
2294    InetBackendOpts = inet_backend_opts(Config),
2295    megaco_tcp:listen(Pid, InetBackendOpts ++ Opts).
2296
2297connect(Config, Ref, Opts)
2298  when is_list(Config) andalso is_list(Opts) ->
2299    InetBackendOpts = inet_backend_opts(Config),
2300    megaco_tcp:connect(Ref, InetBackendOpts ++ Opts).
2301
2302