From 61fd57ddb578230757dad577e1d29d628be5e4ef Mon Sep 17 00:00:00 2001 From: suyuan <175338101@qq.com> Date: Tue, 11 Oct 2022 22:40:25 +0800 Subject: [PATCH] fix --- nginx/Config.in | 25 +- nginx/Config_ssl.in | 23 + nginx/Makefile | 117 +- .../files-luci-support/60_nginx-luci-support | 13 + nginx/files-luci-support/luci_nginx.conf | 14 +- nginx/files-luci-support/luci_nginx_ssl.conf | 20 +- nginx/files-luci-support/luci_uwsgi.conf | 26 +- nginx/files/nginx.init | 7 + .../100-drop-libxslt-dep.patch | 14 + .../101-add-lua-ngx-pipe.patch | 579 ++++ .../patches/201-ignore-invalid-options.patch | 2 +- nginx/patches/400-socket-cloexec.patch | 174 ++ nginx/src/lua-nginx/src/ngx_http_lua_pipe.c | 2499 +++++++++++++++++ nginx/src/lua-nginx/src/ngx_http_lua_pipe.h | 94 + 14 files changed, 3534 insertions(+), 73 deletions(-) create mode 100644 nginx/patches-dav-nginx/100-drop-libxslt-dep.patch create mode 100644 nginx/patches-lua-nginx/101-add-lua-ngx-pipe.patch create mode 100644 nginx/patches/400-socket-cloexec.patch create mode 100644 nginx/src/lua-nginx/src/ngx_http_lua_pipe.c create mode 100644 nginx/src/lua-nginx/src/ngx_http_lua_pipe.h diff --git a/nginx/Config.in b/nginx/Config.in index 68b037098..ccb5e240f 100755 --- a/nginx/Config.in +++ b/nginx/Config.in @@ -22,6 +22,13 @@ config NGINX_DAV Enable the HTTP and WebDAV methods PUT, DELETE, MKCOL, COPY and MOVE. default n +config NGINX_UBUS + bool + prompt "Enable UBUS module" + help + Enable UBUS api support directly from the server. + default y + config NGINX_FLV bool prompt "Enable FLV module" @@ -226,7 +233,23 @@ config NGINX_STREAM_CORE_MODULE prompt "Enable stream support" help Add support for NGINX request streaming. - default y + default n + +config NGINX_STREAM_SSL_MODULE + bool + prompt "Enable stream support with SSL/TLS termination" + depends on NGINX_STREAM_CORE_MODULE + help + Add support for NGINX request streaming with SSL/TLS termination. + default n + +config NGINX_STREAM_SSL_PREREAD_MODULE + bool + prompt "Enable stream support with SSL/TLS pre-read" + depends on NGINX_STREAM_CORE_MODULE + help + Add support for NGINX request streaming using information from the ClientHello message without terminating SSL/TLS. + default n config NGINX_RTMP_MODULE bool diff --git a/nginx/Config_ssl.in b/nginx/Config_ssl.in index 9325fba51..050d71fe3 100755 --- a/nginx/Config_ssl.in +++ b/nginx/Config_ssl.in @@ -15,6 +15,13 @@ config NGINX_DAV Enable the HTTP and WebDAV methods PUT, DELETE, MKCOL, COPY and MOVE. default n +config NGINX_UBUS + bool + prompt "Enable UBUS module" + help + Enable UBUS api support directly from the server. + default y + config NGINX_FLV bool prompt "Enable FLV module" @@ -221,6 +228,22 @@ config NGINX_STREAM_CORE_MODULE Add support for NGINX request streaming. default n +config NGINX_STREAM_SSL_MODULE + bool + prompt "Enable stream support with SSL/TLS termination" + depends on NGINX_STREAM_CORE_MODULE + help + Add support for NGINX request streaming with SSL/TLS termination. + default n + +config NGINX_STREAM_SSL_PREREAD_MODULE + bool + prompt "Enable stream support with SSL/TLS pre-read" + depends on NGINX_STREAM_CORE_MODULE + help + Add support for NGINX request streaming using information from the ClientHello message without terminating SSL/TLS. + default n + config NGINX_RTMP_MODULE bool prompt "Enable RTMP module" diff --git a/nginx/Makefile b/nginx/Makefile index dc32fbb04..bd03ac5f2 100755 --- a/nginx/Makefile +++ b/nginx/Makefile @@ -8,18 +8,17 @@ include $(TOPDIR)/rules.mk PKG_NAME:=nginx -PKG_VERSION:=1.16.1 +PKG_VERSION:=1.17.7 PKG_RELEASE:=2 PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=http://nginx.org/download/ -PKG_HASH:=f11c2a6dd1d3515736f0324857957db2de98be862461b5a542a3ac6188dbe32b +PKG_SOURCE_URL:=https://nginx.org/download/ +PKG_HASH:=b62756842807e5693b794e5d0ae289bd8ae5b098e66538b2a91eb80f25c591ff PKG_MAINTAINER:=Thomas Heil \ Ansuel Smith PKG_LICENSE:=2-clause BSD-like license - -PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION) +PKG_CPE_ID:=cpe:/a:nginx:nginx PKG_FIXUP:=autoreconf PKG_BUILD_PARALLEL:=1 @@ -29,6 +28,7 @@ PKG_CONFIG_DEPENDS := \ CONFIG_NGINX_SSL \ CONFIG_NGINX_DAV \ CONFIG_NGINX_FLV \ + CONFIG_NGINX_UBUS \ CONFIG_NGINX_STUB_STATUS \ CONFIG_NGINX_HTTP_CHARSET \ CONFIG_NGINX_HTTP_GZIP \ @@ -67,8 +67,12 @@ PKG_CONFIG_DEPENDS := \ CONFIG_NGINX_HTTP_BROTLI \ CONFIG_NGINX_HEADERS_MORE \ CONFIG_NGINX_STREAM_CORE_MODULE \ + CONFIG_NGINX_STREAM_SSL_MODULE \ + CONFIG_NGINX_STREAM_SSL_PREREAD_MODULE \ CONFIG_NGINX_RTMP_MODULE \ CONFIG_NGINX_TS_MODULE \ + CONFIG_OPENSSL_ENGINE \ + CONFIG_OPENSSL_WITH_NPN include $(INCLUDE_DIR)/package.mk @@ -78,8 +82,9 @@ define Package/nginx/default SUBMENU:=Web Servers/Proxies TITLE:=Nginx web server URL:=http://nginx.org/ - DEPENDS:=+NGINX_PCRE:libpcre +(NGINX_SSL||NGINX_HTTP_CACHE||NGINX_HTTP_AUTH_BASIC):libopenssl \ - +NGINX_HTTP_GZIP:zlib +NGINX_LUA:liblua +libpthread +NGINX_DAV:libexpat + DEPENDS:=+NGINX_PCRE:libpcre +NGINX_SSL:libopenssl \ + +NGINX_HTTP_GZIP:zlib +NGINX_LUA:liblua +libpthread +NGINX_DAV:libxml2 \ + +NGINX_UBUS:libubus +NGINX_UBUS:libblobmsg-json +NGINX_UBUS:libjson-c endef define Package/nginx/description @@ -102,19 +107,20 @@ define Package/nginx-ssl endef Package/nginx-ssl/description = $(Package/nginx/description) \ - This varian is compiled with SSL support enabled. To enable additional module \ + This variant is compiled with SSL support enabled. To enable additional module \ select them in the nginx default configuration menu. define Package/nginx-all-module $(Package/nginx/default) TITLE += with ALL module selected - DEPENDS:=+libpcre +libopenssl +zlib +liblua +libpthread +libexpat + DEPENDS:=+libpcre +libopenssl +zlib +liblua +libpthread +libxml2 \ + +libubus +libblobmsg-json +libjson-c VARIANT:=all-module PROVIDES:=nginx endef Package/nginx-all-module/description = $(Package/nginx/description) \ - This varian is compiled with ALL module selected. + This variant is compiled with ALL module selected. define Package/nginx/config source "$(SOURCE)/Config.in" @@ -134,7 +140,7 @@ Package/nginx-ssl/conffiles = $(Package/nginx/conffiles) Package/nginx-all-module/conffiles = $(Package/nginx/conffiles) -ADDITIONAL_MODULES:= +ADDITIONAL_MODULES:=--with-http_gzip_static_module ifneq ($(BUILD_VARIANT),all-module) ifneq ($(CONFIG_NGINX_HTTP_CACHE),y) @@ -142,6 +148,8 @@ ifneq ($(BUILD_VARIANT),all-module) endif ifneq ($(CONFIG_NGINX_PCRE),y) ADDITIONAL_MODULES += --without-pcre + else + ADDITIONAL_MODULES += --with-pcre endif ifneq ($(CONFIG_NGINX_HTTP_CHARSET),y) ADDITIONAL_MODULES += --without-http_charset_module @@ -191,6 +199,8 @@ ifneq ($(BUILD_VARIANT),all-module) endif ifneq ($(CONFIG_NGINX_HTTP_UWSGI),y) ADDITIONAL_MODULES += --without-http_uwsgi_module + else + config_files += uwsgi_params endif ifneq ($(CONFIG_NGINX_HTTP_SCGI),y) ADDITIONAL_MODULES += --without-http_scgi_module @@ -250,6 +260,9 @@ ifneq ($(BUILD_VARIANT),all-module) ifeq ($(CONFIG_NGINX_DAV),y) ADDITIONAL_MODULES += --with-http_dav_module --add-module=$(PKG_BUILD_DIR)/nginx-dav-ext-module endif + ifeq ($(CONFIG_NGINX_UBUS),y) + ADDITIONAL_MODULES += --add-module=$(PKG_BUILD_DIR)/nginx-ubus-module + endif ifeq ($(CONFIG_NGINX_HTTP_AUTH_REQUEST),y) ADDITIONAL_MODULES += --with-http_auth_request_module endif @@ -268,6 +281,12 @@ ifneq ($(BUILD_VARIANT),all-module) ifeq ($(CONFIG_NGINX_STREAM_CORE_MODULE),y) ADDITIONAL_MODULES += --with-stream endif + ifeq ($(CONFIG_NGINX_STREAM_SSL_MODULE),y) + ADDITIONAL_MODULES += --with-stream_ssl_module + endif + ifeq ($(CONFIG_NGINX_STREAM_SSL_PREREAD_MODULE),y) + ADDITIONAL_MODULES += --with-stream_ssl_preread_module + endif ifeq ($(CONFIG_NGINX_HEADERS_MORE),y) ADDITIONAL_MODULES += --add-module=$(PKG_BUILD_DIR)/nginx-headers-more endif @@ -288,14 +307,15 @@ else CONFIG_NGINX_NAXSI:=y CONFIG_NGINX_LUA:=y CONFIG_NGINX_DAV:=y + CONFIG_NGINX_UBUS:=y ADDITIONAL_MODULES += --with-http_ssl_module --add-module=$(PKG_BUILD_DIR)/nginx-naxsi/naxsi_src \ --add-module=$(PKG_BUILD_DIR)/lua-nginx --with-ipv6 --with-http_stub_status_module --with-http_flv_module \ --with-http_dav_module --add-module=$(PKG_BUILD_DIR)/nginx-dav-ext-module \ --with-http_auth_request_module --with-http_v2_module --with-http_realip_module \ --with-http_secure_link_module --with-http_sub_module --add-module=$(PKG_BUILD_DIR)/nginx-headers-more \ - --with-stream \ + --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module \ --add-module=$(PKG_BUILD_DIR)/nginx-brotli --add-module=$(PKG_BUILD_DIR)/nginx-rtmp \ - --add-module=$(PKG_BUILD_DIR)/nginx-ts + --add-module=$(PKG_BUILD_DIR)/nginx-ts --add-module=$(PKG_BUILD_DIR)/nginx-ubus-module config_files += koi-utf koi-win win-utf fastcgi_params endif @@ -306,7 +326,7 @@ define Package/nginx-mod-luci/default SUBMENU:=Web Servers/Proxies TITLE:=Support file for Nginx URL:=http://nginx.org/ - DEPENDS:=+uwsgi-cgi +uwsgi-cgi-luci-support + DEPENDS:=+uwsgi +uwsgi-luci-support endef define Package/nginx-mod-luci @@ -396,6 +416,7 @@ define Build/Prepare $(Prepare/nginx-rtmp) $(Prepare/nginx-ts) $(Prepare/nginx-dav-ext-module) + $(Prepare/nginx-ubus-module) endef @@ -403,34 +424,34 @@ ifeq ($(CONFIG_NGINX_HEADERS_MORE),y) define Download/nginx-headers-more VERSION:=a9f7c7e86cc7441d04e2f11f01c2e3a9c4b0301d SUBDIR:=nginx-headers-more - FILE:=headers-more-nginx-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=headers-more-nginx-module-$$(VERSION).tar.xz URL:=https://github.com/openresty/headers-more-nginx-module.git - MIRROR_HASH:=432609015719aaa7241e5166c7cda427acbe004f725887f78ef629d51bd9cb3f + MIRROR_HASH:=ce0b9996ecb2cff790831644d6ab1adc087aa2771d77d3931c06246d11bc59fd PROTO:=git endef $(eval $(call Download,nginx-headers-more)) define Prepare/nginx-headers-more $(eval $(Download/nginx-headers-more)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) endef endif ifeq ($(CONFIG_NGINX_HTTP_BROTLI),y) define Download/nginx-brotli - VERSION:=e26248ee361c04e25f581b92b85d95681bdffb39 + VERSION:=dc37f658ccb5a51d090dc09d1a2aca2f24309869 SUBDIR:=nginx-brotli - FILE:=ngx-brotli-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=ngx-brotli-module-$$(VERSION).tar.xz URL:=https://github.com/eustas/ngx_brotli.git - MIRROR_HASH:=76b891ba49f82f0cfbc9cba875646e26ee986b522373e0aa2698a9923a4adcdb + MIRROR_HASH:=6bc0c40ff24f6e0ac616dfddc803bdc7fcf54764ba9dc4f9cecb3a68beedcdaf PROTO:=git endef $(eval $(call Download,nginx-brotli)) define Prepare/nginx-brotli $(eval $(Download/nginx-brotli)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) endef endif @@ -439,16 +460,16 @@ ifeq ($(CONFIG_NGINX_RTMP_MODULE),y) define Download/nginx-rtmp VERSION:=f0ea62342a4eca504b311cd5df910d026c3ea4cf SUBDIR:=nginx-rtmp - FILE:=ngx-rtmp-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=ngx-rtmp-module-$$(VERSION).tar.xz URL:=https://github.com/ut0mt8/nginx-rtmp-module.git - MIRROR_HASH:=9ba7625718d21f658c4878729271832a07bd989165f1d1c720b3a9b54cf738cc + MIRROR_HASH:=d3f58066f0f858ed79f7f2b0c9b89de2ccc512c94ab3d0625f6dcff3df0b72c1 PROTO:=git endef $(eval $(call Download,nginx-rtmp)) define Prepare/nginx-rtmp $(eval $(Download/nginx-rtmp)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) endef endif @@ -457,16 +478,16 @@ ifeq ($(CONFIG_NGINX_TS_MODULE),y) define Download/nginx-ts VERSION:=ef2f874d95cc75747eb625a292524a702aefb0fd SUBDIR:=nginx-ts - FILE:=ngx-ts-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=ngx-ts-module-$$(VERSION).tar.xz URL:=https://github.com/arut/nginx-ts-module.git - MIRROR_HASH:=31ecc9968b928886b54884138eafe2fa747648bca5094d4c3132e8ae9509d1d3 + MIRROR_HASH:=73938950bb286d40d9e54b0994d1a63827340c1156c72eb04d7041b25b20ec18 PROTO:=git endef $(eval $(call Download,nginx-ts)) define Prepare/nginx-ts $(eval $(Download/nginx-ts)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) endef endif @@ -475,16 +496,16 @@ ifeq ($(CONFIG_NGINX_NAXSI),y) define Download/nginx-naxsi VERSION:=951123ad456bdf5ac94e8d8819342fe3d49bc002 SUBDIR:=nginx-naxsi - FILE:=nginx-naxsi-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=nginx-naxsi-module-$$(VERSION).tar.xz URL:=https://github.com/nbs-system/naxsi.git - MIRROR_HASH:=7ab791f2ff38096f48013141bbfe20ba213d5e04dcac08ca82e0cac07d5c30f0 + MIRROR_HASH:=c734cae19a596affadd62a2df1b58d3df8d1364093a4e80a7cd1ab4555963535 PROTO:=git endef $(eval $(call Download,nginx-naxsi)) define Prepare/nginx-naxsi $(eval $(Download/nginx-naxsi)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) endef endif @@ -493,16 +514,16 @@ ifeq ($(CONFIG_NGINX_LUA),y) define Download/lua-nginx VERSION:=e94f2e5d64daa45ff396e262d8dab8e56f5f10e0 SUBDIR:=lua-nginx - FILE:=lua-nginx-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=lua-nginx-module-$$(VERSION).tar.xz URL:=https://github.com/openresty/lua-nginx-module.git - MIRROR_HASH:=ae439f9a8b3c34d7240735b844db72ee721af4791bbaff5692bca20e6785f541 + MIRROR_HASH:=27729921964f066d97e99c263da153b34622a2f4b811114e4c3ee61c6fc71395 PROTO:=git endef $(eval $(call Download,lua-nginx)) define Prepare/lua-nginx $(eval $(Download/lua-nginx)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) $(call PatchDir,$(PKG_BUILD_DIR),./patches-lua-nginx) endef endif @@ -510,23 +531,41 @@ endif ifeq ($(CONFIG_NGINX_DAV),y) define Download/nginx-dav-ext-module - VERSION:=430fd774fe838a04f1a5defbf1dd571d42300cf9 + VERSION:=f5e30888a256136d9c550bf1ada77d6ea78a48af SUBDIR:=nginx-dav-ext-module - FILE:=nginx-dav-ext-module-$(PKG_VERSION)-$$(VERSION).tar.gz + FILE:=nginx-dav-ext-module-$$(VERSION).tar.xz URL:=https://github.com/arut/nginx-dav-ext-module.git - MIRROR_HASH:=0566053a8756423ecab455fd9d218cec1e017598fcbb3d6415a06f816851611e + MIRROR_HASH:=70bb4c3907f4b783605500ba494e907aede11f8505702e370012abb3c177dc5b PROTO:=git endef $(eval $(call Download,nginx-dav-ext-module)) define Prepare/nginx-dav-ext-module $(eval $(Download/nginx-dav-ext-module)) - gzip -dc $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + $(call PatchDir,$(PKG_BUILD_DIR),./patches-dav-nginx) + endef +endif + +ifeq ($(CONFIG_NGINX_UBUS),y) + define Download/nginx-ubus-module + VERSION:=f30b0167a2cdb40f23bd90928d601bdb0c1b8fad + SUBDIR:=nginx-ubus-module + FILE:=nginx-ubus-module-$$(VERSION).tar.xz + URL:=https://github.com/Ansuel/nginx-ubus-module.git + MIRROR_HASH:=02c7d4b0df7f4b69605e71b0fefdc99b5a9470c68cad7ccfb31ebefe4e7e0704 + PROTO:=git + endef + $(eval $(call Download,nginx-ubus-module)) + + define Prepare/nginx-ubus-module + $(eval $(Download/nginx-ubus-module)) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) endef endif $(eval $(call BuildPackage,nginx)) $(eval $(call BuildPackage,nginx-ssl)) $(eval $(call BuildPackage,nginx-all-module)) -#$(eval $(call BuildPackage,nginx-mod-luci)) -#$(eval $(call BuildPackage,nginx-mod-luci-ssl)) +$(eval $(call BuildPackage,nginx-mod-luci)) +$(eval $(call BuildPackage,nginx-mod-luci-ssl)) diff --git a/nginx/files-luci-support/60_nginx-luci-support b/nginx/files-luci-support/60_nginx-luci-support index dd076d260..b682a832e 100755 --- a/nginx/files-luci-support/60_nginx-luci-support +++ b/nginx/files-luci-support/60_nginx-luci-support @@ -25,4 +25,17 @@ if [ -f "/etc/nginx/luci_nginx.conf" ] && [ -f "/etc/nginx/nginx.conf" ]; then fi fi +if nginx -V 2>&1 | grep -q ubus; then + if [ -z "$(cat /etc/nginx/luci_uwsgi.conf | grep ubus)" ]; then + cat <> /etc/nginx/luci_uwsgi.conf + +location /ubus { + ubus_interpreter; + ubus_socket_path /var/run/ubus.sock; + ubus_parallel_req 2; +} +EOT + fi +fi + exit 0 diff --git a/nginx/files-luci-support/luci_nginx.conf b/nginx/files-luci-support/luci_nginx.conf index 2c02b48fd..75f0f78a6 100755 --- a/nginx/files-luci-support/luci_nginx.conf +++ b/nginx/files-luci-support/luci_nginx.conf @@ -1,5 +1,5 @@ -user nobody nogroup; +user root; worker_processes 1; #error_log logs/error.log; @@ -20,7 +20,7 @@ http { sendfile on; keepalive_timeout 0; - + client_body_buffer_size 10K; client_header_buffer_size 1k; client_max_body_size 1G; @@ -31,15 +31,15 @@ http { gzip_vary on; gzip_comp_level 1; gzip_proxied any; - - index login.html; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml; + root /www; server { - listen 88 default_server; - listen [::]:88 default_server; + listen 80 default_server; + listen [::]:80 default_server; server_name localhost; - + location ~* .(jpg|jpeg|png|gif|ico|css|js)$ { expires 365d; } diff --git a/nginx/files-luci-support/luci_nginx_ssl.conf b/nginx/files-luci-support/luci_nginx_ssl.conf index c35a90d03..db33e554c 100755 --- a/nginx/files-luci-support/luci_nginx_ssl.conf +++ b/nginx/files-luci-support/luci_nginx_ssl.conf @@ -20,7 +20,7 @@ http { sendfile on; keepalive_timeout 0; - + client_body_buffer_size 10K; client_header_buffer_size 1k; client_max_body_size 1G; @@ -31,22 +31,22 @@ http { gzip_vary on; gzip_comp_level 1; gzip_proxied any; - - index login.html; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml; + root /www; - + server { - listen 88 default_server; - listen [::]:88 default_server; + listen 80 default_server; + listen [::]:80 default_server; server_name _; return 301 https://$host$request_uri; } server { - listen 433 ssl default_server; - listen [::]:433 ssl default_server; + listen 443 ssl default_server; + listen [::]:443 ssl default_server; server_name localhost; - + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:DHE+AESGCM:DHE:!RSA!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!CAMELLIA:!SEED"; @@ -54,7 +54,7 @@ http { ssl_certificate /etc/nginx/nginx.cer; ssl_certificate_key /etc/nginx/nginx.key; - + location ~* .(jpg|jpeg|png|gif|ico|css|js)$ { expires 365d; } diff --git a/nginx/files-luci-support/luci_uwsgi.conf b/nginx/files-luci-support/luci_uwsgi.conf index 6211db74a..3ea3de914 100755 --- a/nginx/files-luci-support/luci_uwsgi.conf +++ b/nginx/files-luci-support/luci_uwsgi.conf @@ -1,19 +1,15 @@ location /cgi-bin/luci { - index index.html; - uwsgi_param QUERY_STRING $query_string; - uwsgi_param REQUEST_METHOD $request_method; - uwsgi_param CONTENT_TYPE $content_type; - uwsgi_param CONTENT_LENGTH $content_length if_not_empty; - uwsgi_param REQUEST_URI $request_uri; - uwsgi_param PATH_INFO $document_uri; - uwsgi_param SERVER_PROTOCOL $server_protocol; - uwsgi_param REMOTE_ADDR $remote_addr; - uwsgi_param REMOTE_PORT $remote_port; - uwsgi_param SERVER_ADDR $server_addr; - uwsgi_param SERVER_PORT $server_port; - uwsgi_param SERVER_NAME $server_name; - uwsgi_modifier1 9; - uwsgi_pass unix:////var/run/uwsgi.sock; + index index.html; + include uwsgi_params; + uwsgi_param SERVER_ADDR $server_addr; + uwsgi_modifier1 9; + uwsgi_pass unix:////var/run/luci-webui.socket; +} +location ~ /cgi-bin/cgi-(backup|download|upload|exec) { + include uwsgi_params; + uwsgi_param SERVER_ADDR $server_addr; + uwsgi_modifier1 9; + uwsgi_pass unix:////var/run/luci-cgi_io.socket; } location /luci-static { diff --git a/nginx/files/nginx.init b/nginx/files/nginx.init index 40d389719..528f390e1 100755 --- a/nginx/files/nginx.init +++ b/nginx/files/nginx.init @@ -6,6 +6,13 @@ START=80 USE_PROCD=1 start_service() { + [ -f /etc/init.d/uhttpd ] && { + /etc/init.d/uhttpd enabled && { + /etc/init.d/uhttpd stop + /etc/init.d/uhttpd disable + } + } + [ -d /var/log/nginx ] || mkdir -p /var/log/nginx [ -d /var/lib/nginx ] || mkdir -p /var/lib/nginx diff --git a/nginx/patches-dav-nginx/100-drop-libxslt-dep.patch b/nginx/patches-dav-nginx/100-drop-libxslt-dep.patch new file mode 100644 index 000000000..c5941f422 --- /dev/null +++ b/nginx/patches-dav-nginx/100-drop-libxslt-dep.patch @@ -0,0 +1,14 @@ +--- a/nginx-dav-ext-module/config 2018-12-17 08:45:12.000000000 +0000 ++++ b/nginx-dav-ext-module/config 2019-10-11 13:49:41.446919258 +0000 +@@ -8,9 +8,8 @@ ngx_module_name=ngx_http_dav_ext_module + # building nginx with the xslt module, in which case libxslt will + # be linked anyway. In other cases libxslt is just redundant. + # If that's a big deal, libxml2 can be linked directly: +-# ngx_module_libs=-lxml2 ++ngx_module_libs=-lxml2 ++ngx_module_incs=$(pkg-config --cflags-only-I libxml-2.0 | sed 's/^-I//') + +-ngx_module_libs=LIBXSLT +- + ngx_module_srcs="$ngx_addon_dir/ngx_http_dav_ext_module.c" + diff --git a/nginx/patches-lua-nginx/101-add-lua-ngx-pipe.patch b/nginx/patches-lua-nginx/101-add-lua-ngx-pipe.patch new file mode 100644 index 000000000..bbeb5dce5 --- /dev/null +++ b/nginx/patches-lua-nginx/101-add-lua-ngx-pipe.patch @@ -0,0 +1,579 @@ +Index: nginx-1.17.7/lua-nginx/config +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/config ++++ nginx-1.17.7/lua-nginx/config +@@ -362,6 +362,7 @@ HTTP_LUA_SRCS=" \ + $ngx_addon_dir/src/ngx_http_lua_ssl.c \ + $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \ + $ngx_addon_dir/src/ngx_http_lua_input_filters.c \ ++ $ngx_addon_dir/src/ngx_http_lua_pipe.c \ + " + + HTTP_LUA_DEPS=" \ +@@ -424,6 +425,7 @@ HTTP_LUA_DEPS=" \ + $ngx_addon_dir/src/ngx_http_lua_ssl.h \ + $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \ + $ngx_addon_dir/src/ngx_http_lua_input_filters.h \ ++ $ngx_addon_dir/src/ngx_http_lua_pipe.h \ + " + + CFLAGS="$CFLAGS -DNDK_SET_VAR" +@@ -508,6 +510,51 @@ ngx_feature_test="int rc = malloc_trim(( + SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" + CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS" + ++. auto/feature ++ ++CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" ++ ++# ---------------------------------------- ++ ++ngx_feature="pipe2" ++ngx_feature_libs= ++ngx_feature_name="NGX_HTTP_LUA_HAVE_PIPE2" ++ngx_feature_run=no ++ngx_feature_incs="#include " ++ngx_feature_test="int fd[2]; pipe2(fd, O_CLOEXEC|O_NONBLOCK);" ++SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" ++CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS" ++ ++. auto/feature ++ ++CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" ++ ++# ---------------------------------------- ++ ++ngx_feature="signalfd" ++ngx_feature_libs= ++ngx_feature_name="NGX_HTTP_LUA_HAVE_SIGNALFD" ++ngx_feature_run=no ++ngx_feature_incs="#include " ++ngx_feature_test="sigset_t set; signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC);" ++SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" ++CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS" ++ ++. auto/feature ++ ++CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" ++ ++# ---------------------------------------- ++ ++ngx_feature="execvpe" ++ngx_feature_libs= ++ngx_feature_name="NGX_HTTP_LUA_HAVE_EXECVPE" ++ngx_feature_run=no ++ngx_feature_incs= ++ngx_feature_test='char* argv[] = {"/bin/sh"};execvpe("/bin/sh", argv, NULL);' ++SAVED_CC_TEST_FLAGS="$CC_TEST_FLAGS" ++CC_TEST_FLAGS="-Werror -Wall $CC_TEST_FLAGS" ++ + . auto/feature + + CC_TEST_FLAGS="$SAVED_CC_TEST_FLAGS" +Index: nginx-1.17.7/lua-nginx/src/ngx_http_lua_initworkerby.c +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/src/ngx_http_lua_initworkerby.c ++++ nginx-1.17.7/lua-nginx/src/ngx_http_lua_initworkerby.c +@@ -12,6 +12,7 @@ + + #include "ngx_http_lua_initworkerby.h" + #include "ngx_http_lua_util.h" ++#include "ngx_http_lua_pipe.h" + + + static u_char *ngx_http_lua_log_init_worker_error(ngx_log_t *log, +@@ -65,6 +66,12 @@ ngx_http_lua_init_worker(ngx_cycle_t *cy + + return NGX_OK; + } ++ ++#ifdef HAVE_NGX_LUA_PIPE ++ if (ngx_http_lua_pipe_add_signal_handler(cycle) != NGX_OK) { ++ return NGX_ERROR; ++ } ++#endif + #endif /* NGX_WIN32 */ + + if (lmcf->init_worker_handler == NULL) { +Index: nginx-1.17.7/lua-nginx/src/ngx_http_lua_module.c +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/src/ngx_http_lua_module.c ++++ nginx-1.17.7/lua-nginx/src/ngx_http_lua_module.c +@@ -29,6 +29,7 @@ + #include "ngx_http_lua_ssl_session_storeby.h" + #include "ngx_http_lua_ssl_session_fetchby.h" + #include "ngx_http_lua_headers.h" ++#include "ngx_http_lua_pipe.h" + + + static void *ngx_http_lua_create_main_conf(ngx_conf_t *cf); +@@ -734,6 +735,10 @@ ngx_http_lua_init(ngx_conf_t *cf) + cln->handler = ngx_http_lua_sema_mm_cleanup; + #endif + ++#ifdef HAVE_NGX_LUA_PIPE ++ ngx_http_lua_pipe_init(); ++#endif ++ + #if nginx_version >= 1011011 + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { +Index: nginx-1.17.7/lua-nginx/src/ngx_http_lua_util.h +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/src/ngx_http_lua_util.h ++++ nginx-1.17.7/lua-nginx/src/ngx_http_lua_util.h +@@ -30,6 +30,12 @@ typedef struct { + * user code cache table */ + extern char ngx_http_lua_code_cache_key; + ++#define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE \ ++ | NGX_HTTP_LUA_CONTEXT_ACCESS \ ++ | NGX_HTTP_LUA_CONTEXT_CONTENT \ ++ | NGX_HTTP_LUA_CONTEXT_TIMER \ ++ | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ ++ | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) + + /* key in Lua vm registry for all the "ngx.ctx" tables */ + #define ngx_http_lua_ctx_tables_key "ngx_lua_ctx_tables" +Index: nginx-1.17.7/lua-nginx/src/ngx_http_lua_util.c +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/src/ngx_http_lua_util.c ++++ nginx-1.17.7/lua-nginx/src/ngx_http_lua_util.c +@@ -52,6 +52,7 @@ + #include "ngx_http_lua_ssl_certby.h" + #include "ngx_http_lua_ssl.h" + #include "ngx_http_lua_log_ringbuf.h" ++#include "ngx_http_lua_pipe.h" + + + #if 1 +@@ -744,6 +745,9 @@ ngx_http_lua_inject_ngx_api(lua_State *L + ngx_http_lua_inject_timer_api(L); + ngx_http_lua_inject_config_api(L); + ngx_http_lua_inject_worker_api(L); ++#ifdef HAVE_NGX_LUA_PIPE ++ ngx_http_lua_inject_pipe_api(L); ++#endif + + ngx_http_lua_inject_misc_api(L); + +Index: nginx-1.17.7/lua-nginx/src/ngx_http_lua_pipe.c +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/src/ngx_http_lua_pipe.c ++++ nginx-1.17.7/lua-nginx/src/ngx_http_lua_pipe.c +@@ -83,6 +83,7 @@ static void ngx_http_lua_pipe_proc_wait_ + static ngx_rbtree_t ngx_http_lua_pipe_rbtree; + static ngx_rbtree_node_t ngx_http_lua_pipe_proc_sentinel; + ++static char ngx_http_lua_proc_metatable_key; + + #if (NGX_HTTP_LUA_HAVE_SIGNALFD) + static int ngx_http_lua_signalfd; +@@ -418,6 +419,8 @@ ngx_http_lua_pipe_sigchld_event_handler( + */ + ngx_post_event((&pipe_node->wait_co_ctx->sleep), + &ngx_posted_events); ++ } else { ++ ngx_http_lua_pipe_proc_finalize(pipe_node->proc); + } + + pipe_node->proc->pipe->dead = 1; +@@ -2090,7 +2093,31 @@ ngx_http_lua_pipe_read_retval_helper(ngx + + rc = ngx_http_lua_pipe_read(pipe, pipe_ctx); + if (rc != NGX_AGAIN) { +- return 0; ++ size_t buf_size = 4096; ++ ++ while (1) { ++ u_char *buf, *p; ++ ++ buf = ngx_pcalloc(pipe->pool, buf_size); ++ if (!buf) { ++ lua_pushnil(L); ++ lua_pushliteral(L, "no memory"); ++ return 2; ++ } ++ ++ p = buf; ++ ++ ngx_http_lua_pipe_put_data(pipe, pipe_ctx, &p, &buf_size); ++ if (!p) { ++ ngx_pfree(pipe->pool, buf); ++ continue; ++ } ++ ++ lua_pushlstring(L, (char *)buf, buf_size); ++ ngx_pfree(pipe->pool, buf); ++ break; ++ } ++ return 1; + } + + rev = pipe_ctx->c->read; +@@ -2493,6 +2520,353 @@ ngx_http_lua_pipe_proc_wait_cleanup(void + wait_co_ctx->cleanup = NULL; + } + ++static int lua_table_array_cnt(lua_State *L, int idx) ++{ ++ int n = 0; ++ ++ if (idx < 0) ++ idx = lua_gettop(L) + idx + 1; ++ ++ if (!lua_istable(L, idx)) ++ return 0; ++ ++ lua_pushnil(L); /* stack: table key */ ++ ++ while (lua_next(L, idx)) { /* stack: table key value */ ++ if (lua_type(L, -2) == LUA_TNUMBER) { ++ lua_Number idx = lua_tonumber(L, -2); ++ if (floor(idx) != idx || idx != n + 1) ++ goto non_array; ++ n++; ++ lua_pop(L, 1); /* stack: table key */ ++ continue; ++ } ++non_array: ++ lua_pop(L, 2); ++ break; ++ } ++ ++ return n; ++} ++ ++static void ngx_http_lua_ngx_pipe_set_opt(lua_State *L, int idx, const char *name, int *val) ++{ ++ lua_getfield(L, idx, name); ++ if (!lua_isnil(L, -1)) ++ *val = lua_tointeger(L, -1); ++ lua_pop(L, 1); ++} ++ ++static int ngx_http_lua_ngx_pipe_spawn(lua_State *L) ++{ ++ ngx_http_lua_ffi_pipe_proc_t *proc; ++ int merge_stderr = 0; ++ int buffer_size = 4096; ++ int write_timeout = 10000; ++ int stdout_read_timeout = 10000; ++ int stderr_read_timeout = 10000; ++ int wait_timeout = 10000; ++ const char **args = NULL, **envs = NULL; ++ u_char errbuf[512] = ""; ++ size_t errbuf_size = sizeof(errbuf); ++ ngx_http_request_t *r; ++ int rc = 1; ++ ++ r = ngx_http_lua_get_req(L); ++ if (!r) ++ return luaL_error(L, "no request found"); ++ ++ if (lua_istable(L, 1)) { ++ int nargs = lua_table_array_cnt(L, 1); ++ ++ if (nargs == 0) ++ return luaL_error(L, "bad args arg: non-empty table expected"); ++ ++ args = ngx_pcalloc(r->pool, sizeof(char *) * (nargs + 1)); ++ ++ for (int i = 0; i < nargs; i++) { ++ lua_rawgeti(L, 1, i + 1); ++ args[i] = lua_tostring(L, -1); ++ lua_pop(L, 1); ++ } ++ } else if (lua_isstring(L, 1)) { ++ args = ngx_pcalloc(r->pool, sizeof(char *) * 4); ++ args[0] = "/bin/sh"; ++ args[1] = "-c"; ++ args[2] = lua_tostring(L, 1); ++ } else { ++ return luaL_error(L, "bad args arg: table expected, got '%s'", lua_typename(L, lua_type(L, 1))); ++ } ++ ++ if (lua_istable(L, 2)) { ++ lua_getfield(L, 2, "merge_stderr"); ++ merge_stderr = lua_toboolean(L, -1); ++ lua_pop(L, 1); ++ ++ ngx_http_lua_ngx_pipe_set_opt(L, 2, "buffer_size", &buffer_size); ++ ngx_http_lua_ngx_pipe_set_opt(L, 2, "write_timeout", &write_timeout); ++ ngx_http_lua_ngx_pipe_set_opt(L, 2, "stdout_read_timeout", &stdout_read_timeout); ++ ngx_http_lua_ngx_pipe_set_opt(L, 2, "stderr_read_timeout", &stderr_read_timeout); ++ ngx_http_lua_ngx_pipe_set_opt(L, 2, "wait_timeout", &wait_timeout); ++ ++ lua_getfield(L, 2, "environ"); ++ if (lua_istable(L, -1)) { ++ int nenv = lua_table_array_cnt(L, -1); ++ ++ envs = ngx_pcalloc(r->pool, sizeof(char *) * (nenv + 1)); ++ ++ for (int i = 0; i < nenv; i++) { ++ lua_rawgeti(L, -1, i + 1); ++ envs[i] = lua_tostring(L, -1); ++ lua_pop(L, 1); ++ } ++ } ++ lua_pop(L, 1); ++ } ++ ++ proc = lua_newuserdata(L, sizeof(ngx_http_lua_ffi_pipe_proc_t)); ++ if (!proc) { ++ lua_pushnil(L); ++ lua_pushliteral(L, "no memory"); ++ rc = 2; ++ goto free_mem; ++ } ++ ++ proc->write_timeout = write_timeout; ++ proc->stdout_read_timeout = stdout_read_timeout; ++ proc->stderr_read_timeout = stderr_read_timeout; ++ proc->wait_timeout = wait_timeout; ++ ++ lua_pushlightuserdata(L, &ngx_http_lua_proc_metatable_key); ++ lua_rawget(L, LUA_REGISTRYINDEX); ++ lua_setmetatable(L, -2); ++ ++ rc = ngx_http_lua_ffi_pipe_spawn(proc, args[0], args, merge_stderr, buffer_size, envs, ++ errbuf, &errbuf_size); ++ if (rc != NGX_OK) { ++ lua_pushnil(L); ++ lua_pushlstring(L, (char *)errbuf, errbuf_size); ++ rc = 2; ++ } else { ++ rc = 1; ++ } ++ ++free_mem: ++ if (args) ++ ngx_pfree(r->pool, args); ++ if (envs) ++ ngx_pfree(r->pool, envs); ++ return rc; ++} ++ ++static int ngx_http_lua_proc_pid(lua_State *L) ++{ ++ ngx_http_lua_ffi_pipe_proc_t *proc = lua_touserdata(L, 1); ++ ++ lua_pushinteger(L, proc->_pid); ++ return 1; ++} ++ ++static int ngx_http_lua_proc_wait(lua_State *L) ++{ ++ ngx_http_lua_ffi_pipe_proc_t *proc = lua_touserdata(L, 1); ++ ngx_http_request_t *r; ++ char *reason; ++ int status; ++ u_char errbuf[128] = ""; ++ size_t errbuf_size = sizeof(errbuf); ++ int rc; ++ ++ r = ngx_http_lua_get_req(L); ++ if (!r) ++ return luaL_error(L, "no request found"); ++ ++ rc = ngx_http_lua_ffi_pipe_proc_wait(r, proc, &reason, &status, errbuf, &errbuf_size); ++ switch (rc) { ++ case NGX_OK: ++ lua_pushboolean(L, 1); ++ lua_pushstring(L, reason); ++ lua_pushinteger(L, status); ++ return 3; ++ case NGX_DECLINED: ++ lua_pushboolean(L, 0); ++ lua_pushstring(L, reason); ++ lua_pushinteger(L, status); ++ return 3; ++ case NGX_ERROR: ++ lua_pushnil(L); ++ lua_pushlstring(L, (char *)errbuf, errbuf_size); ++ return 2; ++ default: ++ return lua_yield(L, 0); ++ } ++} ++ ++static int ngx_http_lua_proc_kill(lua_State *L) ++{ ++ ngx_http_lua_ffi_pipe_proc_t *proc = lua_touserdata(L, 1); ++ u_char errbuf[128] = ""; ++ size_t errbuf_size = sizeof(errbuf); ++ int signal; ++ int rc; ++ ++ signal = luaL_checkinteger(L, 2); ++ ++ rc = ngx_http_lua_ffi_pipe_proc_kill(proc, signal, errbuf, &errbuf_size); ++ if (rc == NGX_OK) { ++ lua_pushnil(L); ++ lua_pushlstring(L, (char *)errbuf, errbuf_size); ++ return 2; ++ } ++ ++ lua_pushboolean(L, 1); ++ return 1; ++} ++ ++static int ngx_http_lua_proc_read(lua_State *L, int from_stderr, int reader_type) ++{ ++ ngx_http_lua_ffi_pipe_proc_t *proc = lua_touserdata(L, 1); ++ ssize_t len = lua_tointeger(L, 2); ++ ngx_http_lua_pipe_ctx_t *pipe_ctx; ++ ngx_http_request_t *r; ++ u_char *buf, *p; ++ size_t buf_size = 4096; ++ u_char errbuf[128] = ""; ++ size_t errbuf_size = sizeof(errbuf); ++ int rc; ++ ++ switch (reader_type) { ++ case PIPE_READ_BYTES: ++ if (len <= 0) { ++ if (len < 0) ++ return luaL_error(L, "bad len argument"); ++ lua_pushliteral(L, ""); ++ return 1; ++ } ++ break; ++ case PIPE_READ_ANY: ++ if (len <= 0) ++ return luaL_error(L, "bad max argument"); ++ break; ++ default: ++ len = 0; ++ break; ++ } ++ ++ r = ngx_http_lua_get_req(L); ++ if (!r) ++ return luaL_error(L, "no request found"); ++ ++ buf = ngx_pcalloc(proc->pipe->pool, buf_size); ++ p = buf; ++ ++ if (!p) { ++ lua_pushnil(L); ++ lua_pushliteral(L, "no memory"); ++ return 2; ++ } ++ ++ rc = ngx_http_lua_ffi_pipe_proc_read(r, proc, from_stderr, reader_type, len, &p, ++ &buf_size, errbuf, ++ &errbuf_size); ++ if (rc == NGX_OK || rc == NGX_DECLINED) { ++ if (!p) { ++ if (from_stderr) ++ pipe_ctx = proc->pipe->stderr_ctx; ++ else ++ pipe_ctx = proc->pipe->stdout_ctx; ++ ++ ngx_pfree(proc->pipe->pool, buf); ++ ++ buf = ngx_pcalloc(proc->pipe->pool, buf_size); ++ if (!buf) { ++ lua_pushnil(L); ++ lua_pushliteral(L, "no memory"); ++ return 2; ++ } ++ ngx_http_lua_pipe_put_data(proc->pipe, pipe_ctx, &buf, &buf_size); ++ } ++ ++ if (rc == NGX_OK) { ++ lua_pushlstring(L, (char *)buf, buf_size); ++ ngx_pfree(proc->pipe->pool, buf); ++ return 1; ++ } ++ ++ lua_pushnil(L); ++ lua_pushlstring(L, (char *)errbuf, errbuf_size); ++ lua_pushlstring(L, (char *)buf, buf_size); ++ ngx_pfree(proc->pipe->pool, buf); ++ return 3; ++ } ++ ++ if (rc == NGX_ERROR) { ++ lua_pushnil(L); ++ lua_pushlstring(L, (char *)errbuf, errbuf_size); ++ ngx_pfree(proc->pipe->pool, buf); ++ return 2; ++ } ++ ++ ngx_pfree(proc->pipe->pool, buf); ++ ++ return lua_yield(L, 0); ++} ++ ++#define NGX_LUA_PIPE_DEF_READ_FUN(name, from_stderr, reader_type) \ ++ static int ngx_http_lua_proc_##name(lua_State *L) \ ++ { \ ++ return ngx_http_lua_proc_read(L, from_stderr, reader_type); \ ++ } ++ ++NGX_LUA_PIPE_DEF_READ_FUN(stdout_read_all, 0, PIPE_READ_ALL) ++NGX_LUA_PIPE_DEF_READ_FUN(stdout_read_bytes, 0, PIPE_READ_BYTES) ++NGX_LUA_PIPE_DEF_READ_FUN(stdout_read_line, 0, PIPE_READ_LINE) ++NGX_LUA_PIPE_DEF_READ_FUN(stdout_read_any, 0, PIPE_READ_ANY) ++ ++NGX_LUA_PIPE_DEF_READ_FUN(stderr_read_all, 1, PIPE_READ_ALL) ++NGX_LUA_PIPE_DEF_READ_FUN(stderr_read_bytes, 1, PIPE_READ_BYTES) ++NGX_LUA_PIPE_DEF_READ_FUN(stderr_read_line, 1, PIPE_READ_LINE) ++NGX_LUA_PIPE_DEF_READ_FUN(stderr_read_any, 1, PIPE_READ_ANY) ++ ++#define NGX_LUA_PIPE_ADD_FUN(name) \ ++ do { \ ++ lua_pushcfunction(L, ngx_http_lua_proc_##name); \ ++ lua_setfield(L, -2, #name); \ ++ } while (0) ++ ++void ngx_http_lua_inject_pipe_api(lua_State *L) ++{ ++ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* ngx.pipe. */ ++ ++ lua_pushcfunction(L, ngx_http_lua_ngx_pipe_spawn); ++ lua_setfield(L, -2, "spawn"); ++ ++ lua_setfield(L, -2, "pipe"); ++ ++ /* {{{proc object metatable */ ++ lua_pushlightuserdata(L, &ngx_http_lua_proc_metatable_key); ++ ++ lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* mt */ ++ ++ lua_createtable(L, 0 /* narr */, 11 /* nrec */); /* __index */ ++ ++ NGX_LUA_PIPE_ADD_FUN(pid); ++ NGX_LUA_PIPE_ADD_FUN(wait); ++ NGX_LUA_PIPE_ADD_FUN(kill); ++ NGX_LUA_PIPE_ADD_FUN(stdout_read_all); ++ NGX_LUA_PIPE_ADD_FUN(stdout_read_bytes); ++ NGX_LUA_PIPE_ADD_FUN(stdout_read_line); ++ NGX_LUA_PIPE_ADD_FUN(stdout_read_any); ++ NGX_LUA_PIPE_ADD_FUN(stderr_read_all); ++ NGX_LUA_PIPE_ADD_FUN(stderr_read_bytes); ++ NGX_LUA_PIPE_ADD_FUN(stderr_read_line); ++ NGX_LUA_PIPE_ADD_FUN(stderr_read_any); ++ ++ lua_setfield(L, -2, "__index"); ++ ++ lua_rawset(L, LUA_REGISTRYINDEX); ++ /* }}} */ ++} + + #endif /* HAVE_NGX_LUA_PIPE */ + +Index: nginx-1.17.7/lua-nginx/src/ngx_http_lua_pipe.h +=================================================================== +--- nginx-1.17.7.orig/lua-nginx/src/ngx_http_lua_pipe.h ++++ nginx-1.17.7/lua-nginx/src/ngx_http_lua_pipe.h +@@ -86,6 +86,7 @@ typedef struct { + + void ngx_http_lua_pipe_init(void); + ngx_int_t ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle); ++void ngx_http_lua_inject_pipe_api(lua_State *L); + #endif + + diff --git a/nginx/patches/201-ignore-invalid-options.patch b/nginx/patches/201-ignore-invalid-options.patch index 28be2fb71..88b521353 100755 --- a/nginx/patches/201-ignore-invalid-options.patch +++ b/nginx/patches/201-ignore-invalid-options.patch @@ -1,6 +1,6 @@ --- a/auto/options +++ b/auto/options -@@ -397,8 +397,7 @@ $0: warning: the \"--with-sha1-asm\" opt +@@ -396,8 +396,7 @@ $0: warning: the \"--with-sha1-asm\" opt --test-build-solaris-sendfilev) NGX_TEST_BUILD_SOLARIS_SENDFILEV=YES ;; *) diff --git a/nginx/patches/400-socket-cloexec.patch b/nginx/patches/400-socket-cloexec.patch new file mode 100644 index 000000000..96e48b605 --- /dev/null +++ b/nginx/patches/400-socket-cloexec.patch @@ -0,0 +1,174 @@ +Index: nginx-1.17.7/auto/unix +=================================================================== +--- nginx-1.17.7.orig/auto/unix ++++ nginx-1.17.7/auto/unix +@@ -1037,3 +1037,27 @@ ngx_feature_test='struct addrinfo *res; + if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 1; + freeaddrinfo(res)' + . auto/feature ++ ++ngx_feature="SOCK_CLOEXEC support" ++ngx_feature_name="NGX_HAVE_SOCKET_CLOEXEC" ++ngx_feature_run=no ++ngx_feature_incs="#include ++ #include " ++ngx_feature_path= ++ngx_feature_libs= ++ngx_feature_test="int fd; ++ fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);" ++. auto/feature ++ ++ngx_feature="FD_CLOEXEC support" ++ngx_feature_name="NGX_HAVE_FD_CLOEXEC" ++ngx_feature_run=no ++ngx_feature_incs="#include ++ #include ++ #include " ++ngx_feature_path= ++ngx_feature_libs= ++ngx_feature_test="int fd; ++ fd = socket(AF_INET, SOCK_STREAM, 0); ++ fcntl(fd, F_SETFD, FD_CLOEXEC);" ++. auto/feature +Index: nginx-1.17.7/src/core/ngx_resolver.c +=================================================================== +--- nginx-1.17.7.orig/src/core/ngx_resolver.c ++++ nginx-1.17.7/src/core/ngx_resolver.c +@@ -4492,7 +4492,11 @@ ngx_tcp_connect(ngx_resolver_connection_ + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ++#if (NGX_HAVE_SOCKET_CLOEXEC) ++ s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0); ++#else + s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0); ++#endif + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s); + +@@ -4520,6 +4524,15 @@ ngx_tcp_connect(ngx_resolver_connection_ + goto failed; + } + ++#if (NGX_HAVE_FD_CLOEXEC) ++ if (ngx_cloexec(s) == -1) { ++ ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, ++ ngx_cloexec_n " failed"); ++ ++ goto failed; ++ } ++#endif ++ + rev = c->read; + wev = c->write; + +Index: nginx-1.17.7/src/event/ngx_event.h +=================================================================== +--- nginx-1.17.7.orig/src/event/ngx_event.h ++++ nginx-1.17.7/src/event/ngx_event.h +@@ -73,6 +73,9 @@ struct ngx_event_s { + /* to test on worker exit */ + unsigned channel:1; + unsigned resolver:1; ++#if (HAVE_SOCKET_CLOEXEC_PATCH) ++ unsigned skip_socket_leak_check:1; ++#endif + + unsigned cancelable:1; + +Index: nginx-1.17.7/src/event/ngx_event_accept.c +=================================================================== +--- nginx-1.17.7.orig/src/event/ngx_event_accept.c ++++ nginx-1.17.7/src/event/ngx_event_accept.c +@@ -57,7 +57,7 @@ ngx_event_accept(ngx_event_t *ev) + + #if (NGX_HAVE_ACCEPT4) + if (use_accept4) { +- s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK); ++ s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK | SOCK_CLOEXEC); + } else { + s = accept(lc->fd, &sa.sockaddr, &socklen); + } +@@ -197,6 +197,14 @@ ngx_event_accept(ngx_event_t *ev) + ngx_close_accepted_connection(c); + return; + } ++#if (NGX_HAVE_FD_CLOEXEC) ++ if (ngx_cloexec(s) == -1) { ++ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno, ++ ngx_cloexec_n " failed"); ++ ngx_close_accepted_connection(c); ++ return; ++ } ++#endif + } + } + +Index: nginx-1.17.7/src/event/ngx_event_connect.c +=================================================================== +--- nginx-1.17.7.orig/src/event/ngx_event_connect.c ++++ nginx-1.17.7/src/event/ngx_event_connect.c +@@ -38,7 +38,11 @@ ngx_event_connect_peer(ngx_peer_connecti + + type = (pc->type ? pc->type : SOCK_STREAM); + ++#if (NGX_HAVE_SOCKET_CLOEXEC) ++ s = ngx_socket(pc->sockaddr->sa_family, type | SOCK_CLOEXEC, 0); ++#else + s = ngx_socket(pc->sockaddr->sa_family, type, 0); ++#endif + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d", + (type == SOCK_STREAM) ? "stream" : "dgram", s); +@@ -92,6 +96,15 @@ ngx_event_connect_peer(ngx_peer_connecti + goto failed; + } + ++#if (NGX_HAVE_FD_CLOEXEC) ++ if (ngx_cloexec(s) == -1) { ++ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, ++ ngx_cloexec_n " failed"); ++ ++ goto failed; ++ } ++#endif ++ + if (pc->local) { + + #if (NGX_HAVE_TRANSPARENT_PROXY) +Index: nginx-1.17.7/src/os/unix/ngx_process_cycle.c +=================================================================== +--- nginx-1.17.7.orig/src/os/unix/ngx_process_cycle.c ++++ nginx-1.17.7/src/os/unix/ngx_process_cycle.c +@@ -991,6 +991,9 @@ ngx_worker_process_exit(ngx_cycle_t *cyc + for (i = 0; i < cycle->connection_n; i++) { + if (c[i].fd != -1 + && c[i].read ++#if (HAVE_SOCKET_CLOEXEC_PATCH) ++ && !c[i].read->skip_socket_leak_check ++#endif + && !c[i].read->accept + && !c[i].read->channel + && !c[i].read->resolver) +Index: nginx-1.17.7/src/os/unix/ngx_socket.h +=================================================================== +--- nginx-1.17.7.orig/src/os/unix/ngx_socket.h ++++ nginx-1.17.7/src/os/unix/ngx_socket.h +@@ -45,6 +45,17 @@ int ngx_blocking(ngx_socket_t s); + + #endif + ++#if (NGX_HAVE_FD_CLOEXEC) ++ ++#define ngx_cloexec(s) fcntl(s, F_SETFD, FD_CLOEXEC) ++#define ngx_cloexec_n "fcntl(FD_CLOEXEC)" ++ ++/* at least FD_CLOEXEC is required to ensure connection fd is closed ++ * after execve */ ++#define HAVE_SOCKET_CLOEXEC_PATCH 1 ++ ++#endif ++ + int ngx_tcp_nopush(ngx_socket_t s); + int ngx_tcp_push(ngx_socket_t s); + diff --git a/nginx/src/lua-nginx/src/ngx_http_lua_pipe.c b/nginx/src/lua-nginx/src/ngx_http_lua_pipe.c new file mode 100644 index 000000000..9ea0ba1c4 --- /dev/null +++ b/nginx/src/lua-nginx/src/ngx_http_lua_pipe.c @@ -0,0 +1,2499 @@ + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_common.h" +#include "ngx_http_lua_input_filters.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_lua_pipe.h" +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) +#include +#endif + + +#ifdef HAVE_NGX_LUA_PIPE +static ngx_rbtree_node_t *ngx_http_lua_pipe_lookup_pid(ngx_rbtree_key_t key); +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) +static void ngx_http_lua_pipe_sigchld_handler(int signo, siginfo_t *siginfo, + void *ucontext); +#endif +static void ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev); +static ssize_t ngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf, + size_t size); +static ssize_t ngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf, + size_t size); +static void ngx_http_lua_pipe_close_helper(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, ngx_event_t *ev); +static void ngx_http_lua_pipe_close_stdin(ngx_http_lua_pipe_t *pipe); +static void ngx_http_lua_pipe_close_stdout(ngx_http_lua_pipe_t *pipe); +static void ngx_http_lua_pipe_close_stderr(ngx_http_lua_pipe_t *pipe); +static void ngx_http_lua_pipe_proc_finalize(ngx_http_lua_ffi_pipe_proc_t *proc); +static ngx_int_t ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r, + ngx_http_lua_ctx_t **ctx, u_char *errbuf, size_t *errbuf_size); +static void ngx_http_lua_pipe_put_error(ngx_http_lua_pipe_ctx_t *pipe_ctx, + u_char *errbuf, size_t *errbuf_size); +static void ngx_http_lua_pipe_put_data(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char **buf, size_t *buf_size); +static ngx_int_t ngx_http_lua_pipe_add_input_buffer(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx); +static ngx_int_t ngx_http_lua_pipe_read_all(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read_bytes(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read_line(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read_any(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx); +static ngx_int_t ngx_http_lua_pipe_init_ctx( + ngx_http_lua_pipe_ctx_t **pipe_ctx_pt, int fd, ngx_pool_t *pool, + u_char *errbuf, size_t *errbuf_size); +static ngx_int_t ngx_http_lua_pipe_write(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx); +static int ngx_http_lua_pipe_read_stdout_retval( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L); +static int ngx_http_lua_pipe_read_stderr_retval( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L); +static int ngx_http_lua_pipe_read_retval_helper( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L, int from_stderr); +static int ngx_http_lua_pipe_write_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L); +static int ngx_http_lua_pipe_wait_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L); +static void ngx_http_lua_pipe_resume_helper(ngx_event_t *ev, + ngx_http_lua_co_ctx_t *wait_co_ctx); +static void ngx_http_lua_pipe_resume_read_stdout_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_resume_read_stderr_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_resume_write_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_resume_wait_handler(ngx_event_t *ev); +static ngx_int_t ngx_http_lua_pipe_resume(ngx_http_request_t *r); +static void ngx_http_lua_pipe_dummy_event_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_clear_event(ngx_event_t *ev); +static void ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data); +static void ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data); +static void ngx_http_lua_pipe_proc_write_cleanup(void *data); +static void ngx_http_lua_pipe_proc_wait_cleanup(void *data); + + +static ngx_rbtree_t ngx_http_lua_pipe_rbtree; +static ngx_rbtree_node_t ngx_http_lua_pipe_proc_sentinel; + + +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) +static int ngx_http_lua_signalfd; +static struct signalfd_siginfo ngx_http_lua_pipe_notification; + +#define ngx_http_lua_read_sigfd ngx_http_lua_signalfd + +#else +static int ngx_http_lua_sigchldfd[2]; +static u_char ngx_http_lua_pipe_notification[1]; + +#define ngx_http_lua_read_sigfd ngx_http_lua_sigchldfd[0] +#define ngx_http_lua_write_sigfd ngx_http_lua_sigchldfd[1] +#endif + + +static ngx_connection_t *ngx_http_lua_sigfd_conn = NULL; + + +/* The below signals are ignored by Nginx. + * We need to reset them for the spawned child processes. */ +ngx_http_lua_pipe_signal_t ngx_signals[] = { + { SIGSYS, "SIGSYS" }, + { SIGPIPE, "SIGPIPE" }, + { 0, NULL } +}; + + +enum { + PIPE_ERR_CLOSED = 1, + PIPE_ERR_SYSCALL, + PIPE_ERR_NOMEM, + PIPE_ERR_TIMEOUT, + PIPE_ERR_ADD_READ_EV, + PIPE_ERR_ADD_WRITE_EV, + PIPE_ERR_ABORTED, +}; + + +enum { + PIPE_READ_ALL = 0, + PIPE_READ_BYTES, + PIPE_READ_LINE, + PIPE_READ_ANY, +}; + + +#define REASON_EXIT "exit" +#define REASON_SIGNAL "signal" +#define REASON_UNKNOWN "unknown" + +#define REASON_RUNNING_CODE 0 +#define REASON_EXIT_CODE 1 +#define REASON_SIGNAL_CODE 2 +#define REASON_UNKNOWN_CODE 3 + + +void +ngx_http_lua_pipe_init(void) +{ + ngx_rbtree_init(&ngx_http_lua_pipe_rbtree, + &ngx_http_lua_pipe_proc_sentinel, ngx_rbtree_insert_value); +} + + +ngx_int_t +ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle) +{ + ngx_event_t *rev; +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) + sigset_t set; + +#else + int rc; + struct sigaction sa; +#endif + +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) + if (sigemptyset(&set) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe init signal set failed"); + return NGX_ERROR; + } + + if (sigaddset(&set, SIGCHLD) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe add SIGCHLD to signal set failed"); + return NGX_ERROR; + } + + if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe block SIGCHLD failed"); + return NGX_ERROR; + } + + ngx_http_lua_signalfd = signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC); + if (ngx_http_lua_signalfd < 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe create signalfd instance failed"); + return NGX_ERROR; + } + +#else /* !(NGX_HTTP_LUA_HAVE_SIGNALFD) */ +# if (NGX_HTTP_LUA_HAVE_PIPE2) + rc = pipe2(ngx_http_lua_sigchldfd, O_NONBLOCK|O_CLOEXEC); +# else + rc = pipe(ngx_http_lua_sigchldfd); +# endif + + if (rc == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe init SIGCHLD fd failed"); + return NGX_ERROR; + } + +# if !(NGX_HTTP_LUA_HAVE_PIPE2) + if (ngx_nonblocking(ngx_http_lua_read_sigfd) == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, "lua pipe " + ngx_nonblocking_n " SIGCHLD read fd failed"); + goto failed; + } + + if (ngx_nonblocking(ngx_http_lua_write_sigfd) == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, "lua pipe " + ngx_nonblocking_n " SIGCHLD write fd failed"); + goto failed; + } + + /* it's ok not to set the pipe fd with O_CLOEXEC. This requires + * extra syscall */ +# endif /* !(NGX_HTTP_LUA_HAVE_PIPE2) */ +#endif /* NGX_HTTP_LUA_HAVE_SIGNALFD */ + + ngx_http_lua_sigfd_conn = ngx_get_connection(ngx_http_lua_read_sigfd, + cycle->log); + if (ngx_http_lua_sigfd_conn == NULL) { + goto failed; + } + + ngx_http_lua_sigfd_conn->log = cycle->log; + ngx_http_lua_sigfd_conn->recv = ngx_http_lua_pipe_fd_read; + rev = ngx_http_lua_sigfd_conn->read; + rev->log = ngx_http_lua_sigfd_conn->log; + rev->handler = ngx_http_lua_pipe_sigchld_event_handler; + +#ifdef HAVE_SOCKET_CLOEXEC_PATCH + rev->skip_socket_leak_check = 1; +#endif + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + goto failed; + } + +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_sigaction = ngx_http_lua_pipe_sigchld_handler; + sa.sa_flags = SA_SIGINFO; + + if (sigemptyset(&sa.sa_mask) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe init signal mask failed"); + goto failed; + } + + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe sigaction(SIGCHLD) failed"); + goto failed; + } +#endif + + return NGX_OK; + +failed: + + if (ngx_http_lua_sigfd_conn != NULL) { + ngx_close_connection(ngx_http_lua_sigfd_conn); + ngx_http_lua_sigfd_conn = NULL; + } + + if (close(ngx_http_lua_read_sigfd) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "lua pipe close the read sigfd failed"); + } + +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) + if (close(ngx_http_lua_write_sigfd) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "lua pipe close the write sigfd failed"); + } +#endif + + return NGX_ERROR; +} + + +static ngx_rbtree_node_t * +ngx_http_lua_pipe_lookup_pid(ngx_rbtree_key_t key) +{ + ngx_rbtree_node_t *node, *sentinel; + + node = ngx_http_lua_pipe_rbtree.root; + sentinel = ngx_http_lua_pipe_rbtree.sentinel; + + while (node != sentinel) { + if (key < node->key) { + node = node->left; + continue; + } + + if (key > node->key) { + node = node->right; + continue; + } + + return node; + } + + return NULL; +} + + +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) +static void +ngx_http_lua_pipe_sigchld_handler(int signo, siginfo_t *siginfo, + void *ucontext) +{ + ngx_err_t err, saved_err; + ngx_int_t n; + + saved_err = ngx_errno; + + for ( ;; ) { + n = write(ngx_http_lua_write_sigfd, ngx_http_lua_pipe_notification, + sizeof(ngx_http_lua_pipe_notification)); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, + "lua pipe SIGCHLD fd write siginfo:%p", siginfo); + + if (n >= 0) { + break; + } + + err = ngx_errno; + + if (err != NGX_EINTR) { + if (err != NGX_EAGAIN) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, err, + "lua pipe SIGCHLD fd write failed"); + } + + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, err, + "lua pipe SIGCHLD fd write was interrupted"); + } + + ngx_set_errno(saved_err); +} +#endif + + +static void +ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev) +{ + int n; + int status; + ngx_pid_t pid; + ngx_connection_t *c = ev->data; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_node_t *pipe_node; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, + "lua pipe reaping children"); + + for ( ;; ) { +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) + n = c->recv(c, (u_char *) &ngx_http_lua_pipe_notification, +#else + n = c->recv(c, ngx_http_lua_pipe_notification, +#endif + sizeof(ngx_http_lua_pipe_notification)); + + if (n <= 0) { + if (n == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe SIGCHLD fd read failed"); + } + + break; + } + + for ( ;; ) { + pid = waitpid(-1, &status, WNOHANG); + + if (pid == 0) { + break; + } + + if (pid < 0) { + if (ngx_errno != NGX_ECHILD) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe waitpid failed"); + } + + break; + } + + /* This log is ported from Nginx's signal handler since we override + * or block it in this implementation. */ + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "signal %d (SIGCHLD) received from %P", + SIGCHLD, pid); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe SIGCHLD fd read pid:%P status:%d", pid, + status); + + node = ngx_http_lua_pipe_lookup_pid(pid); + if (node != NULL) { + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + if (pipe_node->wait_co_ctx != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe resume process:%p waiting for %P", + pipe_node->proc, pid); + + /* + * We need the extra parentheses around the first argument + * of ngx_post_event() just to work around macro issues in + * nginx cores older than 1.7.12 (exclusive). + */ + ngx_post_event((&pipe_node->wait_co_ctx->sleep), + &ngx_posted_events); + } + + pipe_node->proc->pipe->dead = 1; + + if (WIFSIGNALED(status)) { + pipe_node->status = WTERMSIG(status); + pipe_node->reason_code = REASON_SIGNAL_CODE; + + } else if (WIFEXITED(status)) { + pipe_node->status = WEXITSTATUS(status); + pipe_node->reason_code = REASON_EXIT_CODE; + + } else { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "lua pipe unknown exit status %d from " + "process %P", status, pid); + pipe_node->status = status; + pipe_node->reason_code = REASON_UNKNOWN_CODE; + } + } + } + } +} + + +static ssize_t +ngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *rev; + + rev = c->read; + + do { + n = read(c->fd, buf, size); + + err = ngx_errno; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "read: fd:%d %z of %uz", c->fd, n, size); + + if (n == 0) { + rev->ready = 0; + rev->eof = 1; + return 0; + } + + if (n > 0) { + if ((size_t) n < size + && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) + { + rev->ready = 0; + } + + return n; + } + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "read() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "read() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; +} + + +static ssize_t +ngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *wev; + + wev = c->write; + + do { + n = write(c->fd, buf, size); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write: fd:%d %z of %uz", c->fd, n, size); + + if (n >= 0) { + if ((size_t) n != size) { + wev->ready = 0; + } + + return n; + } + + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "write() not ready"); + n = NGX_AGAIN; + + } else if (err != NGX_EPIPE) { + n = ngx_connection_error(c, err, "write() failed"); + break; + } + + } while (err == NGX_EINTR); + + wev->ready = 0; + + if (n == NGX_ERROR) { + wev->error = 1; + } + + return n; +} + + +#if !(NGX_HTTP_LUA_HAVE_EXECVPE) +static int +ngx_http_lua_execvpe(const char *program, char * const argv[], + char * const envp[]) +{ + int rc; + char **saved = environ; + + environ = (char **) envp; + rc = execvp(program, argv); + environ = saved; + return rc; +} +#endif + + +int +ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, + const char *file, const char **argv, int merge_stderr, size_t buffer_size, + const char **environ, u_char *errbuf, size_t *errbuf_size) +{ + int rc; + int in[2]; + int out[2]; + int err[2]; + int stdin_fd, stdout_fd, stderr_fd; + int errlog_fd, temp_errlog_fd; + ngx_pid_t pid; + ssize_t pool_size; + ngx_pool_t *pool; + ngx_uint_t i; + ngx_listening_t *ls; + ngx_http_lua_pipe_t *pp; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_node_t *pipe_node; + struct sigaction sa; + ngx_http_lua_pipe_signal_t *sig; + sigset_t set; + + pool_size = ngx_align(NGX_MIN_POOL_SIZE + buffer_size * 2, + NGX_POOL_ALIGNMENT); + + pool = ngx_create_pool(pool_size, ngx_cycle->log); + if (pool == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + return NGX_ERROR; + } + + pp = ngx_pcalloc(pool, sizeof(ngx_http_lua_pipe_t) + + offsetof(ngx_rbtree_node_t, color) + + sizeof(ngx_http_lua_pipe_node_t)); + if (pp == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + goto free_pool; + } + + rc = pipe(in); + if (rc == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe failed: %s", + strerror(errno)) + - errbuf; + goto free_pool; + } + + rc = pipe(out); + if (rc == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe failed: %s", + strerror(errno)) + - errbuf; + goto close_in_fd; + } + + if (!merge_stderr) { + rc = pipe(err); + if (rc == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "pipe failed: %s", strerror(errno)) + - errbuf; + goto close_in_out_fd; + } + } + + pid = fork(); + if (pid == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "fork failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + if (pid == 0) { + +#if (NGX_HAVE_CPU_AFFINITY) + /* reset the CPU affinity mask */ + ngx_uint_t log_level; + ngx_cpuset_t child_cpu_affinity; + + if (ngx_process == NGX_PROCESS_WORKER + && ngx_get_cpu_affinity(ngx_worker) != NULL) + { + CPU_ZERO(&child_cpu_affinity); + + for (i = 0; i < (ngx_uint_t) ngx_min(ngx_ncpu, CPU_SETSIZE); i++) { + CPU_SET(i, &child_cpu_affinity); + } + + log_level = ngx_cycle->log->log_level; + ngx_cycle->log->log_level = NGX_LOG_WARN; + ngx_setaffinity(&child_cpu_affinity, ngx_cycle->log); + ngx_cycle->log->log_level = log_level; + } +#endif + + /* reset the handler of ignored signals to the default */ + for (sig = ngx_signals; sig->signo != 0; sig++) { + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_handler = SIG_DFL; + + if (sigemptyset(&sa.sa_mask) != 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child init signal mask failed"); + exit(EXIT_FAILURE); + } + + if (sigaction(sig->signo, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child reset signal handler for %s " + "failed", sig->signame); + exit(EXIT_FAILURE); + } + } + + /* reset signal mask */ + if (sigemptyset(&set) != 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child init signal set failed"); + exit(EXIT_FAILURE); + } + + if (sigprocmask(SIG_SETMASK, &set, NULL) != 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child reset signal mask failed"); + exit(EXIT_FAILURE); + } + + /* close listening socket fd */ + ls = ngx_cycle->listening.elts; + for (i = 0; i < ngx_cycle->listening.nelts; i++) { + if (ls[i].fd != (ngx_socket_t) -1 && + ngx_close_socket(ls[i].fd) == -1) + { + ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, ngx_socket_errno, + "lua pipe child " ngx_close_socket_n + " %V failed", &ls[i].addr_text); + } + } + + /* close and dup pipefd */ + if (close(in[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe child failed to close the in[1] " + "pipe fd"); + } + + if (close(out[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe child failed to close the out[0] " + "pipe fd"); + } + + if (ngx_cycle->log->file && ngx_cycle->log->file->fd == STDERR_FILENO) { + errlog_fd = ngx_cycle->log->file->fd; + temp_errlog_fd = dup(errlog_fd); + + if (temp_errlog_fd == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup errlog fd failed"); + exit(EXIT_FAILURE); + } + + if (ngx_cloexec(temp_errlog_fd) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child new errlog fd " ngx_cloexec_n + " failed"); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe child dup old errlog fd %d to new fd %d", + ngx_cycle->log->file->fd, temp_errlog_fd); + + ngx_cycle->log->file->fd = temp_errlog_fd; + } + + if (dup2(in[0], STDIN_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stdin failed"); + exit(EXIT_FAILURE); + } + + if (dup2(out[1], STDOUT_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stdout failed"); + exit(EXIT_FAILURE); + } + + if (merge_stderr) { + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stderr failed"); + exit(EXIT_FAILURE); + } + + } else { + if (close(err[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe child failed to close the err[0] " + "pipe fd"); + } + + if (dup2(err[1], STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stderr failed"); + exit(EXIT_FAILURE); + } + } + + if (environ != NULL) { +#if (NGX_HTTP_LUA_HAVE_EXECVPE) + if (execvpe(file, (char * const *) argv, (char * const *) environ) +#else + if (ngx_http_lua_execvpe(file, (char * const *) argv, + (char * const *) environ) +#endif + == -1) + { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child execvpe() failed while " + "executing %s", file); + } + + } else { + if (execvp(file, (char * const *) argv) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child execvp() failed while " + "executing %s", file); + } + } + + exit(EXIT_FAILURE); + } + + /* parent process */ + if (close(in[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe: failed to close the in[0] pipe fd"); + } + + stdin_fd = in[1]; + + if (ngx_nonblocking(stdin_fd) == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + ngx_nonblocking_n " failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + pp->stdin_fd = stdin_fd; + + if (close(out[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe: failed to close the out[1] pipe fd"); + } + + stdout_fd = out[0]; + + if (ngx_nonblocking(stdout_fd) == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + ngx_nonblocking_n " failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + pp->stdout_fd = stdout_fd; + + if (!merge_stderr) { + if (close(err[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe: failed to close the err[1] pipe fd"); + } + + stderr_fd = err[0]; + + if (ngx_nonblocking(stderr_fd) == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + ngx_nonblocking_n " failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + pp->stderr_fd = stderr_fd; + } + + node = (ngx_rbtree_node_t *) (pp + 1); + node->key = pid; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + pipe_node->proc = proc; + ngx_rbtree_insert(&ngx_http_lua_pipe_rbtree, node); + + pp->node = node; + pp->pool = pool; + pp->merge_stderr = merge_stderr; + pp->buffer_size = buffer_size; + + proc->_pid = pid; + proc->pipe = pp; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe spawn process:%p pid:%P merge_stderr:%d " + "buffer_size:%uz", proc, pid, merge_stderr, buffer_size); + return NGX_OK; + +close_in_out_err_fd: + + if (!merge_stderr) { + if (close(err[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the err[0] pipe fd"); + } + + if (close(err[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the err[1] pipe fd"); + } + } + +close_in_out_fd: + + if (close(out[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the out[0] pipe fd"); + } + + if (close(out[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the out[1] pipe fd"); + } + +close_in_fd: + + if (close(in[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the in[0] pipe fd"); + } + + if (close(in[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the in[1] pipe fd"); + } + +free_pool: + + ngx_destroy_pool(pool); + return NGX_ERROR; +} + + +static void +ngx_http_lua_pipe_close_helper(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, ngx_event_t *ev) +{ + if (ev->handler != ngx_http_lua_pipe_dummy_event_handler) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe abort blocking operation pipe_ctx:%p ev:%p", + pipe_ctx, ev); + + if (pipe->dead) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + + } else { + pipe_ctx->err_type = PIPE_ERR_ABORTED; + } + + ngx_post_event(ev, &ngx_posted_events); + return; + } + + ngx_close_connection(pipe_ctx->c); + pipe_ctx->c = NULL; +} + + +static void +ngx_http_lua_pipe_close_stdin(ngx_http_lua_pipe_t *pipe) +{ + ngx_event_t *wev; + + if (pipe->stdin_ctx == NULL) { + if (pipe->stdin_fd != -1) { + if (close(pipe->stdin_fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the stdin pipe fd"); + } + + pipe->stdin_fd = -1; + } + + } else if (pipe->stdin_ctx->c != NULL) { + wev = pipe->stdin_ctx->c->write; + ngx_http_lua_pipe_close_helper(pipe, pipe->stdin_ctx, wev); + } +} + + +static void +ngx_http_lua_pipe_close_stdout(ngx_http_lua_pipe_t *pipe) +{ + ngx_event_t *rev; + + if (pipe->stdout_ctx == NULL) { + if (pipe->stdout_fd != -1) { + if (close(pipe->stdout_fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the stdout pipe fd"); + } + + pipe->stdout_fd = -1; + } + + } else if (pipe->stdout_ctx->c != NULL) { + rev = pipe->stdout_ctx->c->read; + ngx_http_lua_pipe_close_helper(pipe, pipe->stdout_ctx, rev); + } +} + + +static void +ngx_http_lua_pipe_close_stderr(ngx_http_lua_pipe_t *pipe) +{ + ngx_event_t *rev; + + if (pipe->stderr_ctx == NULL) { + if (pipe->stderr_fd != -1) { + if (close(pipe->stderr_fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the stderr pipe fd"); + } + + pipe->stderr_fd = -1; + } + + } else if (pipe->stderr_ctx->c != NULL) { + rev = pipe->stderr_ctx->c->read; + ngx_http_lua_pipe_close_helper(pipe, pipe->stderr_ctx, rev); + } +} + + +int +ngx_http_lua_ffi_pipe_proc_shutdown_stdin(ngx_http_lua_ffi_pipe_proc_t *proc, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + ngx_http_lua_pipe_close_stdin(pipe); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_pipe_proc_shutdown_stdout(ngx_http_lua_ffi_pipe_proc_t *proc, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + ngx_http_lua_pipe_close_stdout(pipe); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_pipe_proc_shutdown_stderr(ngx_http_lua_ffi_pipe_proc_t *proc, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + if (pipe->merge_stderr) { + /* stdout is used internally as stderr when merge_stderr is true */ + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "merged to stdout") + - errbuf; + return NGX_ERROR; + } + + ngx_http_lua_pipe_close_stderr(pipe); + + return NGX_OK; +} + + +static void +ngx_http_lua_pipe_proc_finalize(ngx_http_lua_ffi_pipe_proc_t *proc) +{ + ngx_http_lua_pipe_t *pipe; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe finalize process:%p pid:%P", + proc, proc->_pid); + pipe = proc->pipe; + + if (pipe->node) { + ngx_rbtree_delete(&ngx_http_lua_pipe_rbtree, pipe->node); + pipe->node = NULL; + } + + pipe->dead = 1; + + ngx_http_lua_pipe_close_stdin(pipe); + ngx_http_lua_pipe_close_stdout(pipe); + + if (!pipe->merge_stderr) { + ngx_http_lua_pipe_close_stderr(pipe); + } + + pipe->closed = 1; +} + + +void +ngx_http_lua_ffi_pipe_proc_destroy(ngx_http_lua_ffi_pipe_proc_t *proc) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe destroy process:%p pid:%P", proc, proc->_pid); + + if (!pipe->dead) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe kill process:%p pid:%P", proc, proc->_pid); + + if (kill(proc->_pid, SIGKILL) == -1) { + if (ngx_errno != ESRCH) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe failed to kill process:%p pid:%P", + proc, proc->_pid); + } + } + } + + ngx_http_lua_pipe_proc_finalize(proc); + ngx_destroy_pool(pipe->pool); + proc->pipe = NULL; +} + + +static ngx_int_t +ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r, + ngx_http_lua_ctx_t **ctx, u_char *errbuf, size_t *errbuf_size) +{ + int rc; + + *ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_HTTP_LUA_FFI_NO_REQ_CTX; + } + + rc = ngx_http_lua_ffi_check_context(*ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE, + errbuf, errbuf_size); + if (rc != NGX_OK) { + return NGX_HTTP_LUA_FFI_BAD_CONTEXT; + } + + return NGX_OK; +} + + +static void +ngx_http_lua_pipe_put_error(ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char *errbuf, + size_t *errbuf_size) +{ + switch (pipe_ctx->err_type) { + + case PIPE_ERR_CLOSED: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + break; + + case PIPE_ERR_SYSCALL: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "%s", + strerror(pipe_ctx->pipe_errno)) + - errbuf; + break; + + case PIPE_ERR_NOMEM: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + break; + + case PIPE_ERR_TIMEOUT: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "timeout") + - errbuf; + break; + + case PIPE_ERR_ADD_READ_EV: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "failed to add read event") + - errbuf; + break; + + case PIPE_ERR_ADD_WRITE_EV: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "failed to add write event") + - errbuf; + break; + + case PIPE_ERR_ABORTED: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "aborted") - errbuf; + break; + + default: + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "unexpected err type: %d", pipe_ctx->err_type); + ngx_http_lua_assert(NULL); + } +} + + +static void +ngx_http_lua_pipe_put_data(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char **buf, size_t *buf_size) +{ + size_t size = 0; + size_t chunk_size; + size_t nbufs; + u_char *p; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_chain_t **ll; + + nbufs = 0; + ll = NULL; + + for (cl = pipe_ctx->bufs_in; cl; cl = cl->next) { + b = cl->buf; + chunk_size = b->last - b->pos; + + if (cl->next) { + ll = &cl->next; + } + + size += chunk_size; + + nbufs++; + } + + if (*buf_size < size) { + *buf = NULL; + *buf_size = size; + + return; + } + + *buf_size = size; + + p = *buf; + for (cl = pipe_ctx->bufs_in; cl; cl = cl->next) { + b = cl->buf; + chunk_size = b->last - b->pos; + p = ngx_cpymem(p, b->pos, chunk_size); + } + + if (nbufs > 1 && ll) { + *ll = pipe->free_bufs; + pipe->free_bufs = pipe_ctx->bufs_in; + pipe_ctx->bufs_in = pipe_ctx->buf_in; + } + + if (pipe_ctx->buffer.pos == pipe_ctx->buffer.last) { + pipe_ctx->buffer.pos = pipe_ctx->buffer.start; + pipe_ctx->buffer.last = pipe_ctx->buffer.start; + } + + if (pipe_ctx->bufs_in) { + pipe_ctx->buf_in->buf->last = pipe_ctx->buffer.pos; + pipe_ctx->buf_in->buf->pos = pipe_ctx->buffer.pos; + } +} + + +static ngx_int_t +ngx_http_lua_pipe_add_input_buffer(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx) +{ + ngx_chain_t *cl; + + cl = ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool, + &pipe->free_bufs, + pipe->buffer_size); + + if (cl == NULL) { + pipe_ctx->err_type = PIPE_ERR_NOMEM; + return NGX_ERROR; + } + + pipe_ctx->buf_in->next = cl; + pipe_ctx->buf_in = cl; + pipe_ctx->buffer = *cl->buf; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_pipe_read_all(void *data, ssize_t bytes) +{ + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua pipe read all"); + return ngx_http_lua_read_all(&pipe_ctx->buffer, pipe_ctx->buf_in, bytes, + ngx_cycle->log); +} + + +static ngx_int_t +ngx_http_lua_pipe_read_bytes(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read bytes %z", bytes); + + rc = ngx_http_lua_read_bytes(&pipe_ctx->buffer, pipe_ctx->buf_in, + &pipe_ctx->rest, bytes, ngx_cycle->log); + if (rc == NGX_ERROR) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_pipe_read_line(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read line"); + rc = ngx_http_lua_read_line(&pipe_ctx->buffer, pipe_ctx->buf_in, bytes, + ngx_cycle->log); + if (rc == NGX_ERROR) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_pipe_read_any(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua pipe read any"); + rc = ngx_http_lua_read_any(&pipe_ctx->buffer, pipe_ctx->buf_in, + &pipe_ctx->rest, bytes, ngx_cycle->log); + if (rc == NGX_ERROR) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_pipe_read(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx) +{ + int rc; + int read; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *c; + + c = pipe_ctx->c; + rev = c->read; + b = &pipe_ctx->buffer; + read = 0; + + for ( ;; ) { + size = b->last - b->pos; + + if (size || pipe_ctx->eof) { + rc = pipe_ctx->input_filter(pipe_ctx->input_filter_ctx, size); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read done pipe:%p", pipe_ctx); + return NGX_OK; + } + + /* rc == NGX_AGAIN */ + continue; + } + + if (read && !rev->ready) { + break; + } + + size = b->end - b->last; + + if (size == 0) { + rc = ngx_http_lua_pipe_add_input_buffer(pipe, pipe_ctx); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + b = &pipe_ctx->buffer; + size = (size_t) (b->end - b->last); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe try to read data %uz pipe:%p", + size, pipe_ctx); + + n = c->recv(c, b->last, size); + read = 1; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read data returned %z pipe:%p", n, pipe_ctx); + + if (n == NGX_AGAIN) { + break; + } + + if (n == 0) { + pipe_ctx->eof = 1; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe closed pipe:%p", pipe_ctx); + continue; + } + + if (n == NGX_ERROR) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, ngx_errno, + "lua pipe read data error pipe:%p", pipe_ctx); + + pipe_ctx->err_type = PIPE_ERR_SYSCALL; + pipe_ctx->pipe_errno = ngx_errno; + return NGX_ERROR; + } + + b->last += n; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_lua_pipe_init_ctx(ngx_http_lua_pipe_ctx_t **pipe_ctx_pt, int fd, + ngx_pool_t *pool, u_char *errbuf, size_t *errbuf_size) +{ + ngx_connection_t *c; + + if (fd == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + *pipe_ctx_pt = ngx_pcalloc(pool, sizeof(ngx_http_lua_pipe_ctx_t)); + if (*pipe_ctx_pt == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + return NGX_ERROR; + } + + c = ngx_get_connection(fd, ngx_cycle->log); + if (c == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no connection") + - errbuf; + return NGX_ERROR; + } + + c->log = ngx_cycle->log; + c->recv = ngx_http_lua_pipe_fd_read; + c->read->handler = ngx_http_lua_pipe_dummy_event_handler; + c->read->log = c->log; + +#ifdef HAVE_SOCKET_CLOEXEC_PATCH + c->read->skip_socket_leak_check = 1; +#endif + + c->send = ngx_http_lua_pipe_fd_write; + c->write->handler = ngx_http_lua_pipe_dummy_event_handler; + c->write->log = c->log; + (*pipe_ctx_pt)->c = c; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe init pipe ctx:%p fd:*%d", *pipe_ctx_pt, fd); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_pipe_proc_read(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, int reader_type, + size_t length, u_char **buf, size_t *buf_size, u_char *errbuf, + size_t *errbuf_size) +{ + int rc; + ngx_msec_t timeout; + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size); + if (rc != NGX_OK) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe read process:%p pid:%P", proc, proc->_pid); + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + if (pipe->merge_stderr && from_stderr) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "merged to stdout") + - errbuf; + return NGX_ERROR; + } + + if (from_stderr) { + if (pipe->stderr_ctx == NULL) { + if (ngx_http_lua_pipe_init_ctx(&pipe->stderr_ctx, pipe->stderr_fd, + pipe->pool, errbuf, + errbuf_size) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + pipe->stderr_ctx->err_type = 0; + } + + pipe_ctx = pipe->stderr_ctx; + + } else { + if (pipe->stdout_ctx == NULL) { + if (ngx_http_lua_pipe_init_ctx(&pipe->stdout_ctx, pipe->stdout_fd, + pipe->pool, errbuf, + errbuf_size) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + pipe->stdout_ctx->err_type = 0; + } + + pipe_ctx = pipe->stdout_ctx; + } + + c = pipe_ctx->c; + if (c == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + rev = c->read; + if (rev->handler != ngx_http_lua_pipe_dummy_event_handler) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe busy reading") + - errbuf; + return NGX_ERROR; + } + + pipe_ctx->input_filter_ctx = pipe_ctx; + + switch (reader_type) { + + case PIPE_READ_ALL: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_all; + break; + + case PIPE_READ_BYTES: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_bytes; + break; + + case PIPE_READ_LINE: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_line; + break; + + case PIPE_READ_ANY: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_any; + break; + + default: + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "unexpected reader_type: %d", reader_type); + ngx_http_lua_assert(NULL); + } + + pipe_ctx->rest = length; + + if (pipe_ctx->bufs_in == NULL) { + pipe_ctx->bufs_in = + ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool, + &pipe->free_bufs, + pipe->buffer_size); + + if (pipe_ctx->bufs_in == NULL) { + pipe_ctx->err_type = PIPE_ERR_NOMEM; + goto error; + } + + pipe_ctx->buf_in = pipe_ctx->bufs_in; + pipe_ctx->buffer = *pipe_ctx->buf_in->buf; + } + + rc = ngx_http_lua_pipe_read(pipe, pipe_ctx); + if (rc == NGX_ERROR) { + goto error; + } + + if (rc == NGX_OK) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + return NGX_OK; + } + + /* rc == NGX_AGAIN */ + wait_co_ctx = ctx->cur_co_ctx; + + c->data = wait_co_ctx; + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + pipe_ctx->err_type = PIPE_ERR_ADD_READ_EV; + goto error; + } + + wait_co_ctx->data = proc; + + if (from_stderr) { + rev->handler = ngx_http_lua_pipe_resume_read_stderr_handler; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_read_stderr_cleanup; + timeout = proc->stderr_read_timeout; + + } else { + rev->handler = ngx_http_lua_pipe_resume_read_stdout_handler; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_read_stdout_cleanup; + timeout = proc->stdout_read_timeout; + } + + if (timeout > 0) { + ngx_add_timer(rev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe add timer for reading: %d(ms) process:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + rev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe read yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; + +error: + + if (pipe_ctx->bufs_in) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_DECLINED; + } + + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + + return NGX_ERROR; +} + + +/* + * ngx_http_lua_ffi_pipe_get_read_result should only be called just after + * ngx_http_lua_ffi_pipe_proc_read, so we omit most of the sanity check already + * done in ngx_http_lua_ffi_pipe_proc_read. + */ +int +ngx_http_lua_ffi_pipe_get_read_result(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, u_char **buf, + size_t *buf_size, u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe get read result process:%p pid:%P", proc, + proc->_pid); + + pipe = proc->pipe; + pipe_ctx = from_stderr ? pipe->stderr_ctx : pipe->stdout_ctx; + + if (!pipe_ctx->err_type) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + return NGX_OK; + } + + if (pipe_ctx->bufs_in) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_DECLINED; + } + + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_lua_pipe_write(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx) +{ + size_t size; + ngx_int_t n; + ngx_buf_t *b; + ngx_connection_t *c; + + c = pipe_ctx->c; + b = pipe_ctx->buf_in->buf; + + for ( ;; ) { + size = b->last - b->pos; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe try to write data %uz pipe:%p", size, + pipe_ctx); + + n = c->send(c, b->pos, size); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe write returned %i pipe:%p", n, pipe_ctx); + + if (n >= 0) { + b->pos += n; + + if (b->pos == b->last) { + b->pos = b->start; + b->last = b->start; + + if (!pipe->free_bufs) { + pipe->free_bufs = pipe_ctx->buf_in; + + } else { + pipe->free_bufs->next = pipe_ctx->buf_in; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe write done pipe:%p", pipe_ctx); + return NGX_OK; + } + + continue; + } + + /* NGX_ERROR || NGX_AGAIN */ + break; + } + + if (n == NGX_ERROR) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, ngx_errno, + "lua pipe write data error pipe:%p", pipe_ctx); + + if (ngx_errno == NGX_EPIPE) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + + } else { + pipe_ctx->err_type = PIPE_ERR_SYSCALL; + pipe_ctx->pipe_errno = ngx_errno; + } + + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +ssize_t +ngx_http_lua_ffi_pipe_proc_write(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, const u_char *data, size_t len, + u_char *errbuf, size_t *errbuf_size) +{ + int rc; + ngx_buf_t *b; + ngx_msec_t timeout; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size); + if (rc != NGX_OK) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe write process:%p pid:%P", proc, proc->_pid); + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + if (pipe->stdin_ctx == NULL) { + if (ngx_http_lua_pipe_init_ctx(&pipe->stdin_ctx, pipe->stdin_fd, + pipe->pool, errbuf, + errbuf_size) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + pipe->stdin_ctx->err_type = 0; + } + + pipe_ctx = pipe->stdin_ctx; + if (pipe_ctx->c == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + wev = pipe_ctx->c->write; + if (wev->handler != ngx_http_lua_pipe_dummy_event_handler) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe busy writing") + - errbuf; + return NGX_ERROR; + } + + pipe_ctx->rest = len; + + cl = ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool, + &pipe->free_bufs, len); + if (cl == NULL) { + pipe_ctx->err_type = PIPE_ERR_NOMEM; + goto error; + } + + pipe_ctx->buf_in = cl; + b = pipe_ctx->buf_in->buf; + b->last = ngx_copy(b->last, data, len); + + rc = ngx_http_lua_pipe_write(pipe, pipe_ctx); + if (rc == NGX_ERROR) { + goto error; + } + + if (rc == NGX_OK) { + return len; + } + + /* rc == NGX_AGAIN */ + wait_co_ctx = ctx->cur_co_ctx; + pipe_ctx->c->data = wait_co_ctx; + + wev->handler = ngx_http_lua_pipe_resume_write_handler; + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + pipe_ctx->err_type = PIPE_ERR_ADD_WRITE_EV; + goto error; + } + + wait_co_ctx->data = proc; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_write_cleanup; + timeout = proc->write_timeout; + + if (timeout > 0) { + ngx_add_timer(wev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe add timer for writing: %d(ms) process:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + wev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe write yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; + +error: + + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_ERROR; +} + + +/* + * ngx_http_lua_ffi_pipe_get_write_result should only be called just after + * ngx_http_lua_ffi_pipe_proc_write, so we omit most of the sanity check + * already done in ngx_http_lua_ffi_pipe_proc_write. + */ +ssize_t +ngx_http_lua_ffi_pipe_get_write_result(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe get write result process:%p pid:%P", proc, + proc->_pid); + + pipe = proc->pipe; + pipe_ctx = pipe->stdin_ctx; + + if (pipe_ctx->err_type) { + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_ERROR; + } + + return pipe_ctx->rest; +} + + +int +ngx_http_lua_ffi_pipe_proc_wait(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, char **reason, int *status, + u_char *errbuf, size_t *errbuf_size) +{ + int rc; + ngx_rbtree_node_t *node; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_node_t *pipe_node; + + rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size); + if (rc != NGX_OK) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe wait process:%p pid:%P", proc, proc->_pid); + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "exited") - errbuf; + return NGX_ERROR; + } + + node = pipe->node; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + if (pipe_node->wait_co_ctx) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe busy waiting") + - errbuf; + return NGX_ERROR; + } + + if (pipe_node->reason_code == REASON_RUNNING_CODE) { + wait_co_ctx = ctx->cur_co_ctx; + wait_co_ctx->data = proc; + ngx_memzero(&wait_co_ctx->sleep, sizeof(ngx_event_t)); + wait_co_ctx->sleep.handler = ngx_http_lua_pipe_resume_wait_handler; + wait_co_ctx->sleep.data = wait_co_ctx; + wait_co_ctx->sleep.log = r->connection->log; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_wait_cleanup; + + pipe_node->wait_co_ctx = wait_co_ctx; + + if (proc->wait_timeout > 0) { + ngx_add_timer(&wait_co_ctx->sleep, proc->wait_timeout); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe add timer for waiting: %d(ms) process:%p " + "pid:%P ev:%p", proc->wait_timeout, proc, + proc->_pid, &wait_co_ctx->sleep); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe wait yielding process:%p pid:%P", proc, + proc->_pid); + + return NGX_AGAIN; + } + + *status = pipe_node->status; + + switch (pipe_node->reason_code) { + + case REASON_EXIT_CODE: + *reason = REASON_EXIT; + break; + + case REASON_SIGNAL_CODE: + *reason = REASON_SIGNAL; + break; + + default: + *reason = REASON_UNKNOWN; + } + + ngx_http_lua_pipe_proc_finalize(proc); + + if (*status == 0) { + return NGX_OK; + } + + return NGX_DECLINED; +} + + +int +ngx_http_lua_ffi_pipe_proc_kill(ngx_http_lua_ffi_pipe_proc_t *proc, int signal, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_pid_t pid; + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + + if (pipe == NULL || pipe->dead) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "exited") - errbuf; + return NGX_ERROR; + } + + pid = proc->_pid; + + if (kill(pid, signal) == -1) { + switch (ngx_errno) { + case EINVAL: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "invalid signal") + - errbuf; + break; + + case ESRCH: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "exited") + - errbuf; + break; + + default: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "%s", + strerror(ngx_errno)) + - errbuf; + } + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static int +ngx_http_lua_pipe_read_stdout_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L) +{ + return ngx_http_lua_pipe_read_retval_helper(proc, L, 0); +} + + +static int +ngx_http_lua_pipe_read_stderr_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L) +{ + return ngx_http_lua_pipe_read_retval_helper(proc, L, 1); +} + + +static int +ngx_http_lua_pipe_read_retval_helper(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L, int from_stderr) +{ + int rc; + ngx_msec_t timeout; + ngx_event_t *rev; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + pipe = proc->pipe; + if (from_stderr) { + pipe_ctx = pipe->stderr_ctx; + + } else { + pipe_ctx = pipe->stdout_ctx; + } + + if (pipe->timeout) { + pipe->timeout = 0; + pipe_ctx->err_type = PIPE_ERR_TIMEOUT; + return 0; + } + + if (pipe_ctx->err_type == PIPE_ERR_ABORTED) { + ngx_close_connection(pipe_ctx->c); + pipe_ctx->c = NULL; + return 0; + } + + rc = ngx_http_lua_pipe_read(pipe, pipe_ctx); + if (rc != NGX_AGAIN) { + return 0; + } + + rev = pipe_ctx->c->read; + + if (from_stderr) { + rev->handler = ngx_http_lua_pipe_resume_read_stderr_handler; + timeout = proc->stderr_read_timeout; + + } else { + rev->handler = ngx_http_lua_pipe_resume_read_stdout_handler; + timeout = proc->stdout_read_timeout; + } + + if (timeout > 0) { + ngx_add_timer(rev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe add timer for reading: %d(ms) proc:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + rev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; +} + + +static int +ngx_http_lua_pipe_write_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L) +{ + int rc; + ngx_msec_t timeout; + ngx_event_t *wev; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + pipe = proc->pipe; + pipe_ctx = pipe->stdin_ctx; + + if (pipe->timeout) { + pipe->timeout = 0; + pipe_ctx->err_type = PIPE_ERR_TIMEOUT; + return 0; + } + + if (pipe_ctx->err_type == PIPE_ERR_ABORTED) { + ngx_close_connection(pipe_ctx->c); + pipe_ctx->c = NULL; + return 0; + } + + rc = ngx_http_lua_pipe_write(pipe, pipe_ctx); + if (rc != NGX_AGAIN) { + return 0; + } + + wev = pipe_ctx->c->write; + wev->handler = ngx_http_lua_pipe_resume_write_handler; + timeout = proc->write_timeout; + + if (timeout > 0) { + ngx_add_timer(wev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe add timer for writing: %d(ms) proc:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + wev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe write yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; +} + + +static int +ngx_http_lua_pipe_wait_retval(ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L) +{ + int nret; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_node_t *pipe_node; + + pipe = proc->pipe; + node = pipe->node; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + pipe_node->wait_co_ctx = NULL; + + if (pipe->timeout) { + pipe->timeout = 0; + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + return 2; + } + + ngx_http_lua_pipe_proc_finalize(pipe_node->proc); + + if (pipe_node->status == 0) { + lua_pushboolean(L, 1); + lua_pushliteral(L, REASON_EXIT); + lua_pushinteger(L, pipe_node->status); + nret = 3; + + } else { + lua_pushboolean(L, 0); + + switch (pipe_node->reason_code) { + + case REASON_EXIT_CODE: + lua_pushliteral(L, REASON_EXIT); + break; + + case REASON_SIGNAL_CODE: + lua_pushliteral(L, REASON_SIGNAL); + break; + + default: + lua_pushliteral(L, REASON_UNKNOWN); + } + + lua_pushinteger(L, pipe_node->status); + nret = 3; + } + + return nret; +} + + +static void +ngx_http_lua_pipe_resume_helper(ngx_event_t *ev, + ngx_http_lua_co_ctx_t *wait_co_ctx) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + if (ev->timedout) { + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->timeout = 1; + ev->timedout = 0; + } + + ngx_http_lua_pipe_clear_event(ev); + + r = ngx_http_lua_get_req(wait_co_ctx->co); + c = r->connection; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + ngx_http_lua_assert(ctx != NULL); + + ctx->cur_co_ctx = wait_co_ctx; + + if (ctx->entered_content_phase) { + (void) ngx_http_lua_pipe_resume(r); + + } else { + ctx->resume_handler = ngx_http_lua_pipe_resume; + ngx_http_core_run_phases(r); + } + + ngx_http_run_posted_requests(c); +} + + +static void +ngx_http_lua_pipe_resume_read_stdout_handler(ngx_event_t *ev) +{ + ngx_connection_t *c = ev->data; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + wait_co_ctx = c->data; + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_read_stdout_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static void +ngx_http_lua_pipe_resume_read_stderr_handler(ngx_event_t *ev) +{ + ngx_connection_t *c = ev->data; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + wait_co_ctx = c->data; + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_read_stderr_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static void +ngx_http_lua_pipe_resume_write_handler(ngx_event_t *ev) +{ + ngx_connection_t *c = ev->data; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + wait_co_ctx = c->data; + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_write_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static void +ngx_http_lua_pipe_resume_wait_handler(ngx_event_t *ev) +{ + ngx_http_lua_co_ctx_t *wait_co_ctx = ev->data; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_wait_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static ngx_int_t +ngx_http_lua_pipe_resume(ngx_http_request_t *r) +{ + int nret; + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_http_lua_wev_handler; + ctx->cur_co_ctx->cleanup = NULL; + + proc = ctx->cur_co_ctx->data; + pipe = proc->pipe; + nret = pipe->retval_handler(proc, ctx->cur_co_ctx->co); + if (nret == NGX_AGAIN) { + return NGX_DONE; + } + + c = r->connection; + vm = ngx_http_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(vm, r, ctx, nret); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + /* rc == NGX_ERROR || rc >= NGX_OK */ + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +static void +ngx_http_lua_pipe_dummy_event_handler(ngx_event_t *ev) +{ + /* do nothing */ +} + + +static void +ngx_http_lua_pipe_clear_event(ngx_event_t *ev) +{ + ev->handler = ngx_http_lua_pipe_dummy_event_handler; + + if (ev->timer_set) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua pipe del timer for ev:%p", ev); + ngx_del_timer(ev); + } + + if (ev->posted) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua pipe del posted event for ev:%p", ev); + ngx_delete_posted_event(ev); + } +} + + +static void +ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data) +{ + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc read stdout cleanup"); + + proc = wait_co_ctx->data; + c = proc->pipe->stdout_ctx->c; + if (c) { + rev = c->read; + ngx_http_lua_pipe_clear_event(rev); + } + + wait_co_ctx->cleanup = NULL; +} + + +static void +ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data) +{ + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc read stderr cleanup"); + + proc = wait_co_ctx->data; + c = proc->pipe->stderr_ctx->c; + if (c) { + rev = c->read; + ngx_http_lua_pipe_clear_event(rev); + } + + wait_co_ctx->cleanup = NULL; +} + + +static void +ngx_http_lua_pipe_proc_write_cleanup(void *data) +{ + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc write cleanup"); + + proc = wait_co_ctx->data; + c = proc->pipe->stdin_ctx->c; + if (c) { + wev = c->write; + ngx_http_lua_pipe_clear_event(wev); + } + + wait_co_ctx->cleanup = NULL; +} + + +static void +ngx_http_lua_pipe_proc_wait_cleanup(void *data) +{ + ngx_rbtree_node_t *node; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_pipe_node_t *pipe_node; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc wait cleanup"); + + proc = wait_co_ctx->data; + node = proc->pipe->node; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + pipe_node->wait_co_ctx = NULL; + + ngx_http_lua_pipe_clear_event(&wait_co_ctx->sleep); + + wait_co_ctx->cleanup = NULL; +} + + +#endif /* HAVE_NGX_LUA_PIPE */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/nginx/src/lua-nginx/src/ngx_http_lua_pipe.h b/nginx/src/lua-nginx/src/ngx_http_lua_pipe.h new file mode 100644 index 000000000..ecb86c880 --- /dev/null +++ b/nginx/src/lua-nginx/src/ngx_http_lua_pipe.h @@ -0,0 +1,94 @@ + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef _NGX_HTTP_LUA_PIPE_H_INCLUDED_ +#define _NGX_HTTP_LUA_PIPE_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +typedef ngx_int_t (*ngx_http_lua_pipe_input_filter)(void *data, ssize_t bytes); + + +typedef struct { + ngx_connection_t *c; + ngx_http_lua_pipe_input_filter input_filter; + void *input_filter_ctx; + size_t rest; + ngx_chain_t *buf_in; + ngx_chain_t *bufs_in; + ngx_buf_t buffer; + ngx_err_t pipe_errno; + unsigned err_type:16; + unsigned eof:1; +} ngx_http_lua_pipe_ctx_t; + + +typedef struct ngx_http_lua_pipe_s ngx_http_lua_pipe_t; + + +typedef struct { + ngx_pid_t _pid; + ngx_msec_t write_timeout; + ngx_msec_t stdout_read_timeout; + ngx_msec_t stderr_read_timeout; + ngx_msec_t wait_timeout; + /* pipe hides the implementation from the Lua binding */ + ngx_http_lua_pipe_t *pipe; +} ngx_http_lua_ffi_pipe_proc_t; + + +typedef int (*ngx_http_lua_pipe_retval_handler)( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L); + + +struct ngx_http_lua_pipe_s { + ngx_pool_t *pool; + ngx_chain_t *free_bufs; + ngx_rbtree_node_t *node; + int stdin_fd; + int stdout_fd; + int stderr_fd; + ngx_http_lua_pipe_ctx_t *stdin_ctx; + ngx_http_lua_pipe_ctx_t *stdout_ctx; + ngx_http_lua_pipe_ctx_t *stderr_ctx; + ngx_http_lua_pipe_retval_handler retval_handler; + size_t buffer_size; + unsigned closed:1; + unsigned dead:1; + unsigned timeout:1; + unsigned merge_stderr:1; +}; + + +typedef struct { + u_char color; + u_char reason_code; + int status; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_ffi_pipe_proc_t *proc; +} ngx_http_lua_pipe_node_t; + + +typedef struct { + int signo; + char *signame; +} ngx_http_lua_pipe_signal_t; + + +#if !(NGX_WIN32) && defined(HAVE_SOCKET_CLOEXEC_PATCH) +#define HAVE_NGX_LUA_PIPE 1 + + +void ngx_http_lua_pipe_init(void); +ngx_int_t ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle); +#endif + + +#endif /* _NGX_HTTP_LUA_PIPE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */