%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2007-2010. 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% %% %%%------------------------------------------------------------------- %%% File : erlexec_SUITE.erl %%% Author : Rickard Green %%% Description : Test erlexec's command line parsing %%% %%% Created : 22 May 2007 by Rickard Green %%%------------------------------------------------------------------- -module(erlexec_SUITE). %-define(line_trace, 1). -define(DEFAULT_TIMEOUT, ?t:minutes(1)). -export([all/1, init_per_testcase/2, fin_per_testcase/2]). -export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1]). -include_lib("test_server/include/test_server.hrl"). init_per_testcase(Case, Config) -> Dog = ?t:timetrap(?DEFAULT_TIMEOUT), SavedEnv = save_env(), [{testcase, Case}, {watchdog, Dog}, {erl_flags_env, SavedEnv} |Config]. fin_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), SavedEnv = ?config(erl_flags_env, Config), restore_env(SavedEnv), cleanup_nodes(), ?t:timetrap_cancel(Dog), ok. all(doc) -> []; all(suite) -> [args_file, evil_args_file, env, args_file_env, otp_7461, otp_8209]. otp_8209(doc) -> ["Test that plain first argument does not " "destroy -home switch [OTP-8209]"]; otp_8209(suite) -> []; otp_8209(Config) when is_list(Config) -> ?line {ok,[[PName]]} = init:get_argument(progname), ?line SNameS = "erlexec_test_01", ?line SName = list_to_atom(SNameS++"@"++ hd(tl(string:tokens(atom_to_list(node()),"@")))), ?line Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++ atom_to_list(erlang:get_cookie()), ?line open_port({spawn,Cmd},[]), ?line pong = loop_ping(SName,40), ?line {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]), ?line ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]), ?line ok = cleanup_nodes(), ok. cleanup_nodes() -> cleanup_node("erlexec_test_01",20). cleanup_node(SNameS,0) -> {error, {would_not_die,list_to_atom(SNameS)}}; cleanup_node(SNameS,N) -> SName = list_to_atom(SNameS++"@"++ hd(tl(string:tokens(atom_to_list(node()),"@")))), case rpc:call(SName,init,stop,[]) of {badrpc,_} -> ok; ok -> receive after 500 -> ok end, cleanup_node(SNameS,N-1) end. loop_ping(_,0) -> pang; loop_ping(Node,N) -> case net_adm:ping(Node) of pang -> receive after 500 -> ok end, loop_ping(Node, N-1); pong -> pong end. args_file(doc) -> []; args_file(suite) -> []; args_file(Config) when is_list(Config) -> ?line AFN1 = privfile("1", Config), ?line AFN2 = privfile("2", Config), ?line AFN3 = privfile("3", Config), ?line AFN4 = privfile("4", Config), ?line AFN5 = privfile("5", Config), ?line AFN6 = privfile("6", Config), ?line write_file(AFN1, "-MiscArg2~n" "# a comment +\\#1000~n" "+\\#200 # another comment~n" "~n" "# another config file to read~n" " -args_file ~s#acomment~n" "~n" "-MiscArg7~n" "#~n" "+\\#700~n" "-extra +XtraArg6~n", [AFN2]), ?line write_file(AFN2, "-MiscArg3~n" "+\\#300~n" "-args_file ~s~n" "-MiscArg5~n" "+\\#500#anothercomment -MiscArg10~n" "-args_file ~s~n" "-args_file ~s~n" "-args_file ~s~n" "-extra +XtraArg5~n", [AFN3, AFN4, AFN5, AFN6]), ?line write_file(AFN3, "# comment again~n" " -MiscArg4 +\\#400 -extra +XtraArg1"), ?line write_file(AFN4, " -MiscArg6 +\\#600 -extra +XtraArg2~n" "+XtraArg3~n" "+XtraArg4~n" "# comment again~n"), ?line write_file(AFN5, ""), ?line write_file(AFN6, "-extra # +XtraArg10~n"), ?line CmdLine = "+#100 -MiscArg1 " ++ "-args_file " ++ AFN1 ++ " +#800 -MiscArg8 -extra +XtraArg7 +XtraArg8", ?line {Emu, Misc, Extra} = emu_args(CmdLine), ?line verify_args(["-#100", "-#200", "-#300", "-#400", "-#500", "-#600", "-#700", "-#800"], Emu), ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"], Misc), ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"], Extra), ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", "-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8", "+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"], Emu), ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", "-#100", "-#200", "-#300", "-#400", "-#500", "-#600", "-#700", "-#800", "+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"], Misc), ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10", "-#100", "-#200", "-#300", "-#400", "-#500", "-#600", "-#700", "-#800", "-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"], Extra), ?line ok. evil_args_file(doc) -> []; evil_args_file(suite) -> []; evil_args_file(Config) when is_list(Config) -> ?line Lim = 300, ?line FNums = lists:seq(1, Lim), lists:foreach(fun (End) when End == Lim -> ?line AFN = privfile(integer_to_list(End), Config), ?line write_file(AFN, "-MiscArg~p ", [End]); (I) -> ?line AFNX = privfile(integer_to_list(I), Config), ?line AFNY = privfile(integer_to_list(I+1), Config), {Frmt, Args} = case I rem 2 of 0 -> {"-MiscArg~p -args_file ~s -MiscArg~p", [I, AFNY, I]}; _ -> {"-MiscArg~p -args_file ~s", [I, AFNY]} end, ?line write_file(AFNX, Frmt, Args) end, FNums), ?line {_Emu, Misc, _Extra} = emu_args("-args_file " ++ privfile("1", Config)), ?line ANums = FNums ++ lists:reverse(lists:filter(fun (I) when I == Lim -> false; (I) when I rem 2 == 0 -> true; (_) -> false end, FNums)), ?line verify_args(lists:map(fun (I) -> "-MiscArg"++integer_to_list(I) end, ANums), Misc), ?line ok. env(doc) -> []; env(suite) -> []; env(Config) when is_list(Config) -> ?line os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"), ?line CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4", ?line os:putenv("ERL_FLAGS", "-MiscArg3 +#300 -extra +XtraArg5"), ?line os:putenv("ERL_ZFLAGS", "-MiscArg4 +#400 -extra +XtraArg6"), ?line {Emu, Misc, Extra} = emu_args(CmdLine), ?line verify_args(["-#100", "-#200", "-#300", "-#400"], Emu), ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4"], Misc), ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6"], Extra), ?line ok. args_file_env(doc) -> []; args_file_env(suite) -> []; args_file_env(Config) when is_list(Config) -> ?line AFN1 = privfile("1", Config), ?line AFN2 = privfile("2", Config), ?line write_file(AFN1, "-MiscArg2 +\\#200 -extra +XtraArg1"), ?line write_file(AFN2, "-MiscArg3 +\\#400 -extra +XtraArg3"), ?line os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -args_file "++AFN1++ " -extra +XtraArg2"), ?line CmdLine = "+#300 -args_file "++AFN2++" -MiscArg4 -extra +XtraArg4", ?line os:putenv("ERL_FLAGS", "-MiscArg5 +#500 -extra +XtraArg5"), ?line os:putenv("ERL_ZFLAGS", "-MiscArg6 +#600 -extra +XtraArg6"), ?line {Emu, Misc, Extra} = emu_args(CmdLine), ?line verify_args(["-#100", "-#200", "-#300", "-#400", "-#500", "-#600"], Emu), ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4", "-MiscArg5", "-MiscArg6"], Misc), ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4", "+XtraArg5", "+XtraArg6"], Extra), ?line ok. %% Make sure "erl -detached" survives when parent process group gets killed otp_7461(doc) -> []; otp_7461(suite) -> []; otp_7461(Config) when is_list(Config) -> case os:type() of {unix,_} -> {NetStarted, _} = net_kernel:start([test_server, shortnames]), try net_kernel:monitor_nodes(true), register(otp_7461, self()), otp_7461_do(Config) after catch unregister(otp_7461), catch net_kernel:monitor_nodes(false), case NetStarted of ok -> net_kernel:stop(); _ -> ok end end; _ -> {skip,"Only on Unix."} end. otp_7461_do(Config) -> io:format("alive=~p node=~p\n",[is_alive(), node()]), TestProg = filename:join([?config(data_dir, Config), "erlexec_tests"]), {ok, [[ErlProg]]} = init:get_argument(progname), ?line Cmd = TestProg ++ " " ++ ErlProg ++ " -detached -sname " ++ get_nodename(otp_7461) ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " -s erlexec_SUITE otp_7461_remote init " ++ atom_to_list(node()), %% otp_7461 --------> erlexec_tests.c --------> cerl -detached %% open_port fork+exec io:format("spawn port prog ~p\n",[Cmd]), ?line Port = open_port({spawn, Cmd}, [eof]), io:format("Wait for node to connect...\n",[]), ?line {nodeup, Slave} = receive Msg -> Msg after 20*1000 -> timeout end, io:format("Node alive: ~p\n", [Slave]), ?line pong = net_adm:ping(Slave), io:format("Ping ok towards ~p\n", [Slave]), ?line Port ! { self(), {command, "K"}}, % Kill child process group ?line {Port, {data, "K"}} = receive Msg2 -> Msg2 end, ?line port_close(Port), %% Now the actual test. Detached node should still be alive. ?line pong = net_adm:ping(Slave), io:format("Ping still ok towards ~p\n", [Slave]), %% Halt node ?line rpc:cast(Slave, ?MODULE, otp_7461_remote, [[halt, self()]]), ?line {nodedown, Slave} = receive Msg3 -> Msg3 after 20*1000 -> timeout end, io:format("Node dead: ~p\n", [Slave]), ok. %% Executed on slave node otp_7461_remote([init, Master]) -> io:format("otp_7461_remote(init,~p) at ~p\n",[Master, node()]), net_kernel:connect_node(Master); otp_7461_remote([halt, Pid]) -> io:format("halt order from ~p to node ~p\n",[Pid,node()]), halt(). %% %% Utils %% save_env() -> {erl_flags, os:getenv("ERL_AFLAGS"), os:getenv("ERL_FLAGS"), os:getenv("ERL_"++erlang:system_info(otp_release)++"_FLAGS"), os:getenv("ERL_ZFLAGS")}. restore_env(EVar, false) when is_list(EVar) -> restore_env(EVar, ""); restore_env(EVar, "") when is_list(EVar) -> case os:getenv(EVar) of false -> ok; "" -> ok; " " -> ok; _ -> os:putenv(EVar, " ") end; restore_env(EVar, Value) when is_list(EVar), is_list(Value) -> case os:getenv(EVar) of Value -> ok; _ -> os:putenv(EVar, Value) end. restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) -> restore_env("ERL_AFLAGS", AFlgs), restore_env("ERL_FLAGS", Flgs), restore_env("ERL_"++erlang:system_info(otp_release)++"_FLAGS", RFlgs), restore_env("ERL_ZFLAGS", ZFlgs), ok. privfile(Name, Config) -> filename:join([?config(priv_dir, Config), atom_to_list(?config(testcase, Config)) ++ "." ++ Name]). write_file(FileName, Frmt) -> write_file(FileName, Frmt, []). write_file(FileName, Frmt, Args) -> {ok, File} = file:open(FileName, [write]), io:format(File, Frmt, Args), ok = file:close(File). verify_args([], _Ys) -> ok; verify_args(Xs, []) -> exit({args_not_found_in_order, Xs}); verify_args([X|Xs], [X|Ys]) -> verify_args(Xs, Ys); verify_args(Xs, [_Y|Ys]) -> verify_args(Xs, Ys). verify_not_args(Xs, Ys) -> lists:foreach(fun (X) -> case lists:member(X, Ys) of true -> exit({arg_present, X}); false -> ok end end, Xs). emu_args(CmdLineArgs) -> io:format("CmdLineArgs = ~s~n", [CmdLineArgs]), {ok,[[Erl]]} = init:get_argument(progname), EmuCL = os:cmd(Erl ++ " -emu_args_exit " ++ CmdLineArgs), io:format("EmuCL = ~s", [EmuCL]), split_emu_clt(string:tokens(EmuCL, [$ ,$\t,$\n,$\r])). split_emu_clt(EmuCLT) -> split_emu_clt(EmuCLT, [], [], [], emu). split_emu_clt([], _Emu, _Misc, _Extra, emu) -> exit(bad_cmd_line); split_emu_clt([], Emu, Misc, Extra, _Type) -> {lists:reverse(Emu), lists:reverse(Misc), lists:reverse(Extra)}; split_emu_clt(["--"|As], Emu, Misc, Extra, emu) -> split_emu_clt(As, Emu, Misc, Extra, misc); split_emu_clt([A|As], Emu, Misc, Extra, emu = Type) -> split_emu_clt(As, [A|Emu], Misc, Extra, Type); split_emu_clt(["-extra"|As], Emu, Misc, Extra, misc) -> split_emu_clt(As, Emu, Misc, Extra, extra); split_emu_clt([A|As], Emu, Misc, Extra, misc = Type) -> split_emu_clt(As, Emu, [A|Misc], Extra, Type); split_emu_clt([A|As], Emu, Misc, Extra, extra = Type) -> split_emu_clt(As, Emu, Misc, [A|Extra], Type). get_nodename(T) -> {A, B, C} = now(), atom_to_list(T) ++ "-" ++ atom_to_list(?MODULE) ++ "-" ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C).