%%% @copyright 2012-2020 Michael Santos %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions %%% are met: %%% %%% 1. Redistributions of source code must retain the above copyright notice, %%% this list of conditions and the following disclaimer. %%% %%% 2. Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in the %%% documentation and/or other materials provided with the distribution. %%% %%% 3. Neither the name of the copyright holder nor the names of its %%% contributors may be used to endorse or promote products derived from %%% this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS %%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT %%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR %%% A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT %%% HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, %%% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED %%% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR %%% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF %%% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING %%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS %%% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -module(perc_signal). -behaviour(gen_server). -include_lib("perc/include/perc.hrl"). -include_lib("perc/include/perc_signal.hrl"). -export([ define/1, signalfd_siginfo/1 ]). -export([ start/1, stop/1, getfd/1, start_link/1 ]). -export([ init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3 ]). -record(state, { port, pid, fd }). %%-------------------------------------------------------------------- %%% Exports %%-------------------------------------------------------------------- start(Signals) when is_list(Signals) -> start_link(Signals). stop(Ref) when is_pid(Ref) -> gen_server:call(Ref, stop). getfd(Ref) when is_pid(Ref) -> gen_server:call(Ref, getfd). start_link(Signals) when is_list(Signals) -> case perc:sigaddset(Signals) of {ok, Mask} -> Pid = self(), gen_server:start_link(?MODULE, [Pid, Mask], []); Err -> Err end. %%-------------------------------------------------------------------- %%% Callbacks %%-------------------------------------------------------------------- init([Pid, Mask]) -> process_flag(trap_exit, true), case perc:signalfd(Mask) of {ok, FD} -> Port = erlang:open_port({fd, FD, FD}, [stream, binary]), {ok, #state{ port = Port, pid = Pid, fd = FD }}; Err -> Err end. handle_call(getfd, _From, #state{fd = FD} = State) -> {reply, FD, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. %% Signal received handle_info({Port, {data, Data}}, #state{port = Port, pid = Pid} = State) -> _ = [ Pid ! {signal, self(), signalfd_siginfo(Signal)} || <> <= Data ], {noreply, State}; % WTF? handle_info(Info, State) -> error_logger:error_report([wtf, Info]), {noreply, State}. terminate(_Reason, #state{fd = FD, port = Port}) -> catch erlang:port_close(Port), _ = perc:close(FD), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Utility functions %%-------------------------------------------------------------------- % Signal definitions: linux define(sighup) -> 1; define(sigint) -> 2; define(sigquit) -> 3; define(sigill) -> 4; define(sigtrap) -> 5; define(sigabrt) -> 6; define(sigbus) -> 7; define(sigfpe) -> 8; define(sigkill) -> 9; define(sigusr1) -> 10; define(sigsegv) -> 11; define(sigusr2) -> 12; define(sigpipe) -> 13; define(sigalrm) -> 14; define(sigterm) -> 15; define(sigstkflt) -> 16; define(sigchld) -> 17; define(sigcont) -> 18; define(sigstop) -> 19; define(sigtstp) -> 20; define(sigttin) -> 21; define(sigttou) -> 22; define(sigurg) -> 23; define(sigxcpu) -> 24; define(sigxfsz) -> 25; define(sigvtalrm) -> 26; define(sigprof) -> 27; define(sigwinch) -> 28; define(sigio) -> 29; define(sigpwr) -> 30; define(sigsys) -> 31; define(sigrtmin) -> 34; define(sigrtmax) -> 64. signalfd_siginfo( << % Signal number ?UINT32(Signo), % Error number (unused) ?INT32(Errno), % Signal code ?INT32(Code), % PID of sender ?UINT32(Pid), % Real UID of sender ?UINT32(Uid), % File descriptor (SIGIO) ?INT32(Fd), % Kernel timer ID (POSIX timers) ?UINT32(Tid), % Band event (SIGIO) ?UINT32(Band), % POSIX timer overrun count ?UINT32(Overrun), % Trap number that caused signal ?UINT32(Trapno), % Exit status or signal (SIGCHLD) ?INT32(Status), % Integer sent by sigqueue(2) ?INT32(Int), % Pointer sent by sigqueue(2) ?UINT64(Ptr), % User CPU time consumed (SIGCHLD) ?UINT64(Utime), % System CPU time consumed (SIGCHLD) ?UINT64(Stime), % Address that generated signal (for hardware-generated signals) ?UINT64(Addr), % Pad size to 128 bytes (allow for additional fields in the future) Pad/binary >> = Data ) when byte_size(Data) =:= 128 -> #signalfd_siginfo{ ssi_signo = Signo, ssi_errno = Errno, ssi_code = Code, ssi_pid = Pid, ssi_uid = Uid, ssi_fd = Fd, ssi_tid = Tid, ssi_band = Band, ssi_overrun = Overrun, ssi_trapno = Trapno, ssi_status = Status, ssi_int = Int, ssi_ptr = Ptr, ssi_utime = Utime, ssi_stime = Stime, ssi_addr = Addr, pad = Pad }; signalfd_siginfo(#signalfd_siginfo{ ssi_signo = Signo, ssi_errno = Errno, ssi_code = Code, ssi_pid = Pid, ssi_uid = Uid, ssi_fd = Fd, ssi_tid = Tid, ssi_band = Band, ssi_overrun = Overrun, ssi_trapno = Trapno, ssi_status = Status, ssi_int = Int, ssi_ptr = Ptr, ssi_utime = Utime, ssi_stime = Stime, ssi_addr = Addr, pad = Pad }) -> << % Signal number ?UINT32(Signo), % Error number (unused) ?INT32(Errno), % Signal code ?INT32(Code), % PID of sender ?UINT32(Pid), % Real UID of sender ?UINT32(Uid), % File descriptor (SIGIO) ?INT32(Fd), % Kernel timer ID (POSIX timers) ?UINT32(Tid), % Band event (SIGIO) ?UINT32(Band), % POSIX timer overrun count ?UINT32(Overrun), % Trap number that caused signal ?UINT32(Trapno), % Exit status or signal (SIGCHLD) ?INT32(Status), % Integer sent by sigqueue(2) ?INT32(Int), % Pointer sent by sigqueue(2) ?UINT64(Ptr), % User CPU time consumed (SIGCHLD) ?UINT64(Utime), % System CPU time consumed (SIGCHLD) ?UINT64(Stime), % Address that generated signal (for hardware-generated signals) ?UINT64(Addr), % Pad size to 128 bytes (allow for additional fields in the future) Pad/binary >>. %%-------------------------------------------------------------------- %%% Internal functions %%--------------------------------------------------------------------