summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.hgtags1
-rw-r--r--CHANGES11
-rw-r--r--auto/isolation130
-rw-r--r--auto/modules/java31
-rw-r--r--auto/modules/php6
-rw-r--r--auto/modules/python34
-rw-r--r--auto/modules/ruby38
-rw-r--r--auto/sources7
-rw-r--r--docs/changes.xml47
-rw-r--r--pkg/deb/Makefile12
-rw-r--r--pkg/deb/Makefile.jsc-common2
-rw-r--r--pkg/docker/Dockerfile.full2
-rw-r--r--pkg/docker/Dockerfile.go1.11-dev2
-rw-r--r--pkg/docker/Dockerfile.minimal2
-rw-r--r--pkg/docker/Dockerfile.perl5.282
-rw-r--r--pkg/docker/Dockerfile.php7.32
-rw-r--r--pkg/docker/Dockerfile.python2.72
-rw-r--r--pkg/docker/Dockerfile.python3.72
-rw-r--r--pkg/docker/Dockerfile.ruby2.52
-rw-r--r--src/nxt_application.c824
-rw-r--r--src/nxt_application.h21
-rw-r--r--src/nxt_array.c39
-rw-r--r--src/nxt_array.h3
-rw-r--r--src/nxt_capability.c5
-rw-r--r--src/nxt_capability.h3
-rw-r--r--src/nxt_cert.c11
-rw-r--r--src/nxt_cert.h2
-rw-r--r--src/nxt_clone.h15
-rw-r--r--src/nxt_conf.h2
-rw-r--r--src/nxt_conf_validation.c391
-rw-r--r--src/nxt_controller.c222
-rw-r--r--src/nxt_external.c13
-rw-r--r--src/nxt_fs.c163
-rw-r--r--src/nxt_fs.h36
-rw-r--r--src/nxt_http.h7
-rw-r--r--src/nxt_http_request.c2
-rw-r--r--src/nxt_http_route.c408
-rw-r--r--src/nxt_http_static.c2
-rw-r--r--src/nxt_java.c133
-rw-r--r--src/nxt_main.h1
-rw-r--r--src/nxt_main_process.c1156
-rw-r--r--src/nxt_main_process.h19
-rw-r--r--src/nxt_php_sapi.c480
-rw-r--r--src/nxt_port.c4
-rw-r--r--src/nxt_port.h141
-rw-r--r--src/nxt_port_memory.c2
-rw-r--r--src/nxt_port_rpc.c28
-rw-r--r--src/nxt_port_rpc.h2
-rw-r--r--src/nxt_port_socket.c6
-rw-r--r--src/nxt_process.c874
-rw-r--r--src/nxt_process.h152
-rw-r--r--src/nxt_process_type.h2
-rw-r--r--src/nxt_python_wsgi.c22
-rw-r--r--src/nxt_router.c121
-rw-r--r--src/nxt_router.h3
-rw-r--r--src/nxt_runtime.c65
-rw-r--r--src/nxt_runtime.h1
-rw-r--r--src/nxt_signal_handlers.c67
-rw-r--r--src/nxt_string.c43
-rw-r--r--src/nxt_string.h3
-rw-r--r--src/nxt_unit.c2
-rw-r--r--src/nxt_unit_request.h1
-rw-r--r--src/nxt_unix.h3
-rw-r--r--src/nxt_worker_process.c118
-rw-r--r--src/perl/nxt_perl_psgi.c17
-rw-r--r--src/ruby/nxt_ruby.c25
-rw-r--r--test/go/ns_inspect/app.go21
-rw-r--r--test/php/targets/1.php4
-rw-r--r--test/php/targets/2/2.php4
-rw-r--r--test/php/targets/index.php4
-rw-r--r--test/python/ns_inspect/wsgi.py31
-rwxr-xr-xtest/run.py5
-rw-r--r--test/test_access_log.py5
-rw-r--r--test/test_configuration.py3
-rw-r--r--test/test_go_application.py2
-rw-r--r--test/test_go_isolation.py52
-rw-r--r--test/test_go_isolation_rootfs.py34
-rw-r--r--test/test_http_header.py3
-rw-r--r--test/test_java_application.py5
-rw-r--r--test/test_java_isolation_rootfs.py85
-rw-r--r--test/test_java_websockets.py5
-rw-r--r--test/test_node_application.py3
-rw-r--r--test/test_node_websockets.py5
-rw-r--r--test/test_perl_application.py3
-rw-r--r--test/test_php_application.py4
-rw-r--r--test/test_php_basic.py2
-rw-r--r--test/test_php_isolation.py57
-rw-r--r--test/test_php_targets.py129
-rw-r--r--test/test_proxy.py113
-rw-r--r--test/test_python_application.py6
-rw-r--r--test/test_python_basic.py2
-rw-r--r--test/test_python_environment.py2
-rw-r--r--test/test_python_isolation.py79
-rw-r--r--test/test_python_isolation_chroot.py57
-rw-r--r--test/test_python_procman.py5
-rw-r--r--test/test_respawn.py95
-rw-r--r--test/test_return.py2
-rw-r--r--test/test_routing.py251
-rw-r--r--test/test_routing_tls.py2
-rw-r--r--test/test_ruby_application.py3
-rw-r--r--test/test_ruby_isolation.py71
-rw-r--r--test/test_settings.py5
-rw-r--r--test/test_share_fallback.py23
-rw-r--r--test/test_static.py1
-rw-r--r--test/test_tls.py4
-rw-r--r--test/test_upstreams_rr.py4
-rw-r--r--test/test_usr1.py4
-rw-r--r--test/unit/applications/lang/go.py76
-rw-r--r--test/unit/applications/lang/java.py3
-rw-r--r--test/unit/applications/lang/node.py6
-rw-r--r--test/unit/applications/lang/python.py19
-rw-r--r--test/unit/applications/proto.py1
-rw-r--r--test/unit/applications/tls.py1
-rw-r--r--test/unit/applications/websockets.py9
-rw-r--r--test/unit/control.py1
-rw-r--r--test/unit/feature/isolation.py4
-rw-r--r--test/unit/http.py7
-rw-r--r--test/unit/main.py39
-rw-r--r--version4
119 files changed, 5344 insertions, 2016 deletions
diff --git a/.hgtags b/.hgtags
index 437b2f17..7f918c12 100644
--- a/.hgtags
+++ b/.hgtags
@@ -41,3 +41,4 @@ a3ea27be5ebde3d54e06cf6fe613cb4b53ab76d2 1.15.0-1
0361f3eda67a17295ab324c831cb9d8c560286ed 1.16.0-1
4b13438632bc37ca599113be90af64f6e2f09d83 1.17.0
e0658022962c0d5b0a32a1b0e3090bdec328e3da 1.17.0-1
+a34bc498d976affa5b50584d3d93a4a9a04d5c39 1.18.0
diff --git a/CHANGES b/CHANGES
index 3ffd06b7..5e49979f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,15 @@
+Changes with Unit 1.18.0 28 May 2020
+
+ *) Feature: the "rootfs" isolation option for changing root filesystem
+ for an application.
+
+ *) Feature: multiple "targets" in PHP applications.
+
+ *) Feature: support for percent-encoding in the "uri" and "arguments"
+ matching options and in the "pass" option.
+
+
Changes with Unit 1.17.0 16 Apr 2020
*) Feature: a "return" action with optional "location" for immediate
diff --git a/auto/isolation b/auto/isolation
index d231de12..4238b859 100644
--- a/auto/isolation
+++ b/auto/isolation
@@ -6,6 +6,9 @@
NXT_ISOLATION=NO
NXT_HAVE_CLONE=NO
NXT_HAVE_CLONE_NEWUSER=NO
+NXT_HAVE_MOUNT=NO
+NXT_HAVE_UNMOUNT=NO
+NXT_HAVE_ROOTFS=NO
nsflags="USER NS PID NET UTS CGROUP"
@@ -55,3 +58,130 @@ if [ $nxt_found = yes ]; then
fi
done
fi
+
+
+nxt_feature="Linux pivot_root()"
+nxt_feature_name=NXT_HAVE_PIVOT_ROOT
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <sys/syscall.h>
+
+ int main() {
+ return __NR_pivot_root;
+ }"
+. auto/feature
+
+
+nxt_feature="prctl(PR_SET_NO_NEW_PRIVS)"
+nxt_feature_name=NXT_HAVE_PR_SET_NO_NEW_PRIVS0
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <sys/prctl.h>
+
+ int main() {
+ return PR_SET_NO_NEW_PRIVS;
+ }"
+. auto/feature
+
+
+nxt_feature="Linux mount()"
+nxt_feature_name=NXT_HAVE_LINUX_MOUNT
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <sys/mount.h>
+
+ int main() {
+ return mount((void*)0, (void*)0, (void*)0, 0, (void*)0);
+ }"
+. auto/feature
+
+if [ $nxt_found = yes ]; then
+ NXT_HAVE_MOUNT=YES
+fi
+
+
+nxt_feature="Bind mount()"
+nxt_feature_name=NXT_HAVE_BIND_MOUNT
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <sys/mount.h>
+
+ int main() {
+ return MS_BIND | MS_REC
+ }"
+. auto/feature
+
+if [ $nxt_found = yes ]; then
+ NXT_HAVE_MOUNT=YES
+fi
+
+
+if [ $nxt_found = no ]; then
+ nxt_feature="FreeBSD nmount()"
+ nxt_feature_name=NXT_HAVE_FREEBSD_NMOUNT
+ nxt_feature_run=no
+ nxt_feature_incs=
+ nxt_feature_libs=
+ nxt_feature_test="#include <sys/mount.h>
+
+ int main() {
+ return nmount((void *)0, 0, 0);
+ }"
+ . auto/feature
+
+ if [ $nxt_found = yes ]; then
+ NXT_HAVE_MOUNT=YES
+ fi
+fi
+
+
+nxt_feature="Linux umount2()"
+nxt_feature_name=NXT_HAVE_LINUX_UMOUNT2
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <sys/mount.h>
+
+ int main() {
+ return umount2((void *)0, 0);
+ }"
+. auto/feature
+
+if [ $nxt_found = yes ]; then
+ NXT_HAVE_UNMOUNT=YES
+fi
+
+if [ $nxt_found = no ]; then
+ nxt_feature="unmount()"
+ nxt_feature_name=NXT_HAVE_UNMOUNT
+ nxt_feature_run=no
+ nxt_feature_incs=
+ nxt_feature_libs=
+ nxt_feature_test="#include <sys/mount.h>
+
+ int main() {
+ return unmount((void *)0, 0);
+ }"
+ . auto/feature
+
+ if [ $nxt_found = yes ]; then
+ NXT_HAVE_UNMOUNT=YES
+ fi
+fi
+
+if [ $NXT_HAVE_MOUNT = YES -a $NXT_HAVE_UNMOUNT = YES ]; then
+ NXT_HAVE_ROOTFS=YES
+
+ cat << END >> $NXT_AUTO_CONFIG_H
+
+#ifndef NXT_HAVE_ISOLATION_ROOTFS
+#define NXT_HAVE_ISOLATION_ROOTFS 1
+#endif
+
+END
+
+fi
diff --git a/auto/modules/java b/auto/modules/java
index 68b10836..2e6f292d 100644
--- a/auto/modules/java
+++ b/auto/modules/java
@@ -172,13 +172,13 @@ if [ -z "$NXT_JAVA_LIB_PATH" ]; then
exit 1
fi
- NXT_JAVA_LIB_PATH="${NXT_JAVA_LIB_PATH}/server"
+ NXT_JAVA_LIB_SERVER_PATH="${NXT_JAVA_LIB_PATH}/server"
$echo " $NXT_JAVA_LIB_PATH"
$echo "got library path $NXT_JAVA_LIB_PATH" >> $NXT_AUTOCONF_ERR
fi
-NXT_JAVA_LDFLAGS="-L${NXT_JAVA_LIB_PATH} -Wl,-rpath ${NXT_JAVA_LIB_PATH} -ljvm"
+NXT_JAVA_LDFLAGS="-L${NXT_JAVA_LIB_SERVER_PATH} -Wl,-rpath ${NXT_JAVA_LIB_SERVER_PATH} -ljvm"
nxt_found=no
@@ -227,6 +227,7 @@ NXT_JAVA_INSTALL_JARS=
NXT_JAVA_UNINSTALL_JARS=
NXT_JAVA_JARS=$NXT_BUILD_DIR/$NXT_JAVA_MODULE/nxt_jars.h
+NXT_JAVA_MOUNTS_HEADER=$NXT_BUILD_DIR/$NXT_JAVA_MODULE/nxt_java_mounts.h
mkdir -p $NXT_BUILD_DIR/$NXT_JAVA_MODULE
cat << END > $NXT_JAVA_JARS
@@ -308,6 +309,32 @@ cat << END >> $NXT_JAVA_JARS
#endif /* _NXT_JAVA_JARS_INCLUDED_ */
END
+NXT_JAVA_LIBJVM="$NXT_JAVA_LIB_SERVER_PATH/libjvm.so"
+
+if [ "$NXT_SYSTEM" = "Darwin" ]; then
+NXT_JAVA_LIBC_DIR="/usr/lib"
+else
+NXT_JAVA_LIBC_DIR=`ldd "$NXT_JAVA_LIBJVM" | grep libc.so | cut -d' ' -f3`
+NXT_JAVA_LIBC_DIR=`dirname $NXT_JAVA_LIBC_DIR`
+fi
+
+cat << END > $NXT_JAVA_MOUNTS_HEADER
+#ifndef _NXT_JAVA_MOUNTS_H_INCLUDED_
+#define _NXT_JAVA_MOUNTS_H_INCLUDED_
+
+
+static const nxt_fs_mount_t nxt_java_mounts[] = {
+ {(u_char *) "proc", (u_char *) "/proc", (u_char *) "proc", 0, NULL},
+ {(u_char *) "$NXT_JAVA_LIBC_DIR", (u_char *) "$NXT_JAVA_LIBC_DIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_JAVA_HOME", (u_char *) "$NXT_JAVA_HOME",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+};
+
+
+#endif /* _NXT_JAVA_MOUNTS_H_INCLUDED_ */
+END
+
$echo " + Java module: ${NXT_JAVA_MODULE}.unit.so"
. auto/cc/deps
diff --git a/auto/modules/php b/auto/modules/php
index e2e5498a..2cec2f44 100644
--- a/auto/modules/php
+++ b/auto/modules/php
@@ -100,7 +100,11 @@ if /bin/sh -c "${NXT_PHP_CONFIG} --version" >> $NXT_AUTOCONF_ERR 2>&1; then
`${NXT_PHP_CONFIG} --libs`"
else
- NXT_PHP_LIB="-lphp${NXT_PHP_VERSION%%.*}"
+ if [ $NXT_PHP_MAJOR_VERSION -ge 8 ]; then
+ NXT_PHP_LIB="-lphp"
+ else
+ NXT_PHP_LIB="-lphp${NXT_PHP_VERSION%%.*}"
+ fi
if [ "$NXT_PHP_LIB_PATH" != "" ]; then
# "php-config --ldflags" does not contain path to libphp, but
diff --git a/auto/modules/python b/auto/modules/python
index 6c8198f5..ad862f3c 100644
--- a/auto/modules/python
+++ b/auto/modules/python
@@ -68,6 +68,7 @@ if /bin/sh -c "$NXT_PYTHON_CONFIG --prefix" >> $NXT_AUTOCONF_ERR 2>&1; then
NXT_PYTHON_CONFIG="${NXT_PYTHON_CONFIG} --embed"
fi
+ NXT_PYTHON_EXEC=`${NXT_PYTHON_CONFIG} --exec-prefix`/bin/${NXT_PYTHON}
NXT_PYTHON_INCLUDE=`${NXT_PYTHON_CONFIG} --includes`
NXT_PYTHON_LIBS=`${NXT_PYTHON_CONFIG} --ldflags`
@@ -129,6 +130,37 @@ if grep ^$NXT_PYTHON_MODULE: $NXT_MAKEFILE 2>&1 > /dev/null; then
exit 1;
fi
+
+NXT_PYTHON_MOUNTS_HEADER=$NXT_BUILD_DIR/nxt_python_mounts.h
+
+$NXT_PYTHON_EXEC -c 'import os.path
+import sys
+pyver = "python" + str(sys.version_info[0]) + "." + str(sys.version_info[1])
+
+print("static const nxt_fs_mount_t nxt_python%d%d_mounts[] = {" % (sys.version_info[0], sys.version_info[1]))
+
+pattern = "{(u_char *) \"%s\", (u_char *) \"%s\", (u_char *) \"bind\", NXT_MS_BIND|NXT_MS_REC, NULL},"
+base = None
+for p in sys.path:
+ if len(p) > 0:
+ if os.path.basename(p) == pyver:
+ base = p
+
+if base is None:
+ raise Exception("failed to compute sys.path mount points")
+
+print(pattern % (base, base))
+
+for p in sys.path:
+ if len(p) > 0:
+ if not p.startswith(base):
+ print(pattern % (p, p))
+
+print("};\n\n")
+
+' >> $NXT_PYTHON_MOUNTS_HEADER
+
+
$echo " + Python module: ${NXT_PYTHON_MODULE}.unit.so"
. auto/cc/deps
@@ -165,7 +197,7 @@ END
done
-
+
cat << END >> $NXT_MAKEFILE
.PHONY: ${NXT_PYTHON_MODULE}
diff --git a/auto/modules/ruby b/auto/modules/ruby
index 407406ce..f7334cc7 100644
--- a/auto/modules/ruby
+++ b/auto/modules/ruby
@@ -51,6 +51,7 @@ $echo "configuring Ruby module ..." >> $NXT_AUTOCONF_ERR
NXT_RUBY=${NXT_RUBY=ruby}
NXT_RUBY_MODULE=${NXT_RUBY_MODULE=${NXT_RUBY}}
+NXT_RUBY_MOUNTS_HEADER=$NXT_BUILD_DIR/nxt_ruby_mounts.h
nxt_found=no
@@ -58,6 +59,14 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then
NXT_RUBY_RUBYHDRDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["rubyhdrdir"])'`
NXT_RUBY_ARCHHDRDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["rubyarchhdrdir"])'`
+ NXT_RUBY_SITEARCHDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["sitearchhdrdir"])'`
+ NXT_RUBY_SITEDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["sitedir"])'`
+ NXT_RUBY_LIBDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["rubylibdir"])'`
+ NXT_RUBY_TOPDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["topdir"])'`
+ NXT_RUBY_PREFIXDIR=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["rubylibprefix"])'`
+ NXT_RUBY_GEMDIR=`gem environment gemdir`
+ NXT_RUBY_GEMPATH=`gem environment gempath`
+
NXT_RUBY_INCPATH="-I$NXT_RUBY_ARCHHDRDIR -I$NXT_RUBY_RUBYHDRDIR"
NXT_RUBY_LIBNAME=`$NXT_RUBY -r rbconfig -e 'printf("%s",RbConfig::CONFIG["RUBY_SO_NAME"])'`
@@ -135,6 +144,35 @@ if grep ^$NXT_RUBY_MODULE: $NXT_MAKEFILE 2>&1 > /dev/null; then
exit 1;
fi
+
+cat << END > $NXT_RUBY_MOUNTS_HEADER
+
+static const nxt_fs_mount_t nxt_ruby_mounts[] = {
+ {(u_char *) "$NXT_RUBY_RUBYHDRDIR", (u_char *) "$NXT_RUBY_RUBYHDRDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_RUBY_ARCHHDRDIR", (u_char *) "$NXT_RUBY_ARCHHDRDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_RUBY_SITEDIR", (u_char *) "$NXT_RUBY_SITEDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_RUBY_LIBDIR", (u_char *) "$NXT_RUBY_LIBDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_RUBY_GEMDIR", (u_char *) "$NXT_RUBY_GEMDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_RUBY_TOPDIR", (u_char *) "$NXT_RUBY_TOPDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+ {(u_char *) "$NXT_RUBY_PREFIXDIR", (u_char *) "$NXT_RUBY_PREFIXDIR",
+ (u_char *) "bind", NXT_MS_BIND | NXT_MS_REC, NULL},
+
+END
+
+for path in `echo $NXT_RUBY_GEMPATH | tr ':' '\n'`; do
+ $echo "{(u_char *) \"$path\", (u_char *) \"$path\"," >> $NXT_RUBY_MOUNTS_HEADER
+ $echo "(u_char *) \"bind\", NXT_MS_BIND | NXT_MS_REC, NULL}," >> $NXT_RUBY_MOUNTS_HEADER
+done
+
+$echo "};" >> $NXT_RUBY_MOUNTS_HEADER
+
+
$echo " + Ruby module: ${NXT_RUBY_MODULE}.unit.so"
. auto/cc/deps
diff --git a/auto/sources b/auto/sources
index c6b34bbc..2075ca0f 100644
--- a/auto/sources
+++ b/auto/sources
@@ -78,7 +78,7 @@ NXT_LIB_SRCS=" \
src/nxt_conf.c \
src/nxt_conf_validation.c \
src/nxt_main_process.c \
- src/nxt_worker_process.c \
+ src/nxt_signal_handlers.c \
src/nxt_controller.c \
src/nxt_router.c \
src/nxt_h1proto.c \
@@ -177,6 +177,11 @@ NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
"
+if [ $NXT_HAVE_ROOTFS = YES ]; then
+ NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_fs.c"
+fi
+
+
if [ $NXT_TLS = YES ]; then
nxt_have=NXT_TLS . auto/have
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_TLS_SRCS"
diff --git a/docs/changes.xml b/docs/changes.xml
index 2ff5fcab..686896d4 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -13,6 +13,53 @@
unit-perl
unit-ruby
unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11"
+ ver="1.18.0" rev="1"
+ date="2020-05-28" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change>
+<para>
+NGINX Unit updated to 1.18.0.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit" ver="1.18.0" rev="1"
+ date="2020-05-28" time="18:00:00 +0300"
+ packager="Andrei Belov &lt;defan@nginx.com&gt;">
+
+<change type="feature">
+<para>
+the "rootfs" isolation option for changing root filesystem for an application.
+</para>
+</change>
+
+<change type="feature">
+<para>
+multiple "targets" in PHP applications.
+</para>
+</change>
+
+<change type="feature">
+<para>
+support for percent-encoding in the "uri" and "arguments" matching options
+and in the "pass" option.
+</para>
+</change>
+
+</changes>
+
+
+<changes apply="unit-php
+ unit-python unit-python2.7
+ unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7
+ unit-python3.8
+ unit-go
+ unit-perl
+ unit-ruby
+ unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11"
ver="1.17.0" rev="1"
date="2020-04-16" time="18:00:00 +0300"
packager="Andrei Belov &lt;defan@nginx.com&gt;">
diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile
index 797ff438..55bf7082 100644
--- a/pkg/deb/Makefile
+++ b/pkg/deb/Makefile
@@ -16,6 +16,18 @@ BUILD_DEPENDS = $(BUILD_DEPENDS_unit)
MODULES=
+# Ubuntu 20.04
+ifeq ($(CODENAME),focal)
+include Makefile.php
+include Makefile.python27
+include Makefile.python38
+include Makefile.go
+include Makefile.perl
+include Makefile.ruby
+include Makefile.jsc-common
+include Makefile.jsc11
+endif
+
# Ubuntu 19.10
ifeq ($(CODENAME),eoan)
include Makefile.php
diff --git a/pkg/deb/Makefile.jsc-common b/pkg/deb/Makefile.jsc-common
index 42b4ad74..928376c3 100644
--- a/pkg/deb/Makefile.jsc-common
+++ b/pkg/deb/Makefile.jsc-common
@@ -6,7 +6,7 @@ MODULE_SUMMARY_jsc_common= Java shared packages for NGINX Unit
MODULE_VERSION_jsc_common= $(VERSION)
MODULE_RELEASE_jsc_common= 1
-ifneq (,$(findstring $(CODENAME),eoan disco buster))
+ifneq (,$(findstring $(CODENAME),focal eoan disco buster))
JAVA_MINVERSION= 11
else
JAVA_MINVERSION= 8
diff --git a/pkg/docker/Dockerfile.full b/pkg/docker/Dockerfile.full
index fb9144c2..08593732 100644
--- a/pkg/docker/Dockerfile.full
+++ b/pkg/docker/Dockerfile.full
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.go1.11-dev b/pkg/docker/Dockerfile.go1.11-dev
index 2b213836..ee51f83e 100644
--- a/pkg/docker/Dockerfile.go1.11-dev
+++ b/pkg/docker/Dockerfile.go1.11-dev
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal
index af97aa4f..08be78bc 100644
--- a/pkg/docker/Dockerfile.minimal
+++ b/pkg/docker/Dockerfile.minimal
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.perl5.28 b/pkg/docker/Dockerfile.perl5.28
index 793b48d1..a8f84c17 100644
--- a/pkg/docker/Dockerfile.perl5.28
+++ b/pkg/docker/Dockerfile.perl5.28
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.php7.3 b/pkg/docker/Dockerfile.php7.3
index 5e3f0e97..fe60cf44 100644
--- a/pkg/docker/Dockerfile.php7.3
+++ b/pkg/docker/Dockerfile.php7.3
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python2.7 b/pkg/docker/Dockerfile.python2.7
index 9e3a431c..f80ca098 100644
--- a/pkg/docker/Dockerfile.python2.7
+++ b/pkg/docker/Dockerfile.python2.7
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.python3.7 b/pkg/docker/Dockerfile.python3.7
index 2517896b..8d5b4d9b 100644
--- a/pkg/docker/Dockerfile.python3.7
+++ b/pkg/docker/Dockerfile.python3.7
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/pkg/docker/Dockerfile.ruby2.5 b/pkg/docker/Dockerfile.ruby2.5
index 7258bd28..365d71ac 100644
--- a/pkg/docker/Dockerfile.ruby2.5
+++ b/pkg/docker/Dockerfile.ruby2.5
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
-ENV UNIT_VERSION 1.17.0-1~buster
+ENV UNIT_VERSION 1.18.0-1~buster
RUN set -x \
&& apt-get update \
diff --git a/src/nxt_application.c b/src/nxt_application.c
index bebe3907..566bf256 100644
--- a/src/nxt_application.c
+++ b/src/nxt_application.c
@@ -17,14 +17,21 @@
#include <glob.h>
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+#include <sys/prctl.h>
+#endif
+
typedef struct {
nxt_app_type_t type;
nxt_str_t version;
nxt_str_t file;
+ nxt_array_t *mounts;
} nxt_module_t;
+static nxt_int_t nxt_discovery_start(nxt_task_t *task,
+ nxt_process_data_t *data);
static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path);
static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp,
nxt_array_t *modules, const char *name);
@@ -34,7 +41,45 @@ static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data);
static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task,
const char *name);
+static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process,
+ nxt_mp_t *mp);
+static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process);
static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment);
+static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src);
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+static nxt_int_t nxt_app_prepare_rootfs(nxt_task_t *task,
+ nxt_process_t *process);
+static nxt_int_t nxt_app_prepare_lang_mounts(nxt_task_t *task,
+ nxt_process_t *process, nxt_array_t *syspaths);
+static nxt_int_t nxt_app_set_isolation_rootfs(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_t *process);
+#endif
+
+static nxt_int_t nxt_app_set_isolation(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_t *process);
+
+#if (NXT_HAVE_CLONE)
+static nxt_int_t nxt_app_set_isolation_namespaces(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_t *process);
+static nxt_int_t nxt_app_clone_flags(nxt_task_t *task,
+ nxt_conf_value_t *namespaces, nxt_clone_t *clone);
+#endif
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+static nxt_int_t nxt_app_set_isolation_creds(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_t *process);
+static nxt_int_t nxt_app_isolation_credential_map(nxt_task_t *task,
+ nxt_mp_t *mem_pool, nxt_conf_value_t *map_array,
+ nxt_clone_credential_map_t *map);
+#endif
+
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+static nxt_int_t nxt_app_set_isolation_new_privs(nxt_task_t *task,
+ nxt_conf_value_t *isolation, nxt_process_t *process);
+#endif
+
+nxt_str_t nxt_server = nxt_string(NXT_SERVER);
static uint32_t compat[] = {
@@ -42,14 +87,53 @@ static uint32_t compat[] = {
};
-nxt_str_t nxt_server = nxt_string(NXT_SERVER);
+static nxt_app_module_t *nxt_app;
-static nxt_app_module_t *nxt_app;
+static const nxt_port_handlers_t nxt_discovery_process_port_handlers = {
+ .quit = nxt_signal_quit_handler,
+ .new_port = nxt_port_new_port_handler,
+ .change_file = nxt_port_change_log_file_handler,
+ .mmap = nxt_port_mmap_handler,
+ .data = nxt_port_data_handler,
+ .remove_pid = nxt_port_remove_pid_handler,
+ .rpc_ready = nxt_port_rpc_handler,
+ .rpc_error = nxt_port_rpc_handler,
+};
-nxt_int_t
-nxt_discovery_start(nxt_task_t *task, void *data)
+static const nxt_port_handlers_t nxt_app_process_port_handlers = {
+ .quit = nxt_signal_quit_handler,
+ .rpc_ready = nxt_port_rpc_handler,
+ .rpc_error = nxt_port_rpc_handler,
+};
+
+
+const nxt_process_init_t nxt_discovery_process = {
+ .name = "discovery",
+ .type = NXT_PROCESS_DISCOVERY,
+ .prefork = NULL,
+ .restart = 0,
+ .setup = nxt_process_core_setup,
+ .start = nxt_discovery_start,
+ .port_handlers = &nxt_discovery_process_port_handlers,
+ .signals = nxt_process_signals,
+};
+
+
+const nxt_process_init_t nxt_app_process = {
+ .type = NXT_PROCESS_APP,
+ .setup = nxt_app_setup,
+ .prefork = nxt_app_prefork,
+ .restart = 0,
+ .start = NULL, /* set to module->start */
+ .port_handlers = &nxt_app_process_port_handlers,
+ .signals = nxt_process_signals,
+};
+
+
+static nxt_int_t
+nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data)
{
uint32_t stream;
nxt_buf_t *b;
@@ -57,7 +141,7 @@ nxt_discovery_start(nxt_task_t *task, void *data)
nxt_port_t *main_port, *discovery_port;
nxt_runtime_t *rt;
- nxt_debug(task, "DISCOVERY");
+ nxt_log(task, NXT_LOG_INFO, "discovery started");
rt = task->thread->runtime;
@@ -93,16 +177,17 @@ nxt_discovery_start(nxt_task_t *task, void *data)
static nxt_buf_t *
nxt_discovery_modules(nxt_task_t *task, const char *path)
{
- char *name;
- u_char *p, *end;
- size_t size;
- glob_t glb;
- nxt_mp_t *mp;
- nxt_buf_t *b;
- nxt_int_t ret;
- nxt_uint_t i, n;
- nxt_array_t *modules;
- nxt_module_t *module;
+ char *name;
+ u_char *p, *end;
+ size_t size;
+ glob_t glb;
+ nxt_mp_t *mp;
+ nxt_buf_t *b;
+ nxt_int_t ret;
+ nxt_uint_t i, n, j;
+ nxt_array_t *modules, *mounts;
+ nxt_module_t *module;
+ nxt_fs_mount_t *mnt;
b = NULL;
@@ -145,11 +230,26 @@ nxt_discovery_modules(nxt_task_t *task, const char *path)
size += nxt_length("{\"type\": ,");
size += nxt_length(" \"version\": \"\",");
- size += nxt_length(" \"file\": \"\"},");
+ size += nxt_length(" \"file\": \"\",");
+ size += nxt_length(" \"mounts\": []},");
size += NXT_INT_T_LEN
+ module[i].version.length
+ module[i].file.length;
+
+ mounts = module[i].mounts;
+
+ size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", "
+ "\"fstype\": \"\", \"flags\": , "
+ "\"data\": \"\"},");
+
+ mnt = mounts->elts;
+
+ for (j = 0; j < mounts->nelts; j++) {
+ size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst)
+ + nxt_strlen(mnt[j].fstype) + NXT_INT_T_LEN
+ + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data));
+ }
}
b = nxt_buf_mem_alloc(mp, size, 0);
@@ -164,12 +264,34 @@ nxt_discovery_modules(nxt_task_t *task, const char *path)
*p++ = '[';
for (i = 0; i < n; i++) {
- p = nxt_sprintf(p, end,
- "{\"type\": %d, \"version\": \"%V\", \"file\": \"%V\"},",
- module[i].type, &module[i].version, &module[i].file);
+ mounts = module[i].mounts;
+
+ p = nxt_sprintf(p, end, "{\"type\": %d, \"version\": \"%V\", "
+ "\"file\": \"%V\", \"mounts\": [",
+ module[i].type, &module[i].version, &module[i].file);
+
+ mnt = mounts->elts;
+ for (j = 0; j < mounts->nelts; j++) {
+ p = nxt_sprintf(p, end,
+ "{\"src\": \"%s\", \"dst\": \"%s\", "
+ "\"fstype\": \"%s\", \"flags\": %d, "
+ "\"data\": \"%s\"},",
+ mnt[j].src, mnt[j].dst, mnt[j].fstype, mnt[j].flags,
+ mnt[j].data == NULL ? (u_char *) "" : mnt[j].data);
+ }
+
+ *p++ = ']';
+ *p++ = '}';
+ *p++ = ',';
}
*p++ = ']';
+
+ if (nxt_slow_path(p >= end)) {
+ nxt_alert(task, "discovery write past the buffer");
+ goto fail;
+ }
+
b->mem.free = p;
fail:
@@ -184,13 +306,16 @@ static nxt_int_t
nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules,
const char *name)
{
- void *dl;
- nxt_str_t version;
- nxt_int_t ret;
- nxt_uint_t i, n;
- nxt_module_t *module;
- nxt_app_type_t type;
- nxt_app_module_t *app;
+ void *dl;
+ nxt_str_t version;
+ nxt_int_t ret;
+ nxt_uint_t i, j, n;
+ nxt_array_t *mounts;
+ nxt_module_t *module;
+ nxt_app_type_t type;
+ nxt_fs_mount_t *to;
+ nxt_app_module_t *app;
+ const nxt_fs_mount_t *from;
/*
* Only memory allocation failure should return NXT_ERROR.
@@ -267,6 +392,47 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules,
nxt_memcpy(module->file.start, name, module->file.length);
+ module->mounts = nxt_array_create(mp, app->nmounts,
+ sizeof(nxt_fs_mount_t));
+
+ if (nxt_slow_path(module->mounts == NULL)) {
+ goto fail;
+ }
+
+ mounts = module->mounts;
+
+ for (j = 0; j < app->nmounts; j++) {
+ from = &app->mounts[j];
+ to = nxt_array_zero_add(mounts);
+ if (nxt_slow_path(to == NULL)) {
+ goto fail;
+ }
+
+ to->src = nxt_cstr_dup(mp, to->src, from->src);
+ if (nxt_slow_path(to->src == NULL)) {
+ goto fail;
+ }
+
+ to->dst = nxt_cstr_dup(mp, to->dst, from->dst);
+ if (nxt_slow_path(to->dst == NULL)) {
+ goto fail;
+ }
+
+ to->fstype = nxt_cstr_dup(mp, to->fstype, from->fstype);
+ if (nxt_slow_path(to->fstype == NULL)) {
+ goto fail;
+ }
+
+ if (from->data != NULL) {
+ to->data = nxt_cstr_dup(mp, to->data, from->data);
+ if (nxt_slow_path(to->data == NULL)) {
+ goto fail;
+ }
+ }
+
+ to->flags = from->flags;
+ }
+
} else {
nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror());
}
@@ -301,18 +467,110 @@ nxt_discovery_completion_handler(nxt_task_t *task, void *obj, void *data)
static void
nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data)
{
- nxt_worker_process_quit_handler(task, msg);
+ nxt_signal_quit_handler(task, msg);
}
-nxt_int_t
-nxt_app_start(nxt_task_t *task, void *data)
+static nxt_int_t
+nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp)
+{
+ nxt_int_t cap_setid, cap_chroot;
+ nxt_int_t ret;
+ nxt_runtime_t *rt;
+ nxt_common_app_conf_t *app_conf;
+ nxt_app_lang_module_t *lang;
+
+ rt = task->thread->runtime;
+ app_conf = process->data.app;
+ cap_setid = rt->capabilities.setid;
+ cap_chroot = rt->capabilities.chroot;
+
+ lang = nxt_app_lang_module(rt, &app_conf->type);
+
+ nxt_assert(lang != NULL);
+
+ if (app_conf->isolation != NULL) {
+ ret = nxt_app_set_isolation(task, app_conf->isolation, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) {
+ cap_setid = 1;
+ cap_chroot = 1;
+ }
+#endif
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ if (process->isolation.rootfs != NULL) {
+ if (!cap_chroot) {
+ nxt_log(task, NXT_LOG_ERR,
+ "The \"rootfs\" field requires privileges");
+
+ return NXT_ERROR;
+ }
+
+ if (lang->mounts != NULL && lang->mounts->nelts > 0) {
+ ret = nxt_app_prepare_lang_mounts(task, process, lang->mounts);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+ }
+#endif
+
+ if (cap_setid) {
+ ret = nxt_process_creds_set(task, process, &app_conf->user,
+ &app_conf->group);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ } else {
+ if (!nxt_str_eq(&app_conf->user, (u_char *) rt->user_cred.user,
+ nxt_strlen(rt->user_cred.user)))
+ {
+ nxt_alert(task, "cannot set user \"%V\" for app \"%V\": "
+ "missing capabilities", &app_conf->user, &app_conf->name);
+
+ return NXT_ERROR;
+ }
+
+ if (app_conf->group.length > 0
+ && !nxt_str_eq(&app_conf->group, (u_char *) rt->group,
+ nxt_strlen(rt->group)))
+ {
+ nxt_alert(task, "cannot set group \"%V\" for app \"%V\": "
+ "missing capabilities", &app_conf->group,
+ &app_conf->name);
+
+ return NXT_ERROR;
+ }
+ }
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ ret = nxt_process_vldt_isolation_creds(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+#endif
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_app_setup(nxt_task_t *task, nxt_process_t *process)
{
nxt_int_t ret;
+ nxt_process_init_t *init;
nxt_app_lang_module_t *lang;
nxt_common_app_conf_t *app_conf;
- app_conf = data;
+ app_conf = process->data.app;
lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type);
if (nxt_slow_path(lang == NULL)) {
@@ -332,14 +590,37 @@ nxt_app_start(nxt_task_t *task, void *data)
}
}
- if (nxt_app->pre_init != NULL) {
- ret = nxt_app->pre_init(task, data);
+ if (nxt_slow_path(nxt_app_set_environment(app_conf->environment)
+ != NXT_OK))
+ {
+ nxt_alert(task, "failed to set environment");
+ return NXT_ERROR;
+ }
+
+ if (nxt_app->setup != NULL) {
+ ret = nxt_app->setup(task, process, app_conf);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
}
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ if (process->isolation.rootfs != NULL) {
+ if (process->isolation.mounts != NULL) {
+ ret = nxt_app_prepare_rootfs(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ ret = nxt_process_change_root(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+#endif
+
if (app_conf->working_directory != NULL
&& app_conf->working_directory[0] != 0)
{
@@ -353,23 +634,13 @@ nxt_app_start(nxt_task_t *task, void *data)
}
}
- if (nxt_slow_path(nxt_app_set_environment(app_conf->environment)
- != NXT_OK))
- {
- nxt_alert(task, "failed to set environment");
- return NXT_ERROR;
- }
-
- ret = nxt_app->init(task, data);
+ init = nxt_process_init(process);
- if (nxt_slow_path(ret != NXT_OK)) {
- nxt_debug(task, "application init failed");
+ init->start = nxt_app->start;
- } else {
- nxt_debug(task, "application init done");
- }
+ process->state = NXT_PROCESS_STATE_CREATED;
- return ret;
+ return NXT_OK;
}
@@ -429,6 +700,465 @@ nxt_app_set_environment(nxt_conf_value_t *environment)
}
+static nxt_int_t
+nxt_app_set_isolation(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_t *process)
+{
+#if (NXT_HAVE_CLONE)
+ if (nxt_slow_path(nxt_app_set_isolation_namespaces(task, isolation, process)
+ != NXT_OK))
+ {
+ return NXT_ERROR;
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if (nxt_slow_path(nxt_app_set_isolation_creds(task, isolation, process)
+ != NXT_OK))
+ {
+ return NXT_ERROR;
+ }
+#endif
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ if (nxt_slow_path(nxt_app_set_isolation_rootfs(task, isolation, process)
+ != NXT_OK))
+ {
+ return NXT_ERROR;
+ }
+#endif
+
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+ if (nxt_slow_path(nxt_app_set_isolation_new_privs(task, isolation, process)
+ != NXT_OK))
+ {
+ return NXT_ERROR;
+ }
+#endif
+
+ return NXT_OK;
+}
+
+
+#if (NXT_HAVE_CLONE)
+
+static nxt_int_t
+nxt_app_set_isolation_namespaces(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_t *process)
+{
+ nxt_int_t ret;
+ nxt_conf_value_t *obj;
+
+ static nxt_str_t nsname = nxt_string("namespaces");
+
+ obj = nxt_conf_get_object_member(isolation, &nsname, NULL);
+ if (obj != NULL) {
+ ret = nxt_app_clone_flags(task, obj, &process->isolation.clone);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+static nxt_int_t
+nxt_app_set_isolation_rootfs(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_t *process)
+{
+ nxt_str_t str;
+ nxt_conf_value_t *obj;
+
+ static nxt_str_t rootfs_name = nxt_string("rootfs");
+
+ obj = nxt_conf_get_object_member(isolation, &rootfs_name, NULL);
+ if (obj != NULL) {
+ nxt_conf_get_string(obj, &str);
+
+ if (nxt_slow_path(str.length <= 1 || str.start[0] != '/')) {
+ nxt_log(task, NXT_LOG_ERR, "rootfs requires an absolute path other "
+ "than \"/\" but given \"%V\"", &str);
+
+ return NXT_ERROR;
+ }
+
+ if (str.start[str.length - 1] == '/') {
+ str.length--;
+ }
+
+ process->isolation.rootfs = nxt_mp_alloc(process->mem_pool,
+ str.length + 1);
+
+ if (nxt_slow_path(process->isolation.rootfs == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(process->isolation.rootfs, str.start, str.length);
+
+ process->isolation.rootfs[str.length] = '\0';
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+
+static nxt_int_t
+nxt_app_set_isolation_new_privs(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_t *process)
+{
+ nxt_conf_value_t *obj;
+
+ static nxt_str_t new_privs_name = nxt_string("new_privs");
+
+ obj = nxt_conf_get_object_member(isolation, &new_privs_name, NULL);
+ if (obj != NULL) {
+ process->isolation.new_privs = nxt_conf_get_boolean(obj);
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+
+static nxt_int_t
+nxt_app_set_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation,
+ nxt_process_t *process)
+{
+ nxt_int_t ret;
+ nxt_clone_t *clone;
+ nxt_conf_value_t *array;
+
+ static nxt_str_t uidname = nxt_string("uidmap");
+ static nxt_str_t gidname = nxt_string("gidmap");
+
+ clone = &process->isolation.clone;
+
+ array = nxt_conf_get_object_member(isolation, &uidname, NULL);
+ if (array != NULL) {
+ ret = nxt_app_isolation_credential_map(task, process->mem_pool, array,
+ &clone->uidmap);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ array = nxt_conf_get_object_member(isolation, &gidname, NULL);
+ if (array != NULL) {
+ ret = nxt_app_isolation_credential_map(task, process->mem_pool, array,
+ &clone->gidmap);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_app_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map)
+{
+ nxt_int_t ret;
+ nxt_uint_t i;
+ nxt_conf_value_t *obj;
+
+ static nxt_conf_map_t nxt_clone_map_entry_conf[] = {
+ {
+ nxt_string("container"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_clone_map_entry_t, container),
+ },
+
+ {
+ nxt_string("host"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_clone_map_entry_t, host),
+ },
+
+ {
+ nxt_string("size"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_clone_map_entry_t, size),
+ },
+ };
+
+ map->size = nxt_conf_array_elements_count(map_array);
+
+ if (map->size == 0) {
+ return NXT_OK;
+ }
+
+ map->map = nxt_mp_alloc(mp, map->size * sizeof(nxt_clone_map_entry_t));
+ if (nxt_slow_path(map->map == NULL)) {
+ return NXT_ERROR;
+ }
+
+ for (i = 0; i < map->size; i++) {
+ obj = nxt_conf_get_array_element(map_array, i);
+
+ ret = nxt_conf_map_object(mp, obj, nxt_clone_map_entry_conf,
+ nxt_nitems(nxt_clone_map_entry_conf),
+ map->map + i);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "clone map entry map error");
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+#if (NXT_HAVE_CLONE)
+
+static nxt_int_t
+nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces,
+ nxt_clone_t *clone)
+{
+ uint32_t index;
+ nxt_str_t name;
+ nxt_int_t flag;
+ nxt_conf_value_t *value;
+
+ index = 0;
+
+ for ( ;; ) {
+ value = nxt_conf_next_object_member(namespaces, &name, &index);
+
+ if (value == NULL) {
+ break;
+ }
+
+ flag = 0;
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if (nxt_str_eq(&name, "credential", 10)) {
+ flag = CLONE_NEWUSER;
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWPID)
+ if (nxt_str_eq(&name, "pid", 3)) {
+ flag = CLONE_NEWPID;
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWNET)
+ if (nxt_str_eq(&name, "network", 7)) {
+ flag = CLONE_NEWNET;
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWUTS)
+ if (nxt_str_eq(&name, "uname", 5)) {
+ flag = CLONE_NEWUTS;
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWNS)
+ if (nxt_str_eq(&name, "mount", 5)) {
+ flag = CLONE_NEWNS;
+ }
+#endif
+
+#if (NXT_HAVE_CLONE_NEWCGROUP)
+ if (nxt_str_eq(&name, "cgroup", 6)) {
+ flag = CLONE_NEWCGROUP;
+ }
+#endif
+
+ if (!flag) {
+ nxt_alert(task, "unknown namespace flag: \"%V\"", &name);
+ return NXT_ERROR;
+ }
+
+ if (nxt_conf_get_boolean(value)) {
+ clone->flags |= flag;
+ }
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+static nxt_int_t
+nxt_app_prepare_lang_mounts(nxt_task_t *task, nxt_process_t *process,
+ nxt_array_t *lang_mounts)
+{
+ u_char *p;
+ size_t i, n, rootfs_len, len;
+ nxt_mp_t *mp;
+ nxt_array_t *mounts;
+ const u_char *rootfs;
+ nxt_fs_mount_t *mnt, *lang_mnt;
+
+ rootfs = process->isolation.rootfs;
+ rootfs_len = nxt_strlen(rootfs);
+ mp = process->mem_pool;
+
+ /* copy to init mem pool */
+ mounts = nxt_array_copy(mp, NULL, lang_mounts);
+ if (mounts == NULL) {
+ return NXT_ERROR;
+ }
+
+ n = mounts->nelts;
+ mnt = mounts->elts;
+ lang_mnt = lang_mounts->elts;
+
+ for (i = 0; i < n; i++) {
+ len = nxt_strlen(lang_mnt[i].dst);
+
+ mnt[i].dst = nxt_mp_alloc(mp, rootfs_len + len + 1);
+ if (mnt[i].dst == NULL) {
+ return NXT_ERROR;
+ }
+
+ p = nxt_cpymem(mnt[i].dst, rootfs, rootfs_len);
+ p = nxt_cpymem(p, lang_mnt[i].dst, len);
+ *p = '\0';
+ }
+
+ process->isolation.mounts = mounts;
+
+ return NXT_OK;
+}
+
+
+
+static nxt_int_t
+nxt_app_prepare_rootfs(nxt_task_t *task, nxt_process_t *process)
+{
+ size_t i, n;
+ nxt_int_t ret, hasproc;
+ struct stat st;
+ nxt_array_t *mounts;
+ const u_char *dst;
+ nxt_fs_mount_t *mnt;
+
+ hasproc = 0;
+
+#if (NXT_HAVE_CLONE_NEWPID) && (NXT_HAVE_CLONE_NEWNS)
+ nxt_fs_mount_t mount;
+
+ if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)
+ && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS))
+ {
+ /*
+ * This mount point will automatically be gone when the namespace is
+ * destroyed.
+ */
+
+ mount.fstype = (u_char *) "proc";
+ mount.src = (u_char *) "proc";
+ mount.dst = (u_char *) "/proc";
+ mount.data = (u_char *) "";
+ mount.flags = 0;
+
+ ret = nxt_fs_mkdir_all(mount.dst, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (nxt_fast_path(ret == NXT_OK)) {
+ ret = nxt_fs_mount(task, &mount);
+ if (nxt_fast_path(ret == NXT_OK)) {
+ hasproc = 1;
+ }
+
+ } else {
+ nxt_log(task, NXT_LOG_WARN, "mkdir(%s) %E", mount.dst, nxt_errno);
+ }
+ }
+#endif
+
+ mounts = process->isolation.mounts;
+
+ n = mounts->nelts;
+ mnt = mounts->elts;
+
+ for (i = 0; i < n; i++) {
+ dst = mnt[i].dst;
+
+ if (nxt_slow_path(nxt_memcmp(mnt[i].fstype, "bind", 4) == 0
+ && stat((const char *) mnt[i].src, &st) != 0))
+ {
+ nxt_log(task, NXT_LOG_WARN, "host path not found: %s", mnt[i].src);
+ continue;
+ }
+
+ if (hasproc && nxt_memcmp(mnt[i].fstype, "proc", 4) == 0
+ && nxt_memcmp(mnt[i].dst, "/proc", 5) == 0)
+ {
+ continue;
+ }
+
+ ret = nxt_fs_mkdir_all(dst, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "mkdir(%s) %E", dst, nxt_errno);
+ goto undo;
+ }
+
+ ret = nxt_fs_mount(task, &mnt[i]);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto undo;
+ }
+ }
+
+ return NXT_OK;
+
+undo:
+
+ n = i + 1;
+
+ for (i = 0; i < n; i++) {
+ nxt_fs_unmount(mnt[i].dst);
+ }
+
+ return NXT_ERROR;
+}
+
+#endif
+
+
+static u_char *
+nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src)
+{
+ u_char *p;
+ size_t len;
+
+ len = nxt_strlen(src);
+
+ if (dst == NULL) {
+ dst = nxt_mp_alloc(mp, len + 1);
+ if (nxt_slow_path(dst == NULL)) {
+ return NULL;
+ }
+ }
+
+ p = nxt_cpymem(dst, src, len);
+ *p = '\0';
+
+ return dst;
+}
+
+
nxt_app_lang_module_t *
nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name)
{
@@ -539,7 +1269,7 @@ nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init)
nxt_fd_blocking(task, main_port->pair[1]);
- init->ready_stream = my_port->process->init->stream;
+ init->ready_stream = my_port->process->stream;
init->read_port.id.pid = my_port->pid;
init->read_port.id.id = my_port->id;
diff --git a/src/nxt_application.h b/src/nxt_application.h
index e7177887..3144dc3f 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -27,6 +27,8 @@ typedef enum {
typedef struct nxt_app_module_s nxt_app_module_t;
+typedef nxt_int_t (*nxt_application_setup_t)(nxt_task_t *task,
+ nxt_process_t *process, nxt_common_app_conf_t *conf);
typedef struct {
@@ -34,12 +36,10 @@ typedef struct {
u_char *version;
char *file;
nxt_app_module_t *module;
+ nxt_array_t *mounts; /* of nxt_fs_mount_t */
} nxt_app_lang_module_t;
-typedef struct nxt_common_app_conf_s nxt_common_app_conf_t;
-
-
typedef struct {
char *executable;
nxt_conf_value_t *arguments;
@@ -54,9 +54,7 @@ typedef struct {
typedef struct {
- char *root;
- nxt_str_t script;
- nxt_str_t index;
+ nxt_conf_value_t *targets;
nxt_conf_value_t *options;
} nxt_php_app_conf_t;
@@ -101,6 +99,8 @@ struct nxt_common_app_conf_s {
nxt_ruby_app_conf_t ruby;
nxt_java_app_conf_t java;
} u;
+
+ nxt_conf_value_t *self;
};
@@ -111,10 +111,11 @@ struct nxt_app_module_s {
nxt_str_t type;
const char *version;
- nxt_int_t (*pre_init)(nxt_task_t *task,
- nxt_common_app_conf_t *conf);
- nxt_int_t (*init)(nxt_task_t *task,
- nxt_common_app_conf_t *conf);
+ const nxt_fs_mount_t *mounts;
+ nxt_uint_t nmounts;
+
+ nxt_application_setup_t setup;
+ nxt_process_start_t start;
};
diff --git a/src/nxt_array.c b/src/nxt_array.c
index 82019f92..6fe9ad6a 100644
--- a/src/nxt_array.c
+++ b/src/nxt_array.c
@@ -109,3 +109,42 @@ nxt_array_remove(nxt_array_t *array, void *elt)
array->nelts--;
}
+
+
+nxt_array_t *
+nxt_array_copy(nxt_mp_t *mp, nxt_array_t *dst, nxt_array_t *src)
+{
+ void *data;
+ uint32_t i, size;
+
+ size = src->size;
+
+ if (dst == NULL) {
+ dst = nxt_array_create(mp, src->nelts, size);
+ if (nxt_slow_path(dst == NULL)) {
+ return NULL;
+ }
+ }
+
+ nxt_assert(size == dst->size);
+
+ if (dst->nalloc >= src->nelts) {
+ nxt_memcpy(dst->elts, src->elts, src->nelts * size);
+
+ } else {
+ nxt_memcpy(dst->elts, src->elts, dst->nelts * size);
+
+ for (i = dst->nelts; i < src->nelts; i++) {
+ data = nxt_array_add(dst);
+ if (nxt_slow_path(data == NULL)) {
+ return NULL;
+ }
+
+ nxt_memcpy(data, src->elts + (i * size), size);
+ }
+ }
+
+ dst->nelts = src->nelts;
+
+ return dst;
+}
diff --git a/src/nxt_array.h b/src/nxt_array.h
index 8d2b14f1..5762ec27 100644
--- a/src/nxt_array.h
+++ b/src/nxt_array.h
@@ -24,7 +24,8 @@ NXT_EXPORT void nxt_array_destroy(nxt_array_t *array);
NXT_EXPORT void *nxt_array_add(nxt_array_t *array);
NXT_EXPORT void *nxt_array_zero_add(nxt_array_t *array);
NXT_EXPORT void nxt_array_remove(nxt_array_t *array, void *elt);
-
+NXT_EXPORT nxt_array_t *nxt_array_copy(nxt_mp_t *mp, nxt_array_t *dst,
+ nxt_array_t *src);
#define \
nxt_array_last(array) \
diff --git a/src/nxt_capability.c b/src/nxt_capability.c
index dfa7a834..24fd55d0 100644
--- a/src/nxt_capability.c
+++ b/src/nxt_capability.c
@@ -39,6 +39,7 @@ nxt_capability_set(nxt_task_t *task, nxt_capabilities_t *cap)
if (geteuid() == 0) {
cap->setid = 1;
+ cap->chroot = 1;
return NXT_OK;
}
@@ -91,6 +92,10 @@ nxt_capability_specific_set(nxt_task_t *task, nxt_capabilities_t *cap)
return NXT_ERROR;
}
+ if ((val->effective & (1 << CAP_SYS_CHROOT)) != 0) {
+ cap->chroot = 1;
+ }
+
if ((val->effective & (1 << CAP_SETUID)) == 0) {
return NXT_OK;
}
diff --git a/src/nxt_capability.h b/src/nxt_capability.h
index 60bbd5f8..1575d409 100644
--- a/src/nxt_capability.h
+++ b/src/nxt_capability.h
@@ -7,7 +7,8 @@
#define _NXT_CAPABILITY_INCLUDED_
typedef struct {
- uint8_t setid; /* 1 bit */
+ uint8_t setid; /* 1 bit */
+ uint8_t chroot; /* 1 bit */
} nxt_capabilities_t;
diff --git a/src/nxt_cert.c b/src/nxt_cert.c
index ee258646..9e825d80 100644
--- a/src/nxt_cert.c
+++ b/src/nxt_cert.c
@@ -797,12 +797,11 @@ nxt_cert_info_delete(nxt_str_t *name)
nxt_array_t *
-nxt_cert_store_load(nxt_task_t *task)
+nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mp)
{
DIR *dir;
size_t size, alloc;
u_char *buf, *p;
- nxt_mp_t *mp;
nxt_str_t name;
nxt_int_t ret;
nxt_file_t file;
@@ -818,14 +817,8 @@ nxt_cert_store_load(nxt_task_t *task)
return NULL;
}
- mp = nxt_mp_create(1024, 128, 256, 32);
- if (nxt_slow_path(mp == NULL)) {
- return NULL;
- }
-
certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t));
if (nxt_slow_path(certs == NULL)) {
- nxt_mp_destroy(mp);
return NULL;
}
@@ -933,7 +926,7 @@ nxt_cert_store_release(nxt_array_t *certs)
nxt_fd_close(items[i].fd);
}
- nxt_mp_destroy(certs->mem_pool);
+ nxt_array_destroy(certs);
}
diff --git a/src/nxt_cert.h b/src/nxt_cert.h
index 319d5d3c..dbaddcf9 100644
--- a/src/nxt_cert.h
+++ b/src/nxt_cert.h
@@ -19,7 +19,7 @@ nxt_conf_value_t *nxt_cert_info_get(nxt_str_t *name);
nxt_conf_value_t *nxt_cert_info_get_all(nxt_mp_t *mp);
nxt_int_t nxt_cert_info_delete(nxt_str_t *name);
-nxt_array_t *nxt_cert_store_load(nxt_task_t *task);
+nxt_array_t *nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mem_pool);
void nxt_cert_store_release(nxt_array_t *certs);
void nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
diff --git a/src/nxt_clone.h b/src/nxt_clone.h
index dcccf1db..e89fd82d 100644
--- a/src/nxt_clone.h
+++ b/src/nxt_clone.h
@@ -3,8 +3,8 @@
* Copyright (C) NGINX, Inc.
*/
-#ifndef _NXT_CLONE_INCLUDED_
-#define _NXT_CLONE_INCLUDED_
+#ifndef _NXT_CLONE_H_INCLUDED_
+#define _NXT_CLONE_H_INCLUDED_
#if (NXT_HAVE_CLONE_NEWUSER)
@@ -36,10 +36,14 @@ typedef struct {
pid_t nxt_clone(nxt_int_t flags);
+#define nxt_is_clone_flag_set(flags, test) \
+ ((flags & CLONE_##test) == CLONE_##test)
+
+
#if (NXT_HAVE_CLONE_NEWUSER)
-#define NXT_CLONE_USER(flags) \
- ((flags & CLONE_NEWUSER) == CLONE_NEWUSER)
+#define NXT_CLONE_MNT(flags) \
+ ((flags & CLONE_NEWNS) == CLONE_NEWNS)
NXT_EXPORT nxt_int_t nxt_clone_credential_map(nxt_task_t *task, pid_t pid,
nxt_credential_t *creds, nxt_clone_t *clone);
@@ -50,4 +54,5 @@ NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_gidmap(nxt_task_t *task,
#endif
-#endif /* _NXT_CLONE_INCLUDED_ */
+
+#endif /* _NXT_CLONE_H_INCLUDED_ */
diff --git a/src/nxt_conf.h b/src/nxt_conf.h
index 201a3a14..149af39a 100644
--- a/src/nxt_conf.h
+++ b/src/nxt_conf.h
@@ -118,7 +118,7 @@ NXT_EXPORT double nxt_conf_get_number(nxt_conf_value_t *value);
NXT_EXPORT uint8_t nxt_conf_get_boolean(nxt_conf_value_t *value);
// FIXME reimplement and reorder functions below
-nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value);
+NXT_EXPORT nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value);
nxt_conf_value_t *nxt_conf_create_object(nxt_mp_t *mp, nxt_uint_t count);
void nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name,
nxt_conf_value_t *value, uint32_t index);
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index bc03bdfb..c4f78608 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -23,13 +23,24 @@ typedef enum {
NXT_CONF_VLDT_OBJECT = 1 << NXT_CONF_OBJECT,
} nxt_conf_vldt_type_t;
+#define NXT_CONF_VLDT_ANY_TYPE (NXT_CONF_VLDT_NULL \
+ |NXT_CONF_VLDT_BOOLEAN \
+ |NXT_CONF_VLDT_NUMBER \
+ |NXT_CONF_VLDT_STRING \
+ |NXT_CONF_VLDT_ARRAY \
+ |NXT_CONF_VLDT_OBJECT)
+
+
+typedef nxt_int_t (*nxt_conf_vldt_handler_t)(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value,
+ void *data);
+
typedef struct {
- nxt_str_t name;
- nxt_conf_vldt_type_t type;
- nxt_int_t (*validator)(nxt_conf_validation_t *vldt,
- nxt_conf_value_t *value, void *data);
- void *data;
+ nxt_str_t name;
+ nxt_conf_vldt_type_t type;
+ nxt_conf_vldt_handler_t validator;
+ void *data;
} nxt_conf_vldt_object_t;
@@ -74,6 +85,16 @@ static nxt_int_t nxt_conf_vldt_routes_member(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_route(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set_member(
+ nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_match_encoded_patterns(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_encoded_pattern(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
@@ -106,6 +127,14 @@ static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_php(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_php_targets_exclusive(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt,
+ nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt,
@@ -324,12 +353,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
{ nxt_string("uri"),
NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
- &nxt_conf_vldt_match_patterns,
+ &nxt_conf_vldt_match_encoded_patterns,
NULL },
{ nxt_string("arguments"),
NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
- &nxt_conf_vldt_match_patterns_sets,
+ &nxt_conf_vldt_match_encoded_patterns_sets,
NULL },
{ nxt_string("headers"),
@@ -546,6 +575,24 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = {
#endif
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+ { nxt_string("rootfs"),
+ NXT_CONF_VLDT_STRING,
+ NULL,
+ NULL },
+
+#endif
+
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+
+ { nxt_string("new_privs"),
+ NXT_CONF_VLDT_BOOLEAN,
+ NULL,
+ NULL },
+
+#endif
+
NXT_CONF_VLDT_END
};
@@ -630,6 +677,24 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
};
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = {
+ { nxt_string("root"),
+ NXT_CONF_VLDT_STRING,
+ NULL,
+ NULL },
+
+ { nxt_string("script"),
+ NXT_CONF_VLDT_STRING,
+ NULL,
+ NULL },
+
+ { nxt_string("index"),
+ NXT_CONF_VLDT_STRING,
+ NULL,
+ NULL }
+};
+
+
static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = {
{ nxt_string("file"),
NXT_CONF_VLDT_STRING,
@@ -650,7 +715,17 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = {
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = {
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = {
+ { nxt_string("options"),
+ NXT_CONF_VLDT_OBJECT,
+ &nxt_conf_vldt_object,
+ (void *) &nxt_conf_vldt_php_options_members },
+
+ NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
+};
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = {
{ nxt_string("root"),
NXT_CONF_VLDT_STRING,
NULL,
@@ -666,12 +741,32 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = {
NULL,
NULL },
- { nxt_string("options"),
+ NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members)
+};
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = {
+ { nxt_string("root"),
+ NXT_CONF_VLDT_ANY_TYPE,
+ &nxt_conf_vldt_php_targets_exclusive,
+ (void *) "root" },
+
+ { nxt_string("script"),
+ NXT_CONF_VLDT_ANY_TYPE,
+ &nxt_conf_vldt_php_targets_exclusive,
+ (void *) "script" },
+
+ { nxt_string("index"),
+ NXT_CONF_VLDT_ANY_TYPE,
+ &nxt_conf_vldt_php_targets_exclusive,
+ (void *) "index" },
+
+ { nxt_string("targets"),
NXT_CONF_VLDT_OBJECT,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_php_options_members },
+ &nxt_conf_vldt_php_targets,
+ NULL },
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
+ NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members)
};
@@ -755,7 +850,7 @@ nxt_conf_validate(nxt_conf_validation_t *vldt)
}
-#define NXT_CONF_VLDT_ANY_TYPE \
+#define NXT_CONF_VLDT_ANY_TYPE_STR \
"either a null, a boolean, an integer, " \
"a number, a string, an array, or an object"
@@ -768,7 +863,7 @@ nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_str_t expected;
nxt_bool_t serial;
nxt_uint_t value_type, n, t;
- u_char buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE)];
+ u_char buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE_STR)];
static nxt_str_t type_name[] = {
nxt_string("a null"),
@@ -1032,79 +1127,94 @@ static nxt_int_t
nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
- u_char *p;
- nxt_str_t pass, first, second;
+ nxt_str_t pass;
+ nxt_int_t ret;
+ nxt_str_t segments[3];
- nxt_conf_get_string(value, &pass);
+ static nxt_str_t targets_str = nxt_string("targets");
- p = nxt_memchr(pass.start, '/', pass.length);
+ nxt_conf_get_string(value, &pass);
- if (p != NULL) {
- first.length = p - pass.start;
- first.start = pass.start;
+ ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 3);
- if (pass.length - first.length == 1) {
- goto error;
+ if (ret != NXT_OK) {
+ if (ret == NXT_DECLINED) {
+ return nxt_conf_vldt_error(vldt, "Request \"pass\" value \"%V\" "
+ "is invalid.", &pass);
}
- second.length = pass.length - first.length - 1;
- second.start = p + 1;
-
- } else {
- first = pass;
- second.length = 0;
+ return NXT_ERROR;
}
- if (nxt_str_eq(&first, "applications", 12)) {
+ if (nxt_str_eq(&segments[0], "applications", 12)) {
- if (second.length == 0) {
+ if (segments[1].length == 0) {
goto error;
}
- value = nxt_conf_get_object_member(vldt->conf, &first, NULL);
+ value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL);
- if (nxt_slow_path(value == NULL)) {
+ if (value == NULL) {
goto error;
}
- value = nxt_conf_get_object_member(value, &second, NULL);
+ value = nxt_conf_get_object_member(value, &segments[1], NULL);
- if (nxt_slow_path(value == NULL)) {
+ if (value == NULL) {
goto error;
}
+ if (segments[2].length > 0) {
+ value = nxt_conf_get_object_member(value, &targets_str, NULL);
+
+ if (value == NULL) {
+ goto error;
+ }
+
+ value = nxt_conf_get_object_member(value, &segments[2], NULL);
+
+ if (value == NULL) {
+ goto error;
+ }
+ }
+
return NXT_OK;
}
- if (nxt_str_eq(&first, "upstreams", 9)) {
+ if (nxt_str_eq(&segments[0], "upstreams", 9)) {
- if (second.length == 0) {
+ if (segments[1].length == 0 || segments[2].length != 0) {
goto error;
}
- value = nxt_conf_get_object_member(vldt->conf, &first, NULL);
+ value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL);
- if (nxt_slow_path(value == NULL)) {
+ if (value == NULL) {
goto error;
}
- value = nxt_conf_get_object_member(value, &second, NULL);
+ value = nxt_conf_get_object_member(value, &segments[1], NULL);
- if (nxt_slow_path(value == NULL)) {
+ if (value == NULL) {
goto error;
}
return NXT_OK;
}
- if (nxt_str_eq(&first, "routes", 6)) {
- value = nxt_conf_get_object_member(vldt->conf, &first, NULL);
+ if (nxt_str_eq(&segments[0], "routes", 6)) {
+
+ if (segments[2].length != 0) {
+ goto error;
+ }
+
+ value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL);
- if (nxt_slow_path(value == NULL)) {
+ if (value == NULL) {
goto error;
}
- if (second.length == 0) {
+ if (segments[1].length == 0) {
if (nxt_conf_type(value) != NXT_CONF_ARRAY) {
goto error;
}
@@ -1116,9 +1226,9 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
goto error;
}
- value = nxt_conf_get_object_member(value, &second, NULL);
+ value = nxt_conf_get_object_member(value, &segments[1], NULL);
- if (nxt_slow_path(value == NULL)) {
+ if (value == NULL) {
goto error;
}
@@ -1297,6 +1407,109 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
}
+static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data)
+{
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_match_encoded_patterns_set);
+ }
+
+ /* NXT_CONF_STRING */
+
+ return nxt_conf_vldt_match_encoded_patterns_set(vldt, value);
+}
+
+
+static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
+{
+ if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
+ "\"arguments\" must be an object.");
+ }
+
+ return nxt_conf_vldt_object_iterator(vldt, value,
+ &nxt_conf_vldt_match_encoded_patterns_set_member);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_encoded_patterns_set_member(nxt_conf_validation_t *vldt,
+ nxt_str_t *name, nxt_conf_value_t *value)
+{
+ u_char *p, *end;
+
+ if (nxt_slow_path(name->length == 0)) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must "
+ "not contain empty member names.");
+ }
+
+ p = nxt_mp_nget(vldt->pool, name->length);
+ if (nxt_slow_path(p == NULL)) {
+ return NXT_ERROR;
+ }
+
+ end = nxt_decode_uri(p, name->start, name->length);
+ if (nxt_slow_path(end == NULL)) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
+ "\"arguments\" is encoded but is invalid.");
+ }
+
+ return nxt_conf_vldt_match_encoded_patterns(vldt, value, NULL);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_encoded_patterns(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_match_encoded_pattern);
+ }
+
+ /* NXT_CONF_STRING */
+
+ return nxt_conf_vldt_match_encoded_pattern(vldt, value);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_encoded_pattern(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
+ u_char *p, *end;
+ nxt_int_t ret;
+ nxt_str_t pattern;
+
+ if (nxt_conf_type(value) != NXT_CONF_STRING) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"uri\" "
+ "must be a string.");
+ }
+
+ ret = nxt_conf_vldt_match_pattern(vldt, value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ nxt_conf_get_string(value, &pattern);
+
+ p = nxt_mp_nget(vldt->pool, pattern.length);
+ if (nxt_slow_path(p == NULL)) {
+ return NXT_ERROR;
+ }
+
+ end = nxt_decode_uri(p, pattern.start, pattern.length);
+ if (nxt_slow_path(end == NULL)) {
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"uri\" "
+ "is encoded but is invalid.");
+ }
+
+ return NXT_OK;
+}
+
+
static nxt_int_t
nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
@@ -1488,13 +1701,17 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
static nxt_str_t type_str = nxt_string("type");
- static void *members[] = {
- nxt_conf_vldt_external_members,
- nxt_conf_vldt_python_members,
- nxt_conf_vldt_php_members,
- nxt_conf_vldt_perl_members,
- nxt_conf_vldt_ruby_members,
- nxt_conf_vldt_java_members,
+ static struct {
+ nxt_conf_vldt_handler_t validator;
+ nxt_conf_vldt_object_t *members;
+
+ } types[] = {
+ { nxt_conf_vldt_object, nxt_conf_vldt_external_members },
+ { nxt_conf_vldt_object, nxt_conf_vldt_python_members },
+ { nxt_conf_vldt_php, NULL },
+ { nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
+ { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },
+ { nxt_conf_vldt_object, nxt_conf_vldt_java_members },
};
ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
@@ -1528,7 +1745,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
&type);
}
- return nxt_conf_vldt_object(vldt, value, members[lang->type]);
+ return types[lang->type].validator(vldt, value, types[lang->type].members);
}
@@ -1940,6 +2157,70 @@ nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
static nxt_int_t
+nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ nxt_conf_value_t *targets;
+
+ static nxt_str_t targets_str = nxt_string("targets");
+
+ targets = nxt_conf_get_object_member(value, &targets_str, NULL);
+
+ if (targets != NULL) {
+ return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_php_members);
+ }
+
+ return nxt_conf_vldt_object(vldt, value,
+ nxt_conf_vldt_php_notargets_members);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_php_targets_exclusive(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive "
+ "with the \"targets\" object.", data);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ nxt_uint_t n;
+
+ n = nxt_conf_object_members_count(value);
+
+ if (n > 254) {
+ return nxt_conf_vldt_error(vldt, "The \"targets\" object must not "
+ "contain more than 254 members.");
+ }
+
+ return nxt_conf_vldt_object_iterator(vldt, value,
+ &nxt_conf_vldt_php_target);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, nxt_str_t *name,
+ nxt_conf_value_t *value)
+{
+ if (name->length == 0) {
+ return nxt_conf_vldt_error(vldt,
+ "The PHP target name must not be empty.");
+ }
+
+ if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
+ return nxt_conf_vldt_error(vldt, "The \"%V\" PHP target must be "
+ "an object.", name);
+ }
+
+ return nxt_conf_vldt_object(vldt, value, &nxt_conf_vldt_php_target_members);
+}
+
+
+static nxt_int_t
nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_conf_value_t *value)
{
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index f9b2cf26..a61c127d 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -39,14 +39,21 @@ typedef struct {
} nxt_controller_response_t;
+static nxt_int_t nxt_controller_prefork(nxt_task_t *task,
+ nxt_process_t *process, nxt_mp_t *mp);
+static nxt_int_t nxt_controller_start(nxt_task_t *task,
+ nxt_process_data_t *data);
static void nxt_controller_process_new_port_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
static void nxt_controller_send_current_conf(nxt_task_t *task);
static void nxt_controller_router_ready_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
+static void nxt_controller_remove_pid_handler(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg);
static nxt_int_t nxt_controller_conf_default(void);
static void nxt_controller_conf_init_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
+static void nxt_controller_flush_requests(nxt_task_t *task);
static nxt_int_t nxt_controller_conf_send(nxt_task_t *task,
nxt_conf_value_t *conf, nxt_port_rpc_handler_t handler, void *data);
@@ -75,12 +82,15 @@ static void nxt_controller_process_request(nxt_task_t *task,
nxt_controller_request_t *req);
static void nxt_controller_process_config(nxt_task_t *task,
nxt_controller_request_t *req, nxt_str_t *path);
+static nxt_bool_t nxt_controller_check_postpone_request(nxt_task_t *task);
#if (NXT_TLS)
static void nxt_controller_process_cert(nxt_task_t *task,
nxt_controller_request_t *req, nxt_str_t *path);
static void nxt_controller_process_cert_save(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name);
+static void nxt_controller_cert_cleanup(nxt_task_t *task, void *obj,
+ void *data);
#endif
static void nxt_controller_conf_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
@@ -103,6 +113,7 @@ static nxt_uint_t nxt_controller_listening;
static nxt_uint_t nxt_controller_router_ready;
static nxt_controller_conf_t nxt_controller_conf;
static nxt_queue_t nxt_controller_waiting_requests;
+static nxt_bool_t nxt_controller_waiting_init_conf;
static const nxt_event_conn_state_t nxt_controller_conn_read_state;
@@ -111,21 +122,117 @@ static const nxt_event_conn_state_t nxt_controller_conn_write_state;
static const nxt_event_conn_state_t nxt_controller_conn_close_state;
-nxt_port_handlers_t nxt_controller_process_port_handlers = {
- .quit = nxt_worker_process_quit_handler,
+static const nxt_port_handlers_t nxt_controller_process_port_handlers = {
+ .quit = nxt_signal_quit_handler,
.new_port = nxt_controller_process_new_port_handler,
.change_file = nxt_port_change_log_file_handler,
.mmap = nxt_port_mmap_handler,
.process_ready = nxt_controller_router_ready_handler,
.data = nxt_port_data_handler,
- .remove_pid = nxt_port_remove_pid_handler,
+ .remove_pid = nxt_controller_remove_pid_handler,
.rpc_ready = nxt_port_rpc_handler,
.rpc_error = nxt_port_rpc_handler,
};
-nxt_int_t
-nxt_controller_start(nxt_task_t *task, void *data)
+const nxt_process_init_t nxt_controller_process = {
+ .name = "controller",
+ .type = NXT_PROCESS_CONTROLLER,
+ .prefork = nxt_controller_prefork,
+ .restart = 1,
+ .setup = nxt_process_core_setup,
+ .start = nxt_controller_start,
+ .port_handlers = &nxt_controller_process_port_handlers,
+ .signals = nxt_process_signals,
+};
+
+
+static nxt_int_t
+nxt_controller_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp)
+{
+ ssize_t n;
+ nxt_int_t ret;
+ nxt_str_t *conf;
+ nxt_file_t file;
+ nxt_runtime_t *rt;
+ nxt_file_info_t fi;
+ nxt_controller_init_t ctrl_init;
+
+ nxt_log(task, NXT_LOG_INFO, "controller started");
+
+ rt = task->thread->runtime;
+
+ nxt_memzero(&ctrl_init, sizeof(nxt_controller_init_t));
+
+ conf = &ctrl_init.conf;
+
+ nxt_memzero(&file, sizeof(nxt_file_t));
+
+ file.name = (nxt_file_name_t *) rt->conf;
+
+ ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
+
+ if (ret == NXT_OK) {
+ ret = nxt_file_info(&file, &fi);
+
+ if (nxt_fast_path(ret == NXT_OK && nxt_is_file(&fi))) {
+ conf->length = nxt_file_size(&fi);
+ conf->start = nxt_mp_alloc(mp, conf->length);
+ if (nxt_slow_path(conf->start == NULL)) {
+ nxt_file_close(task, &file);
+ return NXT_ERROR;
+ }
+
+ n = nxt_file_read(&file, conf->start, conf->length, 0);
+
+ if (nxt_slow_path(n != (ssize_t) conf->length)) {
+ conf->start = NULL;
+ conf->length = 0;
+
+ nxt_alert(task, "failed to restore previous configuration: "
+ "cannot read the file");
+ }
+ }
+
+ nxt_file_close(task, &file);
+ }
+
+#if (NXT_TLS)
+ ctrl_init.certs = nxt_cert_store_load(task, mp);
+
+ nxt_mp_cleanup(mp, nxt_controller_cert_cleanup, task, ctrl_init.certs, rt);
+#endif
+
+ process->data.controller = ctrl_init;
+
+ return NXT_OK;
+}
+
+
+#if (NXT_TLS)
+
+static void
+nxt_controller_cert_cleanup(nxt_task_t *task, void *obj, void *data)
+{
+ pid_t main_pid;
+ nxt_array_t *certs;
+ nxt_runtime_t *rt;
+
+ certs = obj;
+ rt = data;
+
+ main_pid = rt->port_by_type[NXT_PROCESS_MAIN]->pid;
+
+ if (nxt_pid == main_pid && certs != NULL) {
+ nxt_cert_store_release(certs);
+ }
+}
+
+#endif
+
+
+static nxt_int_t
+nxt_controller_start(nxt_task_t *task, nxt_process_data_t *data)
{
nxt_mp_t *mp;
nxt_int_t ret;
@@ -144,15 +251,13 @@ nxt_controller_start(nxt_task_t *task, void *data)
nxt_queue_init(&nxt_controller_waiting_requests);
- init = data;
+ init = &data->controller;
#if (NXT_TLS)
-
if (init->certs != NULL) {
nxt_cert_info_init(task, init->certs);
nxt_cert_store_release(init->certs);
}
-
#endif
json = &init->conf;
@@ -167,8 +272,6 @@ nxt_controller_start(nxt_task_t *task, void *data)
}
conf = nxt_conf_json_parse_str(mp, json);
- nxt_free(json->start);
-
if (nxt_slow_path(conf == NULL)) {
nxt_alert(task, "failed to restore previous configuration: "
"file is corrupted or not enough memory");
@@ -245,6 +348,8 @@ nxt_controller_send_current_conf(nxt_task_t *task)
nxt_controller_conf_init_handler, NULL);
if (nxt_fast_path(rc == NXT_OK)) {
+ nxt_controller_waiting_init_conf = 1;
+
return;
}
@@ -266,6 +371,8 @@ nxt_controller_send_current_conf(nxt_task_t *task)
}
nxt_controller_listening = 1;
+
+ nxt_controller_flush_requests(task);
}
@@ -288,6 +395,28 @@ nxt_controller_router_ready_handler(nxt_task_t *task,
}
+static void
+nxt_controller_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ nxt_pid_t pid;
+ nxt_process_t *process;
+ nxt_runtime_t *rt;
+
+ rt = task->thread->runtime;
+
+ nxt_assert(nxt_buf_used_size(msg->buf) == sizeof(pid));
+
+ nxt_memcpy(&pid, msg->buf->mem.pos, sizeof(pid));
+
+ process = nxt_runtime_process_find(rt, pid);
+ if (process != NULL && nxt_process_type(process) == NXT_PROCESS_ROUTER) {
+ nxt_controller_router_ready = 0;
+ }
+
+ nxt_port_remove_pid_handler(task, msg);
+}
+
+
static nxt_int_t
nxt_controller_conf_default(void)
{
@@ -322,6 +451,8 @@ nxt_controller_conf_init_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
{
nxt_runtime_t *rt;
+ nxt_controller_waiting_init_conf = 0;
+
if (msg->port_msg.type != NXT_PORT_MSG_RPC_READY) {
nxt_alert(task, "failed to apply previous configuration");
@@ -343,6 +474,25 @@ nxt_controller_conf_init_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_controller_listening = 1;
}
+
+ nxt_controller_flush_requests(task);
+}
+
+
+static void
+nxt_controller_flush_requests(nxt_task_t *task)
+{
+ nxt_queue_t queue;
+ nxt_controller_request_t *req;
+
+ nxt_queue_init(&queue);
+ nxt_queue_add(&queue, &nxt_controller_waiting_requests);
+
+ nxt_queue_init(&nxt_controller_waiting_requests);
+
+ nxt_queue_each(req, &queue, nxt_controller_request_t, link) {
+ nxt_controller_process_request(task, req);
+ } nxt_queue_loop;
}
@@ -361,9 +511,8 @@ nxt_controller_conf_send(nxt_task_t *task, nxt_conf_value_t *conf,
router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
- if (nxt_slow_path(router_port == NULL || !nxt_controller_router_ready)) {
- return NXT_DECLINED;
- }
+ nxt_assert(router_port != NULL);
+ nxt_assert(nxt_controller_router_ready);
controller_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
@@ -961,7 +1110,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
if (post || nxt_str_eq(&req->parser.method, "PUT", 3)) {
- if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)) {
+ if (nxt_controller_check_postpone_request(task)) {
nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link);
return;
}
@@ -1058,10 +1207,6 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
if (nxt_slow_path(rc != NXT_OK)) {
nxt_mp_destroy(mp);
- if (rc == NXT_DECLINED) {
- goto no_router;
- }
-
/* rc == NXT_ERROR */
goto alloc_fail;
}
@@ -1076,7 +1221,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
if (nxt_str_eq(&req->parser.method, "DELETE", 6)) {
- if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)) {
+ if (nxt_controller_check_postpone_request(task)) {
nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link);
return;
}
@@ -1143,10 +1288,6 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
if (nxt_slow_path(rc != NXT_OK)) {
nxt_mp_destroy(mp);
- if (rc == NXT_DECLINED) {
- goto no_router;
- }
-
/* rc == NXT_ERROR */
goto alloc_fail;
}
@@ -1193,16 +1334,27 @@ alloc_fail:
resp.offset = -1;
nxt_controller_response(task, req, &resp);
- return;
+}
-no_router:
- resp.status = 500;
- resp.title = (u_char *) "Router process isn't available.";
- resp.offset = -1;
+static nxt_bool_t
+nxt_controller_check_postpone_request(nxt_task_t *task)
+{
+ nxt_port_t *router_port;
+ nxt_runtime_t *rt;
- nxt_controller_response(task, req, &resp);
- return;
+ if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)
+ || nxt_controller_waiting_init_conf
+ || !nxt_controller_router_ready)
+ {
+ return 1;
+ }
+
+ rt = task->thread->runtime;
+
+ router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
+
+ return (router_port == NULL);
}
@@ -1469,7 +1621,6 @@ static void
nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
- nxt_queue_t queue;
nxt_controller_request_t *req;
nxt_controller_response_t resp;
@@ -1502,14 +1653,7 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_controller_response(task, req, &resp);
- nxt_queue_init(&queue);
- nxt_queue_add(&queue, &nxt_controller_waiting_requests);
-
- nxt_queue_init(&nxt_controller_waiting_requests);
-
- nxt_queue_each(req, &queue, nxt_controller_request_t, link) {
- nxt_controller_process_request(task, req);
- } nxt_queue_loop;
+ nxt_controller_flush_requests(task);
}
diff --git a/src/nxt_external.c b/src/nxt_external.c
index e498a938..6370a9c4 100644
--- a/src/nxt_external.c
+++ b/src/nxt_external.c
@@ -9,8 +9,7 @@
#include <nxt_unit.h>
-static nxt_int_t nxt_external_init(nxt_task_t *task,
- nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_external_start(nxt_task_t *task, nxt_process_data_t *data);
nxt_app_module_t nxt_external_module = {
@@ -19,7 +18,9 @@ nxt_app_module_t nxt_external_module = {
nxt_string("external"),
"*",
NULL,
- nxt_external_init,
+ 0,
+ NULL,
+ nxt_external_start,
};
@@ -58,7 +59,7 @@ nxt_external_fd_no_cloexec(nxt_task_t *task, nxt_socket_t fd)
static nxt_int_t
-nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_external_start(nxt_task_t *task, nxt_process_data_t *data)
{
char **argv;
u_char buf[256];
@@ -71,9 +72,11 @@ nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_port_t *my_port, *main_port;
nxt_runtime_t *rt;
nxt_conf_value_t *value;
+ nxt_common_app_conf_t *conf;
nxt_external_app_conf_t *c;
rt = task->thread->runtime;
+ conf = data->app;
main_port = rt->port_by_type[NXT_PROCESS_MAIN];
my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
@@ -99,7 +102,7 @@ nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
"%PI,%ud,%d;"
"%PI,%ud,%d;"
"%d,%z,%Z",
- NXT_VERSION, my_port->process->init->stream,
+ NXT_VERSION, my_port->process->stream,
main_port->pid, main_port->id, main_port->pair[1],
my_port->pid, my_port->id, my_port->pair[0],
2, conf->shm_limit);
diff --git a/src/nxt_fs.c b/src/nxt_fs.c
new file mode 100644
index 00000000..fe271802
--- /dev/null
+++ b/src/nxt_fs.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+#if (NXT_HAVE_FREEBSD_NMOUNT)
+#include <sys/param.h>
+#include <sys/uio.h>
+#endif
+
+
+static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode);
+
+
+#if (NXT_HAVE_LINUX_MOUNT)
+
+nxt_int_t
+nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
+{
+ int rc;
+
+ rc = mount((const char *) mnt->src, (const char *) mnt->dst,
+ (const char *) mnt->fstype, mnt->flags, mnt->data);
+
+ if (nxt_slow_path(rc < 0)) {
+ nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %d, \"%s\") %E",
+ mnt->src, mnt->dst, mnt->fstype, mnt->flags, mnt->data,
+ nxt_errno);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+#elif (NXT_HAVE_FREEBSD_NMOUNT)
+
+nxt_int_t
+nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
+{
+ const char *fstype;
+ uint8_t is_bind, is_proc;
+ struct iovec iov[8];
+ char errmsg[256];
+
+ is_bind = nxt_strncmp(mnt->fstype, "bind", 4) == 0;
+ is_proc = nxt_strncmp(mnt->fstype, "proc", 4) == 0;
+
+ if (nxt_slow_path(!is_bind && !is_proc)) {
+ nxt_alert(task, "mount type \"%s\" not implemented.", mnt->fstype);
+ return NXT_ERROR;
+ }
+
+ if (is_bind) {
+ fstype = "nullfs";
+
+ } else {
+ fstype = "procfs";
+ }
+
+ iov[0].iov_base = (void *) "fstype";
+ iov[0].iov_len = 7;
+ iov[1].iov_base = (void *) fstype;
+ iov[1].iov_len = strlen(fstype) + 1;
+ iov[2].iov_base = (void *) "fspath";
+ iov[2].iov_len = 7;
+ iov[3].iov_base = (void *) mnt->dst;
+ iov[3].iov_len = nxt_strlen(mnt->dst) + 1;
+ iov[4].iov_base = (void *) "target";
+ iov[4].iov_len = 7;
+ iov[5].iov_base = (void *) mnt->src;
+ iov[5].iov_len = nxt_strlen(mnt->src) + 1;
+ iov[6].iov_base = (void *) "errmsg";
+ iov[6].iov_len = 7;
+ iov[7].iov_base = (void *) errmsg;
+ iov[7].iov_len = sizeof(errmsg);
+
+ if (nxt_slow_path(nmount(iov, 8, 0) < 0)) {
+ nxt_alert(task, "nmount(%p, 8, 0) %s", errmsg);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
+#if (NXT_HAVE_LINUX_UMOUNT2)
+
+void
+nxt_fs_unmount(const u_char *path)
+{
+ if (nxt_slow_path(umount2((const char *) path, MNT_DETACH) < 0)) {
+ nxt_thread_log_error(NXT_LOG_WARN, "umount2(%s, MNT_DETACH) %E",
+ path, nxt_errno);
+ }
+}
+
+#elif (NXT_HAVE_UNMOUNT)
+
+void
+nxt_fs_unmount(const u_char *path)
+{
+ if (nxt_slow_path(unmount((const char *) path, MNT_FORCE) < 0)) {
+ nxt_thread_log_error(NXT_LOG_WARN, "unmount(%s) %E", path, nxt_errno);
+ }
+}
+
+#endif
+
+
+nxt_int_t
+nxt_fs_mkdir_all(const u_char *dir, mode_t mode)
+{
+ char *start, *end, *dst;
+ size_t dirlen;
+ char path[PATH_MAX];
+
+ dirlen = nxt_strlen(dir);
+
+ nxt_assert(dirlen < PATH_MAX && dirlen > 1 && dir[0] == '/');
+
+ dst = path;
+ start = end = (char *) dir;
+
+ while (*start != '\0') {
+ if (*start == '/') {
+ *dst++ = *start++;
+ }
+
+ end = strchr(start, '/');
+ if (end == NULL) {
+ end = ((char *)dir + dirlen);
+ }
+
+ dst = nxt_cpymem(dst, start, end - start);
+ *dst = '\0';
+
+ if (nxt_slow_path(nxt_fs_mkdir((u_char *) path, mode) != NXT_OK
+ && nxt_errno != EEXIST))
+ {
+ return NXT_ERROR;
+ }
+
+ start = end;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_fs_mkdir(const u_char *dir, mode_t mode)
+{
+ if (nxt_fast_path(mkdir((const char *) dir, mode) == 0)) {
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
diff --git a/src/nxt_fs.h b/src/nxt_fs.h
new file mode 100644
index 00000000..85c78b27
--- /dev/null
+++ b/src/nxt_fs.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_FS_H_INCLUDED_
+#define _NXT_FS_H_INCLUDED_
+
+
+#ifdef MS_BIND
+#define NXT_MS_BIND MS_BIND
+#else
+#define NXT_MS_BIND 0
+#endif
+
+#ifdef MS_REC
+#define NXT_MS_REC MS_BIND
+#else
+#define NXT_MS_REC 0
+#endif
+
+
+typedef struct {
+ u_char *src;
+ u_char *dst;
+ u_char *fstype;
+ nxt_int_t flags;
+ u_char *data;
+} nxt_fs_mount_t;
+
+
+nxt_int_t nxt_fs_mkdir_all(const u_char *dir, mode_t mode);
+nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt);
+void nxt_fs_unmount(const u_char *path);
+
+
+#endif /* _NXT_FS_H_INCLUDED_ */
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 841f5b40..68051e69 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -175,6 +175,7 @@ struct nxt_http_request_s {
nxt_http_status_t status:16;
uint8_t pass_count; /* 8 bits */
+ uint8_t app_target;
nxt_http_protocol_t protocol:8; /* 2 bits */
uint8_t logged; /* 1 bit */
uint8_t header_sent; /* 1 bit */
@@ -201,6 +202,7 @@ struct nxt_http_action_s {
} u;
nxt_str_t name;
+ nxt_int_t target;
};
@@ -277,7 +279,10 @@ nxt_http_routes_t *nxt_http_routes_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *routes_conf);
nxt_http_action_t *nxt_http_action_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_str_t *name);
-void nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf);
+nxt_int_t nxt_http_routes_resolve(nxt_task_t *task,
+ nxt_router_temp_conf_t *tmcf);
+nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass,
+ nxt_str_t *segments, nxt_uint_t n);
nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_str_t *name);
void nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes);
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 050587f7..cc1ae17d 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -341,6 +341,8 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_set(&r->server_name, "localhost");
}
+ r->app_target = action->target;
+
nxt_router_process_http_request(task, r, action->u.application);
return NULL;
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index ca43c060..a8a6b181 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -39,6 +39,13 @@ typedef enum {
} nxt_http_route_pattern_case_t;
+typedef enum {
+ NXT_HTTP_ROUTE_ENCODING_NONE = 0,
+ NXT_HTTP_ROUTE_ENCODING_URI,
+ NXT_HTTP_ROUTE_ENCODING_URI_PLUS
+} nxt_http_route_encoding_t;
+
+
typedef struct {
nxt_conf_value_t *pass;
nxt_conf_value_t *ret;
@@ -181,29 +188,33 @@ static nxt_int_t nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *cv, nxt_http_action_t *action);
static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
- nxt_bool_t case_sensitive);
+ nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
- nxt_bool_t case_sensitive);
+ nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name,
- nxt_bool_t case_sensitive);
+ nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
static nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create(
nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv);
static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
- nxt_http_route_pattern_case_t pattern_case);
+ nxt_http_route_pattern_case_t pattern_case,
+ nxt_http_route_encoding_t encoding);
static int nxt_http_pattern_compare(const void *one, const void *two);
static int nxt_http_addr_pattern_compare(const void *one, const void *two);
static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
- nxt_http_route_pattern_case_t pattern_case);
+ nxt_http_route_pattern_case_t pattern_case,
+ nxt_http_route_encoding_t encoding);
+static nxt_int_t nxt_http_route_decode_str(nxt_str_t *str,
+ nxt_http_route_encoding_t encoding);
static u_char *nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test,
nxt_http_route_pattern_case_t pattern_case);
-static void nxt_http_route_resolve(nxt_task_t *task,
+static nxt_int_t nxt_http_route_resolve(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route);
-static void nxt_http_action_resolve(nxt_task_t *task,
+static nxt_int_t nxt_http_action_resolve(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action);
static void nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
nxt_http_action_t *action);
@@ -463,7 +474,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.scheme != NULL) {
rule = nxt_http_route_rule_create(task, mp, mtcf.scheme, 1,
- NXT_HTTP_ROUTE_PATTERN_NOCASE);
+ NXT_HTTP_ROUTE_PATTERN_NOCASE,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
if (rule == NULL) {
return NULL;
}
@@ -475,7 +487,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.host != NULL) {
rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1,
- NXT_HTTP_ROUTE_PATTERN_LOWCASE);
+ NXT_HTTP_ROUTE_PATTERN_LOWCASE,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
if (rule == NULL) {
return NULL;
}
@@ -488,7 +501,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.uri != NULL) {
rule = nxt_http_route_rule_create(task, mp, mtcf.uri, 1,
- NXT_HTTP_ROUTE_PATTERN_NOCASE);
+ NXT_HTTP_ROUTE_PATTERN_NOCASE,
+ NXT_HTTP_ROUTE_ENCODING_URI);
if (rule == NULL) {
return NULL;
}
@@ -501,7 +515,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.method != NULL) {
rule = nxt_http_route_rule_create(task, mp, mtcf.method, 1,
- NXT_HTTP_ROUTE_PATTERN_UPCASE);
+ NXT_HTTP_ROUTE_PATTERN_UPCASE,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
if (rule == NULL) {
return NULL;
}
@@ -514,7 +529,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.headers != NULL) {
table = nxt_http_route_table_create(task, mp, mtcf.headers,
- NXT_HTTP_ROUTE_HEADER, 0);
+ NXT_HTTP_ROUTE_HEADER, 0,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
if (table == NULL) {
return NULL;
}
@@ -525,7 +541,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.arguments != NULL) {
table = nxt_http_route_table_create(task, mp, mtcf.arguments,
- NXT_HTTP_ROUTE_ARGUMENT, 1);
+ NXT_HTTP_ROUTE_ARGUMENT, 1,
+ NXT_HTTP_ROUTE_ENCODING_URI_PLUS);
if (table == NULL) {
return NULL;
}
@@ -536,7 +553,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (mtcf.cookies != NULL) {
table = nxt_http_route_table_create(task, mp, mtcf.cookies,
- NXT_HTTP_ROUTE_COOKIE, 1);
+ NXT_HTTP_ROUTE_COOKIE, 1,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
if (table == NULL) {
return NULL;
}
@@ -699,7 +717,7 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
static nxt_http_route_table_t *
nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
- nxt_bool_t case_sensitive)
+ nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding)
{
size_t size;
uint32_t i, n;
@@ -722,8 +740,8 @@ nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
table->object = NXT_HTTP_ROUTE_TABLE;
if (!array) {
- ruleset = nxt_http_route_ruleset_create(task, mp, table_cv,
- object, case_sensitive);
+ ruleset = nxt_http_route_ruleset_create(task, mp, table_cv, object,
+ case_sensitive, encoding);
if (nxt_slow_path(ruleset == NULL)) {
return NULL;
}
@@ -736,8 +754,8 @@ nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
for (i = 0; i < n; i++) {
ruleset_cv = nxt_conf_get_array_element(table_cv, i);
- ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv,
- object, case_sensitive);
+ ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, object,
+ case_sensitive, encoding);
if (nxt_slow_path(ruleset == NULL)) {
return NULL;
}
@@ -752,7 +770,7 @@ nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
static nxt_http_route_ruleset_t *
nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
- nxt_bool_t case_sensitive)
+ nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding)
{
size_t size;
uint32_t i, n, next;
@@ -778,7 +796,7 @@ nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name,
- case_sensitive);
+ case_sensitive, encoding);
if (nxt_slow_path(rule == NULL)) {
return NULL;
}
@@ -793,15 +811,18 @@ nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
static nxt_http_route_rule_t *
nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
- nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive)
+ nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive,
+ nxt_http_route_encoding_t encoding)
{
- u_char c, *p;
+ u_char c, *p, *src, *start, *end, plus;
+ uint8_t d0, d1;
uint32_t hash;
nxt_uint_t i;
nxt_http_route_rule_t *rule;
rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive,
- NXT_HTTP_ROUTE_PATTERN_NOCASE);
+ NXT_HTTP_ROUTE_PATTERN_NOCASE,
+ encoding);
if (nxt_slow_path(rule == NULL)) {
return NULL;
}
@@ -813,18 +834,65 @@ nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
return NULL;
}
+ hash = NXT_HTTP_FIELD_HASH_INIT;
rule->u.name.start = p;
- hash = NXT_HTTP_FIELD_HASH_INIT;
+ if (encoding == NXT_HTTP_ROUTE_ENCODING_NONE) {
+ for (i = 0; i < name->length; i++) {
+ c = name->start[i];
+ *p++ = c;
- for (i = 0; i < name->length; i++) {
- c = name->start[i];
- *p++ = c;
+ c = case_sensitive ? c : nxt_lowcase(c);
+ hash = nxt_http_field_hash_char(hash, c);
+ }
+
+ goto end;
+ }
+
+ plus = (encoding == NXT_HTTP_ROUTE_ENCODING_URI_PLUS) ? ' ' : '+';
+
+ start = name->start;
+ end = start + name->length;
+
+ for (src = start; src < end; src++) {
+ c = *src;
+
+ switch (c) {
+ case '%':
+ if (nxt_slow_path(end - src <= 2)) {
+ return NULL;
+ }
+
+ d0 = nxt_hex2int[src[1]];
+ d1 = nxt_hex2int[src[2]];
+ src += 2;
+
+ if (nxt_slow_path((d0 | d1) >= 16)) {
+ return NULL;
+ }
+
+ c = (d0 << 4) + d1;
+ *p++ = c;
+ break;
+
+ case '+':
+ c = plus;
+ *p++ = c;
+ break;
+
+ default:
+ *p++ = c;
+ break;
+ }
c = case_sensitive ? c : nxt_lowcase(c);
hash = nxt_http_field_hash_char(hash, c);
}
+ rule->u.name.length = p - rule->u.name.start;
+
+end:
+
rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF;
return rule;
@@ -834,7 +902,8 @@ nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
static nxt_http_route_rule_t *
nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
- nxt_http_route_pattern_case_t pattern_case)
+ nxt_http_route_pattern_case_t pattern_case,
+ nxt_http_route_encoding_t encoding)
{
size_t size;
uint32_t i, n;
@@ -860,7 +929,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
if (string) {
pattern[0].case_sensitive = case_sensitive;
ret = nxt_http_route_pattern_create(task, mp, cv, &pattern[0],
- pattern_case);
+ pattern_case, encoding);
if (nxt_slow_path(ret != NXT_OK)) {
return NULL;
}
@@ -875,7 +944,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
value = nxt_conf_get_array_element(cv, i);
ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i],
- pattern_case);
+ pattern_case, encoding);
if (nxt_slow_path(ret != NXT_OK)) {
return NULL;
}
@@ -972,22 +1041,22 @@ nxt_http_addr_pattern_compare(const void *one, const void *two)
static nxt_int_t
nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
- nxt_http_route_pattern_case_t pattern_case)
+ nxt_http_route_pattern_case_t pattern_case,
+ nxt_http_route_encoding_t encoding)
{
u_char *start;
- nxt_str_t test;
+ nxt_str_t test, test2;
+ nxt_int_t ret;
nxt_uint_t n, length;
nxt_http_route_pattern_type_t type;
- /* Suppress warning about uninitialized variable. */
- length = 0;
-
type = NXT_HTTP_ROUTE_PATTERN_EXACT;
nxt_conf_get_string(cv, &test);
pattern->negative = 0;
pattern->any = 1;
+ pattern->min_length = 0;
if (test.length != 0) {
@@ -1000,7 +1069,6 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
}
if (test.length != 0) {
-
if (test.start[0] == '*') {
test.start++;
test.length--;
@@ -1026,18 +1094,45 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
length = test.length - 1;
for (n = 1; n < length; n++) {
- if (test.start[n] == '*') {
- test.length = n;
- type = NXT_HTTP_ROUTE_PATTERN_MIDDLE;
- break;
+ if (test.start[n] != '*') {
+ continue;
+ }
+
+ test.length = n;
+
+ test2.start = &test.start[n + 1];
+ test2.length = length - n;
+
+ ret = nxt_http_route_decode_str(&test2, encoding);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
}
+
+ type = NXT_HTTP_ROUTE_PATTERN_MIDDLE;
+
+ pattern->length2 = test2.length;
+ pattern->min_length += test2.length;
+
+ start = nxt_http_route_pattern_copy(mp, &test2,
+ pattern_case);
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ pattern->start2 = start;
+ break;
}
}
+
+ ret = nxt_http_route_decode_str(&test, encoding);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
}
}
pattern->type = type;
- pattern->min_length = test.length;
+ pattern->min_length += test.length;
pattern->length1 = test.length;
start = nxt_http_route_pattern_copy(mp, &test, pattern_case);
@@ -1047,20 +1142,43 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
pattern->start1 = start;
- if (type == NXT_HTTP_ROUTE_PATTERN_MIDDLE) {
- length -= test.length;
- pattern->length2 = length;
- pattern->min_length += length;
+ return NXT_OK;
+}
- test.start = &test.start[test.length + 1];
- test.length = length;
- start = nxt_http_route_pattern_copy(mp, &test, pattern_case);
- if (nxt_slow_path(start == NULL)) {
+static nxt_int_t
+nxt_http_route_decode_str(nxt_str_t *str, nxt_http_route_encoding_t encoding)
+{
+ u_char *start, *end;
+
+ switch (encoding) {
+ case NXT_HTTP_ROUTE_ENCODING_NONE:
+ break;
+
+ case NXT_HTTP_ROUTE_ENCODING_URI:
+ start = str->start;
+
+ end = nxt_decode_uri(start, start, str->length);
+ if (nxt_slow_path(end == NULL)) {
return NXT_ERROR;
}
- pattern->start2 = start;
+ str->length = end - start;
+ break;
+
+ case NXT_HTTP_ROUTE_ENCODING_URI_PLUS:
+ start = str->start;
+
+ end = nxt_decode_uri_plus(start, start, str->length);
+ if (nxt_slow_path(end == NULL)) {
+ return NXT_ERROR;
+ }
+
+ str->length = end - start;
+ break;
+
+ default:
+ nxt_unreachable();
}
return NXT_OK;
@@ -1097,9 +1215,10 @@ nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test,
}
-void
+nxt_int_t
nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
{
+ nxt_int_t ret;
nxt_http_route_t **route, **end;
nxt_http_routes_t *routes;
@@ -1110,75 +1229,141 @@ nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
end = route + routes->items;
while (route < end) {
- nxt_http_route_resolve(task, tmcf, *route);
+ ret = nxt_http_route_resolve(task, tmcf, *route);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
route++;
}
}
+
+ return NXT_OK;
}
-static void
+static nxt_int_t
nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_route_t *route)
{
+ nxt_int_t ret;
nxt_http_route_match_t **match, **end;
match = &route->match[0];
end = match + route->items;
while (match < end) {
- nxt_http_action_resolve(task, tmcf, &(*match)->action);
+ ret = nxt_http_action_resolve(task, tmcf, &(*match)->action);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
match++;
}
+
+ return NXT_OK;
}
-static void
+static nxt_int_t
nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_action_t *action)
{
- nxt_str_t name;
+ nxt_str_t *targets;
+ nxt_int_t ret;
+ nxt_uint_t i;
+ nxt_str_t segments[3];
if (action->handler != NULL) {
if (action->handler == nxt_http_static_handler
&& action->u.fallback != NULL)
{
- nxt_http_action_resolve(task, tmcf, action->u.fallback);
+ return nxt_http_action_resolve(task, tmcf, action->u.fallback);
}
- return;
+ return NXT_OK;
}
- name = action->name;
-
- if (nxt_str_start(&name, "applications/", 13)) {
- name.length -= 13;
- name.start += 13;
+ ret = nxt_http_pass_segments(tmcf->mem_pool, &action->name, segments, 3);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
- nxt_router_listener_application(tmcf, &name, action);
+ if (nxt_str_eq(&segments[0], "applications", 12)) {
+ nxt_router_listener_application(tmcf, &segments[1], action);
nxt_router_app_use(task, action->u.application, 1);
- } else if (nxt_str_start(&name, "upstreams/", 10)) {
- name.length -= 10;
- name.start += 10;
-
- nxt_upstream_find(tmcf->router_conf->upstreams, &name, action);
+ if (segments[2].length != 0) {
+ targets = action->u.application->targets;
- } else if (nxt_str_start(&name, "routes", 6)) {
+ for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++);
- if (name.length == 6) {
- name.length = 0;
- name.start = NULL;
+ action->target = i;
- } else if (name.start[6] == '/') {
- name.length -= 7;
- name.start += 7;
+ } else {
+ action->target = 0;
}
- nxt_http_route_find(tmcf->router_conf->routes, &name, action);
+ } else if (nxt_str_eq(&segments[0], "upstreams", 9)) {
+ nxt_upstream_find(tmcf->router_conf->upstreams, &segments[1], action);
+
+ } else if (nxt_str_eq(&segments[0], "routes", 6)) {
+ nxt_http_route_find(tmcf->router_conf->routes, &segments[1], action);
}
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
+ nxt_uint_t n)
+{
+ u_char *p;
+ nxt_str_t rest;
+
+ if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memzero(segments, n * sizeof(nxt_str_t));
+
+ do {
+ p = nxt_memchr(rest.start, '/', rest.length);
+
+ if (p != NULL) {
+ n--;
+
+ if (n == 0) {
+ return NXT_DECLINED;
+ }
+
+ segments->length = p - rest.start;
+ segments->start = rest.start;
+
+ rest.length -= segments->length + 1;
+ rest.start = p + 1;
+
+ } else {
+ n = 0;
+ *segments = rest;
+ }
+
+ if (segments->length == 0) {
+ return NXT_DECLINED;
+ }
+
+ p = nxt_decode_uri(segments->start, segments->start, segments->length);
+ if (p == NULL) {
+ return NXT_DECLINED;
+ }
+
+ segments->length = p - segments->start;
+ segments++;
+
+ } while (n);
+
+ return NXT_OK;
}
@@ -1244,6 +1429,8 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_router_listener_application(tmcf, name, action);
nxt_router_app_use(task, action->u.application, 1);
+ action->target = 0;
+
return action;
}
@@ -1677,7 +1864,8 @@ static nxt_array_t *
nxt_http_route_arguments_parse(nxt_http_request_t *r)
{
size_t name_length;
- u_char c, *p, *start, *end, *name;
+ u_char c, *p, *dst, *dst_start, *start, *end, *name;
+ uint8_t d0, d1;
uint32_t hash;
nxt_bool_t valid;
nxt_array_t *args;
@@ -1697,39 +1885,81 @@ nxt_http_route_arguments_parse(nxt_http_request_t *r)
name = NULL;
name_length = 0;
+ dst_start = nxt_mp_nget(r->mem_pool, r->args->length);
+ if (nxt_slow_path(dst_start == NULL)) {
+ return NULL;
+ }
+
start = r->args->start;
end = start + r->args->length;
- for (p = start; p < end; p++) {
+ for (p = start, dst = dst_start; p < end; p++, dst++) {
c = *p;
+ *dst = c;
- if (c == '=') {
- name_length = p - start;
- name = start;
- start = p + 1;
+ switch (c) {
+ case '=':
+ if (name != NULL) {
+ break;
+ }
+
+ name_length = dst - dst_start;
valid = (name_length != 0);
+ name = dst_start;
+ dst_start = dst + 1;
- } else if (c == '&') {
+ continue;
+
+ case '&':
if (valid) {
nv = nxt_http_route_argument(args, name, name_length, hash,
- start, p);
+ dst_start, dst);
if (nxt_slow_path(nv == NULL)) {
return NULL;
}
}
hash = NXT_HTTP_FIELD_HASH_INIT;
+ name_length = 0;
valid = 1;
name = NULL;
- start = p + 1;
+ dst_start = dst + 1;
+
+ continue;
+
+ case '+':
+ c = ' ';
+ *dst = ' ';
+
+ break;
+
+ case '%':
+ if (nxt_slow_path(end - p <= 2)) {
+ break;
+ }
+
+ d0 = nxt_hex2int[p[1]];
+ d1 = nxt_hex2int[p[2]];
+
+ if (nxt_slow_path((d0 | d1) >= 16)) {
+ break;
+ }
+
+ p += 2;
+ c = (d0 << 4) + d1;
+ *dst = c;
+
+ break;
+ }
- } else if (name == NULL) {
+ if (name == NULL) {
hash = nxt_http_field_hash_char(hash, c);
}
}
if (valid) {
- nv = nxt_http_route_argument(args, name, name_length, hash, start, p);
+ nv = nxt_http_route_argument(args, name, name_length, hash, dst_start,
+ dst);
if (nxt_slow_path(nv == NULL)) {
return NULL;
}
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index 46ae57a7..ee18be1b 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -76,7 +76,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_set(&extension, ".html");
} else {
- nxt_str_null(&index);
+ nxt_str_set(&index, "");
nxt_str_null(&extension);
}
diff --git a/src/nxt_java.c b/src/nxt_java.c
index 004907d6..c7471509 100644
--- a/src/nxt_java.c
+++ b/src/nxt_java.c
@@ -26,10 +26,12 @@
#include "java/nxt_jni_URLClassLoader.h"
#include "nxt_jars.h"
+#include "nxt_java_mounts.h"
-static nxt_int_t nxt_java_pre_init(nxt_task_t *task,
+static nxt_int_t nxt_java_setup(nxt_task_t *task, nxt_process_t *process,
nxt_common_app_conf_t *conf);
-static nxt_int_t nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_java_start(nxt_task_t *task,
+ nxt_process_data_t *data);
static void nxt_java_request_handler(nxt_unit_request_info_t *req);
static void nxt_java_websocket_handler(nxt_unit_websocket_frame_t *ws);
static void nxt_java_close_handler(nxt_unit_request_info_t *req);
@@ -49,8 +51,10 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
compat,
nxt_string("java"),
NXT_STRING(NXT_JAVA_VERSION),
- nxt_java_pre_init,
- nxt_java_init,
+ nxt_java_mounts,
+ nxt_nitems(nxt_java_mounts),
+ nxt_java_setup,
+ nxt_java_start,
};
typedef struct {
@@ -60,22 +64,69 @@ typedef struct {
static nxt_int_t
-nxt_java_pre_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_java_setup(nxt_task_t *task, nxt_process_t *process,
+ nxt_common_app_conf_t *conf)
{
+ char *path, *relpath, *p, *rootfs;
+ size_t jars_dir_len, rootfs_len;
const char *unit_jars;
+ rootfs = (char *) process->isolation.rootfs;
+ rootfs_len = 0;
+
unit_jars = conf->u.java.unit_jars;
if (unit_jars == NULL) {
- unit_jars = NXT_JARS;
+ if (rootfs != NULL) {
+ unit_jars = "/";
+ } else {
+ unit_jars = NXT_JARS;
+ }
}
- nxt_java_modules = realpath(unit_jars, NULL);
- if (nxt_java_modules == NULL) {
- nxt_alert(task, "realpath(%s) failed: %E", unit_jars, nxt_errno);
+ relpath = strdup(unit_jars);
+ if (nxt_slow_path(relpath == NULL)) {
return NXT_ERROR;
}
+ if (rootfs != NULL) {
+ jars_dir_len = strlen(unit_jars);
+ rootfs_len = strlen(rootfs);
+
+ path = nxt_malloc(jars_dir_len + rootfs_len + 1);
+ if (nxt_slow_path(path == NULL)) {
+ free(relpath);
+ return NXT_ERROR;
+ }
+
+ p = nxt_cpymem(path, process->isolation.rootfs, rootfs_len);
+ p = nxt_cpymem(p, relpath, jars_dir_len);
+ *p = '\0';
+
+ free(relpath);
+
+ } else {
+ path = relpath;
+ }
+
+ nxt_java_modules = realpath(path, NULL);
+ if (nxt_java_modules == NULL) {
+ nxt_alert(task, "realpath(\"%s\") failed %E", path, nxt_errno);
+ goto free;
+ }
+
+ if (rootfs != NULL && strlen(path) > rootfs_len) {
+ nxt_java_modules = path + rootfs_len;
+ }
+
+ nxt_debug(task, "JAVA MODULES: %s", nxt_java_modules);
+
return NXT_OK;
+
+free:
+
+ nxt_free(path);
+
+ return NXT_ERROR;
}
@@ -83,6 +134,7 @@ static char **
nxt_java_module_jars(const char *jars[], int jar_count)
{
char **res, *jurl;
+ uint8_t pathsep;
nxt_int_t modules_len, jlen, i;
const char **jar;
@@ -93,9 +145,13 @@ nxt_java_module_jars(const char *jars[], int jar_count)
modules_len = nxt_strlen(nxt_java_modules);
+ pathsep = nxt_java_modules[modules_len - 1] == '/';
+
for (i = 0, jar = jars; *jar != NULL; jar++) {
- jlen = nxt_length("file:") + modules_len + nxt_length("/")
- + nxt_strlen(*jar) + 1;
+ jlen = nxt_length("file:") + modules_len
+ + (!pathsep ? nxt_length("/") : 0)
+ + nxt_strlen(*jar) + 1;
+
jurl = nxt_malloc(jlen);
if (jurl == NULL) {
return NULL;
@@ -105,7 +161,11 @@ nxt_java_module_jars(const char *jars[], int jar_count)
jurl = nxt_cpymem(jurl, "file:", nxt_length("file:"));
jurl = nxt_cpymem(jurl, nxt_java_modules, modules_len);
- *jurl++ = '/';
+
+ if (!pathsep) {
+ *jurl++ = '/';
+ }
+
jurl = nxt_cpymem(jurl, *jar, nxt_strlen(*jar));
*jurl++ = '\0';
}
@@ -115,24 +175,26 @@ nxt_java_module_jars(const char *jars[], int jar_count)
static nxt_int_t
-nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_java_start(nxt_task_t *task, nxt_process_data_t *data)
{
- jint rc;
- char *opt, *real_path;
- char **classpath_arr, **unit_jars, **system_jars;
- JavaVM *jvm;
- JNIEnv *env;
- jobject cl, classpath;
- nxt_str_t str;
- nxt_int_t opt_len, real_path_len;
- nxt_uint_t i, unit_jars_count, classpath_count, system_jars_count;
- JavaVMOption *jvm_opt;
- JavaVMInitArgs jvm_args;
- nxt_unit_ctx_t *ctx;
- nxt_unit_init_t java_init;
- nxt_java_data_t data;
- nxt_conf_value_t *value;
- nxt_java_app_conf_t *c;
+ jint rc;
+ char *opt, *real_path;
+ char **classpath_arr, **unit_jars, **system_jars;
+ JavaVM *jvm;
+ JNIEnv *env;
+ jobject cl, classpath;
+ nxt_str_t str;
+ nxt_int_t opt_len, real_path_len;
+ nxt_uint_t i, unit_jars_count, classpath_count;
+ nxt_uint_t system_jars_count;
+ JavaVMOption *jvm_opt;
+ JavaVMInitArgs jvm_args;
+ nxt_unit_ctx_t *ctx;
+ nxt_unit_init_t java_init;
+ nxt_java_data_t java_data;
+ nxt_conf_value_t *value;
+ nxt_java_app_conf_t *c;
+ nxt_common_app_conf_t *app_conf;
//setenv("ASAN_OPTIONS", "handle_segv=0", 1);
@@ -140,7 +202,8 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
jvm_args.nOptions = 0;
jvm_args.ignoreUnrecognized = 0;
- c = &conf->u.java;
+ app_conf = data->app;
+ c = &app_conf->u.java;
if (c->options != NULL) {
jvm_args.nOptions += nxt_conf_array_elements_count(c->options);
@@ -338,8 +401,8 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
goto env_failed;
}
- data.env = env;
- data.ctx = nxt_java_startContext(env, c->webapp, classpath);
+ java_data.env = env;
+ java_data.ctx = nxt_java_startContext(env, c->webapp, classpath);
if ((*env)->ExceptionCheck(env)) {
nxt_alert(task, "Unhandled exception in application start");
@@ -353,8 +416,8 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
java_init.callbacks.websocket_handler = nxt_java_websocket_handler;
java_init.callbacks.close_handler = nxt_java_close_handler;
java_init.request_data_size = sizeof(nxt_java_request_data_t);
- java_init.data = &data;
- java_init.shm_limit = conf->shm_limit;
+ java_init.data = &java_data;
+ java_init.shm_limit = app_conf->shm_limit;
ctx = nxt_unit_init(&java_init);
if (nxt_slow_path(ctx == NULL)) {
@@ -367,7 +430,7 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
/* TODO report error */
}
- nxt_java_stopContext(env, data.ctx);
+ nxt_java_stopContext(env, java_data.ctx);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
diff --git a/src/nxt_main.h b/src/nxt_main.h
index b310c4fa..5914fbd1 100644
--- a/src/nxt_main.h
+++ b/src/nxt_main.h
@@ -59,6 +59,7 @@ typedef uint16_t nxt_port_id_t;
#include <nxt_process_type.h>
#include <nxt_capability.h>
#include <nxt_credential.h>
+#include <nxt_fs.h>
#include <nxt_process.h>
#include <nxt_utf8.h>
#include <nxt_file_name.h>
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index eed37752..a16e44d3 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -14,6 +14,8 @@
#include <nxt_cert.h>
#endif
+#include <sys/mount.h>
+
typedef struct {
nxt_socket_t socket;
@@ -36,20 +38,11 @@ extern nxt_port_handlers_t nxt_router_process_port_handlers;
static nxt_int_t nxt_main_process_port_create(nxt_task_t *task,
nxt_runtime_t *rt);
static void nxt_main_process_title(nxt_task_t *task);
-static nxt_int_t nxt_main_start_controller_process(nxt_task_t *task,
- nxt_runtime_t *rt);
-static nxt_int_t nxt_main_create_controller_process(nxt_task_t *task,
- nxt_runtime_t *rt, nxt_process_init_t *init);
-static nxt_int_t nxt_main_create_router_process(nxt_task_t *task, nxt_runtime_t *rt,
- nxt_process_init_t *init);
-static nxt_int_t nxt_main_start_router_process(nxt_task_t *task,
- nxt_runtime_t *rt);
-static nxt_int_t nxt_main_start_discovery_process(nxt_task_t *task,
- nxt_runtime_t *rt);
-static nxt_int_t nxt_main_start_worker_process(nxt_task_t *task,
- nxt_runtime_t *rt, nxt_common_app_conf_t *app_conf, uint32_t stream);
-static nxt_int_t nxt_main_create_worker_process(nxt_task_t *task,
- nxt_runtime_t *rt, nxt_process_init_t *init);
+static nxt_int_t nxt_main_process_create(nxt_task_t *task,
+ const nxt_process_init_t init);
+static nxt_int_t nxt_main_start_process(nxt_task_t *task,
+ nxt_process_t *process);
+static nxt_process_t *nxt_main_process_new(nxt_task_t *task, nxt_runtime_t *rt);
static void nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj,
void *data);
static void nxt_main_process_sigquit_handler(nxt_task_t *task, void *obj,
@@ -60,8 +53,7 @@ static void nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj,
void *data);
static void nxt_main_process_signal_handler(nxt_task_t *task, void *obj,
void *data);
-static void nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid);
-static void nxt_main_stop_worker_processes(nxt_task_t *task, nxt_runtime_t *rt);
+static void nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid);
static void nxt_main_port_socket_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
static nxt_int_t nxt_main_listening_socket(nxt_sockaddr_t *sa,
@@ -73,29 +65,6 @@ static void nxt_main_port_conf_store_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
static void nxt_main_port_access_log_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
-static nxt_process_init_t *nxt_process_init_create(nxt_task_t *task,
- nxt_process_type_t type, const nxt_str_t *name);
-static nxt_int_t nxt_process_init_name_set(nxt_process_init_t *init,
- nxt_process_type_t type, const nxt_str_t *name);
-static nxt_int_t nxt_process_init_creds_set(nxt_task_t *task,
- nxt_process_init_t *init, nxt_str_t *user, nxt_str_t *group);
-
-static nxt_int_t nxt_init_isolation(nxt_task_t *task,
- nxt_conf_value_t *isolation, nxt_process_init_t *init);
-#if (NXT_HAVE_CLONE)
-static nxt_int_t nxt_init_clone_flags(nxt_task_t *task,
- nxt_conf_value_t *namespaces, nxt_process_init_t *init);
-#endif
-
-#if (NXT_HAVE_CLONE_NEWUSER)
-static nxt_int_t nxt_init_isolation_creds(nxt_task_t *task,
- nxt_conf_value_t *isolation, nxt_process_init_t *init);
-static nxt_int_t nxt_init_vldt_isolation_creds(nxt_task_t *task,
- nxt_process_init_t *init);
-static nxt_int_t nxt_init_isolation_credential_map(nxt_task_t *task,
- nxt_mp_t *mem_pool, nxt_conf_value_t *map_array,
- nxt_clone_credential_map_t *map);
-#endif
const nxt_sig_event_t nxt_main_process_signals[] = {
nxt_event_signal(SIGHUP, nxt_main_process_signal_handler),
@@ -108,54 +77,6 @@ const nxt_sig_event_t nxt_main_process_signals[] = {
};
-static const nxt_port_handlers_t nxt_app_process_port_handlers = {
- .new_port = nxt_port_new_port_handler,
- .change_file = nxt_port_change_log_file_handler,
- .mmap = nxt_port_mmap_handler,
- .remove_pid = nxt_port_remove_pid_handler,
-};
-
-
-static const nxt_port_handlers_t nxt_discovery_process_port_handlers = {
- .quit = nxt_worker_process_quit_handler,
- .new_port = nxt_port_new_port_handler,
- .change_file = nxt_port_change_log_file_handler,
- .mmap = nxt_port_mmap_handler,
- .data = nxt_port_data_handler,
- .remove_pid = nxt_port_remove_pid_handler,
- .rpc_ready = nxt_port_rpc_handler,
- .rpc_error = nxt_port_rpc_handler,
-};
-
-
-static const nxt_port_handlers_t *nxt_process_port_handlers[NXT_PROCESS_MAX] =
-{
- NULL,
- &nxt_discovery_process_port_handlers,
- &nxt_controller_process_port_handlers,
- &nxt_router_process_port_handlers,
- &nxt_app_process_port_handlers
-};
-
-
-static const nxt_process_start_t nxt_process_starts[NXT_PROCESS_MAX] = {
- NULL,
- nxt_discovery_start,
- nxt_controller_start,
- nxt_router_start,
- nxt_app_start
-};
-
-
-static const nxt_process_restart_t nxt_process_restarts[NXT_PROCESS_MAX] = {
- NULL,
- NULL,
- &nxt_main_create_controller_process,
- &nxt_main_create_router_process,
- NULL
-};
-
-
static nxt_bool_t nxt_exiting;
@@ -172,11 +93,11 @@ nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task,
nxt_main_process_title(task);
/*
- * The dicsovery process will send a message processed by
+ * The discovery process will send a message processed by
* nxt_main_port_modules_handler() which starts the controller
* and router processes.
*/
- return nxt_main_start_discovery_process(task, rt);
+ return nxt_main_process_create(task, nxt_discovery_process);
}
@@ -275,21 +196,9 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
static nxt_conf_map_t nxt_php_app_conf[] = {
{
- nxt_string("root"),
- NXT_CONF_MAP_CSTRZ,
- offsetof(nxt_common_app_conf_t, u.php.root),
- },
-
- {
- nxt_string("script"),
- NXT_CONF_MAP_STR,
- offsetof(nxt_common_app_conf_t, u.php.script),
- },
-
- {
- nxt_string("index"),
- NXT_CONF_MAP_STR,
- offsetof(nxt_common_app_conf_t, u.php.index),
+ nxt_string("targets"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_common_app_conf_t, u.php.targets),
},
{
@@ -362,48 +271,71 @@ nxt_port_main_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
static void
-nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
{
- u_char *start, ch;
+ u_char *start, *p, ch;
size_t type_len;
- nxt_mp_t *mp;
nxt_int_t ret;
nxt_buf_t *b;
nxt_port_t *port;
nxt_runtime_t *rt;
+ nxt_process_t *process;
nxt_app_type_t idx;
nxt_conf_value_t *conf;
- nxt_common_app_conf_t app_conf;
+ nxt_process_init_t *init;
+ nxt_common_app_conf_t *app_conf;
ret = NXT_ERROR;
- mp = nxt_mp_create(1024, 128, 256, 32);
+ rt = task->thread->runtime;
- if (nxt_slow_path(mp == NULL)) {
+ process = nxt_main_process_new(task, rt);
+ if (nxt_slow_path(process == NULL)) {
return;
}
- b = nxt_buf_chk_make_plain(mp, msg->buf, msg->size);
+ init = nxt_process_init(process);
+
+ *init = nxt_app_process;
+ b = nxt_buf_chk_make_plain(process->mem_pool, msg->buf, msg->size);
if (b == NULL) {
- return;
+ goto failed;
}
- nxt_debug(task, "main start worker: %*s", b->mem.free - b->mem.pos,
+ nxt_debug(task, "main start process: %*s", b->mem.free - b->mem.pos,
b->mem.pos);
- nxt_memzero(&app_conf, sizeof(nxt_common_app_conf_t));
+ app_conf = nxt_mp_zalloc(process->mem_pool, sizeof(nxt_common_app_conf_t));
+ if (nxt_slow_path(app_conf == NULL)) {
+ goto failed;
+ }
start = b->mem.pos;
- app_conf.name.start = start;
- app_conf.name.length = nxt_strlen(start);
- app_conf.shm_limit = 100 * 1024 * 1024;
+ app_conf->name.start = start;
+ app_conf->name.length = nxt_strlen(start);
+
+ init->name = (const char *) start;
+
+ process->name = nxt_mp_alloc(process->mem_pool, app_conf->name.length
+ + sizeof("\"\" application") + 1);
- start += app_conf.name.length + 1;
+ if (nxt_slow_path(process->name == NULL)) {
+ goto failed;
+ }
+
+ p = (u_char *) process->name;
+ *p++ = '"';
+ p = nxt_cpymem(p, init->name, app_conf->name.length);
+ p = nxt_cpymem(p, "\" application", 13);
+ *p = '\0';
- conf = nxt_conf_json_parse(mp, start, b->mem.free, NULL);
+ app_conf->shm_limit = 100 * 1024 * 1024;
+ start += app_conf->name.length + 1;
+
+ conf = nxt_conf_json_parse(process->mem_pool, start, b->mem.free, NULL);
if (conf == NULL) {
nxt_alert(task, "router app configuration parsing error");
@@ -412,44 +344,45 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
rt = task->thread->runtime;
- app_conf.user.start = (u_char*)rt->user_cred.user;
- app_conf.user.length = nxt_strlen(rt->user_cred.user);
+ app_conf->user.start = (u_char*)rt->user_cred.user;
+ app_conf->user.length = nxt_strlen(rt->user_cred.user);
+
+ ret = nxt_conf_map_object(process->mem_pool, conf, nxt_common_app_conf,
+ nxt_nitems(nxt_common_app_conf), app_conf);
- ret = nxt_conf_map_object(mp, conf, nxt_common_app_conf,
- nxt_nitems(nxt_common_app_conf), &app_conf);
if (ret != NXT_OK) {
nxt_alert(task, "failed to map common app conf received from router");
goto failed;
}
- for (type_len = 0; type_len != app_conf.type.length; type_len++) {
- ch = app_conf.type.start[type_len];
+ for (type_len = 0; type_len != app_conf->type.length; type_len++) {
+ ch = app_conf->type.start[type_len];
if (ch == ' ' || nxt_isdigit(ch)) {
break;
}
}
- idx = nxt_app_parse_type(app_conf.type.start, type_len);
+ idx = nxt_app_parse_type(app_conf->type.start, type_len);
if (nxt_slow_path(idx >= nxt_nitems(nxt_app_maps))) {
nxt_alert(task, "invalid app type %d received from router", (int) idx);
goto failed;
}
- ret = nxt_conf_map_object(mp, conf, nxt_app_maps[idx].map,
- nxt_app_maps[idx].size, &app_conf);
+ ret = nxt_conf_map_object(process->mem_pool, conf, nxt_app_maps[idx].map,
+ nxt_app_maps[idx].size, app_conf);
if (nxt_slow_path(ret != NXT_OK)) {
nxt_alert(task, "failed to map app conf received from router");
goto failed;
}
- if (app_conf.limits != NULL) {
- ret = nxt_conf_map_object(mp, app_conf.limits,
+ if (app_conf->limits != NULL) {
+ ret = nxt_conf_map_object(process->mem_pool, app_conf->limits,
nxt_common_app_limits_conf,
nxt_nitems(nxt_common_app_limits_conf),
- &app_conf);
+ app_conf);
if (nxt_slow_path(ret != NXT_OK)) {
nxt_alert(task, "failed to map app limits received from router");
@@ -457,38 +390,91 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
}
}
- ret = nxt_main_start_worker_process(task, task->thread->runtime,
- &app_conf, msg->port_msg.stream);
+ app_conf->self = conf;
+
+ process->stream = msg->port_msg.stream;
+ process->data.app = app_conf;
+
+ ret = nxt_main_start_process(task, process);
+ if (nxt_fast_path(ret == NXT_OK || ret == NXT_AGAIN)) {
+ return;
+ }
failed:
- if (ret == NXT_ERROR) {
- port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
- msg->port_msg.reply_port);
- if (nxt_fast_path(port != NULL)) {
- nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR,
- -1, msg->port_msg.stream, 0, NULL);
- }
+ nxt_process_use(task, process, -1);
+
+ port = nxt_runtime_port_find(rt, msg->port_msg.pid,
+ msg->port_msg.reply_port);
+
+ if (nxt_fast_path(port != NULL)) {
+ nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR,
+ -1, msg->port_msg.stream, 0, NULL);
}
+}
- nxt_mp_destroy(mp);
+
+static void
+nxt_main_process_created_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ nxt_port_t *port;
+ nxt_process_t *process;
+ nxt_runtime_t *rt;
+
+ rt = task->thread->runtime;
+
+ process = nxt_runtime_process_find(rt, msg->port_msg.pid);
+ if (nxt_slow_path(process == NULL)) {
+ return;
+ }
+
+ nxt_assert(process->state == NXT_PROCESS_STATE_CREATING);
+
+ port = nxt_runtime_port_find(rt, msg->port_msg.pid,
+ msg->port_msg.reply_port);
+
+
+ if (nxt_slow_path(port == NULL)) {
+ return;
+ }
+
+#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
+ if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) {
+ if (nxt_slow_path(nxt_clone_credential_map(task, process->pid,
+ process->user_cred,
+ &process->isolation.clone)
+ != NXT_OK))
+ {
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR,
+ -1, msg->port_msg.stream, 0, NULL);
+ return;
+ }
+ }
+
+#endif
+
+ process->state = NXT_PROCESS_STATE_CREATED;
+
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST,
+ -1, msg->port_msg.stream, 0, NULL);
}
static nxt_port_handlers_t nxt_main_process_port_handlers = {
- .data = nxt_port_main_data_handler,
- .process_ready = nxt_port_process_ready_handler,
- .start_worker = nxt_port_main_start_worker_handler,
- .socket = nxt_main_port_socket_handler,
- .modules = nxt_main_port_modules_handler,
- .conf_store = nxt_main_port_conf_store_handler,
+ .data = nxt_port_main_data_handler,
+ .process_created = nxt_main_process_created_handler,
+ .process_ready = nxt_port_process_ready_handler,
+ .start_process = nxt_port_main_start_process_handler,
+ .socket = nxt_main_port_socket_handler,
+ .modules = nxt_main_port_modules_handler,
+ .conf_store = nxt_main_port_conf_store_handler,
#if (NXT_TLS)
- .cert_get = nxt_cert_store_get_handler,
- .cert_delete = nxt_cert_store_delete_handler,
+ .cert_get = nxt_cert_store_get_handler,
+ .cert_delete = nxt_cert_store_delete_handler,
#endif
- .access_log = nxt_main_port_access_log_handler,
- .rpc_ready = nxt_port_rpc_handler,
- .rpc_error = nxt_port_rpc_handler,
+ .access_log = nxt_main_port_access_log_handler,
+ .rpc_ready = nxt_port_rpc_handler,
+ .rpc_error = nxt_port_rpc_handler,
};
@@ -509,16 +495,17 @@ nxt_main_process_port_create(nxt_task_t *task, nxt_runtime_t *rt)
ret = nxt_port_socket_init(task, port, 0);
if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_port_use(task, port, -1);
return ret;
}
/*
* A main process port. A write port is not closed
- * since it should be inherited by worker processes.
+ * since it should be inherited by processes.
*/
nxt_port_enable(task, port, &nxt_main_process_port_handlers);
- process->ready = 1;
+ process->state = NXT_PROCESS_STATE_READY;
return NXT_OK;
}
@@ -551,234 +538,68 @@ nxt_main_process_title(nxt_task_t *task)
static nxt_int_t
-nxt_main_start_controller_process(nxt_task_t *task, nxt_runtime_t *rt)
+nxt_main_process_create(nxt_task_t *task, const nxt_process_init_t init)
{
- nxt_process_init_t *init;
+ nxt_int_t ret;
+ nxt_runtime_t *rt;
+ nxt_process_t *process;
+ nxt_process_init_t *pinit;
- static const nxt_str_t name = nxt_string("controller");
+ rt = task->thread->runtime;
- init = nxt_process_init_create(task, NXT_PROCESS_CONTROLLER, &name);
- if (nxt_slow_path(init == NULL)) {
+ process = nxt_main_process_new(task, rt);
+ if (nxt_slow_path(process == NULL)) {
return NXT_ERROR;
}
- return nxt_main_create_controller_process(task, rt, init);;
-}
-
-
-static nxt_int_t
-nxt_main_create_controller_process(nxt_task_t *task, nxt_runtime_t *rt,
- nxt_process_init_t *init)
-{
- ssize_t n;
- nxt_int_t ret;
- nxt_str_t *conf;
- nxt_file_t file;
- nxt_file_info_t fi;
- nxt_controller_init_t ctrl_init;
-
- nxt_memzero(&ctrl_init, sizeof(nxt_controller_init_t));
-
- conf = &ctrl_init.conf;
-
- nxt_memzero(&file, sizeof(nxt_file_t));
-
- file.name = (nxt_file_name_t *) rt->conf;
-
- ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
-
- if (ret == NXT_OK) {
- ret = nxt_file_info(&file, &fi);
-
- if (nxt_fast_path(ret == NXT_OK && nxt_is_file(&fi))) {
- conf->length = nxt_file_size(&fi);
- conf->start = nxt_malloc(conf->length);
-
- if (nxt_slow_path(conf->start == NULL)) {
- nxt_file_close(task, &file);
- return NXT_ERROR;
- }
-
- n = nxt_file_read(&file, conf->start, conf->length, 0);
-
- if (nxt_slow_path(n != (ssize_t) conf->length)) {
- nxt_free(conf->start);
- conf->start = NULL;
-
- nxt_alert(task, "failed to restore previous configuration: "
- "cannot read the file");
- }
- }
-
- nxt_file_close(task, &file);
- }
-
-#if (NXT_TLS)
- ctrl_init.certs = nxt_cert_store_load(task);
-#endif
-
- init->data = &ctrl_init;
+ process->name = init.name;
+ process->user_cred = &rt->user_cred;
- ret = nxt_main_create_worker_process(task, rt, init);
-
- if (ret == NXT_OK) {
- if (conf->start != NULL) {
- nxt_free(conf->start);
- }
+ pinit = nxt_process_init(process);
+ *pinit = init;
-#if (NXT_TLS)
- if (ctrl_init.certs != NULL) {
- nxt_cert_store_release(ctrl_init.certs);
- }
-#endif
+ ret = nxt_main_start_process(task, process);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ nxt_process_use(task, process, -1);
}
return ret;
}
-static nxt_int_t
-nxt_main_start_discovery_process(nxt_task_t *task, nxt_runtime_t *rt)
+static nxt_process_t *
+nxt_main_process_new(nxt_task_t *task, nxt_runtime_t *rt)
{
- nxt_process_init_t *init;
-
- static const nxt_str_t name = nxt_string("discovery");
+ nxt_process_t *process;
- init = nxt_process_init_create(task, NXT_PROCESS_DISCOVERY, &name);
- if (nxt_slow_path(init == NULL)) {
- return NXT_ERROR;
+ process = nxt_runtime_process_new(rt);
+ if (nxt_slow_path(process == NULL)) {
+ return NULL;
}
- return nxt_main_create_worker_process(task, rt, init);
-}
-
-
-static nxt_int_t
-nxt_main_start_router_process(nxt_task_t *task, nxt_runtime_t *rt)
-{
- nxt_process_init_t *init;
-
- static const nxt_str_t name = nxt_string("router");
-
- init = nxt_process_init_create(task, NXT_PROCESS_ROUTER, &name);
- if (nxt_slow_path(init == NULL)) {
- return NXT_ERROR;
+ process->mem_pool = nxt_mp_create(1024, 128, 256, 32);
+ if (process->mem_pool == NULL) {
+ nxt_process_use(task, process, -1);
+ return NULL;
}
- return nxt_main_create_router_process(task, rt, init);
-}
-
-
-static nxt_int_t
-nxt_main_create_router_process(nxt_task_t *task, nxt_runtime_t *rt,
- nxt_process_init_t *init)
-{
- nxt_main_stop_worker_processes(task, rt);
-
- return nxt_main_create_worker_process(task, rt, init);
+ return process;
}
static nxt_int_t
-nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
- nxt_common_app_conf_t *app_conf, uint32_t stream)
+nxt_main_start_process(nxt_task_t *task, nxt_process_t *process)
{
- nxt_int_t cap_setid;
+ nxt_mp_t *tmp_mp;
nxt_int_t ret;
+ nxt_pid_t pid;
+ nxt_port_t *port;
nxt_process_init_t *init;
- init = nxt_process_init_create(task, NXT_PROCESS_WORKER, &app_conf->name);
- if (nxt_slow_path(init == NULL)) {
- return NXT_ERROR;
- }
-
- cap_setid = rt->capabilities.setid;
-
- if (app_conf->isolation != NULL) {
- ret = nxt_init_isolation(task, app_conf->isolation, init);
- if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
- }
- }
-
-#if (NXT_HAVE_CLONE_NEWUSER)
- if (NXT_CLONE_USER(init->isolation.clone.flags)) {
- cap_setid = 1;
- }
-#endif
-
- if (cap_setid) {
- ret = nxt_process_init_creds_set(task, init, &app_conf->user,
- &app_conf->group);
- if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
- }
-
- } else {
- if (!nxt_str_eq(&app_conf->user, (u_char *) rt->user_cred.user,
- nxt_strlen(rt->user_cred.user)))
- {
- nxt_alert(task, "cannot set user \"%V\" for app \"%V\": "
- "missing capabilities", &app_conf->user, &app_conf->name);
- goto fail;
- }
-
- if (app_conf->group.length > 0
- && !nxt_str_eq(&app_conf->group, (u_char *) rt->group,
- nxt_strlen(rt->group)))
- {
- nxt_alert(task, "cannot set group \"%V\" for app \"%V\": "
- "missing capabilities", &app_conf->group,
- &app_conf->name);
- goto fail;
- }
- }
-
- init->data = app_conf;
- init->stream = stream;
-
-#if (NXT_HAVE_CLONE_NEWUSER)
- ret = nxt_init_vldt_isolation_creds(task, init);
- if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
- }
-#endif
-
- return nxt_main_create_worker_process(task, rt, init);
-
-fail:
-
- nxt_mp_destroy(init->mem_pool);
-
- return NXT_ERROR;
-}
-
-
-nxt_int_t
-nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
- nxt_process_init_t *init)
-{
- nxt_int_t ret;
- nxt_pid_t pid;
- nxt_port_t *port;
- nxt_process_t *process;
-
- /*
- * TODO: remove process, init, ports from array on memory and fork failures.
- */
-
- process = nxt_runtime_process_new(rt);
- if (nxt_slow_path(process == NULL)) {
- nxt_mp_destroy(init->mem_pool);
-
- return NXT_ERROR;
- }
-
- process->init = init;
+ init = nxt_process_init(process);
port = nxt_port_new(task, 0, 0, init->type);
if (nxt_slow_path(port == NULL)) {
- nxt_process_use(task, process, -1);
return NXT_ERROR;
}
@@ -786,10 +607,24 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
nxt_process_use(task, process, -1);
+ ret = NXT_ERROR;
+ tmp_mp = NULL;
+
ret = nxt_port_socket_init(task, port, 0);
if (nxt_slow_path(ret != NXT_OK)) {
- nxt_port_use(task, port, -1);
- return ret;
+ goto fail;
+ }
+
+ tmp_mp = nxt_mp_create(1024, 128, 256, 32);
+ if (tmp_mp == NULL) {
+ goto fail;
+ }
+
+ if (init->prefork) {
+ ret = init->prefork(task, process, tmp_mp);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
}
pid = nxt_process_create(task, process);
@@ -798,15 +633,13 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
case -1:
nxt_port_close(task, port);
- nxt_port_use(task, port, -1);
-
- return NXT_ERROR;
+ break;
case 0:
- /* A worker process, return to the event engine work queue loop. */
- nxt_port_use(task, port, -1);
+ /* The child process: return to the event engine work queue loop. */
- return NXT_AGAIN;
+ ret = NXT_AGAIN;
+ break;
default:
/* The main process created a new process. */
@@ -814,35 +647,22 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
nxt_port_read_close(port);
nxt_port_write_enable(task, port);
- nxt_port_use(task, port, -1);
-
- return NXT_OK;
+ ret = NXT_OK;
+ break;
}
-}
+fail:
-void
-nxt_main_stop_all_processes(nxt_task_t *task, nxt_runtime_t *rt)
-{
- nxt_port_t *port;
- nxt_process_t *process;
-
- nxt_runtime_process_each(rt, process) {
-
- if (nxt_pid != process->pid) {
- nxt_process_port_each(process, port) {
-
- (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT,
- -1, 0, 0, NULL);
+ nxt_port_use(task, port, -1);
- } nxt_process_port_loop;
- }
+ if (nxt_fast_path(tmp_mp != NULL)) {
+ nxt_mp_destroy(tmp_mp);
+ }
- } nxt_runtime_process_loop;
+ return ret;
}
-
static void
nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj, void *data)
{
@@ -1019,7 +839,7 @@ nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data)
pid, WEXITSTATUS(status));
}
- nxt_main_cleanup_worker_process(task, pid);
+ nxt_main_cleanup_process(task, pid);
}
}
@@ -1033,98 +853,83 @@ nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data)
static void
-nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid)
+nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid)
{
- nxt_buf_t *buf;
- nxt_port_t *port;
- nxt_runtime_t *rt;
- nxt_process_t *process;
- nxt_process_type_t ptype;
- nxt_process_init_t *init;
- nxt_process_restart_t restart;
+ int stream;
+ nxt_int_t ret;
+ nxt_buf_t *buf;
+ nxt_port_t *port;
+ const char *name;
+ nxt_runtime_t *rt;
+ nxt_process_t *process;
+ nxt_process_init_t init;
rt = task->thread->runtime;
process = nxt_runtime_process_find(rt, pid);
+ if (!process) {
+ return;
+ }
- if (process) {
- init = process->init;
- process->init = NULL;
-
- ptype = nxt_process_type(process);
- restart = nxt_process_restarts[ptype];
-
- if (process->ready) {
- init->stream = 0;
- }
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ if (process->isolation.rootfs != NULL && process->isolation.mounts) {
+ (void) nxt_process_unmount_all(task, process);
+ }
+#endif
- nxt_process_close_ports(task, process);
+ name = process->name;
+ stream = process->stream;
+ init = *((nxt_process_init_t *) nxt_process_init(process));
- if (nxt_exiting) {
- nxt_mp_destroy(init->mem_pool);
+ if (process->state == NXT_PROCESS_STATE_READY) {
+ process->stream = 0;
+ }
- if (rt->nprocesses <= 2) {
- nxt_runtime_quit(task, 0);
- }
+ nxt_process_close_ports(task, process);
- return;
+ if (nxt_exiting) {
+ if (rt->nprocesses <= 1) {
+ nxt_runtime_quit(task, 0);
}
- nxt_runtime_process_each(rt, process) {
-
- if (process->pid == nxt_pid
- || process->pid == pid
- || nxt_queue_is_empty(&process->ports))
- {
- continue;
- }
-
- port = nxt_process_port_first(process);
-
- if (nxt_proc_remove_notify_matrix[ptype][port->type] == 0) {
- continue;
- }
-
- buf = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool,
- sizeof(pid));
- if (nxt_slow_path(buf == NULL)) {
- continue;
- }
-
- buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(pid));
-
- nxt_port_socket_write(task, port, NXT_PORT_MSG_REMOVE_PID,
- -1, init->stream, 0, buf);
- } nxt_runtime_process_loop;
+ return;
+ }
- if (restart != NULL) {
- restart(task, rt, init);
+ nxt_runtime_process_each(rt, process) {
- } else {
- nxt_mp_destroy(init->mem_pool);
+ if (process->pid == nxt_pid
+ || process->pid == pid
+ || nxt_queue_is_empty(&process->ports))
+ {
+ continue;
}
- }
-}
+ port = nxt_process_port_first(process);
-static void
-nxt_main_stop_worker_processes(nxt_task_t *task, nxt_runtime_t *rt)
-{
- nxt_port_t *port;
- nxt_process_t *process;
+ if (nxt_proc_remove_notify_matrix[init.type][port->type] == 0) {
+ continue;
+ }
- nxt_runtime_process_each(rt, process) {
+ buf = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool,
+ sizeof(pid));
- nxt_process_port_each(process, port) {
+ if (nxt_slow_path(buf == NULL)) {
+ continue;
+ }
- if (port->type == NXT_PROCESS_WORKER) {
- (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT,
- -1, 0, 0, NULL);
- }
+ buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(pid));
- } nxt_process_port_loop;
+ nxt_port_socket_write(task, port, NXT_PORT_MSG_REMOVE_PID, -1,
+ stream, 0, buf);
} nxt_runtime_process_loop;
+
+ if (init.restart) {
+ ret = nxt_main_process_create(task, init);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ nxt_alert(task, "failed to restart %s", name);
+ }
+ }
}
@@ -1335,19 +1140,50 @@ static nxt_conf_map_t nxt_app_lang_module_map[] = {
};
+static nxt_conf_map_t nxt_app_lang_mounts_map[] = {
+ {
+ nxt_string("src"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_fs_mount_t, src),
+ },
+ {
+ nxt_string("dst"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_fs_mount_t, dst),
+ },
+ {
+ nxt_string("fstype"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_fs_mount_t, fstype),
+ },
+ {
+ nxt_string("flags"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_fs_mount_t, flags),
+ },
+ {
+ nxt_string("data"),
+ NXT_CONF_MAP_CSTRZ,
+ offsetof(nxt_fs_mount_t, data),
+ },
+};
+
+
static void
nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
{
- uint32_t index;
+ uint32_t index, jindex, nmounts;
nxt_mp_t *mp;
nxt_int_t ret;
nxt_buf_t *b;
nxt_port_t *port;
nxt_runtime_t *rt;
- nxt_conf_value_t *conf, *root, *value;
+ nxt_fs_mount_t *mnt;
+ nxt_conf_value_t *conf, *root, *value, *mounts;
nxt_app_lang_module_t *lang;
static nxt_str_t root_path = nxt_string("/");
+ static nxt_str_t mounts_name = nxt_string("mounts");
rt = task->thread->runtime;
@@ -1404,7 +1240,7 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
break;
}
- lang = nxt_array_add(rt->languages);
+ lang = nxt_array_zero_add(rt->languages);
if (lang == NULL) {
goto fail;
}
@@ -1418,8 +1254,48 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
goto fail;
}
- nxt_debug(task, "lang %d %s \"%s\"",
- lang->type, lang->version, lang->file);
+ mounts = nxt_conf_get_object_member(value, &mounts_name, NULL);
+ if (mounts == NULL) {
+ nxt_alert(task, "missing mounts from discovery message.");
+ goto fail;
+ }
+
+ if (nxt_conf_type(mounts) != NXT_CONF_ARRAY) {
+ nxt_alert(task, "invalid mounts type from discovery message.");
+ goto fail;
+ }
+
+ nmounts = nxt_conf_array_elements_count(mounts);
+
+ lang->mounts = nxt_array_create(rt->mem_pool, nmounts,
+ sizeof(nxt_fs_mount_t));
+
+ if (lang->mounts == NULL) {
+ goto fail;
+ }
+
+ for (jindex = 0; /* */; jindex++) {
+ value = nxt_conf_get_array_element(mounts, jindex);
+ if (value == NULL) {
+ break;
+ }
+
+ mnt = nxt_array_zero_add(lang->mounts);
+ if (mnt == NULL) {
+ goto fail;
+ }
+
+ ret = nxt_conf_map_object(rt->mem_pool, value,
+ nxt_app_lang_mounts_map,
+ nxt_nitems(nxt_app_lang_mounts_map), mnt);
+
+ if (ret != NXT_OK) {
+ goto fail;
+ }
+ }
+
+ nxt_debug(task, "lang %d %s \"%s\" (%d mounts)",
+ lang->type, lang->version, lang->file, lang->mounts->nelts);
}
qsort(rt->languages->elts, rt->languages->nelts,
@@ -1429,10 +1305,15 @@ fail:
nxt_mp_destroy(mp);
- ret = nxt_main_start_controller_process(task, rt);
-
+ ret = nxt_main_process_create(task, nxt_controller_process);
if (ret == NXT_OK) {
- (void) nxt_main_start_router_process(task, rt);
+ ret = nxt_main_process_create(task, nxt_router_process);
+ }
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ nxt_exiting = 1;
+
+ nxt_runtime_quit(task, 1);
}
}
@@ -1544,362 +1425,3 @@ nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
msg->port_msg.stream, 0, NULL);
}
}
-
-
-static nxt_int_t
-nxt_init_isolation(nxt_task_t *task, nxt_conf_value_t *isolation,
- nxt_process_init_t *init)
-{
-#if (NXT_HAVE_CLONE)
- nxt_int_t ret;
- nxt_conf_value_t *obj;
-
- static nxt_str_t nsname = nxt_string("namespaces");
-
- obj = nxt_conf_get_object_member(isolation, &nsname, NULL);
- if (obj != NULL) {
- ret = nxt_init_clone_flags(task, obj, init);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
- }
-#endif
-
-#if (NXT_HAVE_CLONE_NEWUSER)
- ret = nxt_init_isolation_creds(task, isolation, init);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
-#endif
-
- return NXT_OK;
-}
-
-
-#if (NXT_HAVE_CLONE_NEWUSER)
-
-static nxt_int_t
-nxt_init_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation,
- nxt_process_init_t *init)
-{
- nxt_int_t ret;
- nxt_clone_t *clone;
- nxt_conf_value_t *array;
-
- static nxt_str_t uidname = nxt_string("uidmap");
- static nxt_str_t gidname = nxt_string("gidmap");
-
- clone = &init->isolation.clone;
-
- array = nxt_conf_get_object_member(isolation, &uidname, NULL);
- if (array != NULL) {
- ret = nxt_init_isolation_credential_map(task, init->mem_pool, array,
- &clone->uidmap);
-
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
- }
-
- array = nxt_conf_get_object_member(isolation, &gidname, NULL);
- if (array != NULL) {
- ret = nxt_init_isolation_credential_map(task, init->mem_pool, array,
- &clone->gidmap);
-
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
- }
-
- return NXT_OK;
-}
-
-
-static nxt_int_t
-nxt_init_vldt_isolation_creds(nxt_task_t *task, nxt_process_init_t *init)
-{
- nxt_int_t ret;
- nxt_clone_t *clone;
-
- clone = &init->isolation.clone;
-
- if (clone->uidmap.size == 0 && clone->gidmap.size == 0) {
- return NXT_OK;
- }
-
- if (!NXT_CLONE_USER(clone->flags)) {
- if (nxt_slow_path(clone->uidmap.size > 0)) {
- nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but "
- "\"isolation.namespaces.credential\" is false or unset");
-
- return NXT_ERROR;
- }
-
- if (nxt_slow_path(clone->gidmap.size > 0)) {
- nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but "
- "\"isolation.namespaces.credential\" is false or unset");
-
- return NXT_ERROR;
- }
-
- return NXT_OK;
- }
-
- ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap,
- init->user_cred);
-
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
-
- return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap,
- init->user_cred);
-}
-
-
-static nxt_int_t
-nxt_init_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mem_pool,
- nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map)
-{
- nxt_int_t ret;
- nxt_uint_t i;
- nxt_conf_value_t *obj;
-
- static nxt_conf_map_t nxt_clone_map_entry_conf[] = {
- {
- nxt_string("container"),
- NXT_CONF_MAP_INT,
- offsetof(nxt_clone_map_entry_t, container),
- },
-
- {
- nxt_string("host"),
- NXT_CONF_MAP_INT,
- offsetof(nxt_clone_map_entry_t, host),
- },
-
- {
- nxt_string("size"),
- NXT_CONF_MAP_INT,
- offsetof(nxt_clone_map_entry_t, size),
- },
- };
-
- map->size = nxt_conf_array_elements_count(map_array);
-
- if (map->size == 0) {
- return NXT_OK;
- }
-
- map->map = nxt_mp_alloc(mem_pool,
- map->size * sizeof(nxt_clone_map_entry_t));
- if (nxt_slow_path(map->map == NULL)) {
- return NXT_ERROR;
- }
-
- for (i = 0; i < map->size; i++) {
- obj = nxt_conf_get_array_element(map_array, i);
-
- ret = nxt_conf_map_object(mem_pool, obj, nxt_clone_map_entry_conf,
- nxt_nitems(nxt_clone_map_entry_conf),
- map->map + i);
- if (nxt_slow_path(ret != NXT_OK)) {
- nxt_alert(task, "clone map entry map error");
- return NXT_ERROR;
- }
- }
-
- return NXT_OK;
-}
-
-#endif
-
-#if (NXT_HAVE_CLONE)
-
-static nxt_int_t
-nxt_init_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces,
- nxt_process_init_t *init)
-{
- uint32_t index;
- nxt_str_t name;
- nxt_int_t flag;
- nxt_conf_value_t *value;
-
- index = 0;
-
- for ( ;; ) {
- value = nxt_conf_next_object_member(namespaces, &name, &index);
-
- if (value == NULL) {
- break;
- }
-
- flag = 0;
-
-#if (NXT_HAVE_CLONE_NEWUSER)
- if (nxt_str_eq(&name, "credential", 10)) {
- flag = CLONE_NEWUSER;
- }
-#endif
-
-#if (NXT_HAVE_CLONE_NEWPID)
- if (nxt_str_eq(&name, "pid", 3)) {
- flag = CLONE_NEWPID;
- }
-#endif
-
-#if (NXT_HAVE_CLONE_NEWNET)
- if (nxt_str_eq(&name, "network", 7)) {
- flag = CLONE_NEWNET;
- }
-#endif
-
-#if (NXT_HAVE_CLONE_NEWUTS)
- if (nxt_str_eq(&name, "uname", 5)) {
- flag = CLONE_NEWUTS;
- }
-#endif
-
-#if (NXT_HAVE_CLONE_NEWNS)
- if (nxt_str_eq(&name, "mount", 5)) {
- flag = CLONE_NEWNS;
- }
-#endif
-
-#if (NXT_HAVE_CLONE_NEWCGROUP)
- if (nxt_str_eq(&name, "cgroup", 6)) {
- flag = CLONE_NEWCGROUP;
- }
-#endif
-
- if (!flag) {
- nxt_alert(task, "unknown namespace flag: \"%V\"", &name);
- return NXT_ERROR;
- }
-
- if (nxt_conf_get_boolean(value)) {
- init->isolation.clone.flags |= flag;
- }
- }
-
- return NXT_OK;
-}
-
-#endif
-
-
-static nxt_process_init_t *
-nxt_process_init_create(nxt_task_t *task, nxt_process_type_t type,
- const nxt_str_t *name)
-{
- nxt_mp_t *mp;
- nxt_int_t ret;
- nxt_runtime_t *rt;
- nxt_process_init_t *init;
-
- mp = nxt_mp_create(1024, 128, 256, 32);
- if (nxt_slow_path(mp == NULL)) {
- return NULL;
- }
-
- init = nxt_mp_zalloc(mp, sizeof(nxt_process_init_t));
- if (nxt_slow_path(init == NULL)) {
- goto fail;
- }
-
- init->mem_pool = mp;
-
- ret = nxt_process_init_name_set(init, type, name);
- if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
- }
-
- rt = task->thread->runtime;
-
- init->type = type;
- init->start = nxt_process_starts[type];
- init->port_handlers = nxt_process_port_handlers[type];
- init->signals = nxt_worker_process_signals;
- init->user_cred = &rt->user_cred;
- init->data = &rt;
-
- return init;
-
-fail:
-
- nxt_mp_destroy(mp);
-
- return NULL;
-}
-
-
-static nxt_int_t
-nxt_process_init_name_set(nxt_process_init_t *init, nxt_process_type_t type,
- const nxt_str_t *name)
-{
- u_char *str, *end;
- size_t size;
- const char *fmt;
-
- size = name->length + 1;
-
- if (type == NXT_PROCESS_WORKER) {
- size += nxt_length("\"\" application");
- fmt = "\"%V\" application%Z";
-
- } else {
- fmt = "%V%Z";
- }
-
- str = nxt_mp_alloc(init->mem_pool, size);
- if (nxt_slow_path(str == NULL)) {
- return NXT_ERROR;
- }
-
- end = str + size;
-
- nxt_sprintf(str, end, fmt, name);
-
- init->name = (char *) str;
-
- return NXT_OK;
-}
-
-
-static nxt_int_t
-nxt_process_init_creds_set(nxt_task_t *task, nxt_process_init_t *init,
- nxt_str_t *user, nxt_str_t *group)
-{
- char *str;
-
- init->user_cred = nxt_mp_zalloc(init->mem_pool, sizeof(nxt_credential_t));
-
- if (nxt_slow_path(init->user_cred == NULL)) {
- return NXT_ERROR;
- }
-
- str = nxt_mp_zalloc(init->mem_pool, user->length + 1);
- if (nxt_slow_path(str == NULL)) {
- return NXT_ERROR;
- }
-
- nxt_memcpy(str, user->start, user->length);
- str[user->length] = '\0';
-
- init->user_cred->user = str;
-
- if (group->start != NULL) {
- str = nxt_mp_zalloc(init->mem_pool, group->length + 1);
- if (nxt_slow_path(str == NULL)) {
- return NXT_ERROR;
- }
-
- nxt_memcpy(str, group->start, group->length);
- str[group->length] = '\0';
-
- } else {
- str = NULL;
- }
-
- return nxt_credential_get(task, init->mem_pool, init->user_cred, str);
-}
diff --git a/src/nxt_main_process.h b/src/nxt_main_process.h
index b0570a84..f9c974d8 100644
--- a/src/nxt_main_process.h
+++ b/src/nxt_main_process.h
@@ -19,26 +19,17 @@ typedef enum {
} nxt_socket_error_t;
-typedef struct {
- nxt_str_t conf;
-#if (NXT_TLS)
- nxt_array_t *certs;
-#endif
-} nxt_controller_init_t;
-
-
nxt_int_t nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task,
nxt_runtime_t *runtime);
-void nxt_main_stop_all_processes(nxt_task_t *task, nxt_runtime_t *runtime);
-nxt_int_t nxt_controller_start(nxt_task_t *task, void *data);
-nxt_int_t nxt_router_start(nxt_task_t *task, void *data);
-nxt_int_t nxt_discovery_start(nxt_task_t *task, void *data);
-nxt_int_t nxt_app_start(nxt_task_t *task, void *data);
+NXT_EXPORT extern const nxt_process_init_t nxt_discovery_process;
+NXT_EXPORT extern const nxt_process_init_t nxt_controller_process;
+NXT_EXPORT extern const nxt_process_init_t nxt_router_process;
+NXT_EXPORT extern const nxt_process_init_t nxt_app_process;
extern const nxt_sig_event_t nxt_main_process_signals[];
-extern const nxt_sig_event_t nxt_worker_process_signals[];
+extern const nxt_sig_event_t nxt_process_signals[];
#endif /* _NXT_MAIN_PROCESS_H_INCLUDED_ */
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index f5053652..7ae8484d 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -29,8 +29,28 @@
#define NXT_PHP7 1
#endif
+/* PHP 8 */
+#ifndef TSRMLS_CC
+#define TSRMLS_CC
+#define TSRMLS_DC
+#define TSRMLS_D void
+#define TSRMLS_C
+#endif
+
+
+typedef struct {
+ nxt_str_t root;
+ nxt_str_t index;
+ nxt_str_t script_name;
+ nxt_str_t script_dirname;
+ nxt_str_t script_filename;
+} nxt_php_target_t;
+
+
typedef struct {
char *cookie;
+ nxt_str_t *root;
+ nxt_str_t *index;
nxt_str_t path_info;
nxt_str_t script_name;
nxt_str_t script_filename;
@@ -52,27 +72,28 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS);
#endif
-static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data);
+static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
+ nxt_conf_value_t *conf);
+static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
+ int type);
+static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
+ int type);
+static void nxt_php_disable(nxt_task_t *task, const char *type,
+ nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
+static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir);
static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
-static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir);
nxt_inline u_char *nxt_realpath(const void *c);
-nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req,
- const nxt_str_t *dirname);
-static void nxt_php_script_request_handler(nxt_unit_request_info_t *req);
-static void nxt_php_path_request_handler(nxt_unit_request_info_t *req);
-static nxt_int_t nxt_php_request_init(nxt_php_run_ctx_t *ctx,
+static void nxt_php_request_handler(nxt_unit_request_info_t *req);
+static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx,
nxt_unit_request_t *r);
+static void nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r);
+nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir);
static int nxt_php_startup(sapi_module_struct *sapi_module);
-static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
- int type);
-static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
- int type);
-static void nxt_php_disable(nxt_task_t *task, const char *type,
- nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
static void *nxt_php_hash_str_find_ptr(const HashTable *ht,
const nxt_str_t *str);
@@ -210,13 +231,6 @@ static sapi_module_struct nxt_php_sapi_module =
};
-static nxt_str_t nxt_php_root;
-static nxt_str_t nxt_php_script_name;
-static nxt_str_t nxt_php_script_dirname;
-static nxt_str_t nxt_php_script_filename;
-static nxt_str_t nxt_php_index = nxt_string("index.php");
-
-
static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG,
};
@@ -228,10 +242,15 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
nxt_string("php"),
PHP_VERSION,
NULL,
- nxt_php_init,
+ 0,
+ NULL,
+ nxt_php_start,
};
+static nxt_php_target_t *nxt_php_targets;
+static nxt_int_t nxt_php_last_target = -1;
+
static nxt_task_t *nxt_php_task;
#if defined(ZTS) && PHP_VERSION_ID < 70400
static void ***tsrm_ls;
@@ -239,19 +258,20 @@ static void ***tsrm_ls;
static nxt_int_t
-nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_php_start(nxt_task_t *task, nxt_process_data_t *data)
{
- u_char *p, *tmp;
- nxt_str_t ini_path;
- nxt_str_t *root, *script_filename, *script_dirname, *script_name;
- nxt_str_t *index;
- nxt_int_t ret;
- nxt_port_t *my_port, *main_port;
- nxt_runtime_t *rt;
- nxt_unit_ctx_t *unit_ctx;
- nxt_unit_init_t php_init;
- nxt_conf_value_t *value;
- nxt_php_app_conf_t *c;
+ u_char *p;
+ uint32_t next;
+ nxt_str_t ini_path, name;
+ nxt_int_t ret;
+ nxt_uint_t n;
+ nxt_port_t *my_port, *main_port;
+ nxt_runtime_t *rt;
+ nxt_unit_ctx_t *unit_ctx;
+ nxt_unit_init_t php_init;
+ nxt_conf_value_t *value;
+ nxt_php_app_conf_t *c;
+ nxt_common_app_conf_t *conf;
static nxt_str_t file_str = nxt_string("file");
static nxt_str_t user_str = nxt_string("user");
@@ -259,107 +279,36 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_php_task = task;
+ conf = data->app;
c = &conf->u.php;
- if (c->root == NULL) {
- nxt_alert(task, "php root is empty");
- return NXT_ERROR;
- }
-
- root = &nxt_php_root;
- script_filename = &nxt_php_script_filename;
- script_dirname = &nxt_php_script_dirname;
- script_name = &nxt_php_script_name;
- index = &nxt_php_index;
+ n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1;
- root->start = nxt_realpath(c->root);
- if (nxt_slow_path(root->start == NULL)) {
- nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno);
+ nxt_php_targets = nxt_zalloc(sizeof(nxt_php_target_t) * n);
+ if (nxt_slow_path(nxt_php_targets == NULL)) {
return NXT_ERROR;
}
- root->length = nxt_strlen(root->start);
-
- nxt_php_str_trim_trail(root, '/');
-
- if (c->script.length > 0) {
- nxt_php_str_trim_lead(&c->script, '/');
-
- tmp = nxt_malloc(root->length + 1 + c->script.length + 1);
- if (nxt_slow_path(tmp == NULL)) {
- return NXT_ERROR;
- }
-
- p = tmp;
-
- p = nxt_cpymem(p, root->start, root->length);
- *p++ = '/';
-
- p = nxt_cpymem(p, c->script.start, c->script.length);
- *p = '\0';
-
- script_filename->start = nxt_realpath(tmp);
- if (nxt_slow_path(script_filename->start == NULL)) {
- nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno);
- return NXT_ERROR;
- }
-
- nxt_free(tmp);
-
- script_filename->length = nxt_strlen(script_filename->start);
-
- if (!nxt_str_start(script_filename, root->start, root->length)) {
- nxt_alert(task, "script is not under php root");
- return NXT_ERROR;
- }
+ if (c->targets != NULL) {
+ next = 0;
- ret = nxt_php_dirname(script_filename, script_dirname);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
+ for (n = 0; /* void */; n++) {
+ value = nxt_conf_next_object_member(c->targets, &name, &next);
+ if (value == NULL) {
+ break;
+ }
- script_name->length = c->script.length + 1;
- script_name->start = nxt_malloc(script_name->length);
- if (nxt_slow_path(script_name->start == NULL)) {
- return NXT_ERROR;
+ ret = nxt_php_set_target(task, &nxt_php_targets[n], value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
}
- script_name->start[0] = '/';
- nxt_memcpy(script_name->start + 1, c->script.start, c->script.length);
-
- nxt_log_error(NXT_LOG_INFO, task->log,
- "(ABS_MODE) php script \"%V\" root: \"%V\"",
- script_name, root);
-
} else {
- nxt_log_error(NXT_LOG_INFO, task->log,
- "(non ABS_MODE) php root: \"%V\"", root);
- }
-
- if (c->index.length > 0) {
- index->length = c->index.length;
- index->start = nxt_malloc(index->length);
- if (nxt_slow_path(index->start == NULL)) {
- return NXT_ERROR;
- }
-
- nxt_memcpy(index->start, c->index.start, c->index.length);
- }
-
- nxt_memzero(&php_init, sizeof(nxt_unit_init_t));
-
- if (nxt_php_script_filename.start != NULL) {
- if (nxt_slow_path(chdir((char *) script_dirname->start) != 0)) {
- nxt_alert(task, "failed to chdir(%V) %E", script_dirname,
- nxt_errno);
-
+ ret = nxt_php_set_target(task, &nxt_php_targets[0], conf->self);
+ if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
-
- php_init.callbacks.request_handler = nxt_php_script_request_handler;
-
- } else {
- php_init.callbacks.request_handler = nxt_php_path_request_handler;
}
#ifdef ZTS
@@ -430,13 +379,17 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
return NXT_ERROR;
}
+ nxt_memzero(&php_init, sizeof(nxt_unit_init_t));
+
+ php_init.callbacks.request_handler = nxt_php_request_handler;
+
php_init.ready_port.id.pid = main_port->pid;
php_init.ready_port.id.id = main_port->id;
php_init.ready_port.out_fd = main_port->pair[1];
nxt_fd_blocking(task, main_port->pair[1]);
- php_init.ready_stream = my_port->process->init->stream;
+ php_init.ready_stream = my_port->process->stream;
php_init.read_port.id.pid = my_port->pid;
php_init.read_port.id.id = my_port->id;
@@ -462,6 +415,125 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
}
+static nxt_int_t
+nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
+ nxt_conf_value_t *conf)
+{
+ u_char *tmp, *p;
+ nxt_str_t str;
+ nxt_int_t ret;
+ nxt_conf_value_t *value;
+
+ static nxt_str_t root_str = nxt_string("root");
+ static nxt_str_t script_str = nxt_string("script");
+ static nxt_str_t index_str = nxt_string("index");
+
+ value = nxt_conf_get_object_member(conf, &root_str, NULL);
+
+ if (value == NULL) {
+ nxt_alert(task, "no php root specified");
+ return NXT_ERROR;
+ }
+
+ nxt_conf_get_string(value, &str);
+
+ tmp = nxt_malloc(str.length + 1);
+ if (nxt_slow_path(tmp == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = tmp;
+
+ p = nxt_cpymem(p, str.start, str.length);
+ *p = '\0';
+
+ p = nxt_realpath(tmp);
+ if (nxt_slow_path(p == NULL)) {
+ nxt_alert(task, "root realpath(%s) failed %E", tmp, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ nxt_free(tmp);
+
+ target->root.length = nxt_strlen(p);
+ target->root.start = p;
+
+ nxt_php_str_trim_trail(&target->root, '/');
+
+ value = nxt_conf_get_object_member(conf, &script_str, NULL);
+
+ if (value != NULL) {
+ nxt_conf_get_string(value, &str);
+
+ nxt_php_str_trim_lead(&str, '/');
+
+ tmp = nxt_malloc(target->root.length + 1 + str.length + 1);
+ if (nxt_slow_path(tmp == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = tmp;
+
+ p = nxt_cpymem(p, target->root.start, target->root.length);
+ *p++ = '/';
+
+ p = nxt_cpymem(p, str.start, str.length);
+ *p = '\0';
+
+ p = nxt_realpath(tmp);
+ if (nxt_slow_path(p == NULL)) {
+ nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ nxt_free(tmp);
+
+ target->script_filename.length = nxt_strlen(p);
+ target->script_filename.start = p;
+
+ if (!nxt_str_start(&target->script_filename,
+ target->root.start, target->root.length))
+ {
+ nxt_alert(task, "script is not under php root");
+ return NXT_ERROR;
+ }
+
+ ret = nxt_php_dirname(&target->script_filename,
+ &target->script_dirname);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ target->script_name.length = target->script_filename.length
+ - target->root.length;
+ target->script_name.start = target->script_filename.start
+ + target->root.length;
+
+ } else {
+ value = nxt_conf_get_object_member(conf, &index_str, NULL);
+
+ if (value != NULL) {
+ nxt_conf_get_string(value, &str);
+
+ tmp = nxt_malloc(str.length);
+ if (nxt_slow_path(tmp == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(tmp, str.start, str.length);
+
+ target->index.length = str.length;
+ target->index.start = tmp;
+
+ } else {
+ nxt_str_set(&target->index, "index.php");
+ }
+ }
+
+ return NXT_OK;
+}
+
+
static void
nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
{
@@ -686,56 +758,44 @@ nxt_realpath(const void *c)
static void
-nxt_php_script_request_handler(nxt_unit_request_info_t *req)
+nxt_php_request_handler(nxt_unit_request_info_t *req)
{
- zend_file_handle file_handle;
- nxt_php_run_ctx_t ctx;
+ nxt_php_target_t *target;
+ nxt_php_run_ctx_t ctx;
+ nxt_unit_request_t *r;
+
+ r = req->request;
+ target = &nxt_php_targets[r->app_target];
nxt_memzero(&ctx, sizeof(ctx));
ctx.req = req;
- ctx.script_filename = nxt_php_script_filename;
- ctx.script_dirname = nxt_php_script_dirname;
- ctx.script_name = nxt_php_script_name;
+ ctx.root = &target->root;
+ ctx.index = &target->index;
- nxt_memzero(&file_handle, sizeof(file_handle));
-
- file_handle.type = ZEND_HANDLE_FILENAME;
- file_handle.filename = (char *) ctx.script_filename.start;
-
- if (nxt_slow_path(nxt_php_request_init(&ctx, req->request) != NXT_OK)) {
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ if (target->script_filename.length == 0) {
+ nxt_php_dynamic_request(&ctx, r);
return;
}
- php_execute_script(&file_handle TSRMLS_CC);
+ ctx.script_filename = target->script_filename;
+ ctx.script_dirname = target->script_dirname;
+ ctx.script_name = target->script_name;
- if (ctx.chdir) {
- nxt_php_vcwd_chdir(ctx.req, &nxt_php_script_dirname);
- }
+ ctx.chdir = (r->app_target != nxt_php_last_target);
- php_request_shutdown(NULL);
+ nxt_php_execute(&ctx, r);
- nxt_unit_request_done(req, NXT_UNIT_OK);
+ nxt_php_last_target = ctx.chdir ? -1 : r->app_target;
}
static void
-nxt_php_path_request_handler(nxt_unit_request_info_t *req)
+nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
{
- u_char *p;
- nxt_str_t path, script_name;
- nxt_int_t ret;
- zend_file_handle file_handle;
- nxt_php_run_ctx_t run_ctx, *ctx;
- nxt_unit_request_t *r;
-
- nxt_memzero(&run_ctx, sizeof(run_ctx));
-
- ctx = &run_ctx;
- ctx->req = req;
-
- r = req->request;
+ u_char *p;
+ nxt_str_t path, script_name;
+ nxt_int_t ret;
path.length = r->path_length;
path.start = nxt_unit_sptr_get(&r->path);
@@ -750,26 +810,26 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req)
ctx->path_info.length = r->path_length - path.length;
} else if (path.start[path.length - 1] == '/') {
- script_name = nxt_php_index;
+ script_name = *ctx->index;
} else {
if (nxt_slow_path(path.length < 4
|| nxt_memcmp(path.start + (path.length - 4),
".php", 4)))
{
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
return;
}
}
- ctx->script_filename.length = nxt_php_root.length
+ ctx->script_filename.length = ctx->root->length
+ path.length
+ script_name.length;
p = nxt_malloc(ctx->script_filename.length + 1);
if (nxt_slow_path(p == NULL)) {
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
return;
}
@@ -777,9 +837,9 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req)
ctx->script_filename.start = p;
ctx->script_name.length = path.length + script_name.length;
- ctx->script_name.start = p + nxt_php_root.length;
+ ctx->script_name.start = p + ctx->root->length;
- p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length);
+ p = nxt_cpymem(p, ctx->root->start, ctx->root->length);
p = nxt_cpymem(p, path.start, path.length);
if (script_name.length > 0) {
@@ -788,50 +848,33 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req)
*p = '\0';
- nxt_memzero(&file_handle, sizeof(file_handle));
-
- file_handle.type = ZEND_HANDLE_FILENAME;
- file_handle.filename = (char *) ctx->script_filename.start;
+ ctx->chdir = 1;
ret = nxt_php_dirname(&ctx->script_filename, &ctx->script_dirname);
if (nxt_slow_path(ret != NXT_OK)) {
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
nxt_free(ctx->script_filename.start);
return;
}
- if (nxt_slow_path(nxt_php_request_init(ctx, req->request) != NXT_OK)) {
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
- goto cleanup;
- }
-
- nxt_php_vcwd_chdir(ctx->req, &ctx->script_dirname);
-
- php_execute_script(&file_handle TSRMLS_CC);
-
- php_request_shutdown(NULL);
-
- nxt_unit_request_done(req, NXT_UNIT_OK);
-
-cleanup:
+ nxt_php_execute(ctx, r);
nxt_free(ctx->script_filename.start);
nxt_free(ctx->script_dirname.start);
-}
-
-static int
-nxt_php_startup(sapi_module_struct *sapi_module)
-{
- return php_module_startup(sapi_module, &nxt_php_unit_module, 1);
+ nxt_php_last_target = -1;
}
-static nxt_int_t
-nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
+static void
+nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
{
nxt_unit_field_t *f;
+ zend_file_handle file_handle;
+
+ nxt_unit_req_debug(ctx->req, "PHP execute script %s",
+ ctx->script_filename.start);
SG(server_context) = ctx;
SG(options) |= SAPI_OPTION_NO_CHDIR;
@@ -860,20 +903,6 @@ nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
SG(request_info).path_translated = NULL;
- nxt_unit_req_debug(ctx->req, "handle.filename = '%s'",
- ctx->script_filename.start);
-
- if (nxt_php_script_filename.start != NULL) {
- nxt_unit_req_debug(ctx->req, "run script %.*s in absolute mode",
- (int) nxt_php_script_filename.length,
- (char *) nxt_php_script_filename.start);
-
- } else {
- nxt_unit_req_debug(ctx->req, "run script %.*s",
- (int) ctx->script_filename.length,
- (char *) ctx->script_filename.start);
- }
-
#ifdef NXT_PHP7
if (nxt_slow_path(php_request_startup() == FAILURE)) {
#else
@@ -881,23 +910,45 @@ nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
#endif
nxt_unit_req_debug(ctx->req, "php_request_startup() failed");
- return NXT_ERROR;
+ nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
+ return;
}
- return NXT_OK;
+ if (ctx->chdir) {
+ ctx->chdir = 0;
+ nxt_php_vcwd_chdir(ctx->req, ctx->script_dirname.start);
+ }
+
+ nxt_memzero(&file_handle, sizeof(file_handle));
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = (char *) ctx->script_filename.start;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+
+ php_request_shutdown(NULL);
+
+ nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
}
nxt_inline void
-nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, const nxt_str_t *dir)
+nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir)
{
- if (nxt_slow_path(VCWD_CHDIR((char *) dir->start) != 0)) {
+ if (nxt_slow_path(VCWD_CHDIR((char *) dir) != 0)) {
nxt_unit_req_alert(req, "VCWD_CHDIR(%s) failed (%d: %s)",
- dir->start, errno, strerror(errno));
+ dir, errno, strerror(errno));
}
}
+static int
+nxt_php_startup(sapi_module_struct *sapi_module)
+{
+ return php_module_startup(sapi_module, &nxt_php_unit_module, 1);
+}
+
+
#ifdef NXT_PHP7
static size_t
nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC)
@@ -1061,19 +1112,16 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
* available.
*/
- if (nxt_php_script_name.start != NULL) {
- /* ABS_MODE */
- nxt_php_set_str(req, "PHP_SELF", &nxt_php_script_name,
- track_vars_array TSRMLS_CC);
-
- } else {
+ if (ctx->path_info.length != 0) {
nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
track_vars_array TSRMLS_CC);
- }
- if (ctx->path_info.length != 0) {
nxt_php_set_str(req, "PATH_INFO", &ctx->path_info,
track_vars_array TSRMLS_CC);
+
+ } else {
+ nxt_php_set_str(req, "PHP_SELF", &ctx->script_name,
+ track_vars_array TSRMLS_CC);
}
/*
@@ -1100,7 +1148,7 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
* as defined in the server's configuration file.
*/
- nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root,
+ nxt_php_set_str(req, "DOCUMENT_ROOT", ctx->root,
track_vars_array TSRMLS_CC);
nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
diff --git a/src/nxt_port.c b/src/nxt_port.c
index 70cf33e6..7232c465 100644
--- a/src/nxt_port.c
+++ b/src/nxt_port.c
@@ -296,7 +296,9 @@ nxt_port_process_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
return;
}
- process->ready = 1;
+ nxt_assert(process->state != NXT_PROCESS_STATE_READY);
+
+ process->state = NXT_PROCESS_STATE_READY;
nxt_assert(!nxt_queue_is_empty(&process->ports));
diff --git a/src/nxt_port.h b/src/nxt_port.h
index c6f15238..0e8707f3 100644
--- a/src/nxt_port.h
+++ b/src/nxt_port.h
@@ -14,7 +14,7 @@ struct nxt_port_handlers_s {
nxt_port_handler_t rpc_error;
/* Main process RPC requests. */
- nxt_port_handler_t start_worker;
+ nxt_port_handler_t start_process;
nxt_port_handler_t socket;
nxt_port_handler_t modules;
nxt_port_handler_t conf_store;
@@ -27,7 +27,8 @@ struct nxt_port_handlers_s {
nxt_port_handler_t new_port;
nxt_port_handler_t mmap;
- /* New process ready. */
+ /* New process */
+ nxt_port_handler_t process_created;
nxt_port_handler_t process_ready;
/* Process exit/crash notification. */
@@ -53,76 +54,76 @@ struct nxt_port_handlers_s {
#define nxt_port_handler_idx(name) \
( offsetof(nxt_port_handlers_t, name) / sizeof(nxt_port_handler_t) )
+#define nxt_msg_last(handler) \
+ (handler | NXT_PORT_MSG_LAST)
typedef enum {
- NXT_PORT_MSG_LAST = 0x100,
- NXT_PORT_MSG_CLOSE_FD = 0x200,
- NXT_PORT_MSG_SYNC = 0x400,
-
- NXT_PORT_MSG_MASK = 0xFF,
-
- _NXT_PORT_MSG_RPC_READY = nxt_port_handler_idx(rpc_ready),
- _NXT_PORT_MSG_RPC_ERROR = nxt_port_handler_idx(rpc_error),
-
- _NXT_PORT_MSG_START_WORKER = nxt_port_handler_idx(start_worker),
- _NXT_PORT_MSG_SOCKET = nxt_port_handler_idx(socket),
- _NXT_PORT_MSG_MODULES = nxt_port_handler_idx(modules),
- _NXT_PORT_MSG_CONF_STORE = nxt_port_handler_idx(conf_store),
- _NXT_PORT_MSG_CERT_GET = nxt_port_handler_idx(cert_get),
- _NXT_PORT_MSG_CERT_DELETE = nxt_port_handler_idx(cert_delete),
- _NXT_PORT_MSG_ACCESS_LOG = nxt_port_handler_idx(access_log),
-
- _NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file),
- _NXT_PORT_MSG_NEW_PORT = nxt_port_handler_idx(new_port),
- _NXT_PORT_MSG_MMAP = nxt_port_handler_idx(mmap),
-
- _NXT_PORT_MSG_PROCESS_READY = nxt_port_handler_idx(process_ready),
- _NXT_PORT_MSG_REMOVE_PID = nxt_port_handler_idx(remove_pid),
- _NXT_PORT_MSG_QUIT = nxt_port_handler_idx(quit),
-
- _NXT_PORT_MSG_REQ_HEADERS = nxt_port_handler_idx(req_headers),
- _NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame),
-
- _NXT_PORT_MSG_DATA = nxt_port_handler_idx(data),
-
- _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm),
- _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack),
-
- NXT_PORT_MSG_MAX = sizeof(nxt_port_handlers_t)
- / sizeof(nxt_port_handler_t),
-
- NXT_PORT_MSG_RPC_READY = _NXT_PORT_MSG_RPC_READY,
- NXT_PORT_MSG_RPC_READY_LAST = _NXT_PORT_MSG_RPC_READY | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_RPC_ERROR = _NXT_PORT_MSG_RPC_ERROR | NXT_PORT_MSG_LAST,
-
- NXT_PORT_MSG_START_WORKER = _NXT_PORT_MSG_START_WORKER
- | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_SOCKET = _NXT_PORT_MSG_SOCKET | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_MODULES = _NXT_PORT_MSG_MODULES | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_CONF_STORE = _NXT_PORT_MSG_CONF_STORE | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_CERT_GET = _NXT_PORT_MSG_CERT_GET | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_CERT_DELETE = _NXT_PORT_MSG_CERT_DELETE | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_ACCESS_LOG = _NXT_PORT_MSG_ACCESS_LOG | NXT_PORT_MSG_LAST,
-
- NXT_PORT_MSG_CHANGE_FILE = _NXT_PORT_MSG_CHANGE_FILE | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_NEW_PORT = _NXT_PORT_MSG_NEW_PORT | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_MMAP = _NXT_PORT_MSG_MMAP | NXT_PORT_MSG_LAST
- | NXT_PORT_MSG_CLOSE_FD | NXT_PORT_MSG_SYNC,
-
- NXT_PORT_MSG_PROCESS_READY = _NXT_PORT_MSG_PROCESS_READY
- | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_QUIT = _NXT_PORT_MSG_QUIT | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_REMOVE_PID = _NXT_PORT_MSG_REMOVE_PID | NXT_PORT_MSG_LAST,
-
- NXT_PORT_MSG_REQ_HEADERS = _NXT_PORT_MSG_REQ_HEADERS,
- NXT_PORT_MSG_WEBSOCKET = _NXT_PORT_MSG_WEBSOCKET,
- NXT_PORT_MSG_WEBSOCKET_LAST = _NXT_PORT_MSG_WEBSOCKET | NXT_PORT_MSG_LAST,
-
- NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA,
- NXT_PORT_MSG_DATA_LAST = _NXT_PORT_MSG_DATA | NXT_PORT_MSG_LAST,
-
- NXT_PORT_MSG_OOSM = _NXT_PORT_MSG_OOSM | NXT_PORT_MSG_LAST,
- NXT_PORT_MSG_SHM_ACK = _NXT_PORT_MSG_SHM_ACK | NXT_PORT_MSG_LAST,
+ NXT_PORT_MSG_LAST = 0x100,
+ NXT_PORT_MSG_CLOSE_FD = 0x200,
+ NXT_PORT_MSG_SYNC = 0x400,
+
+ NXT_PORT_MSG_MASK = 0xFF,
+
+ _NXT_PORT_MSG_RPC_READY = nxt_port_handler_idx(rpc_ready),
+ _NXT_PORT_MSG_RPC_ERROR = nxt_port_handler_idx(rpc_error),
+
+ _NXT_PORT_MSG_START_PROCESS = nxt_port_handler_idx(start_process),
+ _NXT_PORT_MSG_SOCKET = nxt_port_handler_idx(socket),
+ _NXT_PORT_MSG_MODULES = nxt_port_handler_idx(modules),
+ _NXT_PORT_MSG_CONF_STORE = nxt_port_handler_idx(conf_store),
+ _NXT_PORT_MSG_CERT_GET = nxt_port_handler_idx(cert_get),
+ _NXT_PORT_MSG_CERT_DELETE = nxt_port_handler_idx(cert_delete),
+ _NXT_PORT_MSG_ACCESS_LOG = nxt_port_handler_idx(access_log),
+
+ _NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file),
+ _NXT_PORT_MSG_NEW_PORT = nxt_port_handler_idx(new_port),
+ _NXT_PORT_MSG_MMAP = nxt_port_handler_idx(mmap),
+
+ _NXT_PORT_MSG_PROCESS_CREATED = nxt_port_handler_idx(process_created),
+ _NXT_PORT_MSG_PROCESS_READY = nxt_port_handler_idx(process_ready),
+ _NXT_PORT_MSG_REMOVE_PID = nxt_port_handler_idx(remove_pid),
+ _NXT_PORT_MSG_QUIT = nxt_port_handler_idx(quit),
+
+ _NXT_PORT_MSG_REQ_HEADERS = nxt_port_handler_idx(req_headers),
+ _NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame),
+
+ _NXT_PORT_MSG_DATA = nxt_port_handler_idx(data),
+
+ _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm),
+ _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack),
+
+ NXT_PORT_MSG_MAX = sizeof(nxt_port_handlers_t)
+ / sizeof(nxt_port_handler_t),
+
+ NXT_PORT_MSG_RPC_READY = _NXT_PORT_MSG_RPC_READY,
+ NXT_PORT_MSG_RPC_READY_LAST = nxt_msg_last(_NXT_PORT_MSG_RPC_READY),
+ NXT_PORT_MSG_RPC_ERROR = nxt_msg_last(_NXT_PORT_MSG_RPC_ERROR),
+ NXT_PORT_MSG_START_PROCESS = nxt_msg_last(_NXT_PORT_MSG_START_PROCESS),
+ NXT_PORT_MSG_SOCKET = nxt_msg_last(_NXT_PORT_MSG_SOCKET),
+ NXT_PORT_MSG_MODULES = nxt_msg_last(_NXT_PORT_MSG_MODULES),
+ NXT_PORT_MSG_CONF_STORE = nxt_msg_last(_NXT_PORT_MSG_CONF_STORE),
+ NXT_PORT_MSG_CERT_GET = nxt_msg_last(_NXT_PORT_MSG_CERT_GET),
+ NXT_PORT_MSG_CERT_DELETE = nxt_msg_last(_NXT_PORT_MSG_CERT_DELETE),
+ NXT_PORT_MSG_ACCESS_LOG = nxt_msg_last(_NXT_PORT_MSG_ACCESS_LOG),
+ NXT_PORT_MSG_CHANGE_FILE = nxt_msg_last(_NXT_PORT_MSG_CHANGE_FILE),
+ NXT_PORT_MSG_NEW_PORT = nxt_msg_last(_NXT_PORT_MSG_NEW_PORT),
+ NXT_PORT_MSG_MMAP = nxt_msg_last(_NXT_PORT_MSG_MMAP)
+ | NXT_PORT_MSG_CLOSE_FD | NXT_PORT_MSG_SYNC,
+
+ NXT_PORT_MSG_PROCESS_CREATED = nxt_msg_last(_NXT_PORT_MSG_PROCESS_CREATED),
+ NXT_PORT_MSG_PROCESS_READY = nxt_msg_last(_NXT_PORT_MSG_PROCESS_READY),
+ NXT_PORT_MSG_QUIT = nxt_msg_last(_NXT_PORT_MSG_QUIT),
+ NXT_PORT_MSG_REMOVE_PID = nxt_msg_last(_NXT_PORT_MSG_REMOVE_PID),
+
+ NXT_PORT_MSG_REQ_HEADERS = _NXT_PORT_MSG_REQ_HEADERS,
+ NXT_PORT_MSG_WEBSOCKET = _NXT_PORT_MSG_WEBSOCKET,
+ NXT_PORT_MSG_WEBSOCKET_LAST = nxt_msg_last(_NXT_PORT_MSG_WEBSOCKET),
+
+ NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA,
+ NXT_PORT_MSG_DATA_LAST = nxt_msg_last(_NXT_PORT_MSG_DATA),
+
+ NXT_PORT_MSG_OOSM = nxt_msg_last(_NXT_PORT_MSG_OOSM),
+ NXT_PORT_MSG_SHM_ACK = nxt_msg_last(_NXT_PORT_MSG_SHM_ACK),
} nxt_port_msg_type_t;
diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c
index 33d3777e..f4d2125c 100644
--- a/src/nxt_port_memory.c
+++ b/src/nxt_port_memory.c
@@ -174,7 +174,7 @@ complete_buf:
if (process != NULL && !nxt_queue_is_empty(&process->ports)) {
port = nxt_process_port_first(process);
- if (port->type == NXT_PROCESS_WORKER) {
+ if (port->type == NXT_PROCESS_APP) {
(void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK,
-1, 0, 0, NULL);
}
diff --git a/src/nxt_port_rpc.c b/src/nxt_port_rpc.c
index 77e8af45..37f2d902 100644
--- a/src/nxt_port_rpc.c
+++ b/src/nxt_port_rpc.c
@@ -8,7 +8,7 @@
#include <nxt_port_rpc.h>
-static nxt_atomic_t nxt_stream_ident = 1;
+static volatile uint32_t *nxt_stream_ident;
typedef struct nxt_port_rpc_reg_s nxt_port_rpc_reg_t;
@@ -30,6 +30,29 @@ nxt_port_rpc_remove_from_peers(nxt_task_t *task, nxt_port_t *port,
nxt_port_rpc_reg_t *reg);
+nxt_int_t
+nxt_port_rpc_init(void)
+{
+ void *p;
+
+ if (nxt_stream_ident != NULL) {
+ return NXT_OK;
+ }
+
+ p = nxt_mem_mmap(NULL, sizeof(*nxt_stream_ident), PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED, -1, 0);
+
+ if (nxt_slow_path(p == MAP_FAILED)) {
+ return NXT_ERROR;
+ }
+
+ nxt_stream_ident = p;
+ *nxt_stream_ident = 1;
+
+ return NXT_OK;
+}
+
+
static nxt_int_t
nxt_rpc_reg_test(nxt_lvlhsh_query_t *lhq, void *data)
{
@@ -105,8 +128,7 @@ nxt_port_rpc_register_handler_ex(nxt_task_t *task, nxt_port_t *port,
nxt_assert(port->pair[0] != -1);
- stream =
- (uint32_t) nxt_atomic_fetch_add(&nxt_stream_ident, 1) & 0x3FFFFFFF;
+ stream = nxt_atomic_fetch_add(nxt_stream_ident, 1);
reg = nxt_mp_zalloc(port->mem_pool, sizeof(nxt_port_rpc_reg_t) + ex_size);
diff --git a/src/nxt_port_rpc.h b/src/nxt_port_rpc.h
index 8011e474..c07683fb 100644
--- a/src/nxt_port_rpc.h
+++ b/src/nxt_port_rpc.h
@@ -11,6 +11,8 @@
typedef void (*nxt_port_rpc_handler_t)(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
+nxt_int_t nxt_port_rpc_init(void);
+
uint32_t nxt_port_rpc_register_handler(nxt_task_t *task, nxt_port_t *port,
nxt_port_rpc_handler_t ready_handler, nxt_port_rpc_handler_t error_handler,
nxt_pid_t peer, void *data);
diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c
index 9c7da970..4e3eaef6 100644
--- a/src/nxt_port_socket.c
+++ b/src/nxt_port_socket.c
@@ -993,6 +993,12 @@ nxt_port_error_handler(nxt_task_t *task, void *obj, void *data)
nxt_queue_each(msg, &port->messages, nxt_port_send_msg_t, link) {
+ if (msg->fd != -1 && msg->close_fd != 0) {
+ nxt_fd_close(msg->fd);
+
+ msg->fd = -1;
+ }
+
for (b = msg->buf; b != NULL; b = next) {
next = b->next;
b->next = NULL;
diff --git a/src/nxt_process.c b/src/nxt_process.c
index f5959edf..c4c44d14 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -13,9 +13,39 @@
#include <signal.h>
-static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
-static nxt_int_t nxt_process_worker_setup(nxt_task_t *task,
- nxt_process_t *process, int parentfd);
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+#include <sys/prctl.h>
+#endif
+
+#if (NXT_HAVE_PIVOT_ROOT)
+#include <mntent.h>
+#endif
+
+static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process);
+static nxt_int_t nxt_process_child_fixup(nxt_task_t *task,
+ nxt_process_t *process);
+static nxt_int_t nxt_process_send_created(nxt_task_t *task,
+ nxt_process_t *process);
+static nxt_int_t nxt_process_send_ready(nxt_task_t *task,
+ nxt_process_t *process);
+static void nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg,
+ void *data);
+static void nxt_process_created_error(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg, void *data);
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+static nxt_int_t nxt_process_chroot(nxt_task_t *task, const char *path);
+#endif
+
+#if (NXT_HAVE_PIVOT_ROOT)
+static nxt_int_t nxt_process_pivot_root(nxt_task_t *task, const char *rootfs);
+static nxt_int_t nxt_process_private_mount(nxt_task_t *task,
+ const char *rootfs);
+#endif
+
+#if (NXT_HAVE_PIVOT_ROOT)
+static int nxt_pivot_root(const char *new_root, const char *old_root);
+#endif
/* A cached process pid. */
nxt_pid_t nxt_pid;
@@ -47,62 +77,58 @@ nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
static nxt_int_t
-nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd)
+nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process)
{
- pid_t rpid, pid;
- ssize_t n;
- nxt_int_t parent_status;
nxt_process_t *p;
nxt_runtime_t *rt;
nxt_process_init_t *init;
nxt_process_type_t ptype;
- pid = getpid();
- rpid = 0;
- rt = task->thread->runtime;
- init = process->init;
+ init = nxt_process_init(process);
- /* Setup the worker process. */
-
- n = read(parentfd, &rpid, sizeof(rpid));
- if (nxt_slow_path(n == -1 || n != sizeof(rpid))) {
- nxt_alert(task, "failed to read real pid");
- return NXT_ERROR;
- }
-
- if (nxt_slow_path(rpid == 0)) {
- nxt_alert(task, "failed to get real pid from parent");
- return NXT_ERROR;
- }
+ nxt_pid = nxt_getpid();
- nxt_pid = rpid;
+ process->pid = nxt_pid;
/* Clean inherited cached thread tid. */
task->thread->tid = 0;
- process->pid = nxt_pid;
+#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWPID)
+ if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)) {
+ ssize_t pidsz;
+ char procpid[10];
- if (nxt_pid != pid) {
- nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid);
- nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid);
- }
+ nxt_debug(task, "%s isolated pid is %d", process->name, nxt_pid);
- n = read(parentfd, &parent_status, sizeof(parent_status));
- if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) {
- nxt_alert(task, "failed to read parent status");
- return NXT_ERROR;
- }
+ pidsz = readlink("/proc/self", procpid, sizeof(procpid));
+
+ if (nxt_slow_path(pidsz < 0 || pidsz >= (ssize_t) sizeof(procpid))) {
+ nxt_alert(task, "failed to read real pid from /proc/self");
+ return NXT_ERROR;
+ }
+
+ procpid[pidsz] = '\0';
+
+ nxt_pid = (nxt_pid_t) strtol(procpid, NULL, 10);
+
+ nxt_assert(nxt_pid > 0 && nxt_errno != ERANGE);
- if (nxt_slow_path(parent_status != NXT_OK)) {
- return parent_status;
+ process->pid = nxt_pid;
+ task->thread->tid = nxt_pid;
+
+ nxt_debug(task, "%s real pid is %d", process->name, nxt_pid);
}
+#endif
+
ptype = init->type;
nxt_port_reset_next_id();
nxt_event_engine_thread_adopt(task->thread->engine);
+ rt = task->thread->runtime;
+
/* Remove not ready processes. */
nxt_runtime_process_each(rt, p) {
@@ -114,7 +140,7 @@ nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd)
continue;
}
- if (!p->ready) {
+ if (p->state != NXT_PROCESS_STATE_READY) {
nxt_debug(task, "remove not ready process %PI", p->pid);
nxt_process_close_ports(task, p);
@@ -127,12 +153,6 @@ nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd)
} nxt_runtime_process_loop;
- nxt_runtime_process_add(task, process);
-
- nxt_process_start(task, process);
-
- process->ready = 1;
-
return NXT_OK;
}
@@ -140,48 +160,36 @@ nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd)
nxt_pid_t
nxt_process_create(nxt_task_t *task, nxt_process_t *process)
{
- int pipefd[2];
nxt_int_t ret;
nxt_pid_t pid;
- nxt_process_init_t *init;
-
- if (nxt_slow_path(pipe(pipefd) == -1)) {
- nxt_alert(task, "failed to create process pipe for passing rpid");
- return -1;
- }
-
- init = process->init;
#if (NXT_HAVE_CLONE)
- pid = nxt_clone(SIGCHLD | init->isolation.clone.flags);
+ pid = nxt_clone(SIGCHLD | process->isolation.clone.flags);
if (nxt_slow_path(pid < 0)) {
- nxt_alert(task, "clone() failed while creating \"%s\" %E",
- init->name, nxt_errno);
- goto cleanup;
+ nxt_alert(task, "clone() failed for %s %E", process->name, nxt_errno);
+ return pid;
}
#else
pid = fork();
if (nxt_slow_path(pid < 0)) {
- nxt_alert(task, "fork() failed while creating \"%s\" %E",
- init->name, nxt_errno);
- goto cleanup;
+ nxt_alert(task, "fork() failed for %s %E", process->name, nxt_errno);
+ return pid;
}
#endif
if (pid == 0) {
/* Child. */
- if (nxt_slow_path(close(pipefd[1]) == -1)) {
- nxt_alert(task, "failed to close writer pipe fd");
- }
-
- ret = nxt_process_worker_setup(task, process, pipefd[0]);
+ ret = nxt_process_child_fixup(task, process);
if (nxt_slow_path(ret != NXT_OK)) {
- exit(1);
+ nxt_process_quit(task, 1);
+ return -1;
}
- if (nxt_slow_path(close(pipefd[0]) == -1)) {
- nxt_alert(task, "failed to close writer pipe fd");
+ nxt_runtime_process_add(task, process);
+
+ if (nxt_slow_path(nxt_process_setup(task, process) != NXT_OK)) {
+ nxt_process_quit(task, 1);
}
/*
@@ -193,169 +201,686 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process)
/* Parent. */
- /*
- * At this point, the child process is blocked reading the
- * pipe fd to get its real pid (rpid).
- *
- * If anything goes wrong now, we need to terminate the child
- * process by sending a NXT_ERROR in the pipe.
- */
-
#if (NXT_HAVE_CLONE)
- nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid);
+ nxt_debug(task, "clone(%s): %PI", process->name, pid);
#else
- nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid);
+ nxt_debug(task, "fork(%s): %PI", process->name, pid);
#endif
- if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) {
- nxt_alert(task, "failed to write real pid");
- goto fail;
+ process->pid = pid;
+
+ nxt_runtime_process_add(task, process);
+
+ return pid;
+}
+
+
+static nxt_int_t
+nxt_process_setup(nxt_task_t *task, nxt_process_t *process)
+{
+ nxt_int_t ret;
+ nxt_port_t *port, *main_port;
+ nxt_thread_t *thread;
+ nxt_runtime_t *rt;
+ nxt_process_init_t *init;
+ nxt_event_engine_t *engine;
+ const nxt_event_interface_t *interface;
+
+ init = nxt_process_init(process);
+
+ nxt_debug(task, "%s setup", process->name);
+
+ nxt_process_title(task, "unit: %s", process->name);
+
+ thread = task->thread;
+ rt = thread->runtime;
+
+ nxt_random_init(&thread->random);
+
+ rt->type = init->type;
+
+ engine = thread->engine;
+
+ /* Update inherited main process event engine and signals processing. */
+ engine->signals->sigev = init->signals;
+
+ interface = nxt_service_get(rt->services, "engine", rt->engine);
+ if (nxt_slow_path(interface == NULL)) {
+ return NXT_ERROR;
}
-#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
- if (NXT_CLONE_USER(init->isolation.clone.flags)) {
- ret = nxt_clone_credential_map(task, pid, init->user_cred,
- &init->isolation.clone);
+ if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
+ 60000 * 1000000LL);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ main_port = rt->port_by_type[NXT_PROCESS_MAIN];
+
+ nxt_port_read_close(main_port);
+ nxt_port_write_enable(task, main_port);
+
+ port = nxt_process_port_first(process);
+
+ nxt_port_write_close(port);
+
+ nxt_port_enable(task, port, init->port_handlers);
+
+ ret = init->setup(task, process);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ switch (process->state) {
+
+ case NXT_PROCESS_STATE_CREATED:
+ ret = nxt_process_send_created(task, process);
+ break;
+
+ case NXT_PROCESS_STATE_READY:
+ ret = nxt_process_send_ready(task, process);
+
if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
+ break;
}
+
+ ret = init->start(task, &process->data);
+ break;
+
+ default:
+ nxt_assert(0);
}
-#endif
- ret = NXT_OK;
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "%s failed to start", process->name);
+ }
- if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
- nxt_alert(task, "failed to write status");
- goto fail;
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_process_send_created(nxt_task_t *task, nxt_process_t *process)
+{
+ uint32_t stream;
+ nxt_int_t ret;
+ nxt_port_t *my_port, *main_port;
+ nxt_runtime_t *rt;
+
+ nxt_assert(process->state == NXT_PROCESS_STATE_CREATED);
+
+ rt = task->thread->runtime;
+
+ my_port = nxt_process_port_first(process);
+ main_port = rt->port_by_type[NXT_PROCESS_MAIN];
+
+ nxt_assert(my_port != NULL && main_port != NULL);
+
+ stream = nxt_port_rpc_register_handler(task, my_port,
+ nxt_process_created_ok,
+ nxt_process_created_error,
+ main_port->pid, process);
+
+ if (nxt_slow_path(stream == 0)) {
+ return NXT_ERROR;
}
- process->pid = pid;
+ ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_CREATED,
+ -1, stream, my_port->id, NULL);
- nxt_runtime_process_add(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "%s failed to send CREATED message", process->name);
+ nxt_port_rpc_cancel(task, my_port, stream);
+ return NXT_ERROR;
+ }
+
+ nxt_debug(task, "%s created", process->name);
+
+ return NXT_OK;
+}
- goto cleanup;
+
+static void
+nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data)
+{
+ nxt_int_t ret;
+ nxt_process_t *process;
+ nxt_process_init_t *init;
+
+ process = data;
+ init = nxt_process_init(process);
+
+ ret = nxt_process_apply_creds(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
+ nxt_log(task, NXT_LOG_INFO, "%s started", process->name);
+
+ ret = init->start(task, &process->data);
fail:
- ret = NXT_ERROR;
+ nxt_process_quit(task, ret == NXT_OK ? 0 : 1);
+}
+
+
+static void
+nxt_process_created_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
+ void *data)
+{
+ nxt_process_t *process;
+ nxt_process_init_t *init;
+
+ process = data;
+ init = nxt_process_init(process);
+
+ nxt_alert(task, "%s failed to start", init->name);
+
+ nxt_process_quit(task, 1);
+}
+
+
+nxt_int_t
+nxt_process_core_setup(nxt_task_t *task, nxt_process_t *process)
+{
+ nxt_int_t ret;
- if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
- nxt_alert(task, "failed to write status");
+ ret = nxt_process_apply_creds(task, process);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
}
- waitpid(pid, NULL, 0);
+ process->state = NXT_PROCESS_STATE_READY;
- pid = -1;
+ return NXT_OK;
+}
-cleanup:
- if (nxt_slow_path(close(pipefd[0]) != 0)) {
- nxt_alert(task, "failed to close pipe: %E", nxt_errno);
+#if (NXT_HAVE_CLONE_NEWUSER)
+
+nxt_int_t
+nxt_process_vldt_isolation_creds(nxt_task_t *task, nxt_process_t *process)
+{
+ nxt_int_t ret;
+ nxt_clone_t *clone;
+ nxt_credential_t *creds;
+
+ clone = &process->isolation.clone;
+ creds = process->user_cred;
+
+ if (clone->uidmap.size == 0 && clone->gidmap.size == 0) {
+ return NXT_OK;
}
- if (nxt_slow_path(close(pipefd[1]) != 0)) {
- nxt_alert(task, "failed to close pipe: %E", nxt_errno);
+ if (!nxt_is_clone_flag_set(clone->flags, NEWUSER)) {
+ if (nxt_slow_path(clone->uidmap.size > 0)) {
+ nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but "
+ "\"isolation.namespaces.credential\" is false or unset");
+
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(clone->gidmap.size > 0)) {
+ nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but "
+ "\"isolation.namespaces.credential\" is false or unset");
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
}
- return pid;
+ ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap, creds);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap, creds);
}
+#endif
-static void
-nxt_process_start(nxt_task_t *task, nxt_process_t *process)
+
+nxt_int_t
+nxt_process_creds_set(nxt_task_t *task, nxt_process_t *process, nxt_str_t *user,
+ nxt_str_t *group)
{
- nxt_int_t ret, cap_setid;
- nxt_port_t *port, *main_port;
- nxt_thread_t *thread;
- nxt_runtime_t *rt;
- nxt_process_init_t *init;
- nxt_event_engine_t *engine;
- const nxt_event_interface_t *interface;
+ char *str;
- init = process->init;
+ process->user_cred = nxt_mp_zalloc(process->mem_pool,
+ sizeof(nxt_credential_t));
- nxt_log(task, NXT_LOG_INFO, "%s started", init->name);
+ if (nxt_slow_path(process->user_cred == NULL)) {
+ return NXT_ERROR;
+ }
- nxt_process_title(task, "unit: %s", init->name);
+ str = nxt_mp_zalloc(process->mem_pool, user->length + 1);
+ if (nxt_slow_path(str == NULL)) {
+ return NXT_ERROR;
+ }
- thread = task->thread;
- rt = thread->runtime;
+ nxt_memcpy(str, user->start, user->length);
+ str[user->length] = '\0';
- nxt_random_init(&thread->random);
+ process->user_cred->user = str;
+
+ if (group->start != NULL) {
+ str = nxt_mp_zalloc(process->mem_pool, group->length + 1);
+ if (nxt_slow_path(str == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(str, group->start, group->length);
+ str[group->length] = '\0';
+
+ } else {
+ str = NULL;
+ }
+
+ return nxt_credential_get(task, process->mem_pool, process->user_cred, str);
+}
+
+
+nxt_int_t
+nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process)
+{
+ nxt_int_t ret, cap_setid;
+ nxt_runtime_t *rt;
+
+ rt = task->thread->runtime;
cap_setid = rt->capabilities.setid;
-#if (NXT_HAVE_CLONE_NEWUSER)
- if (!cap_setid && NXT_CLONE_USER(init->isolation.clone.flags)) {
+#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
+ if (!cap_setid
+ && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) {
cap_setid = 1;
}
#endif
if (cap_setid) {
- ret = nxt_credential_setgids(task, init->user_cred);
+ ret = nxt_credential_setgids(task, process->user_cred);
if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
+ return NXT_ERROR;
}
- ret = nxt_credential_setuid(task, init->user_cred);
+ ret = nxt_credential_setuid(task, process->user_cred);
if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
+ return NXT_ERROR;
}
}
- rt->type = init->type;
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+ if (nxt_slow_path(process->isolation.new_privs == 0
+ && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0))
+ {
+ nxt_alert(task, "failed to set no_new_privs %E", nxt_errno);
+ return NXT_ERROR;
+ }
+#endif
- engine = thread->engine;
+ return NXT_OK;
+}
- /* Update inherited main process event engine and signals processing. */
- engine->signals->sigev = init->signals;
- interface = nxt_service_get(rt->services, "engine", rt->engine);
- if (nxt_slow_path(interface == NULL)) {
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+
+#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS)
+
+
+nxt_int_t
+nxt_process_change_root(nxt_task_t *task, nxt_process_t *process)
+{
+ char *rootfs;
+ nxt_int_t ret;
+
+ rootfs = (char *) process->isolation.rootfs;
+
+ nxt_debug(task, "change root: %s", rootfs);
+
+ if (NXT_CLONE_MNT(process->isolation.clone.flags)) {
+ ret = nxt_process_pivot_root(task, rootfs);
+ } else {
+ ret = nxt_process_chroot(task, rootfs);
+ }
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ if (nxt_slow_path(chdir("/") < 0)) {
+ nxt_alert(task, "chdir(\"/\") %E", nxt_errno);
+ return NXT_ERROR;
+ }
+ }
+
+ return ret;
+}
+
+
+#else
+
+
+nxt_int_t
+nxt_process_change_root(nxt_task_t *task, nxt_process_t *process)
+{
+ char *rootfs;
+
+ rootfs = (char *) process->isolation.rootfs;
+
+ nxt_debug(task, "change root: %s", rootfs);
+
+ if (nxt_fast_path(nxt_process_chroot(task, rootfs) == NXT_OK)) {
+ if (nxt_slow_path(chdir("/") < 0)) {
+ nxt_alert(task, "chdir(\"/\") %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+#endif
+
+
+#endif
+
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+static nxt_int_t
+nxt_process_chroot(nxt_task_t *task, const char *path)
+{
+ if (nxt_slow_path(chroot(path) < 0)) {
+ nxt_alert(task, "chroot(%s) %E", path, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+void
+nxt_process_unmount_all(nxt_task_t *task, nxt_process_t *process)
+{
+ size_t i, n;
+ nxt_array_t *mounts;
+ nxt_fs_mount_t *mnt;
+
+ nxt_debug(task, "unmount all (%s)", process->name);
+
+ mounts = process->isolation.mounts;
+ n = mounts->nelts;
+ mnt = mounts->elts;
+
+ for (i = 0; i < n; i++) {
+ nxt_fs_unmount(mnt[i].dst);
+ }
+}
+
+#endif
+
+
+#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS)
+
+/*
+ * pivot_root(2) can only be safely used with containers, otherwise it can
+ * umount(2) the global root filesystem and screw up the machine.
+ */
+
+static nxt_int_t
+nxt_process_pivot_root(nxt_task_t *task, const char *path)
+{
+ /*
+ * This implementation makes use of a kernel trick that works for ages
+ * and now documented in Linux kernel 5.
+ * https://lore.kernel.org/linux-man/87r24piwhm.fsf@x220.int.ebiederm.org/T/
+ */
+
+ if (nxt_slow_path(mount("", "/", "", MS_SLAVE|MS_REC, "") != 0)) {
+ nxt_alert(task, "failed to make / a slave mount %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_process_private_mount(task, path) != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(mount(path, path, "bind", MS_BIND|MS_REC, "") != 0)) {
+ nxt_alert(task, "error bind mounting rootfs %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(chdir(path) != 0)) {
+ nxt_alert(task, "failed to chdir(%s) %E", path, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_pivot_root(".", ".") != 0)) {
+ nxt_alert(task, "failed to pivot_root %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ /*
+ * Make oldroot a slave mount to avoid unmounts getting propagated to the
+ * host.
+ */
+ if (nxt_slow_path(mount("", ".", "", MS_SLAVE | MS_REC, NULL) != 0)) {
+ nxt_alert(task, "failed to bind mount rootfs %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(umount2(".", MNT_DETACH) != 0)) {
+ nxt_alert(task, "failed to umount old root directory %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_process_private_mount(nxt_task_t *task, const char *rootfs)
+{
+ char *parent_mnt;
+ FILE *procfile;
+ u_char **mounts;
+ size_t len;
+ uint8_t *shared;
+ nxt_int_t ret, index, nmounts;
+ struct mntent *ent;
+
+ static const char *mount_path = "/proc/self/mounts";
+
+ ret = NXT_ERROR;
+ ent = NULL;
+ shared = NULL;
+ procfile = NULL;
+ parent_mnt = NULL;
+
+ nmounts = 256;
+
+ mounts = nxt_malloc(nmounts * sizeof(uintptr_t));
+ if (nxt_slow_path(mounts == NULL)) {
goto fail;
}
- if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
+ shared = nxt_malloc(nmounts);
+ if (nxt_slow_path(shared == NULL)) {
goto fail;
}
- ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
- 60000 * 1000000LL);
- if (nxt_slow_path(ret != NXT_OK)) {
+ procfile = setmntent(mount_path, "r");
+ if (nxt_slow_path(procfile == NULL)) {
+ nxt_alert(task, "failed to open %s %E", mount_path, nxt_errno);
+
goto fail;
}
- main_port = rt->port_by_type[NXT_PROCESS_MAIN];
+ index = 0;
- nxt_port_read_close(main_port);
- nxt_port_write_enable(task, main_port);
+again:
- port = nxt_process_port_first(process);
+ for ( ; index < nmounts; index++) {
+ ent = getmntent(procfile);
+ if (ent == NULL) {
+ nmounts = index;
+ break;
+ }
- nxt_port_write_close(port);
+ mounts[index] = (u_char *) strdup(ent->mnt_dir);
+ shared[index] = hasmntopt(ent, "shared") != NULL;
+ }
- ret = init->start(task, init->data);
+ if (ent != NULL) {
+ /* there are still entries to be read */
- if (nxt_slow_path(ret != NXT_OK)) {
- goto fail;
+ nmounts *= 2;
+ mounts = nxt_realloc(mounts, nmounts);
+ if (nxt_slow_path(mounts == NULL)) {
+ goto fail;
+ }
+
+ shared = nxt_realloc(shared, nmounts);
+ if (nxt_slow_path(shared == NULL)) {
+ goto fail;
+ }
+
+ goto again;
}
- nxt_port_enable(task, port, init->port_handlers);
+ for (index = 0; index < nmounts; index++) {
+ if (nxt_strcmp(mounts[index], rootfs) == 0) {
+ parent_mnt = (char *) rootfs;
+ break;
+ }
+ }
- ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
- -1, init->stream, 0, NULL);
+ if (parent_mnt == NULL) {
+ len = nxt_strlen(rootfs);
- if (nxt_slow_path(ret != NXT_OK)) {
- nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main");
+ parent_mnt = nxt_malloc(len + 1);
+ if (parent_mnt == NULL) {
+ goto fail;
+ }
- goto fail;
+ nxt_memcpy(parent_mnt, rootfs, len);
+ parent_mnt[len] = '\0';
+
+ if (parent_mnt[len - 1] == '/') {
+ parent_mnt[len - 1] = '\0';
+ len--;
+ }
+
+ for ( ;; ) {
+ for (index = 0; index < nmounts; index++) {
+ if (nxt_strcmp(mounts[index], parent_mnt) == 0) {
+ goto found;
+ }
+ }
+
+ if (len == 1 && parent_mnt[0] == '/') {
+ nxt_alert(task, "parent mount not found");
+ goto fail;
+ }
+
+ /* parent dir */
+ while (parent_mnt[len - 1] != '/' && len > 0) {
+ len--;
+ }
+
+ if (nxt_slow_path(len == 0)) {
+ nxt_alert(task, "parent mount not found");
+ goto fail;
+ }
+
+ if (len == 1) {
+ parent_mnt[len] = '\0'; /* / */
+ } else {
+ parent_mnt[len - 1] = '\0'; /* /<path> */
+ }
+ }
}
- return;
+found:
+
+ if (shared[index]) {
+ if (nxt_slow_path(mount("", parent_mnt, "", MS_PRIVATE, "") != 0)) {
+ nxt_alert(task, "mount(\"\", \"%s\", MS_PRIVATE) %E", parent_mnt,
+ nxt_errno);
+
+ goto fail;
+ }
+ }
+
+ ret = NXT_OK;
fail:
- exit(1);
+ if (procfile != NULL) {
+ endmntent(procfile);
+ }
+
+ if (mounts != NULL) {
+ for (index = 0; index < nmounts; index++) {
+ nxt_free(mounts[index]);
+ }
+
+ nxt_free(mounts);
+ }
+
+ if (shared != NULL) {
+ nxt_free(shared);
+ }
+
+ if (parent_mnt != NULL && parent_mnt != rootfs) {
+ nxt_free(parent_mnt);
+ }
+
+ return ret;
+}
+
+
+static int
+nxt_pivot_root(const char *new_root, const char *old_root)
+{
+ return syscall(__NR_pivot_root, new_root, old_root);
+}
+
+#endif
+
+
+static nxt_int_t
+nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process)
+{
+ nxt_int_t ret;
+ nxt_port_t *main_port;
+ nxt_runtime_t *rt;
+
+ rt = task->thread->runtime;
+
+ main_port = rt->port_by_type[NXT_PROCESS_MAIN];
+
+ nxt_assert(main_port != NULL);
+
+ ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
+ -1, process->stream, 0, NULL);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "%s failed to send READY message", process->name);
+ return NXT_ERROR;
+ }
+
+ nxt_debug(task, "%s sent ready", process->name);
+
+ return NXT_OK;
}
@@ -625,3 +1150,50 @@ nxt_process_connected_port_find(nxt_process_t *process, nxt_port_t *port)
return res;
}
+
+
+void
+nxt_process_quit(nxt_task_t *task, nxt_uint_t exit_status)
+{
+ nxt_uint_t n;
+ nxt_queue_t *listen;
+ nxt_runtime_t *rt;
+ nxt_queue_link_t *link, *next;
+ nxt_listen_event_t *lev;
+ nxt_listen_socket_t *ls;
+
+ rt = task->thread->runtime;
+
+ nxt_debug(task, "close listen connections");
+
+ listen = &task->thread->engine->listen_connections;
+
+ for (link = nxt_queue_first(listen);
+ link != nxt_queue_tail(listen);
+ link = next)
+ {
+ next = nxt_queue_next(link);
+ lev = nxt_queue_link_data(link, nxt_listen_event_t, link);
+ nxt_queue_remove(link);
+
+ nxt_fd_event_close(task->thread->engine, &lev->socket);
+ }
+
+ if (rt->listen_sockets != NULL) {
+
+ ls = rt->listen_sockets->elts;
+ n = rt->listen_sockets->nelts;
+
+ while (n != 0) {
+ nxt_socket_close(task, ls->socket);
+ ls->socket = -1;
+
+ ls++;
+ n--;
+ }
+
+ rt->listen_sockets->nelts = 0;
+ }
+
+ nxt_runtime_quit(task, exit_status);
+}
diff --git a/src/nxt_process.h b/src/nxt_process.h
index 3f7155c8..d3311722 100644
--- a/src/nxt_process.h
+++ b/src/nxt_process.h
@@ -8,68 +8,132 @@
#define _NXT_PROCESS_H_INCLUDED_
#if (NXT_HAVE_CLONE)
+#include <unistd.h>
#include <nxt_clone.h>
#endif
+#if (NXT_HAVE_CLONE)
+/*
+ * Old glibc wrapper for getpid(2) returns a cached pid invalidated only by
+ * fork(2) calls. As we use clone(2) for container, it returns the wrong pid.
+ */
+#define nxt_getpid() \
+ syscall(__NR_getpid)
+#else
+#define nxt_getpid() \
+ getpid()
+#endif
+
typedef pid_t nxt_pid_t;
-typedef struct nxt_process_init_s nxt_process_init_t;
-typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task, void *data);
-typedef nxt_int_t (*nxt_process_restart_t)(nxt_task_t *task, nxt_runtime_t *rt,
- nxt_process_init_t *init);
+typedef struct nxt_common_app_conf_s nxt_common_app_conf_t;
-struct nxt_process_init_s {
- nxt_mp_t *mem_pool;
- nxt_process_start_t start;
- const char *name;
- nxt_credential_t *user_cred;
- const nxt_port_handlers_t *port_handlers;
- const nxt_sig_event_t *signals;
-
- nxt_process_type_t type;
+typedef struct {
+ nxt_runtime_t *rt;
+} nxt_discovery_init_t;
- void *data;
- uint32_t stream;
- union {
-#if (NXT_HAVE_CLONE)
- nxt_clone_t clone;
+typedef struct {
+ nxt_str_t conf;
+#if (NXT_TLS)
+ nxt_array_t *certs;
#endif
- } isolation;
-};
+} nxt_controller_init_t;
+
+
+typedef union {
+ void *discovery;
+ nxt_controller_init_t controller;
+ void *router;
+ nxt_common_app_conf_t *app;
+} nxt_process_data_t;
+
+
+typedef enum {
+ NXT_PROCESS_STATE_CREATING = 0,
+ NXT_PROCESS_STATE_CREATED,
+ NXT_PROCESS_STATE_READY,
+} nxt_process_state_t;
typedef struct nxt_port_mmap_s nxt_port_mmap_t;
-typedef struct nxt_port_mmaps_s nxt_port_mmaps_t;
-struct nxt_port_mmaps_s {
+
+typedef struct {
nxt_thread_mutex_t mutex;
uint32_t size;
uint32_t cap;
nxt_port_mmap_t *elts;
-};
+} nxt_port_mmaps_t;
+
+typedef struct {
+ u_char *rootfs;
+ nxt_array_t *mounts; /* of nxt_mount_t */
+
+#if (NXT_HAVE_CLONE)
+ nxt_clone_t clone;
+#endif
+
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+ uint8_t new_privs; /* 1 bit */
+#endif
+} nxt_process_isolation_t;
typedef struct {
- nxt_pid_t pid;
- nxt_queue_t ports; /* of nxt_port_t */
- nxt_bool_t ready;
- nxt_bool_t registered;
- nxt_int_t use_count;
+ nxt_pid_t pid;
+ const char *name;
+ nxt_queue_t ports; /* of nxt_port_t */
+ nxt_process_state_t state;
+ nxt_bool_t registered;
+ nxt_int_t use_count;
+
+ nxt_port_mmaps_t incoming;
+ nxt_port_mmaps_t outgoing;
+
+ nxt_thread_mutex_t cp_mutex;
+ nxt_lvlhsh_t connected_ports; /* of nxt_port_t */
+
+ uint32_t stream;
- nxt_process_init_t *init;
+ nxt_mp_t *mem_pool;
+ nxt_credential_t *user_cred;
- nxt_port_mmaps_t incoming;
- nxt_port_mmaps_t outgoing;
+ nxt_process_data_t data;
- nxt_thread_mutex_t cp_mutex;
- nxt_lvlhsh_t connected_ports; /* of nxt_port_t */
+ nxt_process_isolation_t isolation;
} nxt_process_t;
+typedef nxt_int_t (*nxt_process_prefork_t)(nxt_task_t *task,
+ nxt_process_t *process, nxt_mp_t *mp);
+typedef nxt_int_t (*nxt_process_postfork_t)(nxt_task_t *task,
+ nxt_process_t *process, nxt_mp_t *mp);
+typedef nxt_int_t (*nxt_process_setup_t)(nxt_task_t *task,
+ nxt_process_t *process);
+typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task,
+ nxt_process_data_t *data);
+
+
+typedef struct {
+ const char *name;
+ nxt_process_type_t type;
+
+ nxt_process_prefork_t prefork;
+
+ nxt_process_setup_t setup;
+ nxt_process_start_t start;
+
+ uint8_t restart; /* 1-bit */
+
+ const nxt_port_handlers_t *port_handlers;
+ const nxt_sig_event_t *signals;
+} nxt_process_init_t;
+
+
extern nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX];
extern nxt_bool_t
nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX];
@@ -84,6 +148,9 @@ NXT_EXPORT void nxt_nanosleep(nxt_nsec_t ns);
NXT_EXPORT void nxt_process_arguments(nxt_task_t *task, char **orig_argv,
char ***orig_envp);
+#define nxt_process_init(process) \
+ (nxt_pointer_to(process, sizeof(nxt_process_t)))
+
#define nxt_process_port_remove(port) \
nxt_queue_remove(&port->link)
@@ -113,11 +180,24 @@ void nxt_process_connected_port_remove(nxt_process_t *process,
nxt_port_t *nxt_process_connected_port_find(nxt_process_t *process,
nxt_port_t *port);
-void nxt_worker_process_quit_handler(nxt_task_t *task,
- nxt_port_recv_msg_t *msg);
+void nxt_process_quit(nxt_task_t *task, nxt_uint_t exit_status);
+void nxt_signal_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);
+
+nxt_int_t nxt_process_core_setup(nxt_task_t *task, nxt_process_t *process);
+nxt_int_t nxt_process_creds_set(nxt_task_t *task, nxt_process_t *process,
+ nxt_str_t *user, nxt_str_t *group);
+nxt_int_t nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process);
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+nxt_int_t nxt_process_vldt_isolation_creds(nxt_task_t *task,
+ nxt_process_t *process);
+#endif
-void nxt_init_destroy(nxt_runtime_t *rt, nxt_process_init_t *init);
+nxt_int_t nxt_process_change_root(nxt_task_t *task, nxt_process_t *process);
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+void nxt_process_unmount_all(nxt_task_t *task, nxt_process_t *process);
+#endif
#if (NXT_HAVE_SETPROCTITLE)
diff --git a/src/nxt_process_type.h b/src/nxt_process_type.h
index 5ff06d63..14deda19 100644
--- a/src/nxt_process_type.h
+++ b/src/nxt_process_type.h
@@ -13,7 +13,7 @@ typedef enum {
NXT_PROCESS_DISCOVERY,
NXT_PROCESS_CONTROLLER,
NXT_PROCESS_ROUTER,
- NXT_PROCESS_WORKER,
+ NXT_PROCESS_APP,
NXT_PROCESS_MAX,
} nxt_process_type_t;
diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c
index 14211f3f..b9033a75 100644
--- a/src/nxt_python_wsgi.c
+++ b/src/nxt_python_wsgi.c
@@ -18,6 +18,7 @@
#include <nxt_unit_field.h>
#include <nxt_unit_request.h>
#include <nxt_unit_response.h>
+#include <nxt_python_mounts.h>
/*
* According to "PEP 3333 / A Note On String Types"
@@ -38,11 +39,17 @@
*/
+#define _NXT_PYTHON_MOUNTS(major, minor) \
+ nxt_python ## major ## minor ## _mounts
+
+#define NXT_PYTHON_MOUNTS(major, minor) _NXT_PYTHON_MOUNTS(major, minor)
+
#if PY_MAJOR_VERSION == 3
#define NXT_PYTHON_BYTES_TYPE "bytestring"
#define PyString_FromStringAndSize(str, size) \
PyUnicode_DecodeLatin1((str), (size), "strict")
+
#else
#define NXT_PYTHON_BYTES_TYPE "string"
@@ -65,7 +72,8 @@ typedef struct {
PyObject_HEAD
} nxt_py_error_t;
-static nxt_int_t nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_python_start(nxt_task_t *task,
+ nxt_process_data_t *data);
static nxt_int_t nxt_python_init_strings(void);
static void nxt_python_request_handler(nxt_unit_request_info_t *req);
static void nxt_python_atexit(void);
@@ -115,8 +123,10 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
compat,
nxt_string("python"),
PY_VERSION,
+ NXT_PYTHON_MOUNTS(PY_MAJOR_VERSION, PY_MINOR_VERSION),
+ nxt_nitems(NXT_PYTHON_MOUNTS(PY_MAJOR_VERSION, PY_MINOR_VERSION)),
NULL,
- nxt_python_init,
+ nxt_python_start,
};
@@ -211,7 +221,7 @@ static nxt_python_string_t nxt_python_strings[] = {
static nxt_int_t
-nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
{
int rc;
char *nxt_py_module;
@@ -219,6 +229,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
PyObject *obj, *pypath, *module;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t python_init;
+ nxt_common_app_conf_t *app_conf;
nxt_python_app_conf_t *c;
#if PY_MAJOR_VERSION == 3
char *path;
@@ -229,7 +240,8 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
static const char bin_python[] = "/bin/python";
#endif
- c = &conf->u.python;
+ app_conf = data->app;
+ c = &app_conf->u.python;
if (c->module.length == 0) {
nxt_alert(task, "python module is empty");
@@ -410,7 +422,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_unit_default_init(task, &python_init);
python_init.callbacks.request_handler = nxt_python_request_handler;
- python_init.shm_limit = conf->shm_limit;
+ python_init.shm_limit = data->app->shm_limit;
unit_ctx = nxt_unit_init(&python_init);
if (nxt_slow_path(unit_ctx == NULL)) {
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 93b750a0..788199c7 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -27,6 +27,7 @@ typedef struct {
uint32_t requests;
nxt_conf_value_t *limits_value;
nxt_conf_value_t *processes_value;
+ nxt_conf_value_t *targets_value;
} nxt_router_app_conf_t;
@@ -74,6 +75,9 @@ struct nxt_port_select_state_s {
typedef struct nxt_port_select_state_s nxt_port_select_state_t;
+static nxt_int_t nxt_router_prefork(nxt_task_t *task, nxt_process_t *process,
+ nxt_mp_t *mp);
+static nxt_int_t nxt_router_start(nxt_task_t *task, nxt_process_data_t *data);
static void nxt_router_greet_controller(nxt_task_t *task,
nxt_port_t *controller_port);
@@ -267,8 +271,8 @@ static const nxt_str_t *nxt_app_msg_prefix[] = {
};
-nxt_port_handlers_t nxt_router_process_port_handlers = {
- .quit = nxt_worker_process_quit_handler,
+static const nxt_port_handlers_t nxt_router_process_port_handlers = {
+ .quit = nxt_signal_quit_handler,
.new_port = nxt_router_new_port_handler,
.change_file = nxt_port_change_log_file_handler,
.mmap = nxt_port_mmap_handler,
@@ -281,8 +285,29 @@ nxt_port_handlers_t nxt_router_process_port_handlers = {
};
-nxt_int_t
-nxt_router_start(nxt_task_t *task, void *data)
+const nxt_process_init_t nxt_router_process = {
+ .name = "router",
+ .type = NXT_PROCESS_ROUTER,
+ .prefork = nxt_router_prefork,
+ .restart = 1,
+ .setup = nxt_process_core_setup,
+ .start = nxt_router_start,
+ .port_handlers = &nxt_router_process_port_handlers,
+ .signals = nxt_process_signals,
+};
+
+
+static nxt_int_t
+nxt_router_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp)
+{
+ nxt_runtime_stop_app_processes(task, task->thread->runtime);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_router_start(nxt_task_t *task, nxt_process_data_t *data)
{
nxt_int_t ret;
nxt_port_t *controller_port;
@@ -291,6 +316,8 @@ nxt_router_start(nxt_task_t *task, void *data)
rt = task->thread->runtime;
+ nxt_log(task, NXT_LOG_INFO, "router started");
+
#if (NXT_TLS)
rt->tls = nxt_service_get(rt->services, "SSL/TLS", "OpenSSL");
if (nxt_slow_path(rt->tls == NULL)) {
@@ -381,8 +408,8 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port,
goto failed;
}
- ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_WORKER, -1,
- stream, port->id, b);
+ ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS,
+ -1, stream, port->id, b);
if (nxt_slow_path(ret != NXT_OK)) {
nxt_port_rpc_cancel(task, port, stream);
@@ -861,7 +888,7 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
}
if (msg->u.new_port == NULL
- || msg->u.new_port->type != NXT_PROCESS_WORKER)
+ || msg->u.new_port->type != NXT_PROCESS_APP)
{
msg->port_msg.type = _NXT_PORT_MSG_RPC_ERROR;
}
@@ -943,8 +970,10 @@ nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_queue_each(engine, &nxt_router->engines, nxt_event_engine_t, link0)
{
- nxt_port_post(task, engine->port, nxt_router_app_process_remove_pid,
- msg->u.data);
+ if (nxt_fast_path(engine->port != NULL)) {
+ nxt_port_post(task, engine->port, nxt_router_app_process_remove_pid,
+ msg->u.data);
+ }
}
nxt_queue_loop;
@@ -1272,6 +1301,12 @@ static nxt_conf_map_t nxt_router_app_conf[] = {
NXT_CONF_MAP_PTR,
offsetof(nxt_router_app_conf_t, processes_value),
},
+
+ {
+ nxt_string("targets"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_router_app_conf_t, targets_value),
+ },
};
@@ -1423,12 +1458,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
{
u_char *p;
size_t size;
- nxt_mp_t *mp;
- uint32_t next;
+ nxt_mp_t *mp, *app_mp;
+ uint32_t next, next_target;
nxt_int_t ret;
- nxt_str_t name, path;
+ nxt_str_t name, path, target;
nxt_app_t *app, *prev;
- nxt_str_t *t;
+ nxt_str_t *t, *s, *targets;
+ nxt_uint_t n, i;
nxt_router_t *router;
nxt_app_joint_t *app_joint;
nxt_conf_value_t *conf, *http, *value, *websocket;
@@ -1501,13 +1537,20 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
size = nxt_conf_json_length(application, NULL);
- app = nxt_malloc(sizeof(nxt_app_t) + name.length + size);
- if (app == NULL) {
+ app_mp = nxt_mp_create(4096, 128, 1024, 64);
+ if (nxt_slow_path(app_mp == NULL)) {
goto fail;
}
+ app = nxt_mp_get(app_mp, sizeof(nxt_app_t) + name.length + size);
+ if (app == NULL) {
+ goto app_fail;
+ }
+
nxt_memzero(app, sizeof(nxt_app_t));
+ app->mem_pool = app_mp;
+
app->name.start = nxt_pointer_to(app, sizeof(nxt_app_t));
app->conf.start = nxt_pointer_to(app, sizeof(nxt_app_t)
+ name.length);
@@ -1522,7 +1565,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
prev = nxt_router_app_find(&router->apps, &name);
if (prev != NULL && nxt_strstr_eq(&app->conf, &prev->conf)) {
- nxt_free(app);
+ nxt_mp_destroy(app_mp);
nxt_queue_remove(&prev->link);
nxt_queue_insert_tail(&tmcf->previous, &prev->link);
@@ -1538,6 +1581,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
apcf.requests = 0;
apcf.limits_value = NULL;
apcf.processes_value = NULL;
+ apcf.targets_value = NULL;
app_joint = nxt_malloc(sizeof(nxt_app_joint_t));
if (nxt_slow_path(app_joint == NULL)) {
@@ -1587,6 +1631,30 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
apcf.spare_processes = apcf.processes;
}
+ if (apcf.targets_value != NULL) {
+ n = nxt_conf_object_members_count(apcf.targets_value);
+
+ targets = nxt_mp_get(app_mp, sizeof(nxt_str_t) * n);
+ if (nxt_slow_path(targets == NULL)) {
+ goto app_fail;
+ }
+
+ next_target = 0;
+
+ for (i = 0; i < n; i++) {
+ (void) nxt_conf_next_object_member(apcf.targets_value,
+ &target, &next_target);
+
+ s = nxt_str_dup(app_mp, &targets[i], &target);
+ if (nxt_slow_path(s == NULL)) {
+ goto app_fail;
+ }
+ }
+
+ } else {
+ targets = NULL;
+ }
+
nxt_debug(task, "application type: %V", &apcf.type);
nxt_debug(task, "application processes: %D", apcf.processes);
nxt_debug(task, "application request timeout: %M", apcf.timeout);
@@ -1628,6 +1696,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
app->max_pending_responses = 2;
app->max_requests = apcf.requests;
+ app->targets = targets;
+
engine = task->thread->engine;
app->engine = engine;
@@ -1793,6 +1863,11 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
}
}
+ ret = nxt_http_routes_resolve(task, tmcf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
value = nxt_conf_get_path(conf, &access_log_path);
if (value != NULL) {
@@ -1827,8 +1902,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
tmcf->router_conf->access_log = access_log;
}
- nxt_http_routes_resolve(task, tmcf);
-
nxt_queue_add(&tmcf->deleting, &router->sockets);
nxt_queue_init(&router->sockets);
@@ -1836,7 +1909,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
app_fail:
- nxt_free(app);
+ nxt_mp_destroy(app_mp);
fail:
@@ -1844,7 +1917,7 @@ fail:
nxt_queue_remove(&app->link);
nxt_thread_mutex_destroy(&app->mutex);
- nxt_free(app);
+ nxt_mp_destroy(app->mem_pool);
} nxt_queue_loop;
@@ -2353,8 +2426,8 @@ nxt_router_app_rpc_create(nxt_task_t *task,
goto fail;
}
- ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_WORKER, -1,
- stream, router_port->id, b);
+ ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS,
+ -1, stream, router_port->id, b);
if (nxt_slow_path(ret != NXT_OK)) {
nxt_port_rpc_cancel(task, router_port, stream);
@@ -4535,7 +4608,7 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data)
nxt_assert(nxt_queue_is_empty(&app->idle_ports));
nxt_thread_mutex_destroy(&app->mutex);
- nxt_free(app);
+ nxt_mp_destroy(app->mem_pool);
app_joint->app = NULL;
@@ -4989,6 +5062,8 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r,
req = (nxt_unit_request_t *) out->mem.free;
out->mem.free += req_size;
+ req->app_target = r->app_target;
+
req->content_length = content_length;
p = (u_char *) (req->fields + fields_count);
diff --git a/src/nxt_router.h b/src/nxt_router.h
index 08142ce3..6004a459 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -133,8 +133,11 @@ struct nxt_app_s {
nxt_nsec_t res_timeout;
nxt_msec_t idle_timeout;
+ nxt_str_t *targets;
+
nxt_app_type_t type:8;
+ nxt_mp_t *mem_pool;
nxt_queue_link_t link;
nxt_str_t conf;
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index bcd156ee..5aa061dd 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -21,6 +21,7 @@ static nxt_int_t nxt_runtime_thread_pools(nxt_thread_t *thr, nxt_runtime_t *rt);
static void nxt_runtime_start(nxt_task_t *task, void *obj, void *data);
static void nxt_runtime_initial_start(nxt_task_t *task, nxt_uint_t status);
static void nxt_runtime_close_idle_connections(nxt_event_engine_t *engine);
+static void nxt_runtime_stop_all_processes(nxt_task_t *task, nxt_runtime_t *rt);
static void nxt_runtime_exit(nxt_task_t *task, void *obj, void *data);
static nxt_int_t nxt_runtime_event_engine_change(nxt_task_t *task,
nxt_runtime_t *rt);
@@ -83,6 +84,7 @@ nxt_runtime_create(nxt_task_t *task)
lang->version = (u_char *) "";
lang->file = NULL;
lang->module = &nxt_external_module;
+ lang->mounts = NULL;
listen_sockets = nxt_array_create(mp, 1, sizeof(nxt_listen_socket_t));
if (nxt_slow_path(listen_sockets == NULL)) {
@@ -118,6 +120,10 @@ nxt_runtime_create(nxt_task_t *task)
goto fail;
}
+ if (nxt_port_rpc_init() != NXT_OK) {
+ goto fail;
+ }
+
nxt_work_queue_add(&task->thread->engine->fast_work_queue,
nxt_runtime_start, task, rt, NULL);
@@ -434,7 +440,7 @@ nxt_runtime_quit(nxt_task_t *task, nxt_uint_t status)
}
if (rt->type == NXT_PROCESS_MAIN) {
- nxt_main_stop_all_processes(task, rt);
+ nxt_runtime_stop_all_processes(task, rt);
done = 0;
}
}
@@ -474,6 +480,50 @@ nxt_runtime_close_idle_connections(nxt_event_engine_t *engine)
}
+void
+nxt_runtime_stop_app_processes(nxt_task_t *task, nxt_runtime_t *rt)
+{
+ nxt_port_t *port;
+ nxt_process_t *process;
+ nxt_process_init_t *init;
+
+ nxt_runtime_process_each(rt, process) {
+
+ init = nxt_process_init(process);
+
+ if (init->type == NXT_PROCESS_APP) {
+
+ nxt_process_port_each(process, port) {
+
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1,
+ 0, 0, NULL);
+
+ } nxt_process_port_loop;
+ }
+
+ } nxt_runtime_process_loop;
+}
+
+
+static void
+nxt_runtime_stop_all_processes(nxt_task_t *task, nxt_runtime_t *rt)
+{
+ nxt_port_t *port;
+ nxt_process_t *process;
+
+ nxt_runtime_process_each(rt, process) {
+
+ nxt_process_port_each(process, port) {
+
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0,
+ 0, NULL);
+
+ } nxt_process_port_loop;
+
+ } nxt_runtime_process_loop;
+}
+
+
static void
nxt_runtime_exit(nxt_task_t *task, void *obj, void *data)
{
@@ -521,6 +571,10 @@ nxt_runtime_exit(nxt_task_t *task, void *obj, void *data)
} nxt_runtime_process_loop;
+ if (rt->port_by_type[rt->type] != NULL) {
+ nxt_port_use(task, rt->port_by_type[rt->type], -1);
+ }
+
nxt_thread_mutex_destroy(&rt->processes_mutex);
status = rt->status;
@@ -1302,7 +1356,9 @@ nxt_runtime_process_new(nxt_runtime_t *rt)
/* TODO: memory failures. */
- process = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_t));
+ process = nxt_mp_zalloc(rt->mem_pool,
+ sizeof(nxt_process_t) + sizeof(nxt_process_init_t));
+
if (nxt_slow_path(process == NULL)) {
return NULL;
}
@@ -1343,8 +1399,9 @@ nxt_runtime_process_release(nxt_runtime_t *rt, nxt_process_t *process)
nxt_thread_mutex_destroy(&process->outgoing.mutex);
nxt_thread_mutex_destroy(&process->cp_mutex);
- if (process->init != NULL) {
- nxt_mp_destroy(process->init->mem_pool);
+ /* processes from nxt_runtime_process_get() have no memory pool */
+ if (process->mem_pool != NULL) {
+ nxt_mp_destroy(process->mem_pool);
}
nxt_mp_free(rt->mem_pool, process);
diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h
index a364c38c..d29b6b4d 100644
--- a/src/nxt_runtime.h
+++ b/src/nxt_runtime.h
@@ -110,6 +110,7 @@ nxt_port_t *nxt_runtime_process_port_create(nxt_task_t *task, nxt_runtime_t *rt,
nxt_pid_t pid, nxt_port_id_t id, nxt_process_type_t type);
void nxt_runtime_port_remove(nxt_task_t *task, nxt_port_t *port);
+void nxt_runtime_stop_app_processes(nxt_task_t *task, nxt_runtime_t *rt);
NXT_EXPORT nxt_port_t *nxt_runtime_port_find(nxt_runtime_t *rt, nxt_pid_t pid,
nxt_port_id_t port_id);
diff --git a/src/nxt_signal_handlers.c b/src/nxt_signal_handlers.c
new file mode 100644
index 00000000..69ae2bc4
--- /dev/null
+++ b/src/nxt_signal_handlers.c
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_runtime.h>
+#include <nxt_port.h>
+#include <nxt_main_process.h>
+#include <nxt_router.h>
+
+
+static void nxt_signal_handler(nxt_task_t *task, void *obj, void *data);
+static void nxt_signal_sigterm_handler(nxt_task_t *task, void *obj, void *data);
+static void nxt_signal_sigquit_handler(nxt_task_t *task, void *obj, void *data);
+
+
+const nxt_sig_event_t nxt_process_signals[] = {
+ nxt_event_signal(SIGHUP, nxt_signal_handler),
+ nxt_event_signal(SIGINT, nxt_signal_sigterm_handler),
+ nxt_event_signal(SIGQUIT, nxt_signal_sigquit_handler),
+ nxt_event_signal(SIGTERM, nxt_signal_sigterm_handler),
+ nxt_event_signal(SIGCHLD, nxt_signal_handler),
+ nxt_event_signal(SIGUSR1, nxt_signal_handler),
+ nxt_event_signal(SIGUSR2, nxt_signal_handler),
+ nxt_event_signal_end,
+};
+
+
+static void
+nxt_signal_handler(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_trace(task, "signal signo:%d (%s) recevied, ignored",
+ (int) (uintptr_t) obj, data);
+}
+
+
+void
+nxt_signal_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ nxt_process_quit(task, 0);
+}
+
+
+static void
+nxt_signal_sigterm_handler(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_debug(task, "sigterm handler signo:%d (%s)",
+ (int) (uintptr_t) obj, data);
+
+ /* A fast exit. */
+
+ nxt_runtime_quit(task, 0);
+}
+
+
+static void
+nxt_signal_sigquit_handler(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_debug(task, "sigquit handler signo:%d (%s)",
+ (int) (uintptr_t) obj, data);
+
+ /* A graceful exit. */
+
+ nxt_process_quit(task, 0);
+}
diff --git a/src/nxt_string.c b/src/nxt_string.c
index 54f96abc..ab568990 100644
--- a/src/nxt_string.c
+++ b/src/nxt_string.c
@@ -475,7 +475,7 @@ nxt_strvers_match(u_char *version, u_char *prefix, size_t length)
}
-static const uint8_t nxt_hex2int[256]
+const uint8_t nxt_hex2int[256]
nxt_aligned(32) =
{
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
@@ -551,6 +551,47 @@ nxt_decode_uri(u_char *dst, u_char *src, size_t length)
}
+u_char *
+nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length)
+{
+ u_char *end, ch;
+ uint8_t d0, d1;
+
+ nxt_prefetch(&nxt_hex2int['0']);
+
+ end = src + length;
+
+ while (src < end) {
+ ch = *src++;
+
+ switch (ch) {
+ case '%':
+ if (nxt_slow_path(end - src < 2)) {
+ return NULL;
+ }
+
+ d0 = nxt_hex2int[*src++];
+ d1 = nxt_hex2int[*src++];
+
+ if (nxt_slow_path((d0 | d1) >= 16)) {
+ return NULL;
+ }
+
+ ch = (d0 << 4) + d1;
+ break;
+
+ case '+':
+ ch = ' ';
+ break;
+ }
+
+ *dst++ = ch;
+ }
+
+ return dst;
+}
+
+
uintptr_t
nxt_encode_uri(u_char *dst, u_char *src, size_t length)
{
diff --git a/src/nxt_string.h b/src/nxt_string.h
index 7863c60e..3f9192e2 100644
--- a/src/nxt_string.h
+++ b/src/nxt_string.h
@@ -174,10 +174,13 @@ NXT_EXPORT nxt_bool_t nxt_strvers_match(u_char *version, u_char *prefix,
size_t length);
NXT_EXPORT u_char *nxt_decode_uri(u_char *dst, u_char *src, size_t length);
+NXT_EXPORT u_char *nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length);
NXT_EXPORT uintptr_t nxt_encode_uri(u_char *dst, u_char *src, size_t length);
NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src,
size_t length);
NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length);
+extern const uint8_t nxt_hex2int[256];
+
#endif /* _NXT_STRING_H_INCLUDED_ */
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index 67244420..9f6eab95 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -4248,7 +4248,7 @@ nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *dst,
m.new_port.id = new_port->id;
m.new_port.pid = new_port->pid;
- m.new_port.type = NXT_PROCESS_WORKER;
+ m.new_port.type = NXT_PROCESS_APP;
m.new_port.max_size = 16 * 1024;
m.new_port.max_share = 64 * 1024;
diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h
index 52017a42..fede00d2 100644
--- a/src/nxt_unit_request.h
+++ b/src/nxt_unit_request.h
@@ -21,6 +21,7 @@ struct nxt_unit_request_s {
uint8_t local_length;
uint8_t tls;
uint8_t websocket_handshake;
+ uint8_t app_target;
uint32_t server_name_length;
uint32_t target_length;
uint32_t path_length;
diff --git a/src/nxt_unix.h b/src/nxt_unix.h
index 151dd555..609f7e95 100644
--- a/src/nxt_unix.h
+++ b/src/nxt_unix.h
@@ -238,6 +238,9 @@
#include <sys/random.h> /* getentropy(). */
#endif
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+#include <sys/mount.h>
+#endif
#if (NXT_TEST_BUILD)
#include <nxt_test_build.h>
diff --git a/src/nxt_worker_process.c b/src/nxt_worker_process.c
deleted file mode 100644
index 754e2ea8..00000000
--- a/src/nxt_worker_process.c
+++ /dev/null
@@ -1,118 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) NGINX, Inc.
- */
-
-#include <nxt_main.h>
-#include <nxt_runtime.h>
-#include <nxt_port.h>
-#include <nxt_main_process.h>
-#include <nxt_router.h>
-
-
-static void nxt_worker_process_quit(nxt_task_t *task);
-static void nxt_worker_process_signal_handler(nxt_task_t *task, void *obj,
- void *data);
-static void nxt_worker_process_sigterm_handler(nxt_task_t *task, void *obj,
- void *data);
-static void nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj,
- void *data);
-
-
-const nxt_sig_event_t nxt_worker_process_signals[] = {
- nxt_event_signal(SIGHUP, nxt_worker_process_signal_handler),
- nxt_event_signal(SIGINT, nxt_worker_process_sigterm_handler),
- nxt_event_signal(SIGQUIT, nxt_worker_process_sigquit_handler),
- nxt_event_signal(SIGTERM, nxt_worker_process_sigterm_handler),
- nxt_event_signal(SIGCHLD, nxt_worker_process_signal_handler),
- nxt_event_signal(SIGUSR1, nxt_worker_process_signal_handler),
- nxt_event_signal(SIGUSR2, nxt_worker_process_signal_handler),
- nxt_event_signal_end,
-};
-
-
-static void
-nxt_worker_process_quit(nxt_task_t *task)
-{
- nxt_uint_t n;
- nxt_queue_t *listen;
- nxt_runtime_t *rt;
- nxt_queue_link_t *link, *next;
- nxt_listen_event_t *lev;
- nxt_listen_socket_t *ls;
-
- rt = task->thread->runtime;
-
- nxt_debug(task, "close listen connections");
-
- listen = &task->thread->engine->listen_connections;
-
- for (link = nxt_queue_first(listen);
- link != nxt_queue_tail(listen);
- link = next)
- {
- next = nxt_queue_next(link);
- lev = nxt_queue_link_data(link, nxt_listen_event_t, link);
- nxt_queue_remove(link);
-
- nxt_fd_event_close(task->thread->engine, &lev->socket);
- }
-
- if (rt->listen_sockets != NULL) {
-
- ls = rt->listen_sockets->elts;
- n = rt->listen_sockets->nelts;
-
- while (n != 0) {
- nxt_socket_close(task, ls->socket);
- ls->socket = -1;
-
- ls++;
- n--;
- }
-
- rt->listen_sockets->nelts = 0;
- }
-
- nxt_runtime_quit(task, 0);
-}
-
-
-static void
-nxt_worker_process_signal_handler(nxt_task_t *task, void *obj, void *data)
-{
- nxt_trace(task, "signal signo:%d (%s) recevied, ignored",
- (int) (uintptr_t) obj, data);
-}
-
-
-void
-nxt_worker_process_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
-{
- nxt_worker_process_quit(task);
-}
-
-
-static void
-nxt_worker_process_sigterm_handler(nxt_task_t *task, void *obj, void *data)
-{
- nxt_debug(task, "sigterm handler signo:%d (%s)",
- (int) (uintptr_t) obj, data);
-
- /* A fast exit. */
-
- nxt_runtime_quit(task, 0);
-}
-
-
-static void
-nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj, void *data)
-{
- nxt_debug(task, "sigquit handler signo:%d (%s)",
- (int) (uintptr_t) obj, data);
-
- /* A graceful exit. */
-
- nxt_worker_process_quit(task);
-}
diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c
index 548e6daa..14e107e4 100644
--- a/src/perl/nxt_perl_psgi.c
+++ b/src/perl/nxt_perl_psgi.c
@@ -95,8 +95,8 @@ static int nxt_perl_psgi_result_array(PerlInterpreter *my_perl,
static void nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result,
nxt_unit_request_info_t *req);
-static nxt_int_t nxt_perl_psgi_init(nxt_task_t *task,
- nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_perl_psgi_start(nxt_task_t *task,
+ nxt_process_data_t *conf);
static void nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req);
static void nxt_perl_psgi_atexit(void);
@@ -118,8 +118,14 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
nxt_perl_psgi_compat,
nxt_string("perl"),
PERL_VERSION_STRING,
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
NULL,
- nxt_perl_psgi_init,
+ 0,
+#endif
+
+ NULL,
+ nxt_perl_psgi_start,
};
@@ -1134,14 +1140,17 @@ nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result,
static nxt_int_t
-nxt_perl_psgi_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_perl_psgi_start(nxt_task_t *task, nxt_process_data_t *data)
{
int rc;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t perl_init;
PerlInterpreter *my_perl;
+ nxt_common_app_conf_t *conf;
nxt_perl_psgi_module_t module;
+ conf = data->app;
+
my_perl = nxt_perl_psgi_interpreter_init(task, conf->u.perl.script,
&module.app);
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index 417e2d8d..489ddcf4 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -7,6 +7,7 @@
#include <nxt_unit.h>
#include <nxt_unit_request.h>
+#include <nxt_ruby_mounts.h>
#define NXT_RUBY_RACK_API_VERSION_MAJOR 1
@@ -28,7 +29,8 @@ typedef struct {
} nxt_ruby_rack_init_t;
-static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
+static nxt_int_t nxt_ruby_start(nxt_task_t *task,
+ nxt_process_data_t *data);
static VALUE nxt_ruby_init_basic(VALUE arg);
static nxt_int_t nxt_ruby_init_io(nxt_task_t *task);
static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
@@ -77,22 +79,29 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
compat,
nxt_string("ruby"),
ruby_version,
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ nxt_ruby_mounts,
+ nxt_nitems(nxt_ruby_mounts),
+#endif
NULL,
- nxt_ruby_init,
+ nxt_ruby_start,
};
static nxt_int_t
-nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
+nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
{
- int state, rc;
- VALUE res;
- nxt_unit_ctx_t *unit_ctx;
- nxt_unit_init_t ruby_unit_init;
- nxt_ruby_rack_init_t rack_init;
+ int state, rc;
+ VALUE res;
+ nxt_unit_ctx_t *unit_ctx;
+ nxt_unit_init_t ruby_unit_init;
+ nxt_ruby_rack_init_t rack_init;
+ nxt_common_app_conf_t *conf;
static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" };
+ conf = data->app;
+
RUBY_INIT_STACK
ruby_init();
ruby_options(2, argv);
diff --git a/test/go/ns_inspect/app.go b/test/go/ns_inspect/app.go
index d9b561c9..4d19a796 100644
--- a/test/go/ns_inspect/app.go
+++ b/test/go/ns_inspect/app.go
@@ -21,10 +21,11 @@ type (
}
Output struct {
- PID int
- UID int
- GID int
- NS NS
+ PID int
+ UID int
+ GID int
+ NS NS
+ FileExists bool
}
)
@@ -64,6 +65,18 @@ func handler(w http.ResponseWriter, r *http.Request) {
CGROUP: getns("cgroup"),
},
}
+
+ err := r.ParseForm()
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ if fname := r.Form.Get("file"); fname != "" {
+ _, err = os.Stat(fname);
+ out.FileExists = err == nil
+ }
+
data, err := json.Marshal(out)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
diff --git a/test/php/targets/1.php b/test/php/targets/1.php
new file mode 100644
index 00000000..09f7ae2c
--- /dev/null
+++ b/test/php/targets/1.php
@@ -0,0 +1,4 @@
+<?php
+header('Content-Length: 1');
+echo '1';
+?>
diff --git a/test/php/targets/2/2.php b/test/php/targets/2/2.php
new file mode 100644
index 00000000..0c5d27c6
--- /dev/null
+++ b/test/php/targets/2/2.php
@@ -0,0 +1,4 @@
+<?php
+header('Content-Length: 1');
+echo '2';
+?>
diff --git a/test/php/targets/index.php b/test/php/targets/index.php
new file mode 100644
index 00000000..88239d5f
--- /dev/null
+++ b/test/php/targets/index.php
@@ -0,0 +1,4 @@
+<?php
+header('Content-Length: 5');
+echo 'index';
+?>
diff --git a/test/python/ns_inspect/wsgi.py b/test/python/ns_inspect/wsgi.py
new file mode 100644
index 00000000..fa1222e4
--- /dev/null
+++ b/test/python/ns_inspect/wsgi.py
@@ -0,0 +1,31 @@
+import json
+import os
+
+try:
+ # Python 3
+ from urllib.parse import parse_qs
+except ImportError:
+ # Python 2
+ from urlparse import parse_qs
+
+
+def application(environ, start_response):
+ ret = {
+ 'FileExists': False,
+ }
+
+ d = parse_qs(environ['QUERY_STRING'])
+
+ ret['FileExists'] = os.path.exists(d.get('path')[0])
+
+ out = json.dumps(ret)
+
+ start_response(
+ '200',
+ [
+ ('Content-Type', 'application/json'),
+ ('Content-Length', str(len(out))),
+ ],
+ )
+
+ return out.encode('utf-8')
diff --git a/test/run.py b/test/run.py
index 59e06bcb..384663f9 100755
--- a/test/run.py
+++ b/test/run.py
@@ -1,8 +1,7 @@
#!/usr/bin/env python3
-
-import unittest
-import sys
import os
+import sys
+import unittest
if __name__ == '__main__':
loader = unittest.TestLoader()
diff --git a/test/test_access_log.py b/test/test_access_log.py
index 898d8b24..3ef8f7a0 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -1,12 +1,11 @@
-import os
-import re
import time
import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestAccessLog(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def load(self, script):
super().load(script)
diff --git a/test/test_configuration.py b/test/test_configuration.py
index daba874b..0329ef5e 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -1,9 +1,10 @@
import unittest
+
from unit.control import TestControl
class TestConfiguration(TestControl):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def test_json_empty(self):
self.assertIn('error', self.conf(''), 'empty')
diff --git a/test/test_go_application.py b/test/test_go_application.py
index c9d4ba77..b9b78e2b 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -2,7 +2,7 @@ from unit.applications.lang.go import TestApplicationGo
class TestGoApplication(TestApplicationGo):
- prerequisites = {'modules': ['go']}
+ prerequisites = {'modules': {'go': 'all'}}
def test_go_application_variables(self):
self.load('variables')
diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py
index 7884274d..61d39617 100644
--- a/test/test_go_isolation.py
+++ b/test/test_go_isolation.py
@@ -1,13 +1,13 @@
-import pwd
import grp
-import json
+import pwd
import unittest
+
from unit.applications.lang.go import TestApplicationGo
from unit.feature.isolation import TestFeatureIsolation
class TestGoIsolation(TestApplicationGo):
- prerequisites = {'modules': ['go'], 'features': ['isolation']}
+ prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']}
isolation = TestFeatureIsolation()
@@ -281,6 +281,52 @@ class TestGoIsolation(TestApplicationGo):
'%s match' % ns,
)
+ def test_go_isolation_rootfs_container(self):
+ if not self.isolation_key('unprivileged_userns_clone'):
+ print('unprivileged clone is not available')
+ raise unittest.SkipTest()
+
+ if not self.isolation_key('mnt'):
+ print('mnt namespace is not supported')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'namespaces': {'mount': True, 'credential': True},
+ 'rootfs': self.testdir,
+ }
+
+ self.load('ns_inspect', isolation=isolation)
+
+ obj = self.getjson(url='/?file=/go/app')['body']
+
+ self.assertEqual(obj['FileExists'], True, 'app relative to rootfs')
+
+ obj = self.getjson(url='/?file=/bin/sh')['body']
+ self.assertEqual(obj['FileExists'], False, 'file should not exists')
+
+ def test_go_isolation_rootfs_container_priv(self):
+ if not self.is_su:
+ print("requires root")
+ raise unittest.SkipTest()
+
+ if not self.isolation_key('mnt'):
+ print('mnt namespace is not supported')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'namespaces': {'mount': True},
+ 'rootfs': self.testdir,
+ }
+
+ self.load('ns_inspect', isolation=isolation)
+
+ obj = self.getjson(url='/?file=/go/app')['body']
+
+ self.assertEqual(obj['FileExists'], True, 'app relative to rootfs')
+
+ obj = self.getjson(url='/?file=/bin/sh')['body']
+ self.assertEqual(obj['FileExists'], False, 'file should not exists')
+
if __name__ == '__main__':
TestGoIsolation.main()
diff --git a/test/test_go_isolation_rootfs.py b/test/test_go_isolation_rootfs.py
new file mode 100644
index 00000000..0039ff87
--- /dev/null
+++ b/test/test_go_isolation_rootfs.py
@@ -0,0 +1,34 @@
+import os
+import unittest
+
+from unit.applications.lang.go import TestApplicationGo
+
+
+class TestGoIsolationRootfs(TestApplicationGo):
+ prerequisites = {'modules': {'go': 'all'}}
+
+ def test_go_isolation_rootfs_chroot(self):
+ if not self.is_su:
+ print("requires root")
+ raise unittest.SkipTest()
+
+ if os.uname().sysname == 'Darwin':
+ print('chroot tests not supported on OSX')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'rootfs': self.testdir,
+ }
+
+ self.load('ns_inspect', isolation=isolation)
+
+ obj = self.getjson(url='/?file=/go/app')['body']
+
+ self.assertEqual(obj['FileExists'], True, 'app relative to rootfs')
+
+ obj = self.getjson(url='/?file=/bin/sh')['body']
+ self.assertEqual(obj['FileExists'], False, 'file should not exists')
+
+
+if __name__ == '__main__':
+ TestGoIsolationRootfs.main()
diff --git a/test/test_http_header.py b/test/test_http_header.py
index b773bd68..ea4520c1 100644
--- a/test/test_http_header.py
+++ b/test/test_http_header.py
@@ -1,9 +1,10 @@
import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestHTTPHeader(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def test_http_header_value_leading_sp(self):
self.load('custom_header')
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 7bd351a4..0cb18c25 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -1,18 +1,19 @@
import io
import os
import time
-import unittest
+
from unit.applications.lang.java import TestApplicationJava
class TestJavaApplication(TestApplicationJava):
- prerequisites = {'modules': ['java']}
+ prerequisites = {'modules': {'java': 'all'}}
def test_java_conf_error(self):
self.skip_alerts.extend(
[
r'realpath.*failed',
r'failed to apply new conf',
+ r'application setup failed',
]
)
self.assertIn(
diff --git a/test/test_java_isolation_rootfs.py b/test/test_java_isolation_rootfs.py
new file mode 100644
index 00000000..4d39bdc3
--- /dev/null
+++ b/test/test_java_isolation_rootfs.py
@@ -0,0 +1,85 @@
+import os
+import subprocess
+import unittest
+
+from unit.applications.lang.java import TestApplicationJava
+
+
+class TestJavaIsolationRootfs(TestApplicationJava):
+ prerequisites = {'modules': {'java': 'all'}}
+
+ def setUp(self):
+ if not self.is_su:
+ return
+
+ super().setUp()
+
+ os.makedirs(self.testdir + '/jars')
+ os.makedirs(self.testdir + '/tmp')
+ os.chmod(self.testdir + '/tmp', 0o777)
+
+ try:
+ process = subprocess.Popen(
+ [
+ "mount",
+ "--bind",
+ self.pardir + "/build",
+ self.testdir + "/jars",
+ ],
+ stderr=subprocess.STDOUT,
+ )
+
+ process.communicate()
+
+ except:
+ self.fail('Cann\'t run mount process.')
+
+ def tearDown(self):
+ if not self.is_su:
+ return
+
+ try:
+ process = subprocess.Popen(
+ ["umount", "--lazy", self.testdir + "/jars"],
+ stderr=subprocess.STDOUT,
+ )
+
+ process.communicate()
+
+ except:
+ self.fail('Cann\'t run mount process.')
+
+ # super teardown must happen after unmount to avoid deletion of /build
+ super().tearDown()
+
+ def test_java_isolation_rootfs_chroot_war(self):
+ if not self.is_su:
+ print('require root')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'rootfs': self.testdir,
+ }
+
+ self.load('empty_war', isolation=isolation)
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"/"', '/config/applications/empty_war/working_directory',
+ ),
+ )
+
+ self.assertIn(
+ 'success', self.conf('"/jars"', 'applications/empty_war/unit_jars')
+ )
+ self.assertIn(
+ 'success',
+ self.conf('"/java/empty.war"', 'applications/empty_war/webapp'),
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'war')
+
+
+if __name__ == '__main__':
+ TestJavaIsolationRootfs.main()
diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py
index 7ea04620..d78f7263 100644
--- a/test/test_java_websockets.py
+++ b/test/test_java_websockets.py
@@ -1,12 +1,13 @@
-import time
import struct
+import time
import unittest
+
from unit.applications.lang.java import TestApplicationJava
from unit.applications.websockets import TestApplicationWebsocket
class TestJavaWebsockets(TestApplicationJava):
- prerequisites = {'modules': ['java']}
+ prerequisites = {'modules': {'java': 'any'}}
ws = TestApplicationWebsocket()
diff --git a/test/test_node_application.py b/test/test_node_application.py
index 174af15d..e46cc6a1 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -1,9 +1,10 @@
import unittest
+
from unit.applications.lang.node import TestApplicationNode
class TestNodeApplication(TestApplicationNode):
- prerequisites = {'modules': ['node']}
+ prerequisites = {'modules': {'node': 'all'}}
def test_node_application_basic(self):
self.load('basic')
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
index 4ce727db..1928d8c9 100644
--- a/test/test_node_websockets.py
+++ b/test/test_node_websockets.py
@@ -1,12 +1,13 @@
-import time
import struct
+import time
import unittest
+
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
class TestNodeWebsockets(TestApplicationNode):
- prerequisites = {'modules': ['node']}
+ prerequisites = {'modules': {'node': 'any'}}
ws = TestApplicationWebsocket()
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index cc4eb915..dbf6abf7 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -1,9 +1,10 @@
import unittest
+
from unit.applications.lang.perl import TestApplicationPerl
class TestPerlApplication(TestApplicationPerl):
- prerequisites = {'modules': ['perl']}
+ prerequisites = {'modules': {'perl': 'all'}}
def test_perl_application(self):
self.load('variables')
diff --git a/test/test_php_application.py b/test/test_php_application.py
index 48e1e815..1259d22d 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -1,11 +1,11 @@
import os
-import re
import shutil
import unittest
+
from unit.applications.lang.php import TestApplicationPHP
class TestPHPApplication(TestApplicationPHP):
- prerequisites = {'modules': ['php']}
+ prerequisites = {'modules': {'php': 'all'}}
def before_disable_functions(self):
body = self.get()['body']
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 5fde3e00..16483c4a 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -2,7 +2,7 @@ from unit.control import TestControl
class TestPHPBasic(TestControl):
- prerequisites = {'modules': ['php']}
+ prerequisites = {'modules': {'php': 'any'}}
conf_app = {
"app": {
diff --git a/test/test_php_isolation.py b/test/test_php_isolation.py
new file mode 100644
index 00000000..1b70ef02
--- /dev/null
+++ b/test/test_php_isolation.py
@@ -0,0 +1,57 @@
+import unittest
+
+from unit.applications.lang.php import TestApplicationPHP
+from unit.feature.isolation import TestFeatureIsolation
+
+
+class TestPHPIsolation(TestApplicationPHP):
+ prerequisites = {'modules': {'php': 'any'}, 'features': ['isolation']}
+
+ isolation = TestFeatureIsolation()
+
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
+
+ TestFeatureIsolation().check(cls.available, unit.testdir)
+
+ return unit if not complete_check else unit.complete()
+
+ def test_php_isolation_rootfs(self):
+ isolation_features = self.available['features']['isolation'].keys()
+
+ if 'mnt' not in isolation_features:
+ print('requires mnt ns')
+ raise unittest.SkipTest()
+
+ if not self.is_su:
+ if 'user' not in isolation_features:
+ print('requires unprivileged userns or root')
+ raise unittest.SkipTest()
+
+ if not 'unprivileged_userns_clone' in isolation_features:
+ print('requires unprivileged userns or root')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'namespaces': {'credential': not self.is_su, 'mount': True},
+ 'rootfs': self.current_dir,
+ }
+
+ self.load('phpinfo', isolation=isolation)
+
+ self.assertIn(
+ 'success', self.conf('"/php/phpinfo"', 'applications/phpinfo/root')
+ )
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"/php/phpinfo"', 'applications/phpinfo/working_directory'
+ ),
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'empty rootfs')
+
+
+if __name__ == '__main__':
+ TestPHPIsolation.main()
diff --git a/test/test_php_targets.py b/test/test_php_targets.py
new file mode 100644
index 00000000..9c1ba2a6
--- /dev/null
+++ b/test/test_php_targets.py
@@ -0,0 +1,129 @@
+import unittest
+from unit.applications.lang.php import TestApplicationPHP
+
+class TestPHPTargets(TestApplicationPHP):
+ prerequisites = {'modules': {'php': 'any'}}
+
+ def test_php_application_targets(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [
+ {
+ "match": {"uri": "/1"},
+ "action": {"pass": "applications/targets/1"},
+ },
+ {
+ "match": {"uri": "/2"},
+ "action": {"pass": "applications/targets/2"},
+ },
+ {"action": {"pass": "applications/targets/default"}},
+ ],
+ "applications": {
+ "targets": {
+ "type": "php",
+ "processes": {"spare": 0},
+ "targets": {
+ "1": {
+ "script": "1.php",
+ "root": self.current_dir + "/php/targets",
+ },
+ "2": {
+ "script": "2.php",
+ "root": self.current_dir
+ + "/php/targets/2",
+ },
+ "default": {
+ "index": "index.php",
+ "root": self.current_dir + "/php/targets",
+ },
+ },
+ }
+ },
+ }
+ ),
+ )
+
+ self.assertEqual(self.get(url='/1')['body'], '1')
+ self.assertEqual(self.get(url='/2')['body'], '2')
+ self.assertEqual(self.get(url='/blah')['status'], 503) # TODO 404
+ self.assertEqual(self.get(url='/')['body'], 'index')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ "\"1.php\"", 'applications/targets/targets/default/index'
+ ),
+ 'change targets index',
+ )
+ self.assertEqual(self.get(url='/')['body'], '1')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('applications/targets/targets/default/index'),
+ 'remove targets index',
+ )
+ self.assertEqual(self.get(url='/')['body'], 'index')
+
+ def test_php_application_targets_error(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "applications/targets/default"}
+ },
+ "applications": {
+ "targets": {
+ "type": "php",
+ "processes": {"spare": 0},
+ "targets": {
+ "default": {
+ "index": "index.php",
+ "root": self.current_dir + "/php/targets",
+ },
+ },
+ }
+ },
+ }
+ ),
+ 'initial configuration',
+ )
+ self.assertEqual(self.get()['status'], 200)
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"pass": "applications/targets/blah"}, 'listeners/*:7080'
+ ),
+ 'invalid targets pass',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ '"' + self.current_dir + '/php/targets\"',
+ 'applications/targets/root',
+ ),
+ 'invalid root',
+ )
+ self.assertIn(
+ 'error',
+ self.conf('"index.php"', 'applications/targets/index'),
+ 'invalid index',
+ )
+ self.assertIn(
+ 'error',
+ self.conf('"index.php"', 'applications/targets/script'),
+ 'invalid script',
+ )
+ self.assertIn(
+ 'error',
+ self.conf_delete('applications/targets/default/root'),
+ 'root remove',
+ )
+
+
+if __name__ == '__main__':
+ TestPHPTargets.main()
diff --git a/test/test_proxy.py b/test/test_proxy.py
index 74bd0873..feec1ac4 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -1,12 +1,13 @@
import re
-import time
import socket
+import time
import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestProxy(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
SERVER_PORT = 7999
@@ -521,88 +522,35 @@ Content-Length: 10
self.assertEqual(len(resp['body']), 10, 'body gt Content-Length 15')
def test_proxy_invalid(self):
- self.assertIn(
- 'error',
- self.conf([{"action": {"proxy": 'blah'}}], 'routes'),
- 'proxy invalid',
- )
- self.assertIn(
- 'error',
- self.conf([{"action": {"proxy": '/blah'}}], 'routes'),
- 'proxy invalid 2',
- )
- self.assertIn(
- 'error',
- self.conf([{"action": {"proxy": 'unix:/blah'}}], 'routes'),
- 'proxy unix invalid 2',
- )
- self.assertIn(
- 'error',
- self.conf([{"action": {"proxy": 'http://blah'}}], 'routes'),
- 'proxy unix invalid 3',
- )
- self.assertIn(
- 'error',
- self.conf([{"action": {"proxy": 'http://127.0.0.1'}}], 'routes'),
- 'proxy ipv4 invalid',
- )
- self.assertIn(
- 'error',
- self.conf([{"action": {"proxy": 'http://127.0.0.1:'}}], 'routes'),
- 'proxy ipv4 invalid 2',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://127.0.0.1:blah'}}], 'routes'
- ),
- 'proxy ipv4 invalid 3',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://127.0.0.1:-1'}}], 'routes'
- ),
- 'proxy ipv4 invalid 4',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://127.0.0.1:7080b'}}], 'routes'
- ),
- 'proxy ipv4 invalid 5',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://[]'}}], 'routes'
- ),
- 'proxy ipv6 invalid',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://[]:7080'}}], 'routes'
- ),
- 'proxy ipv6 invalid 2',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://[:]:7080'}}], 'routes'
- ),
- 'proxy ipv6 invalid 3',
- )
- self.assertIn(
- 'error',
- self.conf(
- [{"action": {"proxy": 'http://[::7080'}}], 'routes'
- ),
- 'proxy ipv6 invalid 4',
- )
+ def check_proxy(proxy):
+ self.assertIn(
+ 'error',
+ self.conf([{"action": {"proxy": proxy}}], 'routes'),
+ 'proxy invalid',
+ )
+
+ check_proxy('blah')
+ check_proxy('/blah')
+ check_proxy('unix:/blah')
+ check_proxy('http://blah')
+ check_proxy('http://127.0.0.1')
+ check_proxy('http://127.0.0.1:')
+ check_proxy('http://127.0.0.1:blah')
+ check_proxy('http://127.0.0.1:-1')
+ check_proxy('http://127.0.0.1:7080b')
+ check_proxy('http://[]')
+ check_proxy('http://[]:7080')
+ check_proxy('http://[:]:7080')
+ check_proxy('http://[::7080')
- @unittest.skip('not yet')
def test_proxy_loop(self):
+ self.skip_alerts.extend(
+ [
+ r'socket.*failed',
+ r'accept.*failed',
+ r'new connections are not accepted',
+ ]
+ )
self.conf(
{
"listeners": {
@@ -625,6 +573,7 @@ Content-Length: 10
)
self.get_http10(no_recv=True)
+ self.get_http10(read_timeout=1)
if __name__ == '__main__':
TestProxy.main()
diff --git a/test/test_python_application.py b/test/test_python_application.py
index 8d435b48..8bd3f750 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -1,14 +1,14 @@
-import re
-import os
import grp
import pwd
+import re
import time
import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestPythonApplication(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'all'}}
def findall(self, pattern):
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index 3233fca2..d6445ac2 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -2,7 +2,7 @@ from unit.control import TestControl
class TestPythonBasic(TestControl):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
conf_app = {
"app": {
diff --git a/test/test_python_environment.py b/test/test_python_environment.py
index f808f795..a03b96e6 100644
--- a/test/test_python_environment.py
+++ b/test/test_python_environment.py
@@ -2,7 +2,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestPythonEnvironment(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def test_python_environment_name_null(self):
self.load('environment')
diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py
new file mode 100644
index 00000000..1bed64ba
--- /dev/null
+++ b/test/test_python_isolation.py
@@ -0,0 +1,79 @@
+import unittest
+
+from unit.applications.lang.python import TestApplicationPython
+from unit.feature.isolation import TestFeatureIsolation
+
+
+class TestPythonIsolation(TestApplicationPython):
+ prerequisites = {'modules': {'python': 'any'}, 'features': ['isolation']}
+
+ isolation = TestFeatureIsolation()
+
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
+
+ TestFeatureIsolation().check(cls.available, unit.testdir)
+
+ return unit if not complete_check else unit.complete()
+
+ def test_python_isolation_rootfs(self):
+ isolation_features = self.available['features']['isolation'].keys()
+
+ if 'mnt' not in isolation_features:
+ print('requires mnt ns')
+ raise unittest.SkipTest()
+
+ if not self.is_su:
+ if 'user' not in isolation_features:
+ print('requires unprivileged userns or root')
+ raise unittest.SkipTest()
+
+ if not 'unprivileged_userns_clone' in isolation_features:
+ print('requires unprivileged userns or root')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'namespaces': {'credential': not self.is_su, 'mount': True},
+ 'rootfs': self.testdir,
+ }
+
+ self.load('empty', isolation=isolation)
+
+ self.assertEqual(self.get()['status'], 200, 'python rootfs')
+
+ self.load('ns_inspect', isolation=isolation)
+
+ self.assertEqual(
+ self.getjson(url='/?path=' + self.testdir)['body']['FileExists'],
+ False,
+ 'testdir does not exists in rootfs',
+ )
+
+ self.assertEqual(
+ self.getjson(url='/?path=/proc/self')['body']['FileExists'],
+ False,
+ 'no /proc/self',
+ )
+
+ self.assertEqual(
+ self.getjson(url='/?path=/dev/pts')['body']['FileExists'],
+ False,
+ 'no /dev/pts',
+ )
+
+ self.assertEqual(
+ self.getjson(url='/?path=/sys/kernel')['body']['FileExists'],
+ False,
+ 'no /sys/kernel',
+ )
+
+ ret = self.getjson(url='/?path=/app/python/ns_inspect')
+
+ self.assertEqual(
+ ret['body']['FileExists'], True, 'application exists in rootfs',
+ )
+
+
+if __name__ == '__main__':
+ TestPythonIsolation.main()
diff --git a/test/test_python_isolation_chroot.py b/test/test_python_isolation_chroot.py
new file mode 100644
index 00000000..7761128e
--- /dev/null
+++ b/test/test_python_isolation_chroot.py
@@ -0,0 +1,57 @@
+import unittest
+
+from unit.applications.lang.python import TestApplicationPython
+from unit.feature.isolation import TestFeatureIsolation
+
+
+class TestPythonIsolation(TestApplicationPython):
+ prerequisites = {'modules': {'python': 'any'}}
+
+ def test_python_isolation_chroot(self):
+ if not self.is_su:
+ print('requires root')
+ raise unittest.SkipTest()
+
+ isolation = {
+ 'rootfs': self.testdir,
+ }
+
+ self.load('empty', isolation=isolation)
+
+ self.assertEqual(self.get()['status'], 200, 'python chroot')
+
+ self.load('ns_inspect', isolation=isolation)
+
+ self.assertEqual(
+ self.getjson(url='/?path=' + self.testdir)['body']['FileExists'],
+ False,
+ 'testdir does not exists in rootfs',
+ )
+
+ self.assertEqual(
+ self.getjson(url='/?path=/proc/self')['body']['FileExists'],
+ False,
+ 'no /proc/self',
+ )
+
+ self.assertEqual(
+ self.getjson(url='/?path=/dev/pts')['body']['FileExists'],
+ False,
+ 'no /dev/pts',
+ )
+
+ self.assertEqual(
+ self.getjson(url='/?path=/sys/kernel')['body']['FileExists'],
+ False,
+ 'no /sys/kernel',
+ )
+
+ ret = self.getjson(url='/?path=/app/python/ns_inspect')
+
+ self.assertEqual(
+ ret['body']['FileExists'], True, 'application exists in rootfs',
+ )
+
+
+if __name__ == '__main__':
+ TestPythonIsolation.main()
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index a2e6126c..8613f58e 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -1,12 +1,13 @@
import re
-import time
import subprocess
+import time
import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestPythonProcman(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def setUp(self):
super().setUp()
diff --git a/test/test_respawn.py b/test/test_respawn.py
new file mode 100644
index 00000000..f1c71a20
--- /dev/null
+++ b/test/test_respawn.py
@@ -0,0 +1,95 @@
+import re
+import subprocess
+import time
+
+from unit.applications.lang.python import TestApplicationPython
+
+
+class TestRespawn(TestApplicationPython):
+ prerequisites = {'modules': {'python': 'any'}}
+
+ PATTERN_ROUTER = 'unit: router'
+ PATTERN_CONTROLLER = 'unit: controller'
+
+ def setUp(self):
+ super().setUp()
+
+ self.app_name = "app-" + self.testdir.split('/')[-1]
+
+ self.load('empty', self.app_name)
+
+ self.assertIn(
+ 'success',
+ self.conf('1', 'applications/' + self.app_name + '/processes')
+ )
+
+ def pid_by_name(self, name):
+ output = subprocess.check_output(['ps', 'ax']).decode()
+ m = re.search('\s*(\d+).*' + name, output)
+ return m if m is None else m.group(1)
+
+ def kill_pids(self, *pids):
+ subprocess.call(['kill', '-9'] + list(pids))
+
+ def wait_for_process(self, process):
+ for i in range(50):
+ found = self.pid_by_name(process)
+
+ if found is not None:
+ break
+
+ time.sleep(0.1)
+
+ return found
+
+ def smoke_test(self):
+ for _ in range(5):
+ self.assertIn(
+ 'success',
+ self.conf('1', 'applications/' + self.app_name + '/processes')
+ )
+ self.assertEqual(self.get()['status'], 200)
+
+ # Check if the only one router, controller,
+ # and application processes running.
+
+ output = subprocess.check_output(['ps', 'ax']).decode()
+ self.assertEqual(len(re.findall(self.PATTERN_ROUTER, output)), 1)
+ self.assertEqual(len(re.findall(self.PATTERN_CONTROLLER, output)), 1)
+ self.assertEqual(len(re.findall(self.app_name, output)), 1)
+
+ def test_respawn_router(self):
+ pid = self.pid_by_name(self.PATTERN_ROUTER)
+
+ self.kill_pids(pid)
+ self.skip_alerts.append(r'process %s exited on signal 9' % pid)
+
+ self.assertIsNotNone(self.wait_for_process(self.PATTERN_ROUTER))
+
+ self.smoke_test()
+
+ def test_respawn_controller(self):
+ pid = self.pid_by_name(self.PATTERN_CONTROLLER)
+
+ self.kill_pids(pid)
+ self.skip_alerts.append(r'process %s exited on signal 9' % pid)
+
+ self.assertIsNotNone(self.wait_for_process(self.PATTERN_CONTROLLER))
+
+ self.assertEqual(self.get()['status'], 200)
+
+ self.smoke_test()
+
+ def test_respawn_application(self):
+ pid = self.pid_by_name(self.app_name)
+
+ self.kill_pids(pid)
+ self.skip_alerts.append(r'process %s exited on signal 9' % pid)
+
+ self.assertIsNotNone(self.wait_for_process(self.app_name))
+
+ self.smoke_test()
+
+
+if __name__ == '__main__':
+ TestRespawn.main()
diff --git a/test/test_return.py b/test/test_return.py
index fcb51745..a89d97e6 100644
--- a/test/test_return.py
+++ b/test/test_return.py
@@ -1,5 +1,5 @@
import re
-import unittest
+
from unit.applications.proto import TestApplicationProto
diff --git a/test/test_routing.py b/test/test_routing.py
index ad793662..3cf4009c 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -1,9 +1,11 @@
+# -*- coding: utf-8 -*-
import unittest
+
from unit.applications.proto import TestApplicationProto
class TestRouting(TestApplicationProto):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def setUp(self):
super().setUp()
@@ -179,6 +181,61 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get(url='/blah')['status'], 200, '/blah')
self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH')
+ def test_routes_pass_encode(self):
+ def check_pass(path, name):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "applications/" + path}
+ },
+ "applications": {
+ name: {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + '/python/empty',
+ "working_directory": self.current_dir
+ + '/python/empty',
+ "module": "wsgi",
+ }
+ },
+ }
+ ),
+ )
+
+ self.assertEqual(self.get()['status'], 200)
+
+ check_pass("%25", "%")
+ check_pass("blah%2Fblah", "blah/blah")
+ check_pass("%2Fblah%2F%2Fblah%2F", "/blah//blah/")
+ check_pass("%20blah%252Fblah%7E", " blah%2Fblah~")
+
+ def check_pass_error(path, name):
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "applications/" + path}
+ },
+ "applications": {
+ name: {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + '/python/empty',
+ "working_directory": self.current_dir
+ + '/python/empty',
+ "module": "wsgi",
+ }
+ },
+ }
+ ),
+ )
+
+ check_pass_error("%", "%")
+ check_pass_error("%1", "%1")
+
def test_routes_absent(self):
self.conf(
{
@@ -1069,6 +1126,33 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get(url='/?Foo=bar')['status'], 404, 'case')
self.assertEqual(self.get(url='/?foo=Bar')['status'], 404, 'case 2')
+ def test_routes_match_arguments_chars(self):
+ chars = (
+ " !\"%23$%25%26'()*%2B,-./0123456789:;<%3D>?@"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ )
+
+ chars_enc = ""
+ for h1 in ["2", "3", "4", "5", "6", "7"]:
+ for h2 in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
+ "B", "C", "D", "E", "F",
+ ]:
+ chars_enc += "%" + h1 + h2
+ chars_enc = chars_enc[:-3]
+
+ def check_args(args, query):
+ self.route_match({"arguments": args})
+ self.assertEqual(self.get(url='/?' + query)['status'], 200)
+
+ check_args({chars: chars}, chars + '=' + chars)
+ check_args({chars: chars}, chars + '=' + chars_enc)
+ check_args({chars: chars}, chars_enc + '=' + chars)
+ check_args({chars: chars}, chars_enc + '=' + chars_enc)
+ check_args({chars_enc: chars_enc}, chars + '=' + chars)
+ check_args({chars_enc: chars_enc}, chars + '=' + chars_enc)
+ check_args({chars_enc: chars_enc}, chars_enc + '=' + chars)
+ check_args({chars_enc: chars_enc}, chars_enc + '=' + chars_enc)
+
def test_routes_match_arguments_empty(self):
self.route_match({"arguments": {}})
self.assertEqual(self.get()['status'], 200, 'arguments empty')
@@ -1076,43 +1160,113 @@ class TestRouting(TestApplicationProto):
self.route_match({"arguments": []})
self.assertEqual(self.get()['status'], 200, 'arguments empty 2')
- def test_routes_match_arguments_invalid(self):
- self.route_match_invalid({"arguments": ["var"]})
- self.route_match_invalid({"arguments": [{"var1": {}}]})
- self.route_match_invalid({"arguments": {"": "bar"}})
-
- @unittest.skip('not yet')
def test_routes_match_arguments_space(self):
- self.route_match({"arguments": {"foo": "bar "}})
-
- self.assertEqual(self.get(url='/?foo=bar &')['status'], 200, 'sp')
- # FAIL
- self.assertEqual(self.get(url='/?foo=bar+&')['status'], 200, 'sp 2')
- # FAIL
- self.assertEqual(self.get(url='/?foo=bar%20&')['status'], 200, 'sp 3')
-
- @unittest.skip('not yet')
- def test_routes_match_arguments_plus(self):
- self.route_match({"arguments": [{"foo": "bar+"}]})
-
- self.assertEqual(self.get(url='/?foo=bar+&')['status'], 200, 'plus')
- # FAIL
+ self.route_match({"arguments": {"+fo o%20": "%20b+a r"}})
+ self.assertEqual(self.get(url='/? fo o = b a r&')['status'], 200)
+ self.assertEqual(self.get(url='/?+fo+o+=+b+a+r&')['status'], 200)
self.assertEqual(
- self.get(url='/?foo=bar%2B&')['status'], 200, 'plus 2'
- )
-
- @unittest.skip('not yet')
- def test_routes_match_arguments_hex(self):
- self.route_match({"arguments": [{"foo": "bar"}]})
-
- self.assertEqual(
- self.get(url='/?%66%6F%6f=%62%61%72&')['status'], 200, 'hex'
- )
-
- def test_routes_match_arguments_chars(self):
- self.route_match({"arguments": {"foo": "-._()[],;"}})
-
- self.assertEqual(self.get(url='/?foo=-._()[],;')['status'], 200, 'chs')
+ self.get(url='/?%20fo%20o%20=%20b%20a%20r&')['status'], 200
+ )
+
+ self.route_match({"arguments": {"%20foo": " bar"}})
+ self.assertEqual(self.get(url='/? foo= bar')['status'], 200)
+ self.assertEqual(self.get(url='/?+foo=+bar')['status'], 200)
+ self.assertEqual(self.get(url='/?%20foo=%20bar')['status'], 200)
+ self.assertEqual(self.get(url='/?+foo= bar')['status'], 200)
+ self.assertEqual(self.get(url='/?%20foo=+bar')['status'], 200)
+
+ def test_routes_match_arguments_equal(self):
+ self.route_match({"arguments": {"=": "="}})
+ self.assertEqual(self.get(url='/?%3D=%3D')['status'], 200)
+ self.assertEqual(self.get(url='/?%3D==')['status'], 200)
+ self.assertEqual(self.get(url='/?===')['status'], 404)
+ self.assertEqual(self.get(url='/?%3D%3D%3D')['status'], 404)
+ self.assertEqual(self.get(url='/?==%3D')['status'], 404)
+
+ def test_routes_match_arguments_enc(self):
+ self.route_match({"arguments": {"Ю": "н"}})
+ self.assertEqual(self.get(url='/?%D0%AE=%D0%BD')['status'], 200)
+ self.assertEqual(self.get(url='/?%d0%ae=%d0%Bd')['status'], 200)
+
+ def test_routes_match_arguments_hash(self):
+ self.route_match({"arguments": {"#": "#"}})
+ self.assertEqual(self.get(url='/?%23=%23')['status'], 200)
+ self.assertEqual(self.get(url='/?%23=%23#')['status'], 200)
+ self.assertEqual(self.get(url='/?#=#')['status'], 404)
+ self.assertEqual(self.get(url='/?%23=#')['status'], 404)
+
+ def test_routes_match_arguments_wildcard(self):
+ self.route_match({"arguments": {"foo": "*"}})
+ self.assertEqual(self.get(url='/?foo')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=blah')['status'], 200)
+ self.assertEqual(self.get(url='/?blah=foo')['status'], 404)
+
+ self.route_match({"arguments": {"foo": "%25*"}})
+ self.assertEqual(self.get(url='/?foo=%xx')['status'], 200)
+
+ self.route_match({"arguments": {"foo": "%2A*"}})
+ self.assertEqual(self.get(url='/?foo=*xx')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=xx')['status'], 404)
+
+ self.route_match({"arguments": {"foo": "*%2A"}})
+ self.assertEqual(self.get(url='/?foo=xx*')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=xx*x')['status'], 404)
+
+ self.route_match({"arguments": {"foo": "1*2"}})
+ self.assertEqual(self.get(url='/?foo=12')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=1blah2')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=1%2A2')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=x12')['status'], 404)
+
+ self.route_match({"arguments": {"foo": "bar*", "%25": "%25"}})
+ self.assertEqual(self.get(url='/?foo=barxx&%=%')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=barxx&x%=%')['status'], 404)
+
+ def test_routes_match_arguments_negative(self):
+ self.route_match({"arguments": {"foo": "!%25"}})
+ self.assertEqual(self.get(url='/?foo=blah')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=%')['status'], 404)
+
+ self.route_match({"arguments": {"foo": "%21blah"}})
+ self.assertEqual(self.get(url='/?foo=%21blah')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=!blah')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=bar')['status'], 404)
+
+ self.route_match({"arguments": {"foo": "!!%21*a"}})
+ self.assertEqual(self.get(url='/?foo=blah')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=!blah')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=!!a')['status'], 404)
+ self.assertEqual(self.get(url='/?foo=!!bla')['status'], 404)
+
+ def test_routes_match_arguments_percent(self):
+ self.route_match({"arguments": {"%25": "%25"}})
+ self.assertEqual(self.get(url='/?%=%')['status'], 200)
+ self.assertEqual(self.get(url='/?%25=%25')['status'], 200)
+ self.assertEqual(self.get(url='/?%25=%')['status'], 200)
+
+ self.route_match({"arguments": {"%251": "%252"}})
+ self.assertEqual(self.get(url='/?%1=%2')['status'], 200)
+ self.assertEqual(self.get(url='/?%251=%252')['status'], 200)
+ self.assertEqual(self.get(url='/?%251=%2')['status'], 200)
+
+ self.route_match({"arguments": {"%25%21%251": "%25%24%252"}})
+ self.assertEqual(self.get(url='/?%!%1=%$%2')['status'], 200)
+ self.assertEqual(self.get(url='/?%25!%251=%25$%252')['status'], 200)
+ self.assertEqual(self.get(url='/?%25!%1=%$%2')['status'], 200)
+
+ def test_routes_match_arguments_ampersand(self):
+ self.route_match({"arguments": {"foo": "&"}})
+ self.assertEqual(self.get(url='/?foo=%26')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=%26&')['status'], 200)
+ self.assertEqual(self.get(url='/?foo=%26%26')['status'], 404)
+ self.assertEqual(self.get(url='/?foo=&')['status'], 404)
+
+ self.route_match({"arguments": {"&": ""}})
+ self.assertEqual(self.get(url='/?%26=')['status'], 200)
+ self.assertEqual(self.get(url='/?%26=&')['status'], 200)
+ self.assertEqual(self.get(url='/?%26=%26')['status'], 404)
+ self.assertEqual(self.get(url='/?&=')['status'], 404)
def test_routes_match_arguments_complex(self):
self.route_match({"arguments": {"foo": ""}})
@@ -1147,6 +1301,14 @@ class TestRouting(TestApplicationProto):
self.assertEqual(
self.get(url='/?foo=bar&blah')['status'], 404, 'multiple 3'
)
+ self.assertEqual(
+ self.get(url='/?foo=bar&blah=tes')['status'], 404, 'multiple 4'
+ )
+ self.assertEqual(
+ self.get(url='/?foo=b%61r&bl%61h=t%65st')['status'],
+ 200,
+ 'multiple 5',
+ )
def test_routes_match_arguments_multiple_rules(self):
self.route_match({"arguments": {"foo": ["bar", "blah"]}})
@@ -1193,6 +1355,22 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get(url='/?var2=val2')['status'], 404, 'arr 7')
self.assertEqual(self.get(url='/?var3=foo')['status'], 200, 'arr 8')
+ def test_routes_match_arguments_invalid(self):
+ # TODO remove it after controller fixed
+ self.skip_alerts.append(r'failed to apply new conf')
+
+ self.route_match_invalid({"arguments": ["var"]})
+ self.route_match_invalid({"arguments": [{"var1": {}}]})
+ self.route_match_invalid({"arguments": {"": "bar"}})
+ self.route_match_invalid({"arguments": {"foo": "*ba*r"}})
+ self.route_match_invalid({"arguments": {"foo": "%"}})
+ self.route_match_invalid({"arguments": {"foo": "%1G"}})
+ self.route_match_invalid({"arguments": {"%": "bar"}})
+ self.route_match_invalid({"arguments": {"foo": "%0"}})
+ self.route_match_invalid({"arguments": {"foo": "%%1F"}})
+ self.route_match_invalid({"arguments": {"%%1F": ""}})
+ self.route_match_invalid({"arguments": {"%7%F": ""}})
+
def test_routes_match_cookies(self):
self.route_match({"cookies": {"foO": "bar"}})
@@ -1748,5 +1926,6 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get()['status'], 200, 'proxy')
+
if __name__ == '__main__':
TestRouting.main()
diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py
index 36bd9057..a9b8f88d 100644
--- a/test/test_routing_tls.py
+++ b/test/test_routing_tls.py
@@ -2,7 +2,7 @@ from unit.applications.tls import TestApplicationTLS
class TestRoutingTLS(TestApplicationTLS):
- prerequisites = {'modules': ['openssl']}
+ prerequisites = {'modules': {'openssl': 'any'}}
def test_routes_match_scheme_tls(self):
self.certificate()
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index bdaabe51..4709df6c 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -1,9 +1,10 @@
import unittest
+
from unit.applications.lang.ruby import TestApplicationRuby
class TestRubyApplication(TestApplicationRuby):
- prerequisites = {'modules': ['ruby']}
+ prerequisites = {'modules': {'ruby': 'all'}}
def test_ruby_application(self):
self.load('variables')
diff --git a/test/test_ruby_isolation.py b/test/test_ruby_isolation.py
new file mode 100644
index 00000000..9bac162e
--- /dev/null
+++ b/test/test_ruby_isolation.py
@@ -0,0 +1,71 @@
+import os
+import shutil
+import unittest
+
+from unit.applications.lang.ruby import TestApplicationRuby
+from unit.feature.isolation import TestFeatureIsolation
+
+
+class TestRubyIsolation(TestApplicationRuby):
+ prerequisites = {'modules': {'ruby': 'any'}, 'features': ['isolation']}
+
+ isolation = TestFeatureIsolation()
+
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
+
+ TestFeatureIsolation().check(cls.available, unit.testdir)
+
+ return unit if not complete_check else unit.complete()
+
+ def test_ruby_isolation_rootfs(self):
+ isolation_features = self.available['features']['isolation'].keys()
+
+ if 'mnt' not in isolation_features:
+ print('requires mnt ns')
+ raise unittest.SkipTest()
+
+ if not self.is_su:
+ if 'user' not in isolation_features:
+ print('requires unprivileged userns or root')
+ raise unittest.SkipTest()
+
+ if not 'unprivileged_userns_clone' in isolation_features:
+ print('requires unprivileged userns or root')
+ raise unittest.SkipTest()
+
+ os.mkdir(self.testdir + '/ruby')
+
+ shutil.copytree(
+ self.current_dir + '/ruby/status_int',
+ self.testdir + '/ruby/status_int',
+ )
+ isolation = {
+ 'namespaces': {'credential': not self.is_su, 'mount': True},
+ 'rootfs': self.testdir,
+ }
+
+ self.load('status_int', isolation=isolation)
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"/ruby/status_int/config.ru"',
+ 'applications/status_int/script',
+ ),
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"/ruby/status_int"',
+ 'applications/status_int/working_directory',
+ ),
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'status int')
+
+
+if __name__ == '__main__':
+ TestRubyIsolation.main()
diff --git a/test/test_settings.py b/test/test_settings.py
index 9de3a928..6600358d 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -1,11 +1,12 @@
-import time
import socket
+import time
import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestSettings(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def test_settings_header_read_timeout(self):
self.load('empty')
diff --git a/test/test_share_fallback.py b/test/test_share_fallback.py
index c51e43ee..ca5e2678 100644
--- a/test/test_share_fallback.py
+++ b/test/test_share_fallback.py
@@ -1,5 +1,5 @@
import os
-import unittest
+
from unit.applications.proto import TestApplicationProto
@@ -125,18 +125,23 @@ class TestStatic(TestApplicationProto):
self.assertEqual(resp['status'], 200, 'fallback proxy status')
self.assertEqual(resp['body'], '', 'fallback proxy')
- @unittest.skip('not yet')
- def test_fallback_proxy_cycle(self):
+ def test_fallback_proxy_loop(self):
+ self.skip_alerts.extend(
+ [
+ r'open.*/blah/index.html.*failed',
+ r'accept.*failed',
+ r'socket.*failed',
+ r'new connections are not accepted',
+ ]
+ )
+
self.action_update(
- {
- "share": "/blah",
- "fallback": {"proxy": "http://127.0.0.1:7080"},
- }
+ {"share": "/blah", "fallback": {"proxy": "http://127.0.0.1:7080"}}
)
- self.assertNotEqual(self.get()['status'], 200, 'fallback cycle')
+ self.get(no_recv=True)
self.assertIn('success', self.conf_delete('listeners/*:7081'))
- self.assertNotEqual(self.get()['status'], 200, 'fallback cycle 2')
+ self.get(read_timeout=1)
def test_fallback_invalid(self):
def check_error(conf):
diff --git a/test/test_static.py b/test/test_static.py
index b2489aa0..bee5db28 100644
--- a/test/test_static.py
+++ b/test/test_static.py
@@ -1,6 +1,7 @@
import os
import socket
import unittest
+
from unit.applications.proto import TestApplicationProto
diff --git a/test/test_tls.py b/test/test_tls.py
index d9dcf237..a0434174 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -1,14 +1,14 @@
import io
-import os
import re
import ssl
import subprocess
import unittest
+
from unit.applications.tls import TestApplicationTLS
class TestTLS(TestApplicationTLS):
- prerequisites = {'modules': ['python', 'openssl']}
+ prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
def findall(self, pattern):
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py
index 7045318a..2f74fbde 100644
--- a/test/test_upstreams_rr.py
+++ b/test/test_upstreams_rr.py
@@ -1,11 +1,11 @@
import os
import re
-import unittest
+
from unit.applications.lang.python import TestApplicationPython
class TestUpstreamsRR(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def setUp(self):
super().setUp()
diff --git a/test/test_usr1.py b/test/test_usr1.py
index 155303ea..d1db652f 100644
--- a/test/test_usr1.py
+++ b/test/test_usr1.py
@@ -1,11 +1,11 @@
import os
-import unittest
from subprocess import call
+
from unit.applications.lang.python import TestApplicationPython
class TestUSR1(TestApplicationPython):
- prerequisites = {'modules': ['python']}
+ prerequisites = {'modules': {'python': 'any'}}
def test_usr1_access_log(self):
self.load('empty')
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
index e0f83c0a..83bde4d8 100644
--- a/test/unit/applications/lang/go.py
+++ b/test/unit/applications/lang/go.py
@@ -1,5 +1,6 @@
import os
import subprocess
+
from unit.applications.proto import TestApplicationProto
@@ -18,26 +19,36 @@ class TestApplicationGo(TestApplicationProto):
return unit if not complete_check else unit.complete()
- def prepare_env(self, script, name):
+ def prepare_env(self, script, name, static=False):
if not os.path.exists(self.testdir + '/go'):
os.mkdir(self.testdir + '/go')
env = os.environ.copy()
env['GOPATH'] = self.pardir + '/build/go'
- try:
- process = subprocess.Popen(
- [
- 'go',
- 'build',
- '-o',
- self.testdir + '/go/' + name,
- self.current_dir + '/go/' + script + '/' + name + '.go',
- ],
- env=env,
- stderr=subprocess.STDOUT,
- )
+ if static:
+ args = [
+ 'go',
+ 'build',
+ '-tags',
+ 'netgo',
+ '-ldflags',
+ '-extldflags "-static"',
+ '-o',
+ self.testdir + '/go/' + name,
+ self.current_dir + '/go/' + script + '/' + name + '.go',
+ ]
+ else:
+ args = [
+ 'go',
+ 'build',
+ '-o',
+ self.testdir + '/go/' + name,
+ self.current_dir + '/go/' + script + '/' + name + '.go',
+ ]
+ try:
+ process = subprocess.Popen(args, env=env)
process.communicate()
except:
@@ -46,21 +57,28 @@ class TestApplicationGo(TestApplicationProto):
return process
def load(self, script, name='app', **kwargs):
- self.prepare_env(script, name)
-
- self._load_conf(
- {
- "listeners": {"*:7080": {"pass": "applications/" + script}},
- "applications": {
- script: {
- "type": "external",
- "processes": {"spare": 0},
- "working_directory": self.current_dir
- + "/go/"
- + script,
- "executable": self.testdir + "/go/" + name,
- }
+ static_build = False
+
+ wdir = self.current_dir + "/go/" + script
+ executable = self.testdir + "/go/" + name
+
+ if 'isolation' in kwargs and 'rootfs' in kwargs['isolation']:
+ wdir = "/go/"
+ executable = "/go/" + name
+ static_build = True
+
+ self.prepare_env(script, name, static=static_build)
+
+ conf = {
+ "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "applications": {
+ script: {
+ "type": "external",
+ "processes": {"spare": 0},
+ "working_directory": wdir,
+ "executable": executable,
},
},
- **kwargs
- )
+ }
+
+ self._load_conf(conf, **kwargs)
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index a8a09ce5..c2c6dc51 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -1,7 +1,8 @@
-import os
import glob
+import os
import shutil
import subprocess
+
from unit.applications.proto import TestApplicationProto
diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py
index d818298f..cf2a99f6 100644
--- a/test/unit/applications/lang/node.py
+++ b/test/unit/applications/lang/node.py
@@ -1,5 +1,7 @@
import os
import shutil
+from urllib.parse import quote
+
from unit.applications.proto import TestApplicationProto
@@ -33,7 +35,9 @@ class TestApplicationNode(TestApplicationProto):
self._load_conf(
{
- "listeners": {"*:7080": {"pass": "applications/" + script}},
+ "listeners": {
+ "*:7080": {"pass": "applications/" + quote(script, '')}
+ },
"applications": {
script: {
"type": "external",
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index fdda024a..31a04107 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -1,3 +1,6 @@
+import shutil
+import os
+
from unit.applications.proto import TestApplicationProto
@@ -8,7 +11,21 @@ class TestApplicationPython(TestApplicationProto):
if name is None:
name = script
- script_path = self.current_dir + '/python/' + script
+ if script[0] == '/':
+ script_path = script
+ else:
+ script_path = self.current_dir + '/python/' + script
+
+ if kwargs.get('isolation') and kwargs['isolation'].get('rootfs'):
+ rootfs = kwargs['isolation']['rootfs']
+
+ if not os.path.exists(rootfs + '/app/python/'):
+ os.makedirs(rootfs + '/app/python/')
+
+ if not os.path.exists(rootfs + '/app/python/' + name):
+ shutil.copytree(script_path, rootfs + '/app/python/' + name)
+
+ script_path = '/app/python/' + name
self._load_conf(
{
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
index ae1af354..244cb5be 100644
--- a/test/unit/applications/proto.py
+++ b/test/unit/applications/proto.py
@@ -1,5 +1,6 @@
import re
import time
+
from unit.control import TestControl
diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py
index 9213974a..e6a846b2 100644
--- a/test/unit/applications/tls.py
+++ b/test/unit/applications/tls.py
@@ -2,6 +2,7 @@ import os
import re
import ssl
import subprocess
+
from unit.applications.proto import TestApplicationProto
diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py
index fc15e8e4..e0dd2c0d 100644
--- a/test/unit/applications/websockets.py
+++ b/test/unit/applications/websockets.py
@@ -1,10 +1,11 @@
-import re
-import random
import base64
-import struct
-import select
import hashlib
import itertools
+import random
+import re
+import select
+import struct
+
from unit.applications.proto import TestApplicationProto
GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
diff --git a/test/unit/control.py b/test/unit/control.py
index 0b344ed5..029072b5 100644
--- a/test/unit/control.py
+++ b/test/unit/control.py
@@ -1,4 +1,5 @@
import json
+
from unit.http import TestHTTP
diff --git a/test/unit/feature/isolation.py b/test/unit/feature/isolation.py
index 3f474993..4f33d04a 100644
--- a/test/unit/feature/isolation.py
+++ b/test/unit/feature/isolation.py
@@ -1,6 +1,5 @@
import os
-import json
-from unit.applications.proto import TestApplicationProto
+
from unit.applications.lang.go import TestApplicationGo
from unit.applications.lang.java import TestApplicationJava
from unit.applications.lang.node import TestApplicationNode
@@ -8,6 +7,7 @@ from unit.applications.lang.perl import TestApplicationPerl
from unit.applications.lang.php import TestApplicationPHP
from unit.applications.lang.python import TestApplicationPython
from unit.applications.lang.ruby import TestApplicationRuby
+from unit.applications.proto import TestApplicationProto
class TestFeatureIsolation(TestApplicationProto):
diff --git a/test/unit/http.py b/test/unit/http.py
index 13384dc8..de3bb2a4 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -1,11 +1,12 @@
import binascii
import io
+import json
import os
import re
-import time
-import json
-import socket
import select
+import socket
+import time
+
from unit.main import TestUnit
diff --git a/test/unit/main.py b/test/unit/main.py
index 4507f71a..408cf31c 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -1,17 +1,17 @@
+import argparse
+import atexit
+import fcntl
import os
+import platform
import re
-import sys
-import stat
-import time
-import fcntl
-import atexit
import shutil
import signal
-import argparse
-import platform
+import stat
+import subprocess
+import sys
import tempfile
+import time
import unittest
-import subprocess
from multiprocessing import Process
@@ -52,9 +52,22 @@ class TestUnit(unittest.TestCase):
type = self.application_type
for module in self.prerequisites['modules']:
if module in self.available['modules']:
- for version in self.available['modules'][module]:
- self.application_type = type + ' ' + version
+ prereq_version = self.prerequisites['modules'][module]
+ available_versions = self.available['modules'][module]
+
+ if prereq_version == 'all':
+ for version in available_versions:
+ self.application_type = type + ' ' + version
+ self.application_version = version
+ super().run(result)
+ elif prereq_version == 'any':
+ self.application_type = type + ' ' + available_versions[0]
super().run(result)
+ else:
+ for version in available_versions:
+ if version.startswith(prereq_version):
+ self.application_type = type + ' ' + version
+ super().run(result)
@classmethod
def main(cls):
@@ -90,7 +103,7 @@ class TestUnit(unittest.TestCase):
break
if m is None:
- unit.stop()
+ unit._print_log()
exit("Unit is writing log too long")
# discover available modules from unit.log
@@ -153,7 +166,7 @@ class TestUnit(unittest.TestCase):
self._run()
def _run(self):
- build_dir = self.pardir + '/build'
+ build_dir = os.path.join(self.pardir, 'build')
self.unitd = build_dir + '/unitd'
if not os.path.isfile(self.unitd):
@@ -186,6 +199,7 @@ class TestUnit(unittest.TestCase):
atexit.register(self.stop)
if not self.waitforfiles(self.testdir + '/control.unit.sock'):
+ self._print_log()
exit("Could not start unit")
self.skip_alerts = [
@@ -398,4 +412,3 @@ class TestUnit(unittest.TestCase):
data = f.read()
print(data)
-
diff --git a/version b/version
index b5283235..f1cf1dc6 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.17.0
-NXT_VERNUM=11700
+NXT_VERSION=1.18.0
+NXT_VERNUM=11800