├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── c_src └── sighandler_drv.c ├── include └── sighandler.hrl ├── rebar.config ├── src ├── sighandler.app.src ├── sighandler.erl ├── sighandler_app.erl ├── sighandler_drv.erl ├── sighandler_nif_tests.erl ├── sighandler_server.erl └── sighandler_sup.erl ├── support └── gen_nif.escript └── test.config /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | ebin 4 | .eunit 5 | .DS_Store 6 | c_src/sighandler_nif.c 7 | src/sighandler_nif.erl 8 | *~ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR:=$(shell which rebar || echo ./rebar) 2 | REBAR_URL:="https://github.com/downloads/basho/rebar/rebar" 3 | 4 | .PHONY: all doc clean test 5 | 6 | all: deps compile 7 | 8 | $(REBAR): 9 | @echo "No rebar was found so a copy will be downloaded in 5 seconds." 10 | @echo "Source: ${REBAR_URL} Destination: ${REBAR}" 11 | @sleep 5 12 | @echo "Commencing download... " 13 | @erl -noshell -eval "\ 14 | [ application:start(X) || X <- [crypto,public_key,ssl,inets]],\ 15 | Request = {\"${REBAR_URL}\", []},\ 16 | HttpOpts = [],\ 17 | Opts = [{stream, \"$(REBAR)\"}],\ 18 | Result = httpc:request(get, Request, HttpOpts, Opts),\ 19 | Status = case Result of {ok, _} -> 0; _ -> 1 end,\ 20 | init:stop(Status)." 21 | @chmod u+x ./rebar 22 | @echo "ok" 23 | 24 | compile: $(REBAR) 25 | @$(REBAR) compile 26 | 27 | deps: $(REBAR) 28 | @$(REBAR) get-deps 29 | 30 | doc: $(REBAR) 31 | @$(REBAR) doc skip_deps=true 32 | 33 | clean: $(REBAR) 34 | @$(REBAR) clean 35 | @rm -fr doc/* 36 | 37 | test: $(REBAR) all 38 | @$(REBAR) eunit skip_deps=true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #sighandler 2 | 3 | **License: Apache 2** 4 | 5 | 6 | 1> application:start(sighandler). 7 | ok 8 | 2> {ok, Ref} = sighandler:install(hup, fun() -> io:format("HUP!~n") end). 9 | {ok,#Ref<0.0.0.66>} 10 | 3> os:cmd(io_lib:format("kill -HUP ~s", [os:getpid()])). 11 | HUP! 12 | [] 13 | 4> ok = sighandler:remove(Ref). 14 | ok 15 | 3> os:cmd(io_lib:format("kill -HUP ~s", [os:getpid()])). 16 | Hangup: 1 17 | 18 | Should work with Erlang R13B04 or later on UNIX like platforms. Has been known to work with R13B04 and R15B on Darwin and with R14B04 on Linux. 19 | 20 | To add a signal handler using configuration instead of programmatically, first create a config file 21 | 22 | #hello.config 23 | [ 24 | {sighandler, [ 25 | {handlers, [ 26 | {hup, {io, fwrite, ["Hello, world!~n"]}} 27 | ]} 28 | ]} 29 | ]. 30 | 31 | Then start erlang: 32 | 33 | erl -pa ebin/ -config $CONFIGDIR/hello -s sighandler 34 | 35 | Then the handler will automatically be registered. -------------------------------------------------------------------------------- /c_src/sighandler_drv.c: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------- 2 | 3 | Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | 5 | This file is provided to you under the Apache License, 6 | Version 2.0 (the "License"); you may not use this file 7 | except in compliance with the License. You may obtain 8 | a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | -------------------------------------------------------------------- */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #if ERL_DRV_EXTENDED_MAJOR_VERSION < 2 29 | #define ErlDrvSizeT int 30 | #define ErlDrvSSizeT int 31 | #endif 32 | 33 | #if ERL_DRV_EXTENDED_MAJOR_VERSION > 2 || \ 34 | (ERL_DRV_EXTENDED_MAJOR_VERSION == 2 && ERL_DRV_EXTENDED_MINOR_VERSION >= 1) 35 | #define USE_ERL_DRV_OUTPUT_TERM 36 | #endif 37 | 38 | static int sh_pipe[2]; 39 | static void* sh_handlers_old[45]; 40 | static int sh_handlers_installed[45]; 41 | 42 | typedef struct _sh_drv_t { 43 | ErlDrvPort erl_port; 44 | ErlDrvTermData term_port; 45 | ErlDrvTermData term_installed; 46 | ErlDrvTermData term_removed; 47 | } sh_drv_t; 48 | 49 | static int sh_init(void); 50 | static ErlDrvData sh_start(ErlDrvPort port, char* cmd); 51 | static void sh_stop(ErlDrvData handle); 52 | static void sh_io(ErlDrvData port, ErlDrvEvent ev); 53 | static void sh_finish(void); 54 | static ErlDrvSSizeT sh_call(ErlDrvData drv_data, 55 | unsigned int sig, 56 | char *buf, 57 | ErlDrvSizeT len, 58 | char **rbuf, 59 | ErlDrvSizeT rlen, 60 | unsigned int *flags); 61 | static int sh_handler_install(int no); 62 | static int sh_handler_remove(int no); 63 | static void sh_handler(int sig); 64 | 65 | static ErlDrvEntry sh_drv_entry = { 66 | sh_init, /* init */ 67 | sh_start, /* startup */ 68 | sh_stop, /* shutdown */ 69 | NULL, /* output */ 70 | sh_io, /* ready_input */ 71 | NULL, /* ready_output */ 72 | "sighandler_drv", /* the name of the driver */ 73 | sh_finish, /* finish */ 74 | NULL, /* handle */ 75 | NULL, /* control */ 76 | NULL, /* timeout */ 77 | NULL, /* process */ 78 | NULL, /* ready_async */ 79 | NULL, /* flush */ 80 | sh_call, /* call */ 81 | NULL, /* event */ 82 | ERL_DRV_EXTENDED_MARKER, /* ERL_DRV_EXTENDED_MARKER */ 83 | ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MAJOR_VERSION */ 84 | ERL_DRV_EXTENDED_MINOR_VERSION, /* ERL_DRV_EXTENDED_MINOR_VERSION */ 85 | 0 /* ERL_DRV_FLAGs */ 86 | }; 87 | 88 | DRIVER_INIT(sighandler_drv) { 89 | return &sh_drv_entry; 90 | } 91 | 92 | static int sh_init(void) { 93 | if (sh_pipe[0]) return -1; 94 | if (pipe(sh_pipe) != 0) return -1; 95 | 96 | return 0; 97 | } 98 | 99 | static ErlDrvData sh_start(ErlDrvPort port, char* buf) { 100 | sh_drv_t* retval = (sh_drv_t*) driver_alloc(sizeof(sh_drv_t)); 101 | retval->erl_port = port; 102 | retval->term_port = driver_mk_port(port); 103 | driver_select(port, (ErlDrvEvent)(size_t) sh_pipe[0], DO_READ, 1); 104 | return (ErlDrvData) retval; 105 | } 106 | 107 | static void sh_stop(ErlDrvData edd) { 108 | sh_drv_t* dd = (sh_drv_t*) edd; 109 | int i; 110 | driver_select(dd->erl_port, (ErlDrvEvent)(size_t) sh_pipe[0], DO_READ, 0); 111 | driver_free(dd); 112 | for (i = 0; i < (46); i++) { 113 | if (sh_handlers_installed[i]) sh_handler_remove(i); 114 | } 115 | } 116 | 117 | static void sh_io(ErlDrvData edd, ErlDrvEvent ev) { 118 | sh_drv_t* dd = (sh_drv_t*) edd; 119 | int sig = 0; 120 | if ((read(sh_pipe[0], &sig, sizeof(sig)) == 1)) { 121 | ErlDrvTermData spec[] = {ERL_DRV_PORT, dd->term_port, 122 | ERL_DRV_INT, (ErlDrvTermData) sig, 123 | ERL_DRV_TUPLE, 2}; 124 | #ifdef USE_ERL_DRV_OUTPUT_TERM 125 | erl_drv_output_term(driver_mk_port(dd->erl_port), spec, sizeof(spec) / sizeof(spec[0])); 126 | #else 127 | driver_output_term(dd->erl_port, spec, sizeof(spec) / sizeof(spec[0])); 128 | #endif 129 | } 130 | } 131 | 132 | static void sh_finish(void) { 133 | close(sh_pipe[0]); 134 | close(sh_pipe[1]); 135 | } 136 | 137 | static ErlDrvSSizeT sh_call(ErlDrvData drv_data, 138 | unsigned int sig, 139 | char *buf, 140 | ErlDrvSizeT len, 141 | char **rbuf, 142 | ErlDrvSizeT rlen, 143 | unsigned int *flags) { 144 | ei_term arg; 145 | int version; 146 | int index = 0; 147 | int rindex = 0; 148 | char* atom_txt = "sig_err"; 149 | if (sig < 1 || sig > 45) goto respond; 150 | ei_decode_version(buf, &index, &version); 151 | ei_decode_ei_term(buf, &index, &arg); 152 | if (arg.ei_type != ERL_ATOM_EXT) return -1; 153 | if (strncmp(arg.value.atom_name, "toggle", 6) == 0) { 154 | if (sh_handlers_installed[sig]) { 155 | if (sh_handler_remove(sig)) atom_txt = "removed"; 156 | } else { 157 | if (sh_handler_install(sig)) atom_txt = "installed"; 158 | } 159 | } else if (strncmp(arg.value.atom_name, "status", 6) == 0) { 160 | atom_txt = sh_handlers_installed[sig] ? "active" : "inactive"; 161 | } else { 162 | return -1; 163 | } 164 | respond: 165 | ei_encode_version(NULL, &rindex); 166 | ei_encode_atom(NULL, &rindex, atom_txt); 167 | if (rindex < rlen) { 168 | *rbuf = driver_alloc(rindex); 169 | } 170 | rindex = 0; 171 | ei_encode_version(*rbuf, &rindex); 172 | ei_encode_atom(*rbuf, &rindex, atom_txt); 173 | return rlen; 174 | } 175 | 176 | static int sh_handler_install(int no) { 177 | if ((sh_handlers_old[no] = signal(no, sh_handler)) == SIG_ERR) { 178 | return 0; 179 | } 180 | sh_handlers_installed[no] = 1; 181 | return 1; 182 | } 183 | 184 | static int sh_handler_remove(int no) { 185 | signal(no, sh_handlers_old[no]); 186 | sh_handlers_old[no] = NULL; 187 | sh_handlers_installed[no] = 0; 188 | return 1; 189 | } 190 | 191 | static void sh_handler(int no) { 192 | write(sh_pipe[1], &no, 1); 193 | } 194 | -------------------------------------------------------------------------------- /include/sighandler.hrl: -------------------------------------------------------------------------------- 1 | -ifndef('__sighandler.hrl__'). 2 | -define('__sighandler.hrl__', ok). 3 | 4 | -ifdef(TEST). 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -endif. 7 | 8 | -define(SH_IS_SIG(S), (is_integer(S))). 9 | -define(SH_IS_FUN(F), (is_function(F, 0) orelse is_function(F, 1))). 10 | -define(SH_IS_FUNREF(F), (?SH_IS_FUN(F) orelse is_reference(F))). 11 | -define(SH_IS_SIG_FUN(S,F), (?SH_IS_SIG(S) andalso ?SH_IS_FUN(F))). 12 | -define(SH_IS_SIG_FUNREF(S,F), (?SH_IS_SIG(S) andalso ?SH_IS_FUNREF(F))). 13 | 14 | -endif. 15 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {cover_enabled, true}. 2 | {cover_print_enabled, true}. 3 | {erl_opts, [debug_info, {platform_define, "(R14|R15)", 'warnings_as_errors'}]}. 4 | {port_specs, [{"priv/sighandler_drv.so", ["c_src/sighandler_drv.c"]}, 5 | {"priv/sighandler_nif.so", ["c_src/sighandler_nif.c"]}]}. 6 | {pre_hooks, [{compile, "$PWD/support/gen_nif.escript $PWD"}]}. 7 | {clean_files, ["*/sighandler_nif.*"]}. 8 | {eunit_opts, [verbose]}. 9 | -------------------------------------------------------------------------------- /src/sighandler.app.src: -------------------------------------------------------------------------------- 1 | {application, sighandler, 2 | [ 3 | {description, "Handle UNIX signals in Erlang"}, 4 | {vsn, "0.1"}, 5 | {registered, []}, 6 | {applications, [kernel, stdlib]}, 7 | {mod, { sighandler_app, []}}, 8 | {env, []} 9 | ]}. 10 | -------------------------------------------------------------------------------- /src/sighandler.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @private 21 | -module(sighandler). 22 | -include("sighandler.hrl"). 23 | 24 | -export([start/0, stop/0, install/2, remove/1, remove/2, int/1, atom/1]). 25 | 26 | start() -> application:start(sighandler). 27 | stop() -> application:stop(sighandler). 28 | 29 | install(Sig, Fun) when is_atom(Sig) andalso ?SH_IS_FUN(Fun) -> 30 | case int(Sig) of 31 | {ok, SigInt} -> install(SigInt, Fun); 32 | {error, _Reason} = Error -> Error 33 | end; 34 | install(Sig, Fun) when ?SH_IS_SIG_FUN(Sig, Fun) -> 35 | sighandler_server:install(Sig, Fun). 36 | 37 | remove(FunRef) when ?SH_IS_FUNREF(FunRef) -> 38 | sighandler_server:remove(FunRef). 39 | 40 | remove(Sig, FunRef) when is_atom(Sig) andalso ?SH_IS_FUNREF(FunRef) -> 41 | case int(Sig) of 42 | {ok, SigInt} -> remove(SigInt, FunRef); 43 | {error, _Reason} = Error -> Error 44 | end; 45 | remove(Sig, FunRef) when ?SH_IS_SIG_FUNREF(Sig, FunRef) -> 46 | sighandler_server:remove(Sig, FunRef). 47 | 48 | int(Atom) when is_atom(Atom) -> sighandler_server:lookup_term(Atom). 49 | 50 | atom(Int) when is_integer(Int) -> sighandler_server:lookup_term(Int). 51 | -------------------------------------------------------------------------------- /src/sighandler_app.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @private 21 | -module(sighandler_app). 22 | -behaviour(application). 23 | -include("sighandler.hrl"). 24 | 25 | %% Application callbacks 26 | -export([start/2, stop/1]). 27 | 28 | %% =================================================================== 29 | %% Application callbacks 30 | %% =================================================================== 31 | 32 | start(_StartType, _StartArgs) -> 33 | case sighandler_sup:start_link() of 34 | {ok, Pid} -> 35 | setup(), 36 | {ok,Pid}; 37 | Error -> 38 | Error 39 | end. 40 | 41 | setup()-> 42 | case application:get_env(sighandler, handlers) of 43 | {ok, Registry} -> 44 | lists:foreach(fun({Signal,{Module,Function,Args}}) -> 45 | sighandler:install(Signal,fun()->apply(Module,Function,Args) end) 46 | end, Registry); 47 | undefined -> 48 | ok; 49 | Reason -> 50 | error_logger:error_msg("Error processing config: ~p~n", [Reason]) 51 | end. 52 | 53 | stop(_State) -> ok. 54 | 55 | -ifdef(TEST). 56 | 57 | app_test_() -> 58 | [?_assertEqual(ok, sighandler:start()), 59 | ?_assertEqual(ok, sighandler:stop())]. 60 | 61 | -endif. 62 | -------------------------------------------------------------------------------- /src/sighandler_drv.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @private 21 | -module(sighandler_drv). 22 | -include("sighandler.hrl"). 23 | 24 | -export([open/0, close/1, status/2, toggle/2]). 25 | 26 | -define(DRIVER_NAME, ?MODULE_STRING). 27 | 28 | open() -> 29 | ok = load(), 30 | erlang:open_port({spawn_driver, ?DRIVER_NAME}, [binary]). 31 | 32 | close(Port) when is_port(Port) -> 33 | case erlang:port_info(Port) of 34 | undefined -> ok; 35 | _ -> 36 | true = erlang:port_close(Port), 37 | ok = unload() 38 | end. 39 | 40 | status(Port, Sig) when is_port(Port) andalso ?SH_IS_SIG(Sig) -> 41 | erlang:port_call(Port, Sig, status). 42 | 43 | toggle(Port, Sig) when is_port(Port) andalso ?SH_IS_SIG(Sig) -> 44 | erlang:port_call(Port, Sig, toggle). 45 | 46 | %%%=================================================================== 47 | %%% Internal functions 48 | %%%=================================================================== 49 | 50 | load() -> 51 | {ok, Drivers} = erl_ddll:loaded_drivers(), 52 | case lists:member(?DRIVER_NAME, Drivers) of 53 | true -> ok; 54 | false -> 55 | case erl_ddll:load(priv_dir(), ?DRIVER_NAME) of 56 | ok -> ok; 57 | {error, Reason} = Error -> 58 | error_logger:error_msg( 59 | ?MODULE_STRING ": Error loading ~p: ~p~n", 60 | [?DRIVER_NAME, erl_ddll:format_error(Reason)] 61 | ), 62 | Error 63 | end 64 | end. 65 | 66 | unload() -> 67 | case erl_ddll:unload_driver(?DRIVER_NAME) of 68 | ok -> ok; 69 | {error, Reason} = Error -> 70 | error_logger:error_msg( 71 | ?MODULE_STRING ": Error unloading ~p: ~p~n", 72 | [?DRIVER_NAME, erl_ddll:format_error(Reason)] 73 | ), 74 | Error 75 | end. 76 | 77 | 78 | priv_dir() -> 79 | case code:priv_dir(sighandler) of 80 | List when is_list(List) -> List; 81 | {error, bad_name} -> 82 | filename:join(filename:dirname(code:which(?MODULE)), "../priv") 83 | end. 84 | 85 | %%%=================================================================== 86 | %%% Tests 87 | %%%=================================================================== 88 | 89 | -ifdef(TEST). 90 | 91 | drv_test_() -> 92 | [?_assertEqual(ok, load()), 93 | ?_assertEqual(ok, unload())]. 94 | 95 | -endif. 96 | -------------------------------------------------------------------------------- /src/sighandler_nif_tests.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @private 21 | -module(sighandler_nif_tests). 22 | -include("sighandler.hrl"). 23 | 24 | -ifdef(TEST). 25 | 26 | simple_test_() -> 27 | [ ?_assert(begin 28 | Result = sighandler_nif:X(), 29 | is_integer(Result) orelse Result =:= undefined 30 | end) 31 | || {X, 0} <- sighandler_nif:module_info(exports), 32 | X =/= on_load, X =/= module_info ]. 33 | 34 | -endif. 35 | -------------------------------------------------------------------------------- /src/sighandler_server.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @private 21 | -module(sighandler_server). 22 | -behaviour(gen_server). 23 | -include("sighandler.hrl"). 24 | 25 | %% API 26 | -export([start_link/0, stop/0, install/2, remove/1, remove/2, lookup_term/1, registered/0]). 27 | 28 | %% gen_server callbacks 29 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 30 | terminate/2, code_change/3]). 31 | 32 | -record(state, {port :: port(), handlers = dict:new()}). 33 | 34 | -define(SERVER, ?MODULE). 35 | -define(TAB_TERM, sighandler_term). 36 | 37 | %%%=================================================================== 38 | %%% API 39 | %%%=================================================================== 40 | 41 | install(Sig, Fun) when ?SH_IS_SIG_FUN(Sig, Fun) -> 42 | gen_server:call(?SERVER, {install, Sig, Fun}). 43 | 44 | remove(FunRef) when ?SH_IS_FUNREF(FunRef) -> 45 | gen_server:call(?SERVER, {remove, FunRef}). 46 | 47 | remove(Sig, FunRef) when ?SH_IS_SIG_FUNREF(Sig, FunRef) -> 48 | gen_server:call(?SERVER, {remove, Sig, FunRef}). 49 | 50 | lookup_term(Term) when is_atom(Term) orelse is_integer(Term) -> 51 | case (catch ets:lookup_element(?TAB_TERM, Term, 2)) of 52 | {'EXIT', _} -> {error, undefined}; 53 | Other -> {ok, Other} 54 | end. 55 | 56 | start_link() -> 57 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 58 | 59 | stop() -> gen_server:call(?SERVER, stop). 60 | 61 | registered()-> 62 | State=gen_server:call(?SERVER,registered), 63 | State#state.handlers. 64 | 65 | %%%=================================================================== 66 | %%% gen_server callbacks 67 | %%%=================================================================== 68 | 69 | init([]) -> 70 | ok = setup_sig_tab(), 71 | Port = sighandler_drv:open(), 72 | {ok, #state{port = Port}}. 73 | 74 | handle_call({install, Sig, Fun}, _From, #state{} = State) -> 75 | Ref = make_ref(), 76 | {Result, State0} = install(Sig, Fun, Ref, State), 77 | Reply = case Result of 78 | ok -> {ok, Ref}; 79 | {error, _Reason} = Error -> Error 80 | end, 81 | {reply, Reply, State0}; 82 | handle_call({remove, FunRef}, _From, #state{} = State) -> 83 | {ok, State0} = find_and_remove(FunRef, State), 84 | {reply, ok, State0}; 85 | handle_call({remove, Sig, Fun}, _From, #state{} = State) -> 86 | {ok, State0} = remove(Sig, Fun, State), 87 | {reply, ok, State0}; 88 | handle_call(stop, _From, State) -> {stop, normal, ok, State}; 89 | handle_call(registered,_From,State)-> 90 | {reply, State,State}; 91 | handle_call(Request, _From, State) -> 92 | error_logger:info_msg(?MODULE_STRING " ~p ignoring call: ~p~n", 93 | [self(), Request]), 94 | {noreply, State}. 95 | 96 | handle_cast(Msg, State) -> 97 | error_logger:info_msg(?MODULE_STRING " ~p ignoring cast: ~p~n", 98 | [self(), Msg]), 99 | {noreply, State}. 100 | 101 | handle_info({Port, Sig}, #state{port = Port} = State) 102 | when is_integer(Sig) -> 103 | {ok, State0} = fire(Sig, State), 104 | {noreply, State0}; 105 | handle_info(Info, State) -> 106 | error_logger:info_msg(?MODULE_STRING " ~p discarded message: ~p~n", 107 | [self(), Info]), 108 | {noreply, State}. 109 | 110 | terminate(_Reason, #state{port = Port} = _State) -> 111 | ok = sighandler_drv:close(Port). 112 | 113 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 114 | 115 | %%%=================================================================== 116 | %%% Internal functions 117 | %%%=================================================================== 118 | 119 | %% sig_tab 120 | 121 | setup_sig_tab() -> 122 | ?TAB_TERM = ets:new(?TAB_TERM, [named_table, protected]), 123 | Fun = fun({Atom, 0}) when Atom =/= on_load andalso Atom =/= module_info -> 124 | case sighandler_nif:Atom() of 125 | Int when is_integer(Int) -> 126 | Terms = [{Int, Atom}, {Atom, Int}], 127 | true = ets:insert(?TAB_TERM, Terms); 128 | undefined -> ok 129 | end; 130 | (_) -> ok end, 131 | lists:foreach(Fun, sighandler_nif:module_info(exports)). 132 | 133 | %% State management 134 | install(Sig, Fun, Ref, #state{handlers = Handlers, port = Port} = State) -> 135 | Installed = case sighandler_drv:status(Port, Sig) of 136 | active -> installed; 137 | inactive -> sighandler_drv:toggle(Port, Sig); 138 | Other -> Other 139 | end, 140 | case Installed of 141 | installed -> 142 | Handlers0 = dict:append(Sig, {Ref, Fun}, Handlers), 143 | {ok, State#state{handlers = Handlers0}}; 144 | sig_err = Err -> {{error, Err}, State} 145 | end. 146 | 147 | remove(Sig, FunRef, #state{handlers = Handlers, port = Port} = State) -> 148 | case dict:is_key(Sig, Handlers) of 149 | true -> 150 | Funs = dict:fetch(Sig, Handlers), 151 | case lists:keydelete(FunRef, remove_pos(FunRef), Funs) of 152 | [] -> 153 | Handlers0 = dict:erase(Sig, Handlers), 154 | removed = sighandler_drv:toggle(Port, Sig), 155 | {ok, State#state{handlers = Handlers0}}; 156 | Funs -> {ok, State}; 157 | Funs0 -> 158 | Handlers0 = dict:store(Sig, Funs0, Handlers), 159 | {ok, State#state{handlers = Handlers0}} 160 | end; 161 | false -> {ok, State} 162 | end. 163 | 164 | find_and_remove(FunRef, #state{handlers = Handlers} = State) -> 165 | Pos = remove_pos(FunRef), 166 | Sigs = dict:fold(fun(Sig, Funs, Acc) -> 167 | case lists:keymember(FunRef, Pos, Funs) of 168 | true -> [Sig|Acc]; 169 | false -> Acc 170 | end 171 | end, [], Handlers), 172 | find_and_remove(Sigs, FunRef, State). 173 | 174 | find_and_remove([Sig|Sigs], FunRef, State) -> 175 | {ok, State0} = remove(Sig, FunRef, State), 176 | find_and_remove(Sigs, FunRef, State0); 177 | find_and_remove([], _FunRef, State) -> {ok, State}. 178 | 179 | remove_pos(Ref) when is_reference(Ref) -> 1; 180 | remove_pos(Fun) when is_function(Fun) -> 2. 181 | 182 | fire(Sig, #state{handlers = Handlers} = State) -> 183 | case dict:is_key(Sig, Handlers) of 184 | true -> 185 | Funs = dict:fetch(Sig, Handlers), 186 | fire(Funs, Sig, State); 187 | false -> {ok, State} 188 | end. 189 | 190 | fire([{Ref, Fun}|Funs], Sig, #state{} = State) -> 191 | Run = fun(F) when is_function(F, 0) -> catch F(); 192 | (F) when is_function(F, 1) -> catch F(Sig) end, 193 | case Run(Fun) of 194 | ok -> fire(Funs, Sig, State); 195 | _ -> 196 | {ok, State0} = remove(Sig, Ref, State), 197 | fire(Funs, Sig, State0) 198 | end; 199 | fire([], _Sig, #state{} = State) -> {ok, State}. 200 | 201 | %% tests 202 | 203 | -ifdef(TEST). 204 | 205 | fire_test() -> 206 | ok = application:start(sighandler), 207 | Sig = 1, 208 | Tab = ets:new(sh_test, [public]), 209 | Fun = fun(X) -> 210 | io:format("fire_test ~p~n", [X]), 211 | ets:insert(Tab, {X}), 212 | ok 213 | end, 214 | {ok, Ref} = sighandler:install(Sig, Fun), 215 | _ = os:cmd(io_lib:format("kill -~p ~s", [Sig, os:getpid()])), 216 | ok = sighandler:remove(Sig, Ref), 217 | Result = (catch ets:lookup(Tab, Sig)), 218 | ets:delete(Tab), 219 | ok = application:stop(sighandler), 220 | ?assertEqual([{Sig}], Result). 221 | 222 | fire_badfun_test() -> 223 | ok = application:start(sighandler), 224 | Sig = 1, 225 | Cmd = io_lib:format("kill -~p ~s", [Sig, os:getpid()]), 226 | Tab = ets:new(sh_test, [public]), 227 | true = ets:insert(Tab, {Sig, 0}), 228 | FunA = fun(X) -> 229 | io:format("fire_badfun_test a ~p~n", [X]), 230 | ets:update_counter(Tab, X, 1), 231 | ok 232 | end, 233 | {ok, Ref0} = sighandler:install(Sig, FunA), 234 | FunB = fun(X) -> 235 | io:format("fire_badfun_test b ~p~n", [X]), 236 | ets:update_counter(Tab, X, 1), 237 | throw(bad) 238 | end, 239 | {ok, Ref1} = sighandler:install(Sig, FunB), 240 | _ = os:cmd(Cmd), 241 | _ = os:cmd(Cmd), 242 | ok = sighandler:remove(Sig, Ref0), 243 | ok = sighandler:remove(Sig, Ref1), 244 | Result = (catch ets:lookup(Tab, Sig)), 245 | ets:delete(Tab), 246 | ok = application:stop(sighandler), 247 | ?assertEqual([{Sig, 3}], Result). 248 | 249 | find_and_remove_ref_test() -> 250 | ok = application:start(sighandler), 251 | Sig = 1, 252 | Cmd = io_lib:format("kill -~p ~s", [Sig, os:getpid()]), 253 | Tab = ets:new(sh_test, [public]), 254 | true = ets:insert(Tab, {Sig, 0}), 255 | Fun = fun(X) -> 256 | io:format("find_and_remove_ref_test ~p~n", [X]), 257 | ets:update_counter(Tab, X, 1), 258 | ok 259 | end, 260 | {ok, Ref0} = sighandler:install(Sig, Fun), 261 | {ok, Ref1} = sighandler:install(Sig, Fun), 262 | ok = sighandler:remove(Sig, Ref0), 263 | _ = os:cmd(Cmd), 264 | _ = os:cmd(Cmd), 265 | ok = sighandler:remove(Ref1), 266 | Result = (catch ets:lookup(Tab, Sig)), 267 | ets:delete(Tab), 268 | ok = application:stop(sighandler), 269 | ?assertEqual([{Sig, 2}], Result). 270 | 271 | find_and_remove_fun_test() -> 272 | ok = application:start(sighandler), 273 | Sig = 1, 274 | Cmd = io_lib:format("kill -~p ~s", [Sig, os:getpid()]), 275 | Tab = ets:new(sh_test, [public]), 276 | true = ets:insert(Tab, {Sig, 0}), 277 | Fun0 = fun(X) -> 278 | io:format("find_and_remove_fun_test a ~p~n", [X]), 279 | ets:update_counter(Tab, X, 1), 280 | ok 281 | end, 282 | Fun1 = fun(X) -> 283 | io:format("find_and_remove_fun_test b ~p~n", [X]), 284 | ets:update_counter(Tab, X, 1), 285 | ok 286 | end, 287 | {ok, Ref0} = sighandler:install(Sig, Fun0), 288 | {ok, _Ref1} = sighandler:install(Sig, Fun1), 289 | _ = os:cmd(Cmd), 290 | ok = sighandler:remove(Fun1), 291 | _ = os:cmd(Cmd), 292 | ok = sighandler:remove(Sig, Ref0), 293 | Result = (catch ets:lookup(Tab, Sig)), 294 | ets:delete(Tab), 295 | ok = application:stop(sighandler), 296 | ?assertEqual([{Sig, 3}], Result). 297 | 298 | -endif. 299 | -------------------------------------------------------------------------------- /src/sighandler_sup.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @private 21 | -module(sighandler_sup). 22 | -behaviour(supervisor). 23 | 24 | %% API 25 | -export([start_link/0]). 26 | 27 | %% Supervisor callbacks 28 | -export([init/1]). 29 | 30 | %% =================================================================== 31 | %% API functions 32 | %% =================================================================== 33 | 34 | start_link() -> 35 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 36 | 37 | %% =================================================================== 38 | %% Supervisor callbacks 39 | %% =================================================================== 40 | 41 | init([]) -> 42 | Child = {sighandler_server, 43 | {sighandler_server, start_link, []}, 44 | permanent, 60, worker, [sighandler_server]}, 45 | {ok, { {one_for_one, 5, 10}, [Child]} }. 46 | -------------------------------------------------------------------------------- /support/gen_nif.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %% ------------------------------------------------------------------- 3 | %% 4 | %% Copyright (c) 2012 Andrew Tunnell-Jones. All Rights Reserved. 5 | %% 6 | %% This file is provided to you under the Apache License, 7 | %% Version 2.0 (the "License"); you may not use this file 8 | %% except in compliance with the License. You may obtain 9 | %% a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, 14 | %% software distributed under the License is distributed on an 15 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | %% KIND, either express or implied. See the License for the 17 | %% specific language governing permissions and limitations 18 | %% under the License. 19 | %% 20 | %% ------------------------------------------------------------------- 21 | 22 | -define(SIGS, 23 | ["SIGABRT", "SIGALRM", "SIGBUS", "SIGCANCEL", "SIGCHLD", "SIGCONT", 24 | "SIGEMT", "SIGFPE", "SIGFREEZE", "SIGHUP", "SIGILL", "SIGINFO", 25 | "SIGINT", "SIGIO", "SIGIOT", "SIGKILL", "SIGLOST", "SIGLWP", "SIGPIPE", 26 | "SIGPOLL", "SIGPROF", "SIGPWR", "SIGQUIT", "SIGRTMAX", "SIGRTMIN", 27 | "SIGSEGV", "SIGSTKSZ", "SIGSTOP", "SIGSYS", "SIGTERM", "SIGTHAW", 28 | "SIGTRAP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGUSR1", 29 | "SIGUSR2", "SIGVTALRM", "SIGWAITING", "SIGWINCH", "SIGXCPU", 30 | "SIGXFSZ"] 31 | ). 32 | 33 | -define(ERL_HEAD, 34 | "-module(sighandler_nif). 35 | -compile([export_all]). 36 | -on_load(on_load/0). 37 | on_load() -> 38 | So = case code:priv_dir(sighandler) of 39 | {error, bad_name} -> 40 | ModPath = filename:dirname(code:which(?MODULE)), 41 | filename:join(ModPath, \"../priv/\" ?MODULE_STRING); 42 | Dir -> 43 | filename:join(Dir, ?MODULE_STRING) 44 | end, 45 | case erlang:load_nif(So, 0) of 46 | {error, {load_failed, Reason}} -> 47 | Format = ?MODULE_STRING \" load NIF failed:~n~p~n\", 48 | error_logger:warning_msg(Format, [Reason]); 49 | ok -> ok 50 | end. 51 | "). 52 | 53 | fun_name(Sig) -> string:to_lower(lists:sublist(Sig, 4, length(Sig))). 54 | 55 | gen_erl() -> 56 | [ 57 | ?ERL_HEAD, 58 | [ io_lib:format("~s() -> undefined.~n", [fun_name(Sig)]) || Sig <- ?SIGS ] 59 | ]. 60 | 61 | gen_c() -> 62 | [ 63 | "#include \n#include \"erl_nif.h\"\n", 64 | [ io_lib:format( 65 | "static ERL_NIF_TERM " 66 | "sh_~s(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {~n" 67 | "\t#ifdef ~s~n" 68 | "\treturn enif_make_int(env, ~s);~n" 69 | "\t#else~n" 70 | "\treturn enif_make_atom(env, \"undefined\");~n" 71 | "\t#endif~n" 72 | "}~n", 73 | [fun_name(Sig), Sig, Sig]) || Sig <- ?SIGS ], 74 | "static ErlNifFunc nif_funcs[] = {\n", 75 | lists:map(fun(Sig) -> 76 | Prefix = case get(done_first) =:= undefined of 77 | true -> put(done_first, true), ""; 78 | false -> ",\n" 79 | end, 80 | io_lib:format("~s\t{\"~s\", 0, sh_~s}", 81 | [Prefix, fun_name(Sig), fun_name(Sig)]) 82 | end, ?SIGS), 83 | "\n};\n" 84 | "ERL_NIF_INIT(sighandler_nif, nif_funcs, NULL,NULL, NULL, NULL);\n" 85 | ]. 86 | 87 | main([OutputDirPrefix]) -> 88 | NifErl = filename:join([OutputDirPrefix, "src", "sighandler_nif.erl"]), 89 | NifC = filename:join([OutputDirPrefix, "c_src", "sighandler_nif.c"]), 90 | ok = file:write_file(NifErl, gen_erl()), 91 | ok = file:write_file(NifC, gen_c()). 92 | -------------------------------------------------------------------------------- /test.config: -------------------------------------------------------------------------------- 1 | [ 2 | {sighandler, [ 3 | {handlers, [ 4 | {hup, {io, fwrite, ["Hello, world!~n"]}} 5 | ]} 6 | ]} 7 | ]. --------------------------------------------------------------------------------