Merge commit 'ee5198ba2810e33e08ff987ede5abe10fc74f6e3' as 'src/deps/src/lua-resty-signal'

This commit is contained in:
Théophile Diot 2023-06-30 15:38:56 -04:00
commit fdf0050a91
11 changed files with 961 additions and 0 deletions

View File

@ -0,0 +1,9 @@
*~
*.swp
*.swo
t/servroot*
/go
/reindex
/a.lua
*.o
*.so

View File

@ -0,0 +1,46 @@
sudo: required
dist: xenial
os: linux
language: c
compiler:
- gcc
- clang
env:
global:
- JOBS=3
- NGX_BUILD_JOBS=$JOBS
- LUAJIT_PREFIX=/opt/luajit21
- LUAJIT_LIB=$LUAJIT_PREFIX/lib
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
- LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
matrix:
- NGINX_VERSION=1.19.3
install:
- sudo apt-get install -qq -y cpanminus axel
- sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1)
- git clone https://github.com/openresty/openresty.git ../openresty
- git clone https://github.com/openresty/nginx-devel-utils.git
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git
script:
- make
- cd luajit2/
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1)
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
- cd ..
- export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
- export NGX_BUILD_CC=$CC
- ngx-build $NGINX_VERSION --with-http_realip_module --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --with-debug > build.log 2>&1 || (cat build.log && exit 1)
- nginx -V
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
- prove -r t

View File

@ -0,0 +1,46 @@
OPENRESTY_PREFIX=/usr/local/openresty
PREFIX ?= /usr/local
LUA_INCLUDE_DIR ?= $(PREFIX)/include
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
INSTALL ?= install
.PHONY: all test install
SRC := resty_signal.c
OBJ := $(SRC:.c=.o)
C_SO_NAME := librestysignal.so
CFLAGS := -O3 -g -Wall -fpic
LDFLAGS := -shared
# on Mac OS X, one should set instead:
# LDFLAGS := -bundle -undefined dynamic_lookup
MY_CFLAGS := $(CFLAGS)
MY_LDFLAGS := $(LDFLAGS) -fvisibility=hidden
test := t
.PHONY = all test clean install
all : $(C_SO_NAME)
${OBJ} : %.o : %.c
$(CC) $(MY_CFLAGS) -c $<
${C_SO_NAME} : ${OBJ}
$(CC) $(MY_LDFLAGS) $^ -o $@
#export TEST_NGINX_NO_CLEAN=1
clean:; rm -f *.o *.so a.out *.d
install:
$(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/resty
$(INSTALL) lib/resty/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty
$(INSTALL) $(C_SO_NAME) $(DESTDIR)$(LUA_LIB_DIR)/
test : all
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r $(test)

View File

@ -0,0 +1,132 @@
Name
====
lua-resty-signal - Lua library for killing or sending signals to Linux processes
Table of Contents
=================
* [Name](#name)
* [Synopsis](#synopsis)
* [Functions](#functions)
* [kill](#kill)
* [signum](#signum)
* [Author](#author)
* [Copyright & Licenses](#copyright--licenses)
Synopsis
========
```lua
local resty_signal = require "resty.signal"
local pid = 12345
local ok, err = resty_signal.kill(pid, "TERM")
if not ok then
ngx.log(ngx.ERR, "failed to kill process of pid ", pid, ": ", err)
return
end
-- send the signal 0 to check the existence of a process
local ok, err = resty_signal.kill(pid, "NONE")
local ok, err = resty_signal.kill(pid, "HUP")
local ok, err = resty_signal.kill(pid, "KILL")
```
Functions
=========
kill
----
**syntax:** `ok, err = resty_signal.kill(pid, signal_name_or_num)`
Sends a signal with its name string or number value to the process of the
specified pid.
All signal names accepted by [signum](#signum) are supported, like `HUP`,
`KILL`, and `TERM`.
Signal numbers are also supported when specifying nonportable system-specific
signals is desired.
[Back to TOC](#table-of-contents)
signum
------
**syntax:** `num = resty_signal.signum(sig_name)`
Maps the signal name specified to the system-specific signal number. Returns
`nil` if the signal name is not known.
All the POSIX and BSD signal names are supported:
```
HUP
INT
QUIT
ILL
TRAP
ABRT
BUS
FPE
KILL
USR1
SEGV
USR2
PIPE
ALRM
TERM
CHLD
CONT
STOP
TSTP
TTIN
TTOU
URG
XCPU
XFSZ
VTALRM
PROF
WINCH
IO
PWR
EMT
SYS
INFO
```
The special signal name `NONE` is also supported, which is mapped to zero (0).
[Back to TOC](#table-of-contents)
Author
======
Yichun Zhang (agentzh) <yichun@openresty.com>
[Back to TOC](#table-of-contents)
Copyright & Licenses
====================
This module is licensed under the BSD license.
Copyright (C) 2018-2019, [OpenResty Inc.](https://openresty.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 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.
* 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.
[Back to TOC](#table-of-contents)

View File

@ -0,0 +1,156 @@
local _M = {
version = 0.03
}
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_str = ffi.string
local tonumber = tonumber
local assert = assert
local errno = ffi.errno
local type = type
local new_tab = base.new_tab
local error = error
local string_format = string.format
local load_shared_lib
do
local string_gmatch = string.gmatch
local string_match = string.match
local io_open = io.open
local io_close = io.close
local cpath = package.cpath
function load_shared_lib(so_name)
local tried_paths = new_tab(32, 0)
local i = 1
for k, _ in string_gmatch(cpath, "[^;]+") do
local fpath = string_match(k, "(.*/)")
fpath = fpath .. so_name
-- Don't get me wrong, the only way to know if a file exist is
-- trying to open it.
local f = io_open(fpath)
if f ~= nil then
io_close(f)
return ffi.load(fpath)
end
tried_paths[i] = fpath
i = i + 1
end
return nil, tried_paths
end -- function
end -- do
local resty_signal, tried_paths = load_shared_lib("librestysignal.so")
if not resty_signal then
error("could not load librestysignal.so from the following paths:\n" ..
table.concat(tried_paths, "\n"), 2)
end
ffi.cdef[[
int resty_signal_signum(int num);
]]
if not pcall(function () return C.kill end) then
ffi.cdef("int kill(int32_t pid, int sig);")
end
if not pcall(function () return C.strerror end) then
ffi.cdef("char *strerror(int errnum);")
end
-- Below is just the ID numbers for each POSIX signal. We map these signal IDs
-- to system-specific signal numbers on the C land (via librestysignal.so).
local signals = {
NONE = 0,
HUP = 1,
INT = 2,
QUIT = 3,
ILL = 4,
TRAP = 5,
ABRT = 6,
BUS = 7,
FPE = 8,
KILL = 9,
USR1 = 10,
SEGV = 11,
USR2 = 12,
PIPE = 13,
ALRM = 14,
TERM = 15,
CHLD = 17,
CONT = 18,
STOP = 19,
TSTP = 20,
TTIN = 21,
TTOU = 22,
URG = 23,
XCPU = 24,
XFSZ = 25,
VTALRM = 26,
PROF = 27,
WINCH = 28,
IO = 29,
PWR = 30,
EMT = 31,
SYS = 32,
INFO = 33
}
local function signum(name)
local sig_num
if type(name) == "number" then
sig_num = name
else
local id = signals[name]
if not id then
return nil, "unknown signal name"
end
sig_num = tonumber(resty_signal.resty_signal_signum(id))
if sig_num < 0 then
error(
string_format("missing C def for signal %s = %d", name, id),
2
)
end
end
return sig_num
end
function _M.kill(pid, sig)
assert(sig)
local sig_num, err = signum(sig)
if err then
return nil, err
end
local rc = tonumber(C.kill(assert(pid), sig_num))
if rc == 0 then
return true
end
local err = ffi_str(C.strerror(errno()))
return nil, err
end
_M.signum = signum
return _M

View File

@ -0,0 +1,152 @@
#include <signal.h>
enum {
RS_NONE = 0,
RS_HUP = 1,
RS_INT = 2,
RS_QUIT = 3,
RS_ILL = 4,
RS_TRAP = 5,
RS_ABRT = 6,
RS_BUS = 7,
RS_FPE = 8,
RS_KILL = 9,
RS_USR1 = 10,
RS_SEGV = 11,
RS_USR2 = 12,
RS_PIPE = 13,
RS_ALRM = 14,
RS_TERM = 15,
RS_CHLD = 17,
RS_CONT = 18,
RS_STOP = 19,
RS_TSTP = 20,
RS_TTIN = 21,
RS_TTOU = 22,
RS_URG = 23,
RS_XCPU = 24,
RS_XFSZ = 25,
RS_VTALRM = 26,
RS_PROF = 27,
RS_WINCH = 28,
RS_IO = 29,
RS_PWR = 30,
RS_EMT = 31,
RS_SYS = 32,
RS_INFO = 33
};
int
resty_signal_signum(int num)
{
switch (num) {
case RS_NONE:
return 0;
case RS_HUP:
return SIGHUP;
case RS_INT:
return SIGINT;
case RS_QUIT:
return SIGQUIT;
case RS_ILL:
return SIGILL;
case RS_TRAP:
return SIGTRAP;
case RS_ABRT:
return SIGABRT;
case RS_BUS:
return SIGBUS;
case RS_FPE:
return SIGFPE;
case RS_KILL:
return SIGKILL;
case RS_SEGV:
return SIGSEGV;
case RS_PIPE:
return SIGPIPE;
case RS_ALRM:
return SIGALRM;
case RS_TERM:
return SIGTERM;
case RS_CHLD:
return SIGCHLD;
case RS_CONT:
return SIGCONT;
case RS_STOP:
return SIGSTOP;
case RS_TSTP:
return SIGTSTP;
case RS_TTIN:
return SIGTTIN;
case RS_TTOU:
return SIGTTOU;
case RS_XCPU:
return SIGXCPU;
case RS_XFSZ:
return SIGXFSZ;
case RS_VTALRM:
return SIGVTALRM;
case RS_PROF:
return SIGPROF;
case RS_WINCH:
return SIGWINCH;
case RS_IO:
return SIGIO;
#ifdef __linux__
case RS_PWR:
return SIGPWR;
#endif
case RS_USR1:
return SIGUSR1;
case RS_USR2:
return SIGUSR2;
case RS_URG:
return SIGURG;
#ifdef __APPLE__
case RS_EMT:
return SIGEMT;
case RS_SYS:
return SIGSYS;
case RS_INFO:
return SIGINFO;
#endif
default:
return -1;
}
}

View File

@ -0,0 +1,32 @@
package t::TestKiller;
use v5.10.1;
use Test::Nginx::Socket::Lua -Base;
add_block_preprocessor(sub {
my $block = shift;
my $http_config = $block->http_config // '';
my $init_by_lua_block = $block->init_by_lua_block // 'require "resty.core"';
$http_config .= <<_EOC_;
lua_package_path "./lib/?.lua;../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
lua_package_cpath "./?.so;;";
init_by_lua_block {
$init_by_lua_block
}
_EOC_
$block->set_value("http_config", $http_config);
if (!defined $block->error_log) {
$block->set_value("no_error_log", "[error]");
}
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
});
1;

View File

@ -0,0 +1,207 @@
# vi:ft=
use lib '.';
use t::TestKiller;
plan tests => 3 * blocks();
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: returns an error if signal is unknown
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local ok, err = resty_signal.kill(pid, "FOO")
if not ok then
ngx.say("failed to send FOO signal: ", err)
return
end
ngx.say("ok")
}
}
--- response_body
failed to send FOO signal: unknown signal name
=== TEST 2: send NONE to a non-existing process
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local say = ngx.say
local ngx_pipe = require "ngx.pipe"
local proc = assert(ngx_pipe.spawn("echo ok"))
local pid = assert(proc:pid())
assert(proc:wait())
local ok, err = resty_signal.kill(pid, "NONE")
if not ok then
ngx.say("failed to send NONE signal: ", err)
return
end
ngx.say("ok")
}
}
--- response_body
failed to send NONE signal: No such process
=== TEST 3: send TERM to a non-existing process
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local say = ngx.say
local ngx_pipe = require "ngx.pipe"
local proc = assert(ngx_pipe.spawn("echo ok"))
local pid = assert(proc:pid())
assert(proc:wait())
local ok, err = resty_signal.kill(pid, "TERM")
if not ok then
ngx.say("failed to send TERM signal: ", err)
return
end
ngx.say("ok")
}
}
--- response_body
failed to send TERM signal: No such process
=== TEST 4: send NONE to an existing process
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local say = ngx.say
local ngx_pipe = require "ngx.pipe"
local proc = assert(ngx_pipe.spawn("echo ok"))
local pid = assert(proc:pid())
-- assert(proc:wait())
for i = 1, 2 do
ngx.say("i = ", i)
local ok, err = resty_signal.kill(pid, "NONE")
if not ok then
ngx.say("failed to send NONE signal: ", err)
return
end
end
ngx.say("ok")
}
}
--- response_body
i = 1
i = 2
ok
=== TEST 5: send TERM to an existing process
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local say = ngx.say
local ngx_pipe = require "ngx.pipe"
local proc = assert(ngx_pipe.spawn("echo ok"))
local pid = assert(proc:pid())
-- assert(proc:wait())
for i = 1, 2 do
ngx.say("i = ", i)
local ok, err = resty_signal.kill(pid, "TERM")
if not ok then
ngx.say("failed to send TERM signal: ", err)
return
end
ngx.sleep(0.01)
end
ngx.say("ok")
}
}
--- response_body
i = 1
i = 2
failed to send TERM signal: No such process
=== TEST 6: send KILL to an existing process
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local say = ngx.say
local ngx_pipe = require "ngx.pipe"
local proc = assert(ngx_pipe.spawn("echo ok"))
local pid = assert(proc:pid())
-- assert(proc:wait())
for i = 1, 2 do
ngx.say("i = ", i)
local ok, err = resty_signal.kill(pid, "KILL")
if not ok then
ngx.say("failed to send KILL signal: ", err)
return
end
ngx.sleep(0.01)
end
ngx.say("ok")
}
}
--- response_body
i = 1
i = 2
failed to send KILL signal: No such process
=== TEST 7: send TERM signal value, 15, directly to an existing process
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local say = ngx.say
local ngx_pipe = require "ngx.pipe"
local proc = assert(ngx_pipe.spawn("echo ok"))
local pid = assert(proc:pid())
-- assert(proc:wait())
for i = 1, 2 do
ngx.say("i = ", i)
local ok, err = resty_signal.kill(pid, 15)
if not ok then
ngx.say("failed to send TERM signal: ", err)
return
end
ngx.sleep(0.01)
end
ngx.say("ok")
}
}
--- response_body
i = 1
i = 2
failed to send TERM signal: No such process

View File

@ -0,0 +1,35 @@
# vi:ft=
use lib '.';
use t::TestKiller;
plan tests => 3 * blocks();
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: failure to load librestysignal.so
--- config
location = /t {
content_by_lua_block {
local cpath = package.cpath
package.cpath = "/foo/?.so;/bar/?.so;"
local ok, perr = pcall(require, "resty.signal")
if not ok then
ngx.say(perr)
end
package.cpath = cpath
}
}
--- response_body
could not load librestysignal.so from the following paths:
/foo/librestysignal.so
/bar/librestysignal.so
--- no_error_log
[error]

View File

@ -0,0 +1,116 @@
# vi:ft=
use lib '.';
use t::TestKiller;
plan tests => 3 * blocks();
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: signals whose values are specified by POSIX
--- config
location = /t {
content_by_lua_block {
local resty_signal = require "resty.signal"
local ffi = require "ffi"
local say = ngx.say
local signum = resty_signal.signum
for i, signame in ipairs{ "ABRT", "ALRM", "HUP", "INT", "KILL",
"QUIT", "TERM", "TRAP", "BLAH" } do
say(signame, ": ", tostring(signum(signame)))
end
local linux_signals = {
NONE = 0,
HUP = 1,
INT = 2,
QUIT = 3,
ILL = 4,
TRAP = 5,
ABRT = 6,
BUS = 7,
FPE = 8,
KILL = 9,
USR1 = 10,
SEGV = 11,
USR2 = 12,
PIPE = 13,
ALRM = 14,
TERM = 15,
CHLD = 17,
CONT = 18,
STOP = 19,
TSTP = 20,
TTIN = 21,
TTOU = 22,
URG = 23,
XCPU = 24,
XFSZ = 25,
VTALRM = 26,
PROF = 27,
WINCH = 28,
IO = 29,
PWR = 30
}
local macosx_signals = {
HUP = 1,
INT = 2,
QUIT = 3,
ILL = 4,
TRAP = 5,
ABRT = 6,
EMT = 7,
FPE = 8,
KILL = 9,
BUS = 10,
SEGV = 11,
SYS = 12,
PIPE = 13,
ALRM = 14,
TERM = 15,
URG = 16,
STOP = 17,
TSTP = 18,
CONT = 19,
CHLD = 20,
TTIN = 21,
TTOU = 22,
IO = 23,
XCPU = 24,
XFSZ = 25,
VTALRM = 26,
PROF = 27,
WINCH = 28,
INFO = 29,
USR1 = 30,
USR2 = 31
}
if ffi.os == "Linux" then
for signame, num in pairs(linux_signals) do
assert(num == tonumber(signum(signame)))
end
elseif ffi.os == "OSX" then
for signame, num in pairs(macosx_signals) do
assert(num == tonumber(signum(signame)))
end
end
}
}
--- response_body
ABRT: 6
ALRM: 14
HUP: 1
INT: 2
KILL: 9
QUIT: 3
TERM: 15
TRAP: 5
BLAH: nil

View File

@ -0,0 +1,30 @@
{
<insert_a_suppression_name_here>
Memcheck:Param
epoll_ctl(event)
fun:epoll_ctl
fun:ngx_epoll_test_rdhup
fun:ngx_epoll_init
fun:ngx_event_process_init
fun:ngx_single_process_cycle
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:ngx_alloc
fun:ngx_set_environment
fun:ngx_single_process_cycle
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:ngx_alloc
fun:ngx_set_environment
fun:ngx_worker_process_init
}