diff --git a/nginx/Config.in b/nginx/Config.in new file mode 100755 index 000000000..ccb5e240f --- /dev/null +++ b/nginx/Config.in @@ -0,0 +1,270 @@ +# +# Copyright (C) 2010-2016 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +menu "Configuration" + depends on PACKAGE_nginx + +config NGINX_SSL + bool + prompt "Enable SSL module" + help + Enable HTTPS/SSL support. + default n + +config NGINX_DAV + bool + prompt "Enable WebDAV module" + help + 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" + help + Provides the ability to seek within FLV (Flash) files using time-based offsets. + default n + +config NGINX_STUB_STATUS + bool + prompt "Enable stub status module" + help + Enable the stub status module which gives some status from the server. + default n + +config NGINX_HTTP_CHARSET + bool + prompt "Enable HTTP charset module" + default y + +config NGINX_HTTP_GZIP + bool + prompt "Enable HTTP gzip module" + default y + +config NGINX_HTTP_SSI + bool + prompt "Enable HTTP ssi module" + default y + +config NGINX_HTTP_USERID + bool + prompt "Enable HTTP userid module" + default y + +config NGINX_HTTP_ACCESS + bool + prompt "Enable HTTP access module" + default y + +config NGINX_HTTP_AUTH_BASIC + bool + prompt "Enable HTTP auth basic" + default y + +config NGINX_HTTP_AUTH_REQUEST + bool + prompt "Enable HTTP auth request module" + default n + +config NGINX_HTTP_AUTOINDEX + bool + prompt "Enable HTTP autoindex module" + default y + +config NGINX_HTTP_GEO + bool + prompt "Enable HTTP geo module" + default y + +config NGINX_HTTP_MAP + bool + prompt "Enable HTTP map module" + default y + +config NGINX_HTTP_SPLIT_CLIENTS + bool + prompt "Enable HTTP split clients" + default y + +config NGINX_HTTP_REFERER + bool + prompt "Enable HTTP referer module" + default y + +config NGINX_HTTP_REWRITE + bool + prompt "Enable HTTP rewrite module" + select NGINX_PCRE + default y + +config NGINX_HTTP_PROXY + bool + prompt "Enable HTTP proxy module" + default y + +config NGINX_HTTP_FASTCGI + bool + prompt "Enable HTTP fastcgi module" + default y + +config NGINX_HTTP_UWSGI + bool + prompt "Enable HTTP uwsgi module" + default y + +config NGINX_HTTP_SCGI + bool + prompt "Enable HTTP scgi module" + default y + +config NGINX_HTTP_MEMCACHED + bool + prompt "Enable HTTP memcached module" + default y + +config NGINX_HTTP_LIMIT_CONN + bool + prompt "Enable HTTP limit conn" + default y + +config NGINX_HTTP_LIMIT_REQ + bool + prompt "Enable HTTP limit req" + default y + +config NGINX_HTTP_EMPTY_GIF + bool + prompt "Enable HTTP empty gif" + default y + +config NGINX_HTTP_BROWSER + bool + prompt "Enable HTTP browser module" + default y + +config NGINX_HTTP_UPSTREAM_HASH + bool + prompt "Enable HTTP hash module" + default y + +config NGINX_HTTP_UPSTREAM_IP_HASH + bool + prompt "Enable HTTP IP hash module" + default y + +config NGINX_HTTP_UPSTREAM_LEAST_CONN + bool + prompt "Enable HTTP least conn module" + default y + +config NGINX_HTTP_UPSTREAM_KEEPALIVE + bool + prompt "Enable HTTP keepalive module" + default y + +config NGINX_HTTP_CACHE + bool + prompt "Enable HTTP cache" + default y + +config NGINX_HTTP_V2 + bool + prompt "Enable HTTP_V2 module" + default n + +config NGINX_PCRE + bool + prompt "Enable PCRE library usage" + default y + +config NGINX_NAXSI + bool + prompt "Enable NAXSI module" + default y + +config NGINX_LUA + bool + prompt "Enable Lua module" + default n + +config NGINX_HTTP_REAL_IP + bool + prompt "Enable HTTP real ip module" + default n + +config NGINX_HTTP_SECURE_LINK + bool + prompt "Enable HTTP secure link module" + default n + +config NGINX_HTTP_SUB + bool + prompt "Enable HTTP sub module" + default n + +config NGINX_HEADERS_MORE + bool + prompt "Enable Headers_more module" + help + Set and clear input and output headers...more than "add"! + default y + +config NGINX_HTTP_BROTLI + bool + prompt "Enable Brotli compression module" + help + Add support for brotli compression module. + default n + +config NGINX_STREAM_CORE_MODULE + bool + prompt "Enable stream support" + help + 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" + depends on NGINX_SSL + help + Add support for NGINX-based Media Streaming Server module. + DASH enhanced - https://github.com/ut0mt8/nginx-rtmp-module + default n + +config NGINX_TS_MODULE + bool + prompt "Enable TS module" + help + Add support for MPEG-TS Live Module module. + default n + +endmenu diff --git a/nginx/Config_ssl.in b/nginx/Config_ssl.in index 1c53dab6a..098017aa6 100755 --- a/nginx/Config_ssl.in +++ b/nginx/Config_ssl.in @@ -46,6 +46,11 @@ config NGINX_HTTP_GZIP prompt "Enable HTTP gzip module" default y +config NGINX_HTTP_GZIP_STATIC + bool + prompt "Enable HTTP gzip static module" + default y + config NGINX_HTTP_SSI bool prompt "Enable HTTP ssi module" @@ -175,7 +180,7 @@ config NGINX_HTTP_CACHE config NGINX_HTTP_V2 bool prompt "Enable HTTP_V2 module" - default y + default n config NGINX_PCRE bool diff --git a/nginx/Makefile b/nginx/Makefile index 7105e6c15..56d75ea52 100755 --- a/nginx/Makefile +++ b/nginx/Makefile @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=nginx -PKG_VERSION:=1.19.6 +PKG_VERSION:=1.17.7 PKG_RELEASE:=2 PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://nginx.org/download/ -PKG_HASH:=b11195a02b1d3285ddf2987e02c6b6d28df41bb1b1dd25f33542848ef4fc33b5 +PKG_HASH:=b62756842807e5693b794e5d0ae289bd8ae5b098e66538b2a91eb80f25c591ff PKG_MAINTAINER:=Thomas Heil \ Ansuel Smith @@ -25,12 +25,14 @@ PKG_BUILD_PARALLEL:=1 PKG_INSTALL:=1 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 \ + CONFIG_NGINX_HTTP_GZIP_STATIC \ CONFIG_NGINX_HTTP_SSI \ CONFIG_NGINX_HTTP_USERID \ CONFIG_NGINX_HTTP_ACCESS \ @@ -74,7 +76,6 @@ PKG_CONFIG_DEPENDS := \ CONFIG_OPENSSL_WITH_NPN include $(INCLUDE_DIR)/package.mk -include $(INCLUDE_DIR)/nls.mk define Package/nginx/default SECTION:=net @@ -82,26 +83,28 @@ define Package/nginx/default SUBMENU:=Web Servers/Proxies TITLE:=Nginx web server URL:=http://nginx.org/ - DEPENDS:=+libopenssl +libpthread - # TODO: add PROVIDES when removing nginx - # PROVIDES:=nginx + 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 nginx is an HTTP and reverse proxy server, as well as a mail proxy server, \ - written by Igor Sysoev. + written by Igor Sysoev. (Some module require SSL module enable to show up in \ + config menu) +endef + +define Package/nginx + $(Package/nginx/default) + VARIANT:=no-ssl endef define Package/nginx-ssl $(Package/nginx/default) TITLE += with SSL support + DEPENDS +=+libopenssl VARIANT:=ssl - DEPENDS+= +NGINX_PCRE:libpcre \ - +NGINX_PCRE:nginx-ssl-util +!NGINX_PCRE:nginx-ssl-util-nopcre \ - +NGINX_HTTP_GZIP:zlib +NGINX_LUA:liblua +NGINX_DAV:libxml2 \ - +NGINX_UBUS:libubus +NGINX_UBUS:libblobmsg-json +NGINX_UBUS:libjson-c - EXTRA_DEPENDS:=nginx-ssl-util$(if $(CONFIG_NGINX_PCRE),,-nopcre) (>=1.5-1) (<2) - CONFLICTS:=nginx-all-module + PROVIDES:=nginx endef Package/nginx-ssl/description = $(Package/nginx/description) \ @@ -111,21 +114,24 @@ Package/nginx-ssl/description = $(Package/nginx/description) \ define Package/nginx-all-module $(Package/nginx/default) TITLE += with ALL module selected - DEPENDS+=+libpcre +nginx-ssl-util +zlib +liblua +libxml2 +libubus \ - +libblobmsg-json +libjson-c - EXTRA_DEPENDS:=nginx-ssl-util (>=1.5-1) (<2) + DEPENDS:=+libpcre +libopenssl +zlib +liblua +libpthread +libxml2 \ + +libubus +libblobmsg-json +libjson-c VARIANT:=all-module - PROVIDES += nginx-ssl + PROVIDES:=nginx endef Package/nginx-all-module/description = $(Package/nginx/description) \ This variant is compiled with ALL module selected. +define Package/nginx/config + source "$(SOURCE)/Config.in" +endef + define Package/nginx-ssl/config source "$(SOURCE)/Config_ssl.in" endef -config_files=mime.types +config_files=nginx.conf mime.types define Package/nginx/conffiles /etc/nginx/ @@ -135,7 +141,7 @@ Package/nginx-ssl/conffiles = $(Package/nginx/conffiles) Package/nginx-all-module/conffiles = $(Package/nginx/conffiles) -ADDITIONAL_MODULES:= --with-http_ssl_module +ADDITIONAL_MODULES:=--with-http_gzip_static_module ifneq ($(BUILD_VARIANT),all-module) ifneq ($(CONFIG_NGINX_HTTP_CACHE),y) @@ -143,6 +149,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 @@ -152,6 +160,9 @@ ifneq ($(BUILD_VARIANT),all-module) ifneq ($(CONFIG_NGINX_HTTP_GZIP),y) ADDITIONAL_MODULES += --without-http_gzip_module endif + ifeq ($(CONFIG_NGINX_HTTP_GZIP_STATIC),y) + ADDITIONAL_MODULES += --with-http_gzip_static_module + endif ifneq ($(CONFIG_NGINX_HTTP_SSI),y) ADDITIONAL_MODULES += --without-http_ssi_module endif @@ -225,6 +236,16 @@ ifneq ($(BUILD_VARIANT),all-module) ifneq ($(CONFIG_NGINX_HTTP_UPSTREAM_KEEPALIVE),y) ADDITIONAL_MODULES += --without-http_upstream_keepalive_module endif + + ifeq ($(BUILD_VARIANT),ssl) + ifneq ($(CONFIG_NGINX_SSL),y) + ADDITIONAL_MODULES += --with-http_ssl_module + endif + endif + + ifeq ($(CONFIG_NGINX_SSL),y) + ADDITIONAL_MODULES += --with-http_ssl_module + endif ifeq ($(CONFIG_NGINX_NAXSI),y) ADDITIONAL_MODULES += --add-module=$(PKG_BUILD_DIR)/nginx-naxsi/naxsi_src endif @@ -291,36 +312,44 @@ else CONFIG_NGINX_LUA:=y CONFIG_NGINX_DAV:=y CONFIG_NGINX_UBUS:=y - ADDITIONAL_MODULES += --with-ipv6 --with-http_stub_status_module --with-http_flv_module \ - --with-http_dav_module \ + 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 \ + --with-http_secure_link_module --with-http_sub_module --add-module=$(PKG_BUILD_DIR)/nginx-headers-more \ --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module \ - --add-module=$(PKG_BUILD_DIR)/nginx-headers-more \ - --add-module=$(PKG_BUILD_DIR)/nginx-naxsi/naxsi_src \ - --add-module=$(PKG_BUILD_DIR)/lua-nginx \ - --add-module=$(PKG_BUILD_DIR)/nginx-dav-ext-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-ubus-module - config_files += koi-utf koi-win win-utf fastcgi_params uwsgi_params + config_files += koi-utf koi-win win-utf fastcgi_params endif -define Package/nginx-mod-luci +define Package/nginx-mod-luci/default TITLE:=Nginx on LuCI SECTION:=net CATEGORY:=Network SUBMENU:=Web Servers/Proxies TITLE:=Support file for Nginx URL:=http://nginx.org/ - DEPENDS:=+uwsgi +uwsgi-luci-support +nginx - # TODO: add PROVIDES when removing nginx-mod-luci-ssl - # PROVIDES:=nginx-mod-luci-ssl + DEPENDS:=+uwsgi +uwsgi-luci-support +endef + +define Package/nginx-mod-luci + $(Package/nginx-mod-luci/default) + DEPENDS += +nginx endef define Package/nginx-mod-luci/description Support file for LuCI in nginx. Include custom nginx configuration, autostart script for uwsgi. endef +define Package/nginx-mod-luci-ssl + $(Package/nginx-mod-luci/default) + TITLE += with HTTPS support + DEPENDS += +nginx-ssl +endef + +Package/nginx-mod-luci-ssl/description = $(define Package/nginx-mod-luci/description) \ + This also include redirect from http to https and cert autogeneration. TARGET_CFLAGS += -fvisibility=hidden -ffunction-sections -fdata-sections -DNGX_LUA_NO_BY_LUA_BLOCK TARGET_LDFLAGS += -Wl,--gc-sections @@ -330,14 +359,12 @@ ifeq ($(CONFIG_NGINX_LUA),y) LUA_LIB=$(STAGING_DIR)/usr/lib endif -CONFIGURE_VARS += CONFIG_BIG_ENDIAN=$(CONFIG_BIG_ENDIAN) - CONFIGURE_ARGS += \ --crossbuild=Linux::$(ARCH) \ --prefix=/usr \ --conf-path=/etc/nginx/nginx.conf \ $(ADDITIONAL_MODULES) \ - --error-log-path=stderr \ + --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/lock/nginx.lock \ --http-log-path=/var/log/nginx/access.log \ @@ -350,16 +377,25 @@ CONFIGURE_ARGS += \ --without-http_upstream_zone_module define Package/nginx-mod-luci/install - $(INSTALL_DIR) $(1)/etc/nginx/conf.d - $(INSTALL_CONF) ./files-luci-support/luci.locations $(1)/etc/nginx/conf.d/ + $(INSTALL_DIR) $(1)/etc/nginx + $(INSTALL_BIN) ./files-luci-support/luci_uwsgi.conf $(1)/etc/nginx/luci_uwsgi.conf + $(INSTALL_BIN) ./files-luci-support/luci_nginx.conf $(1)/etc/nginx/luci_nginx.conf $(INSTALL_DIR) $(1)/etc/uci-defaults $(INSTALL_BIN) ./files-luci-support/60_nginx-luci-support $(1)/etc/uci-defaults/60_nginx-luci-support endef -define Package/nginx-ssl/install +define Package/nginx-mod-luci-ssl/install + $(Package/nginx-mod-luci/install) + $(INSTALL_DIR) $(1)/etc/nginx + $(INSTALL_BIN) ./files-luci-support/luci_nginx_ssl.conf $(1)/etc/nginx/luci_nginx_ssl.conf + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files-luci-support/70_nginx-luci-support-ssl $(1)/etc/uci-defaults/70_nginx-luci-support-ssl +endef + +define Package/nginx/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/nginx $(1)/usr/sbin/ - $(INSTALL_DIR) $(1)/etc/nginx/conf.d + $(INSTALL_DIR) $(1)/etc/nginx $(INSTALL_DATA) $(addprefix $(PKG_INSTALL_DIR)/etc/nginx/,$(config_files)) $(1)/etc/nginx/ $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_BIN) ./files/nginx.init $(1)/etc/init.d/nginx @@ -372,20 +408,8 @@ endif $(if $(CONFIG_NGINX_NAXSI),$(chmod 0640 $(1)/etc/nginx/naxsi_core.rules)) endef -Package/nginx-all-module/install = $(Package/nginx-ssl/install) - -define Package/nginx-ssl/prerm -#!/bin/sh -[ -z "$${IPKG_INSTROOT}" ] || exit 0 -[ "$${PKG_UPGRADE}" = "1" ] && exit 0 -eval $$(/usr/bin/nginx-util get_env) -[ "$$(uci get "nginx.$${LAN_NAME}.$${MANAGE_SSL}")" = "self-signed" ] || exit 0 -rm -f "$$(uci get "nginx.$${LAN_NAME}.ssl_certificate")" -rm -f "$$(uci get "nginx.$${LAN_NAME}.ssl_certificate_key")" -exit 0 -endef - -Package/nginx-all-module/prerm = $(Package/nginx-ssl/prerm) +Package/nginx-ssl/install = $(Package/nginx/install) +Package/nginx-all-module/install = $(Package/nginx/install) define Build/Prepare $(Build/Prepare/Default) @@ -420,11 +444,11 @@ endif ifeq ($(CONFIG_NGINX_HTTP_BROTLI),y) define Download/nginx-brotli - VERSION:=e505dce68acc190cc5a1e780a3b0275e39f160ca + VERSION:=dc37f658ccb5a51d090dc09d1a2aca2f24309869 SUBDIR:=nginx-brotli FILE:=ngx-brotli-module-$$(VERSION).tar.xz - URL:=https://github.com/google/ngx_brotli.git - MIRROR_HASH:=04847f11ef808fed50f44b2af0ef3abf59ff0ffc06dfc7394d9ab51d53fef31f + URL:=https://github.com/eustas/ngx_brotli.git + MIRROR_HASH:=6bc0c40ff24f6e0ac616dfddc803bdc7fcf54764ba9dc4f9cecb3a68beedcdaf PROTO:=git endef $(eval $(call Download,nginx-brotli)) @@ -529,11 +553,11 @@ endif ifeq ($(CONFIG_NGINX_UBUS),y) define Download/nginx-ubus-module - VERSION:=b2d7260dcb428b2fb65540edb28d7538602b4a26 + VERSION:=f30b0167a2cdb40f23bd90928d601bdb0c1b8fad SUBDIR:=nginx-ubus-module FILE:=nginx-ubus-module-$$(VERSION).tar.xz URL:=https://github.com/Ansuel/nginx-ubus-module.git - MIRROR_HASH:=472cef416d25effcac66c85417ab6596e634a7a64d45b709bb090892d567553c + MIRROR_HASH:=02c7d4b0df7f4b69605e71b0fefdc99b5a9470c68cad7ccfb31ebefe4e7e0704 PROTO:=git endef $(eval $(call Download,nginx-ubus-module)) @@ -544,34 +568,8 @@ ifeq ($(CONFIG_NGINX_UBUS),y) endef endif +$(eval $(call BuildPackage,nginx)) $(eval $(call BuildPackage,nginx-ssl)) $(eval $(call BuildPackage,nginx-all-module)) $(eval $(call BuildPackage,nginx-mod-luci)) - -# TODO: remove after a transition period (together with pkg nginx-util): -# It is for smoothly substituting nginx and nginx-mod-luci-ssl (by nginx-ssl -# respectively nginx-mod-luci). Add above commented PROVIDES when removing. - -define Package/nginx - TITLE:=Dummy package for transition when upgrading. - DEPENDS:=+nginx-ssl - PKGARCH:=all -endef - -define Package/nginx/install - $(INSTALL_DIR) $(1)/usr/bin -endef - -$(eval $(call BuildPackage,nginx)) - -define Package/nginx-mod-luci-ssl - TITLE:=Dummy package for transition when upgrading. - DEPENDS:=+nginx-mod-luci - PKGARCH:=all -endef - -define Package/nginx-mod-luci-ssl/install - $(INSTALL_DIR) $(1)/usr/bin -endef - $(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 b2564444c..b682a832e 100755 --- a/nginx/files-luci-support/60_nginx-luci-support +++ b/nginx/files-luci-support/60_nginx-luci-support @@ -1,41 +1,41 @@ #!/bin/sh +if [ -f "/etc/nginx/luci_nginx.conf" ] && [ -f "/etc/nginx/nginx.conf" ]; then + if [ ! "$(cat '/etc/nginx/nginx.conf' | grep 'luci_uwsgi.conf')" ]; then + mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_old + mv /etc/nginx/luci_nginx.conf /etc/nginx/nginx.conf + core_number=$(grep -c ^processor /proc/cpuinfo) + sed -i "3s/.*/worker_processes "$core_number";/" /etc/nginx/nginx.conf + if [ -n "$(pgrep uhttpd)" ]; then + /etc/init.d/uhttpd stop + /etc/init.d/uhttpd disable + fi + if [ -n "$(pgrep nginx)" ]; then + /etc/init.d/nginx restart + else + /etc/init.d/nginx start + fi + if [ -n "$(pgrep uwsgi)" ]; then + /etc/init.d/uwsgi restart + else + /etc/init.d/uwsgi start + fi + else + rm /etc/nginx/luci_nginx.conf + fi +fi + if nginx -V 2>&1 | grep -q ubus; then - if [ -z "$(cat /etc/nginx/conf.d/luci.locations | grep ubus)" ]; then - cat <> /etc/nginx/conf.d/luci.locations + 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/ubus.sock; + ubus_socket_path /var/run/ubus.sock; ubus_parallel_req 2; } EOT fi fi -grep -q /var/run/ubus.sock /etc/nginx/conf.d/luci.locations && - sed -i 's#/var/run/ubus.sock#/var/run/ubus/ubus.sock#' /etc/nginx/conf.d/luci.locations - -if [ -x /etc/init.d/uhttpd ]; then - /etc/init.d/uhttpd disable - if [ -n "$(pgrep uhttpd)" ]; then - /etc/init.d/uhttpd stop - fi -fi - -/etc/init.d/nginx enable -if [ -n "$(pgrep nginx)" ]; then - /etc/init.d/nginx restart -else - /etc/init.d/nginx start -fi - -/etc/init.d/uwsgi enable -if [ -n "$(pgrep uwsgi)" ]; then - /etc/init.d/uwsgi restart -else - /etc/init.d/uwsgi start -fi - - exit 0 diff --git a/nginx/files-luci-support/70_nginx-luci-support-ssl b/nginx/files-luci-support/70_nginx-luci-support-ssl new file mode 100755 index 000000000..76ce3a819 --- /dev/null +++ b/nginx/files-luci-support/70_nginx-luci-support-ssl @@ -0,0 +1,48 @@ +#!/bin/sh + + +if [ -f "/etc/nginx/luci_nginx_ssl.conf" ] && [ -f "/etc/nginx/nginx.conf" ]; then + if [ ! "$(cat '/etc/nginx/nginx.conf' | grep 'return 301 https://$host$request_uri;')" ]; then + if [ -f "/etc/nginx/nginx.conf_old" ]; then + rm /etc/nginx/nginx.conf + else + mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_old + fi + mv /etc/nginx/luci_nginx_ssl.conf /etc/nginx/nginx.conf + core_number=$(grep -c ^processor /proc/cpuinfo) + sed -i "3s/.*/worker_processes "$core_number";/" /etc/nginx/nginx.conf + if [ -n "$(pgrep nginx)" ]; then + /etc/init.d/nginx restart + else + /etc/init.d/nginx start + fi + else + rm /etc/nginx/luci_nginx_ssl.conf + fi +fi + + +if [ ! -f "/etc/nginx/nginx.key" ]; then + + NGINX_KEY=/etc/nginx/nginx.key + NGINX_CER=/etc/nginx/nginx.cer + OPENSSL_BIN=/usr/bin/openssl + PX5G_BIN=/usr/sbin/px5g + + # Prefer px5g for certificate generation (existence evaluated last) + GENKEY_CMD="" + UNIQUEID=$(dd if=/dev/urandom bs=1 count=4 | hexdump -e '1/1 "%02x"') + [ -x "$OPENSSL_BIN" ] && GENKEY_CMD="$OPENSSL_BIN req -x509 -nodes" + [ -x "$PX5G_BIN" ] && GENKEY_CMD="$PX5G_BIN selfsigned" + [ -n "$GENKEY_CMD" ] && { + $GENKEY_CMD \ + -days 730 -newkey rsa:2048 -keyout "${NGINX_KEY}.new" -out "${NGINX_CER}.new" \ + -subj /C="ZZ"/ST="Somewhere"/L="Unknown"/O="OpenWrt""$UNIQUEID"/CN="OpenWrt" + sync + mv "${NGINX_KEY}.new" "${NGINX_KEY}" + mv "${NGINX_CER}.new" "${NGINX_CER}" + } +fi + + +exit 0 diff --git a/nginx/files-luci-support/luci_nginx.conf b/nginx/files-luci-support/luci_nginx.conf new file mode 100755 index 000000000..75f0f78a6 --- /dev/null +++ b/nginx/files-luci-support/luci_nginx.conf @@ -0,0 +1,52 @@ + +user root; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 0; + + client_body_buffer_size 10K; + client_header_buffer_size 1k; + client_max_body_size 1G; + large_client_header_buffers 2 1k; + + gzip on; + gzip_http_version 1.1; + gzip_vary on; + gzip_comp_level 1; + gzip_proxied any; + 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 80 default_server; + listen [::]:80 default_server; + server_name localhost; + + location ~* .(jpg|jpeg|png|gif|ico|css|js)$ { + expires 365d; + } + + include luci_uwsgi.conf; + + } + + include /etc/nginx/conf.d/*.conf; +} diff --git a/nginx/files-luci-support/luci_nginx_ssl.conf b/nginx/files-luci-support/luci_nginx_ssl.conf new file mode 100755 index 000000000..db33e554c --- /dev/null +++ b/nginx/files-luci-support/luci_nginx_ssl.conf @@ -0,0 +1,67 @@ + +user root; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 0; + + client_body_buffer_size 10K; + client_header_buffer_size 1k; + client_max_body_size 1G; + large_client_header_buffers 2 1k; + + gzip on; + gzip_http_version 1.1; + gzip_vary on; + gzip_comp_level 1; + gzip_proxied any; + 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 80 default_server; + listen [::]:80 default_server; + server_name _; + return 301 https://$host$request_uri; + } + + 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"; + ssl_session_tickets off; + + ssl_certificate /etc/nginx/nginx.cer; + ssl_certificate_key /etc/nginx/nginx.key; + + location ~* .(jpg|jpeg|png|gif|ico|css|js)$ { + expires 365d; + } + + include luci_uwsgi.conf; + + } + + include /etc/nginx/conf.d/*.conf; +} diff --git a/nginx/files-luci-support/luci.locations b/nginx/files-luci-support/luci_uwsgi.conf old mode 100644 new mode 100755 similarity index 94% rename from nginx/files-luci-support/luci.locations rename to nginx/files-luci-support/luci_uwsgi.conf index 374ee5d9f..3ea3de914 --- a/nginx/files-luci-support/luci.locations +++ b/nginx/files-luci-support/luci_uwsgi.conf @@ -13,5 +13,4 @@ location ~ /cgi-bin/cgi-(backup|download|upload|exec) { } location /luci-static { - error_log stderr crit; } diff --git a/nginx/files/nginx.init b/nginx/files/nginx.init index 300a8c657..528f390e1 100755 --- a/nginx/files/nginx.init +++ b/nginx/files/nginx.init @@ -5,69 +5,20 @@ START=80 USE_PROCD=1 -G_OPTS="daemon off;" - -NGINX_UTIL="/usr/bin/nginx-util" - -eval $("${NGINX_UTIL}" get_env) - -CONF="" - - -nginx_init() { - [ -z "${CONF}" ] || return # already called. +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 - rm -f "$(readlink "${UCI_CONF}")" - ${NGINX_UTIL} init_lan - - if [ -e "${UCI_CONF}" ] - then CONF="${UCI_CONF}" - else CONF="${NGINX_CONF}" - fi - - local message - message="$(/usr/sbin/nginx -t -c "${CONF}" -g "${G_OPTS}" 2>&1)" || - { - echo -e "${message}" | logger -t "nginx_init" -p "daemon.err" - logger -s -t "nginx_init" -p "daemon.err" "NOT using conf file!" - echo "show config to be used by: nginx -T -c '${CONF}'" >&2 - exit 1 - } - - logger -t "nginx_init" -p "daemon.info" "using ${CONF} (the test is ok)" -} - - -start_service() { - nginx_init - procd_open_instance - procd_set_param command /usr/sbin/nginx -c "${CONF}" -g "${G_OPTS}" - procd_set_param stdout 1 - procd_set_param stderr 1 - procd_set_param file "${CONF}" "${CONF_DIR}*.crt" "${CONF_DIR}*.key" \ - "${CONF_DIR}*.conf" "${CONF_DIR}*.locations" + procd_set_param command /usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;' + procd_set_param file /etc/nginx/nginx.conf procd_set_param respawn procd_close_instance } - - -reload_service() { - nginx_init - - if [ "$(cat "/proc/$(cat "/var/run/nginx.pid")/cmdline")" = \ - "nginx: master process /usr/sbin/nginx -c ${CONF} -g ${G_OPTS}" ] - then procd_send_signal nginx - else restart - fi -} - - -extra_command "relog" "Reopen log files (without reloading)" -relog() { - [ -d /var/log/nginx ] || mkdir -p /var/log/nginx - procd_send_signal nginx '*' USR1 -} 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/104-endianness_fix.patch b/nginx/patches/104-endianness_fix.patch deleted file mode 100644 index 4d8255e93..000000000 --- a/nginx/patches/104-endianness_fix.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/auto/endianness -+++ b/auto/endianness -@@ -12,6 +12,16 @@ checking for system byte ordering - - END - -+if [ "${CONFIG_BIG_ENDIAN}" != "y" ]; then -+ echo " little endian" -+ have=NGX_HAVE_LITTLE_ENDIAN . auto/have -+else -+ echo " big endian" -+fi -+ -+return -+ -+ - - cat << END > $NGX_AUTOTEST.c - diff --git a/nginx/patches/201-ignore-invalid-options.patch b/nginx/patches/201-ignore-invalid-options.patch index d208bf507..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 -@@ -400,8 +400,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/300-max-processes.patch b/nginx/patches/300-max-processes.patch new file mode 100755 index 000000000..f7465d434 --- /dev/null +++ b/nginx/patches/300-max-processes.patch @@ -0,0 +1,11 @@ +--- a/src/os/unix/ngx_process.h ++++ b/src/os/unix/ngx_process.h +@@ -44,7 +44,7 @@ typedef struct { + } ngx_exec_ctx_t; + + +-#define NGX_MAX_PROCESSES 1024 ++#define NGX_MAX_PROCESSES 8 + + #define NGX_PROCESS_NORESPAWN -1 + #define NGX_PROCESS_JUST_SPAWN -2 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: */