From afbec46949f0d31448386234efa89cec78b905e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Jan 2017 11:52:39 +0100 Subject: erts: Add OS signal tests --- erts/emulator/test/Makefile | 1 + erts/emulator/test/os_signal_SUITE.erl | 354 +++++++++++++++++++++ .../test/os_signal_SUITE_data/Makefile.src | 6 + .../test/os_signal_SUITE_data/os_signal_nif.c | 66 ++++ 4 files changed, 427 insertions(+) create mode 100644 erts/emulator/test/os_signal_SUITE.erl create mode 100644 erts/emulator/test/os_signal_SUITE_data/Makefile.src create mode 100644 erts/emulator/test/os_signal_SUITE_data/os_signal_nif.c (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 2e48c475d5..5ec6ff1901 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -85,6 +85,7 @@ MODULES= \ num_bif_SUITE \ message_queue_data_SUITE \ op_SUITE \ + os_signal_SUITE \ port_SUITE \ port_bif_SUITE \ process_SUITE \ diff --git a/erts/emulator/test/os_signal_SUITE.erl b/erts/emulator/test/os_signal_SUITE.erl new file mode 100644 index 0000000000..2b9343663d --- /dev/null +++ b/erts/emulator/test/os_signal_SUITE.erl @@ -0,0 +1,354 @@ +%% +%% %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) -> true = erts_internal:set_signal(Sig,handle) end, + F2 = fun(Sig) -> true = erts_internal:set_signal(Sig,default) end, + F3 = fun(Sig) -> true = erts_internal: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(), + erts_internal: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(), + erts_internal: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) + erts_internal:set_signal(sighup, handle), + ok. + +t_sigusr1(_Config) -> + Pid1 = setup_service(), + OsPid = os:getpid(), + erts_internal: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(), + erts_internal: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) + erts_internal:set_signal(sigusr1, handle), + ok. + +t_sigusr2(_Config) -> + Pid1 = setup_service(), + OsPid = os:getpid(), + erts_internal: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(), + erts_internal: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) + erts_internal:set_signal(sigusr2, ignore), + ok. + +t_sigterm(_Config) -> + Pid1 = setup_service(), + OsPid = os:getpid(), + erts_internal: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(), + erts_internal: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) + erts_internal:set_signal(sigterm, handle), + ok. + +t_sigchld(_Config) -> + Pid1 = setup_service(), + OsPid = os:getpid(), + erts_internal: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(), + erts_internal: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) + erts_internal:set_signal(sigchld, ignore), + ok. + + +t_sigalrm(_Config) -> + Pid1 = setup_service(), + true = erts_internal: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]), + erts_internal: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(), + erts_internal: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]), + erts_internal:set_signal(sigalrm, ignore), + ok. + +t_sigchld_fork(_Config) -> + Pid1 = setup_service(), + true = erts_internal: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) + erts_internal: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), + ok = erlang:load_nif(filename:join(Path,"os_signal_nif"), 0). + +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. diff --git a/erts/emulator/test/os_signal_SUITE_data/Makefile.src b/erts/emulator/test/os_signal_SUITE_data/Makefile.src new file mode 100644 index 0000000000..a7f5cdbba5 --- /dev/null +++ b/erts/emulator/test/os_signal_SUITE_data/Makefile.src @@ -0,0 +1,6 @@ + +NIF_LIBS = os_signal_nif@dll@ + +all: $(NIF_LIBS) + +@SHLIB_RULES@ diff --git a/erts/emulator/test/os_signal_SUITE_data/os_signal_nif.c b/erts/emulator/test/os_signal_SUITE_data/os_signal_nif.c new file mode 100644 index 0000000000..78e1348383 --- /dev/null +++ b/erts/emulator/test/os_signal_SUITE_data/os_signal_nif.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#include + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + return 0; +} + +static ERL_NIF_TERM set_alarm(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int t; + if (!enif_get_int(env, argv[0], &t)) { + return enif_make_badarg(env); + } + + alarm(t); + + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM fork_0(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + pid_t pid; + + pid = fork(); + + if (pid == 0) { + /* child */ + exit(42); + } + + return enif_make_tuple(env, 2, + enif_make_atom(env, "ok"), + enif_make_int(env, (int)pid)); +} + +static ERL_NIF_TERM get_exit_code(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int x; + pid_t pid; + if (!enif_get_int(env, argv[0], &x)) { + return enif_make_badarg(env); + } + + pid = (pid_t) x; + + waitpid(pid, &x, 0); + + return enif_make_tuple(env, 2, + enif_make_atom(env, "ok"), + enif_make_int(env, WEXITSTATUS(x))); +} + + +static ErlNifFunc nif_funcs[] = +{ + {"set_alarm", 1, set_alarm}, + {"fork", 0, fork_0}, + {"get_exit_code", 1, get_exit_code} +}; + +ERL_NIF_INIT(os_signal_SUITE,nif_funcs,load,NULL,NULL,NULL) -- cgit v1.2.3 From 9ccb988eeb79815a33e0dd138278e0c29ed3ca6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 23 Jan 2017 16:05:07 +0100 Subject: erts: Do not handle SIGILL * Remove SIGILL from signal whitelist --- erts/emulator/test/os_signal_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/os_signal_SUITE.erl b/erts/emulator/test/os_signal_SUITE.erl index 2b9343663d..7c3950114f 100644 --- a/erts/emulator/test/os_signal_SUITE.erl +++ b/erts/emulator/test/os_signal_SUITE.erl @@ -86,7 +86,7 @@ end_per_suite(_Config) -> set_unset(_Config) -> Signals = [sighup, %sigint, - sigquit, sigill, + sigquit, %sigill, sigabrt, sigalrm, sigterm, sigusr1, sigusr2, -- cgit v1.2.3 From d4bd17da9759af54227891a90d9fb83d5bfe6d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Jan 2017 10:59:30 +0100 Subject: erts: Use os module instead of erts_internal for set_signal/2 * Add specs * Change return signature to 'ok' instead of 'true' --- erts/emulator/test/os_signal_SUITE.erl | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/os_signal_SUITE.erl b/erts/emulator/test/os_signal_SUITE.erl index 7c3950114f..9aa49a453e 100644 --- a/erts/emulator/test/os_signal_SUITE.erl +++ b/erts/emulator/test/os_signal_SUITE.erl @@ -92,9 +92,9 @@ set_unset(_Config) -> sigusr1, sigusr2, sigchld, sigstop, sigtstp], - F1 = fun(Sig) -> true = erts_internal:set_signal(Sig,handle) end, - F2 = fun(Sig) -> true = erts_internal:set_signal(Sig,default) end, - F3 = fun(Sig) -> true = erts_internal:set_signal(Sig,ignore) end, + 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 @@ -106,7 +106,7 @@ set_unset(_Config) -> t_sighup(_Config) -> Pid1 = setup_service(), OsPid = os:getpid(), - erts_internal:set_signal(sighup, handle), + os:set_signal(sighup, handle), ok = kill("HUP", OsPid), ok = kill("HUP", OsPid), ok = kill("HUP", OsPid), @@ -121,7 +121,7 @@ t_sighup(_Config) -> ok = kill("HUP", OsPid), %% ignore Pid2 = setup_service(), - erts_internal:set_signal(sighup, ignore), + os:set_signal(sighup, ignore), ok = kill("HUP", OsPid), ok = kill("HUP", OsPid), ok = kill("HUP", OsPid), @@ -129,13 +129,13 @@ t_sighup(_Config) -> io:format("Msgs2: ~p~n", [Msgs2]), [] = Msgs2, %% reset to handle (it's the default) - erts_internal:set_signal(sighup, handle), + os:set_signal(sighup, handle), ok. t_sigusr1(_Config) -> Pid1 = setup_service(), OsPid = os:getpid(), - erts_internal:set_signal(sigusr1, handle), + os:set_signal(sigusr1, handle), ok = kill("USR1", OsPid), ok = kill("USR1", OsPid), ok = kill("USR1", OsPid), @@ -150,7 +150,7 @@ t_sigusr1(_Config) -> ok = kill("USR1", OsPid), %% ignore Pid2 = setup_service(), - erts_internal:set_signal(sigusr1, ignore), + os:set_signal(sigusr1, ignore), ok = kill("USR1", OsPid), ok = kill("USR1", OsPid), ok = kill("USR1", OsPid), @@ -158,13 +158,13 @@ t_sigusr1(_Config) -> io:format("Msgs2: ~p~n", [Msgs2]), [] = Msgs2, %% reset to ignore (it's the default) - erts_internal:set_signal(sigusr1, handle), + os:set_signal(sigusr1, handle), ok. t_sigusr2(_Config) -> Pid1 = setup_service(), OsPid = os:getpid(), - erts_internal:set_signal(sigusr2, handle), + os:set_signal(sigusr2, handle), ok = kill("USR2", OsPid), ok = kill("USR2", OsPid), ok = kill("USR2", OsPid), @@ -179,7 +179,7 @@ t_sigusr2(_Config) -> ok = kill("USR2", OsPid), %% ignore Pid2 = setup_service(), - erts_internal:set_signal(sigusr2, ignore), + os:set_signal(sigusr2, ignore), ok = kill("USR2", OsPid), ok = kill("USR2", OsPid), ok = kill("USR2", OsPid), @@ -187,13 +187,13 @@ t_sigusr2(_Config) -> io:format("Msgs2: ~p~n", [Msgs2]), [] = Msgs2, %% reset to ignore (it's the default) - erts_internal:set_signal(sigusr2, ignore), + os:set_signal(sigusr2, ignore), ok. t_sigterm(_Config) -> Pid1 = setup_service(), OsPid = os:getpid(), - erts_internal:set_signal(sigterm, handle), + os:set_signal(sigterm, handle), ok = kill("TERM", OsPid), ok = kill("TERM", OsPid), ok = kill("TERM", OsPid), @@ -208,7 +208,7 @@ t_sigterm(_Config) -> ok = kill("TERM", OsPid), %% ignore Pid2 = setup_service(), - erts_internal:set_signal(sigterm, ignore), + os:set_signal(sigterm, ignore), ok = kill("TERM", OsPid), ok = kill("TERM", OsPid), ok = kill("TERM", OsPid), @@ -216,13 +216,13 @@ t_sigterm(_Config) -> io:format("Msgs2: ~p~n", [Msgs2]), [] = Msgs2, %% reset to handle (it's the default) - erts_internal:set_signal(sigterm, handle), + os:set_signal(sigterm, handle), ok. t_sigchld(_Config) -> Pid1 = setup_service(), OsPid = os:getpid(), - erts_internal:set_signal(sigchld, handle), + os:set_signal(sigchld, handle), ok = kill("CHLD", OsPid), ok = kill("CHLD", OsPid), ok = kill("CHLD", OsPid), @@ -237,7 +237,7 @@ t_sigchld(_Config) -> ok = kill("CHLD", OsPid), %% ignore Pid2 = setup_service(), - erts_internal:set_signal(sigchld, ignore), + os:set_signal(sigchld, ignore), ok = kill("CHLD", OsPid), ok = kill("CHLD", OsPid), ok = kill("CHLD", OsPid), @@ -245,19 +245,19 @@ t_sigchld(_Config) -> io:format("Msgs2: ~p~n", [Msgs2]), [] = Msgs2, %% reset to handle (it's the default) - erts_internal:set_signal(sigchld, ignore), + os:set_signal(sigchld, ignore), ok. t_sigalrm(_Config) -> Pid1 = setup_service(), - true = erts_internal:set_signal(sigalrm, handle), + 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]), - erts_internal:set_signal(sigalrm, ignore), + os:set_signal(sigalrm, ignore), Pid2 = setup_service(), ok = os_signal_SUITE:set_alarm(1), receive after 3000 -> ok end, @@ -265,18 +265,18 @@ t_sigalrm(_Config) -> [] = Msgs2, io:format("Msgs2: ~p~n", [Msgs2]), Pid3 = setup_service(), - erts_internal:set_signal(sigalrm, handle), + 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]), - erts_internal:set_signal(sigalrm, ignore), + os:set_signal(sigalrm, ignore), ok. t_sigchld_fork(_Config) -> Pid1 = setup_service(), - true = erts_internal:set_signal(sigchld, handle), + ok = os:set_signal(sigchld, handle), {ok,OsPid} = os_signal_SUITE:fork(), receive after 3000 -> ok end, Msgs1 = fetch_msgs(Pid1), @@ -286,7 +286,7 @@ t_sigchld_fork(_Config) -> io:format("exit status from ~w : ~w~n", [OsPid,Status]), 42 = Status, %% reset to ignore (it's the default) - erts_internal:set_signal(sigchld, ignore), + os:set_signal(sigchld, ignore), ok. -- cgit v1.2.3