1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1998-2013. 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: %%% Purpose: Test NT specific utilities 20: -module(nt_SUITE). 21: 22: -include_lib("test_server/include/test_server.hrl"). 23: -include_lib("kernel/include/file.hrl"). 24: 25: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 26: init_per_group/2,end_per_group/2,init_per_testcase/2, 27: end_per_testcase/2,nt/1,handle_eventlog/2, 28: middleman/1,service_basic/1, service_env/1, user_env/1, synced/1, 29: service_prio/1, 30: logout/1, debug/1, restart/1, restart_always/1,stopaction/1, 31: shutdown_io/0,do_shutdown_io/0]). 32: -define(TEST_TIMEOUT, ?t:seconds(180)). 33: 34: -define(TEST_SERVICES, [1,2,3,4,5,6,7,8,9,10,11]). 35: 36: suite() -> [{ct_hooks,[ts_install_cth]}]. 37: 38: all() -> 39: case os:type() of 40: {win32, nt} -> 41: [nt, service_basic, service_env, user_env, synced, 42: service_prio, logout, debug, restart, restart_always, 43: stopaction]; 44: _ -> [nt] 45: end. 46: 47: groups() -> 48: []. 49: 50: init_per_suite(Config) -> 51: Config. 52: 53: end_per_suite(_Config) -> 54: ok. 55: 56: init_per_group(_GroupName, Config) -> 57: Config. 58: 59: end_per_group(_GroupName, Config) -> 60: Config. 61: 62: 63: init_per_testcase(_Func, Config) -> 64: Dog = test_server:timetrap(?TEST_TIMEOUT), 65: [{watchdog, Dog} | Config]. 66: 67: end_per_testcase(_Func, Config) -> 68: lists:foreach(fun(X) -> 69: catch remove_service("test_service_" ++ 70: integer_to_list(X)) end, 71: ?TEST_SERVICES), 72: Dog = ?config(watchdog, Config), 73: catch test_server:timetrap_cancel(Dog), 74: ok. 75: 76: erlsrv() -> 77: os:find_executable(erlsrv). 78: 79: 80: recv_prog_output(Port) -> 81: receive 82: {Port, {data, {eol,Data}}} -> 83: %%io:format("Got data: ~s~n", [Data]), 84: [ Data | recv_prog_output(Port)]; 85: _X -> 86: %%io:format("Got data: ~p~n", [_X]), 87: Port ! {self(), close}, 88: receive 89: _ -> 90: [] 91: end 92: end. 93: 94: 95: %%% X == parameters to erlsrv 96: %%% returns command output without stderr 97: do_command(X) -> 98: %%io:format("Command: ~s~n", [erlsrv() ++ " " ++ X]), 99: Port = open_port({spawn, erlsrv() ++ " " ++ X}, [stream, {line, 100}, eof, in]), 100: Res = recv_prog_output(Port), 101: case Res of 102: [] -> 103: failed; 104: _Y -> 105: %%io:format("~p~n",[_Y]), 106: ok 107: end. 108: 109: 110: create_service(Name) -> 111: ok = do_command("add " ++ Name). 112: 113: start_service(Name) -> 114: ok = do_command("start " ++ Name). 115: 116: stop_service(Name) -> 117: ok = do_command("stop " ++ Name). 118: 119: remove_service(Name) -> 120: ok = do_command("remove " ++ Name). 121: do_wait_for_it(_,0) -> 122: false; 123: do_wait_for_it(FullName,N) -> 124: case net_adm:ping(FullName) of 125: pong -> 126: true; 127: _ -> 128: receive 129: after 1000 -> 130: do_wait_for_it(FullName,N-1) 131: end 132: end. 133: 134: wait_for_node(Name) -> 135: FullName = make_full_name(Name), 136: do_wait_for_it(FullName,30). 137: 138: make_full_name(Name) -> 139: [_,Suffix] = string:tokens(atom_to_list(node()),"@"), 140: list_to_atom(Name ++ "@" ++ Suffix). 141: 142: 143: %%% The following tests are only run on NT: 144: 145: service_basic(doc) -> 146: ["Check some basic (cosmetic) service parameters"]; 147: service_basic(suite) -> []; 148: service_basic(Config) when is_list(Config) -> 149: ?line Name = "test_service_20", 150: ?line IntName = Name++"_internal", 151: ?line Service = [{servicename,Name}, 152: {args, ["-setcookie", 153: atom_to_list(erlang:get_cookie())]}, 154: {internalservicename,IntName}, 155: {comment,"Epic comment"}], 156: ?line ok = erlsrv:store_service(Service), 157: ?line start_service(Name), 158: ?line true = wait_for_node(Name), 159: ?line S2 = erlsrv:get_service(Name), 160: ?line {value,{comment,"Epic comment"}} = lists:keysearch(comment,1,S2), 161: ?line {value,{internalservicename,IntName}} = 162: lists:keysearch(internalservicename,1,S2), 163: ?line S3 = lists:keyreplace(comment,1,S2,{comment,"Basic comment"}), 164: ?line S4 = lists:keyreplace(internalservicename,1,S3, 165: {internalservicename,"WillNotHappen"}), 166: ?line ok = erlsrv:store_service(S4), 167: ?line S5 = erlsrv:get_service(Name), 168: ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S5), 169: ?line {value,{internalservicename,IntName}} = 170: lists:keysearch(internalservicename,1,S5), 171: ?line NewName = "test_service_21", 172: ?line S6 = erlsrv:new_service(NewName,S5,[]), % should remove 173: % internalservicename 174: ?line ok = erlsrv:store_service(S6), 175: ?line S7 = erlsrv:get_service(NewName), 176: ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S7), 177: ?line {value,{internalservicename,[$t,$e,$s,$t | _]}} = 178: lists:keysearch(internalservicename,1,S7), 179: ?line remove_service(Name), 180: ?line remove_service(NewName), 181: ok. 182: 183: service_env(doc) -> 184: ["Check that service name and executable is in the environment of the " ++ 185: "erlang process created by erlsrv."]; 186: service_env(suite) -> []; 187: service_env(Config) when is_list(Config) -> 188: ?line Name = "test_service_2", 189: ?line Service = [{servicename,Name}, 190: {args, ["-setcookie", 191: atom_to_list(erlang:get_cookie())]}], 192: ?line ok = erlsrv:store_service(Service), 193: ?line start_service(Name), 194: ?line true = wait_for_node(Name), 195: ?line Name = rpc:call(make_full_name(Name),os,getenv, 196: ["ERLSRV_SERVICE_NAME"]), 197: ?line "erlsrv.exe" = filename:basename( 198: hd( 199: string:tokens( 200: rpc:call(make_full_name(Name), 201: os, 202: getenv, 203: ["ERLSRV_EXECUTABLE"]), 204: "\""))), 205: ?line remove_service(Name), 206: ok. 207: user_env(doc) -> 208: ["Check that the user defined environment is ADDED to the service's"++ 209: " normal dito."]; 210: user_env(suite) -> []; 211: user_env(Config) when is_list(Config) -> 212: ?line Name = "test_service_3", 213: ?line Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]}, 214: {args, ["-setcookie", 215: atom_to_list(erlang:get_cookie())]}], 216: ?line ok = erlsrv:store_service(Service), 217: ?line start_service(Name), 218: ?line true = wait_for_node(Name), 219: ?line true = rpc:call(make_full_name(Name),os,getenv, 220: ["SystemDrive"]) =/= false, 221: ?line "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]), 222: ?line remove_service(Name), 223: ok. 224: synced(doc) -> 225: ["Check that services are stopped and started syncronous and that"++ 226: " failed stopactions kill the erlang machine anyway."]; 227: synced(suite) -> []; 228: synced(Config) when is_list(Config) -> 229: ?line Name0 = "test_service_4", 230: ?line Service0 = [{servicename,Name0}, 231: {machine, "N:\\nickeNyfikenPaSjukhus"}], 232: ?line ok = erlsrv:store_service(Service0), 233: ?line true = (catch start_service(Name0)) =/= ok, 234: ?line remove_service(Name0), 235: ?line Name = "test_service_5", 236: ?line Service = [{servicename,Name}, 237: {stopaction,"erlang:info(garbage_collection)."}, 238: {args, ["-setcookie", 239: atom_to_list(erlang:get_cookie())]}], 240: ?line ok = erlsrv:store_service(Service), 241: ?line start_service(Name), 242: ?line true = wait_for_node(Name), 243: ?line T1 = calendar:datetime_to_gregorian_seconds( 244: calendar:universal_time()), 245: ?line stop_service(Name), 246: ?line Diff1 = calendar:datetime_to_gregorian_seconds( 247: calendar:universal_time()) - T1, 248: ?line true = Diff1 > 30, 249: ?line start_service(Name), 250: ?line true = wait_for_node(Name), 251: ?line T2 = calendar:datetime_to_gregorian_seconds( 252: calendar:universal_time()), 253: ?line remove_service(Name), 254: ?line Diff2 = calendar:datetime_to_gregorian_seconds( 255: calendar:universal_time()) - T2, 256: ?line true = Diff2 > 30, 257: ok. 258: service_prio(doc) -> 259: ["Check that a service with higher prio create port programs with " 260: "higher prio."]; 261: service_prio(suite) -> []; 262: service_prio(Config) when is_list(Config) -> 263: ?line Name = "test_service_6", 264: ?line Service = [{servicename,Name},{prio,"high"}, 265: {env, [{"HEART_COMMAND","echo off"}]}, 266: {args, ["-setcookie", 267: atom_to_list(erlang:get_cookie()), 268: "-heart"]}], 269: ?line ok = erlsrv:store_service(Service), 270: ?line {ok, OldProcs} = get_current_procs(Config), 271: ?line start_service(Name), 272: ?line {ok, NewProcs} = get_current_procs(Config), 273: timer:sleep(2000), 274: ?line {ok, NewProcs2} = get_current_procs(Config), 275: ?line remove_service(Name), 276: ?line Diff = arrived_procs(OldProcs,NewProcs), 277: io:format("NewProcs ~p~n after sleep~n ~p~n",[Diff, arrived_procs(OldProcs,NewProcs2)]), 278: %% Not really correct, could fail if another heart is 279: %% started at the same time... 280: ?line {value, {"heart.exe",_,"high"}} = 281: lists:keysearch("heart.exe",1,Diff), 282: ok. 283: logout(doc) -> 284: ["Check that logout does not kill services"]; 285: logout(suite) -> []; 286: logout(Config) when is_list(Config) -> 287: ?line {comment, "Have to be run manually by registering a service with " ++ 288: "heart, logout and log in again and then examine that the heart " ++ 289: "process id is not changed."}. 290: debug(doc) -> 291: ["Check the debug options to erlsrv."]; 292: debug(suite) -> []; 293: debug(Config) when is_list(Config) -> 294: ?line Name0 = "test_service_7", 295: 296: %% We used to set the privdir as temporary directory, but for some 297: %% reason we don't seem to have write access to that directory, 298: %% so we'll use the directory specified in the next line. 299: ?line TempDir = "C:/TEMP", 300: ?line Service0 = [{servicename,Name0}, 301: {workdir,filename:nativename(TempDir)}, 302: {debugtype,"reuse"}, 303: {args, ["-setcookie", 304: atom_to_list(erlang:get_cookie())]}], 305: ?line ok = erlsrv:store_service(Service0), 306: ?line T1 = calendar:datetime_to_gregorian_seconds( 307: calendar:local_time()), 308: %% sleep a little 309: ?line receive after 2000 -> ok end, 310: ?line start_service(Name0), 311: ?line true = wait_for_node(Name0), 312: ?line LF = filename:join(TempDir, Name0++".debug"), 313: ?line {ok,Info0} = file:read_file_info(LF), 314: ?line T2 = calendar:datetime_to_gregorian_seconds( 315: Info0#file_info.mtime), 316: ?line true = T2 > T1, 317: ?line remove_service(Name0), 318: ?line file:delete(LF), 319: ?line Name1 = "test_service_8", 320: ?line Service1 = [{servicename,Name1}, 321: {workdir, filename:nativename(TempDir)}, 322: {debugtype,"new"}, 323: {args, ["-setcookie", 324: atom_to_list(erlang:get_cookie())]}], 325: ?line ok = erlsrv:store_service(Service1), 326: ?line T3 = calendar:datetime_to_gregorian_seconds( 327: calendar:local_time()), 328: %% sleep a little 329: ?line receive after 2000 -> ok end, 330: ?line NF = next_logfile(TempDir, Name1), 331: ?line start_service(Name1), 332: ?line true = wait_for_node(Name1), 333: ?line {ok,Info1} = file:read_file_info(NF), 334: ?line T4 = calendar:datetime_to_gregorian_seconds( 335: Info1#file_info.mtime), 336: ?line true = T4 > T3, 337: ?line remove_service(Name1), 338: ?line file:delete(NF), 339: ok. 340: 341: restart(doc) -> 342: ["Check the restart options to erlsrv"]; 343: restart(suite) -> []; 344: restart(Config) when is_list(Config) -> 345: ?line Name = "test_service_9", 346: ?line Service = [{servicename,Name}, 347: {workdir, filename:nativename(logdir(Config))}, 348: {onfail,"restart"}, 349: {args, ["-setcookie", 350: atom_to_list(erlang:get_cookie())]}], 351: ?line ok = erlsrv:store_service(Service), 352: ?line start_service(Name), 353: ?line true = wait_for_node(Name), 354: ?line receive after 20000 -> ok end, 355: ?line rpc:call(make_full_name(Name),erlang,halt,[]), 356: ?line receive after 1000 -> ok end, 357: ?line true = wait_for_node(Name), 358: ?line rpc:call(make_full_name(Name),erlang,halt,[]), 359: ?line receive after 1000 -> ok end, 360: ?line false = wait_for_node(Name), 361: ?line remove_service(Name), 362: ok. 363: 364: restart_always(doc) -> 365: ["Check the restart options to erlsrv"]; 366: restart_always(suite) -> []; 367: restart_always(Config) when is_list(Config) -> 368: ?line Name = "test_service_10", 369: ?line Service = [{servicename,Name}, 370: {workdir, filename:nativename(logdir(Config))}, 371: {onfail,"restart_always"}, 372: {args, ["-setcookie", 373: atom_to_list(erlang:get_cookie())]}], 374: ?line ok = erlsrv:store_service(Service), 375: ?line start_service(Name), 376: ?line true = wait_for_node(Name), 377: ?line rpc:call(make_full_name(Name),erlang,halt,[]), 378: ?line receive after 1000 -> ok end, 379: ?line true = wait_for_node(Name), 380: ?line rpc:call(make_full_name(Name),erlang,halt,[]), 381: ?line receive after 1000 -> ok end, 382: ?line true = wait_for_node(Name), 383: ?line remove_service(Name), 384: ok. 385: stopaction(doc) -> 386: ["Check that stopaction does not hang output while shutting down"]; 387: stopaction(suite) -> []; 388: stopaction(Config) when is_list(Config) -> 389: ?line Name = "test_service_11", 390: %% Icky, I prepend the first element in the codepath, cause 391: %% I "suppose" it's the one to where I am. 392: ?line Service = [{servicename,Name}, 393: {stopaction,atom_to_list(?MODULE) ++ ":shutdown_io()."}, 394: {args, ["-setcookie", 395: atom_to_list(erlang:get_cookie()), 396: "-pa", hd(code:get_path())]}], 397: ?line ok = erlsrv:store_service(Service), 398: ?line start_service(Name), 399: ?line true = wait_for_node(Name), 400: ?line T1 = calendar:datetime_to_gregorian_seconds( 401: calendar:universal_time()), 402: ?line stop_service(Name), 403: ?line Diff1 = calendar:datetime_to_gregorian_seconds( 404: calendar:universal_time()) - T1, 405: ?line true = Diff1 < 30, 406: ?line remove_service(Name), 407: ok. 408: 409: 410: %%% This test is run on all platforms, but just gives a comment on 411: %%% other platforms than NT. 412: 413: nt(doc) -> 414: ["Run NT specific tests."]; 415: nt(suite) -> 416: []; 417: nt(Config) when is_list(Config) -> 418: case os:type() of 419: {win32,nt} -> 420: nt_run(); 421: _ -> 422: {skipped, "This test case is intended for Win NT only."} 423: end. 424: 425: 426: nt_run() -> 427: ?line start_all(), 428: ?line create_service("test_service_1"), 429: ?line R = start_look_for_single("System","ErlSrv","Informational", 430: ".*test_service_1.*started.*"), 431: ?line start_service("test_service_1"), 432: ?line Res = look_for_single(R), 433: ?line io:format("Result from eventlog: ~p~n", 434: [Res]), 435: ?line remove_service("test_service_1"), 436: ?line stop_all(), 437: ok. 438: 439: start_all() -> 440: Pid1 = spawn_link(?MODULE,middleman,[[]]), 441: register(?MODULE,Pid1), 442: _Pid2 = nteventlog:start("log_testing", 443: {?MODULE,handle_eventlog,[Pid1]}). 444: 445: stop_all() -> 446: ?MODULE ! stop, 447: nteventlog:stop(). 448: 449: start_look_for_single(Cat,Fac,Sev,MessRE) -> 450: Ref = make_ref(), 451: ?MODULE ! {lookfor, {self(), Ref, {Cat,Fac,Sev,MessRE}}}, 452: Ref. 453: 454: look_for_single(Ref) -> 455: receive 456: {Ref,Time,Mes} -> 457: {Time,Mes} 458: after 60000 -> 459: timeout 460: end. 461: 462: 463: %%% Mes = {Time,Category,Facility,Severity,Message} 464: handle_eventlog(Mes,Pid) -> 465: Pid ! Mes. 466: 467: %%% Waitfor = [{Pid, Ref, {Category,Facility,Severity,MessageRE}} ...] 468: middleman(Waitfor) -> 469: receive 470: {Time,Category,Facility,Severity,Message} -> 471: io:format("Middleman got ~s...", [Message]), 472: case match_event({Time,Category,Facility,Severity,Message}, 473: Waitfor) of 474: {ok, {Pid,Ref,Time,Mes}, Rest} -> 475: io:format("matched~n"), 476: Pid ! {Ref,Time,Mes}, 477: middleman(Rest); 478: _ -> 479: io:format("no match~n"), 480: middleman(Waitfor) 481: end; 482: {lookfor, X} -> 483: io:format("Middleman told to look for ~p~n", [X]), 484: middleman([X|Waitfor]); 485: stop -> 486: stopped; 487: _ -> 488: middleman(Waitfor) 489: end. 490: 491: 492: %%% Matches events, not tail recursive. 493: match_event(_X, []) -> 494: nomatch; 495: match_event({Time,Cat,Fac,Sev,Mes},[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Tail]) -> 496: case re:run(Mes,MesRE,[{capture,none}]) of 497: match -> 498: %%io:format("Match!~n"), 499: {ok,{Pid,Ref,Time,Mes},Tail}; 500: nomatch -> 501: %%io:format("No match~n"), 502: case match_event({Time,Cat,Fac,Sev,Mes},Tail) of 503: {ok,X,Rest} -> 504: {ok,X,[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Rest]}; 505: X -> 506: X 507: end 508: end; 509: match_event(X,[Y | T]) -> 510: %%io:format("X == ~p, Y == ~p~n",[X,Y]), 511: case match_event(X,T) of 512: {ok,Z,R} -> 513: {ok,Z,[Y|R]}; 514: XX -> 515: XX 516: end. 517: 518: arrived_procs(_,[]) -> 519: []; 520: arrived_procs(OldProcs,[{Executable, Pid, Priority} | TNewProcs]) -> 521: case lists:keysearch(Pid,2,OldProcs) of 522: {value, _} -> 523: arrived_procs(OldProcs, TNewProcs); 524: false -> 525: [{Executable, Pid, Priority} | arrived_procs(OldProcs, TNewProcs)] 526: end. 527: 528: 529: get_current_procs(Config) -> 530: ?line P = open_port({spawn,nt_info(Config) ++ " -E"}, 531: [{line,10000}]), 532: ?line L = receive 533: {P,{data,{eol,D}}} -> 534: D; 535: _ -> "error. " 536: end, 537: ?line P ! {self(), close}, 538: ?line receive 539: {P, closed} -> ok 540: end, 541: ?line {done,{ok,Tok,_},_} = erl_scan:tokens([],L,0), 542: ?line erl_parse:parse_term(Tok). 543: 544: nt_info(Config) when is_list(Config) -> 545: ?line filename:join(?config(data_dir, Config), "nt_info"). 546: 547: 548: logdir(Config) -> 549: ?line ?config(priv_dir, Config). 550: 551: look_for_next(Template,L,N) -> 552: ?line FN = Template ++ integer_to_list(N), 553: ?line case lists:member(FN,L) of 554: true -> 555: ?line look_for_next(Template,L,N+1); 556: false -> 557: ?line FN 558: end. 559: 560: next_logfile(LD, Servicename) -> 561: ?line {ok, Files} = file:list_dir(LD), 562: ?line Ftmpl = Servicename ++ ".debug.", 563: ?line filename:join(LD,look_for_next(Ftmpl,Files,1)). 564: 565: %%% Functions run by the service 566: 567: do_shutdown_io() -> 568: receive 569: after 2000 -> 570: io:format("IO in shutting down...~n"), 571: erlang:halt() 572: end. 573: 574: shutdown_io() -> 575: spawn(?MODULE,do_shutdown_io,[]).