1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2007-2011. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%%-------------------------------------------------------------------
   21: %%% File    : erlexec_SUITE.erl
   22: %%% Author  : Rickard Green <rickard.s.green@ericsson.com>
   23: %%% Description : Test erlexec's command line parsing
   24: %%%
   25: %%% Created : 22 May 2007 by Rickard Green <rickard.s.green@ericsson.com>
   26: %%%-------------------------------------------------------------------
   27: -module(erlexec_SUITE).
   28: 
   29: 
   30: %-define(line_trace, 1).
   31: 
   32: -define(DEFAULT_TIMEOUT, ?t:minutes(1)).
   33: 
   34: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   35: 	 init_per_group/2,end_per_group/2, 
   36: 	 init_per_testcase/2, end_per_testcase/2]).
   37: 
   38: -export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1, zdbbl_dist_buf_busy_limit/1]).
   39: 
   40: -include_lib("test_server/include/test_server.hrl").
   41:     
   42: 
   43: init_per_testcase(Case, Config) ->
   44:     Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
   45:     SavedEnv = save_env(),
   46:     [{testcase, Case}, {watchdog, Dog}, {erl_flags_env, SavedEnv} |Config].
   47: 
   48: end_per_testcase(_Case, Config) ->
   49:     Dog = ?config(watchdog, Config),
   50:     SavedEnv = ?config(erl_flags_env, Config),
   51:     restore_env(SavedEnv),
   52:     cleanup_nodes(),
   53:     ?t:timetrap_cancel(Dog),
   54:     ok.
   55: 
   56: suite() -> [{ct_hooks,[ts_install_cth]}].
   57: 
   58: all() -> 
   59:     [args_file, evil_args_file, env, args_file_env,
   60:      otp_7461, otp_8209, zdbbl_dist_buf_busy_limit].
   61: 
   62: groups() -> 
   63:     [].
   64: 
   65: init_per_suite(Config) ->
   66:     Config.
   67: 
   68: end_per_suite(_Config) ->
   69:     ok.
   70: 
   71: init_per_group(_GroupName, Config) ->
   72:     Config.
   73: 
   74: end_per_group(_GroupName, Config) ->
   75:     Config.
   76: 
   77: otp_8209(doc) ->
   78:     ["Test that plain first argument does not "
   79:      "destroy -home switch [OTP-8209]"];
   80: otp_8209(suite) ->
   81:     [];
   82: otp_8209(Config) when is_list(Config) ->
   83:     ?line {ok,[[PName]]} = init:get_argument(progname),
   84:     ?line SNameS = "erlexec_test_01",
   85:     ?line SName = list_to_atom(SNameS++"@"++
   86: 			 hd(tl(string:tokens(atom_to_list(node()),"@")))),
   87:     ?line Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++
   88: 	atom_to_list(erlang:get_cookie()),
   89:     ?line open_port({spawn,Cmd},[]),
   90:     ?line pong = loop_ping(SName,40),
   91:     ?line {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]),
   92:     ?line ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]),
   93:     ?line ok = cleanup_nodes(),
   94:     ok.
   95: 
   96: cleanup_nodes() ->
   97:     cleanup_node("erlexec_test_01",20).
   98: cleanup_node(SNameS,0) ->
   99:     {error, {would_not_die,list_to_atom(SNameS)}};
  100: cleanup_node(SNameS,N) ->
  101:     SName = list_to_atom(SNameS++"@"++
  102: 			 hd(tl(string:tokens(atom_to_list(node()),"@")))),
  103:     case rpc:call(SName,init,stop,[]) of
  104: 	{badrpc,_} ->
  105: 	    ok;
  106: 	ok ->
  107: 	    receive after 500 -> ok end,
  108: 	    cleanup_node(SNameS,N-1)
  109:     end.
  110: 
  111: loop_ping(_,0) ->
  112:     pang;
  113: loop_ping(Node,N) ->
  114:     case net_adm:ping(Node) of
  115: 	pang ->
  116: 	    receive 
  117: 	    after 500 ->
  118: 		    ok 
  119: 	    end,
  120: 	    loop_ping(Node, N-1);
  121: 	pong ->
  122: 	    pong
  123:     end.
  124: 
  125: args_file(doc) -> [];
  126: args_file(suite) -> [];
  127: args_file(Config) when is_list(Config) ->
  128:     ?line AFN1 = privfile("1", Config),
  129:     ?line AFN2 = privfile("2", Config),
  130:     ?line AFN3 = privfile("3", Config),
  131:     ?line AFN4 = privfile("4", Config),
  132:     ?line AFN5 = privfile("5", Config),
  133:     ?line AFN6 = privfile("6", Config),
  134:     ?line write_file(AFN1,
  135: 		     "-MiscArg2~n"
  136: 		     "# a comment +\\#1000~n"
  137: 		     "+\\#200 # another comment~n"
  138: 		     "~n"
  139: 		     "# another config file to read~n"
  140: 		     " -args_file ~s#acomment~n"
  141: 		     "~n"
  142: 		     "-MiscArg7~n"
  143: 		     "#~n"
  144: 		     "+\\#700~n"
  145: 		     "-extra +XtraArg6~n",
  146: 	       [AFN2]),
  147:     ?line write_file(AFN2,
  148: 		     "-MiscArg3~n"
  149: 		     "+\\#300~n"
  150: 		     "-args_file ~s~n"
  151: 		     "-MiscArg5~n"
  152: 		     "+\\#500#anothercomment -MiscArg10~n"
  153: 		     "-args_file ~s~n"
  154: 		     "-args_file ~s~n"
  155: 		     "-args_file ~s~n"
  156: 		     "-extra +XtraArg5~n",
  157: 		     [AFN3, AFN4, AFN5, AFN6]),
  158:     ?line write_file(AFN3,
  159: 		     "# comment again~n"
  160: 		     " -MiscArg4 +\\#400 -extra +XtraArg1"),
  161:     ?line write_file(AFN4,
  162: 		     " -MiscArg6 +\\#600 -extra +XtraArg2~n"
  163: 		     "+XtraArg3~n"
  164: 		     "+XtraArg4~n"
  165: 		     "# comment again~n"),
  166:     ?line write_file(AFN5, ""),
  167:     ?line write_file(AFN6, "-extra # +XtraArg10~n"),
  168:     ?line CmdLine = "+#100 -MiscArg1 "
  169: 	++ "-args_file " ++ AFN1
  170: 	++ " +#800 -MiscArg8 -extra +XtraArg7 +XtraArg8",
  171:     ?line {Emu, Misc, Extra} = emu_args(CmdLine),
  172:     ?line verify_args(["-#100", "-#200", "-#300", "-#400",
  173: 		       "-#500", "-#600", "-#700", "-#800"], Emu),
  174:     ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
  175: 		       "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
  176: 		      Misc),
  177:     ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
  178: 		       "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
  179: 		      Extra),
  180:     ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
  181: 			   "-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
  182: 			   "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8",
  183: 			   "+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
  184: 			   "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
  185: 			  Emu),
  186:     ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
  187: 			   "-#100", "-#200", "-#300", "-#400",
  188: 			   "-#500", "-#600", "-#700", "-#800",
  189: 			   "+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
  190: 			   "+XtraArg5", "+XtraArg6", "+XtraArg7", "+XtraArg8"],
  191: 			  Misc),
  192:     ?line verify_not_args(["-MiscArg10", "-#1000", "+XtraArg10",
  193: 			   "-#100", "-#200", "-#300", "-#400",
  194: 			   "-#500", "-#600", "-#700", "-#800",
  195: 			   "-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
  196: 			   "-MiscArg5", "-MiscArg6", "-MiscArg7", "-MiscArg8"],
  197: 			  Extra),
  198:     ?line ok.
  199: 
  200: evil_args_file(doc) -> [];
  201: evil_args_file(suite) -> [];
  202: evil_args_file(Config) when is_list(Config) ->
  203:     ?line Lim = 300,
  204:     ?line FNums = lists:seq(1, Lim),
  205:     lists:foreach(fun (End) when End == Lim ->
  206: 			  ?line AFN = privfile(integer_to_list(End), Config),
  207: 			  ?line write_file(AFN,
  208: 					   "-MiscArg~p ",
  209: 					   [End]);
  210: 		      (I) ->
  211: 			  ?line AFNX = privfile(integer_to_list(I), Config),
  212: 			  ?line AFNY = privfile(integer_to_list(I+1), Config),
  213: 			  {Frmt, Args} =
  214: 			      case I rem 2 of
  215: 				  0 ->
  216: 				      {"-MiscArg~p -args_file ~s -MiscArg~p",
  217: 				       [I, AFNY, I]};
  218: 				  _ ->
  219: 				      {"-MiscArg~p -args_file ~s",
  220: 				       [I, AFNY]}
  221: 			      end,
  222: 			  ?line write_file(AFNX, Frmt, Args)
  223: 		  end,
  224: 		  FNums),
  225:     ?line {_Emu, Misc, _Extra} = emu_args("-args_file "
  226: 					  ++ privfile("1", Config)),
  227:     ?line ANums = FNums
  228: 	++ lists:reverse(lists:filter(fun (I) when I == Lim -> false;
  229: 					  (I) when I rem 2 == 0 -> true;
  230: 					  (_) -> false
  231: 				      end, FNums)),
  232:     ?line verify_args(lists:map(fun (I) -> "-MiscArg"++integer_to_list(I) end,
  233: 				ANums),
  234: 		      Misc),
  235:     ?line ok.
  236: 		      
  237: 			  
  238: 
  239: env(doc) -> [];
  240: env(suite) -> [];
  241: env(Config) when is_list(Config) ->
  242:     ?line os:putenv("ERL_AFLAGS", "-MiscArg1 +#100 -extra +XtraArg1 +XtraArg2"),
  243:     ?line CmdLine = "+#200 -MiscArg2 -extra +XtraArg3 +XtraArg4",
  244:     ?line os:putenv("ERL_FLAGS", "-MiscArg3 +#300 -extra +XtraArg5"),
  245:     ?line os:putenv("ERL_ZFLAGS", "-MiscArg4 +#400 -extra +XtraArg6"),
  246:     ?line {Emu, Misc, Extra} = emu_args(CmdLine),
  247:     ?line verify_args(["-#100", "-#200", "-#300", "-#400"], Emu),
  248:     ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4"],
  249: 		      Misc),
  250:     ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
  251: 		       "+XtraArg5", "+XtraArg6"],
  252: 		      Extra),
  253:     ?line ok.
  254: 
  255: args_file_env(doc) -> [];
  256: args_file_env(suite) -> [];
  257: args_file_env(Config) when is_list(Config) ->
  258:     ?line AFN1 = privfile("1", Config),
  259:     ?line AFN2 = privfile("2", Config),
  260:     ?line write_file(AFN1, "-MiscArg2 +\\#200 -extra +XtraArg1"),
  261:     ?line write_file(AFN2, "-MiscArg3 +\\#400 -extra +XtraArg3"),
  262:     ?line os:putenv("ERL_AFLAGS",
  263: 		    "-MiscArg1 +#100 -args_file "++AFN1++ " -extra +XtraArg2"),
  264:     ?line CmdLine = "+#300 -args_file "++AFN2++" -MiscArg4 -extra +XtraArg4",
  265:     ?line os:putenv("ERL_FLAGS", "-MiscArg5 +#500 -extra +XtraArg5"),
  266:     ?line os:putenv("ERL_ZFLAGS", "-MiscArg6 +#600 -extra +XtraArg6"),
  267:     ?line {Emu, Misc, Extra} = emu_args(CmdLine),
  268:     ?line verify_args(["-#100", "-#200", "-#300", "-#400",
  269: 		       "-#500", "-#600"], Emu),
  270:     ?line verify_args(["-MiscArg1", "-MiscArg2", "-MiscArg3", "-MiscArg4",
  271: 		       "-MiscArg5", "-MiscArg6"],
  272: 		      Misc),
  273:     ?line verify_args(["+XtraArg1", "+XtraArg2", "+XtraArg3", "+XtraArg4",
  274: 		       "+XtraArg5", "+XtraArg6"],
  275: 		      Extra),
  276:     ?line ok.
  277: 
  278: %% Make sure "erl -detached" survives when parent process group gets killed
  279: otp_7461(doc) -> [];
  280: otp_7461(suite) -> [];
  281: otp_7461(Config) when is_list(Config) ->   
  282:     case os:type() of
  283:     	{unix,_} ->
  284: 	    {NetStarted, _} = net_kernel:start([test_server, shortnames]),
  285: 	    try
  286: 		net_kernel:monitor_nodes(true),
  287: 		register(otp_7461, self()),	    
  288: 
  289: 		otp_7461_do(Config)		
  290: 	    after 
  291: 		catch unregister(otp_7461),
  292: 	        catch net_kernel:monitor_nodes(false),
  293: 	        case NetStarted of
  294: 		    ok -> net_kernel:stop();
  295: 		    _ -> ok
  296: 		end
  297: 	    end;
  298: 	_ ->
  299: 	    {skip,"Only on Unix."}
  300:     end.
  301: 	
  302: otp_7461_do(Config) ->
  303:     io:format("alive=~p node=~p\n",[is_alive(), node()]),
  304:     TestProg = filename:join([?config(data_dir, Config), "erlexec_tests"]),
  305:     {ok, [[ErlProg]]} = init:get_argument(progname),
  306:     ?line Cmd = TestProg ++ " " ++ ErlProg ++
  307: 	" -detached -sname " ++ get_nodename(otp_7461) ++
  308: 	" -setcookie " ++ atom_to_list(erlang:get_cookie()) ++
  309: 	" -pa " ++ filename:dirname(code:which(?MODULE)) ++
  310: 	" -s erlexec_SUITE otp_7461_remote init " ++ atom_to_list(node()),
  311:     
  312:     %% otp_7461 --------> erlexec_tests.c --------> cerl -detached
  313:     %%          open_port                 fork+exec
  314:     
  315:     io:format("spawn port prog ~p\n",[Cmd]),
  316:     ?line Port = open_port({spawn, Cmd}, [eof]),
  317:     
  318:     io:format("Wait for node to connect...\n",[]),    
  319:     ?line {nodeup, Slave} = receive Msg -> Msg
  320: 			    after 20*1000 -> timeout end,
  321:     io:format("Node alive: ~p\n", [Slave]),
  322:     
  323:     ?line pong = net_adm:ping(Slave),
  324:     io:format("Ping ok towards ~p\n", [Slave]),
  325:     
  326:     ?line Port ! { self(), {command, "K"}}, % Kill child process group
  327:     ?line {Port, {data, "K"}} = receive Msg2 -> Msg2 end,
  328:     ?line port_close(Port),
  329:     
  330:     %% Now the actual test. Detached node should still be alive.
  331:     ?line pong = net_adm:ping(Slave),
  332:     io:format("Ping still ok towards ~p\n", [Slave]),
  333:     
  334:     %% Halt node
  335:     ?line rpc:cast(Slave, ?MODULE, otp_7461_remote, [[halt, self()]]),
  336:     
  337:     ?line {nodedown, Slave} = receive Msg3 -> Msg3 
  338: 			      after 20*1000 -> timeout end,
  339:     io:format("Node dead: ~p\n", [Slave]),
  340:     ok.
  341: 
  342:       	    
  343: %% Executed on slave node
  344: otp_7461_remote([init, Master]) ->
  345:     io:format("otp_7461_remote(init,~p) at ~p\n",[Master, node()]),
  346:     net_kernel:connect_node(Master);
  347: otp_7461_remote([halt, Pid]) ->
  348:     io:format("halt order from ~p to node ~p\n",[Pid,node()]),
  349:     halt().
  350: 
  351: zdbbl_dist_buf_busy_limit(doc) ->    
  352:     ["Check +zdbbl flag"];
  353: zdbbl_dist_buf_busy_limit(suite) ->
  354:     [];
  355: zdbbl_dist_buf_busy_limit(Config) when is_list(Config) ->
  356:     LimKB = 1122233,
  357:     LimB = LimKB*1024,
  358:     ?line {ok,[[PName]]} = init:get_argument(progname),
  359:     ?line SNameS = "erlexec_test_02",
  360:     ?line SName = list_to_atom(SNameS++"@"++
  361:                          hd(tl(string:tokens(atom_to_list(node()),"@")))),
  362:     ?line Cmd = PName ++ " -sname "++SNameS++" -setcookie "++
  363:         atom_to_list(erlang:get_cookie()) ++
  364: 	" +zdbbl " ++ integer_to_list(LimKB),
  365:     ?line open_port({spawn,Cmd},[]),
  366:     ?line pong = loop_ping(SName,40),
  367:     ?line LimB = rpc:call(SName,erlang,system_info,[dist_buf_busy_limit]),
  368:     ?line ok = cleanup_node(SNameS, 10),
  369:     ok.
  370:     
  371: 
  372: %%
  373: %% Utils
  374: %%
  375: 
  376: save_env() ->
  377:     {erl_flags,
  378:      os:getenv("ERL_AFLAGS"),
  379:      os:getenv("ERL_FLAGS"),
  380:      os:getenv("ERL_"++erlang:system_info(otp_release)++"_FLAGS"),
  381:      os:getenv("ERL_ZFLAGS")}.
  382: 
  383: restore_env(EVar, false) when is_list(EVar) ->
  384:     restore_env(EVar, "");
  385: restore_env(EVar, "") when is_list(EVar) ->
  386:     case os:getenv(EVar) of
  387: 	false -> ok;
  388: 	"" -> ok;
  389: 	" " -> ok;
  390: 	_ -> os:putenv(EVar, " ")
  391:     end;
  392: restore_env(EVar, Value) when is_list(EVar), is_list(Value) ->
  393:     case os:getenv(EVar) of
  394: 	Value -> ok;
  395: 	_ -> os:putenv(EVar, Value)
  396:     end.
  397: 
  398: restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) ->
  399:     restore_env("ERL_AFLAGS", AFlgs),
  400:     restore_env("ERL_FLAGS", Flgs),
  401:     restore_env("ERL_"++erlang:system_info(otp_release)++"_FLAGS", RFlgs),
  402:     restore_env("ERL_ZFLAGS", ZFlgs),
  403:     ok.
  404: 
  405: privfile(Name, Config) ->
  406:     filename:join([?config(priv_dir, Config),
  407: 		   atom_to_list(?config(testcase, Config)) ++ "." ++ Name]).
  408: 
  409: write_file(FileName, Frmt) ->
  410:     write_file(FileName, Frmt, []).
  411: 
  412: write_file(FileName, Frmt, Args) ->
  413:     {ok, File} = file:open(FileName, [write]),
  414:     io:format(File, Frmt, Args),
  415:     ok = file:close(File).
  416: 
  417: verify_args([], _Ys) ->
  418:     ok;
  419: verify_args(Xs, []) ->
  420:     exit({args_not_found_in_order, Xs});
  421: verify_args([X|Xs], [X|Ys]) ->
  422:     verify_args(Xs, Ys);
  423: verify_args(Xs, [_Y|Ys]) ->
  424:     verify_args(Xs, Ys).
  425: 
  426: verify_not_args(Xs, Ys) ->
  427:     lists:foreach(fun (X) ->
  428: 			  case lists:member(X, Ys) of
  429: 			      true -> exit({arg_present, X});
  430: 			      false -> ok
  431: 			  end
  432: 		  end,
  433: 		  Xs).
  434: 
  435: emu_args(CmdLineArgs) ->
  436:     io:format("CmdLineArgs = ~s~n", [CmdLineArgs]),
  437:     {ok,[[Erl]]} = init:get_argument(progname),
  438:     EmuCL = os:cmd(Erl ++ " -emu_args_exit " ++ CmdLineArgs),
  439:     io:format("EmuCL = ~s", [EmuCL]),
  440:     split_emu_clt(string:tokens(EmuCL, [$ ,$\t,$\n,$\r])).
  441: 
  442: split_emu_clt(EmuCLT) ->
  443:     split_emu_clt(EmuCLT, [], [], [], emu).
  444: 
  445: split_emu_clt([], _Emu, _Misc, _Extra, emu) ->
  446:     exit(bad_cmd_line);
  447: split_emu_clt([], Emu, Misc, Extra, _Type) ->
  448:     {lists:reverse(Emu), lists:reverse(Misc), lists:reverse(Extra)};
  449: 
  450: split_emu_clt(["--"|As], Emu, Misc, Extra, emu) ->
  451:     split_emu_clt(As, Emu, Misc, Extra, misc);
  452: split_emu_clt([A|As], Emu, Misc, Extra, emu = Type) ->
  453:     split_emu_clt(As, [A|Emu], Misc, Extra, Type);
  454: 
  455: split_emu_clt(["-extra"|As], Emu, Misc, Extra, misc) ->
  456:     split_emu_clt(As, Emu, Misc, Extra, extra);
  457: split_emu_clt([A|As], Emu, Misc, Extra, misc = Type) ->
  458:     split_emu_clt(As, Emu, [A|Misc], Extra, Type);
  459: 
  460: split_emu_clt([A|As], Emu, Misc, Extra, extra = Type) ->
  461:     split_emu_clt(As, Emu, Misc, [A|Extra], Type).
  462:     
  463: 
  464: get_nodename(T) ->
  465:     {A, B, C} = now(),
  466:     atom_to_list(T)
  467: 	++ "-"
  468: 	++ atom_to_list(?MODULE)
  469: 	++ "-"
  470: 	++ integer_to_list(A)
  471: 	++ "-"
  472: 	++ integer_to_list(B)
  473: 	++ "-"
  474: 	++ integer_to_list(C).