aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/os_signal_SUITE.erl
blob: 6bafb0e18c670db7e5905b70e0c399b44aa606e6 (plain) (tree)























































































                                                                           
                                




                                 


                                                         










                                    
                                  













                                      
                                  






                                         
                                  




                           
                                   













                                      
                                   






                                         
                                   




                           
                                   













                                      
                                   






                                         
                                   




                           
                                   













                                      
                                   






                                         
                                   




                           
                                   













                                      
                                   






                                         
                                   




                           
                                        




                                      
                                   






                                      
                                   




                                      
                                   



                           
                                        








                                                            
                                   



































                                                               



                                                                   



























                                                               
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2017. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%

%%
%% File:    os_signal_SUITE.erl
%% Author:  Björn-Egil Dahlberg
%% Created: 2017-01-13
%%

-module(os_signal_SUITE).

-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([init_per_suite/1, end_per_suite/1]).

-export([set_alarm/1, fork/0, get_exit_code/1]).

% Test cases
-export([set_unset/1,
         t_sighup/1,
         t_sigusr1/1,
         t_sigusr2/1,
         t_sigterm/1,
         t_sigalrm/1,
         t_sigchld/1,
         t_sigchld_fork/1]).

-define(signal_server, erl_signal_server).

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap, {minutes, 2}}].

all() ->
    case os:type() of
        {win32, _} -> [];
        _ -> [set_unset,
              t_sighup,
              t_sigusr1,
              t_sigusr2,
              t_sigterm,
              t_sigalrm,
              t_sigchld,
              t_sigchld_fork]
    end.

init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
    Pid = erlang:whereis(?signal_server),
    true = erlang:unregister(?signal_server),
    [{signal_server, Pid}|Config].

end_per_testcase(_Func, Config) ->
    case proplists:get_value(signal_server, Config) of
        undefined -> ok;
        Pid ->
            true = erlang:register(?signal_server, Pid),
            ok
    end.

init_per_suite(Config) ->
    load_nif(Config),
    Config.

end_per_suite(_Config) ->
    ok.

%% tests

set_unset(_Config) ->
    Signals = [sighup, %sigint,
               sigquit, %sigill,
               sigabrt,
               sigalrm, sigterm,
               sigusr1, sigusr2,
               sigchld,
               sigstop, sigtstp],
    F1 = fun(Sig) -> ok = os:set_signal(Sig,handle) end,
    F2 = fun(Sig) -> ok = os:set_signal(Sig,default) end,
    F3 = fun(Sig) -> ok = os:set_signal(Sig,ignore) end,
    %% set handle
    ok = lists:foreach(F1, Signals),
    %% set ignore
    ok = lists:foreach(F2, Signals),
    %% set default
    ok = lists:foreach(F3, Signals),
    ok.

t_sighup(_Config) ->
    Pid1 = setup_service(),
    OsPid = os:getpid(),
    os:set_signal(sighup, handle),
    ok = kill("HUP", OsPid),
    ok = kill("HUP", OsPid),
    ok = kill("HUP", OsPid),
    Msgs1 = fetch_msgs(Pid1),
    io:format("Msgs1: ~p~n", [Msgs1]),
    [{notify,sighup},
     {notify,sighup},
     {notify,sighup}] = Msgs1,
    %% no proc
    ok = kill("HUP", OsPid),
    ok = kill("HUP", OsPid),
    ok = kill("HUP", OsPid),
    %% ignore
    Pid2 = setup_service(),
    os:set_signal(sighup, ignore),
    ok = kill("HUP", OsPid),
    ok = kill("HUP", OsPid),
    ok = kill("HUP", OsPid),
    Msgs2 = fetch_msgs(Pid2),
    io:format("Msgs2: ~p~n", [Msgs2]),
    [] = Msgs2,
    %% reset to handle (it's the default)
    os:set_signal(sighup, handle),
    ok.

t_sigusr1(_Config) ->
    Pid1 = setup_service(),
    OsPid = os:getpid(),
    os:set_signal(sigusr1, handle),
    ok = kill("USR1", OsPid),
    ok = kill("USR1", OsPid),
    ok = kill("USR1", OsPid),
    Msgs1 = fetch_msgs(Pid1),
    io:format("Msgs1: ~p~n", [Msgs1]),
    [{notify,sigusr1},
     {notify,sigusr1},
     {notify,sigusr1}] = Msgs1,
    %% no proc
    ok = kill("USR1", OsPid),
    ok = kill("USR1", OsPid),
    ok = kill("USR1", OsPid),
    %% ignore
    Pid2 = setup_service(),
    os:set_signal(sigusr1, ignore),
    ok = kill("USR1", OsPid),
    ok = kill("USR1", OsPid),
    ok = kill("USR1", OsPid),
    Msgs2 = fetch_msgs(Pid2),
    io:format("Msgs2: ~p~n", [Msgs2]),
    [] = Msgs2,
    %% reset to ignore (it's the default)
    os:set_signal(sigusr1, handle),
    ok.

t_sigusr2(_Config) ->
    Pid1 = setup_service(),
    OsPid = os:getpid(),
    os:set_signal(sigusr2, handle),
    ok = kill("USR2", OsPid),
    ok = kill("USR2", OsPid),
    ok = kill("USR2", OsPid),
    Msgs1 = fetch_msgs(Pid1),
    io:format("Msgs1: ~p~n", [Msgs1]),
    [{notify,sigusr2},
     {notify,sigusr2},
     {notify,sigusr2}] = Msgs1,
    %% no proc
    ok = kill("USR2", OsPid),
    ok = kill("USR2", OsPid),
    ok = kill("USR2", OsPid),
    %% ignore
    Pid2 = setup_service(),
    os:set_signal(sigusr2, ignore),
    ok = kill("USR2", OsPid),
    ok = kill("USR2", OsPid),
    ok = kill("USR2", OsPid),
    Msgs2 = fetch_msgs(Pid2),
    io:format("Msgs2: ~p~n", [Msgs2]),
    [] = Msgs2,
    %% reset to ignore (it's the default)
    os:set_signal(sigusr2, ignore),
    ok.

t_sigterm(_Config) ->
    Pid1 = setup_service(),
    OsPid = os:getpid(),
    os:set_signal(sigterm, handle),
    ok = kill("TERM", OsPid),
    ok = kill("TERM", OsPid),
    ok = kill("TERM", OsPid),
    Msgs1 = fetch_msgs(Pid1),
    io:format("Msgs1: ~p~n", [Msgs1]),
    [{notify,sigterm},
     {notify,sigterm},
     {notify,sigterm}] = Msgs1,
    %% no proc
    ok = kill("TERM", OsPid),
    ok = kill("TERM", OsPid),
    ok = kill("TERM", OsPid),
    %% ignore
    Pid2 = setup_service(),
    os:set_signal(sigterm, ignore),
    ok = kill("TERM", OsPid),
    ok = kill("TERM", OsPid),
    ok = kill("TERM", OsPid),
    Msgs2 = fetch_msgs(Pid2),
    io:format("Msgs2: ~p~n", [Msgs2]),
    [] = Msgs2,
    %% reset to handle (it's the default)
    os:set_signal(sigterm, handle),
    ok.

t_sigchld(_Config) ->
    Pid1 = setup_service(),
    OsPid = os:getpid(),
    os:set_signal(sigchld, handle),
    ok = kill("CHLD", OsPid),
    ok = kill("CHLD", OsPid),
    ok = kill("CHLD", OsPid),
    Msgs1 = fetch_msgs(Pid1),
    io:format("Msgs1: ~p~n", [Msgs1]),
    [{notify,sigchld},
     {notify,sigchld},
     {notify,sigchld}] = Msgs1,
    %% no proc
    ok = kill("CHLD", OsPid),
    ok = kill("CHLD", OsPid),
    ok = kill("CHLD", OsPid),
    %% ignore
    Pid2 = setup_service(),
    os:set_signal(sigchld, ignore),
    ok = kill("CHLD", OsPid),
    ok = kill("CHLD", OsPid),
    ok = kill("CHLD", OsPid),
    Msgs2 = fetch_msgs(Pid2),
    io:format("Msgs2: ~p~n", [Msgs2]),
    [] = Msgs2,
    %% reset to handle (it's the default)
    os:set_signal(sigchld, ignore),
    ok.


t_sigalrm(_Config) ->
    Pid1 = setup_service(),
    ok = os:set_signal(sigalrm, handle),
    ok = os_signal_SUITE:set_alarm(1),
    receive after 3000 -> ok end,
    Msgs1 = fetch_msgs(Pid1),
    [{notify,sigalrm}] = Msgs1,
    io:format("Msgs1: ~p~n", [Msgs1]),
    os:set_signal(sigalrm, ignore),
    Pid2 = setup_service(),
    ok = os_signal_SUITE:set_alarm(1),
    receive after 3000 -> ok end,
    Msgs2 = fetch_msgs(Pid2),
    [] = Msgs2,
    io:format("Msgs2: ~p~n", [Msgs2]),
    Pid3 = setup_service(),
    os:set_signal(sigalrm, handle),
    ok = os_signal_SUITE:set_alarm(1),
    receive after 3000 -> ok end,
    Msgs3 = fetch_msgs(Pid3),
    [{notify,sigalrm}] = Msgs3,
    io:format("Msgs3: ~p~n", [Msgs3]),
    os:set_signal(sigalrm, ignore),
    ok.

t_sigchld_fork(_Config) ->
    Pid1 = setup_service(),
    ok = os:set_signal(sigchld, handle),
    {ok,OsPid} = os_signal_SUITE:fork(),
    receive after 3000 -> ok end,
    Msgs1 = fetch_msgs(Pid1),
    io:format("Msgs1: ~p~n", [Msgs1]),
    [{notify,sigchld}] = Msgs1,
    {ok,Status} = os_signal_SUITE:get_exit_code(OsPid),
    io:format("exit status from ~w : ~w~n", [OsPid,Status]),
    42 = Status,
    %% reset to ignore (it's the default)
    os:set_signal(sigchld, ignore),
    ok.


%% nif stubs

set_alarm(_Secs) -> no.
fork() -> no.
get_exit_code(_OsPid) -> no.

%% aux

setup_service() ->
    Pid = spawn_link(fun msgs/0),
    true = erlang:register(?signal_server, Pid),
    Pid.

msgs() ->
    msgs([]).
msgs(Ms) ->
    receive
        {Pid, fetch_msgs} -> Pid ! {self(), lists:reverse(Ms)};
        Msg ->
            msgs([Msg|Ms])
    end.

fetch_msgs(Pid) ->
    Pid ! {self(), fetch_msgs},
    receive {Pid, Msgs} -> Msgs end.

kill(Signal, Pid) ->
    {0,_} = run("kill", ["-s", Signal, Pid]),
    receive after 200 -> ok end,
    ok.

load_nif(Config) ->
    Path = proplists:get_value(data_dir, Config),
    case erlang:load_nif(filename:join(Path,"os_signal_nif"), 0) of
        ok -> ok;
        {error,{reload,_}} -> ok
    end.

run(Program0, Args) -> run(".", Program0, Args).
run(Cwd, Program0, Args) when is_list(Cwd) ->
    Program = case os:find_executable(Program0) of
                  Path when is_list(Path) ->
                      Path;
                  false ->
                      exit(no)
              end,
    Options = [{args,Args},binary,exit_status,stderr_to_stdout,
               {line,4096}, {cd, Cwd}],
    try open_port({spawn_executable,Program}, Options) of
        Port ->
            run_loop(Port, [])
    catch
        error:_ ->
                      exit(no)
    end.

run_loop(Port, Output) ->
    receive
        {Port,{exit_status,Status}} ->
            {Status,lists:reverse(Output)};
        {Port,{data,{eol,Bin}}} ->
            run_loop(Port, [Bin|Output]);
        _Msg ->
            run_loop(Port, Output)
    end.