summaryrefslogtreecommitdiff
path: root/sapi/fpm
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /sapi/fpm
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'sapi/fpm')
-rw-r--r--sapi/fpm/CREDITS2
-rw-r--r--sapi/fpm/LICENSE23
-rw-r--r--sapi/fpm/Makefile.frag23
-rw-r--r--sapi/fpm/config.m4657
-rw-r--r--sapi/fpm/fpm/events/devpoll.c248
-rw-r--r--sapi/fpm/fpm/events/devpoll.h29
-rw-r--r--sapi/fpm/fpm/events/epoll.c211
-rw-r--r--sapi/fpm/fpm/events/epoll.h29
-rw-r--r--sapi/fpm/fpm/events/kqueue.c208
-rw-r--r--sapi/fpm/fpm/events/kqueue.h29
-rw-r--r--sapi/fpm/fpm/events/poll.c276
-rw-r--r--sapi/fpm/fpm/events/poll.h29
-rw-r--r--sapi/fpm/fpm/events/port.c185
-rw-r--r--sapi/fpm/fpm/events/port.h29
-rw-r--r--sapi/fpm/fpm/events/select.c175
-rw-r--r--sapi/fpm/fpm/events/select.h29
-rw-r--r--sapi/fpm/fpm/fastcgi.c1111
-rw-r--r--sapi/fpm/fpm/fastcgi.h145
-rw-r--r--sapi/fpm/fpm/fpm.c123
-rw-r--r--sapi/fpm/fpm/fpm.h63
-rw-r--r--sapi/fpm/fpm/fpm_arrays.h116
-rw-r--r--sapi/fpm/fpm/fpm_atomic.h168
-rw-r--r--sapi/fpm/fpm/fpm_children.c478
-rw-r--r--sapi/fpm/fpm/fpm_children.h36
-rw-r--r--sapi/fpm/fpm/fpm_cleanup.c52
-rw-r--r--sapi/fpm/fpm/fpm_cleanup.h21
-rw-r--r--sapi/fpm/fpm/fpm_clock.c121
-rw-r--r--sapi/fpm/fpm/fpm_clock.h13
-rw-r--r--sapi/fpm/fpm/fpm_conf.c1658
-rw-r--r--sapi/fpm/fpm/fpm_conf.h106
-rw-r--r--sapi/fpm/fpm/fpm_config.h79
-rw-r--r--sapi/fpm/fpm/fpm_env.c276
-rw-r--r--sapi/fpm/fpm/fpm_env.h27
-rw-r--r--sapi/fpm/fpm/fpm_events.c533
-rw-r--r--sapi/fpm/fpm/fpm_events.h52
-rw-r--r--sapi/fpm/fpm/fpm_log.c466
-rw-r--r--sapi/fpm/fpm/fpm_log.h13
-rw-r--r--sapi/fpm/fpm/fpm_main.c2000
-rw-r--r--sapi/fpm/fpm/fpm_php.c297
-rw-r--r--sapi/fpm/fpm/fpm_php.h50
-rw-r--r--sapi/fpm/fpm/fpm_php_trace.c177
-rw-r--r--sapi/fpm/fpm/fpm_php_trace.h13
-rw-r--r--sapi/fpm/fpm/fpm_process_ctl.c539
-rw-r--r--sapi/fpm/fpm/fpm_process_ctl.h53
-rw-r--r--sapi/fpm/fpm/fpm_request.c316
-rw-r--r--sapi/fpm/fpm/fpm_request.h32
-rw-r--r--sapi/fpm/fpm/fpm_scoreboard.c331
-rw-r--r--sapi/fpm/fpm/fpm_scoreboard.h94
-rw-r--r--sapi/fpm/fpm/fpm_shm.c70
-rw-r--r--sapi/fpm/fpm/fpm_shm.h13
-rw-r--r--sapi/fpm/fpm/fpm_signals.c251
-rw-r--r--sapi/fpm/fpm/fpm_signals.h16
-rw-r--r--sapi/fpm/fpm/fpm_sockets.c477
-rw-r--r--sapi/fpm/fpm/fpm_sockets.h54
-rw-r--r--sapi/fpm/fpm/fpm_status.c477
-rw-r--r--sapi/fpm/fpm/fpm_status.h35
-rw-r--r--sapi/fpm/fpm/fpm_stdio.c301
-rw-r--r--sapi/fpm/fpm/fpm_stdio.h20
-rw-r--r--sapi/fpm/fpm/fpm_str.h29
-rw-r--r--sapi/fpm/fpm/fpm_trace.c41
-rw-r--r--sapi/fpm/fpm/fpm_trace.h17
-rw-r--r--sapi/fpm/fpm/fpm_trace_mach.c99
-rw-r--r--sapi/fpm/fpm/fpm_trace_pread.c67
-rw-r--r--sapi/fpm/fpm/fpm_trace_ptrace.c82
-rw-r--r--sapi/fpm/fpm/fpm_unix.c368
-rw-r--r--sapi/fpm/fpm/fpm_unix.h17
-rw-r--r--sapi/fpm/fpm/fpm_worker_pool.c65
-rw-r--r--sapi/fpm/fpm/fpm_worker_pool.h53
-rw-r--r--sapi/fpm/fpm/zlog.c199
-rw-r--r--sapi/fpm/fpm/zlog.h45
-rw-r--r--sapi/fpm/init.d.php-fpm.in138
-rw-r--r--sapi/fpm/php-fpm.8.in220
-rw-r--r--sapi/fpm/php-fpm.conf.in510
-rw-r--r--sapi/fpm/php-fpm.service.in12
-rw-r--r--sapi/fpm/status.html.in459
75 files changed, 15876 insertions, 0 deletions
diff --git a/sapi/fpm/CREDITS b/sapi/fpm/CREDITS
new file mode 100644
index 0000000..cd87daa
--- /dev/null
+++ b/sapi/fpm/CREDITS
@@ -0,0 +1,2 @@
+FastCGI Process Manager
+Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet
diff --git a/sapi/fpm/LICENSE b/sapi/fpm/LICENSE
new file mode 100644
index 0000000..4e1dd99
--- /dev/null
+++ b/sapi/fpm/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2007-2009, Andrei Nigmatulin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/sapi/fpm/Makefile.frag b/sapi/fpm/Makefile.frag
new file mode 100644
index 0000000..6ed9e4a
--- /dev/null
+++ b/sapi/fpm/Makefile.frag
@@ -0,0 +1,23 @@
+fpm: $(SAPI_FPM_PATH)
+
+$(SAPI_FPM_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FPM_OBJS)
+ $(BUILD_FPM)
+
+install-fpm: $(SAPI_FPM_PATH)
+ @echo "Installing PHP FPM binary: $(INSTALL_ROOT)$(sbindir)/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(sbindir)
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/run
+ @$(INSTALL) -m 0755 $(SAPI_FPM_PATH) $(INSTALL_ROOT)$(sbindir)/$(program_prefix)php-fpm$(program_suffix)$(EXEEXT)
+
+ @echo "Installing PHP FPM config: $(INSTALL_ROOT)$(sysconfdir)/" && \
+ $(mkinstalldirs) $(INSTALL_ROOT)$(sysconfdir) || :
+ @$(INSTALL_DATA) sapi/fpm/php-fpm.conf $(INSTALL_ROOT)$(sysconfdir)/php-fpm.conf.default || :
+
+ @echo "Installing PHP FPM man page: $(INSTALL_ROOT)$(mandir)/man8/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man8
+ @$(INSTALL_DATA) sapi/fpm/php-fpm.8 $(INSTALL_ROOT)$(mandir)/man8/php-fpm$(program_suffix).8
+
+ @echo "Installing PHP FPM status page: $(INSTALL_ROOT)$(datadir)/fpm/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(datadir)/fpm
+ @$(INSTALL_DATA) sapi/fpm/status.html $(INSTALL_ROOT)$(datadir)/fpm/status.html
diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4
new file mode 100644
index 0000000..6191c32
--- /dev/null
+++ b/sapi/fpm/config.m4
@@ -0,0 +1,657 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(fpm,,
+[ --enable-fpm Enable building of the fpm SAPI executable], no, no)
+
+dnl configure checks {{{
+AC_DEFUN([AC_FPM_STDLIBS],
+[
+ AC_CHECK_FUNCS(setenv clearenv setproctitle)
+
+ AC_SEARCH_LIBS(socket, socket)
+ AC_SEARCH_LIBS(inet_addr, nsl)
+
+ AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h sys/uio.h])
+ AC_CHECK_HEADERS([sys/select.h sys/socket.h sys/time.h])
+ AC_CHECK_HEADERS([arpa/inet.h netinet/in.h])
+ AC_CHECK_HEADERS([sysexits.h])
+])
+
+AC_DEFUN([AC_FPM_PRCTL],
+[
+ AC_MSG_CHECKING([for prctl])
+
+ AC_TRY_COMPILE([ #include <sys/prctl.h> ], [prctl(0, 0, 0, 0, 0);], [
+ AC_DEFINE([HAVE_PRCTL], 1, [do we have prctl?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+
+AC_DEFUN([AC_FPM_CLOCK],
+[
+ have_clock_gettime=no
+
+ AC_MSG_CHECKING([for clock_gettime])
+
+ AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
+ have_clock_gettime=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_clock_gettime" = "no"; then
+ AC_MSG_CHECKING([for clock_gettime in -lrt])
+
+ SAVED_LIBS="$LIBS"
+ LIBS="$LIBS -lrt"
+
+ AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
+ have_clock_gettime=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ LIBS="$SAVED_LIBS"
+ AC_MSG_RESULT([no])
+ ])
+ fi
+
+ if test "$have_clock_gettime" = "yes"; then
+ AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [do we have clock_gettime?])
+ fi
+
+ have_clock_get_time=no
+
+ if test "$have_clock_gettime" = "no"; then
+ AC_MSG_CHECKING([for clock_get_time])
+
+ AC_TRY_RUN([ #include <mach/mach.h>
+ #include <mach/clock.h>
+ #include <mach/mach_error.h>
+
+ int main()
+ {
+ kern_return_t ret; clock_serv_t aClock; mach_timespec_t aTime;
+ ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &aClock);
+
+ if (ret != KERN_SUCCESS) {
+ return 1;
+ }
+
+ ret = clock_get_time(aClock, &aTime);
+ if (ret != KERN_SUCCESS) {
+ return 2;
+ }
+
+ return 0;
+ }
+ ], [
+ have_clock_get_time=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+ fi
+
+ if test "$have_clock_get_time" = "yes"; then
+ AC_DEFINE([HAVE_CLOCK_GET_TIME], 1, [do we have clock_get_time?])
+ fi
+])
+
+AC_DEFUN([AC_FPM_TRACE],
+[
+ have_ptrace=no
+ have_broken_ptrace=no
+
+ AC_MSG_CHECKING([for ptrace])
+
+ AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/ptrace.h> ], [ptrace(0, 0, (void *) 0, 0);], [
+ have_ptrace=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_ptrace" = "yes"; then
+ AC_MSG_CHECKING([whether ptrace works])
+
+ AC_TRY_RUN([
+ #include <unistd.h>
+ #include <signal.h>
+ #include <sys/wait.h>
+ #include <sys/types.h>
+ #include <sys/ptrace.h>
+ #include <errno.h>
+
+ #if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
+ #define PTRACE_ATTACH PT_ATTACH
+ #endif
+
+ #if !defined(PTRACE_DETACH) && defined(PT_DETACH)
+ #define PTRACE_DETACH PT_DETACH
+ #endif
+
+ #if !defined(PTRACE_PEEKDATA) && defined(PT_READ_D)
+ #define PTRACE_PEEKDATA PT_READ_D
+ #endif
+
+ int main()
+ {
+ long v1 = (unsigned int) -1; /* copy will fail if sizeof(long) == 8 and we've got "int ptrace()" */
+ long v2;
+ pid_t child;
+ int status;
+
+ if ( (child = fork()) ) { /* parent */
+ int ret = 0;
+
+ if (0 > ptrace(PTRACE_ATTACH, child, 0, 0)) {
+ return 2;
+ }
+
+ waitpid(child, &status, 0);
+
+ #ifdef PT_IO
+ struct ptrace_io_desc ptio = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &v1,
+ .piod_addr = &v2,
+ .piod_len = sizeof(v1)
+ };
+
+ if (0 > ptrace(PT_IO, child, (void *) &ptio, 0)) {
+ ret = 3;
+ }
+ #else
+ errno = 0;
+
+ v2 = ptrace(PTRACE_PEEKDATA, child, (void *) &v1, 0);
+
+ if (errno) {
+ ret = 4;
+ }
+ #endif
+ ptrace(PTRACE_DETACH, child, (void *) 1, 0);
+
+ kill(child, SIGKILL);
+
+ return ret ? ret : (v1 != v2);
+ }
+ else { /* child */
+ sleep(10);
+ return 0;
+ }
+ }
+ ], [
+ AC_MSG_RESULT([yes])
+ ], [
+ have_ptrace=no
+ have_broken_ptrace=yes
+ AC_MSG_RESULT([no])
+ ], [
+ AC_MSG_RESULT([skipped (cross compiling)])
+ ])
+ fi
+
+ if test "$have_ptrace" = "yes"; then
+ AC_DEFINE([HAVE_PTRACE], 1, [do we have ptrace?])
+ fi
+
+ have_mach_vm_read=no
+
+ if test "$have_broken_ptrace" = "yes"; then
+ AC_MSG_CHECKING([for mach_vm_read])
+
+ AC_TRY_COMPILE([ #include <mach/mach.h>
+ #include <mach/mach_vm.h>
+ ], [
+ mach_vm_read((vm_map_t)0, (mach_vm_address_t)0, (mach_vm_size_t)0, (vm_offset_t *)0, (mach_msg_type_number_t*)0);
+ ], [
+ have_mach_vm_read=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+ fi
+
+ if test "$have_mach_vm_read" = "yes"; then
+ AC_DEFINE([HAVE_MACH_VM_READ], 1, [do we have mach_vm_read?])
+ fi
+
+ proc_mem_file=""
+
+ if test -r /proc/$$/mem ; then
+ proc_mem_file="mem"
+ else
+ if test -r /proc/$$/as ; then
+ proc_mem_file="as"
+ fi
+ fi
+
+ if test -n "$proc_mem_file" ; then
+ AC_MSG_CHECKING([for proc mem file])
+
+ AC_TRY_RUN([
+ #define _GNU_SOURCE
+ #define _FILE_OFFSET_BITS 64
+ #include <stdint.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <stdio.h>
+ int main()
+ {
+ long v1 = (unsigned int) -1, v2 = 0;
+ char buf[128];
+ int fd;
+ sprintf(buf, "/proc/%d/$proc_mem_file", getpid());
+ fd = open(buf, O_RDONLY);
+ if (0 > fd) {
+ return 1;
+ }
+ if (sizeof(long) != pread(fd, &v2, sizeof(long), (uintptr_t) &v1)) {
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ return v1 != v2;
+ }
+ ], [
+ AC_MSG_RESULT([$proc_mem_file])
+ ], [
+ proc_mem_file=""
+ AC_MSG_RESULT([no])
+ ], [
+ AC_MSG_RESULT([skipped (cross compiling)])
+ ])
+ fi
+
+ if test -n "$proc_mem_file"; then
+ AC_DEFINE_UNQUOTED([PROC_MEM_FILE], "$proc_mem_file", [/proc/pid/mem interface])
+ fi
+
+ fpm_trace_type=""
+
+ if test "$have_ptrace" = "yes"; then
+ fpm_trace_type=ptrace
+
+ elif test -n "$proc_mem_file"; then
+ fpm_trace_type=pread
+
+ elif test "$have_mach_vm_read" = "yes" ; then
+ fpm_trace_type=mach
+
+ else
+ AC_MSG_WARN([FPM Trace - ptrace, pread, or mach: could not be found])
+ fi
+
+])
+
+AC_DEFUN([AC_FPM_BUILTIN_ATOMIC],
+[
+ AC_MSG_CHECKING([if gcc supports __sync_bool_compare_and_swap])
+ AC_TRY_LINK(,
+ [
+ int variable = 1;
+ return (__sync_bool_compare_and_swap(&variable, 1, 2)
+ && __sync_add_and_fetch(&variable, 1)) ? 1 : 0;
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Define to 1 if gcc supports __sync_bool_compare_and_swap() a.o.])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ])
+])
+
+AC_DEFUN([AC_FPM_LQ],
+[
+ have_lq=no
+
+ AC_MSG_CHECKING([for TCP_INFO])
+
+ AC_TRY_COMPILE([ #include <netinet/tcp.h> ], [struct tcp_info ti; int x = TCP_INFO;], [
+ have_lq=tcp_info
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_lq" = "tcp_info"; then
+ AC_DEFINE([HAVE_LQ_TCP_INFO], 1, [do we have TCP_INFO?])
+ fi
+
+ if test "$have_lq" = "no" ; then
+ AC_MSG_CHECKING([for SO_LISTENQLEN])
+
+ AC_TRY_COMPILE([ #include <sys/socket.h> ], [int x = SO_LISTENQLIMIT; int y = SO_LISTENQLEN;], [
+ have_lq=so_listenq
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_lq" = "tcp_info"; then
+ AC_DEFINE([HAVE_LQ_SO_LISTENQ], 1, [do we have SO_LISTENQxxx?])
+ fi
+ fi
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_SYSCONF],
+[
+ AC_MSG_CHECKING([for sysconf])
+
+ AC_TRY_COMPILE([ #include <unistd.h> ], [sysconf(_SC_CLK_TCK);], [
+ AC_DEFINE([HAVE_SYSCONF], 1, [do we have sysconf?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_TIMES],
+[
+ AC_MSG_CHECKING([for times])
+
+ AC_TRY_COMPILE([ #include <sys/times.h> ], [struct tms t; times(&t);], [
+ AC_DEFINE([HAVE_TIMES], 1, [do we have times?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_KQUEUE],
+[
+ AC_MSG_CHECKING([for kqueue])
+
+ AC_TRY_COMPILE(
+ [
+ #include <sys/types.h>
+ #include <sys/event.h>
+ #include <sys/time.h>
+ ], [
+ int kfd;
+ struct kevent k;
+ kfd = kqueue();
+ /* 0 -> STDIN_FILENO */
+ EV_SET(&k, 0, EVFILT_READ , EV_ADD | EV_CLEAR, 0, 0, NULL);
+ ], [
+ AC_DEFINE([HAVE_KQUEUE], 1, [do we have kqueue?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_PORT],
+[
+ AC_MSG_CHECKING([for port framework])
+
+ AC_TRY_COMPILE(
+ [
+ #include <port.h>
+ ], [
+ int port;
+
+ port = port_create();
+ if (port < 0) {
+ return 1;
+ }
+ ], [
+ AC_DEFINE([HAVE_PORT], 1, [do we have port framework?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_DEVPOLL],
+[
+ AC_MSG_CHECKING([for /dev/poll])
+
+ AC_TRY_COMPILE(
+ [
+ #include <stdio.h>
+ #include <sys/devpoll.h>
+ ], [
+ int n, dp;
+ struct dvpoll dvp;
+ dp = 0;
+ dvp.dp_fds = NULL;
+ dvp.dp_nfds = 0;
+ dvp.dp_timeout = 0;
+ n = ioctl(dp, DP_POLL, &dvp)
+ ], [
+ AC_DEFINE([HAVE_DEVPOLL], 1, [do we have /dev/poll?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_EPOLL],
+[
+ AC_MSG_CHECKING([for epoll])
+
+ AC_TRY_COMPILE(
+ [
+ #include <sys/epoll.h>
+ ], [
+ int epollfd;
+ struct epoll_event e;
+
+ epollfd = epoll_create(1);
+ if (epollfd < 0) {
+ return 1;
+ }
+
+ e.events = EPOLLIN | EPOLLET;
+ e.data.fd = 0;
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, 0, &e) == -1) {
+ return 1;
+ }
+
+ e.events = 0;
+ if (epoll_wait(epollfd, &e, 1, 1) < 0) {
+ return 1;
+ }
+ ], [
+ AC_DEFINE([HAVE_EPOLL], 1, [do we have epoll?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_POLL],
+[
+ AC_MSG_CHECKING([for poll])
+
+ AC_TRY_COMPILE(
+ [
+ #include <poll.h>
+ ], [
+ struct pollfd fds[2];
+
+ fds[0].fd = 0;
+ fds[0].events = POLLIN;
+
+ fds[1].fd = 0;
+ fds[1].events = POLLIN;
+
+ poll(fds, 2, 1);
+ ], [
+ AC_DEFINE([HAVE_POLL], 1, [do we have poll?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_SELECT],
+[
+ AC_MSG_CHECKING([for select])
+
+ AC_TRY_COMPILE(
+ [
+ /* According to POSIX.1-2001 */
+ #include <sys/select.h>
+
+ /* According to earlier standards */
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ ], [
+ fd_set fds;
+ struct timeval t;
+ t.tv_sec = 0;
+ t.tv_usec = 42;
+ FD_ZERO(&fds);
+ /* 0 -> STDIN_FILENO */
+ FD_SET(0, &fds);
+ select(FD_SETSIZE, &fds, NULL, NULL, &t);
+ ], [
+ AC_DEFINE([HAVE_SELECT], 1, [do we have select?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+
+AC_MSG_CHECKING(for FPM build)
+if test "$PHP_FPM" != "no"; then
+ AC_MSG_RESULT($PHP_FPM)
+
+ AC_FPM_STDLIBS
+ AC_FPM_PRCTL
+ AC_FPM_CLOCK
+ AC_FPM_TRACE
+ AC_FPM_BUILTIN_ATOMIC
+ AC_FPM_LQ
+ AC_FPM_SYSCONF
+ AC_FPM_TIMES
+ AC_FPM_KQUEUE
+ AC_FPM_PORT
+ AC_FPM_DEVPOLL
+ AC_FPM_EPOLL
+ AC_FPM_POLL
+ AC_FPM_SELECT
+
+ PHP_ARG_WITH(fpm-user,,
+ [ --with-fpm-user[=USER] Set the user for php-fpm to run as. (default: nobody)], nobody, no)
+
+ PHP_ARG_WITH(fpm-group,,
+ [ --with-fpm-group[=GRP] Set the group for php-fpm to run as. For a system user, this
+ should usually be set to match the fpm username (default: nobody)], nobody, no)
+
+ if test -z "$PHP_FPM_USER" -o "$PHP_FPM_USER" = "yes" -o "$PHP_FPM_USER" = "no"; then
+ php_fpm_user="nobody"
+ else
+ php_fpm_user="$PHP_FPM_USER"
+ fi
+
+ if test -z "$PHP_FPM_GROUP" -o "$PHP_FPM_GROUP" = "yes" -o "$PHP_FPM_GROUP" = "no"; then
+ php_fpm_group="nobody"
+ else
+ php_fpm_group="$PHP_FPM_GROUP"
+ fi
+
+ PHP_SUBST_OLD(php_fpm_user)
+ PHP_SUBST_OLD(php_fpm_group)
+ php_fpm_sysconfdir=`eval echo $sysconfdir`
+ PHP_SUBST_OLD(php_fpm_sysconfdir)
+ php_fpm_localstatedir=`eval echo $localstatedir`
+ PHP_SUBST_OLD(php_fpm_localstatedir)
+ php_fpm_prefix=`eval echo $prefix`
+ PHP_SUBST_OLD(php_fpm_prefix)
+
+ AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name])
+ AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name])
+
+ AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name])
+ AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name])
+
+ PHP_ADD_BUILD_DIR(sapi/fpm/fpm)
+ PHP_ADD_BUILD_DIR(sapi/fpm/fpm/events)
+ PHP_OUTPUT(sapi/fpm/php-fpm.conf sapi/fpm/init.d.php-fpm sapi/fpm/php-fpm.service sapi/fpm/php-fpm.8 sapi/fpm/status.html)
+ PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/fpm/Makefile.frag])
+
+ SAPI_FPM_PATH=sapi/fpm/php-fpm
+
+
+ if test "$fpm_trace_type" && test -f "$abs_srcdir/sapi/fpm/fpm/fpm_trace_$fpm_trace_type.c"; then
+ PHP_FPM_TRACE_FILES="fpm/fpm_trace.c fpm/fpm_trace_$fpm_trace_type.c"
+ fi
+
+ PHP_FPM_CFLAGS="-I$abs_srcdir/sapi/fpm"
+
+ PHP_FPM_FILES="fpm/fastcgi.c \
+ fpm/fpm.c \
+ fpm/fpm_children.c \
+ fpm/fpm_cleanup.c \
+ fpm/fpm_clock.c \
+ fpm/fpm_conf.c \
+ fpm/fpm_env.c \
+ fpm/fpm_events.c \
+ fpm/fpm_log.c \
+ fpm/fpm_main.c \
+ fpm/fpm_php.c \
+ fpm/fpm_php_trace.c \
+ fpm/fpm_process_ctl.c \
+ fpm/fpm_request.c \
+ fpm/fpm_shm.c \
+ fpm/fpm_scoreboard.c \
+ fpm/fpm_signals.c \
+ fpm/fpm_sockets.c \
+ fpm/fpm_status.c \
+ fpm/fpm_stdio.c \
+ fpm/fpm_unix.c \
+ fpm/fpm_worker_pool.c \
+ fpm/zlog.c \
+ fpm/events/select.c \
+ fpm/events/poll.c \
+ fpm/events/epoll.c \
+ fpm/events/kqueue.c \
+ fpm/events/devpoll.c \
+ fpm/events/port.c \
+ "
+
+ PHP_SELECT_SAPI(fpm, program, $PHP_FPM_FILES $PHP_FPM_TRACE_FILES, $PHP_FPM_CFLAGS, '$(SAPI_FPM_PATH)')
+
+ case $host_alias in
+ *aix*)
+ BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
+ ;;
+ *darwin*)
+ BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
+ ;;
+ *)
+ BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
+ ;;
+ esac
+
+ PHP_SUBST(SAPI_FPM_PATH)
+ PHP_SUBST(BUILD_FPM)
+
+else
+ AC_MSG_RESULT(no)
+fi
diff --git a/sapi/fpm/fpm/events/devpoll.c b/sapi/fpm/fpm/events/devpoll.c
new file mode 100644
index 0000000..223d072
--- /dev/null
+++ b/sapi/fpm/fpm/events/devpoll.c
@@ -0,0 +1,248 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_DEVPOLL
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/devpoll.h>
+#include <errno.h>
+
+static int fpm_event_devpoll_init(int max);
+static int fpm_event_devpoll_clean();
+static int fpm_event_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_devpoll_add(struct fpm_event_s *ev);
+static int fpm_event_devpoll_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s devpoll_module = {
+ .name = "/dev/poll",
+ .support_edge_trigger = 0,
+ .init = fpm_event_devpoll_init,
+ .clean = fpm_event_devpoll_clean,
+ .wait = fpm_event_devpoll_wait,
+ .add = fpm_event_devpoll_add,
+ .remove = fpm_event_devpoll_remove,
+};
+
+int dpfd = -1;
+static struct pollfd *pollfds = NULL;
+static struct pollfd *active_pollfds = NULL;
+static int npollfds = 0;
+
+#endif /* HAVE_DEVPOLL */
+
+struct fpm_event_module_s *fpm_event_devpoll_module() /* {{{ */
+{
+#if HAVE_DEVPOLL
+ return &devpoll_module;
+#else
+ return NULL;
+#endif /* HAVE_DEVPOLL */
+}
+/* }}} */
+
+#if HAVE_DEVPOLL
+
+/*
+ * Init module
+ */
+static int fpm_event_devpoll_init(int max) /* {{{ */
+{
+ int i;
+
+ /* open /dev/poll for future usages */
+ dpfd = open("/dev/poll", O_RDWR);
+ if (dpfd < 0) {
+ zlog(ZLOG_ERROR, "Unable to open /dev/poll");
+ return -1;
+ }
+
+ if (max < 1) {
+ return 0;
+ }
+
+ /* alloc and clear pollfds */
+ pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!pollfds) {
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* set all fd to -1 in order to ensure it's not set */
+ for (i = 0; i < max; i++) {
+ pollfds[i].fd = -1;
+ }
+
+ /* alloc and clear active_pollfds */
+ active_pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!active_pollfds) {
+ free(pollfds);
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(active_pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* save max */
+ npollfds = max;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_devpoll_clean() /* {{{ */
+{
+ /* close /dev/poll if open */
+ if (dpfd > -1) {
+ close(dpfd);
+ dpfd = -1;
+ }
+
+ /* free pollfds */
+ if (pollfds) {
+ free(pollfds);
+ pollfds = NULL;
+ }
+
+ /* free active_pollfds */
+ if (active_pollfds) {
+ free(active_pollfds);
+ active_pollfds = NULL;
+ }
+
+ npollfds = 0;
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret, i;
+ struct fpm_event_queue_s *q;
+ struct dvpoll dopoll;
+
+ /* setup /dev/poll */
+ dopoll.dp_fds = active_pollfds;
+ dopoll.dp_nfds = npollfds;
+ dopoll.dp_timeout = (int)timeout;
+
+ /* wait for inconming event or timeout */
+ ret = ioctl(dpfd, DP_POLL, &dopoll);
+
+ if (ret < 0) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "/dev/poll: ioctl() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* iterate throught triggered events */
+ for (i = 0; i < ret; i++) {
+
+ /* find the corresponding event */
+ q = queue;
+ while (q) {
+
+ /* found */
+ if (q->ev && q->ev->fd == active_pollfds[i].fd) {
+
+ /* fire the event */
+ fpm_event_fire(q->ev);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ break; /* next triggered event */
+ }
+ q = q->next; /* iterate */
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD from the fd set
+ */
+static int fpm_event_devpoll_add(struct fpm_event_s *ev) /* {{{ */
+{
+ struct pollfd pollfd;
+
+ /* fill pollfd with event informations */
+ pollfd.fd = ev->fd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+
+ /* add the event to the internal queue */
+ if (write(dpfd, &pollfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
+ zlog(ZLOG_ERROR, "/dev/poll: Unable to add the event in the internal queue");
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = ev->fd;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_devpoll_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ struct pollfd pollfd;
+
+ /* fill pollfd with the same informations as fpm_event_devpoll_add */
+ pollfd.fd = ev->fd;
+ pollfd.events = POLLIN | POLLREMOVE;
+ pollfd.revents = 0;
+
+ /* add the event to the internal queue */
+ if (write(dpfd, &pollfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
+ zlog(ZLOG_ERROR, "/dev/poll: Unable to remove the event in the internal queue");
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = -1;
+
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_DEVPOLL */
diff --git a/sapi/fpm/fpm/events/devpoll.h b/sapi/fpm/fpm/events/devpoll.h
new file mode 100644
index 0000000..f9bc4af
--- /dev/null
+++ b/sapi/fpm/fpm/events/devpoll.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_DEVPOLL_H
+#define FPM_EVENTS_DEVPOLL_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_devpoll_module();
+
+#endif /* FPM_EVENTS_DEVPOLL_H */
diff --git a/sapi/fpm/fpm/events/epoll.c b/sapi/fpm/fpm/events/epoll.c
new file mode 100644
index 0000000..c9c7f1f
--- /dev/null
+++ b/sapi/fpm/fpm/events/epoll.c
@@ -0,0 +1,211 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_EPOLL
+
+#include <sys/epoll.h>
+#include <errno.h>
+
+static int fpm_event_epoll_init(int max);
+static int fpm_event_epoll_clean();
+static int fpm_event_epoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_epoll_add(struct fpm_event_s *ev);
+static int fpm_event_epoll_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s epoll_module = {
+ .name = "epoll",
+ .support_edge_trigger = 1,
+ .init = fpm_event_epoll_init,
+ .clean = fpm_event_epoll_clean,
+ .wait = fpm_event_epoll_wait,
+ .add = fpm_event_epoll_add,
+ .remove = fpm_event_epoll_remove,
+};
+
+static struct epoll_event *epollfds = NULL;
+static int nepollfds = 0;
+static int epollfd = 0;
+
+#endif /* HAVE_EPOLL */
+
+struct fpm_event_module_s *fpm_event_epoll_module() /* {{{ */
+{
+#if HAVE_EPOLL
+ return &epoll_module;
+#else
+ return NULL;
+#endif /* HAVE_EPOLL */
+}
+/* }}} */
+
+#if HAVE_EPOLL
+
+/*
+ * Init the module
+ */
+static int fpm_event_epoll_init(int max) /* {{{ */
+{
+ if (max < 1) {
+ return 0;
+ }
+
+ /* init epoll */
+ epollfd = epoll_create(max + 1);
+ if (epollfd < 0) {
+ zlog(ZLOG_ERROR, "epoll: unable to initialize");
+ return -1;
+ }
+
+ /* allocate fds */
+ epollfds = malloc(sizeof(struct epoll_event) * max);
+ if (!epollfds) {
+ zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(epollfds, 0, sizeof(struct epoll_event) * max);
+
+ /* save max */
+ nepollfds = max;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_epoll_clean() /* {{{ */
+{
+ /* free epollfds */
+ if (epollfds) {
+ free(epollfds);
+ epollfds = NULL;
+ }
+
+ nepollfds = 0;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_epoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret, i;
+
+ /* ensure we have a clean epoolfds before calling epoll_wait() */
+ memset(epollfds, 0, sizeof(struct epoll_event) * nepollfds);
+
+ /* wait for inconming event or timeout */
+ ret = epoll_wait(epollfd, epollfds, nepollfds, timeout);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "epoll_wait() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* events have been triggered, let's fire them */
+ for (i = 0; i < ret; i++) {
+
+ /* do we have a valid ev ptr ? */
+ if (!epollfds[i].data.ptr) {
+ continue;
+ }
+
+ /* fire the event */
+ fpm_event_fire((struct fpm_event_s *)epollfds[i].data.ptr);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_epoll_add(struct fpm_event_s *ev) /* {{{ */
+{
+ struct epoll_event e;
+
+ /* fill epoll struct */
+ e.events = EPOLLIN;
+ e.data.fd = ev->fd;
+ e.data.ptr = (void *)ev;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ e.events = e.events | EPOLLET;
+ }
+
+ /* add the event to epoll internal queue */
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev->fd, &e) == -1) {
+ zlog(ZLOG_ERROR, "epoll: unable to add fd %d", ev->fd);
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = ev->fd;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_epoll_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ struct epoll_event e;
+
+ /* fill epoll struct the same way we did in fpm_event_epoll_add() */
+ e.events = EPOLLIN;
+ e.data.fd = ev->fd;
+ e.data.ptr = (void *)ev;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ e.events = e.events | EPOLLET;
+ }
+
+ /* remove the event from epoll internal queue */
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, ev->fd, &e) == -1) {
+ zlog(ZLOG_ERROR, "epoll: unable to remove fd %d", ev->fd);
+ return -1;
+ }
+
+ /* mark the event as not registered */
+ ev->index = -1;
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_EPOLL */
diff --git a/sapi/fpm/fpm/events/epoll.h b/sapi/fpm/fpm/events/epoll.h
new file mode 100644
index 0000000..abc5a21
--- /dev/null
+++ b/sapi/fpm/fpm/events/epoll.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_EPOLL_H
+#define FPM_EVENTS_EPOLL_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_epoll_module();
+
+#endif /* FPM_EVENTS_EPOLL_H */
diff --git a/sapi/fpm/fpm/events/kqueue.c b/sapi/fpm/fpm/events/kqueue.c
new file mode 100644
index 0000000..7ce0760
--- /dev/null
+++ b/sapi/fpm/fpm/events/kqueue.c
@@ -0,0 +1,208 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_KQUEUE
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+static int fpm_event_kqueue_init(int max);
+static int fpm_event_kqueue_clean();
+static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_kqueue_add(struct fpm_event_s *ev);
+static int fpm_event_kqueue_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s kqueue_module = {
+ .name = "kqueue",
+ .support_edge_trigger = 1,
+ .init = fpm_event_kqueue_init,
+ .clean = fpm_event_kqueue_clean,
+ .wait = fpm_event_kqueue_wait,
+ .add = fpm_event_kqueue_add,
+ .remove = fpm_event_kqueue_remove,
+};
+
+static struct kevent *kevents = NULL;
+static int nkevents = 0;
+static int kfd = 0;
+
+#endif /* HAVE_KQUEUE */
+
+/*
+ * Return the module configuration
+ */
+struct fpm_event_module_s *fpm_event_kqueue_module() /* {{{ */
+{
+#if HAVE_KQUEUE
+ return &kqueue_module;
+#else
+ return NULL;
+#endif /* HAVE_KQUEUE */
+}
+/* }}} */
+
+#if HAVE_KQUEUE
+
+/*
+ * init kqueue and stuff
+ */
+static int fpm_event_kqueue_init(int max) /* {{{ */
+{
+ if (max < 1) {
+ return 0;
+ }
+
+ kfd = kqueue();
+ if (kfd < 0) {
+ zlog(ZLOG_ERROR, "kqueue: unable to initialize");
+ return -1;
+ }
+
+ kevents = malloc(sizeof(struct kevent) * max);
+ if (!kevents) {
+ zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max);
+ return -1;
+ }
+
+ memset(kevents, 0, sizeof(struct kevent) * max);
+
+ nkevents = max;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * release kqueue stuff
+ */
+static int fpm_event_kqueue_clean() /* {{{ */
+{
+ if (kevents) {
+ free(kevents);
+ kevents = NULL;
+ }
+
+ nkevents = 0;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ struct timespec t;
+ int ret, i;
+
+ /* ensure we have a clean kevents before calling kevent() */
+ memset(kevents, 0, sizeof(struct kevent) * nkevents);
+
+ /* convert ms to timespec struct */
+ t.tv_sec = timeout / 1000;
+ t.tv_nsec = (timeout % 1000) * 1000 * 1000;
+
+ /* wait for incoming event or timeout */
+ ret = kevent(kfd, NULL, 0, kevents, nkevents, &t);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "epoll_wait() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* fire triggered events */
+ for (i = 0; i < ret; i++) {
+ if (kevents[i].udata) {
+ struct fpm_event_s *ev = (struct fpm_event_s *)kevents[i].udata;
+ fpm_event_fire(ev);
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD to to kevent queue
+ */
+static int fpm_event_kqueue_add(struct fpm_event_s *ev) /* {{{ */
+{
+ struct kevent k;
+ int flags = EV_ADD;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ flags = flags | EV_CLEAR;
+ }
+
+ EV_SET(&k, ev->fd, EVFILT_READ, flags, 0, 0, (void *)ev);
+
+ if (kevent(kfd, &k, 1, NULL, 0, NULL) < 0) {
+ zlog(ZLOG_ERROR, "kevent: unable to add event");
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = ev->fd;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the kevent queue
+ */
+static int fpm_event_kqueue_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ struct kevent k;
+ int flags = EV_DELETE;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ flags = flags | EV_CLEAR;
+ }
+
+ EV_SET(&k, ev->fd, EVFILT_READ, flags, 0, 0, (void *)ev);
+
+ if (kevent(kfd, &k, 1, NULL, 0, NULL) < 0) {
+ zlog(ZLOG_ERROR, "kevent: unable to add event");
+ return -1;
+ }
+
+ /* mark the vent as not registered */
+ ev->index = -1;
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_KQUEUE */
diff --git a/sapi/fpm/fpm/events/kqueue.h b/sapi/fpm/fpm/events/kqueue.h
new file mode 100644
index 0000000..2642aca
--- /dev/null
+++ b/sapi/fpm/fpm/events/kqueue.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_KQUEUE_H
+#define FPM_EVENTS_KQUEUE_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_kqueue_module();
+
+#endif /* FPM_EVENTS_KQUEUE_H */
diff --git a/sapi/fpm/fpm/events/poll.c b/sapi/fpm/fpm/events/poll.c
new file mode 100644
index 0000000..185eceb
--- /dev/null
+++ b/sapi/fpm/fpm/events/poll.c
@@ -0,0 +1,276 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_POLL
+
+#include <poll.h>
+#include <errno.h>
+#include <string.h>
+
+static int fpm_event_poll_init(int max);
+static int fpm_event_poll_clean();
+static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_poll_add(struct fpm_event_s *ev);
+static int fpm_event_poll_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s poll_module = {
+ .name = "poll",
+ .support_edge_trigger = 0,
+ .init = fpm_event_poll_init,
+ .clean = fpm_event_poll_clean,
+ .wait = fpm_event_poll_wait,
+ .add = fpm_event_poll_add,
+ .remove = fpm_event_poll_remove,
+};
+
+static struct pollfd *pollfds = NULL;
+static struct pollfd *active_pollfds = NULL;
+static int npollfds = 0;
+static int next_free_slot = 0;
+#endif /* HAVE_POLL */
+
+/*
+ * return the module configuration
+ */
+struct fpm_event_module_s *fpm_event_poll_module() /* {{{ */
+{
+#if HAVE_POLL
+ return &poll_module;
+#else
+ return NULL;
+#endif /* HAVE_POLL */
+}
+/* }}} */
+
+#if HAVE_POLL
+
+/*
+ * Init the module
+ */
+static int fpm_event_poll_init(int max) /* {{{ */
+{
+ int i;
+
+ if (max < 1) {
+ return 0;
+ }
+
+ /* alloc and clear pollfds */
+ pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!pollfds) {
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* set all fd to -1 in order to ensure it's not set */
+ for (i = 0; i < max; i++) {
+ pollfds[i].fd = -1;
+ }
+
+ /* alloc and clear active_pollfds */
+ active_pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!active_pollfds) {
+ free(pollfds);
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(active_pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* save max */
+ npollfds = max;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_poll_clean() /* {{{ */
+{
+ /* free pollfds */
+ if (pollfds) {
+ free(pollfds);
+ pollfds = NULL;
+ }
+
+ /* free active_pollfds */
+ if (active_pollfds) {
+ free(active_pollfds);
+ active_pollfds = NULL;
+ }
+
+ npollfds = 0;
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret;
+ struct fpm_event_queue_s *q;
+
+ if (npollfds > 0) {
+ /* copy pollfds because poll() alters it */
+ memcpy(active_pollfds, pollfds, sizeof(struct pollfd) * npollfds);
+ }
+
+ /* wait for inconming event or timeout */
+ ret = poll(active_pollfds, npollfds, timeout);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "poll() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* events have been triggered */
+ if (ret > 0) {
+
+ /* trigger POLLIN events */
+ q = queue;
+ while (q) {
+ /* ensure ev->index is valid */
+ if (q->ev && q->ev->index >= 0 && q->ev->index < npollfds && q->ev->fd == active_pollfds[q->ev->index].fd) {
+
+ /* has the event has been triggered ? */
+ if (active_pollfds[q->ev->index].revents & POLLIN) {
+
+ /* fire the event */
+ fpm_event_fire(q->ev);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ }
+ q = q->next; /* iterate */
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_poll_add(struct fpm_event_s *ev) /* {{{ */
+{
+ int i;
+
+ /* do we have a direct free slot */
+ if (pollfds[next_free_slot].fd == -1) {
+ /* register the event */
+ pollfds[next_free_slot].fd = ev->fd;
+ pollfds[next_free_slot].events = POLLIN;
+
+ /* remember the event place in the fd list and suppose next slot is free */
+ ev->index = next_free_slot++;
+ if (next_free_slot >= npollfds) {
+ next_free_slot = 0;
+ }
+ return 0;
+ }
+
+ /* let's search */
+ for (i = 0; i < npollfds; i++) {
+ if (pollfds[i].fd != -1) {
+ /* not free */
+ continue;
+ }
+
+ /* register the event */
+ pollfds[i].fd = ev->fd;
+ pollfds[i].events = POLLIN;
+
+ /* remember the event place in the fd list and suppose next slot is free */
+ ev->index = next_free_slot++;
+ if (next_free_slot >= npollfds) {
+ next_free_slot = 0;
+ }
+ return 0;
+ }
+
+ zlog(ZLOG_ERROR, "poll: not enought space to add event (fd=%d)", ev->fd);
+ return -1;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_poll_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ int i;
+
+ /* do we have a direct access */
+ if (ev->index >= 0 && ev->index < npollfds && pollfds[ev->index].fd == ev->fd) {
+ /* remember this slot as free */
+ next_free_slot = ev->index;
+
+ /* clear event in pollfds */
+ pollfds[ev->index].fd = -1;
+ pollfds[ev->index].events = 0;
+
+ /* mark the event as not registered */
+ ev->index = -1;
+
+ return 0;
+ }
+
+ /* let's search */
+ for (i = 0; i < npollfds; i++) {
+
+ if (pollfds[i].fd != ev->fd) {
+ /* not found */
+ continue;
+ }
+
+ /* remember this slot as free */
+ next_free_slot = i;
+
+ /* clear event in pollfds */
+ pollfds[i].fd = -1;
+ pollfds[i].events = 0;
+
+ /* mark the event as not registered */
+ ev->index = -1;
+
+ return 0;
+ }
+
+ zlog(ZLOG_ERROR, "poll: unable to remove event: not found (fd=%d, index=%d)", ev->fd, ev->index);
+ return -1;
+}
+/* }}} */
+
+#endif /* HAVE_POLL */
diff --git a/sapi/fpm/fpm/events/poll.h b/sapi/fpm/fpm/events/poll.h
new file mode 100644
index 0000000..d553192
--- /dev/null
+++ b/sapi/fpm/fpm/events/poll.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_POLL_H
+#define FPM_EVENTS_POLL_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_poll_module();
+
+#endif /* FPM_EVENTS_POLL_H */
diff --git a/sapi/fpm/fpm/events/port.c b/sapi/fpm/fpm/events/port.c
new file mode 100644
index 0000000..3cbf092
--- /dev/null
+++ b/sapi/fpm/fpm/events/port.c
@@ -0,0 +1,185 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_PORT
+
+#include <port.h>
+#include <poll.h>
+#include <errno.h>
+
+static int fpm_event_port_init(int max);
+static int fpm_event_port_clean();
+static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_port_add(struct fpm_event_s *ev);
+static int fpm_event_port_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s port_module = {
+ .name = "port",
+ .support_edge_trigger = 0,
+ .init = fpm_event_port_init,
+ .clean = fpm_event_port_clean,
+ .wait = fpm_event_port_wait,
+ .add = fpm_event_port_add,
+ .remove = fpm_event_port_remove,
+};
+
+port_event_t *events = NULL;
+int nevents = 0;
+static int pfd = -1;
+
+#endif /* HAVE_PORT */
+
+struct fpm_event_module_s *fpm_event_port_module() /* {{{ */
+{
+#if HAVE_PORT
+ return &port_module;
+#else
+ return NULL;
+#endif /* HAVE_PORT */
+}
+/* }}} */
+
+#if HAVE_PORT
+
+/*
+ * Init the module
+ */
+static int fpm_event_port_init(int max) /* {{{ */
+{
+ /* open port */
+ pfd = port_create();
+ if (pfd < 0) {
+ zlog(ZLOG_ERROR, "port: unable to initialize port_create()");
+ return -1;
+ }
+
+ if (max < 1) {
+ return 0;
+ }
+
+ /* alloc and clear active_pollfds */
+ events = malloc(sizeof(port_event_t) * max);
+ if (!events) {
+ zlog(ZLOG_ERROR, "port: Unable to allocate %d events", max);
+ return -1;
+ }
+
+ nevents = max;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_port_clean() /* {{{ */
+{
+ if (pfd > -1) {
+ close(pfd);
+ pfd = -1;
+ }
+
+ if (events) {
+ free(events);
+ events = NULL;
+ }
+
+ nevents = 0;
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret, i, nget;
+ timespec_t t;
+
+ /* convert timeout into timespec_t */
+ t.tv_sec = (int)(timeout / 1000);
+ t.tv_nsec = (timeout % 1000) * 1000 * 1000;
+
+ /* wait for inconming event or timeout. We want at least one event or timeout */
+ nget = 1;
+ ret = port_getn(pfd, events, nevents, &nget, &t);
+ if (ret < 0) {
+
+ /* trigger error unless signal interrupt or timeout */
+ if (errno != EINTR && errno != ETIME) {
+ zlog(ZLOG_WARNING, "poll() returns %d", errno);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < nget; i++) {
+
+ /* do we have a ptr to the event ? */
+ if (!events[i].portev_user) {
+ continue;
+ }
+
+ /* fire the event */
+ fpm_event_fire((struct fpm_event_s *)events[i].portev_user);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ return nget;
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_port_add(struct fpm_event_s *ev) /* {{{ */
+{
+ /* add the event to port */
+ if (port_associate(pfd, PORT_SOURCE_FD, ev->fd, POLLIN, (void *)ev) < 0) {
+ zlog(ZLOG_ERROR, "port: unable to add the event");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_port_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ /* remove the event from port */
+ if (port_dissociate(pfd, PORT_SOURCE_FD, ev->fd) < 0) {
+ zlog(ZLOG_ERROR, "port: unable to add the event");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_PORT */
diff --git a/sapi/fpm/fpm/events/port.h b/sapi/fpm/fpm/events/port.h
new file mode 100644
index 0000000..666a157
--- /dev/null
+++ b/sapi/fpm/fpm/events/port.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_PORT_H
+#define FPM_EVENTS_PORT_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_port_module();
+
+#endif /* FPM_EVENTS_PORT_H */
diff --git a/sapi/fpm/fpm/events/select.c b/sapi/fpm/fpm/events/select.c
new file mode 100644
index 0000000..e3af067
--- /dev/null
+++ b/sapi/fpm/fpm/events/select.c
@@ -0,0 +1,175 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_SELECT
+
+/* According to POSIX.1-2001 */
+#include <sys/select.h>
+
+/* According to earlier standards */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <errno.h>
+
+static int fpm_event_select_init(int max);
+static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_select_add(struct fpm_event_s *ev);
+static int fpm_event_select_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s select_module = {
+ .name = "select",
+ .support_edge_trigger = 0,
+ .init = fpm_event_select_init,
+ .clean = NULL,
+ .wait = fpm_event_select_wait,
+ .add = fpm_event_select_add,
+ .remove = fpm_event_select_remove,
+};
+
+static fd_set fds;
+
+#endif /* HAVE_SELECT */
+
+/*
+ * return the module configuration
+ */
+struct fpm_event_module_s *fpm_event_select_module() /* {{{ */
+{
+#if HAVE_SELECT
+ return &select_module;
+#else
+ return NULL;
+#endif /* HAVE_SELECT */
+}
+/* }}} */
+
+#if HAVE_SELECT
+
+/*
+ * Init the module
+ */
+static int fpm_event_select_init(int max) /* {{{ */
+{
+ FD_ZERO(&fds);
+ return 0;
+}
+/* }}} */
+
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret;
+ struct fpm_event_queue_s *q;
+ fd_set current_fds;
+ struct timeval t;
+
+ /* copy fds because select() alters it */
+ current_fds = fds;
+
+ /* fill struct timeval with timeout */
+ t.tv_sec = timeout / 1000;
+ t.tv_usec = (timeout % 1000) * 1000;
+
+ /* wait for inconming event or timeout */
+ ret = select(FD_SETSIZE, &current_fds, NULL, NULL, &t);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "poll() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* events have been triggered */
+ if (ret > 0) {
+
+ /* trigger POLLIN events */
+ q = queue;
+ while (q) {
+ if (q->ev) { /* sanity check */
+
+ /* check if the event has been triggered */
+ if (FD_ISSET(q->ev->fd, &current_fds)) {
+
+ /* fire the event */
+ fpm_event_fire(q->ev);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ }
+ q = q->next; /* iterate */
+ }
+ }
+ return ret;
+
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_select_add(struct fpm_event_s *ev) /* {{{ */
+{
+ /* check size limitation */
+ if (ev->fd >= FD_SETSIZE) {
+ zlog(ZLOG_ERROR, "select: not enough space in the select fd list (max = %d). Please consider using another event mechanism.", FD_SETSIZE);
+ return -1;
+ }
+
+ /* add the FD if not already in */
+ if (!FD_ISSET(ev->fd, &fds)) {
+ FD_SET(ev->fd, &fds);
+ ev->index = ev->fd;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_select_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ /* remove the fd if it's in */
+ if (FD_ISSET(ev->fd, &fds)) {
+ FD_CLR(ev->fd, &fds);
+ ev->index = -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_SELECT */
diff --git a/sapi/fpm/fpm/events/select.h b/sapi/fpm/fpm/events/select.h
new file mode 100644
index 0000000..db52503
--- /dev/null
+++ b/sapi/fpm/fpm/events/select.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_SELECT_H
+#define FPM_EVENTS_SELECT_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_select_module();
+
+#endif /* FPM_EVENTS_SELECT_H */
diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c
new file mode 100644
index 0000000..cf3f098
--- /dev/null
+++ b/sapi/fpm/fpm/fastcgi.c
@@ -0,0 +1,1111 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: fastcgi.c 287777 2009-08-26 19:17:32Z pajoye $ */
+
+#include "php.h"
+#include "fastcgi.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <php_config.h>
+#include "fpm.h"
+#include "fpm_request.h"
+#include "zlog.h"
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+ typedef unsigned int in_addr_t;
+
+ struct sockaddr_un {
+ short sun_family;
+ char sun_path[MAXPATHLEN];
+ };
+
+ static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE;
+ static int is_impersonate = 0;
+
+#define FCGI_LOCK(fd) \
+ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
+ DWORD ret; \
+ while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \
+ if (in_shutdown) return -1; \
+ } \
+ if (ret == WAIT_FAILED) { \
+ fprintf(stderr, "WaitForSingleObject() failed\n"); \
+ return -1; \
+ } \
+ }
+
+#define FCGI_UNLOCK(fd) \
+ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
+ ReleaseMutex(fcgi_accept_mutex); \
+ }
+
+#else
+
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <signal.h>
+
+# define closesocket(s) close(s)
+
+# if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+# include <sys/poll.h>
+# endif
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
+#endif
+
+# ifndef HAVE_SOCKLEN_T
+ typedef unsigned int socklen_t;
+# endif
+
+# ifdef USE_LOCKING
+# define FCGI_LOCK(fd) \
+ do { \
+ struct flock lock; \
+ lock.l_type = F_WRLCK; \
+ lock.l_start = 0; \
+ lock.l_whence = SEEK_SET; \
+ lock.l_len = 0; \
+ if (fcntl(fd, F_SETLKW, &lock) != -1) { \
+ break; \
+ } else if (errno != EINTR || in_shutdown) { \
+ return -1; \
+ } \
+ } while (1)
+
+# define FCGI_UNLOCK(fd) \
+ do { \
+ int orig_errno = errno; \
+ while (1) { \
+ struct flock lock; \
+ lock.l_type = F_UNLCK; \
+ lock.l_start = 0; \
+ lock.l_whence = SEEK_SET; \
+ lock.l_len = 0; \
+ if (fcntl(fd, F_SETLK, &lock) != -1) { \
+ break; \
+ } else if (errno != EINTR) { \
+ return -1; \
+ } \
+ } \
+ errno = orig_errno; \
+ } while (0)
+# else
+# define FCGI_LOCK(fd)
+# define FCGI_UNLOCK(fd)
+# endif
+
+#endif
+
+typedef union _sa_t {
+ struct sockaddr sa;
+ struct sockaddr_un sa_unix;
+ struct sockaddr_in sa_inet;
+} sa_t;
+
+static HashTable fcgi_mgmt_vars;
+
+static int is_initialized = 0;
+static int in_shutdown = 0;
+static in_addr_t *allowed_clients = NULL;
+
+static sa_t client_sa;
+
+#ifdef _WIN32
+
+static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
+{
+ HANDLE shutdown_event = (HANDLE) arg;
+ WaitForSingleObject(shutdown_event, INFINITE);
+ in_shutdown = 1;
+ return 0;
+}
+
+#else
+
+static void fcgi_signal_handler(int signo)
+{
+ if (signo == SIGUSR1 || signo == SIGTERM) {
+ in_shutdown = 1;
+ }
+}
+
+static void fcgi_setup_signals(void)
+{
+ struct sigaction new_sa, old_sa;
+
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = 0;
+ new_sa.sa_handler = fcgi_signal_handler;
+ sigaction(SIGUSR1, &new_sa, NULL);
+ sigaction(SIGTERM, &new_sa, NULL);
+ sigaction(SIGPIPE, NULL, &old_sa);
+ if (old_sa.sa_handler == SIG_DFL) {
+ sigaction(SIGPIPE, &new_sa, NULL);
+ }
+}
+#endif
+
+int fcgi_init(void)
+{
+ if (!is_initialized) {
+ zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1);
+ fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS") - 1, "0", sizeof("0")-1);
+
+ is_initialized = 1;
+#ifdef _WIN32
+# if 0
+ /* TODO: Support for TCP sockets */
+ WSADATA wsaData;
+
+ if (WSAStartup(MAKEWORD(2,0), &wsaData)) {
+ fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError());
+ return 0;
+ }
+# endif
+ {
+ char *str;
+ DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
+ HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);
+
+ SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);
+
+ str = getenv("_FCGI_SHUTDOWN_EVENT_");
+ if (str != NULL) {
+ HANDLE shutdown_event = (HANDLE) atoi(str);
+ if (!CreateThread(NULL, 0, fcgi_shutdown_thread,
+ shutdown_event, 0, NULL)) {
+ return -1;
+ }
+ }
+ str = getenv("_FCGI_MUTEX_");
+ if (str != NULL) {
+ fcgi_accept_mutex = (HANDLE) atoi(str);
+ }
+ return 1;
+ }
+#else
+ fcgi_setup_signals();
+ return 1;
+#endif
+ }
+ return 1;
+}
+
+void fcgi_set_in_shutdown(int new_value)
+{
+ in_shutdown = new_value;
+}
+
+void fcgi_shutdown(void)
+{
+ if (is_initialized) {
+ zend_hash_destroy(&fcgi_mgmt_vars);
+ }
+ if (allowed_clients) {
+ free(allowed_clients);
+ }
+}
+
+void fcgi_set_allowed_clients(char *ip)
+{
+ char *cur, *end;
+ int n;
+
+ if (ip) {
+ ip = strdup(ip);
+ cur = ip;
+ n = 0;
+ while (*cur) {
+ if (*cur == ',') n++;
+ cur++;
+ }
+ if (allowed_clients) free(allowed_clients);
+ allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
+ n = 0;
+ cur = ip;
+ while (cur) {
+ end = strchr(cur, ',');
+ if (end) {
+ *end = 0;
+ end++;
+ }
+ allowed_clients[n] = inet_addr(cur);
+ if (allowed_clients[n] == INADDR_NONE) {
+ zlog(ZLOG_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur);
+ }
+ n++;
+ cur = end;
+ }
+ allowed_clients[n] = INADDR_NONE;
+ free(ip);
+ }
+}
+
+void fcgi_init_request(fcgi_request *req, int listen_socket)
+{
+ memset(req, 0, sizeof(fcgi_request));
+ req->listen_socket = listen_socket;
+ req->fd = -1;
+ req->id = -1;
+
+ req->in_len = 0;
+ req->in_pad = 0;
+
+ req->out_hdr = NULL;
+ req->out_pos = req->out_buf;
+
+#ifdef _WIN32
+ req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
+#endif
+}
+
+static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
+{
+ int ret;
+ size_t n = 0;
+
+ do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = send(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+#endif
+ if (ret > 0) {
+ n += ret;
+ } else if (ret <= 0 && errno != 0 && errno != EINTR) {
+ return ret;
+ }
+ } while (n != count);
+ return n;
+}
+
+static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count)
+{
+ int ret;
+ size_t n = 0;
+
+ do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+#endif
+ if (ret > 0) {
+ n += ret;
+ } else if (ret == 0 && errno == 0) {
+ return n;
+ } else if (ret <= 0 && errno != 0 && errno != EINTR) {
+ return ret;
+ }
+ } while (n != count);
+ return n;
+}
+
+static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len)
+{
+ int pad = ((len + 7) & ~7) - len;
+
+ hdr->contentLengthB0 = (unsigned char)(len & 0xff);
+ hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff);
+ hdr->paddingLength = (unsigned char)pad;
+ hdr->requestIdB0 = (unsigned char)(req_id & 0xff);
+ hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff);
+ hdr->reserved = 0;
+ hdr->type = type;
+ hdr->version = FCGI_VERSION_1;
+ if (pad) {
+ memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad);
+ }
+ return pad;
+}
+
+static inline size_t fcgi_get_params_len( int *result, unsigned char *p, unsigned char *end)
+{
+ size_t ret = 0;
+
+ if (p < end) {
+ *result = p[0];
+ if (*result < 128) {
+ ret = 1;
+ }
+ else if (p + 3 < end) {
+ *result = ((*result & 0x7f) << 24);
+ *result |= (p[1] << 16);
+ *result |= (p[2] << 8);
+ *result |= p[3];
+ ret = 4;
+ }
+ }
+ if (*result < 0) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static inline int fcgi_param_get_eff_len( unsigned char *p, unsigned char *end, uint *eff_len)
+{
+ int ret = 1;
+ int zero_found = 0;
+ *eff_len = 0;
+ for (; p != end; ++p) {
+ if (*p == '\0') {
+ zero_found = 1;
+ }
+ else {
+ if (zero_found) {
+ ret = 0;
+ break;
+ }
+ if (*eff_len < ((uint)-1)) {
+ ++*eff_len;
+ }
+ else {
+ ret = 0;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end)
+{
+ char buf[128];
+ char *tmp = buf;
+ size_t buf_size = sizeof(buf);
+ int name_len, val_len;
+ uint eff_name_len;
+ char *s;
+ int ret = 1;
+ size_t bytes_consumed;
+
+ while (p < end) {
+ bytes_consumed = fcgi_get_params_len(&name_len, p, end);
+ if (!bytes_consumed) {
+ /* Malformated request */
+ ret = 0;
+ break;
+ }
+ p += bytes_consumed;
+ bytes_consumed = fcgi_get_params_len(&val_len, p, end);
+ if (!bytes_consumed) {
+ /* Malformated request */
+ ret = 0;
+ break;
+ }
+ p += bytes_consumed;
+ if (name_len > (INT_MAX - val_len) || /* would the addition overflow? */
+ name_len + val_len > end - p) { /* would we exceed the buffer? */
+ /* Malformated request */
+ ret = 0;
+ break;
+ }
+
+ /*
+ * get the effective length of the name in case it's not a valid string
+ * don't do this on the value because it can be binary data
+ */
+ if (!fcgi_param_get_eff_len(p, p+name_len, &eff_name_len)){
+ /* Malicious request */
+ ret = 0;
+ break;
+ }
+ if (eff_name_len >= buf_size-1) {
+ if (eff_name_len > ((uint)-1)-64) {
+ ret = 0;
+ break;
+ }
+ buf_size = eff_name_len + 64;
+ tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size));
+ if (tmp == NULL) {
+ ret = 0;
+ break;
+ }
+ }
+ memcpy(tmp, p, eff_name_len);
+ tmp[eff_name_len] = 0;
+ s = estrndup((char*)p + name_len, val_len);
+ if (s == NULL) {
+ ret = 0;
+ break;
+ }
+ zend_hash_update(req->env, tmp, eff_name_len+1, &s, sizeof(char*), NULL);
+ p += name_len + val_len;
+ }
+ if (tmp != buf && tmp != NULL) {
+ efree(tmp);
+ }
+ return ret;
+}
+
+static void fcgi_free_var(char **s)
+{
+ efree(*s);
+}
+
+static int fcgi_read_request(fcgi_request *req)
+{
+ fcgi_header hdr;
+ int len, padding;
+ unsigned char buf[FCGI_MAX_LENGTH+8];
+
+ req->keep = 0;
+ req->closed = 0;
+ req->in_len = 0;
+ req->out_hdr = NULL;
+ req->out_pos = req->out_buf;
+ ALLOC_HASHTABLE(req->env);
+ zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0);
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+
+ while (hdr.type == FCGI_STDIN && len == 0) {
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+ }
+
+ if (len + padding > FCGI_MAX_LENGTH) {
+ return 0;
+ }
+
+ req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;
+
+ if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
+ char *val;
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ return 0;
+ }
+
+ req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);
+ switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
+ case FCGI_RESPONDER:
+ val = estrdup("RESPONDER");
+ zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
+ break;
+ case FCGI_AUTHORIZER:
+ val = estrdup("AUTHORIZER");
+ zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
+ break;
+ case FCGI_FILTER:
+ val = estrdup("FILTER");
+ zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
+ break;
+ default:
+ return 0;
+ }
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+
+ while (hdr.type == FCGI_PARAMS && len > 0) {
+ if (len + padding > FCGI_MAX_LENGTH) {
+ return 0;
+ }
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (!fcgi_get_params(req, buf, buf+len)) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ req->keep = 0;
+ return 0;
+ }
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+ }
+ } else if (hdr.type == FCGI_GET_VALUES) {
+ unsigned char *p = buf + sizeof(fcgi_header);
+ HashPosition pos;
+ char * str_index;
+ uint str_length;
+ ulong num_index;
+ int key_type;
+ zval ** value;
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (!fcgi_get_params(req, buf, buf+len)) {
+ req->keep = 0;
+ return 0;
+ }
+
+ zend_hash_internal_pointer_reset_ex(req->env, &pos);
+ while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
+ int zlen;
+ zend_hash_move_forward_ex(req->env, &pos);
+ if (key_type != HASH_KEY_IS_STRING) {
+ continue;
+ }
+ if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) {
+ continue;
+ }
+ --str_length;
+ zlen = Z_STRLEN_PP(value);
+ if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) {
+ break;
+ }
+ if (str_length < 0x80) {
+ *p++ = str_length;
+ } else {
+ *p++ = ((str_length >> 24) & 0xff) | 0x80;
+ *p++ = (str_length >> 16) & 0xff;
+ *p++ = (str_length >> 8) & 0xff;
+ *p++ = str_length & 0xff;
+ }
+ if (zlen < 0x80) {
+ *p++ = zlen;
+ } else {
+ *p++ = ((zlen >> 24) & 0xff) | 0x80;
+ *p++ = (zlen >> 16) & 0xff;
+ *p++ = (zlen >> 8) & 0xff;
+ *p++ = zlen & 0xff;
+ }
+ memcpy(p, str_index, str_length);
+ p += str_length;
+ memcpy(p, Z_STRVAL_PP(value), zlen);
+ p += zlen;
+ }
+ len = p - buf - sizeof(fcgi_header);
+ len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
+ if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
+ req->keep = 0;
+ return 0;
+ }
+ return 0;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+int fcgi_read(fcgi_request *req, char *str, int len)
+{
+ int ret, n, rest;
+ fcgi_header hdr;
+ unsigned char buf[255];
+
+ n = 0;
+ rest = len;
+ while (rest > 0) {
+ if (req->in_len == 0) {
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1 ||
+ hdr.type != FCGI_STDIN) {
+ req->keep = 0;
+ return 0;
+ }
+ req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ req->in_pad = hdr.paddingLength;
+ if (req->in_len == 0) {
+ return n;
+ }
+ }
+
+ if (req->in_len >= rest) {
+ ret = safe_read(req, str, rest);
+ } else {
+ ret = safe_read(req, str, req->in_len);
+ }
+ if (ret < 0) {
+ req->keep = 0;
+ return ret;
+ } else if (ret > 0) {
+ req->in_len -= ret;
+ rest -= ret;
+ n += ret;
+ str += ret;
+ if (req->in_len == 0) {
+ if (req->in_pad) {
+ if (safe_read(req, buf, req->in_pad) != req->in_pad) {
+ req->keep = 0;
+ return ret;
+ }
+ }
+ } else {
+ return n;
+ }
+ } else {
+ return n;
+ }
+ }
+ return n;
+}
+
+void fcgi_close(fcgi_request *req, int force, int destroy)
+{
+ if (destroy && req->env) {
+ zend_hash_destroy(req->env);
+ FREE_HASHTABLE(req->env);
+ req->env = NULL;
+ }
+
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ RevertToSelf();
+ }
+#endif
+
+ if ((force || !req->keep) && req->fd >= 0) {
+#ifdef _WIN32
+ if (!req->tcp) {
+ HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
+
+ if (!force) {
+ FlushFileBuffers(pipe);
+ }
+ DisconnectNamedPipe(pipe);
+ } else {
+ if (!force) {
+ char buf[8];
+
+ shutdown(req->fd, 1);
+ while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
+ }
+ closesocket(req->fd);
+ }
+#else
+ if (!force) {
+ char buf[8];
+
+ shutdown(req->fd, 1);
+ while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
+ }
+ close(req->fd);
+#endif
+ req->fd = -1;
+ fpm_request_finished();
+ }
+}
+
+int fcgi_accept_request(fcgi_request *req)
+{
+#ifdef _WIN32
+ HANDLE pipe;
+ OVERLAPPED ov;
+#endif
+
+ while (1) {
+ if (req->fd < 0) {
+ while (1) {
+ if (in_shutdown) {
+ return -1;
+ }
+#ifdef _WIN32
+ if (!req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->listen_socket);
+ FCGI_LOCK(req->listen_socket);
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!ConnectNamedPipe(pipe, &ov)) {
+ errno = GetLastError();
+ if (errno == ERROR_IO_PENDING) {
+ while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
+ if (in_shutdown) {
+ CloseHandle(ov.hEvent);
+ FCGI_UNLOCK(req->listen_socket);
+ return -1;
+ }
+ }
+ } else if (errno != ERROR_PIPE_CONNECTED) {
+ }
+ }
+ CloseHandle(ov.hEvent);
+ req->fd = req->listen_socket;
+ FCGI_UNLOCK(req->listen_socket);
+ } else {
+ SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
+#else
+ {
+ int listen_socket = req->listen_socket;
+#endif
+ sa_t sa;
+ socklen_t len = sizeof(sa);
+
+ fpm_request_accepting();
+
+ FCGI_LOCK(req->listen_socket);
+ req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
+ FCGI_UNLOCK(req->listen_socket);
+
+ client_sa = sa;
+ if (sa.sa.sa_family == AF_INET && req->fd >= 0 && allowed_clients) {
+ int n = 0;
+ int allowed = 0;
+
+ while (allowed_clients[n] != INADDR_NONE) {
+ if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
+ allowed = 1;
+ break;
+ }
+ n++;
+ }
+ if (!allowed) {
+ zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", inet_ntoa(sa.sa_inet.sin_addr));
+ closesocket(req->fd);
+ req->fd = -1;
+ continue;
+ }
+ }
+ }
+
+#ifdef _WIN32
+ if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
+#else
+ if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) {
+#endif
+ return -1;
+ }
+
+#ifdef _WIN32
+ break;
+#else
+ if (req->fd >= 0) {
+#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+ struct pollfd fds;
+ int ret;
+
+ fpm_request_reading_headers();
+
+ fds.fd = req->fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ do {
+ errno = 0;
+ ret = poll(&fds, 1, 5000);
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && (fds.revents & POLLIN)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+#else
+ fpm_request_reading_headers();
+
+ if (req->fd < FD_SETSIZE) {
+ struct timeval tv = {5,0};
+ fd_set set;
+ int ret;
+
+ FD_ZERO(&set);
+ FD_SET(req->fd, &set);
+ do {
+ errno = 0;
+ ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && FD_ISSET(req->fd, &set)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+ } else {
+ zlog(ZLOG_ERROR, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
+ fcgi_close(req, 1, 0);
+ }
+#endif
+ }
+#endif
+ }
+ } else if (in_shutdown) {
+ return -1;
+ }
+ if (fcgi_read_request(req)) {
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->fd);
+ if (!ImpersonateNamedPipeClient(pipe)) {
+ fcgi_close(req, 1, 1);
+ continue;
+ }
+ }
+#endif
+ return req->fd;
+ } else {
+ fcgi_close(req, 1, 1);
+ }
+ }
+}
+
+static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type)
+{
+ req->out_hdr = (fcgi_header*) req->out_pos;
+ req->out_hdr->type = type;
+ req->out_pos += sizeof(fcgi_header);
+ return req->out_hdr;
+}
+
+static inline void close_packet(fcgi_request *req)
+{
+ if (req->out_hdr) {
+ int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header));
+
+ req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len);
+ req->out_hdr = NULL;
+ }
+}
+
+int fcgi_flush(fcgi_request *req, int close)
+{
+ int len;
+
+ close_packet(req);
+
+ len = req->out_pos - req->out_buf;
+
+ if (close) {
+ fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos);
+
+ fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));
+ rec->body.appStatusB3 = 0;
+ rec->body.appStatusB2 = 0;
+ rec->body.appStatusB1 = 0;
+ rec->body.appStatusB0 = 0;
+ rec->body.protocolStatus = FCGI_REQUEST_COMPLETE;
+ len += sizeof(fcgi_end_request_rec);
+ }
+
+ if (safe_write(req, req->out_buf, len) != len) {
+ req->keep = 0;
+ return 0;
+ }
+
+ req->out_pos = req->out_buf;
+ return 1;
+}
+
+ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len)
+{
+ int limit, rest;
+
+ if (len <= 0) {
+ return 0;
+ }
+
+ if (req->out_hdr && req->out_hdr->type != type) {
+ close_packet(req);
+ }
+
+ /* Optimized version */
+ limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
+ if (!req->out_hdr) {
+ limit -= sizeof(fcgi_header);
+ if (limit < 0) limit = 0;
+ }
+
+ if (len < limit) {
+ if (!req->out_hdr) {
+ open_packet(req, type);
+ }
+ memcpy(req->out_pos, str, len);
+ req->out_pos += len;
+ } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) {
+ if (!req->out_hdr) {
+ open_packet(req, type);
+ }
+ if (limit > 0) {
+ memcpy(req->out_pos, str, limit);
+ req->out_pos += limit;
+ }
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (len > limit) {
+ open_packet(req, type);
+ memcpy(req->out_pos, str + limit, len - limit);
+ req->out_pos += len - limit;
+ }
+ } else {
+ int pos = 0;
+ int pad;
+
+ close_packet(req);
+ while ((len - pos) > 0xffff) {
+ open_packet(req, type);
+ fcgi_make_header(req->out_hdr, type, req->id, 0xfff8);
+ req->out_hdr = NULL;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (safe_write(req, str + pos, 0xfff8) != 0xfff8) {
+ req->keep = 0;
+ return -1;
+ }
+ pos += 0xfff8;
+ }
+
+ pad = (((len - pos) + 7) & ~7) - (len - pos);
+ rest = pad ? 8 - pad : 0;
+
+ open_packet(req, type);
+ fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest);
+ req->out_hdr = NULL;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) {
+ req->keep = 0;
+ return -1;
+ }
+ if (pad) {
+ open_packet(req, type);
+ memcpy(req->out_pos, str + len - rest, rest);
+ req->out_pos += rest;
+ }
+ }
+
+ return len;
+}
+
+int fcgi_finish_request(fcgi_request *req, int force_close)
+{
+ int ret = 1;
+
+ if (req->fd >= 0) {
+ if (!req->closed) {
+ ret = fcgi_flush(req, 1);
+ req->closed = 1;
+ }
+ fcgi_close(req, force_close, 1);
+ }
+ return ret;
+}
+
+char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
+{
+ char **val;
+
+ if (!req) return NULL;
+
+ if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) {
+ return *val;
+ }
+ return NULL;
+}
+
+char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
+{
+ if (var && req) {
+ if (val == NULL) {
+ zend_hash_del(req->env, var, var_len+1);
+ } else {
+ char **ret;
+
+ val = estrdup(val);
+ if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
+ return *ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
+{
+ zval * zvalue;
+ zvalue = pemalloc(sizeof(*zvalue), 1);
+ Z_TYPE_P(zvalue) = IS_STRING;
+ Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1);
+ Z_STRLEN_P(zvalue) = value_len;
+ zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL);
+}
+
+void fcgi_free_mgmt_var_cb(void * ptr)
+{
+ zval ** var = (zval **)ptr;
+ pefree(Z_STRVAL_PP(var), 1);
+ pefree(*var, 1);
+}
+
+char *fcgi_get_last_client_ip() /* {{{ */
+{
+ if (client_sa.sa.sa_family == AF_UNIX) {
+ return NULL;
+ }
+ return inet_ntoa(client_sa.sa_inet.sin_addr);
+}
+/* }}} */
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/fpm/fpm/fastcgi.h b/sapi/fpm/fpm/fastcgi.h
new file mode 100644
index 0000000..ee78675
--- /dev/null
+++ b/sapi/fpm/fpm/fastcgi.h
@@ -0,0 +1,145 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: fastcgi.h 272370 2008-12-31 11:15:49Z sebastian $ */
+
+/* FastCGI protocol */
+
+#define FCGI_VERSION_1 1
+
+#define FCGI_MAX_LENGTH 0xffff
+
+#define FCGI_KEEP_CONN 1
+
+typedef enum _fcgi_role {
+ FCGI_RESPONDER = 1,
+ FCGI_AUTHORIZER = 2,
+ FCGI_FILTER = 3
+} fcgi_role;
+
+typedef enum _fcgi_request_type {
+ FCGI_BEGIN_REQUEST = 1, /* [in] */
+ FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */
+ FCGI_END_REQUEST = 3, /* [out] */
+ FCGI_PARAMS = 4, /* [in] environment variables */
+ FCGI_STDIN = 5, /* [in] post data */
+ FCGI_STDOUT = 6, /* [out] response */
+ FCGI_STDERR = 7, /* [out] errors */
+ FCGI_DATA = 8, /* [in] filter data (not supported) */
+ FCGI_GET_VALUES = 9, /* [in] */
+ FCGI_GET_VALUES_RESULT = 10 /* [out] */
+} fcgi_request_type;
+
+typedef enum _fcgi_protocol_status {
+ FCGI_REQUEST_COMPLETE = 0,
+ FCGI_CANT_MPX_CONN = 1,
+ FCGI_OVERLOADED = 2,
+ FCGI_UNKNOWN_ROLE = 3
+} dcgi_protocol_status;
+
+typedef struct _fcgi_header {
+ unsigned char version;
+ unsigned char type;
+ unsigned char requestIdB1;
+ unsigned char requestIdB0;
+ unsigned char contentLengthB1;
+ unsigned char contentLengthB0;
+ unsigned char paddingLength;
+ unsigned char reserved;
+} fcgi_header;
+
+typedef struct _fcgi_begin_request {
+ unsigned char roleB1;
+ unsigned char roleB0;
+ unsigned char flags;
+ unsigned char reserved[5];
+} fcgi_begin_request;
+
+typedef struct _fcgi_begin_request_rec {
+ fcgi_header hdr;
+ fcgi_begin_request body;
+} fcgi_begin_request_rec;
+
+typedef struct _fcgi_end_request {
+ unsigned char appStatusB3;
+ unsigned char appStatusB2;
+ unsigned char appStatusB1;
+ unsigned char appStatusB0;
+ unsigned char protocolStatus;
+ unsigned char reserved[3];
+} fcgi_end_request;
+
+typedef struct _fcgi_end_request_rec {
+ fcgi_header hdr;
+ fcgi_end_request body;
+} fcgi_end_request_rec;
+
+/* FastCGI client API */
+
+typedef struct _fcgi_request {
+ int listen_socket;
+#ifdef _WIN32
+ int tcp;
+#endif
+ int fd;
+ int id;
+ int keep;
+ int closed;
+
+ int in_len;
+ int in_pad;
+
+ fcgi_header *out_hdr;
+ unsigned char *out_pos;
+ unsigned char out_buf[1024*8];
+ unsigned char reserved[sizeof(fcgi_end_request_rec)];
+
+ HashTable *env;
+} fcgi_request;
+
+int fcgi_init(void);
+void fcgi_shutdown(void);
+void fcgi_init_request(fcgi_request *req, int listen_socket);
+int fcgi_accept_request(fcgi_request *req);
+int fcgi_finish_request(fcgi_request *req, int force_close);
+
+void fcgi_set_in_shutdown(int);
+void fcgi_set_allowed_clients(char *);
+void fcgi_close(fcgi_request *req, int force, int destroy);
+
+char* fcgi_getenv(fcgi_request *req, const char* var, int var_len);
+char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val);
+
+int fcgi_read(fcgi_request *req, char *str, int len);
+
+ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len);
+int fcgi_flush(fcgi_request *req, int close);
+
+void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len);
+void fcgi_free_mgmt_var_cb(void * ptr);
+
+char *fcgi_get_last_client_ip();
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/fpm/fpm/fpm.c b/sapi/fpm/fpm/fpm.c
new file mode 100644
index 0000000..b866f37
--- /dev/null
+++ b/sapi/fpm/fpm/fpm.c
@@ -0,0 +1,123 @@
+
+ /* $Id: fpm.c,v 1.23 2008/07/20 16:38:31 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdlib.h> /* for exit */
+
+#include "fpm.h"
+#include "fpm_children.h"
+#include "fpm_signals.h"
+#include "fpm_env.h"
+#include "fpm_events.h"
+#include "fpm_cleanup.h"
+#include "fpm_php.h"
+#include "fpm_sockets.h"
+#include "fpm_unix.h"
+#include "fpm_process_ctl.h"
+#include "fpm_conf.h"
+#include "fpm_worker_pool.h"
+#include "fpm_scoreboard.h"
+#include "fpm_stdio.h"
+#include "fpm_log.h"
+#include "zlog.h"
+
+struct fpm_globals_s fpm_globals = {
+ .parent_pid = 0,
+ .argc = 0,
+ .argv = NULL,
+ .config = NULL,
+ .prefix = NULL,
+ .pid = NULL,
+ .running_children = 0,
+ .error_log_fd = 0,
+ .log_level = 0,
+ .listening_socket = 0,
+ .max_requests = 0,
+ .is_child = 0,
+ .test_successful = 0,
+ .heartbeat = 0,
+ .run_as_root = 0,
+ .send_config_pipe = {0, 0},
+};
+
+int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon) /* {{{ */
+{
+ fpm_globals.argc = argc;
+ fpm_globals.argv = argv;
+ if (config && *config) {
+ fpm_globals.config = strdup(config);
+ }
+ fpm_globals.prefix = prefix;
+ fpm_globals.pid = pid;
+ fpm_globals.run_as_root = run_as_root;
+
+ if (0 > fpm_php_init_main() ||
+ 0 > fpm_stdio_init_main() ||
+ 0 > fpm_conf_init_main(test_conf, force_daemon) ||
+ 0 > fpm_unix_init_main() ||
+ 0 > fpm_scoreboard_init_main() ||
+ 0 > fpm_pctl_init_main() ||
+ 0 > fpm_env_init_main() ||
+ 0 > fpm_signals_init_main() ||
+ 0 > fpm_children_init_main() ||
+ 0 > fpm_sockets_init_main() ||
+ 0 > fpm_worker_pool_init_main() ||
+ 0 > fpm_event_init_main()) {
+
+ if (fpm_globals.test_successful) {
+ exit(FPM_EXIT_OK);
+ } else {
+ zlog(ZLOG_ERROR, "FPM initialization failed");
+ return -1;
+ }
+ }
+
+ if (0 > fpm_conf_write_pid()) {
+ zlog(ZLOG_ERROR, "FPM initialization failed");
+ return -1;
+ }
+
+ fpm_stdio_init_final();
+ zlog(ZLOG_NOTICE, "fpm is running, pid %d", (int) fpm_globals.parent_pid);
+
+ return 0;
+}
+/* }}} */
+
+/* children: return listening socket
+ parent: never return */
+int fpm_run(int *max_requests) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ /* create initial children in all pools */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ int is_parent;
+
+ is_parent = fpm_children_create_initial(wp);
+
+ if (!is_parent) {
+ goto run_child;
+ }
+
+ /* handle error */
+ if (is_parent == 2) {
+ fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
+ fpm_event_loop(1);
+ }
+ }
+
+ /* run event loop forever */
+ fpm_event_loop(0);
+
+run_child: /* only workers reach this point */
+
+ fpm_cleanups_run(FPM_CLEANUP_CHILD);
+
+ *max_requests = fpm_globals.max_requests;
+ return fpm_globals.listening_socket;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm.h b/sapi/fpm/fpm/fpm.h
new file mode 100644
index 0000000..65d0e0d
--- /dev/null
+++ b/sapi/fpm/fpm/fpm.h
@@ -0,0 +1,63 @@
+
+ /* $Id: fpm.h,v 1.13 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_H
+#define FPM_H 1
+
+#include <unistd.h>
+
+#ifdef HAVE_SYSEXITS_H
+#include <sysexits.h>
+#endif
+
+#ifdef EX_OK
+#define FPM_EXIT_OK EX_OK
+#else
+#define FPM_EXIT_OK 0
+#endif
+
+#ifdef EX_USAGE
+#define FPM_EXIT_USAGE EX_USAGE
+#else
+#define FPM_EXIT_USAGE 64
+#endif
+
+#ifdef EX_SOFTWARE
+#define FPM_EXIT_SOFTWARE EX_SOFTWARE
+#else
+#define FPM_EXIT_SOFTWARE 70
+#endif
+
+#ifdef EX_CONFIG
+#define FPM_EXIT_CONFIG EX_CONFIG
+#else
+#define FPM_EXIT_CONFIG 78
+#endif
+
+
+int fpm_run(int *max_requests);
+int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon);
+
+struct fpm_globals_s {
+ pid_t parent_pid;
+ int argc;
+ char **argv;
+ char *config;
+ char *prefix;
+ char *pid;
+ int running_children;
+ int error_log_fd;
+ int log_level;
+ int listening_socket; /* for this child */
+ int max_requests; /* for this child */
+ int is_child;
+ int test_successful;
+ int heartbeat;
+ int run_as_root;
+ int send_config_pipe[2];
+};
+
+extern struct fpm_globals_s fpm_globals;
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_arrays.h b/sapi/fpm/fpm/fpm_arrays.h
new file mode 100644
index 0000000..02846b7
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_arrays.h
@@ -0,0 +1,116 @@
+
+ /* $Id: fpm_arrays.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_ARRAYS_H
+#define FPM_ARRAYS_H 1
+
+#include <stdlib.h>
+#include <string.h>
+
+struct fpm_array_s {
+ void *data;
+ size_t sz;
+ size_t used;
+ size_t allocated;
+};
+
+static inline struct fpm_array_s *fpm_array_init(struct fpm_array_s *a, unsigned int sz, unsigned int initial_num) /* {{{ */
+{
+ void *allocated = 0;
+
+ if (!a) {
+ a = malloc(sizeof(struct fpm_array_s));
+
+ if (!a) {
+ return 0;
+ }
+
+ allocated = a;
+ }
+
+ a->sz = sz;
+
+ a->data = calloc(sz, initial_num);
+
+ if (!a->data) {
+ free(allocated);
+ return 0;
+ }
+
+ a->allocated = initial_num;
+ a->used = 0;
+
+ return a;
+}
+/* }}} */
+
+static inline void *fpm_array_item(struct fpm_array_s *a, unsigned int n) /* {{{ */
+{
+ char *ret;
+
+ ret = (char *) a->data + a->sz * n;
+
+ return ret;
+}
+/* }}} */
+
+static inline void *fpm_array_item_last(struct fpm_array_s *a) /* {{{ */
+{
+ return fpm_array_item(a, a->used - 1);
+}
+/* }}} */
+
+static inline int fpm_array_item_remove(struct fpm_array_s *a, unsigned int n) /* {{{ */
+{
+ int ret = -1;
+
+ if (n < a->used - 1) {
+ void *last = fpm_array_item(a, a->used - 1);
+ void *to_remove = fpm_array_item(a, n);
+
+ memcpy(to_remove, last, a->sz);
+
+ ret = n;
+ }
+
+ --a->used;
+
+ return ret;
+}
+/* }}} */
+
+static inline void *fpm_array_push(struct fpm_array_s *a) /* {{{ */
+{
+ void *ret;
+
+ if (a->used == a->allocated) {
+ size_t new_allocated = a->allocated ? a->allocated * 2 : 20;
+ void *new_ptr = realloc(a->data, a->sz * new_allocated);
+
+ if (!new_ptr) {
+ return 0;
+ }
+
+ a->data = new_ptr;
+ a->allocated = new_allocated;
+ }
+
+ ret = fpm_array_item(a, a->used);
+
+ ++a->used;
+
+ return ret;
+}
+/* }}} */
+
+static inline void fpm_array_free(struct fpm_array_s *a) /* {{{ */
+{
+ free(a->data);
+ a->data = 0;
+ a->sz = 0;
+ a->used = a->allocated = 0;
+}
+/* }}} */
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_atomic.h b/sapi/fpm/fpm/fpm_atomic.h
new file mode 100644
index 0000000..662dd47
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_atomic.h
@@ -0,0 +1,168 @@
+
+ /* $Id: fpm_atomic.h,v 1.3 2008/09/18 23:34:11 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_ATOMIC_H
+#define FPM_ATOMIC_H 1
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+#include <sched.h>
+
+#ifdef HAVE_BUILTIN_ATOMIC
+
+/**
+ * all the cases below (as provided by upstream) define:
+ * word as atomic_int_t, and
+ * unsigned word as atomic_uint_t
+ * and only use volatile atomic_uint_t as atomic_t
+ */
+
+typedef volatile unsigned long atomic_t;
+#define atomic_cmp_set(a,b,c) __sync_bool_compare_and_swap(a,b,c)
+
+#elif ( __i386__ || __i386 )
+
+typedef int32_t atomic_int_t;
+typedef uint32_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+
+static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add) /* {{{ */
+{
+ __asm__ volatile ( "lock;" "xaddl %0, %1;" :
+ "+r" (add) : "m" (*value) : "memory");
+
+ return add;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ unsigned char res;
+
+ __asm__ volatile ( "lock;" "cmpxchgl %3, %1;" "sete %0;" :
+ "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");
+
+ return res;
+}
+/* }}} */
+
+#elif ( __amd64__ || __amd64 || __x86_64__ )
+
+typedef int64_t atomic_int_t;
+typedef uint64_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add) /* {{{ */
+{
+ __asm__ volatile ( "lock;" "xaddq %0, %1;" :
+ "+r" (add) : "m" (*value) : "memory");
+
+ return add;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ unsigned char res;
+
+ __asm__ volatile ( "lock;" "cmpxchgq %3, %1;" "sete %0;" :
+ "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");
+
+ return res;
+}
+/* }}} */
+
+#if (__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))
+
+#elif ( __arm__ || __arm ) /* W-Mark Kubacki */
+
+#if (__arch64__ || __arch64)
+typedef int64_t atomic_int_t;
+typedef uint64_t atomic_uint_t;
+#else
+typedef int32_t atomic_int_t;
+typedef uint32_t atomic_uint_t;
+#endif
+
+#define atomic_cmp_set(a,b,c) __sync_bool_compare_and_swap(a,b,c)
+
+#endif /* defined (__GNUC__) &&... */
+
+#elif ( __sparc__ || __sparc ) /* Marcin Ochab */
+
+#if (__sparcv9 || __sparcv9__)
+
+#if (__arch64__ || __arch64)
+typedef uint64_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+static inline int atomic_cas_64(atomic_t *lock, atomic_uint_t old, atomic_uint_t new) /* {{{ */
+{
+ __asm__ __volatile__("casx [%2], %3, %0 " : "=&r"(new) : "0"(new), "r"(lock), "r"(old): "memory");
+
+ return new;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ return (atomic_cas_64(lock, old, set)==old);
+}
+/* }}} */
+#else
+typedef uint32_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+static inline int atomic_cas_32(atomic_t *lock, atomic_uint_t old, atomic_uint_t new) /* {{{ */
+{
+ __asm__ __volatile__("cas [%2], %3, %0 " : "=&r"(new) : "0"(new), "r"(lock), "r"(old): "memory");
+
+ return new;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ return (atomic_cas_32(lock, old, set)==old);
+}
+/* }}} */
+#endif
+
+#else /* #if (__sparcv9 || __sparcv9__) */
+#error Sparc v8 and predecessors are not and will not be supported (see bug report 53310)
+#endif /* #if (__sparcv9 || __sparcv9__) */
+
+#else
+
+#error Unsupported processor. Please open a bug report (bugs.php.net).
+
+#endif
+
+static inline int fpm_spinlock(atomic_t *lock, int try_once) /* {{{ */
+{
+ if (try_once) {
+ return atomic_cmp_set(lock, 0, 1) ? 1 : 0;
+ }
+
+ for (;;) {
+
+ if (atomic_cmp_set(lock, 0, 1)) {
+ break;
+ }
+
+ sched_yield();
+ }
+
+ return 1;
+}
+/* }}} */
+
+#define fpm_unlock(lock) lock = 0
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c
new file mode 100644
index 0000000..84a9474
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_children.c
@@ -0,0 +1,478 @@
+
+ /* $Id: fpm_children.c,v 1.32.2.2 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "fpm.h"
+#include "fpm_children.h"
+#include "fpm_signals.h"
+#include "fpm_worker_pool.h"
+#include "fpm_sockets.h"
+#include "fpm_process_ctl.h"
+#include "fpm_php.h"
+#include "fpm_conf.h"
+#include "fpm_cleanup.h"
+#include "fpm_events.h"
+#include "fpm_clock.h"
+#include "fpm_stdio.h"
+#include "fpm_unix.h"
+#include "fpm_env.h"
+#include "fpm_scoreboard.h"
+#include "fpm_status.h"
+#include "fpm_log.h"
+
+#include "zlog.h"
+
+static time_t *last_faults;
+static int fault;
+
+static void fpm_children_cleanup(int which, void *arg) /* {{{ */
+{
+ free(last_faults);
+}
+/* }}} */
+
+static struct fpm_child_s *fpm_child_alloc() /* {{{ */
+{
+ struct fpm_child_s *ret;
+
+ ret = malloc(sizeof(struct fpm_child_s));
+
+ if (!ret) {
+ return 0;
+ }
+
+ memset(ret, 0, sizeof(*ret));
+ ret->scoreboard_i = -1;
+ return ret;
+}
+/* }}} */
+
+static void fpm_child_free(struct fpm_child_s *child) /* {{{ */
+{
+ free(child);
+}
+/* }}} */
+
+static void fpm_child_close(struct fpm_child_s *child, int in_event_loop) /* {{{ */
+{
+ if (child->fd_stdout != -1) {
+ if (in_event_loop) {
+ fpm_event_fire(&child->ev_stdout);
+ }
+ if (child->fd_stdout != -1) {
+ close(child->fd_stdout);
+ }
+ }
+
+ if (child->fd_stderr != -1) {
+ if (in_event_loop) {
+ fpm_event_fire(&child->ev_stderr);
+ }
+ if (child->fd_stderr != -1) {
+ close(child->fd_stderr);
+ }
+ }
+
+ fpm_child_free(child);
+}
+/* }}} */
+
+static void fpm_child_link(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp = child->wp;
+
+ ++wp->running_children;
+ ++fpm_globals.running_children;
+
+ child->next = wp->children;
+ if (child->next) {
+ child->next->prev = child;
+ }
+ child->prev = 0;
+ wp->children = child;
+}
+/* }}} */
+
+static void fpm_child_unlink(struct fpm_child_s *child) /* {{{ */
+{
+ --child->wp->running_children;
+ --fpm_globals.running_children;
+
+ if (child->prev) {
+ child->prev->next = child->next;
+ } else {
+ child->wp->children = child->next;
+ }
+
+ if (child->next) {
+ child->next->prev = child->prev;
+ }
+}
+/* }}} */
+
+static struct fpm_child_s *fpm_child_find(pid_t pid) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ struct fpm_child_s *child = 0;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+
+ for (child = wp->children; child; child = child->next) {
+ if (child->pid == pid) {
+ break;
+ }
+ }
+
+ if (child) break;
+ }
+
+ if (!child) {
+ return 0;
+ }
+
+ return child;
+}
+/* }}} */
+
+static void fpm_child_init(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ fpm_globals.max_requests = wp->config->pm_max_requests;
+
+ if (0 > fpm_stdio_init_child(wp) ||
+ 0 > fpm_log_init_child(wp) ||
+ 0 > fpm_status_init_child(wp) ||
+ 0 > fpm_unix_init_child(wp) ||
+ 0 > fpm_signals_init_child() ||
+ 0 > fpm_env_init_child(wp) ||
+ 0 > fpm_php_init_child(wp)) {
+
+ zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
+ exit(FPM_EXIT_SOFTWARE);
+ }
+}
+/* }}} */
+
+int fpm_children_free(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_child_s *next;
+
+ for (; child; child = next) {
+ next = child->next;
+ fpm_child_close(child, 0 /* in_event_loop */);
+ }
+
+ return 0;
+}
+/* }}} */
+
+void fpm_children_bury() /* {{{ */
+{
+ int status;
+ pid_t pid;
+ struct fpm_child_s *child;
+
+ while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+ char buf[128];
+ int severity = ZLOG_NOTICE;
+ int restart_child = 1;
+
+ child = fpm_child_find(pid);
+
+ if (WIFEXITED(status)) {
+
+ snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
+
+ /* if it's been killed because of dynamic process management
+ * don't restart it automaticaly
+ */
+ if (child && child->idle_kill) {
+ restart_child = 0;
+ }
+
+ if (WEXITSTATUS(status) != FPM_EXIT_OK) {
+ severity = ZLOG_WARNING;
+ }
+
+ } else if (WIFSIGNALED(status)) {
+ const char *signame = fpm_signal_names[WTERMSIG(status)];
+ const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
+
+ if (signame == NULL) {
+ signame = "";
+ }
+
+ snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
+
+ /* if it's been killed because of dynamic process management
+ * don't restart it automaticaly
+ */
+ if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) {
+ restart_child = 0;
+ }
+
+ if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
+ severity = ZLOG_WARNING;
+ }
+ } else if (WIFSTOPPED(status)) {
+
+ zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
+
+ if (child && child->tracer) {
+ child->tracer(child);
+ }
+
+ continue;
+ }
+
+ if (child) {
+ struct fpm_worker_pool_s *wp = child->wp;
+ struct timeval tv1, tv2;
+
+ fpm_child_unlink(child);
+
+ fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
+
+ fpm_clock_get(&tv1);
+
+ timersub(&tv1, &child->started, &tv2);
+
+ if (restart_child) {
+ if (!fpm_pctl_can_spawn_children()) {
+ severity = ZLOG_DEBUG;
+ }
+ zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
+ } else {
+ zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process managment after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
+ }
+
+ fpm_child_close(child, 1 /* in event_loop */);
+
+ fpm_pctl_child_exited();
+
+ if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
+ time_t now = tv1.tv_sec;
+ int restart_condition = 1;
+ int i;
+
+ last_faults[fault++] = now;
+
+ if (fault == fpm_global_config.emergency_restart_threshold) {
+ fault = 0;
+ }
+
+ for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
+ if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
+ restart_condition = 0;
+ break;
+ }
+ }
+
+ if (restart_condition) {
+
+ zlog(ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload", fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval);
+
+ fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
+ }
+ }
+
+ if (restart_child) {
+ fpm_children_make(wp, 1 /* in event loop */, 1, 0);
+
+ if (fpm_globals.is_child) {
+ break;
+ }
+ }
+ } else {
+ zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s. Please open a bug report (https://bugs.php.net).", pid, buf);
+ }
+ }
+}
+/* }}} */
+
+static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct fpm_child_s *c;
+
+ c = fpm_child_alloc();
+
+ if (!c) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to malloc new child", wp->config->name);
+ return 0;
+ }
+
+ c->wp = wp;
+ c->fd_stdout = -1; c->fd_stderr = -1;
+
+ if (0 > fpm_stdio_prepare_pipes(c)) {
+ fpm_child_free(c);
+ return 0;
+ }
+
+ if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
+ fpm_stdio_discard_pipes(c);
+ fpm_child_free(c);
+ return 0;
+ }
+
+ return c;
+}
+/* }}} */
+
+static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
+{
+ fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
+ fpm_stdio_discard_pipes(child);
+ fpm_child_free(child);
+}
+/* }}} */
+
+static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (wp == child->wp) {
+ continue;
+ }
+ fpm_scoreboard_free(wp->scoreboard);
+ }
+
+ fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
+ fpm_stdio_child_use_pipes(child);
+ fpm_child_free(child);
+}
+/* }}} */
+
+static void fpm_parent_resources_use(struct fpm_child_s *child) /* {{{ */
+{
+ fpm_stdio_parent_use_pipes(child);
+ fpm_child_link(child);
+}
+/* }}} */
+
+int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */
+{
+ pid_t pid;
+ struct fpm_child_s *child;
+ int max;
+ static int warned = 0;
+
+ if (wp->config->pm == PM_STYLE_DYNAMIC) {
+ if (!in_event_loop) { /* starting */
+ max = wp->config->pm_start_servers;
+ } else {
+ max = wp->running_children + nb_to_spawn;
+ }
+ } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ if (!in_event_loop) { /* starting */
+ max = 0; /* do not create any child at startup */
+ } else {
+ max = wp->running_children + nb_to_spawn;
+ }
+ } else { /* PM_STYLE_STATIC */
+ max = wp->config->pm_max_children;
+ }
+
+ /*
+ * fork children while:
+ * - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload)
+ * - wp->running_children < max : there is less than the max process for the current pool
+ * - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max):
+ * if fpm_global_config.process_max is set, FPM has not fork this number of processes (globaly)
+ */
+ while (fpm_pctl_can_spawn_children() && wp->running_children < max && (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max)) {
+
+ warned = 0;
+ child = fpm_resources_prepare(wp);
+
+ if (!child) {
+ return 2;
+ }
+
+ pid = fork();
+
+ switch (pid) {
+
+ case 0 :
+ fpm_child_resources_use(child);
+ fpm_globals.is_child = 1;
+ fpm_child_init(wp);
+ return 0;
+
+ case -1 :
+ zlog(ZLOG_SYSERROR, "fork() failed");
+
+ fpm_resources_discard(child);
+ return 2;
+
+ default :
+ child->pid = pid;
+ fpm_clock_get(&child->started);
+ fpm_parent_resources_use(child);
+
+ zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
+ }
+
+ }
+
+ if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) {
+ warned = 1;
+ zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'");
+ }
+
+ return 1; /* we are done */
+}
+/* }}} */
+
+int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
+
+ if (!wp->ondemand_event) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
+ // FIXME handle crash
+ return 1;
+ }
+
+ memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
+ fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp);
+ wp->socket_event_set = 1;
+ fpm_event_add(wp->ondemand_event, 0);
+
+ return 1;
+ }
+ return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
+}
+/* }}} */
+
+int fpm_children_init_main() /* {{{ */
+{
+ if (fpm_global_config.emergency_restart_threshold &&
+ fpm_global_config.emergency_restart_interval) {
+
+ last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
+
+ if (!last_faults) {
+ return -1;
+ }
+
+ memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
+ }
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_children.h b/sapi/fpm/fpm/fpm_children.h
new file mode 100644
index 0000000..9c79f23
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_children.h
@@ -0,0 +1,36 @@
+
+ /* $Id: fpm_children.h,v 1.9 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CHILDREN_H
+#define FPM_CHILDREN_H 1
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "fpm_worker_pool.h"
+#include "fpm_events.h"
+
+int fpm_children_create_initial(struct fpm_worker_pool_s *wp);
+int fpm_children_free(struct fpm_child_s *child);
+void fpm_children_bury();
+int fpm_children_init_main();
+int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug);
+
+struct fpm_child_s;
+
+struct fpm_child_s {
+ struct fpm_child_s *prev, *next;
+ struct timeval started;
+ struct fpm_worker_pool_s *wp;
+ struct fpm_event_s ev_stdout, ev_stderr;
+ int shm_slot_i;
+ int fd_stdout, fd_stderr;
+ void (*tracer)(struct fpm_child_s *);
+ struct timeval slow_logged;
+ int idle_kill;
+ pid_t pid;
+ int scoreboard_i;
+};
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_cleanup.c b/sapi/fpm/fpm/fpm_cleanup.c
new file mode 100644
index 0000000..260ddb3
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_cleanup.c
@@ -0,0 +1,52 @@
+
+ /* $Id: fpm_cleanup.c,v 1.8 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdlib.h>
+
+#include "fpm_arrays.h"
+#include "fpm_cleanup.h"
+
+struct cleanup_s {
+ int type;
+ void (*cleanup)(int, void *);
+ void *arg;
+};
+
+static struct fpm_array_s cleanups = { .sz = sizeof(struct cleanup_s) };
+
+int fpm_cleanup_add(int type, void (*cleanup)(int, void *), void *arg) /* {{{ */
+{
+ struct cleanup_s *c;
+
+ c = fpm_array_push(&cleanups);
+
+ if (!c) {
+ return -1;
+ }
+
+ c->type = type;
+ c->cleanup = cleanup;
+ c->arg = arg;
+
+ return 0;
+}
+/* }}} */
+
+void fpm_cleanups_run(int type) /* {{{ */
+{
+ struct cleanup_s *c = fpm_array_item_last(&cleanups);
+ int cl = cleanups.used;
+
+ for ( ; cl--; c--) {
+ if (c->type & type) {
+ c->cleanup(type, c->arg);
+ }
+ }
+
+ fpm_array_free(&cleanups);
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_cleanup.h b/sapi/fpm/fpm/fpm_cleanup.h
new file mode 100644
index 0000000..4d7cf39
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_cleanup.h
@@ -0,0 +1,21 @@
+
+ /* $Id: fpm_cleanup.h,v 1.5 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CLEANUP_H
+#define FPM_CLEANUP_H 1
+
+int fpm_cleanup_add(int type, void (*cleanup)(int, void *), void *);
+void fpm_cleanups_run(int type);
+
+enum {
+ FPM_CLEANUP_CHILD = (1 << 0),
+ FPM_CLEANUP_PARENT_EXIT = (1 << 1),
+ FPM_CLEANUP_PARENT_EXIT_MAIN = (1 << 2),
+ FPM_CLEANUP_PARENT_EXEC = (1 << 3),
+ FPM_CLEANUP_PARENT = (1 << 1) | (1 << 2) | (1 << 3),
+ FPM_CLEANUP_ALL = ~0,
+};
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_clock.c b/sapi/fpm/fpm/fpm_clock.c
new file mode 100644
index 0000000..66751b8
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_clock.c
@@ -0,0 +1,121 @@
+
+ /* $Id: fpm_clock.c,v 1.4 2008/09/18 23:19:59 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#if defined(HAVE_CLOCK_GETTIME)
+#include <time.h> /* for CLOCK_MONOTONIC */
+#endif
+
+#include "fpm_clock.h"
+#include "zlog.h"
+
+
+/* posix monotonic clock - preferred source of time */
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+
+static int monotonic_works;
+
+int fpm_clock_init() /* {{{ */
+{
+ struct timespec ts;
+
+ monotonic_works = 0;
+
+ if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ monotonic_works = 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_clock_get(struct timeval *tv) /* {{{ */
+{
+ if (monotonic_works) {
+ struct timespec ts;
+
+ if (0 > clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ zlog(ZLOG_SYSERROR, "clock_gettime() failed");
+ return -1;
+ }
+
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+ return 0;
+ }
+
+ return gettimeofday(tv, 0);
+}
+/* }}} */
+
+/* macosx clock */
+#elif defined(HAVE_CLOCK_GET_TIME)
+
+#include <mach/mach.h>
+#include <mach/clock.h>
+#include <mach/mach_error.h>
+
+static clock_serv_t mach_clock;
+
+/* this code borrowed from here: http://lists.apple.com/archives/Darwin-development/2002/Mar/msg00746.html */
+/* mach_clock also should be re-initialized in child process after fork */
+int fpm_clock_init() /* {{{ */
+{
+ kern_return_t ret;
+ mach_timespec_t aTime;
+
+ ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &mach_clock);
+
+ if (ret != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "host_get_clock_service() failed: %s", mach_error_string(ret));
+ return -1;
+ }
+
+ /* test if it works */
+ ret = clock_get_time(mach_clock, &aTime);
+
+ if (ret != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "clock_get_time() failed: %s", mach_error_string(ret));
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_clock_get(struct timeval *tv) /* {{{ */
+{
+ kern_return_t ret;
+ mach_timespec_t aTime;
+
+ ret = clock_get_time(mach_clock, &aTime);
+
+ if (ret != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "clock_get_time() failed: %s", mach_error_string(ret));
+ return -1;
+ }
+
+ tv->tv_sec = aTime.tv_sec;
+ tv->tv_usec = aTime.tv_nsec / 1000;
+
+ return 0;
+}
+/* }}} */
+
+#else /* no clock */
+
+int fpm_clock_init() /* {{{ */
+{
+ return 0;
+}
+/* }}} */
+
+int fpm_clock_get(struct timeval *tv) /* {{{ */
+{
+ return gettimeofday(tv, 0);
+}
+/* }}} */
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_clock.h b/sapi/fpm/fpm/fpm_clock.h
new file mode 100644
index 0000000..6aab959
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_clock.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_clock.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CLOCK_H
+#define FPM_CLOCK_H 1
+
+#include <sys/time.h>
+
+int fpm_clock_init();
+int fpm_clock_get(struct timeval *tv);
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
new file mode 100644
index 0000000..25e2cc4
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -0,0 +1,1658 @@
+
+ /* $Id: fpm_conf.c,v 1.33.2.3 2008/12/13 03:50:29 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+#ifdef HAVE_GLOB
+# ifndef PHP_WIN32
+# include <glob.h>
+# else
+# include "win32/glob.h"
+# endif
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "php.h"
+#include "zend_ini_scanner.h"
+#include "zend_globals.h"
+#include "zend_stream.h"
+#include "php_syslog.h"
+
+#include "fpm.h"
+#include "fpm_conf.h"
+#include "fpm_stdio.h"
+#include "fpm_worker_pool.h"
+#include "fpm_cleanup.h"
+#include "fpm_php.h"
+#include "fpm_sockets.h"
+#include "fpm_shm.h"
+#include "fpm_status.h"
+#include "fpm_log.h"
+#include "fpm_events.h"
+#include "zlog.h"
+
+#define STR2STR(a) (a ? a : "undefined")
+#define BOOL2STR(a) (a ? "yes" : "no")
+#define GO(field) offsetof(struct fpm_global_config_s, field)
+#define WPO(field) offsetof(struct fpm_worker_pool_config_s, field)
+
+static int fpm_conf_load_ini_file(char *filename TSRMLS_DC);
+static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset);
+#if 0 /* not used for now */
+static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset);
+#endif
+static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset);
+#ifdef HAVE_SYSLOG_H
+static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset);
+#endif
+
+struct fpm_global_config_s fpm_global_config = {
+ .daemonize = 1,
+#ifdef HAVE_SYSLOG_H
+ .syslog_facility = -1,
+#endif
+ .process_max = 0,
+ .process_priority = 64, /* 64 means unset */
+};
+static struct fpm_worker_pool_s *current_wp = NULL;
+static int ini_recursion = 0;
+static char *ini_filename = NULL;
+static int ini_lineno = 0;
+static char *ini_include = NULL;
+
+/*
+ * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
+ */
+static struct ini_value_parser_s ini_fpm_global_options[] = {
+ { "pid", &fpm_conf_set_string, GO(pid_file) },
+ { "error_log", &fpm_conf_set_string, GO(error_log) },
+#ifdef HAVE_SYSLOG_H
+ { "syslog.ident", &fpm_conf_set_string, GO(syslog_ident) },
+ { "syslog.facility", &fpm_conf_set_syslog_facility, GO(syslog_facility) },
+#endif
+ { "log_level", &fpm_conf_set_log_level, GO(log_level) },
+ { "emergency_restart_threshold", &fpm_conf_set_integer, GO(emergency_restart_threshold) },
+ { "emergency_restart_interval", &fpm_conf_set_time, GO(emergency_restart_interval) },
+ { "process_control_timeout", &fpm_conf_set_time, GO(process_control_timeout) },
+ { "process.max", &fpm_conf_set_integer, GO(process_max) },
+ { "process.priority", &fpm_conf_set_integer, GO(process_priority) },
+ { "daemonize", &fpm_conf_set_boolean, GO(daemonize) },
+ { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) },
+ { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) },
+ { "events.mechanism", &fpm_conf_set_string, GO(events_mechanism) },
+ { 0, 0, 0 }
+};
+
+/*
+ * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
+ */
+static struct ini_value_parser_s ini_fpm_pool_options[] = {
+ { "prefix", &fpm_conf_set_string, WPO(prefix) },
+ { "user", &fpm_conf_set_string, WPO(user) },
+ { "group", &fpm_conf_set_string, WPO(group) },
+ { "listen", &fpm_conf_set_string, WPO(listen_address) },
+ { "listen.backlog", &fpm_conf_set_integer, WPO(listen_backlog) },
+ { "listen.owner", &fpm_conf_set_string, WPO(listen_owner) },
+ { "listen.group", &fpm_conf_set_string, WPO(listen_group) },
+ { "listen.mode", &fpm_conf_set_string, WPO(listen_mode) },
+ { "listen.allowed_clients", &fpm_conf_set_string, WPO(listen_allowed_clients) },
+ { "process.priority", &fpm_conf_set_integer, WPO(process_priority) },
+ { "pm", &fpm_conf_set_pm, WPO(pm) },
+ { "pm.max_children", &fpm_conf_set_integer, WPO(pm_max_children) },
+ { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) },
+ { "pm.min_spare_servers", &fpm_conf_set_integer, WPO(pm_min_spare_servers) },
+ { "pm.max_spare_servers", &fpm_conf_set_integer, WPO(pm_max_spare_servers) },
+ { "pm.process_idle_timeout", &fpm_conf_set_time, WPO(pm_process_idle_timeout) },
+ { "pm.max_requests", &fpm_conf_set_integer, WPO(pm_max_requests) },
+ { "pm.status_path", &fpm_conf_set_string, WPO(pm_status_path) },
+ { "ping.path", &fpm_conf_set_string, WPO(ping_path) },
+ { "ping.response", &fpm_conf_set_string, WPO(ping_response) },
+ { "access.log", &fpm_conf_set_string, WPO(access_log) },
+ { "access.format", &fpm_conf_set_string, WPO(access_format) },
+ { "slowlog", &fpm_conf_set_string, WPO(slowlog) },
+ { "request_slowlog_timeout", &fpm_conf_set_time, WPO(request_slowlog_timeout) },
+ { "request_terminate_timeout", &fpm_conf_set_time, WPO(request_terminate_timeout) },
+ { "rlimit_files", &fpm_conf_set_integer, WPO(rlimit_files) },
+ { "rlimit_core", &fpm_conf_set_rlimit_core, WPO(rlimit_core) },
+ { "chroot", &fpm_conf_set_string, WPO(chroot) },
+ { "chdir", &fpm_conf_set_string, WPO(chdir) },
+ { "catch_workers_output", &fpm_conf_set_boolean, WPO(catch_workers_output) },
+ { "security.limit_extensions", &fpm_conf_set_string, WPO(security_limit_extensions) },
+ { 0, 0, 0 }
+};
+
+static int fpm_conf_is_dir(char *path) /* {{{ */
+{
+ struct stat sb;
+
+ if (stat(path, &sb) != 0) {
+ return 0;
+ }
+
+ return (sb.st_mode & S_IFMT) == S_IFDIR;
+}
+/* }}} */
+
+/*
+ * Expands the '$pool' token in a dynamically allocated string
+ */
+static int fpm_conf_expand_pool_name(char **value) {
+ char *token;
+
+ if (!value || !*value) {
+ return 0;
+ }
+
+ while (*value && (token = strstr(*value, "$pool"))) {
+ char *buf;
+ char *p2 = token + strlen("$pool");
+
+ /* If we are not in a pool, we cannot expand this name now */
+ if (!current_wp || !current_wp->config || !current_wp->config->name) {
+ return -1;
+ }
+
+ /* "aaa$poolbbb" becomes "aaa\0oolbbb" */
+ token[0] = '\0';
+
+ /* Build a brand new string with the expanded token */
+ spprintf(&buf, 0, "%s%s%s", *value, current_wp->config->name, p2);
+
+ /* Free the previous value and save the new one */
+ free(*value);
+ *value = strdup(buf);
+ efree(buf);
+ }
+
+ return 0;
+}
+
+static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ long value_y = !strcasecmp(val, "1");
+ long value_n = !strcasecmp(val, "");
+
+ if (!value_y && !value_n) {
+ return "invalid boolean value";
+ }
+
+ * (int *) ((char *) *config + offset) = value_y ? 1 : 0;
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char **config_val = (char **) ((char *) *config + offset);
+
+ if (!config_val) {
+ return "internal error: NULL value";
+ }
+
+ /* Check if there is a previous value to deallocate */
+ if (*config_val) {
+ free(*config_val);
+ }
+
+ *config_val = strdup(Z_STRVAL_P(value));
+ if (!*config_val) {
+ return "fpm_conf_set_string(): strdup() failed";
+ }
+ if (fpm_conf_expand_pool_name(config_val) == -1) {
+ return "Can't use '$pool' when the pool is not defined";
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ char *p;
+
+ /* we don't use strtol because we don't want to allow negative values */
+ for (p = val; *p; p++) {
+ if (p == val && *p == '-') continue;
+ if (*p < '0' || *p > '9') {
+ return "is not a valid number (greater or equal than zero)";
+ }
+ }
+ * (int *) ((char *) *config + offset) = atoi(val);
+ return NULL;
+}
+/* }}} */
+
+#if 0 /* not used for now */
+static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ char *p;
+
+ for (p = val; *p; p++) {
+ if ( p == val && *p == '-' ) continue;
+ if (*p < '0' || *p > '9') {
+ return "is not a valid number (greater or equal than zero)";
+ }
+ }
+ * (long int *) ((char *) *config + offset) = atol(val);
+ return NULL;
+}
+/* }}} */
+#endif
+
+static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int len = strlen(val);
+ char suffix;
+ int seconds;
+ if (!len) {
+ return "invalid time value";
+ }
+
+ suffix = val[len-1];
+ switch (suffix) {
+ case 'm' :
+ val[len-1] = '\0';
+ seconds = 60 * atoi(val);
+ break;
+ case 'h' :
+ val[len-1] = '\0';
+ seconds = 60 * 60 * atoi(val);
+ break;
+ case 'd' :
+ val[len-1] = '\0';
+ seconds = 24 * 60 * 60 * atoi(val);
+ break;
+ case 's' : /* s is the default suffix */
+ val[len-1] = '\0';
+ suffix = '0';
+ default :
+ if (suffix < '0' || suffix > '9') {
+ return "unknown suffix used in time value";
+ }
+ seconds = atoi(val);
+ break;
+ }
+
+ * (int *) ((char *) *config + offset) = seconds;
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int log_level;
+
+ if (!strcasecmp(val, "debug")) {
+ log_level = ZLOG_DEBUG;
+ } else if (!strcasecmp(val, "notice")) {
+ log_level = ZLOG_NOTICE;
+ } else if (!strcasecmp(val, "warning") || !strcasecmp(val, "warn")) {
+ log_level = ZLOG_WARNING;
+ } else if (!strcasecmp(val, "error")) {
+ log_level = ZLOG_ERROR;
+ } else if (!strcasecmp(val, "alert")) {
+ log_level = ZLOG_ALERT;
+ } else {
+ return "invalid value for 'log_level'";
+ }
+
+ * (int *) ((char *) *config + offset) = log_level;
+ return NULL;
+}
+/* }}} */
+
+#ifdef HAVE_SYSLOG_H
+static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int *conf = (int *) ((char *) *config + offset);
+
+#ifdef LOG_AUTH
+ if (!strcasecmp(val, "AUTH")) {
+ *conf = LOG_AUTH;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_AUTHPRIV
+ if (!strcasecmp(val, "AUTHPRIV")) {
+ *conf = LOG_AUTHPRIV;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_CRON
+ if (!strcasecmp(val, "CRON")) {
+ *conf = LOG_CRON;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_DAEMON
+ if (!strcasecmp(val, "DAEMON")) {
+ *conf = LOG_DAEMON;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_FTP
+ if (!strcasecmp(val, "FTP")) {
+ *conf = LOG_FTP;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_KERN
+ if (!strcasecmp(val, "KERN")) {
+ *conf = LOG_KERN;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LPR
+ if (!strcasecmp(val, "LPR")) {
+ *conf = LOG_LPR;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_MAIL
+ if (!strcasecmp(val, "MAIL")) {
+ *conf = LOG_MAIL;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_NEWS
+ if (!strcasecmp(val, "NEWS")) {
+ *conf = LOG_NEWS;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_SYSLOG
+ if (!strcasecmp(val, "SYSLOG")) {
+ *conf = LOG_SYSLOG;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_USER
+ if (!strcasecmp(val, "USER")) {
+ *conf = LOG_USER;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_UUCP
+ if (!strcasecmp(val, "UUCP")) {
+ *conf = LOG_UUCP;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL0
+ if (!strcasecmp(val, "LOCAL0")) {
+ *conf = LOG_LOCAL0;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL1
+ if (!strcasecmp(val, "LOCAL1")) {
+ *conf = LOG_LOCAL1;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL2
+ if (!strcasecmp(val, "LOCAL2")) {
+ *conf = LOG_LOCAL2;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL3
+ if (!strcasecmp(val, "LOCAL3")) {
+ *conf = LOG_LOCAL3;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL4
+ if (!strcasecmp(val, "LOCAL4")) {
+ *conf = LOG_LOCAL4;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL5
+ if (!strcasecmp(val, "LOCAL5")) {
+ *conf = LOG_LOCAL5;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL6
+ if (!strcasecmp(val, "LOCAL6")) {
+ *conf = LOG_LOCAL6;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL7
+ if (!strcasecmp(val, "LOCAL7")) {
+ *conf = LOG_LOCAL7;
+ return NULL;
+ }
+#endif
+
+ return "invalid value";
+}
+/* }}} */
+#endif
+
+static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int *ptr = (int *) ((char *) *config + offset);
+
+ if (!strcasecmp(val, "unlimited")) {
+ *ptr = -1;
+ } else {
+ int int_value;
+ void *subconf = &int_value;
+ char *error;
+
+ error = fpm_conf_set_integer(value, &subconf, 0);
+
+ if (error) {
+ return error;
+ }
+
+ if (int_value < 0) {
+ return "must be greater than zero or 'unlimited'";
+ }
+
+ *ptr = int_value;
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ struct fpm_worker_pool_config_s *c = *config;
+ if (!strcasecmp(val, "static")) {
+ c->pm = PM_STYLE_STATIC;
+ } else if (!strcasecmp(val, "dynamic")) {
+ c->pm = PM_STYLE_DYNAMIC;
+ } else if (!strcasecmp(val, "ondemand")) {
+ c->pm = PM_STYLE_ONDEMAND;
+ } else {
+ return "invalid process manager (static, dynamic or ondemand)";
+ }
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_array(zval *key, zval *value, void **config, int convert_to_bool) /* {{{ */
+{
+ struct key_value_s *kv;
+ struct key_value_s ***parent = (struct key_value_s ***) config;
+ int b;
+ void *subconf = &b;
+
+ kv = malloc(sizeof(*kv));
+
+ if (!kv) {
+ return "malloc() failed";
+ }
+
+ memset(kv, 0, sizeof(*kv));
+ kv->key = strdup(Z_STRVAL_P(key));
+
+ if (!kv->key) {
+ return "fpm_conf_set_array: strdup(key) failed";
+ }
+
+ if (convert_to_bool) {
+ char *err = fpm_conf_set_boolean(value, &subconf, 0);
+ if (err) return err;
+ kv->value = strdup(b ? "1" : "0");
+ } else {
+ kv->value = strdup(Z_STRVAL_P(value));
+ if (fpm_conf_expand_pool_name(&kv->value) == -1) {
+ return "Can't use '$pool' when the pool is not defined";
+ }
+ }
+
+ if (!kv->value) {
+ free(kv->key);
+ return "fpm_conf_set_array: strdup(value) failed";
+ }
+
+ kv->next = **parent;
+ **parent = kv;
+ return NULL;
+}
+/* }}} */
+
+static void *fpm_worker_pool_config_alloc() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ wp = fpm_worker_pool_alloc();
+
+ if (!wp) {
+ return 0;
+ }
+
+ wp->config = malloc(sizeof(struct fpm_worker_pool_config_s));
+
+ if (!wp->config) {
+ return 0;
+ }
+
+ memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s));
+ wp->config->listen_backlog = FPM_BACKLOG_DEFAULT;
+ wp->config->pm_process_idle_timeout = 10; /* 10s by default */
+ wp->config->process_priority = 64; /* 64 means unset */
+
+ if (!fpm_worker_all_pools) {
+ fpm_worker_all_pools = wp;
+ } else {
+ struct fpm_worker_pool_s *tmp = fpm_worker_all_pools;
+ while (tmp) {
+ if (!tmp->next) {
+ tmp->next = wp;
+ break;
+ }
+ tmp = tmp->next;
+ }
+ }
+
+ current_wp = wp;
+ return wp->config;
+}
+/* }}} */
+
+int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
+{
+ struct key_value_s *kv, *kv_next;
+
+ free(wpc->name);
+ free(wpc->prefix);
+ free(wpc->user);
+ free(wpc->group);
+ free(wpc->listen_address);
+ free(wpc->listen_owner);
+ free(wpc->listen_group);
+ free(wpc->listen_mode);
+ free(wpc->listen_allowed_clients);
+ free(wpc->pm_status_path);
+ free(wpc->ping_path);
+ free(wpc->ping_response);
+ free(wpc->access_log);
+ free(wpc->access_format);
+ free(wpc->slowlog);
+ free(wpc->chroot);
+ free(wpc->chdir);
+ free(wpc->security_limit_extensions);
+
+ for (kv = wpc->php_values; kv; kv = kv_next) {
+ kv_next = kv->next;
+ free(kv->key);
+ free(kv->value);
+ free(kv);
+ }
+ for (kv = wpc->php_admin_values; kv; kv = kv_next) {
+ kv_next = kv->next;
+ free(kv->key);
+ free(kv->value);
+ free(kv);
+ }
+ for (kv = wpc->env; kv; kv = kv_next) {
+ kv_next = kv->next;
+ free(kv->key);
+ free(kv->value);
+ free(kv);
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, char *default_prefix, int expand) /* {{{ */
+{
+ char *prefix = NULL;
+ char *full_path;
+
+ if (!path || !*path || **path == '/') {
+ return 0;
+ }
+
+ if (wp && wp->config) {
+ prefix = wp->config->prefix;
+ }
+
+ /* if the wp prefix is not set */
+ if (prefix == NULL) {
+ prefix = fpm_globals.prefix;
+ }
+
+ /* if the global prefix is not set */
+ if (prefix == NULL) {
+ prefix = default_prefix ? default_prefix : PHP_PREFIX;
+ }
+
+ if (expand) {
+ char *tmp;
+ tmp = strstr(*path, "$prefix");
+ if (tmp != NULL) {
+
+ if (tmp != *path) {
+ zlog(ZLOG_ERROR, "'$prefix' must be use at the begining of the value");
+ return -1;
+ }
+
+ if (strlen(*path) > strlen("$prefix")) {
+ free(*path);
+ tmp = strdup((*path) + strlen("$prefix"));
+ *path = tmp;
+ } else {
+ free(*path);
+ *path = NULL;
+ }
+ }
+ }
+
+ if (*path) {
+ spprintf(&full_path, 0, "%s/%s", prefix, *path);
+ free(*path);
+ *path = strdup(full_path);
+ efree(full_path);
+ } else {
+ *path = strdup(prefix);
+ }
+
+ if (**path != '/' && wp != NULL && wp->config) {
+ return fpm_evaluate_full_path(path, NULL, default_prefix, expand);
+ }
+ return 0;
+}
+/* }}} */
+
+static int fpm_conf_process_all_pools() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp, *wp2;
+
+ if (!fpm_worker_all_pools) {
+ zlog(ZLOG_ERROR, "No pool defined. at least one pool section must be specified in config file");
+ return -1;
+ }
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+
+ /* prefix */
+ if (wp->config->prefix && *wp->config->prefix) {
+ fpm_evaluate_full_path(&wp->config->prefix, NULL, NULL, 0);
+
+ if (!fpm_conf_is_dir(wp->config->prefix)) {
+ zlog(ZLOG_ERROR, "[pool %s] the prefix '%s' does not exist or is not a directory", wp->config->name, wp->config->prefix);
+ return -1;
+ }
+ }
+
+ /* alert if user is not set only if we are not root*/
+ if (!wp->config->user && !geteuid()) {
+ zlog(ZLOG_ALERT, "[pool %s] user has not been defined", wp->config->name);
+ return -1;
+ }
+
+ /* listen */
+ if (wp->config->listen_address && *wp->config->listen_address) {
+ wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address);
+
+ if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') {
+ fpm_evaluate_full_path(&wp->config->listen_address, wp, NULL, 0);
+ }
+ } else {
+ zlog(ZLOG_ALERT, "[pool %s] no listen address have been defined!", wp->config->name);
+ return -1;
+ }
+
+ if (wp->config->process_priority != 64 && (wp->config->process_priority < -19 || wp->config->process_priority > 20)) {
+ zlog(ZLOG_ERROR, "[pool %s] process.priority must be included into [-19,20]", wp->config->name);
+ return -1;
+ }
+
+ /* pm */
+ if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC && wp->config->pm != PM_STYLE_ONDEMAND) {
+ zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static, dynamic or ondemand)", wp->config->name);
+ return -1;
+ }
+
+ /* pm.max_children */
+ if (wp->config->pm_max_children < 1) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.max_children must be a positive value", wp->config->name);
+ return -1;
+ }
+
+ /* pm.start_servers, pm.min_spare_servers, pm.max_spare_servers */
+ if (wp->config->pm == PM_STYLE_DYNAMIC) {
+ struct fpm_worker_pool_config_s *config = wp->config;
+
+ if (config->pm_min_spare_servers <= 0) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) must be a positive value", wp->config->name, config->pm_min_spare_servers);
+ return -1;
+ }
+
+ if (config->pm_max_spare_servers <= 0) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must be a positive value", wp->config->name, config->pm_max_spare_servers);
+ return -1;
+ }
+
+ if (config->pm_min_spare_servers > config->pm_max_children ||
+ config->pm_max_spare_servers > config->pm_max_children) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) and pm.max_spare_servers(%d) cannot be greater than pm.max_children(%d)", wp->config->name, config->pm_min_spare_servers, config->pm_max_spare_servers, config->pm_max_children);
+ return -1;
+ }
+
+ if (config->pm_max_spare_servers < config->pm_min_spare_servers) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must not be less than pm.min_spare_servers(%d)", wp->config->name, config->pm_max_spare_servers, config->pm_min_spare_servers);
+ return -1;
+ }
+
+ if (config->pm_start_servers <= 0) {
+ config->pm_start_servers = config->pm_min_spare_servers + ((config->pm_max_spare_servers - config->pm_min_spare_servers) / 2);
+ zlog(ZLOG_WARNING, "[pool %s] pm.start_servers is not set. It's been set to %d.", wp->config->name, config->pm_start_servers);
+
+ } else if (config->pm_start_servers < config->pm_min_spare_servers || config->pm_start_servers > config->pm_max_spare_servers) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.start_servers(%d) must not be less than pm.min_spare_servers(%d) and not greater than pm.max_spare_servers(%d)", wp->config->name, config->pm_start_servers, config->pm_min_spare_servers, config->pm_max_spare_servers);
+ return -1;
+ }
+ } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ struct fpm_worker_pool_config_s *config = wp->config;
+
+ if (!fpm_event_support_edge_trigger()) {
+ zlog(ZLOG_ALERT, "[pool %s] ondemand process manager can ONLY be used when events.mechanisme is either epoll (Linux) or kqueue (*BSD).", wp->config->name);
+ return -1;
+ }
+
+ if (config->pm_process_idle_timeout < 1) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout);
+ return -1;
+ }
+
+ if (config->listen_backlog < FPM_BACKLOG_DEFAULT) {
+ zlog(ZLOG_WARNING, "[pool %s] listen.backlog(%d) was too low for the ondemand process manager. I updated it for you to %d.", wp->config->name, config->listen_backlog, FPM_BACKLOG_DEFAULT);
+ config->listen_backlog = FPM_BACKLOG_DEFAULT;
+ }
+
+ /* certainely useless but proper */
+ config->pm_start_servers = 0;
+ config->pm_min_spare_servers = 0;
+ config->pm_max_spare_servers = 0;
+ }
+
+ /* status */
+ if (wp->config->pm_status_path && *wp->config->pm_status_path) {
+ int i;
+ char *status = wp->config->pm_status_path;
+
+ if (*status != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must start with a '/'", wp->config->name, status);
+ return -1;
+ }
+
+ if (strlen(status) < 2) {
+ zlog(ZLOG_ERROR, "[pool %s] the status path '%s' is not long enough", wp->config->name, status);
+ return -1;
+ }
+
+ for (i = 0; i < strlen(status); i++) {
+ if (!isalnum(status[i]) && status[i] != '/' && status[i] != '-' && status[i] != '_' && status[i] != '.') {
+ zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must contain only the following characters '[alphanum]/_-.'", wp->config->name, status);
+ return -1;
+ }
+ }
+ }
+
+ /* ping */
+ if (wp->config->ping_path && *wp->config->ping_path) {
+ char *ping = wp->config->ping_path;
+ int i;
+
+ if (*ping != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must start with a '/'", wp->config->name, ping);
+ return -1;
+ }
+
+ if (strlen(ping) < 2) {
+ zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' is not long enough", wp->config->name, ping);
+ return -1;
+ }
+
+ for (i = 0; i < strlen(ping); i++) {
+ if (!isalnum(ping[i]) && ping[i] != '/' && ping[i] != '-' && ping[i] != '_' && ping[i] != '.') {
+ zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must containt only the following characters '[alphanum]/_-.'", wp->config->name, ping);
+ return -1;
+ }
+ }
+
+ if (!wp->config->ping_response) {
+ wp->config->ping_response = strdup("pong");
+ } else {
+ if (strlen(wp->config->ping_response) < 1) {
+ zlog(ZLOG_ERROR, "[pool %s] the ping response page '%s' is not long enough", wp->config->name, wp->config->ping_response);
+ return -1;
+ }
+ }
+ } else {
+ if (wp->config->ping_response) {
+ free(wp->config->ping_response);
+ wp->config->ping_response = NULL;
+ }
+ }
+
+ /* access.log, access.format */
+ if (wp->config->access_log && *wp->config->access_log) {
+ fpm_evaluate_full_path(&wp->config->access_log, wp, NULL, 0);
+ if (!wp->config->access_format) {
+ wp->config->access_format = strdup("%R - %u %t \"%m %r\" %s");
+ }
+ }
+
+ if (wp->config->request_terminate_timeout) {
+ fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_terminate_timeout * 1000) / 3) : (wp->config->request_terminate_timeout * 1000) / 3;
+ }
+
+ /* slowlog */
+ if (wp->config->slowlog && *wp->config->slowlog) {
+ fpm_evaluate_full_path(&wp->config->slowlog, wp, NULL, 0);
+ }
+
+ /* request_slowlog_timeout */
+ if (wp->config->request_slowlog_timeout) {
+#if HAVE_FPM_TRACE
+ if (! (wp->config->slowlog && *wp->config->slowlog)) {
+ zlog(ZLOG_ERROR, "[pool %s] 'slowlog' must be specified for use with 'request_slowlog_timeout'", wp->config->name);
+ return -1;
+ }
+#else
+ static int warned = 0;
+
+ if (!warned) {
+ zlog(ZLOG_WARNING, "[pool %s] 'request_slowlog_timeout' is not supported on your system", wp->config->name);
+ warned = 1;
+ }
+
+ wp->config->request_slowlog_timeout = 0;
+#endif
+
+ if (wp->config->slowlog && *wp->config->slowlog) {
+ int fd;
+
+ fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "Unable to create or open slowlog(%s)", wp->config->slowlog);
+ return -1;
+ }
+ close(fd);
+ }
+
+ fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_slowlog_timeout * 1000) / 3) : (wp->config->request_slowlog_timeout * 1000) / 3;
+
+ if (wp->config->request_terminate_timeout && wp->config->request_slowlog_timeout > wp->config->request_terminate_timeout) {
+ zlog(ZLOG_ERROR, "[pool %s] 'request_slowlog_timeout' (%d) can't be greater than 'request_terminate_timeout' (%d)", wp->config->name, wp->config->request_slowlog_timeout, wp->config->request_terminate_timeout);
+ return -1;
+ }
+ }
+
+ /* chroot */
+ if (wp->config->chroot && *wp->config->chroot) {
+
+ fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
+
+ if (*wp->config->chroot != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot);
+ return -1;
+ }
+
+ if (!fpm_conf_is_dir(wp->config->chroot)) {
+ zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' does not exist or is not a directory", wp->config->name, wp->config->chroot);
+ return -1;
+ }
+ }
+
+ /* chdir */
+ if (wp->config->chdir && *wp->config->chdir) {
+
+ fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0);
+
+ if (*wp->config->chdir != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir);
+ return -1;
+ }
+
+ if (wp->config->chroot) {
+ char *buf;
+
+ spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir);
+
+ if (!fpm_conf_is_dir(buf)) {
+ zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' within the chroot path '%s' ('%s') does not exist or is not a directory", wp->config->name, wp->config->chdir, wp->config->chroot, buf);
+ efree(buf);
+ return -1;
+ }
+
+ efree(buf);
+ } else {
+ if (!fpm_conf_is_dir(wp->config->chdir)) {
+ zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir);
+ return -1;
+ }
+ }
+ }
+
+ /* security.limit_extensions */
+ if (!wp->config->security_limit_extensions) {
+ wp->config->security_limit_extensions = strdup(".php .phar");
+ }
+
+ if (*wp->config->security_limit_extensions) {
+ int nb_ext;
+ char *ext;
+ char *security_limit_extensions;
+ char *limit_extensions;
+
+
+ /* strdup because strtok(3) alters the string it parses */
+ security_limit_extensions = strdup(wp->config->security_limit_extensions);
+ limit_extensions = security_limit_extensions;
+ nb_ext = 0;
+
+ /* find the number of extensions */
+ while ((ext = strtok(limit_extensions, " \t"))) {
+ limit_extensions = NULL;
+ nb_ext++;
+ }
+ free(security_limit_extensions);
+
+ /* if something found */
+ if (nb_ext > 0) {
+
+ /* malloc the extension array */
+ wp->limit_extensions = malloc(sizeof(char *) * (nb_ext + 1));
+ if (!wp->limit_extensions) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to malloc extensions array", wp->config->name);
+ return -1;
+ }
+
+ /* strdup because strtok(3) alters the string it parses */
+ security_limit_extensions = strdup(wp->config->security_limit_extensions);
+ limit_extensions = security_limit_extensions;
+ nb_ext = 0;
+
+ /* parse the string and save the extension in the array */
+ while ((ext = strtok(security_limit_extensions, " \t"))) {
+ security_limit_extensions = NULL;
+ wp->limit_extensions[nb_ext++] = strdup(ext);
+ }
+
+ /* end the array with NULL in order to parse it */
+ wp->limit_extensions[nb_ext] = NULL;
+ free(security_limit_extensions);
+ }
+ }
+
+ /* env[], php_value[], php_admin_values[] */
+ if (!wp->config->chroot) {
+ struct key_value_s *kv;
+ char *options[] = FPM_PHP_INI_TO_EXPAND;
+ char **p;
+
+ for (kv = wp->config->php_values; kv; kv = kv->next) {
+ for (p = options; *p; p++) {
+ if (!strcasecmp(kv->key, *p)) {
+ fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
+ }
+ }
+ }
+ for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+ for (p = options; *p; p++) {
+ if (!strcasecmp(kv->key, *p)) {
+ fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
+ }
+ }
+ }
+ }
+ }
+
+ /* ensure 2 pools do not use the same listening address */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ for (wp2 = fpm_worker_all_pools; wp2; wp2 = wp2->next) {
+ if (wp == wp2) {
+ continue;
+ }
+
+ if (wp->config->listen_address && *wp->config->listen_address && wp2->config->listen_address && *wp2->config->listen_address && !strcmp(wp->config->listen_address, wp2->config->listen_address)) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to set listen address as it's already used in another pool '%s'", wp2->config->name, wp->config->name);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_conf_unlink_pid() /* {{{ */
+{
+ if (fpm_global_config.pid_file) {
+ if (0 > unlink(fpm_global_config.pid_file)) {
+ zlog(ZLOG_SYSERROR, "Unable to remove the PID file (%s).", fpm_global_config.pid_file);
+ return -1;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_conf_write_pid() /* {{{ */
+{
+ int fd;
+
+ if (fpm_global_config.pid_file) {
+ char buf[64];
+ int len;
+
+ unlink(fpm_global_config.pid_file);
+ fd = creat(fpm_global_config.pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ if (fd < 0) {
+ zlog(ZLOG_SYSERROR, "Unable to create the PID file (%s).", fpm_global_config.pid_file);
+ return -1;
+ }
+
+ len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
+
+ if (len != write(fd, buf, len)) {
+ zlog(ZLOG_SYSERROR, "Unable to write to the PID file.");
+ return -1;
+ }
+ close(fd);
+ }
+ return 0;
+}
+/* }}} */
+
+static int fpm_conf_post_process(int force_daemon TSRMLS_DC) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ if (fpm_global_config.pid_file) {
+ fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0);
+ }
+
+ if (force_daemon >= 0) {
+ /* forced from command line options */
+ fpm_global_config.daemonize = force_daemon;
+ }
+
+ fpm_globals.log_level = fpm_global_config.log_level;
+
+ if (fpm_global_config.process_max < 0) {
+ zlog(ZLOG_ERROR, "process_max can't be negative");
+ return -1;
+ }
+
+ if (fpm_global_config.process_priority != 64 && (fpm_global_config.process_priority < -19 || fpm_global_config.process_priority > 20)) {
+ zlog(ZLOG_ERROR, "process.priority must be included into [-19,20]");
+ return -1;
+ }
+
+ if (!fpm_global_config.error_log) {
+ fpm_global_config.error_log = strdup("log/php-fpm.log");
+ }
+
+#ifdef HAVE_SYSLOG_H
+ if (!fpm_global_config.syslog_ident) {
+ fpm_global_config.syslog_ident = strdup("php-fpm");
+ }
+
+ if (fpm_global_config.syslog_facility < 0) {
+ fpm_global_config.syslog_facility = LOG_DAEMON;
+ }
+
+ if (strcasecmp(fpm_global_config.error_log, "syslog") != 0)
+#endif
+ {
+ fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
+ }
+
+ if (0 > fpm_stdio_open_error_log(0)) {
+ return -1;
+ }
+
+ if (0 > fpm_log_open(0)) {
+ return -1;
+ }
+
+ if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) {
+ return -1;
+ }
+
+ if (0 > fpm_conf_process_all_pools()) {
+ return -1;
+ }
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config->access_log || !*wp->config->access_log) {
+ continue;
+ }
+ if (0 > fpm_log_write(wp->config->access_format TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "[pool %s] wrong format for access.format '%s'", wp->config->name, wp->config->access_format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+static void fpm_conf_cleanup(int which, void *arg) /* {{{ */
+{
+ free(fpm_global_config.pid_file);
+ free(fpm_global_config.error_log);
+ free(fpm_global_config.events_mechanism);
+ fpm_global_config.pid_file = 0;
+ fpm_global_config.error_log = 0;
+#ifdef HAVE_SYSLOG_H
+ free(fpm_global_config.syslog_ident);
+ fpm_global_config.syslog_ident = 0;
+#endif
+ free(fpm_globals.config);
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_include(char *inc, void *arg TSRMLS_DC) /* {{{ */
+{
+ char *filename;
+ int *error = (int *)arg;;
+#ifdef HAVE_GLOB
+ glob_t g;
+#endif
+ int i;
+
+ if (!inc || !arg) return;
+ if (*error) return; /* We got already an error. Switch to the end. */
+ spprintf(&filename, 0, "%s", ini_filename);
+
+#ifdef HAVE_GLOB
+ {
+ g.gl_offs = 0;
+ if ((i = glob(inc, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) != 0) {
+#ifdef GLOB_NOMATCH
+ if (i == GLOB_NOMATCH) {
+ zlog(ZLOG_WARNING, "Nothing matches the include pattern '%s' from %s at line %d.", inc, filename, ini_lineno);
+ efree(filename);
+ return;
+ }
+#endif /* GLOB_NOMATCH */
+ zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%d) from %s at line %d.", inc, i, filename, ini_lineno);
+ *error = 1;
+ efree(filename);
+ return;
+ }
+
+ for (i = 0; i < g.gl_pathc; i++) {
+ int len = strlen(g.gl_pathv[i]);
+ if (len < 1) continue;
+ if (g.gl_pathv[i][len - 1] == '/') continue; /* don't parse directories */
+ if (0 > fpm_conf_load_ini_file(g.gl_pathv[i] TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", g.gl_pathv[i], filename, ini_lineno);
+ *error = 1;
+ efree(filename);
+ return;
+ }
+ }
+ globfree(&g);
+ }
+#else /* HAVE_GLOB */
+ if (0 > fpm_conf_load_ini_file(inc TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", inc, filename, ini_lineno);
+ *error = 1;
+ efree(filename);
+ return;
+ }
+#endif /* HAVE_GLOB */
+
+ efree(filename);
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_section(zval *section, void *arg TSRMLS_DC) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ struct fpm_worker_pool_config_s *config;
+ int *error = (int *)arg;
+
+ /* switch to global conf */
+ if (!strcasecmp(Z_STRVAL_P(section), "global")) {
+ current_wp = NULL;
+ return;
+ }
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config) continue;
+ if (!wp->config->name) continue;
+ if (!strcasecmp(wp->config->name, Z_STRVAL_P(section))) {
+ /* Found a wp with the same name. Bring it back */
+ current_wp = wp;
+ return;
+ }
+ }
+
+ /* it's a new pool */
+ config = (struct fpm_worker_pool_config_s *)fpm_worker_pool_config_alloc();
+ if (!current_wp || !config) {
+ zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc a new WorkerPool for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
+ *error = 1;
+ return;
+ }
+ config->name = strdup(Z_STRVAL_P(section));
+ if (!config->name) {
+ zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc memory for configuration name for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
+ *error = 1;
+ return;
+ }
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_entry(zval *name, zval *value, void *arg TSRMLS_DC) /* {{{ */
+{
+ struct ini_value_parser_s *parser;
+ void *config = NULL;
+
+ int *error = (int *)arg;
+ if (!value) {
+ zlog(ZLOG_ERROR, "[%s:%d] value is NULL for a ZEND_INI_PARSER_ENTRY", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+
+ if (!strcmp(Z_STRVAL_P(name), "include")) {
+ if (ini_include) {
+ zlog(ZLOG_ERROR, "[%s:%d] two includes at the same time !", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+ ini_include = strdup(Z_STRVAL_P(value));
+ return;
+ }
+
+ if (!current_wp) { /* we are in the global section */
+ parser = ini_fpm_global_options;
+ config = &fpm_global_config;
+ } else {
+ parser = ini_fpm_pool_options;
+ config = current_wp->config;
+ }
+
+ for (; parser->name; parser++) {
+ if (!strcasecmp(parser->name, Z_STRVAL_P(name))) {
+ char *ret;
+ if (!parser->parser) {
+ zlog(ZLOG_ERROR, "[%s:%d] the parser for entry '%s' is not defined", ini_filename, ini_lineno, parser->name);
+ *error = 1;
+ return;
+ }
+
+ ret = parser->parser(value, &config, parser->offset);
+ if (ret) {
+ zlog(ZLOG_ERROR, "[%s:%d] unable to parse value for entry '%s': %s", ini_filename, ini_lineno, parser->name, ret);
+ *error = 1;
+ return;
+ }
+
+ /* all is good ! */
+ return;
+ }
+ }
+
+ /* nothing has been found if we got here */
+ zlog(ZLOG_ERROR, "[%s:%d] unknown entry '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
+ *error = 1;
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *arg TSRMLS_DC) /* {{{ */
+{
+ int *error = (int *)arg;
+ char *err = NULL;
+ void *config;
+
+ if (!Z_STRVAL_P(key) || !Z_STRVAL_P(value) || !*Z_STRVAL_P(key)) {
+ zlog(ZLOG_ERROR, "[%s:%d] Misspelled array ?", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+ if (!current_wp || !current_wp->config) {
+ zlog(ZLOG_ERROR, "[%s:%d] Array are not allowed in the global section", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+
+ if (!strcmp("env", Z_STRVAL_P(name))) {
+ if (!*Z_STRVAL_P(value)) {
+ zlog(ZLOG_ERROR, "[%s:%d] empty value", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+ config = (char *)current_wp->config + WPO(env);
+ err = fpm_conf_set_array(key, value, &config, 0);
+
+ } else if (!strcmp("php_value", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_values);
+ err = fpm_conf_set_array(key, value, &config, 0);
+
+ } else if (!strcmp("php_admin_value", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_admin_values);
+ err = fpm_conf_set_array(key, value, &config, 0);
+
+ } else if (!strcmp("php_flag", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_values);
+ err = fpm_conf_set_array(key, value, &config, 1);
+
+ } else if (!strcmp("php_admin_flag", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_admin_values);
+ err = fpm_conf_set_array(key, value, &config, 1);
+
+ } else {
+ zlog(ZLOG_ERROR, "[%s:%d] unknown directive '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
+ *error = 1;
+ return;
+ }
+
+ if (err) {
+ zlog(ZLOG_ERROR, "[%s:%d] error while parsing '%s[%s]' : %s", ini_filename, ini_lineno, Z_STRVAL_P(name), Z_STRVAL_P(key), err);
+ *error = 1;
+ return;
+ }
+}
+/* }}} */
+
+static void fpm_conf_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */
+{
+ int *error;
+
+ if (!arg1 || !arg) return;
+ error = (int *)arg;
+ if (*error) return; /* We got already an error. Switch to the end. */
+
+ switch(callback_type) {
+ case ZEND_INI_PARSER_ENTRY:
+ fpm_conf_ini_parser_entry(arg1, arg2, error TSRMLS_CC);
+ break;;
+ case ZEND_INI_PARSER_SECTION:
+ fpm_conf_ini_parser_section(arg1, error TSRMLS_CC);
+ break;;
+ case ZEND_INI_PARSER_POP_ENTRY:
+ fpm_conf_ini_parser_array(arg1, arg3, arg2, error TSRMLS_CC);
+ break;;
+ default:
+ zlog(ZLOG_ERROR, "[%s:%d] Unknown INI syntax", ini_filename, ini_lineno);
+ *error = 1;
+ break;;
+ }
+}
+/* }}} */
+
+int fpm_conf_load_ini_file(char *filename TSRMLS_DC) /* {{{ */
+{
+ int error = 0;
+ char buf[1024+1];
+ int fd, n;
+ int nb_read = 1;
+ char c = '*';
+
+ int ret = 1;
+
+ if (!filename || !filename[0]) {
+ zlog(ZLOG_ERROR, "configuration filename is empty");
+ return -1;
+ }
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd < 0) {
+ zlog(ZLOG_SYSERROR, "failed to open configuration file '%s'", filename);
+ return -1;
+ }
+
+ if (ini_recursion++ > 4) {
+ zlog(ZLOG_ERROR, "failed to include more than 5 files recusively");
+ return -1;
+ }
+
+ ini_lineno = 0;
+ while (nb_read > 0) {
+ int tmp;
+ memset(buf, 0, sizeof(char) * (1024 + 1));
+ for (n = 0; n < 1024 && (nb_read = read(fd, &c, sizeof(char))) == sizeof(char) && c != '\n'; n++) {
+ buf[n] = c;
+ }
+ buf[n++] = '\n';
+ ini_lineno++;
+ ini_filename = filename;
+ tmp = zend_parse_ini_string(buf, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fpm_conf_ini_parser, &error TSRMLS_CC);
+ ini_filename = filename;
+ if (error || tmp == FAILURE) {
+ if (ini_include) free(ini_include);
+ ini_recursion--;
+ close(fd);
+ return -1;
+ }
+ if (ini_include) {
+ char *tmp = ini_include;
+ ini_include = NULL;
+ fpm_evaluate_full_path(&tmp, NULL, NULL, 0);
+ fpm_conf_ini_parser_include(tmp, &error TSRMLS_CC);
+ if (error) {
+ free(tmp);
+ ini_recursion--;
+ close(fd);
+ return -1;
+ }
+ free(tmp);
+ }
+ }
+
+ ini_recursion--;
+ close(fd);
+ return ret;
+
+}
+/* }}} */
+
+static void fpm_conf_dump() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ /*
+ * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
+ */
+ zlog(ZLOG_NOTICE, "[General]");
+ zlog(ZLOG_NOTICE, "\tpid = %s", STR2STR(fpm_global_config.pid_file));
+ zlog(ZLOG_NOTICE, "\terror_log = %s", STR2STR(fpm_global_config.error_log));
+#ifdef HAVE_SYSLOG_H
+ zlog(ZLOG_NOTICE, "\tsyslog.ident = %s", STR2STR(fpm_global_config.syslog_ident));
+ zlog(ZLOG_NOTICE, "\tsyslog.facility = %d", fpm_global_config.syslog_facility); /* FIXME: convert to string */
+#endif
+ zlog(ZLOG_NOTICE, "\tlog_level = %s", zlog_get_level_name(fpm_globals.log_level));
+ zlog(ZLOG_NOTICE, "\temergency_restart_interval = %ds", fpm_global_config.emergency_restart_interval);
+ zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold);
+ zlog(ZLOG_NOTICE, "\tprocess_control_timeout = %ds", fpm_global_config.process_control_timeout);
+ zlog(ZLOG_NOTICE, "\tprocess.max = %d", fpm_global_config.process_max);
+ if (fpm_global_config.process_priority == 64) {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
+ } else {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = %d", fpm_global_config.process_priority);
+ }
+ zlog(ZLOG_NOTICE, "\tdaemonize = %s", BOOL2STR(fpm_global_config.daemonize));
+ zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files);
+ zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core);
+ zlog(ZLOG_NOTICE, "\tevents.mechanism = %s", fpm_event_machanism_name());
+ zlog(ZLOG_NOTICE, " ");
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ struct key_value_s *kv;
+ if (!wp->config) continue;
+ zlog(ZLOG_NOTICE, "[%s]", STR2STR(wp->config->name));
+ zlog(ZLOG_NOTICE, "\tprefix = %s", STR2STR(wp->config->prefix));
+ zlog(ZLOG_NOTICE, "\tuser = %s", STR2STR(wp->config->user));
+ zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group));
+ zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address));
+ zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog);
+ zlog(ZLOG_NOTICE, "\tlisten.owner = %s", STR2STR(wp->config->listen_owner));
+ zlog(ZLOG_NOTICE, "\tlisten.group = %s", STR2STR(wp->config->listen_group));
+ zlog(ZLOG_NOTICE, "\tlisten.mode = %s", STR2STR(wp->config->listen_mode));
+ zlog(ZLOG_NOTICE, "\tlisten.allowed_clients = %s", STR2STR(wp->config->listen_allowed_clients));
+ if (wp->config->process_priority == 64) {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
+ } else {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = %d", wp->config->process_priority);
+ }
+ zlog(ZLOG_NOTICE, "\tpm = %s", PM2STR(wp->config->pm));
+ zlog(ZLOG_NOTICE, "\tpm.max_children = %d", wp->config->pm_max_children);
+ zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers);
+ zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d", wp->config->pm_min_spare_servers);
+ zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d", wp->config->pm_max_spare_servers);
+ zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d", wp->config->pm_process_idle_timeout);
+ zlog(ZLOG_NOTICE, "\tpm.max_requests = %d", wp->config->pm_max_requests);
+ zlog(ZLOG_NOTICE, "\tpm.status_path = %s", STR2STR(wp->config->pm_status_path));
+ zlog(ZLOG_NOTICE, "\tping.path = %s", STR2STR(wp->config->ping_path));
+ zlog(ZLOG_NOTICE, "\tping.response = %s", STR2STR(wp->config->ping_response));
+ zlog(ZLOG_NOTICE, "\taccess.log = %s", STR2STR(wp->config->access_log));
+ zlog(ZLOG_NOTICE, "\taccess.format = %s", STR2STR(wp->config->access_format));
+ zlog(ZLOG_NOTICE, "\tslowlog = %s", STR2STR(wp->config->slowlog));
+ zlog(ZLOG_NOTICE, "\trequest_slowlog_timeout = %ds", wp->config->request_slowlog_timeout);
+ zlog(ZLOG_NOTICE, "\trequest_terminate_timeout = %ds", wp->config->request_terminate_timeout);
+ zlog(ZLOG_NOTICE, "\trlimit_files = %d", wp->config->rlimit_files);
+ zlog(ZLOG_NOTICE, "\trlimit_core = %d", wp->config->rlimit_core);
+ zlog(ZLOG_NOTICE, "\tchroot = %s", STR2STR(wp->config->chroot));
+ zlog(ZLOG_NOTICE, "\tchdir = %s", STR2STR(wp->config->chdir));
+ zlog(ZLOG_NOTICE, "\tcatch_workers_output = %s", BOOL2STR(wp->config->catch_workers_output));
+ zlog(ZLOG_NOTICE, "\tsecurity.limit_extensions = %s", wp->config->security_limit_extensions);
+
+ for (kv = wp->config->env; kv; kv = kv->next) {
+ zlog(ZLOG_NOTICE, "\tenv[%s] = %s", kv->key, kv->value);
+ }
+
+ for (kv = wp->config->php_values; kv; kv = kv->next) {
+ zlog(ZLOG_NOTICE, "\tphp_value[%s] = %s", kv->key, kv->value);
+ }
+
+ for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+ zlog(ZLOG_NOTICE, "\tphp_admin_value[%s] = %s", kv->key, kv->value);
+ }
+ zlog(ZLOG_NOTICE, " ");
+ }
+}
+/* }}} */
+
+int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
+{
+ int ret;
+ TSRMLS_FETCH();
+
+ if (fpm_globals.prefix && *fpm_globals.prefix) {
+ if (!fpm_conf_is_dir(fpm_globals.prefix)) {
+ zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix);
+ return -1;
+ }
+ }
+
+ if (fpm_globals.pid && *fpm_globals.pid) {
+ fpm_global_config.pid_file = strdup(fpm_globals.pid);
+ }
+
+ if (fpm_globals.config == NULL) {
+ char *tmp;
+
+ if (fpm_globals.prefix == NULL) {
+ spprintf(&tmp, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
+ } else {
+ spprintf(&tmp, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix);
+ }
+
+ if (!tmp) {
+ zlog(ZLOG_SYSERROR, "spprintf() failed (tmp for fpm_globals.config)");
+ return -1;
+ }
+
+ fpm_globals.config = strdup(tmp);
+ efree(tmp);
+
+ if (!fpm_globals.config) {
+ zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)");
+ return -1;
+ }
+ }
+
+ ret = fpm_conf_load_ini_file(fpm_globals.config TSRMLS_CC);
+
+ if (0 > ret) {
+ zlog(ZLOG_ERROR, "failed to load configuration file '%s'", fpm_globals.config);
+ return -1;
+ }
+
+ if (0 > fpm_conf_post_process(force_daemon TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "failed to post process the configuration");
+ return -1;
+ }
+
+ if (test_conf) {
+ if (test_conf > 1) {
+ fpm_conf_dump();
+ }
+ zlog(ZLOG_NOTICE, "configuration file %s test is successful\n", fpm_globals.config);
+ fpm_globals.test_successful = 1;
+ return -1;
+ }
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h
new file mode 100644
index 0000000..dc54133
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_conf.h
@@ -0,0 +1,106 @@
+
+ /* $Id: fpm_conf.h,v 1.12.2.2 2008/12/13 03:46:49 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CONF_H
+#define FPM_CONF_H 1
+
+#include <stdint.h>
+#include "php.h"
+
+#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : (a == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand"))
+
+#define FPM_CONF_MAX_PONG_LENGTH 64
+
+struct key_value_s;
+
+struct key_value_s {
+ struct key_value_s *next;
+ char *key;
+ char *value;
+};
+
+/*
+ * Please keep the same order as in fpm_conf.c and in php-fpm.conf.in
+ */
+struct fpm_global_config_s {
+ char *pid_file;
+ char *error_log;
+#ifdef HAVE_SYSLOG_H
+ char *syslog_ident;
+ int syslog_facility;
+#endif
+ int log_level;
+ int emergency_restart_threshold;
+ int emergency_restart_interval;
+ int process_control_timeout;
+ int process_max;
+ int process_priority;
+ int daemonize;
+ int rlimit_files;
+ int rlimit_core;
+ char *events_mechanism;
+};
+
+extern struct fpm_global_config_s fpm_global_config;
+
+/*
+ * Please keep the same order as in fpm_conf.c and in php-fpm.conf.in
+ */
+struct fpm_worker_pool_config_s {
+ char *name;
+ char *prefix;
+ char *user;
+ char *group;
+ char *listen_address;
+ int listen_backlog;
+ char *listen_owner;
+ char *listen_group;
+ char *listen_mode;
+ char *listen_allowed_clients;
+ int process_priority;
+ int pm;
+ int pm_max_children;
+ int pm_start_servers;
+ int pm_min_spare_servers;
+ int pm_max_spare_servers;
+ int pm_process_idle_timeout;
+ int pm_max_requests;
+ char *pm_status_path;
+ char *ping_path;
+ char *ping_response;
+ char *access_log;
+ char *access_format;
+ char *slowlog;
+ int request_slowlog_timeout;
+ int request_terminate_timeout;
+ int rlimit_files;
+ int rlimit_core;
+ char *chroot;
+ char *chdir;
+ int catch_workers_output;
+ char *security_limit_extensions;
+ struct key_value_s *env;
+ struct key_value_s *php_admin_values;
+ struct key_value_s *php_values;
+};
+
+struct ini_value_parser_s {
+ char *name;
+ char *(*parser)(zval *, void **, intptr_t);
+ intptr_t offset;
+};
+
+enum {
+ PM_STYLE_STATIC = 1,
+ PM_STYLE_DYNAMIC = 2,
+ PM_STYLE_ONDEMAND = 3
+};
+
+int fpm_conf_init_main(int test_conf, int force_daemon);
+int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc);
+int fpm_conf_write_pid();
+int fpm_conf_unlink_pid();
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_config.h b/sapi/fpm/fpm/fpm_config.h
new file mode 100644
index 0000000..856414a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_config.h
@@ -0,0 +1,79 @@
+/* $Id: fpm_config.h,v 1.16 2008/05/25 00:30:43 anight Exp $ */
+/* (c) 2007,2008 Andrei Nigmatulin */
+
+#include <php_config.h>
+
+/* Solaris does not have it */
+#ifndef INADDR_NONE
+# define INADDR_NONE (-1)
+#endif
+
+
+/* If we're not using GNU C, elide __attribute__ */
+#ifndef __GNUC__
+# define __attribute__(x) /*NOTHING*/
+#endif
+
+/* Missing timer* macros (for solaris) */
+#ifndef timerisset
+# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+#ifndef timerclear
+# define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#endif
+
+#ifndef timersub
+# define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+#ifndef timeradd
+# define timeradd(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
+ if ((result)->tv_usec >= 1000000) \
+ { \
+ ++(result)->tv_sec; \
+ (result)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#endif
+
+#ifndef timercmp
+/* does not work for >= and <= */
+# define timercmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) ? \
+ ((a)->tv_usec CMP (b)->tv_usec) : \
+ ((a)->tv_sec CMP (b)->tv_sec))
+#endif
+/* endof timer* macros */
+
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#if defined(HAVE_PTRACE) || defined(PROC_MEM_FILE) || defined(HAVE_MACH_VM_READ)
+# define HAVE_FPM_TRACE 1
+#else
+# define HAVE_FPM_TRACE 0
+#endif
+
+#if defined(HAVE_LQ_TCP_INFO) || defined(HAVE_LQ_SO_LISTENQ)
+# define HAVE_FPM_LQ 1
+#else
+# define HAVE_FPM_LQ 0
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_env.c b/sapi/fpm/fpm/fpm_env.c
new file mode 100644
index 0000000..6b64fed
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_env.c
@@ -0,0 +1,276 @@
+
+ /* $Id: fpm_env.c,v 1.15 2008/09/18 23:19:59 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fpm_env.h"
+#include "fpm.h"
+
+#ifndef HAVE_SETPROCTITLE
+#ifdef __linux__
+static char **fpm_env_argv = NULL;
+static size_t fpm_env_argv_len = 0;
+#endif
+#endif
+
+#ifndef HAVE_SETENV
+# ifdef (__sparc__ || __sparc)
+int setenv(char *name, char *value, int clobber) /* {{{ */
+{
+ char *malloc();
+ char *getenv();
+ char *cp;
+
+ if (clobber == 0 && getenv(name) != 0) {
+ return 0;
+ }
+
+ if ((cp = malloc(strlen(name) + strlen(value) + 2)) == 0) {
+ return 1;
+ }
+ sprintf(cp, "%s=%s", name, value);
+ return putenv(cp);
+}
+/* }}} */
+# else
+int setenv(char *name, char *value, int overwrite) /* {{{ */
+{
+ int name_len = strlen(name);
+ int value_len = strlen(value);
+ char *var = alloca(name_len + 1 + value_len + 1);
+
+ memcpy(var, name, name_len);
+
+ var[name_len] = '=';
+
+ memcpy(var + name_len + 1, value, value_len);
+
+ var[name_len + 1 + value_len] = '\0';
+
+ return putenv(var);
+}
+/* }}} */
+# endif
+#endif
+
+#ifndef HAVE_CLEARENV
+void clearenv() /* {{{ */
+{
+ char **envp;
+ char *s;
+
+ /* this algo is the only one known to me
+ that works well on all systems */
+ while (*(envp = environ)) {
+ char *eq = strchr(*envp, '=');
+
+ s = strdup(*envp);
+
+ if (eq) s[eq - *envp] = '\0';
+
+ unsetenv(s);
+ free(s);
+ }
+
+}
+/* }}} */
+#endif
+
+#ifndef HAVE_UNSETENV
+void unsetenv(const char *name) /* {{{ */
+{
+ if(getenv(name) != NULL) {
+ int ct = 0;
+ int del = 0;
+
+ while(environ[ct] != NULL) {
+ if (nvmatch(name, environ[ct]) != 0) del=ct; /* <--- WTF?! */
+ { ct++; } /* <--- WTF?! */
+ }
+ /* isn't needed free here?? */
+ environ[del] = environ[ct-1];
+ environ[ct-1] = NULL;
+ }
+}
+/* }}} */
+
+static char * nvmatch(char *s1, char *s2) /* {{{ */
+{
+ while(*s1 == *s2++)
+ {
+ if(*s1++ == '=') {
+ return s2;
+ }
+ }
+ if(*s1 == '\0' && *(s2-1) == '=') {
+ return s2;
+ }
+ return NULL;
+}
+/* }}} */
+#endif
+
+void fpm_env_setproctitle(char *title) /* {{{ */
+{
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("%s", title);
+#else
+#ifdef __linux__
+ if (fpm_env_argv != NULL && fpm_env_argv_len > strlen(SETPROCTITLE_PREFIX) + 3) {
+ memset(fpm_env_argv[0], 0, fpm_env_argv_len);
+ strncpy(fpm_env_argv[0], SETPROCTITLE_PREFIX, fpm_env_argv_len - 2);
+ strncpy(fpm_env_argv[0] + strlen(SETPROCTITLE_PREFIX), title, fpm_env_argv_len - strlen(SETPROCTITLE_PREFIX) - 2);
+ fpm_env_argv[1] = NULL;
+ }
+#endif
+#endif
+}
+/* }}} */
+
+int fpm_env_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct key_value_s *kv;
+ char *title;
+ spprintf(&title, 0, "pool %s", wp->config->name);
+ fpm_env_setproctitle(title);
+ efree(title);
+
+ clearenv();
+
+ for (kv = wp->config->env; kv; kv = kv->next) {
+ setenv(kv->key, kv->value, 1);
+ }
+
+ if (wp->user) {
+ setenv("USER", wp->user, 1);
+ }
+
+ if (wp->home) {
+ setenv("HOME", wp->home, 1);
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int fpm_env_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct key_value_s *kv;
+
+ for (kv = wp->config->env; kv; kv = kv->next) {
+ if (*kv->value == '$') {
+ char *value = getenv(kv->value + 1);
+
+ if (!value) {
+ value = "";
+ }
+
+ free(kv->value);
+ kv->value = strdup(value);
+ }
+
+ /* autodetected values should be removed
+ if these vars specified in config */
+ if (!strcmp(kv->key, "USER")) {
+ free(wp->user);
+ wp->user = 0;
+ }
+
+ if (!strcmp(kv->key, "HOME")) {
+ free(wp->home);
+ wp->home = 0;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_env_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int i;
+ char *first = NULL;
+ char *last = NULL;
+ char *title;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (0 > fpm_env_conf_wp(wp)) {
+ return -1;
+ }
+ }
+#ifndef HAVE_SETPROCTITLE
+#ifdef __linux__
+ /*
+ * This piece of code has been inspirated from nginx and pureftpd code, whic
+ * are under BSD licence.
+ *
+ * To change the process title in Linux we have to set argv[1] to NULL
+ * and to copy the title to the same place where the argv[0] points to.
+ * However, argv[0] may be too small to hold a new title. Fortunately, Linux
+ * store argv[] and environ[] one after another. So we should ensure that is
+ * the continuous memory and then we allocate the new memory for environ[]
+ * and copy it. After this we could use the memory starting from argv[0] for
+ * our process title.
+ */
+
+ for (i = 0; i < fpm_globals.argc; i++) {
+ if (first == NULL) {
+ first = fpm_globals.argv[i];
+ }
+ if (last == NULL || fpm_globals.argv[i] == last + 1) {
+ last = fpm_globals.argv[i] + strlen(fpm_globals.argv[i]);
+ }
+ }
+ if (environ) {
+ for (i = 0; environ[i]; i++) {
+ if (first == NULL) {
+ first = environ[i];
+ }
+ if (last == NULL || environ[i] == last + 1) {
+ last = environ[i] + strlen(environ[i]);
+ }
+ }
+ }
+ if (first == NULL || last == NULL) {
+ return 0;
+ }
+
+ fpm_env_argv_len = last - first;
+ fpm_env_argv = fpm_globals.argv;
+ if (environ != NULL) {
+ char **new_environ;
+ unsigned int env_nb = 0U;
+
+ while (environ[env_nb]) {
+ env_nb++;
+ }
+
+ if ((new_environ = malloc((1U + env_nb) * sizeof (char *))) == NULL) {
+ return -1;
+ }
+ new_environ[env_nb] = NULL;
+ while (env_nb > 0U) {
+ env_nb--;
+ new_environ[env_nb] = strdup(environ[env_nb]);
+ }
+ environ = new_environ;
+ }
+#endif
+#endif
+
+ spprintf(&title, 0, "master process (%s)", fpm_globals.config);
+ fpm_env_setproctitle(title);
+ efree(title);
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_env.h b/sapi/fpm/fpm/fpm_env.h
new file mode 100644
index 0000000..bf47236
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_env.h
@@ -0,0 +1,27 @@
+
+ /* $Id: fpm_env.h,v 1.9 2008/09/18 23:19:59 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_ENV_H
+#define FPM_ENV_H 1
+
+#include "fpm_worker_pool.h"
+
+#define SETPROCTITLE_PREFIX "php-fpm: "
+
+int fpm_env_init_child(struct fpm_worker_pool_s *wp);
+int fpm_env_init_main();
+void fpm_env_setproctitle(char *title);
+
+extern char **environ;
+
+#ifndef HAVE_SETENV
+int setenv(char *name, char *value, int overwrite);
+#endif
+
+#ifndef HAVE_CLEARENV
+void clearenv();
+#endif
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c
new file mode 100644
index 0000000..d5835f0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_events.c
@@ -0,0 +1,533 @@
+
+ /* $Id: fpm_events.c,v 1.21.2.2 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h> /* for putenv */
+#include <string.h>
+
+#include <php.h>
+
+#include "fpm.h"
+#include "fpm_process_ctl.h"
+#include "fpm_events.h"
+#include "fpm_cleanup.h"
+#include "fpm_stdio.h"
+#include "fpm_signals.h"
+#include "fpm_children.h"
+#include "zlog.h"
+#include "fpm_clock.h"
+#include "fpm_log.h"
+
+#include "events/select.h"
+#include "events/poll.h"
+#include "events/epoll.h"
+#include "events/devpoll.h"
+#include "events/port.h"
+#include "events/kqueue.h"
+
+#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
+
+static void fpm_event_cleanup(int which, void *arg);
+static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg);
+static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev);
+static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
+static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
+static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue);
+
+static struct fpm_event_module_s *module;
+static struct fpm_event_queue_s *fpm_event_queue_timer = NULL;
+static struct fpm_event_queue_s *fpm_event_queue_fd = NULL;
+
+static void fpm_event_cleanup(int which, void *arg) /* {{{ */
+{
+ fpm_event_queue_destroy(&fpm_event_queue_timer);
+ fpm_event_queue_destroy(&fpm_event_queue_fd);
+}
+/* }}} */
+
+static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ char c;
+ int res, ret;
+ int fd = ev->fd;
+
+ do {
+ do {
+ res = read(fd, &c, 1);
+ } while (res == -1 && errno == EINTR);
+
+ if (res <= 0) {
+ if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ zlog(ZLOG_SYSERROR, "unable to read from the signal pipe");
+ }
+ return;
+ }
+
+ switch (c) {
+ case 'C' : /* SIGCHLD */
+ zlog(ZLOG_DEBUG, "received SIGCHLD");
+ fpm_children_bury();
+ break;
+ case 'I' : /* SIGINT */
+ zlog(ZLOG_DEBUG, "received SIGINT");
+ zlog(ZLOG_NOTICE, "Terminating ...");
+ fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
+ break;
+ case 'T' : /* SIGTERM */
+ zlog(ZLOG_DEBUG, "received SIGTERM");
+ zlog(ZLOG_NOTICE, "Terminating ...");
+ fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
+ break;
+ case 'Q' : /* SIGQUIT */
+ zlog(ZLOG_DEBUG, "received SIGQUIT");
+ zlog(ZLOG_NOTICE, "Finishing ...");
+ fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET);
+ break;
+ case '1' : /* SIGUSR1 */
+ zlog(ZLOG_DEBUG, "received SIGUSR1");
+ if (0 == fpm_stdio_open_error_log(1)) {
+ zlog(ZLOG_NOTICE, "error log file re-opened");
+ } else {
+ zlog(ZLOG_ERROR, "unable to re-opened error log file");
+ }
+
+ ret = fpm_log_open(1);
+ if (ret == 0) {
+ zlog(ZLOG_NOTICE, "access log file re-opened");
+ } else if (ret == -1) {
+ zlog(ZLOG_ERROR, "unable to re-opened access log file");
+ }
+ /* else no access log are set */
+
+ break;
+ case '2' : /* SIGUSR2 */
+ zlog(ZLOG_DEBUG, "received SIGUSR2");
+ zlog(ZLOG_NOTICE, "Reloading in progress ...");
+ fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
+ break;
+ }
+
+ if (fpm_globals.is_child) {
+ break;
+ }
+ } while (1);
+ return;
+}
+/* }}} */
+
+static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev) /* {{{ */
+{
+ if (!ev) {
+ return NULL;
+ }
+
+ while (queue) {
+ if (queue->ev == ev) {
+ return ev;
+ }
+ queue = queue->next;
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
+{
+ struct fpm_event_queue_s *elt;
+
+ if (!queue || !ev) {
+ return -1;
+ }
+
+ if (fpm_event_queue_isset(*queue, ev)) {
+ return 0;
+ }
+
+ if (!(elt = malloc(sizeof(struct fpm_event_queue_s)))) {
+ zlog(ZLOG_SYSERROR, "Unable to add the event to queue: malloc() failed");
+ return -1;
+ }
+ elt->prev = NULL;
+ elt->next = NULL;
+ elt->ev = ev;
+
+ if (*queue) {
+ (*queue)->prev = elt;
+ elt->next = *queue;
+ }
+ *queue = elt;
+
+ /* ask the event module to add the fd from its own queue */
+ if (*queue == fpm_event_queue_fd && module->add) {
+ module->add(ev);
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
+{
+ struct fpm_event_queue_s *q;
+ if (!queue || !ev) {
+ return -1;
+ }
+ q = *queue;
+ while (q) {
+ if (q->ev == ev) {
+ if (q->prev) {
+ q->prev->next = q->next;
+ }
+ if (q->next) {
+ q->next->prev = q->prev;
+ }
+ if (q == *queue) {
+ *queue = q->next;
+ if (*queue) {
+ (*queue)->prev = NULL;
+ }
+ }
+
+ /* ask the event module to remove the fd from its own queue */
+ if (*queue == fpm_event_queue_fd && module->remove) {
+ module->remove(ev);
+ }
+
+ free(q);
+ return 0;
+ }
+ q = q->next;
+ }
+ return -1;
+}
+/* }}} */
+
+static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */
+{
+ struct fpm_event_queue_s *q, *tmp;
+
+ if (!queue) {
+ return;
+ }
+
+ if (*queue == fpm_event_queue_fd && module->clean) {
+ module->clean();
+ }
+
+ q = *queue;
+ while (q) {
+ tmp = q;
+ q = q->next;
+ /* q->prev = NULL */
+ free(tmp);
+ }
+ *queue = NULL;
+}
+/* }}} */
+
+int fpm_event_pre_init(char *machanism) /* {{{ */
+{
+ /* kqueue */
+ module = fpm_event_kqueue_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* port */
+ module = fpm_event_port_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* epoll */
+ module = fpm_event_epoll_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* /dev/poll */
+ module = fpm_event_devpoll_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* poll */
+ module = fpm_event_poll_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* select */
+ module = fpm_event_select_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ if (machanism) {
+ zlog(ZLOG_ERROR, "event mechanism '%s' is not available on this system", machanism);
+ } else {
+ zlog(ZLOG_ERROR, "unable to find a suitable event mechanism on this system");
+ }
+ return -1;
+}
+/* }} */
+
+const char *fpm_event_machanism_name() /* {{{ */
+{
+ return module ? module->name : NULL;
+}
+/* }}} */
+
+int fpm_event_support_edge_trigger() /* {{{ */
+{
+ return module ? module->support_edge_trigger : 0;
+}
+/* }}} */
+
+int fpm_event_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int max;
+
+ if (!module) {
+ zlog(ZLOG_ERROR, "no event module found");
+ return -1;
+ }
+
+ if (!module->wait) {
+ zlog(ZLOG_ERROR, "Incomplete event implementation. Please open a bug report on https://bugs.php.net.");
+ return -1;
+ }
+
+ /* count the max number of necessary fds for polling */
+ max = 1; /* only one FD is necessary at startup for the master process signal pipe */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config) continue;
+ if (wp->config->catch_workers_output && wp->config->pm_max_children > 0) {
+ max += (wp->config->pm_max_children * 2);
+ }
+ }
+
+ if (module->init(max) < 0) {
+ zlog(ZLOG_ERROR, "Unable to initialize the event module %s", module->name);
+ return -1;
+ }
+
+ zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module->name, max);
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+void fpm_event_loop(int err) /* {{{ */
+{
+ static struct fpm_event_s signal_fd_event;
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return;
+ }
+
+ fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
+ fpm_event_add(&signal_fd_event, 0);
+
+ /* add timers */
+ if (fpm_globals.heartbeat > 0) {
+ fpm_pctl_heartbeat(NULL, 0, NULL);
+ }
+
+ if (!err) {
+ fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);
+
+ zlog(ZLOG_DEBUG, "%zu bytes have been reserved in SHM", fpm_shm_get_size_allocated());
+ zlog(ZLOG_NOTICE, "ready to handle connections");
+ }
+
+ while (1) {
+ struct fpm_event_queue_s *q, *q2;
+ struct timeval ms;
+ struct timeval tmp;
+ struct timeval now;
+ unsigned long int timeout;
+ int ret;
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return;
+ }
+
+ fpm_clock_get(&now);
+ timerclear(&ms);
+
+ /* search in the timeout queue for the next timer to trigger */
+ q = fpm_event_queue_timer;
+ while (q) {
+ if (!timerisset(&ms)) {
+ ms = q->ev->timeout;
+ } else {
+ if (timercmp(&q->ev->timeout, &ms, <)) {
+ ms = q->ev->timeout;
+ }
+ }
+ q = q->next;
+ }
+
+ /* 1s timeout if none has been set */
+ if (!timerisset(&ms) || timercmp(&ms, &now, <) || timercmp(&ms, &now, ==)) {
+ timeout = 1000;
+ } else {
+ timersub(&ms, &now, &tmp);
+ timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1;
+ }
+
+ ret = module->wait(fpm_event_queue_fd, timeout);
+
+ /* is a child, nothing to do here */
+ if (ret == -2) {
+ return;
+ }
+
+ if (ret > 0) {
+ zlog(ZLOG_DEBUG, "event module triggered %d events", ret);
+ }
+
+ /* trigger timers */
+ q = fpm_event_queue_timer;
+ while (q) {
+ fpm_clock_get(&now);
+ if (q->ev) {
+ if (timercmp(&now, &q->ev->timeout, >) || timercmp(&now, &q->ev->timeout, ==)) {
+ fpm_event_fire(q->ev);
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return;
+ }
+ if (q->ev->flags & FPM_EV_PERSIST) {
+ fpm_event_set_timeout(q->ev, now);
+ } else { /* delete the event */
+ q2 = q;
+ if (q->prev) {
+ q->prev->next = q->next;
+ }
+ if (q->next) {
+ q->next->prev = q->prev;
+ }
+ if (q == fpm_event_queue_timer) {
+ fpm_event_queue_timer = q->next;
+ if (fpm_event_queue_timer) {
+ fpm_event_queue_timer->prev = NULL;
+ }
+ }
+ q = q->next;
+ free(q2);
+ continue;
+ }
+ }
+ }
+ q = q->next;
+ }
+ }
+}
+/* }}} */
+
+void fpm_event_fire(struct fpm_event_s *ev) /* {{{ */
+{
+ if (!ev || !ev->callback) {
+ return;
+ }
+
+ (*ev->callback)( (struct fpm_event_s *) ev, ev->which, ev->arg);
+}
+/* }}} */
+
+int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */
+{
+ if (!ev || !callback || fd < -1) {
+ return -1;
+ }
+ memset(ev, 0, sizeof(struct fpm_event_s));
+ ev->fd = fd;
+ ev->callback = callback;
+ ev->arg = arg;
+ ev->flags = flags;
+ return 0;
+}
+/* }}} */
+
+int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency) /* {{{ */
+{
+ struct timeval now;
+ struct timeval tmp;
+
+ if (!ev) {
+ return -1;
+ }
+
+ ev->index = -1;
+
+ /* it's a triggered event on incoming data */
+ if (ev->flags & FPM_EV_READ) {
+ ev->which = FPM_EV_READ;
+ if (fpm_event_queue_add(&fpm_event_queue_fd, ev) != 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /* it's a timer event */
+ ev->which = FPM_EV_TIMEOUT;
+
+ fpm_clock_get(&now);
+ if (frequency >= 1000) {
+ tmp.tv_sec = frequency / 1000;
+ tmp.tv_usec = (frequency % 1000) * 1000;
+ } else {
+ tmp.tv_sec = 0;
+ tmp.tv_usec = frequency * 1000;
+ }
+ ev->frequency = tmp;
+ fpm_event_set_timeout(ev, now);
+
+ if (fpm_event_queue_add(&fpm_event_queue_timer, ev) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
+{
+ if (ev->index >= 0 && fpm_event_queue_del(&fpm_event_queue_fd, ev) != 0) {
+ return -1;
+ }
+
+ if (ev->index < 0 && fpm_event_queue_del(&fpm_event_queue_timer, ev) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_events.h b/sapi/fpm/fpm/fpm_events.h
new file mode 100644
index 0000000..416ec6e
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_events.h
@@ -0,0 +1,52 @@
+
+ /* $Id: fpm_events.h,v 1.9 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_EVENTS_H
+#define FPM_EVENTS_H 1
+
+#define FPM_EV_TIMEOUT (1 << 0)
+#define FPM_EV_READ (1 << 1)
+#define FPM_EV_PERSIST (1 << 2)
+#define FPM_EV_EDGE (1 << 3)
+
+#define fpm_event_set_timer(ev, flags, cb, arg) fpm_event_set((ev), -1, (flags), (cb), (arg))
+
+struct fpm_event_s {
+ int fd; /* not set with FPM_EV_TIMEOUT */
+ struct timeval timeout; /* next time to trigger */
+ struct timeval frequency;
+ void (*callback)(struct fpm_event_s *, short, void *);
+ void *arg;
+ int flags;
+ int index; /* index of the fd in the ufds array */
+ short which; /* type of event */
+};
+
+typedef struct fpm_event_queue_s {
+ struct fpm_event_queue_s *prev;
+ struct fpm_event_queue_s *next;
+ struct fpm_event_s *ev;
+} fpm_event_queue;
+
+struct fpm_event_module_s {
+ const char *name;
+ int support_edge_trigger;
+ int (*init)(int max_fd);
+ int (*clean)(void);
+ int (*wait)(struct fpm_event_queue_s *queue, unsigned long int timeout);
+ int (*add)(struct fpm_event_s *ev);
+ int (*remove)(struct fpm_event_s *ev);
+};
+
+void fpm_event_loop(int err);
+void fpm_event_fire(struct fpm_event_s *ev);
+int fpm_event_init_main();
+int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg);
+int fpm_event_add(struct fpm_event_s *ev, unsigned long int timeout);
+int fpm_event_del(struct fpm_event_s *ev);
+int fpm_event_pre_init(char *machanism);
+const char *fpm_event_machanism_name();
+int fpm_event_support_edge_trigger();
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_log.c b/sapi/fpm/fpm/fpm_log.c
new file mode 100644
index 0000000..6b014b5
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_log.c
@@ -0,0 +1,466 @@
+
+ /* $Id: fpm_status.c 312262 2011-06-18 17:41:56Z felipe $ */
+ /* (c) 2009 Jerome Loyet */
+
+#include "php.h"
+#include "SAPI.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_TIMES
+#include <sys/times.h>
+#endif
+
+#include "fpm_config.h"
+#include "fpm_log.h"
+#include "fpm_clock.h"
+#include "fpm_process_ctl.h"
+#include "fpm_signals.h"
+#include "fpm_scoreboard.h"
+#include "fastcgi.h"
+#include "zlog.h"
+
+#ifdef MAX_LINE_LENGTH
+# define FPM_LOG_BUFFER MAX_LINE_LENGTH
+#else
+# define FPM_LOG_BUFFER 1024
+#endif
+
+static char *fpm_log_format = NULL;
+static int fpm_log_fd = -1;
+
+int fpm_log_open(int reopen) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int ret = 1;
+
+ int fd;
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config->access_log) {
+ continue;
+ }
+ ret = 0;
+
+ fd = open(wp->config->access_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "failed to open access log (%s)", wp->config->access_log);
+ return -1;
+ }
+
+ if (reopen) {
+ dup2(fd, wp->log_fd);
+ close(fd);
+ fd = wp->log_fd;
+ fpm_pctl_kill_all(SIGQUIT);
+ } else {
+ wp->log_fd = fd;
+ }
+
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ }
+
+ return ret;
+}
+/* }}} */
+
+/* }}} */
+int fpm_log_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (!wp || !wp->config) {
+ return -1;
+ }
+
+ if (wp->config->access_log && *wp->config->access_log) {
+ if (wp->config->access_format) {
+ fpm_log_format = strdup(wp->config->access_format);
+ }
+ }
+
+ if (fpm_log_fd == -1) {
+ fpm_log_fd = wp->log_fd;
+ }
+
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (wp->log_fd > -1 && wp->log_fd != fpm_log_fd) {
+ close(wp->log_fd);
+ wp->log_fd = -1;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */
+{
+ char *s, *b;
+ char buffer[FPM_LOG_BUFFER+1];
+ int token, test;
+ size_t len, len2;
+ struct fpm_scoreboard_proc_s proc, *proc_p;
+ struct fpm_scoreboard_s *scoreboard;
+ char tmp[129];
+ char format[129];
+ time_t now_epoch;
+#ifdef HAVE_TIMES
+ clock_t tms_total;
+#endif
+
+ if (!log_format && (!fpm_log_format || fpm_log_fd == -1)) {
+ return -1;
+ }
+
+ if (!log_format) {
+ log_format = fpm_log_format;
+ test = 0;
+ } else {
+ test = 1;
+ }
+
+ now_epoch = time(NULL);
+
+ if (!test) {
+ scoreboard = fpm_scoreboard_get();
+ if (!scoreboard) {
+ zlog(ZLOG_WARNING, "unable to get scoreboard while preparing the access log");
+ return -1;
+ }
+ proc_p = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (!proc_p) {
+ zlog(ZLOG_WARNING, "[pool %s] Unable to acquire shm slot while preparing the access log", scoreboard->pool);
+ return -1;
+ }
+ proc = *proc_p;
+ fpm_scoreboard_proc_release(proc_p);
+ }
+
+ token = 0;
+
+ memset(buffer, '\0', sizeof(buffer));
+ b = buffer;
+ len = 0;
+
+
+ s = log_format;
+
+ while (*s != '\0') {
+ /* Test is we have place for 1 more char. */
+ if (len >= FPM_LOG_BUFFER) {
+ zlog(ZLOG_NOTICE, "the log buffer is full (%d). The access log request has been truncated.", FPM_LOG_BUFFER);
+ len = FPM_LOG_BUFFER;
+ break;
+ }
+
+ if (!token && *s == '%') {
+ token = 1;
+ memset(format, '\0', sizeof(format)); /* reset format */
+ s++;
+ continue;
+ }
+
+ if (token) {
+ token = 0;
+ len2 = 0;
+ switch (*s) {
+
+ case '%': /* '%' */
+ *b = '%';
+ len2 = 1;
+ break;
+
+#ifdef HAVE_TIMES
+ case 'C': /* %CPU */
+ if (format[0] == '\0' || !strcasecmp(format, "total")) {
+ if (!test) {
+ tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime;
+ }
+ } else if (!strcasecmp(format, "user")) {
+ if (!test) {
+ tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_cutime;
+ }
+ } else if (!strcasecmp(format, "system")) {
+ if (!test) {
+ tms_total = proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cstime;
+ }
+ } else {
+ zlog(ZLOG_WARNING, "only 'total', 'user' or 'system' are allowed as a modifier for %%%c ('%s')", *s, format);
+ return -1;
+ }
+
+ format[0] = '\0';
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.2f", tms_total / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.);
+ }
+ break;
+#endif
+
+ case 'd': /* duration µs */
+ /* seconds */
+ if (format[0] == '\0' || !strcasecmp(format, "seconds")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec + proc.duration.tv_usec / 1000000.);
+ }
+
+ /* miliseconds */
+ } else if (!strcasecmp(format, "miliseconds") || !strcasecmp(format, "mili")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec * 1000. + proc.duration.tv_usec / 1000.);
+ }
+
+ /* microseconds */
+ } else if (!strcasecmp(format, "microseconds") || !strcasecmp(format, "micro")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.duration.tv_sec * 1000000UL + proc.duration.tv_usec);
+ }
+
+ } else {
+ zlog(ZLOG_WARNING, "only 'seconds', 'mili', 'miliseconds', 'micro' or 'microseconds' are allowed as a modifier for %%%c ('%s')", *s, format);
+ return -1;
+ }
+ format[0] = '\0';
+ break;
+
+ case 'e': /* fastcgi env */
+ if (format[0] == '\0') {
+ zlog(ZLOG_WARNING, "the name of the environment variable must be set between embraces for %%%c", *s);
+ return -1;
+ }
+
+ if (!test) {
+ char *env = fcgi_getenv((fcgi_request*) SG(server_context), format, strlen(format));
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", env ? env : "-");
+ }
+ format[0] = '\0';
+ break;
+
+ case 'f': /* script */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.script_filename && *proc.script_filename ? proc.script_filename : "-");
+ }
+ break;
+
+ case 'l': /* content length */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.content_length);
+ }
+ break;
+
+ case 'm': /* method */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.request_method && *proc.request_method ? proc.request_method : "-");
+ }
+ break;
+
+ case 'M': /* memory */
+ /* seconds */
+ if (format[0] == '\0' || !strcasecmp(format, "bytes")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.memory);
+ }
+
+ /* kilobytes */
+ } else if (!strcasecmp(format, "kilobytes") || !strcasecmp(format, "kilo")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024);
+ }
+
+ /* megabytes */
+ } else if (!strcasecmp(format, "megabytes") || !strcasecmp(format, "mega")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024 / 1024);
+ }
+
+ } else {
+ zlog(ZLOG_WARNING, "only 'bytes', 'kilo', 'kilobytes', 'mega' or 'megabytes' are allowed as a modifier for %%%c ('%s')", *s, format);
+ return -1;
+ }
+ format[0] = '\0';
+ break;
+
+ case 'n': /* pool name */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", scoreboard->pool[0] ? scoreboard->pool : "-");
+ }
+ break;
+
+ case 'o': /* header output */
+ if (format[0] == '\0') {
+ zlog(ZLOG_WARNING, "the name of the header must be set between embraces for %%%c", *s);
+ return -1;
+ }
+ if (!test) {
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ sapi_headers_struct *sapi_headers = &SG(sapi_headers);
+ size_t format_len = strlen(format);
+
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ char *header;
+ if (!h->header_len) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+ if (!strstr(h->header, format)) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+
+ /* test if enought char after the header name + ': ' */
+ if (h->header_len <= format_len + 2) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+
+ if (h->header[format_len] != ':' || h->header[format_len + 1] != ' ') {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+
+ header = h->header + format_len + 2;
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", header && *header ? header : "-");
+
+ /* found, done */
+ break;
+ }
+ if (!len2) {
+ len2 = 1;
+ *b = '-';
+ }
+ }
+ format[0] = '\0';
+ break;
+
+ case 'p': /* PID */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%ld", (long)getpid());
+ }
+ break;
+
+ case 'P': /* PID */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%ld", (long)getppid());
+ }
+ break;
+
+ case 'q': /* query_string */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.query_string ? proc.query_string : "");
+ }
+ break;
+
+ case 'Q': /* '?' */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.query_string && *proc.query_string ? "?" : "");
+ }
+ break;
+
+ case 'r': /* request URI */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.request_uri ? proc.request_uri : "-");
+ }
+ break;
+
+ case 'R': /* remote IP address */
+ if (!test) {
+ char *tmp = fcgi_get_last_client_ip();
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp ? tmp : "-");
+ }
+ break;
+
+ case 's': /* status */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%d", SG(sapi_headers).http_response_code);
+ }
+ break;
+
+ case 'T':
+ case 't': /* time */
+ if (!test) {
+ time_t *t;
+ if (*s == 't') {
+ t = &proc.accepted_epoch;
+ } else {
+ t = &now_epoch;
+ }
+ if (format[0] == '\0') {
+ strftime(tmp, sizeof(tmp) - 1, "%d/%b/%Y:%H:%M:%S %z", localtime(t));
+ } else {
+ strftime(tmp, sizeof(tmp) - 1, format, localtime(t));
+ }
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp);
+ }
+ format[0] = '\0';
+ break;
+
+ case 'u': /* remote user */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.auth_user ? proc.auth_user : "-");
+ }
+ break;
+
+ case '{': /* complex var */
+ token = 1;
+ {
+ char *start;
+ size_t l;
+
+ start = ++s;
+
+ while (*s != '\0') {
+ if (*s == '}') {
+ l = s - start;
+
+ if (l >= sizeof(format) - 1) {
+ l = sizeof(format) - 1;
+ }
+
+ memcpy(format, start, l);
+ format[l] = '\0';
+ break;
+ }
+ s++;
+ }
+ if (s[1] == '\0') {
+ zlog(ZLOG_WARNING, "missing closing embrace in the access.format");
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ zlog(ZLOG_WARNING, "Invalid token in the access.format (%%%c)", *s);
+ return -1;
+ }
+
+ if (*s != '}' && format[0] != '\0') {
+ zlog(ZLOG_WARNING, "embrace is not allowed for modifier %%%c", *s);
+ return -1;
+ }
+ s++;
+ if (!test) {
+ b += len2;
+ len += len2;
+ }
+ continue;
+ }
+
+ if (!test) {
+ // push the normal char to the output buffer
+ *b = *s;
+ b++;
+ len++;
+ }
+ s++;
+ }
+
+ if (!test && strlen(buffer) > 0) {
+ buffer[len] = '\n';
+ write(fpm_log_fd, buffer, len + 1);
+ }
+
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_log.h b/sapi/fpm/fpm/fpm_log.h
new file mode 100644
index 0000000..f0199d9
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_log.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_status.h 312263 2011-06-18 17:46:16Z felipe $ */
+ /* (c) 2009 Jerome Loyet */
+
+#ifndef FPM_LOG_H
+#define FPM_LOG_H 1
+#include "fpm_worker_pool.h"
+
+int fpm_log_init_child(struct fpm_worker_pool_s *wp);
+int fpm_log_write(char *log_format TSRMLS_DC);
+int fpm_log_open(int reopen);
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
new file mode 100644
index 0000000..61088c4
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -0,0 +1,2000 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Bakken <ssb@php.net> |
+ | Zeev Suraski <zeev@zend.com> |
+ | FastCGI: Ben Mansell <php@slimyhorror.com> |
+ | Shane Caraveo <shane@caraveo.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: cgi_main.c 291497 2009-11-30 14:43:22Z dmitry $ */
+
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "zend_modules.h"
+#include "php.h"
+#include "zend_ini_scanner.h"
+#include "zend_globals.h"
+#include "zend_stream.h"
+
+#include "SAPI.h"
+
+#include <stdio.h>
+#include "php.h"
+
+#ifdef PHP_WIN32
+# include "win32/time.h"
+# include "win32/signal.h"
+# include <process.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+
+#if HAVE_SETLOCALE
+# include <locale.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#include "zend.h"
+#include "zend_extensions.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "php_main.h"
+#include "fopen_wrappers.h"
+#include "ext/standard/php_standard.h"
+
+#ifdef PHP_WIN32
+# include <io.h>
+# include <fcntl.h>
+# include "win32/php_registry.h"
+#endif
+
+#ifdef __riscos__
+# include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
+#endif
+
+#include "zend_compile.h"
+#include "zend_execute.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+
+#include "php_getopt.h"
+
+#include "fastcgi.h"
+
+#include <php_config.h>
+#include "fpm.h"
+#include "fpm_request.h"
+#include "fpm_status.h"
+#include "fpm_conf.h"
+#include "fpm_php.h"
+#include "fpm_log.h"
+#include "zlog.h"
+
+#ifndef PHP_WIN32
+/* XXX this will need to change later when threaded fastcgi is implemented. shane */
+struct sigaction act, old_term, old_quit, old_int;
+#endif
+
+static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
+
+#ifndef PHP_WIN32
+/* these globals used for forking children on unix systems */
+
+/**
+ * Set to non-zero if we are the parent process
+ */
+static int parent = 1;
+#endif
+
+static int request_body_fd;
+static int fpm_is_running = 0;
+
+static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC);
+static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC);
+
+#define PHP_MODE_STANDARD 1
+#define PHP_MODE_HIGHLIGHT 2
+#define PHP_MODE_INDENT 3
+#define PHP_MODE_LINT 4
+#define PHP_MODE_STRIP 5
+
+static char *php_optarg = NULL;
+static int php_optind = 1;
+static zend_module_entry cgi_module_entry;
+
+static const opt_struct OPTIONS[] = {
+ {'c', 1, "php-ini"},
+ {'d', 1, "define"},
+ {'e', 0, "profile-info"},
+ {'h', 0, "help"},
+ {'i', 0, "info"},
+ {'m', 0, "modules"},
+ {'n', 0, "no-php-ini"},
+ {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
+ {'v', 0, "version"},
+ {'y', 1, "fpm-config"},
+ {'t', 0, "test"},
+ {'p', 1, "prefix"},
+ {'g', 1, "pid"},
+ {'R', 0, "allow-to-run-as-root"},
+ {'D', 0, "daemonize"},
+ {'F', 0, "nodaemonize"},
+ {'-', 0, NULL} /* end of args */
+};
+
+typedef struct _php_cgi_globals_struct {
+ zend_bool rfc2616_headers;
+ zend_bool nph;
+ zend_bool fix_pathinfo;
+ zend_bool force_redirect;
+ zend_bool discard_path;
+ zend_bool fcgi_logging;
+ char *redirect_status_env;
+ HashTable user_config_cache;
+ char *error_header;
+ char *fpm_config;
+} php_cgi_globals_struct;
+
+/* {{{ user_config_cache
+ *
+ * Key for each cache entry is dirname(PATH_TRANSLATED).
+ *
+ * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
+ * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point
+ * storing per-file entries as it would not be possible to detect added / deleted entries
+ * between separate files.
+ */
+typedef struct _user_config_cache_entry {
+ time_t expires;
+ HashTable *user_config;
+} user_config_cache_entry;
+
+static void user_config_cache_entry_dtor(user_config_cache_entry *entry)
+{
+ zend_hash_destroy(entry->user_config);
+ free(entry->user_config);
+}
+/* }}} */
+
+#ifdef ZTS
+static int php_cgi_globals_id;
+#define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
+#else
+static php_cgi_globals_struct php_cgi_globals;
+#define CGIG(v) (php_cgi_globals.v)
+#endif
+
+#ifdef PHP_WIN32
+#define TRANSLATE_SLASHES(path) \
+ { \
+ char *tmp = path; \
+ while (*tmp) { \
+ if (*tmp == '\\') *tmp = '/'; \
+ tmp++; \
+ } \
+ }
+#else
+#define TRANSLATE_SLASHES(path)
+#endif
+
+static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
+{
+ php_printf("%s\n", module->name);
+ return 0;
+}
+
+static int module_name_cmp(const void *a, const void *b TSRMLS_DC)
+{
+ Bucket *f = *((Bucket **) a);
+ Bucket *s = *((Bucket **) b);
+
+ return strcasecmp( ((zend_module_entry *)f->pData)->name,
+ ((zend_module_entry *)s->pData)->name);
+}
+
+static void print_modules(TSRMLS_D)
+{
+ HashTable sorted_registry;
+ zend_module_entry tmp;
+
+ zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
+ zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
+ zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
+ zend_hash_apply_with_argument(&sorted_registry, (apply_func_arg_t) print_module_info, NULL TSRMLS_CC);
+ zend_hash_destroy(&sorted_registry);
+}
+
+static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC)
+{
+ php_printf("%s\n", ext->name);
+ return 0;
+}
+
+static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC)
+{
+ return strcmp( ((zend_extension *)(*f)->data)->name,
+ ((zend_extension *)(*s)->data)->name);
+}
+
+static void print_extensions(TSRMLS_D)
+{
+ zend_llist sorted_exts;
+
+ zend_llist_copy(&sorted_exts, &zend_extensions);
+ sorted_exts.dtor = NULL;
+ zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
+ zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL TSRMLS_CC);
+ zend_llist_destroy(&sorted_exts);
+}
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC)
+{
+ ssize_t ret;
+
+ /* sapi has started which means everyhting must be send through fcgi */
+ if (fpm_is_running) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ ret = fcgi_write(request, FCGI_STDOUT, str, str_length);
+ if (ret <= 0) {
+ return 0;
+ }
+ return (size_t)ret;
+ }
+
+ /* sapi has not started, output to stdout instead of fcgi */
+#ifdef PHP_WRITE_STDOUT
+ ret = write(STDOUT_FILENO, str, str_length);
+ if (ret <= 0) {
+ return 0;
+ }
+ return (size_t)ret;
+#else
+ return fwrite(str, 1, MIN(str_length, 16384), stdout);
+#endif
+}
+
+static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ const char *ptr = str;
+ uint remaining = str_length;
+ size_t ret;
+
+ while (remaining > 0) {
+ ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
+ if (!ret) {
+ php_handle_aborted_connection();
+ return str_length - remaining;
+ }
+ ptr += ret;
+ remaining -= ret;
+ }
+
+ return str_length;
+}
+
+
+static void sapi_cgibin_flush(void *server_context)
+{
+ /* fpm has started, let use fcgi instead of stdout */
+ if (fpm_is_running) {
+ fcgi_request *request = (fcgi_request*) server_context;
+ if (
+#ifndef PHP_WIN32
+ !parent &&
+#endif
+ request && !fcgi_flush(request, 0)) {
+ php_handle_aborted_connection();
+ }
+ return;
+ }
+
+ /* fpm has not started yet, let use stdout instead of fcgi */
+ if (fflush(stdout) == EOF) {
+ php_handle_aborted_connection();
+ }
+}
+
+#define SAPI_CGI_MAX_HEADER_LENGTH 1024
+
+typedef struct _http_error {
+ int code;
+ const char* msg;
+} http_error;
+
+static const http_error http_error_codes[] = {
+ {100, "Continue"},
+ {101, "Switching Protocols"},
+ {200, "OK"},
+ {201, "Created"},
+ {202, "Accepted"},
+ {203, "Non-Authoritative Information"},
+ {204, "No Content"},
+ {205, "Reset Content"},
+ {206, "Partial Content"},
+ {300, "Multiple Choices"},
+ {301, "Moved Permanently"},
+ {302, "Moved Temporarily"},
+ {303, "See Other"},
+ {304, "Not Modified"},
+ {305, "Use Proxy"},
+ {400, "Bad Request"},
+ {401, "Unauthorized"},
+ {402, "Payment Required"},
+ {403, "Forbidden"},
+ {404, "Not Found"},
+ {405, "Method Not Allowed"},
+ {406, "Not Acceptable"},
+ {407, "Proxy Authentication Required"},
+ {408, "Request Time-out"},
+ {409, "Conflict"},
+ {410, "Gone"},
+ {411, "Length Required"},
+ {412, "Precondition Failed"},
+ {413, "Request Entity Too Large"},
+ {414, "Request-URI Too Large"},
+ {415, "Unsupported Media Type"},
+ {428, "Precondition Required"},
+ {429, "Too Many Requests"},
+ {431, "Request Header Fields Too Large"},
+ {500, "Internal Server Error"},
+ {501, "Not Implemented"},
+ {502, "Bad Gateway"},
+ {503, "Service Unavailable"},
+ {504, "Gateway Time-out"},
+ {505, "HTTP Version not supported"},
+ {511, "Network Authentication Required"},
+ {0, NULL}
+};
+
+static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char buf[SAPI_CGI_MAX_HEADER_LENGTH];
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ zend_bool ignore_status = 0;
+ int response_status = SG(sapi_headers).http_response_code;
+
+ if (SG(request_info).no_headers == 1) {
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+ }
+
+ if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
+ {
+ int len;
+ zend_bool has_status = 0;
+
+ if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
+ char *s;
+ len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
+ if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
+ response_status = atoi((s + 1));
+ }
+
+ if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
+ len = SAPI_CGI_MAX_HEADER_LENGTH;
+ }
+
+ } else {
+ char *s;
+
+ if (SG(sapi_headers).http_status_line &&
+ (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
+ (s - SG(sapi_headers).http_status_line) >= 5 &&
+ strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
+ ) {
+ len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
+ response_status = atoi((s + 1));
+ } else {
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ if (h->header_len > sizeof("Status:") - 1 &&
+ strncasecmp(h->header, "Status:", sizeof("Status:") - 1) == 0
+ ) {
+ has_status = 1;
+ break;
+ }
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ if (!has_status) {
+ http_error *err = (http_error*)http_error_codes;
+
+ while (err->code != 0) {
+ if (err->code == SG(sapi_headers).http_response_code) {
+ break;
+ }
+ err++;
+ }
+ if (err->msg) {
+ len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->msg);
+ } else {
+ len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
+ }
+ }
+ }
+ }
+
+ if (!has_status) {
+ PHPWRITE_H(buf, len);
+ ignore_status = 1;
+ }
+ }
+
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ /* prevent CRLFCRLF */
+ if (h->header_len) {
+ if (h->header_len > sizeof("Status:") - 1 &&
+ strncasecmp(h->header, "Status:", sizeof("Status:") - 1) == 0
+ ) {
+ if (!ignore_status) {
+ ignore_status = 1;
+ PHPWRITE_H(h->header, h->header_len);
+ PHPWRITE_H("\r\n", 2);
+ }
+ } else if (response_status == 304 && h->header_len > sizeof("Content-Type:") - 1 &&
+ strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:") - 1) == 0
+ ) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ } else {
+ PHPWRITE_H(h->header, h->header_len);
+ PHPWRITE_H("\r\n", 2);
+ }
+ }
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ PHPWRITE_H("\r\n", 2);
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+
+static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ uint read_bytes = 0;
+ int tmp_read_bytes;
+
+ count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
+ while (read_bytes < count_bytes) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ if (request_body_fd == -1) {
+ char *request_body_filename = sapi_cgibin_getenv((char *) "REQUEST_BODY_FILE",
+ sizeof("REQUEST_BODY_FILE") - 1 TSRMLS_CC);
+
+ if (request_body_filename && *request_body_filename) {
+ request_body_fd = open(request_body_filename, O_RDONLY);
+
+ if (0 > request_body_fd) {
+ php_error(E_WARNING, "REQUEST_BODY_FILE: open('%s') failed: %s (%d)",
+ request_body_filename, strerror(errno), errno);
+ return 0;
+ }
+ }
+ }
+
+ /* If REQUEST_BODY_FILE variable not available - read post body from fastcgi stream */
+ if (request_body_fd < 0) {
+ tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
+ } else {
+ tmp_read_bytes = read(request_body_fd, buffer + read_bytes, count_bytes - read_bytes);
+ }
+ if (tmp_read_bytes <= 0) {
+ break;
+ }
+ read_bytes += tmp_read_bytes;
+ }
+ return read_bytes;
+}
+
+static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ /* if fpm has started, use fcgi env */
+ if (fpm_is_running) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ return fcgi_getenv(request, name, name_len);
+ }
+
+ /* if fpm has not started yet, use std env */
+ return getenv(name);
+}
+
+static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC)
+{
+ int name_len;
+
+ if (!name) {
+ return NULL;
+ }
+ name_len = strlen(name);
+
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ return fcgi_putenv(request, name, name_len, value);
+}
+
+static char *sapi_cgi_read_cookies(TSRMLS_D)
+{
+ return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE") - 1 TSRMLS_CC);
+}
+
+void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
+{
+ fcgi_request *request;
+ HashPosition pos;
+ char *var, **val;
+ uint var_len;
+ ulong idx;
+ int filter_arg;
+
+
+ if (PG(http_globals)[TRACK_VARS_ENV] &&
+ array_ptr != PG(http_globals)[TRACK_VARS_ENV] &&
+ Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
+ zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0
+ ) {
+ zval_dtor(array_ptr);
+ *array_ptr = *PG(http_globals)[TRACK_VARS_ENV];
+ INIT_PZVAL(array_ptr);
+ zval_copy_ctor(array_ptr);
+ return;
+ } else if (PG(http_globals)[TRACK_VARS_SERVER] &&
+ array_ptr != PG(http_globals)[TRACK_VARS_SERVER] &&
+ Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
+ zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0
+ ) {
+ zval_dtor(array_ptr);
+ *array_ptr = *PG(http_globals)[TRACK_VARS_SERVER];
+ INIT_PZVAL(array_ptr);
+ zval_copy_ctor(array_ptr);
+ return;
+ }
+
+ /* call php's original import as a catch-all */
+ php_php_import_environment_variables(array_ptr TSRMLS_CC);
+
+ request = (fcgi_request*) SG(server_context);
+ filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER;
+
+ for (zend_hash_internal_pointer_reset_ex(request->env, &pos);
+ zend_hash_get_current_key_ex(request->env, &var, &var_len, &idx, 0, &pos) == HASH_KEY_IS_STRING &&
+ zend_hash_get_current_data_ex(request->env, (void **) &val, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(request->env, &pos)
+ ) {
+ unsigned int new_val_len;
+
+ if (sapi_module.input_filter(filter_arg, var, val, strlen(*val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe(var, *val, new_val_len, array_ptr TSRMLS_CC);
+ }
+ }
+}
+
+static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ unsigned int php_self_len;
+ char *php_self;
+
+ /* In CGI mode, we consider the environment to be a part of the server
+ * variables
+ */
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ if (CGIG(fix_pathinfo)) {
+ char *script_name = SG(request_info).request_uri;
+ unsigned int script_name_len = script_name ? strlen(script_name) : 0;
+ char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO") - 1 TSRMLS_CC);
+ unsigned int path_info_len = path_info ? strlen(path_info) : 0;
+
+ php_self_len = script_name_len + path_info_len;
+ php_self = emalloc(php_self_len + 1);
+
+ /* Concat script_name and path_info into php_self */
+ if (script_name) {
+ memcpy(php_self, script_name, script_name_len + 1);
+ }
+ if (path_info) {
+ memcpy(php_self + script_name_len, path_info, path_info_len + 1);
+ }
+
+ /* Build the special-case PHP_SELF variable for the CGI version */
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
+ }
+ efree(php_self);
+ } else {
+ php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
+ php_self_len = strlen(php_self);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
+ }
+ }
+}
+
+/* {{{ sapi_cgi_log_fastcgi
+ *
+ * Ignore level, we want to send all messages through fastcgi
+ */
+void sapi_cgi_log_fastcgi(int level, char *message, size_t len)
+{
+ TSRMLS_FETCH();
+
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ /* ensure we want:
+ * - to log (fastcgi.logging in php.ini)
+ * - we are currently dealing with a request
+ * - the message is not empty
+ */
+ if (CGIG(fcgi_logging) && request && message && len > 0) {
+ char *buf = malloc(len + 2);
+ memcpy(buf, message, len);
+ memcpy(buf + len, "\n", sizeof("\n"));
+ fcgi_write(request, FCGI_STDERR, buf, len+1);
+ free(buf);
+ }
+}
+/* }}} */
+
+/* {{{ sapi_cgi_log_message
+ */
+static void sapi_cgi_log_message(char *message)
+{
+ zlog(ZLOG_NOTICE, "PHP message: %s", message);
+}
+/* }}} */
+
+/* {{{ php_cgi_ini_activate_user_config
+ */
+static void php_cgi_ini_activate_user_config(char *path, int path_len, const char *doc_root, int doc_root_len, int start TSRMLS_DC)
+{
+ char *ptr;
+ user_config_cache_entry *new_entry, *entry;
+ time_t request_time = sapi_get_request_time(TSRMLS_C);
+
+ /* Find cached config entry: If not found, create one */
+ if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) {
+ new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
+ new_entry->expires = 0;
+ new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
+ zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1);
+ zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry);
+ free(new_entry);
+ }
+
+ /* Check whether cache entry has expired and rescan if it is */
+ if (request_time > entry->expires) {
+ char * real_path;
+ int real_path_len;
+ char *s1, *s2;
+ int s_len;
+
+ /* Clear the expired config */
+ zend_hash_clean(entry->user_config);
+
+ if (!IS_ABSOLUTE_PATH(path, path_len)) {
+ real_path = tsrm_realpath(path, NULL TSRMLS_CC);
+ if (real_path == NULL) {
+ return;
+ }
+ real_path_len = strlen(real_path);
+ path = real_path;
+ path_len = real_path_len;
+ }
+
+ if (path_len > doc_root_len) {
+ s1 = (char *) doc_root;
+ s2 = path;
+ s_len = doc_root_len;
+ } else {
+ s1 = path;
+ s2 = (char *) doc_root;
+ s_len = path_len;
+ }
+
+ /* we have to test if path is part of DOCUMENT_ROOT.
+ if it is inside the docroot, we scan the tree up to the docroot
+ to find more user.ini, if not we only scan the current path.
+ */
+#ifdef PHP_WIN32
+ if (strnicmp(s1, s2, s_len) == 0) {
+#else
+ if (strncmp(s1, s2, s_len) == 0) {
+#endif
+ ptr = s2 + start; /* start is the point where doc_root ends! */
+ while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
+ *ptr = 0;
+ php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
+ *ptr = '/';
+ ptr++;
+ }
+ } else {
+ php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
+ }
+
+ entry->expires = request_time + PG(user_ini_cache_ttl);
+ }
+
+ /* Activate ini entries with values from the user config hash */
+ php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC);
+}
+/* }}} */
+
+static int sapi_cgi_activate(TSRMLS_D)
+{
+ char *path, *doc_root, *server_name;
+ uint path_len, doc_root_len, server_name_len;
+
+ /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
+ if (!SG(request_info).path_translated) {
+ return FAILURE;
+ }
+
+ if (php_ini_has_per_host_config()) {
+ /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
+ server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1 TSRMLS_CC);
+ /* SERVER_NAME should also be defined at this stage..but better check it anyway */
+ if (server_name) {
+ server_name_len = strlen(server_name);
+ server_name = estrndup(server_name, server_name_len);
+ zend_str_tolower(server_name, server_name_len);
+ php_ini_activate_per_host_config(server_name, server_name_len + 1 TSRMLS_CC);
+ efree(server_name);
+ }
+ }
+
+ if (php_ini_has_per_dir_config() ||
+ (PG(user_ini_filename) && *PG(user_ini_filename))
+ ) {
+ /* Prepare search path */
+ path_len = strlen(SG(request_info).path_translated);
+
+ /* Make sure we have trailing slash! */
+ if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
+ path = emalloc(path_len + 2);
+ memcpy(path, SG(request_info).path_translated, path_len + 1);
+ path_len = zend_dirname(path, path_len);
+ path[path_len++] = DEFAULT_SLASH;
+ } else {
+ path = estrndup(SG(request_info).path_translated, path_len);
+ path_len = zend_dirname(path, path_len);
+ }
+ path[path_len] = 0;
+
+ /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
+ php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */
+
+ /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
+ if (PG(user_ini_filename) && *PG(user_ini_filename)) {
+ doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
+ /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
+ if (doc_root) {
+ doc_root_len = strlen(doc_root);
+ if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
+ --doc_root_len;
+ }
+#ifdef PHP_WIN32
+ /* paths on windows should be case-insensitive */
+ doc_root = estrndup(doc_root, doc_root_len);
+ zend_str_tolower(doc_root, doc_root_len);
+#endif
+ php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, doc_root_len - 1 TSRMLS_CC);
+ }
+ }
+
+#ifdef PHP_WIN32
+ efree(doc_root);
+#endif
+ efree(path);
+ }
+
+ return SUCCESS;
+}
+
+static int sapi_cgi_deactivate(TSRMLS_D)
+{
+ /* flush only when SAPI was started. The reasons are:
+ 1. SAPI Deactivate is called from two places: module init and request shutdown
+ 2. When the first call occurs and the request is not set up, flush fails on FastCGI.
+ */
+ if (SG(sapi_started)) {
+ if (
+#ifndef PHP_WIN32
+ !parent &&
+#endif
+ !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
+ php_handle_aborted_connection();
+ }
+ }
+ return SUCCESS;
+}
+
+static int php_cgi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+/* {{{ sapi_module_struct cgi_sapi_module
+ */
+static sapi_module_struct cgi_sapi_module = {
+ "fpm-fcgi", /* name */
+ "FPM/FastCGI", /* pretty name */
+
+ php_cgi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ sapi_cgi_activate, /* activate */
+ sapi_cgi_deactivate, /* deactivate */
+
+ sapi_cgibin_ub_write, /* unbuffered write */
+ sapi_cgibin_flush, /* flush */
+ NULL, /* get uid */
+ sapi_cgibin_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ sapi_cgi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_cgi_read_post, /* read POST data */
+ sapi_cgi_read_cookies, /* read Cookies */
+
+ sapi_cgi_register_variables, /* register server variables */
+ sapi_cgi_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ arginfo ext/standard/dl.c */
+ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
+ ZEND_ARG_INFO(0, extension_filename)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry additional_functions[] = {
+ ZEND_FE(dl, arginfo_dl)
+ {NULL, NULL, NULL}
+};
+
+/* {{{ php_cgi_usage
+ */
+static void php_cgi_usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "php";
+ }
+
+ php_printf( "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix>] [-g <pid>] [-c <file>] [-d foo[=bar]] [-y <file>] [-D] [-F]\n"
+ " -c <path>|<file> Look for php.ini file in this directory\n"
+ " -n No php.ini file will be used\n"
+ " -d foo[=bar] Define INI entry foo with value 'bar'\n"
+ " -e Generate extended information for debugger/profiler\n"
+ " -h This help\n"
+ " -i PHP information\n"
+ " -m Show compiled in modules\n"
+ " -v Version number\n"
+ " -p, --prefix <dir>\n"
+ " Specify alternative prefix path to FastCGI process manager (default: %s).\n"
+ " -g, --pid <file>\n"
+ " Specify the PID file location.\n"
+ " -y, --fpm-config <file>\n"
+ " Specify alternative path to FastCGI process manager config file.\n"
+ " -t, --test Test FPM configuration and exit\n"
+ " -D, --daemonize force to run in background, and ignore daemonize option from config file\n"
+ " -F, --nodaemonize\n"
+ " force to stay in foreground, and ignore daemonize option from config file\n"
+ " -R, --allow-to-run-as-root\n"
+ " Allow pool to run as root (disabled by default)\n",
+ prog, PHP_PREFIX);
+}
+/* }}} */
+
+/* {{{ is_valid_path
+ *
+ * some server configurations allow '..' to slip through in the
+ * translated path. We'll just refuse to handle such a path.
+ */
+static int is_valid_path(const char *path)
+{
+ const char *p;
+
+ if (!path) {
+ return 0;
+ }
+ p = strstr(path, "..");
+ if (p) {
+ if ((p == path || IS_SLASH(*(p-1))) &&
+ (*(p+2) == 0 || IS_SLASH(*(p+2)))
+ ) {
+ return 0;
+ }
+ while (1) {
+ p = strstr(p+1, "..");
+ if (!p) {
+ break;
+ }
+ if (IS_SLASH(*(p-1)) &&
+ (*(p+2) == 0 || IS_SLASH(*(p+2)))
+ ) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ init_request_info
+
+ initializes request_info structure
+
+ specificly in this section we handle proper translations
+ for:
+
+ PATH_INFO
+ derived from the portion of the URI path following
+ the script name but preceding any query data
+ may be empty
+
+ PATH_TRANSLATED
+ derived by taking any path-info component of the
+ request URI and performing any virtual-to-physical
+ translation appropriate to map it onto the server's
+ document repository structure
+
+ empty if PATH_INFO is empty
+
+ The env var PATH_TRANSLATED **IS DIFFERENT** than the
+ request_info.path_translated variable, the latter should
+ match SCRIPT_FILENAME instead.
+
+ SCRIPT_NAME
+ set to a URL path that could identify the CGI script
+ rather than the interpreter. PHP_SELF is set to this
+
+ REQUEST_URI
+ uri section following the domain:port part of a URI
+
+ SCRIPT_FILENAME
+ The virtual-to-physical translation of SCRIPT_NAME (as per
+ PATH_TRANSLATED)
+
+ These settings are documented at
+ http://cgi-spec.golux.com/
+
+
+ Based on the following URL request:
+
+ http://localhost/info.php/test?a=b
+
+ should produce, which btw is the same as if
+ we were running under mod_cgi on apache (ie. not
+ using ScriptAlias directives):
+
+ PATH_INFO=/test
+ PATH_TRANSLATED=/docroot/test
+ SCRIPT_NAME=/info.php
+ REQUEST_URI=/info.php/test?a=b
+ SCRIPT_FILENAME=/docroot/info.php
+ QUERY_STRING=a=b
+
+ but what we get is (cgi/mod_fastcgi under apache):
+
+ PATH_INFO=/info.php/test
+ PATH_TRANSLATED=/docroot/info.php/test
+ SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose)
+ REQUEST_URI=/info.php/test?a=b
+ SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated)
+ QUERY_STRING=a=b
+
+ Comments in the code below refer to using the above URL in a request
+
+ */
+static void init_request_info(TSRMLS_D)
+{
+ char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME") - 1 TSRMLS_CC);
+ char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED") - 1 TSRMLS_CC);
+ char *script_path_translated = env_script_filename;
+ char *ini;
+ int apache_was_here = 0;
+
+ /* some broken servers do not have script_filename or argv0
+ * an example, IIS configured in some ways. then they do more
+ * broken stuff and set path_translated to the cgi script location */
+ if (!script_path_translated && env_path_translated) {
+ script_path_translated = env_path_translated;
+ }
+
+ /* initialize the defaults */
+ SG(request_info).path_translated = NULL;
+ SG(request_info).request_method = NULL;
+ SG(request_info).proto_num = 1000;
+ SG(request_info).query_string = NULL;
+ SG(request_info).request_uri = NULL;
+ SG(request_info).content_type = NULL;
+ SG(request_info).content_length = 0;
+ SG(sapi_headers).http_response_code = 200;
+
+ /* script_path_translated being set is a good indication that
+ * we are running in a cgi environment, since it is always
+ * null otherwise. otherwise, the filename
+ * of the script will be retreived later via argc/argv */
+ if (script_path_translated) {
+ const char *auth;
+ char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH") - 1 TSRMLS_CC);
+ char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE") - 1 TSRMLS_CC);
+ char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO") - 1 TSRMLS_CC);
+ char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME") - 1 TSRMLS_CC);
+
+ /* Hack for buggy IIS that sets incorrect PATH_INFO */
+ char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE") - 1 TSRMLS_CC);
+ if (env_server_software &&
+ env_script_name &&
+ env_path_info &&
+ strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS") - 1) == 0 &&
+ strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
+ ) {
+ env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC);
+ env_path_info += strlen(env_script_name);
+ if (*env_path_info == 0) {
+ env_path_info = NULL;
+ }
+ env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info TSRMLS_CC);
+ }
+
+#define APACHE_PROXY_FCGI_PREFIX "proxy:fcgi://"
+ /* Fix proxy URLs in SCRIPT_FILENAME generated by Apache mod_proxy_fcgi:
+ * proxy:fcgi://localhost:9000/some-dir/info.php/test
+ * should be changed to:
+ * /some-dir/info.php/test
+ * See: http://bugs.php.net/bug.php?id=54152
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=50851
+ */
+ if (env_script_filename &&
+ strncasecmp(env_script_filename, APACHE_PROXY_FCGI_PREFIX, sizeof(APACHE_PROXY_FCGI_PREFIX) - 1) == 0) {
+ /* advance to first character of hostname */
+ char *p = env_script_filename + (sizeof(APACHE_PROXY_FCGI_PREFIX) - 1);
+ while (*p != '\0' && *p != '/') {
+ p++; /* move past hostname and port */
+ }
+ if (*p != '\0') {
+ /* Copy path portion in place to avoid memory leak. Note
+ * that this also affects what script_path_translated points
+ * to. */
+ memmove(env_script_filename, p, strlen(p) + 1);
+ apache_was_here = 1;
+ }
+ }
+
+ if (CGIG(fix_pathinfo)) {
+ struct stat st;
+ char *real_path = NULL;
+ char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL") - 1 TSRMLS_CC);
+ char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
+ char *orig_path_translated = env_path_translated;
+ char *orig_path_info = env_path_info;
+ char *orig_script_name = env_script_name;
+ char *orig_script_filename = env_script_filename;
+ int script_path_translated_len;
+
+ if (!env_document_root && PG(doc_root)) {
+ env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC);
+ /* fix docroot */
+ TRANSLATE_SLASHES(env_document_root);
+ }
+
+ if (env_path_translated != NULL && env_redirect_url != NULL &&
+ env_path_translated != script_path_translated &&
+ strcmp(env_path_translated, script_path_translated) != 0) {
+ /*
+ * pretty much apache specific. If we have a redirect_url
+ * then our script_filename and script_name point to the
+ * php executable
+ */
+ script_path_translated = env_path_translated;
+ /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
+ env_script_name = env_redirect_url;
+ }
+
+#ifdef __riscos__
+ /* Convert path to unix format*/
+ __riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
+ script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
+#endif
+
+ /*
+ * if the file doesn't exist, try to extract PATH_INFO out
+ * of it by stat'ing back through the '/'
+ * this fixes url's like /info.php/test
+ */
+ if (script_path_translated &&
+ (script_path_translated_len = strlen(script_path_translated)) > 0 &&
+ (script_path_translated[script_path_translated_len-1] == '/' ||
+#ifdef PHP_WIN32
+ script_path_translated[script_path_translated_len-1] == '\\' ||
+#endif
+ (real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL)
+ ) {
+ char *pt = estrndup(script_path_translated, script_path_translated_len);
+ int len = script_path_translated_len;
+ char *ptr;
+
+ while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
+ *ptr = 0;
+ if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
+ /*
+ * okay, we found the base script!
+ * work out how many chars we had to strip off;
+ * then we can modify PATH_INFO
+ * accordingly
+ *
+ * we now have the makings of
+ * PATH_INFO=/test
+ * SCRIPT_FILENAME=/docroot/info.php
+ *
+ * we now need to figure out what docroot is.
+ * if DOCUMENT_ROOT is set, this is easy, otherwise,
+ * we have to play the game of hide and seek to figure
+ * out what SCRIPT_NAME should be
+ */
+ int ptlen = strlen(pt);
+ int slen = len - ptlen;
+ int pilen = env_path_info ? strlen(env_path_info) : 0;
+ int tflag = 0;
+ char *path_info;
+ if (apache_was_here) {
+ /* recall that PATH_INFO won't exist */
+ path_info = script_path_translated + ptlen;
+ tflag = (slen != 0 && (!orig_path_info || strcmp(orig_path_info, path_info) != 0));
+ } else {
+ path_info = env_path_info ? env_path_info + pilen - slen : NULL;
+ tflag = (orig_path_info != path_info);
+ }
+
+ if (tflag) {
+ if (orig_path_info) {
+ char old;
+
+ _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
+ old = path_info[0];
+ path_info[0] = 0;
+ if (!orig_script_name ||
+ strcmp(orig_script_name, env_path_info) != 0) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
+ path_info[0] = old;
+ }
+ env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC);
+ }
+ if (!orig_script_filename ||
+ strcmp(orig_script_filename, pt) != 0) {
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC);
+ }
+ TRANSLATE_SLASHES(pt);
+
+ /* figure out docroot
+ * SCRIPT_FILENAME minus SCRIPT_NAME
+ */
+ if (env_document_root) {
+ int l = strlen(env_document_root);
+ int path_translated_len = 0;
+ char *path_translated = NULL;
+
+ if (l && env_document_root[l - 1] == '/') {
+ --l;
+ }
+
+ /* we have docroot, so we should have:
+ * DOCUMENT_ROOT=/docroot
+ * SCRIPT_FILENAME=/docroot/info.php
+ */
+
+ /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
+ path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, env_document_root, l);
+ if (env_path_info) {
+ memcpy(path_translated + l, env_path_info, (path_translated_len - l));
+ }
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ }
+ env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
+ efree(path_translated);
+ } else if ( env_script_name &&
+ strstr(pt, env_script_name)
+ ) {
+ /* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
+ int ptlen = strlen(pt) - strlen(env_script_name);
+ int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
+ char *path_translated = NULL;
+
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, pt, ptlen);
+ if (env_path_info) {
+ memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
+ }
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ }
+ env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
+ efree(path_translated);
+ }
+ break;
+ }
+ }
+ if (!ptr) {
+ /*
+ * if we stripped out all the '/' and still didn't find
+ * a valid path... we will fail, badly. of course we would
+ * have failed anyway... we output 'no input file' now.
+ */
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC);
+ SG(sapi_headers).http_response_code = 404;
+ }
+ if (!SG(request_info).request_uri) {
+ if (!orig_script_name ||
+ strcmp(orig_script_name, env_script_name) != 0) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
+ }
+ if (pt) {
+ efree(pt);
+ }
+ } else {
+ /* make sure path_info/translated are empty */
+ if (!orig_script_filename ||
+ (script_path_translated != orig_script_filename &&
+ strcmp(script_path_translated, orig_script_filename) != 0)) {
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC);
+ }
+ if (env_redirect_url) {
+ if (orig_path_info) {
+ _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
+ _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC);
+ }
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC);
+ }
+ }
+ if (env_script_name != orig_script_name) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ free(real_path);
+ }
+ } else {
+ /* pre 4.3 behaviour, shouldn't be used but provides BC */
+ if (env_path_info) {
+ SG(request_info).request_uri = env_path_info;
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ if (!CGIG(discard_path) && env_path_translated) {
+ script_path_translated = env_path_translated;
+ }
+ }
+
+ if (is_valid_path(script_path_translated)) {
+ SG(request_info).path_translated = estrdup(script_path_translated);
+ }
+
+ SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD") - 1 TSRMLS_CC);
+ /* FIXME - Work out proto_num here */
+ SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING") - 1 TSRMLS_CC);
+ SG(request_info).content_type = (content_type ? content_type : "" );
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+
+ /* The CGI RFC allows servers to pass on unvalidated Authorization data */
+ auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION") - 1 TSRMLS_CC);
+ php_handle_auth_data(auth TSRMLS_CC);
+ }
+
+ /* INI stuff */
+ ini = sapi_cgibin_getenv("PHP_VALUE", sizeof("PHP_VALUE") - 1 TSRMLS_CC);
+ if (ini) {
+ int mode = ZEND_INI_USER;
+ char *tmp;
+ spprintf(&tmp, 0, "%s\n", ini);
+ zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fastcgi_ini_parser, &mode TSRMLS_CC);
+ efree(tmp);
+ }
+
+ ini = sapi_cgibin_getenv("PHP_ADMIN_VALUE", sizeof("PHP_ADMIN_VALUE") - 1 TSRMLS_CC);
+ if (ini) {
+ int mode = ZEND_INI_SYSTEM;
+ char *tmp;
+ spprintf(&tmp, 0, "%s\n", ini);
+ zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fastcgi_ini_parser, &mode TSRMLS_CC);
+ efree(tmp);
+ }
+}
+/* }}} */
+
+static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */
+{
+ int *mode = (int *)arg;
+ char *key;
+ char *value = NULL;
+ struct key_value_s kv;
+
+ if (!mode || !arg1) return;
+
+ if (callback_type != ZEND_INI_PARSER_ENTRY) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: only classic entries are allowed");
+ return;
+ }
+
+ key = Z_STRVAL_P(arg1);
+
+ if (!key || strlen(key) < 1) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: empty key");
+ return;
+ }
+
+ if (arg2) {
+ value = Z_STRVAL_P(arg2);
+ }
+
+ if (!value) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: empty value for key '%s'", key);
+ return;
+ }
+
+ kv.key = key;
+ kv.value = value;
+ kv.next = NULL;
+ if (fpm_php_apply_defines_ex(&kv, *mode) == -1) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: unable to set '%s'", key);
+ }
+}
+/* }}} */
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fastcgi.error_header", NULL, PHP_INI_SYSTEM, OnUpdateString, error_header, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fpm.config", NULL, PHP_INI_SYSTEM, OnUpdateString, fpm_config, php_cgi_globals_struct, php_cgi_globals)
+PHP_INI_END()
+
+/* {{{ php_cgi_globals_ctor
+ */
+static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
+{
+ php_cgi_globals->rfc2616_headers = 0;
+ php_cgi_globals->nph = 0;
+ php_cgi_globals->force_redirect = 1;
+ php_cgi_globals->redirect_status_env = NULL;
+ php_cgi_globals->fix_pathinfo = 1;
+ php_cgi_globals->discard_path = 0;
+ php_cgi_globals->fcgi_logging = 1;
+ zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1);
+ php_cgi_globals->error_header = NULL;
+ php_cgi_globals->fpm_config = NULL;
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(cgi)
+{
+#ifdef ZTS
+ ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
+#else
+ php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
+#endif
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+static PHP_MSHUTDOWN_FUNCTION(cgi)
+{
+ zend_hash_destroy(&CGIG(user_config_cache));
+
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+static PHP_MINFO_FUNCTION(cgi)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2, "php-fpm", "active");
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+PHP_FUNCTION(fastcgi_finish_request) /* {{{ */
+{
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ if (request->fd >= 0) {
+
+ php_output_end_all(TSRMLS_C);
+ php_header(TSRMLS_C);
+
+ fcgi_flush(request, 1);
+ fcgi_close(request, 0, 0);
+ RETURN_TRUE;
+ }
+
+ RETURN_FALSE;
+
+}
+/* }}} */
+
+static const zend_function_entry cgi_fcgi_sapi_functions[] = {
+ PHP_FE(fastcgi_finish_request, NULL)
+ {NULL, NULL, NULL}
+};
+
+static zend_module_entry cgi_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "cgi-fcgi",
+ cgi_fcgi_sapi_functions,
+ PHP_MINIT(cgi),
+ PHP_MSHUTDOWN(cgi),
+ NULL,
+ NULL,
+ PHP_MINFO(cgi),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/* {{{ main
+ */
+int main(int argc, char *argv[])
+{
+ int exit_status = FPM_EXIT_OK;
+ int cgi = 0, c, use_extended_info = 0;
+ zend_file_handle file_handle;
+
+ /* temporary locals */
+ int orig_optind = php_optind;
+ char *orig_optarg = php_optarg;
+ int ini_entries_len = 0;
+ /* end of temporary locals */
+
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+
+ int max_requests = 500;
+ int requests = 0;
+ int fcgi_fd = 0;
+ fcgi_request request;
+ char *fpm_config = NULL;
+ char *fpm_prefix = NULL;
+ char *fpm_pid = NULL;
+ int test_conf = 0;
+ int force_daemon = -1;
+ int php_information = 0;
+ int php_allow_to_run_as_root = 0;
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
+ that sockets created via fsockopen()
+ don't kill PHP if the remote site
+ closes it. in apache|apxs mode apache
+ does that for us! thies@thieso.net
+ 20000419 */
+#endif
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ tsrm_ls = ts_resource(0);
+#endif
+
+ sapi_startup(&cgi_sapi_module);
+ cgi_sapi_module.php_ini_path_override = NULL;
+ cgi_sapi_module.php_ini_ignore_cwd = 1;
+
+ fcgi_init();
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /* sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (c) {
+ case 'c':
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
+ cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
+ break;
+
+ case 'n':
+ cgi_sapi_module.php_ini_ignore = 1;
+ break;
+
+ case 'd': {
+ /* define ini entries on command line */
+ int len = strlen(php_optarg);
+ char *val;
+
+ if ((val = strchr(php_optarg, '='))) {
+ val++;
+ if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
+ ini_entries_len += (val - php_optarg);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
+ ini_entries_len++;
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
+ ini_entries_len += len - (val - php_optarg);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
+ ini_entries_len += sizeof("\n\0\"") - 2;
+ } else {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
+ ini_entries_len += len + sizeof("\n\0") - 2;
+ }
+ } else {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
+ ini_entries_len += len + sizeof("=1\n\0") - 2;
+ }
+ break;
+ }
+
+ case 'y':
+ fpm_config = php_optarg;
+ break;
+
+ case 'p':
+ fpm_prefix = php_optarg;
+ break;
+
+ case 'g':
+ fpm_pid = php_optarg;
+ break;
+
+ case 'e': /* enable extended info output */
+ use_extended_info = 1;
+ break;
+
+ case 't':
+ test_conf++;
+ break;
+
+ case 'm': /* list compiled in modules */
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ php_output_activate(TSRMLS_C);
+ SG(headers_sent) = 1;
+ php_printf("[PHP Modules]\n");
+ print_modules(TSRMLS_C);
+ php_printf("\n[Zend Modules]\n");
+ print_extensions(TSRMLS_C);
+ php_printf("\n");
+ php_output_end_all(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_OK;
+ goto out;
+
+ case 'i': /* php info & quit */
+ php_information = 1;
+ break;
+
+ case 'R': /* allow to run as root */
+ php_allow_to_run_as_root = 1;
+ break;
+
+ case 'D': /* daemonize */
+ force_daemon = 1;
+ break;
+
+ case 'F': /* nodaemonize */
+ force_daemon = 0;
+ break;
+
+ default:
+ case 'h':
+ case '?':
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ php_output_activate(TSRMLS_C);
+ SG(headers_sent) = 1;
+ php_cgi_usage(argv[0]);
+ php_output_end_all(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = (c == 'h') ? FPM_EXIT_OK : FPM_EXIT_USAGE;
+ goto out;
+
+ case 'v': /* show php version & quit */
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FPM_EXIT_SOFTWARE;
+ }
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+
+#if ZEND_DEBUG
+ php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#else
+ php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#endif
+ php_request_shutdown((void *) 0);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_OK;
+ goto out;
+ }
+ }
+
+ if (php_information) {
+ cgi_sapi_module.phpinfo_as_text = 1;
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FPM_EXIT_SOFTWARE;
+ }
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ php_print_info(0xFFFFFFFF TSRMLS_CC);
+ php_request_shutdown((void *) 0);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_OK;
+ goto out;
+ }
+
+ /* No other args are permitted here as there is no interactive mode */
+ if (argc != php_optind) {
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ php_output_activate(TSRMLS_C);
+ SG(headers_sent) = 1;
+ php_cgi_usage(argv[0]);
+ php_output_end_all(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_USAGE;
+ goto out;
+ }
+
+ php_optind = orig_optind;
+ php_optarg = orig_optarg;
+
+#ifdef ZTS
+ SG(request_info).path_translated = NULL;
+#endif
+
+ cgi_sapi_module.additional_functions = additional_functions;
+ cgi_sapi_module.executable_location = argv[0];
+
+ /* startup after we get the above ini override se we get things right */
+ if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return FPM_EXIT_SOFTWARE;
+ }
+
+ if (use_extended_info) {
+ CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
+ }
+
+ /* check force_cgi after startup, so we have proper output */
+ if (cgi && CGIG(force_redirect)) {
+ /* Apache will generate REDIRECT_STATUS,
+ * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
+ * redirect.so and installation instructions available from
+ * http://www.koehntopp.de/php.
+ * -- kk@netuse.de
+ */
+ if (!getenv("REDIRECT_STATUS") &&
+ !getenv ("HTTP_REDIRECT_STATUS") &&
+ /* this is to allow a different env var to be configured
+ * in case some server does something different than above */
+ (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
+ ) {
+ zend_try {
+ SG(sapi_headers).http_response_code = 400;
+ PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
+<p>This PHP CGI binary was compiled with force-cgi-redirect enabled. This\n\
+means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
+set, e.g. via an Apache Action directive.</p>\n\
+<p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
+manual page for CGI security</a>.</p>\n\
+<p>For more information about changing this behaviour or re-enabling this webserver,\n\
+consult the installation file that came with this distribution, or visit \n\
+<a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
+ } zend_catch {
+ } zend_end_try();
+#if defined(ZTS) && !defined(PHP_DEBUG)
+ /* XXX we're crashing here in msvc6 debug builds at
+ * php_message_handler_for_zend:839 because
+ * SG(request_info).path_translated is an invalid pointer.
+ * It still happens even though I set it to null, so something
+ * weird is going on.
+ */
+ tsrm_shutdown();
+#endif
+ return FPM_EXIT_SOFTWARE;
+ }
+ }
+
+ if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, fpm_pid, test_conf, php_allow_to_run_as_root, force_daemon)) {
+
+ if (fpm_globals.send_config_pipe[1]) {
+ int writeval = 0;
+ zlog(ZLOG_DEBUG, "Sending \"0\" (error) to parent via fd=%d", fpm_globals.send_config_pipe[1]);
+ write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
+ close(fpm_globals.send_config_pipe[1]);
+ }
+ return FPM_EXIT_CONFIG;
+ }
+
+ if (fpm_globals.send_config_pipe[1]) {
+ int writeval = 1;
+ zlog(ZLOG_DEBUG, "Sending \"1\" (OK) to parent via fd=%d", fpm_globals.send_config_pipe[1]);
+ write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
+ close(fpm_globals.send_config_pipe[1]);
+ }
+ fpm_is_running = 1;
+
+ fcgi_fd = fpm_run(&max_requests);
+ parent = 0;
+
+ /* onced forked tell zlog to also send messages through sapi_cgi_log_fastcgi() */
+ zlog_set_external_logger(sapi_cgi_log_fastcgi);
+
+ /* make php call us to get _ENV vars */
+ php_php_import_environment_variables = php_import_environment_variables;
+ php_import_environment_variables = cgi_php_import_environment_variables;
+
+ /* library is already initialized, now init our request */
+ fcgi_init_request(&request, fcgi_fd);
+
+ zend_first_try {
+ while (fcgi_accept_request(&request) >= 0) {
+ request_body_fd = -1;
+ SG(server_context) = (void *) &request;
+ init_request_info(TSRMLS_C);
+ CG(interactive) = 0;
+ char *primary_script = NULL;
+
+ fpm_request_info();
+
+ /* request startup only after we've done all we can to
+ * get path_translated */
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ fcgi_finish_request(&request, 1);
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FPM_EXIT_SOFTWARE;
+ }
+
+ /* check if request_method has been sent.
+ * if not, it's certainly not an HTTP over fcgi request */
+ if (!SG(request_info).request_method) {
+ goto fastcgi_request_done;
+ }
+
+ if (fpm_status_handle_request(TSRMLS_C)) {
+ goto fastcgi_request_done;
+ }
+
+ /* If path_translated is NULL, terminate here with a 404 */
+ if (!SG(request_info).path_translated) {
+ zend_try {
+ zlog(ZLOG_DEBUG, "Primary script unknown");
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("File not found.\n");
+ } zend_catch {
+ } zend_end_try();
+ goto fastcgi_request_done;
+ }
+
+ if (fpm_php_limit_extensions(SG(request_info).path_translated)) {
+ SG(sapi_headers).http_response_code = 403;
+ PUTS("Access denied.\n");
+ goto fastcgi_request_done;
+ }
+
+ /*
+ * have to duplicate SG(request_info).path_translated to be able to log errrors
+ * php_fopen_primary_script seems to delete SG(request_info).path_translated on failure
+ */
+ primary_script = estrdup(SG(request_info).path_translated);
+
+ /* path_translated exists, we can continue ! */
+ if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
+ zend_try {
+ zlog(ZLOG_ERROR, "Unable to open primary script: %s (%s)", primary_script, strerror(errno));
+ if (errno == EACCES) {
+ SG(sapi_headers).http_response_code = 403;
+ PUTS("Access denied.\n");
+ } else {
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("No input file specified.\n");
+ }
+ } zend_catch {
+ } zend_end_try();
+ /* we want to serve more requests if this is fastcgi
+ * so cleanup and continue, request shutdown is
+ * handled later */
+
+ goto fastcgi_request_done;
+ }
+
+ fpm_request_executing();
+
+ php_execute_script(&file_handle TSRMLS_CC);
+
+fastcgi_request_done:
+ if (primary_script) {
+ efree(primary_script);
+ }
+
+ if (request_body_fd != -1) {
+ close(request_body_fd);
+ }
+ request_body_fd = -2;
+
+ if (EG(exit_status) == 255) {
+ if (CGIG(error_header) && *CGIG(error_header)) {
+ sapi_header_line ctr = {0};
+
+ ctr.line = CGIG(error_header);
+ ctr.line_len = strlen(CGIG(error_header));
+ sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
+ }
+ }
+
+ fpm_request_end(TSRMLS_C);
+ fpm_log_write(NULL TSRMLS_CC);
+
+ STR_FREE(SG(request_info).path_translated);
+ SG(request_info).path_translated = NULL;
+
+ php_request_shutdown((void *) 0);
+
+ requests++;
+ if (max_requests && (requests == max_requests)) {
+ fcgi_finish_request(&request, 1);
+ break;
+ }
+ /* end of fastcgi loop */
+ }
+ fcgi_shutdown();
+
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
+ if (cgi_sapi_module.ini_entries) {
+ free(cgi_sapi_module.ini_entries);
+ }
+ } zend_catch {
+ exit_status = FPM_EXIT_SOFTWARE;
+ } zend_end_try();
+
+out:
+
+ SG(server_context) = NULL;
+ if (parent) {
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+ }
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+
+#if defined(PHP_WIN32) && ZEND_DEBUG && 0
+ _CrtDumpMemoryLeaks();
+#endif
+
+ return exit_status;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c
new file mode 100644
index 0000000..cd4d3ae
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php.c
@@ -0,0 +1,297 @@
+
+ /* $Id: fpm_php.c,v 1.22.2.4 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "php.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "ext/standard/dl.h"
+
+#include "fastcgi.h"
+
+#include "fpm.h"
+#include "fpm_php.h"
+#include "fpm_cleanup.h"
+#include "fpm_worker_pool.h"
+#include "zlog.h"
+
+static char **limit_extensions = NULL;
+
+static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage TSRMLS_DC) /* {{{ */
+{
+ zend_ini_entry *ini_entry;
+ char *duplicate;
+
+ if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
+ return FAILURE;
+ }
+
+ duplicate = strdup(new_value);
+
+ if (!ini_entry->on_modify
+ || ini_entry->on_modify(ini_entry, duplicate, new_value_length,
+ ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC) == SUCCESS) {
+ ini_entry->value = duplicate;
+ ini_entry->value_length = new_value_length;
+ ini_entry->modifiable = mode;
+ } else {
+ free(duplicate);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+static void fpm_php_disable(char *value, int (*zend_disable)(char *, uint TSRMLS_DC) TSRMLS_DC) /* {{{ */
+{
+ char *s = 0, *e = value;
+
+ while (*e) {
+ switch (*e) {
+ case ' ':
+ case ',':
+ if (s) {
+ *e = '\0';
+ zend_disable(s, e - s TSRMLS_CC);
+ s = 0;
+ }
+ break;
+ default:
+ if (!s) {
+ s = e;
+ }
+ break;
+ }
+ e++;
+ }
+
+ if (s) {
+ zend_disable(s, e - s TSRMLS_CC);
+ }
+}
+/* }}} */
+
+int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
+{
+ TSRMLS_FETCH();
+
+ char *name = kv->key;
+ char *value = kv->value;
+ int name_len = strlen(name);
+ int value_len = strlen(value);
+
+ if (!strcmp(name, "extension") && *value) {
+ zval zv;
+ php_dl(value, MODULE_PERSISTENT, &zv, 1 TSRMLS_CC);
+ return Z_BVAL(zv) ? 1 : -1;
+ }
+
+ if (fpm_php_zend_ini_alter_master(name, name_len+1, value, value_len, mode, PHP_INI_STAGE_ACTIVATE TSRMLS_CC) == FAILURE) {
+ return -1;
+ }
+
+ if (!strcmp(name, "disable_functions") && *value) {
+ char *v = strdup(value);
+ PG(disable_functions) = v;
+ fpm_php_disable(v, zend_disable_function TSRMLS_CC);
+ return 1;
+ }
+
+ if (!strcmp(name, "disable_classes") && *value) {
+ char *v = strdup(value);
+ PG(disable_classes) = v;
+ fpm_php_disable(v, zend_disable_class TSRMLS_CC);
+ return 1;
+ }
+
+ return 1;
+}
+/* }}} */
+
+static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct key_value_s *kv;
+
+ for (kv = wp->config->php_values; kv; kv = kv->next) {
+ if (fpm_php_apply_defines_ex(kv, ZEND_INI_USER) == -1) {
+ zlog(ZLOG_ERROR, "Unable to set php_value '%s'", kv->key);
+ }
+ }
+
+ for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+ if (fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM) == -1) {
+ zlog(ZLOG_ERROR, "Unable to set php_admin_value '%s'", kv->key);
+ }
+ }
+
+ return 0;
+}
+
+static int fpm_php_set_allowed_clients(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (wp->listen_address_domain == FPM_AF_INET) {
+ fcgi_set_allowed_clients(wp->config->listen_allowed_clients);
+ }
+ return 0;
+}
+/* }}} */
+
+#if 0 /* Comment out this non used function. It could be used later. */
+static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ char max_workers[10 + 1]; /* 4294967295 */
+ int len;
+
+ len = sprintf(max_workers, "%u", (unsigned int) wp->config->pm_max_children);
+
+ fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, max_workers, len);
+ fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, max_workers, len);
+ return 0;
+}
+/* }}} */
+#endif
+
+char *fpm_php_script_filename(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).path_translated;
+}
+/* }}} */
+
+char *fpm_php_request_uri(TSRMLS_D) /* {{{ */
+{
+ return (char *) SG(request_info).request_uri;
+}
+/* }}} */
+
+char *fpm_php_request_method(TSRMLS_D) /* {{{ */
+{
+ return (char *) SG(request_info).request_method;
+}
+/* }}} */
+
+char *fpm_php_query_string(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).query_string;
+}
+/* }}} */
+
+char *fpm_php_auth_user(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).auth_user;
+}
+/* }}} */
+
+size_t fpm_php_content_length(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).content_length;
+}
+/* }}} */
+
+static void fpm_php_cleanup(int which, void *arg) /* {{{ */
+{
+ TSRMLS_FETCH();
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+}
+/* }}} */
+
+void fpm_php_soft_quit() /* {{{ */
+{
+ fcgi_set_in_shutdown(1);
+}
+/* }}} */
+
+int fpm_php_init_main() /* {{{ */
+{
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (0 > fpm_php_apply_defines(wp) ||
+ 0 > fpm_php_set_allowed_clients(wp)) {
+ return -1;
+ }
+
+ if (wp->limit_extensions) {
+ limit_extensions = wp->limit_extensions;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_php_limit_extensions(char *path) /* {{{ */
+{
+ char **p;
+ size_t path_len;
+
+ if (!path || !limit_extensions) {
+ return 0; /* allowed by default */
+ }
+
+ p = limit_extensions;
+ path_len = strlen(path);
+ while (p && *p) {
+ size_t ext_len = strlen(*p);
+ if (path_len > ext_len) {
+ char *path_ext = path + path_len - ext_len;
+ if (strcmp(*p, path_ext) == 0) {
+ return 0; /* allow as the extension has been found */
+ }
+ }
+ p++;
+ }
+
+
+ zlog(ZLOG_NOTICE, "Access to the script '%s' has been denied (see security.limit_extensions)", path);
+ return 1; /* extension not found: not allowed */
+}
+/* }}} */
+
+char* fpm_php_get_string_from_table(char *table, char *key TSRMLS_DC) /* {{{ */
+{
+ zval **data, **tmp;
+ char *string_key;
+ uint string_len;
+ ulong num_key;
+ if (!table || !key) {
+ return NULL;
+ }
+
+ /* inspired from ext/standard/info.c */
+
+ zend_is_auto_global(table, strlen(table) TSRMLS_CC);
+
+ /* find the table and ensure it's an array */
+ if (zend_hash_find(&EG(symbol_table), table, strlen(table) + 1, (void **) &data) == SUCCESS && Z_TYPE_PP(data) == IS_ARRAY) {
+
+ /* reset the internal pointer */
+ zend_hash_internal_pointer_reset(Z_ARRVAL_PP(data));
+
+ /* parse the array to look for our key */
+ while (zend_hash_get_current_data(Z_ARRVAL_PP(data), (void **) &tmp) == SUCCESS) {
+ /* ensure the key is a string */
+ if (zend_hash_get_current_key_ex(Z_ARRVAL_PP(data), &string_key, &string_len, &num_key, 0, NULL) == HASH_KEY_IS_STRING) {
+ /* compare to our key */
+ if (!strncmp(string_key, key, string_len)) {
+ return Z_STRVAL_PP(tmp);
+ }
+ }
+ zend_hash_move_forward(Z_ARRVAL_PP(data));
+ }
+ }
+
+ return NULL;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_php.h b/sapi/fpm/fpm/fpm_php.h
new file mode 100644
index 0000000..d605473
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php.h
@@ -0,0 +1,50 @@
+
+ /* $Id: fpm_php.h,v 1.10.2.1 2008/11/15 00:57:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_PHP_H
+#define FPM_PHP_H 1
+
+#include <TSRM.h>
+
+#include "php.h"
+#include "build-defs.h" /* for PHP_ defines */
+#include "fpm/fpm_conf.h"
+
+#define FPM_PHP_INI_TO_EXPAND \
+ { \
+ "error_log", \
+ "extension_dir", \
+ "mime_magic.magicfile", \
+ "sendmail_path", \
+ "session.cookie_path", \
+ "session_pgsql.sem_file_name", \
+ "soap.wsdl_cache_dir", \
+ "uploadprogress.file.filename_template", \
+ "xdebug.output_dir", \
+ "xdebug.profiler_output_dir", \
+ "xdebug.trace_output_dir", \
+ "xmms.path", \
+ "axis2.client_home", \
+ "blenc.key_file", \
+ "coin_acceptor.device", \
+ NULL \
+ }
+
+struct fpm_worker_pool_s;
+
+int fpm_php_init_child(struct fpm_worker_pool_s *wp);
+char *fpm_php_script_filename(TSRMLS_D);
+char *fpm_php_request_uri(TSRMLS_D);
+char *fpm_php_request_method(TSRMLS_D);
+char *fpm_php_query_string(TSRMLS_D);
+char *fpm_php_auth_user(TSRMLS_D);
+size_t fpm_php_content_length(TSRMLS_D);
+void fpm_php_soft_quit();
+int fpm_php_init_main();
+int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode);
+int fpm_php_limit_extensions(char *path);
+char* fpm_php_get_string_from_table(char *table, char *key TSRMLS_DC);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_php_trace.c b/sapi/fpm/fpm/fpm_php_trace.c
new file mode 100644
index 0000000..d95d66a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php_trace.c
@@ -0,0 +1,177 @@
+
+ /* $Id: fpm_php_trace.c,v 1.27.2.1 2008/11/15 00:57:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#if HAVE_FPM_TRACE
+
+#include "php.h"
+#include "php_main.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "fpm_trace.h"
+#include "fpm_php_trace.h"
+#include "fpm_children.h"
+#include "fpm_worker_pool.h"
+#include "fpm_process_ctl.h"
+#include "fpm_scoreboard.h"
+
+#include "zlog.h"
+
+
+#define valid_ptr(p) ((p) && 0 == ((p) & (sizeof(long) - 1)))
+
+#if SIZEOF_LONG == 4
+#define PTR_FMT "08"
+#elif SIZEOF_LONG == 8
+#define PTR_FMT "016"
+#endif
+
+
+static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog TSRMLS_DC) /* {{{ */
+{
+ int callers_limit = 20;
+ pid_t pid = child->pid;
+ struct timeval tv;
+ static const int buf_size = 1024;
+ char buf[buf_size];
+ long execute_data;
+ long l;
+
+ gettimeofday(&tv, 0);
+
+ zlog_print_time(&tv, buf, buf_size);
+
+ fprintf(slowlog, "\n%s [pool %s] pid %d\n", buf, child->wp->config->name, (int) pid);
+
+ if (0 > fpm_trace_get_strz(buf, buf_size, (long) &SG(request_info).path_translated)) {
+ return -1;
+ }
+
+ fprintf(slowlog, "script_filename = %s\n", buf);
+
+ if (0 > fpm_trace_get_long((long) &EG(current_execute_data), &l)) {
+ return -1;
+ }
+
+ execute_data = l;
+
+ while (execute_data) {
+ long function;
+ uint lineno = 0;
+
+ fprintf(slowlog, "[0x%" PTR_FMT "lx] ", execute_data);
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, function_state.function), &l)) {
+ return -1;
+ }
+
+ function = l;
+
+ if (valid_ptr(function)) {
+ if (0 > fpm_trace_get_strz(buf, buf_size, function + offsetof(zend_function, common.function_name))) {
+ return -1;
+ }
+
+ fprintf(slowlog, "%s()", buf);
+ } else {
+ fprintf(slowlog, "???");
+ }
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, op_array), &l)) {
+ return -1;
+ }
+
+ *buf = '\0';
+
+ if (valid_ptr(l)) {
+ long op_array = l;
+
+ if (0 > fpm_trace_get_strz(buf, buf_size, op_array + offsetof(zend_op_array, filename))) {
+ return -1;
+ }
+ }
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, opline), &l)) {
+ return -1;
+ }
+
+ if (valid_ptr(l)) {
+ long opline = l;
+ uint *lu = (uint *) &l;
+
+ if (0 > fpm_trace_get_long(opline + offsetof(struct _zend_op, lineno), &l)) {
+ return -1;
+ }
+
+ lineno = *lu;
+ }
+
+ fprintf(slowlog, " %s:%u\n", *buf ? buf : "unknown", lineno);
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, prev_execute_data), &l)) {
+ return -1;
+ }
+
+ execute_data = l;
+
+ if (0 == --callers_limit) {
+ break;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+void fpm_php_trace(struct fpm_child_s *child) /* {{{ */
+{
+ TSRMLS_FETCH();
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_SET, child->wp->scoreboard);
+ FILE *slowlog;
+
+ zlog(ZLOG_NOTICE, "about to trace %d", (int) child->pid);
+
+ slowlog = fopen(child->wp->config->slowlog, "a+");
+
+ if (!slowlog) {
+ zlog(ZLOG_SYSERROR, "unable to open slowlog (%s)", child->wp->config->slowlog);
+ goto done0;
+ }
+
+ if (0 > fpm_trace_ready(child->pid)) {
+ goto done1;
+ }
+
+ if (0 > fpm_php_trace_dump(child, slowlog TSRMLS_CC)) {
+ fprintf(slowlog, "+++ dump failed\n");
+ }
+
+ if (0 > fpm_trace_close(child->pid)) {
+ goto done1;
+ }
+
+done1:
+ fclose(slowlog);
+
+done0:
+ fpm_pctl_kill(child->pid, FPM_PCTL_CONT);
+ child->tracer = 0;
+
+ zlog(ZLOG_NOTICE, "finished trace of %d", (int) child->pid);
+}
+/* }}} */
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_php_trace.h b/sapi/fpm/fpm/fpm_php_trace.h
new file mode 100644
index 0000000..af5e456
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php_trace.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_php_trace.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_PHP_TRACE_H
+#define FPM_PHP_TRACE_H 1
+
+struct fpm_child_s;
+
+void fpm_php_trace(struct fpm_child_s *);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_process_ctl.c b/sapi/fpm/fpm/fpm_process_ctl.c
new file mode 100644
index 0000000..76ea4d3
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_process_ctl.c
@@ -0,0 +1,539 @@
+
+ /* $Id: fpm_process_ctl.c,v 1.19.2.2 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "fpm.h"
+#include "fpm_clock.h"
+#include "fpm_children.h"
+#include "fpm_signals.h"
+#include "fpm_events.h"
+#include "fpm_process_ctl.h"
+#include "fpm_cleanup.h"
+#include "fpm_request.h"
+#include "fpm_worker_pool.h"
+#include "fpm_scoreboard.h"
+#include "fpm_sockets.h"
+#include "zlog.h"
+
+
+static int fpm_state = FPM_PCTL_STATE_NORMAL;
+static int fpm_signal_sent = 0;
+
+
+static const char *fpm_state_names[] = {
+ [FPM_PCTL_STATE_NORMAL] = "normal",
+ [FPM_PCTL_STATE_RELOADING] = "reloading",
+ [FPM_PCTL_STATE_TERMINATING] = "terminating",
+ [FPM_PCTL_STATE_FINISHING] = "finishing"
+};
+
+static int saved_argc;
+static char **saved_argv;
+
+static void fpm_pctl_cleanup(int which, void *arg) /* {{{ */
+{
+ int i;
+ if (which != FPM_CLEANUP_PARENT_EXEC) {
+ for (i = 0; i < saved_argc; i++) {
+ free(saved_argv[i]);
+ }
+ free(saved_argv);
+ }
+}
+/* }}} */
+
+static struct fpm_event_s pctl_event;
+
+static void fpm_pctl_action(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
+}
+/* }}} */
+
+static int fpm_pctl_timeout_set(int sec) /* {{{ */
+{
+ fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
+ fpm_event_add(&pctl_event, sec * 1000);
+ return 0;
+}
+/* }}} */
+
+static void fpm_pctl_exit() /* {{{ */
+{
+ zlog(ZLOG_NOTICE, "exiting, bye-bye!");
+
+ fpm_conf_unlink_pid();
+ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN);
+ exit(FPM_EXIT_OK);
+}
+/* }}} */
+
+#define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
+
+static void fpm_pctl_exec() /* {{{ */
+{
+
+ zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
+ "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
+ "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
+ "})",
+ saved_argv[0], saved_argv[0],
+ optional_arg(1),
+ optional_arg(2),
+ optional_arg(3),
+ optional_arg(4),
+ optional_arg(5),
+ optional_arg(6),
+ optional_arg(7),
+ optional_arg(8),
+ optional_arg(9),
+ optional_arg(10)
+ );
+
+ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC);
+ execvp(saved_argv[0], saved_argv);
+ zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed");
+ exit(FPM_EXIT_SOFTWARE);
+}
+/* }}} */
+
+static void fpm_pctl_action_last() /* {{{ */
+{
+ switch (fpm_state) {
+ case FPM_PCTL_STATE_RELOADING:
+ fpm_pctl_exec();
+ break;
+
+ case FPM_PCTL_STATE_FINISHING:
+ case FPM_PCTL_STATE_TERMINATING:
+ fpm_pctl_exit();
+ break;
+ }
+}
+/* }}} */
+
+int fpm_pctl_kill(pid_t pid, int how) /* {{{ */
+{
+ int s = 0;
+
+ switch (how) {
+ case FPM_PCTL_TERM :
+ s = SIGTERM;
+ break;
+ case FPM_PCTL_STOP :
+ s = SIGSTOP;
+ break;
+ case FPM_PCTL_CONT :
+ s = SIGCONT;
+ break;
+ case FPM_PCTL_QUIT :
+ s = SIGQUIT;
+ break;
+ default :
+ break;
+ }
+ return kill(pid, s);
+}
+/* }}} */
+
+void fpm_pctl_kill_all(int signo) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int alive_children = 0;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ struct fpm_child_s *child;
+
+ for (child = wp->children; child; child = child->next) {
+ int res = kill(child->pid, signo);
+
+ zlog(ZLOG_DEBUG, "[pool %s] sending signal %d %s to child %d",
+ child->wp->config->name, signo,
+ fpm_signal_names[signo] ? fpm_signal_names[signo] : "", (int) child->pid);
+
+ if (res == 0) {
+ ++alive_children;
+ }
+ }
+ }
+
+ if (alive_children) {
+ zlog(ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
+ }
+}
+/* }}} */
+
+static void fpm_pctl_action_next() /* {{{ */
+{
+ int sig, timeout;
+
+ if (!fpm_globals.running_children) {
+ fpm_pctl_action_last();
+ }
+
+ if (fpm_signal_sent == 0) {
+ if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
+ sig = SIGTERM;
+ } else {
+ sig = SIGQUIT;
+ }
+ timeout = fpm_global_config.process_control_timeout;
+ } else {
+ if (fpm_signal_sent == SIGQUIT) {
+ sig = SIGTERM;
+ } else {
+ sig = SIGKILL;
+ }
+ timeout = 1;
+ }
+
+ fpm_pctl_kill_all(sig);
+ fpm_signal_sent = sig;
+ fpm_pctl_timeout_set(timeout);
+}
+/* }}} */
+
+void fpm_pctl(int new_state, int action) /* {{{ */
+{
+ switch (action) {
+ case FPM_PCTL_ACTION_SET :
+ if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
+ return;
+ }
+
+ switch (fpm_state) { /* check which states can be overridden */
+ case FPM_PCTL_STATE_NORMAL :
+ /* 'normal' can be overridden by any other state */
+ break;
+ case FPM_PCTL_STATE_RELOADING :
+ /* 'reloading' can be overridden by 'finishing' */
+ if (new_state == FPM_PCTL_STATE_FINISHING) break;
+ case FPM_PCTL_STATE_FINISHING :
+ /* 'reloading' and 'finishing' can be overridden by 'terminating' */
+ if (new_state == FPM_PCTL_STATE_TERMINATING) break;
+ case FPM_PCTL_STATE_TERMINATING :
+ /* nothing can override 'terminating' state */
+ zlog(ZLOG_DEBUG, "not switching to '%s' state, because already in '%s' state",
+ fpm_state_names[new_state], fpm_state_names[fpm_state]);
+ return;
+ }
+
+ fpm_signal_sent = 0;
+ fpm_state = new_state;
+
+ zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
+ /* fall down */
+
+ case FPM_PCTL_ACTION_TIMEOUT :
+ fpm_pctl_action_next();
+ break;
+ case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
+ fpm_pctl_action_last();
+ break;
+
+ }
+}
+/* }}} */
+
+int fpm_pctl_can_spawn_children() /* {{{ */
+{
+ return fpm_state == FPM_PCTL_STATE_NORMAL;
+}
+/* }}} */
+
+int fpm_pctl_child_exited() /* {{{ */
+{
+ if (fpm_state == FPM_PCTL_STATE_NORMAL) {
+ return 0;
+ }
+
+ if (!fpm_globals.running_children) {
+ fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_pctl_init_main() /* {{{ */
+{
+ int i;
+
+ saved_argc = fpm_globals.argc;
+ saved_argv = malloc(sizeof(char *) * (saved_argc + 1));
+
+ if (!saved_argv) {
+ return -1;
+ }
+
+ for (i = 0; i < saved_argc; i++) {
+ saved_argv[i] = strdup(fpm_globals.argv[i]);
+
+ if (!saved_argv[i]) {
+ return -1;
+ }
+ }
+
+ saved_argv[i] = 0;
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ int terminate_timeout = wp->config->request_terminate_timeout;
+ int slowlog_timeout = wp->config->request_slowlog_timeout;
+ struct fpm_child_s *child;
+
+ if (terminate_timeout || slowlog_timeout) {
+ for (child = wp->children; child; child = child->next) {
+ fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
+ }
+ }
+ }
+}
+/* }}} */
+
+static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ struct fpm_child_s *child;
+ struct fpm_child_s *last_idle_child = NULL;
+ int idle = 0;
+ int active = 0;
+ int children_to_fork;
+ unsigned cur_lq = 0;
+
+ if (wp->config == NULL) continue;
+
+ for (child = wp->children; child; child = child->next) {
+ if (fpm_request_is_idle(child)) {
+ if (last_idle_child == NULL) {
+ last_idle_child = child;
+ } else {
+ if (timercmp(&child->started, &last_idle_child->started, <)) {
+ last_idle_child = child;
+ }
+ }
+ idle++;
+ } else {
+ active++;
+ }
+ }
+
+ /* update status structure for all PMs */
+ if (wp->listen_address_domain == FPM_AF_INET) {
+ if (0 > fpm_socket_get_listening_queue(wp->listening_socket, &cur_lq, NULL)) {
+ cur_lq = 0;
+#if 0
+ } else {
+ if (cur_lq > 0) {
+ if (!wp->warn_lq) {
+ zlog(ZLOG_WARNING, "[pool %s] listening queue is not empty, #%d requests are waiting to be served, consider raising pm.max_children setting (%d)", wp->config->name, cur_lq, wp->config->pm_max_children);
+ wp->warn_lq = 1;
+ }
+ } else {
+ wp->warn_lq = 0;
+ }
+#endif
+ }
+ }
+ fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
+
+ /* this is specific to PM_STYLE_ONDEMAND */
+ if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ struct timeval last, now;
+
+ zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle);
+
+ if (!last_idle_child) continue;
+
+ fpm_request_last_activity(last_idle_child, &last);
+ fpm_clock_get(&now);
+ if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
+ last_idle_child->idle_kill = 1;
+ fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
+ }
+
+ continue;
+ }
+
+ /* the rest is only used by PM_STYLE_DYNAMIC */
+ if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
+
+ zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children, %d running children. Spawning rate %d", wp->config->name, active, idle, wp->running_children, wp->idle_spawn_rate);
+
+ if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
+ last_idle_child->idle_kill = 1;
+ fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
+ wp->idle_spawn_rate = 1;
+ continue;
+ }
+
+ if (idle < wp->config->pm_min_spare_servers) {
+ if (wp->running_children >= wp->config->pm_max_children) {
+ if (!wp->warn_max_children) {
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
+ zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
+ wp->warn_max_children = 1;
+ }
+ wp->idle_spawn_rate = 1;
+ continue;
+ }
+
+ if (wp->idle_spawn_rate >= 8) {
+ zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
+ }
+
+ /* compute the number of idle process to spawn */
+ children_to_fork = MIN(wp->idle_spawn_rate, wp->config->pm_min_spare_servers - idle);
+
+ /* get sure it won't exceed max_children */
+ children_to_fork = MIN(children_to_fork, wp->config->pm_max_children - wp->running_children);
+ if (children_to_fork <= 0) {
+ if (!wp->warn_max_children) {
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
+ zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
+ wp->warn_max_children = 1;
+ }
+ wp->idle_spawn_rate = 1;
+ continue;
+ }
+ wp->warn_max_children = 0;
+
+ fpm_children_make(wp, 1, children_to_fork, 1);
+
+ /* if it's a child, stop here without creating the next event
+ * this event is reserved to the master process
+ */
+ if (fpm_globals.is_child) {
+ return;
+ }
+
+ zlog(ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, children_to_fork);
+
+ /* Double the spawn rate for the next iteration */
+ if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
+ wp->idle_spawn_rate *= 2;
+ }
+ continue;
+ }
+ wp->idle_spawn_rate = 1;
+ }
+}
+/* }}} */
+
+void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ static struct fpm_event_s heartbeat;
+ struct timeval now;
+
+ if (fpm_globals.parent_pid != getpid()) {
+ return; /* sanity check */
+ }
+
+ if (which == FPM_EV_TIMEOUT) {
+ fpm_clock_get(&now);
+ fpm_pctl_check_request_timeout(&now);
+ return;
+ }
+
+ /* ensure heartbeat is not lower than FPM_PCTL_MIN_HEARTBEAT */
+ fpm_globals.heartbeat = MAX(fpm_globals.heartbeat, FPM_PCTL_MIN_HEARTBEAT);
+
+ /* first call without setting to initialize the timer */
+ zlog(ZLOG_DEBUG, "heartbeat have been set up with a timeout of %dms", fpm_globals.heartbeat);
+ fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_heartbeat, NULL);
+ fpm_event_add(&heartbeat, fpm_globals.heartbeat);
+}
+/* }}} */
+
+void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ static struct fpm_event_s heartbeat;
+ struct timeval now;
+
+ if (fpm_globals.parent_pid != getpid()) {
+ return; /* sanity check */
+ }
+
+ if (which == FPM_EV_TIMEOUT) {
+ fpm_clock_get(&now);
+ if (fpm_pctl_can_spawn_children()) {
+ fpm_pctl_perform_idle_server_maintenance(&now);
+
+ /* if it's a child, stop here without creating the next event
+ * this event is reserved to the master process
+ */
+ if (fpm_globals.is_child) {
+ return;
+ }
+ }
+ return;
+ }
+
+ /* first call without setting which to initialize the timer */
+ fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_perform_idle_server_maintenance_heartbeat, NULL);
+ fpm_event_add(&heartbeat, FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT);
+}
+/* }}} */
+
+void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
+ struct fpm_child_s *child;
+
+
+ if (fpm_globals.parent_pid != getpid()) {
+ /* prevent a event race condition when child process
+ * have not set up its own event loop */
+ return;
+ }
+
+ wp->socket_event_set = 0;
+
+/* zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/
+
+ if (wp->running_children >= wp->config->pm_max_children) {
+ if (!wp->warn_max_children) {
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
+ zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
+ wp->warn_max_children = 1;
+ }
+
+ return;
+ }
+
+ for (child = wp->children; child; child = child->next) {
+ /* if there is at least on idle child, it will handle the connection, stop here */
+ if (fpm_request_is_idle(child)) {
+ return;
+ }
+ }
+
+ wp->warn_max_children = 0;
+ fpm_children_make(wp, 1, 1, 1);
+
+ if (fpm_globals.is_child) {
+ return;
+ }
+
+ zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_process_ctl.h b/sapi/fpm/fpm/fpm_process_ctl.h
new file mode 100644
index 0000000..86a6ef0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_process_ctl.h
@@ -0,0 +1,53 @@
+
+ /* $Id: fpm_process_ctl.h,v 1.6 2008/07/20 21:33:10 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_PROCESS_CTL_H
+#define FPM_PROCESS_CTL_H 1
+
+#include "fpm_events.h"
+
+/* spawn max 32 children at once */
+#define FPM_MAX_SPAWN_RATE (32)
+/* 1s (in ms) heartbeat for idle server maintenance */
+#define FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT (1000)
+/* a minimum of 130ms heartbeat for pctl */
+#define FPM_PCTL_MIN_HEARTBEAT (130)
+
+
+struct fpm_child_s;
+
+void fpm_pctl(int new_state, int action);
+int fpm_pctl_can_spawn_children();
+int fpm_pctl_kill(pid_t pid, int how);
+void fpm_pctl_kill_all(int signo);
+void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg);
+void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg);
+void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg);
+int fpm_pctl_child_exited();
+int fpm_pctl_init_main();
+
+
+enum {
+ FPM_PCTL_STATE_UNSPECIFIED,
+ FPM_PCTL_STATE_NORMAL,
+ FPM_PCTL_STATE_RELOADING,
+ FPM_PCTL_STATE_TERMINATING,
+ FPM_PCTL_STATE_FINISHING
+};
+
+enum {
+ FPM_PCTL_ACTION_SET,
+ FPM_PCTL_ACTION_TIMEOUT,
+ FPM_PCTL_ACTION_LAST_CHILD_EXITED
+};
+
+enum {
+ FPM_PCTL_TERM,
+ FPM_PCTL_STOP,
+ FPM_PCTL_CONT,
+ FPM_PCTL_QUIT
+};
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c
new file mode 100644
index 0000000..bf431a0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_request.c
@@ -0,0 +1,316 @@
+
+ /* $Id: fpm_request.c,v 1.9.2.1 2008/11/15 00:57:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+#ifdef HAVE_TIMES
+#include <sys/times.h>
+#endif
+
+#include "fpm_config.h"
+
+#include "fpm.h"
+#include "fpm_php.h"
+#include "fpm_str.h"
+#include "fpm_clock.h"
+#include "fpm_conf.h"
+#include "fpm_trace.h"
+#include "fpm_php_trace.h"
+#include "fpm_process_ctl.h"
+#include "fpm_children.h"
+#include "fpm_scoreboard.h"
+#include "fpm_status.h"
+#include "fpm_request.h"
+#include "fpm_log.h"
+
+#include "zlog.h"
+
+static const char *requests_stages[] = {
+ [FPM_REQUEST_ACCEPTING] = "Idle",
+ [FPM_REQUEST_READING_HEADERS] = "Reading headers",
+ [FPM_REQUEST_INFO] = "Getting request informations",
+ [FPM_REQUEST_EXECUTING] = "Running",
+ [FPM_REQUEST_END] = "Ending",
+ [FPM_REQUEST_FINISHED] = "Finishing",
+};
+
+const char *fpm_request_get_stage_name(int stage) {
+ return requests_stages[stage];
+}
+
+void fpm_request_accepting() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_ACCEPTING;
+ proc->tv = now;
+ fpm_scoreboard_proc_release(proc);
+
+ /* idle++, active-- */
+ fpm_scoreboard_update(1, -1, 0, 0, 0, 0, 0, FPM_SCOREBOARD_ACTION_INC, NULL);
+}
+/* }}} */
+
+void fpm_request_reading_headers() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ struct timeval now;
+ clock_t now_epoch;
+#ifdef HAVE_TIMES
+ struct tms cpu;
+#endif
+
+ fpm_clock_get(&now);
+ now_epoch = time(NULL);
+#ifdef HAVE_TIMES
+ times(&cpu);
+#endif
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_READING_HEADERS;
+ proc->tv = now;
+ proc->accepted = now;
+ proc->accepted_epoch = now_epoch;
+#ifdef HAVE_TIMES
+ proc->cpu_accepted = cpu;
+#endif
+ proc->requests++;
+ proc->request_uri[0] = '\0';
+ proc->request_method[0] = '\0';
+ proc->script_filename[0] = '\0';
+ proc->query_string[0] = '\0';
+ proc->query_string[0] = '\0';
+ proc->auth_user[0] = '\0';
+ proc->content_length = 0;
+ fpm_scoreboard_proc_release(proc);
+
+ /* idle--, active++, request++ */
+ fpm_scoreboard_update(-1, 1, 0, 0, 1, 0, 0, FPM_SCOREBOARD_ACTION_INC, NULL);
+}
+/* }}} */
+
+void fpm_request_info() /* {{{ */
+{
+ TSRMLS_FETCH();
+ struct fpm_scoreboard_proc_s *proc;
+ char *request_uri = fpm_php_request_uri(TSRMLS_C);
+ char *request_method = fpm_php_request_method(TSRMLS_C);
+ char *script_filename = fpm_php_script_filename(TSRMLS_C);
+ char *query_string = fpm_php_query_string(TSRMLS_C);
+ char *auth_user = fpm_php_auth_user(TSRMLS_C);
+ size_t content_length = fpm_php_content_length(TSRMLS_C);
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_INFO;
+ proc->tv = now;
+
+ if (request_uri) {
+ strlcpy(proc->request_uri, request_uri, sizeof(proc->request_uri));
+ }
+
+ if (request_method) {
+ strlcpy(proc->request_method, request_method, sizeof(proc->request_method));
+ }
+
+ if (query_string) {
+ strlcpy(proc->query_string, query_string, sizeof(proc->query_string));
+ }
+
+ if (auth_user) {
+ strlcpy(proc->auth_user, auth_user, sizeof(proc->auth_user));
+ }
+
+ proc->content_length = content_length;
+
+ /* if cgi.fix_pathinfo is set to "1" and script cannot be found (404)
+ the sapi_globals.request_info.path_translated is set to NULL */
+ if (script_filename) {
+ strlcpy(proc->script_filename, script_filename, sizeof(proc->script_filename));
+ }
+
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_executing() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_EXECUTING;
+ proc->tv = now;
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_end(TSRMLS_D) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+#ifdef HAVE_TIMES
+ struct tms cpu;
+#endif
+ size_t memory = zend_memory_peak_usage(1 TSRMLS_CC);
+
+ fpm_clock_get(&now);
+#ifdef HAVE_TIMES
+ times(&cpu);
+#endif
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+ proc->request_stage = FPM_REQUEST_FINISHED;
+ proc->tv = now;
+ timersub(&now, &proc->accepted, &proc->duration);
+#ifdef HAVE_TIMES
+ timersub(&proc->tv, &proc->accepted, &proc->cpu_duration);
+ proc->last_request_cpu.tms_utime = cpu.tms_utime - proc->cpu_accepted.tms_utime;
+ proc->last_request_cpu.tms_stime = cpu.tms_stime - proc->cpu_accepted.tms_stime;
+ proc->last_request_cpu.tms_cutime = cpu.tms_cutime - proc->cpu_accepted.tms_cutime;
+ proc->last_request_cpu.tms_cstime = cpu.tms_cstime - proc->cpu_accepted.tms_cstime;
+#endif
+ proc->memory = memory;
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_finished() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_FINISHED;
+ proc->tv = now;
+ memset(&proc->accepted, 0, sizeof(proc->accepted));
+ proc->accepted_epoch = 0;
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *now, int terminate_timeout, int slowlog_timeout) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s proc, *proc_p;
+
+ proc_p = fpm_scoreboard_proc_acquire(child->wp->scoreboard, child->scoreboard_i, 1);
+ if (!proc_p) {
+ zlog(ZLOG_WARNING, "failed to acquire scoreboard");
+ return;
+ }
+
+ proc = *proc_p;
+ fpm_scoreboard_proc_release(proc_p);
+
+#if HAVE_FPM_TRACE
+ if (child->slow_logged.tv_sec) {
+ if (child->slow_logged.tv_sec != proc.accepted.tv_sec || child->slow_logged.tv_usec != proc.accepted.tv_usec) {
+ child->slow_logged.tv_sec = 0;
+ child->slow_logged.tv_usec = 0;
+ }
+ }
+#endif
+
+ if (proc.request_stage > FPM_REQUEST_ACCEPTING && proc.request_stage < FPM_REQUEST_END) {
+ char purified_script_filename[sizeof(proc.script_filename)];
+ struct timeval tv;
+
+ timersub(now, &proc.accepted, &tv);
+
+#if HAVE_FPM_TRACE
+ if (child->slow_logged.tv_sec == 0 && slowlog_timeout &&
+ proc.request_stage == FPM_REQUEST_EXECUTING && tv.tv_sec >= slowlog_timeout) {
+
+ str_purify_filename(purified_script_filename, proc.script_filename, sizeof(proc.script_filename));
+
+ child->slow_logged = proc.accepted;
+ child->tracer = fpm_php_trace;
+
+ fpm_trace_signal(child->pid);
+
+ zlog(ZLOG_WARNING, "[pool %s] child %d, script '%s' (request: \"%s %s\") executing too slow (%d.%06d sec), logging",
+ child->wp->config->name, (int) child->pid, purified_script_filename, proc.request_method, proc.request_uri,
+ (int) tv.tv_sec, (int) tv.tv_usec);
+ }
+ else
+#endif
+ if (terminate_timeout && tv.tv_sec >= terminate_timeout) {
+ str_purify_filename(purified_script_filename, proc.script_filename, sizeof(proc.script_filename));
+ fpm_pctl_kill(child->pid, FPM_PCTL_TERM);
+
+ zlog(ZLOG_WARNING, "[pool %s] child %d, script '%s' (request: \"%s %s\") execution timed out (%d.%06d sec), terminating",
+ child->wp->config->name, (int) child->pid, purified_script_filename, proc.request_method, proc.request_uri,
+ (int) tv.tv_sec, (int) tv.tv_usec);
+ }
+ }
+}
+/* }}} */
+
+int fpm_request_is_idle(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ /* no need in atomicity here */
+ proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
+ if (!proc) {
+ return 0;
+ }
+
+ return proc->request_stage == FPM_REQUEST_ACCEPTING;
+}
+/* }}} */
+
+int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ if (!tv) return -1;
+
+ proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
+ if (!proc) {
+ return -1;
+ }
+
+ *tv = proc->tv;
+
+ return 1;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h
new file mode 100644
index 0000000..aebd36c
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_request.h
@@ -0,0 +1,32 @@
+
+ /* $Id: fpm_request.h,v 1.4 2008/07/20 01:47:16 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_REQUEST_H
+#define FPM_REQUEST_H 1
+
+void fpm_request_accepting(); /* hanging in accept() */
+void fpm_request_reading_headers(); /* start reading fastcgi request from very first byte */
+void fpm_request_info(); /* not a stage really but a point in the php code, where all request params have become known to sapi */
+void fpm_request_executing(); /* the script is executing */
+void fpm_request_end(TSRMLS_D); /* request ended: script response have been sent to web server */
+void fpm_request_finished(); /* request processed: cleaning current request */
+
+struct fpm_child_s;
+struct timeval;
+
+void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *tv, int terminate_timeout, int slowlog_timeout);
+int fpm_request_is_idle(struct fpm_child_s *child);
+const char *fpm_request_get_stage_name(int stage);
+int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv);
+
+enum fpm_request_stage_e {
+ FPM_REQUEST_ACCEPTING = 1,
+ FPM_REQUEST_READING_HEADERS,
+ FPM_REQUEST_INFO,
+ FPM_REQUEST_EXECUTING,
+ FPM_REQUEST_END,
+ FPM_REQUEST_FINISHED
+};
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c
new file mode 100644
index 0000000..24463a9
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_scoreboard.c
@@ -0,0 +1,331 @@
+
+ /* $Id: fpm_status.c 312399 2011-06-23 08:03:52Z fat $ */
+ /* (c) 2009 Jerome Loyet */
+
+#include "php.h"
+#include "SAPI.h"
+#include <stdio.h>
+#include <time.h>
+
+#include "fpm_config.h"
+#include "fpm_scoreboard.h"
+#include "fpm_shm.h"
+#include "fpm_sockets.h"
+#include "fpm_worker_pool.h"
+#include "fpm_clock.h"
+#include "zlog.h"
+
+static struct fpm_scoreboard_s *fpm_scoreboard = NULL;
+static int fpm_scoreboard_i = -1;
+#ifdef HAVE_TIMES
+static float fpm_scoreboard_tick;
+#endif
+
+
+int fpm_scoreboard_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int i;
+
+#ifdef HAVE_TIMES
+#if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK))
+ fpm_scoreboard_tick = sysconf(_SC_CLK_TCK);
+#else /* _SC_CLK_TCK */
+#ifdef HZ
+ fpm_scoreboard_tick = HZ;
+#else /* HZ */
+ fpm_scoreboard_tick = 100;
+#endif /* HZ */
+#endif /* _SC_CLK_TCK */
+ zlog(ZLOG_DEBUG, "got clock tick '%.0f'", fpm_scoreboard_tick);
+#endif /* HAVE_TIMES */
+
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (wp->config->pm_max_children < 1) {
+ zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because max_client is not set", wp->config->name);
+ return -1;
+ }
+
+ if (wp->scoreboard) {
+ zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because it already exists", wp->config->name);
+ return -1;
+ }
+
+ wp->scoreboard = fpm_shm_alloc(sizeof(struct fpm_scoreboard_s) + (wp->config->pm_max_children - 1) * sizeof(struct fpm_scoreboard_proc_s *));
+ if (!wp->scoreboard) {
+ return -1;
+ }
+ wp->scoreboard->nprocs = wp->config->pm_max_children;
+ for (i = 0; i < wp->scoreboard->nprocs; i++) {
+ wp->scoreboard->procs[i] = fpm_shm_alloc(sizeof(struct fpm_scoreboard_proc_s));
+ if (!wp->scoreboard->procs[i]) {
+ return -1;
+ }
+ memset(wp->scoreboard->procs[i], 0, sizeof(struct fpm_scoreboard_proc_s));
+ }
+
+ wp->scoreboard->pm = wp->config->pm;
+ wp->scoreboard->start_epoch = time(NULL);
+ strlcpy(wp->scoreboard->pool, wp->config->name, sizeof(wp->scoreboard->pool));
+ }
+ return 0;
+}
+/* }}} */
+
+void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, int action, struct fpm_scoreboard_s *scoreboard) /* {{{ */
+{
+ if (!scoreboard) {
+ scoreboard = fpm_scoreboard;
+ }
+ if (!scoreboard) {
+ zlog(ZLOG_WARNING, "Unable to update scoreboard: the SHM has not been found");
+ return;
+ }
+
+
+ fpm_spinlock(&scoreboard->lock, 0);
+ if (action == FPM_SCOREBOARD_ACTION_SET) {
+ if (idle >= 0) {
+ scoreboard->idle = idle;
+ }
+ if (active >= 0) {
+ scoreboard->active = active;
+ }
+ if (lq >= 0) {
+ scoreboard->lq = lq;
+ }
+ if (lq_len >= 0) {
+ scoreboard->lq_len = lq_len;
+ }
+#ifdef HAVE_FPM_LQ /* prevent unnecessary test */
+ if (scoreboard->lq > scoreboard->lq_max) {
+ scoreboard->lq_max = scoreboard->lq;
+ }
+#endif
+ if (requests >= 0) {
+ scoreboard->requests = requests;
+ }
+
+ if (max_children_reached >= 0) {
+ scoreboard->max_children_reached = max_children_reached;
+ }
+ if (slow_rq > 0) {
+ scoreboard->slow_rq += slow_rq;
+ }
+ } else {
+ if (scoreboard->idle + idle > 0) {
+ scoreboard->idle += idle;
+ } else {
+ scoreboard->idle = 0;
+ }
+
+ if (scoreboard->active + active > 0) {
+ scoreboard->active += active;
+ } else {
+ scoreboard->active = 0;
+ }
+
+ if (scoreboard->requests + requests > 0) {
+ scoreboard->requests += requests;
+ } else {
+ scoreboard->requests = 0;
+ }
+
+ if (scoreboard->max_children_reached + max_children_reached > 0) {
+ scoreboard->max_children_reached += max_children_reached;
+ } else {
+ scoreboard->max_children_reached = 0;
+ }
+ }
+
+ if (scoreboard->active > scoreboard->active_max) {
+ scoreboard->active_max = scoreboard->active;
+ }
+
+ fpm_unlock(scoreboard->lock);
+}
+/* }}} */
+
+struct fpm_scoreboard_s *fpm_scoreboard_get() /* {{{*/
+{
+ return fpm_scoreboard;
+}
+/* }}} */
+
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
+{
+ if (!scoreboard) {
+ scoreboard = fpm_scoreboard;
+ }
+
+ if (!scoreboard) {
+ return NULL;
+ }
+
+ if (child_index < 0) {
+ child_index = fpm_scoreboard_i;
+ }
+
+ if (child_index < 0 || child_index >= scoreboard->nprocs) {
+ return NULL;
+ }
+
+ return scoreboard->procs[child_index];
+}
+/* }}} */
+
+struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */
+{
+ struct fpm_scoreboard_s *s;
+
+ s = scoreboard ? scoreboard : fpm_scoreboard;
+ if (!s) {
+ return NULL;
+ }
+
+ if (!fpm_spinlock(&s->lock, nohang)) {
+ return NULL;
+ }
+ return s;
+}
+/* }}} */
+
+void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard) {
+ if (!scoreboard) {
+ return;
+ }
+
+ scoreboard->lock = 0;
+}
+
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ proc = fpm_scoreboard_proc_get(scoreboard, child_index);
+ if (!proc) {
+ return NULL;
+ }
+
+ if (!fpm_spinlock(&proc->lock, nohang)) {
+ return NULL;
+ }
+
+ return proc;
+}
+/* }}} */
+
+void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc) /* {{{ */
+{
+ if (!proc) {
+ return;
+ }
+
+ proc->lock = 0;
+}
+
+void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard) /* {{{ */
+{
+ int i;
+
+ if (!scoreboard) {
+ zlog(ZLOG_ERROR, "**scoreboard is NULL");
+ return;
+ }
+
+ for (i = 0; i < scoreboard->nprocs; i++) {
+ if (!scoreboard->procs[i]) {
+ continue;
+ }
+ fpm_shm_free(scoreboard->procs[i], sizeof(struct fpm_scoreboard_proc_s));
+ }
+ fpm_shm_free(scoreboard, sizeof(struct fpm_scoreboard_s));
+}
+/* }}} */
+
+void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ fpm_scoreboard = scoreboard;
+ fpm_scoreboard_i = child_index;
+ proc = fpm_scoreboard_proc_get(scoreboard, child_index);
+ if (!proc) {
+ return;
+ }
+ proc->pid = pid;
+ proc->start_epoch = time(NULL);
+}
+/* }}} */
+
+void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{ */
+{
+ if (!scoreboard) {
+ return;
+ }
+
+ if (child_index < 0 || child_index >= scoreboard->nprocs) {
+ return;
+ }
+
+ if (scoreboard->procs[child_index] && scoreboard->procs[child_index]->used > 0) {
+ memset(scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
+ }
+
+ /* set this slot as free to avoid search on next alloc */
+ scoreboard->free_proc = child_index;
+}
+/* }}} */
+
+int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index) /* {{{ */
+{
+ int i = -1;
+
+ if (!scoreboard || !child_index) {
+ return -1;
+ }
+
+ /* first try the slot which is supposed to be free */
+ if (scoreboard->free_proc >= 0 && scoreboard->free_proc < scoreboard->nprocs) {
+ if (scoreboard->procs[scoreboard->free_proc] && !scoreboard->procs[scoreboard->free_proc]->used) {
+ i = scoreboard->free_proc;
+ }
+ }
+
+ if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
+ zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
+ for (i = 0; i < scoreboard->nprocs; i++) {
+ if (scoreboard->procs[i] && !scoreboard->procs[i]->used) { /* found */
+ break;
+ }
+ }
+ }
+
+ /* no free slot */
+ if (i < 0 || i >= scoreboard->nprocs) {
+ zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
+ return -1;
+ }
+
+ scoreboard->procs[i]->used = 1;
+ *child_index = i;
+
+ /* supposed next slot is free */
+ if (i + 1 >= scoreboard->nprocs) {
+ scoreboard->free_proc = 0;
+ } else {
+ scoreboard->free_proc = i + 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+#ifdef HAVE_TIMES
+float fpm_scoreboard_get_tick() /* {{{ */
+{
+ return fpm_scoreboard_tick;
+}
+/* }}} */
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_scoreboard.h b/sapi/fpm/fpm/fpm_scoreboard.h
new file mode 100644
index 0000000..f58a287
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_scoreboard.h
@@ -0,0 +1,94 @@
+
+ /* $Id: fpm_status.h 312263 2011-06-18 17:46:16Z felipe $ */
+ /* (c) 2009 Jerome Loyet */
+
+#ifndef FPM_SCOREBOARD_H
+#define FPM_SCOREBOARD_H 1
+
+#include <sys/time.h>
+#ifdef HAVE_TIMES
+#include <sys/times.h>
+#endif
+
+#include "fpm_request.h"
+#include "fpm_worker_pool.h"
+#include "fpm_atomic.h"
+
+#define FPM_SCOREBOARD_ACTION_SET 0
+#define FPM_SCOREBOARD_ACTION_INC 1
+
+struct fpm_scoreboard_proc_s {
+ union {
+ atomic_t lock;
+ char dummy[16];
+ };
+ int used;
+ time_t start_epoch;
+ pid_t pid;
+ unsigned long requests;
+ enum fpm_request_stage_e request_stage;
+ struct timeval accepted;
+ struct timeval duration;
+ time_t accepted_epoch;
+ struct timeval tv;
+ char request_uri[128];
+ char query_string[512];
+ char request_method[16];
+ size_t content_length; /* used with POST only */
+ char script_filename[256];
+ char auth_user[32];
+#ifdef HAVE_TIMES
+ struct tms cpu_accepted;
+ struct timeval cpu_duration;
+ struct tms last_request_cpu;
+ struct timeval last_request_cpu_duration;
+#endif
+ size_t memory;
+};
+
+struct fpm_scoreboard_s {
+ union {
+ atomic_t lock;
+ char dummy[16];
+ };
+ char pool[32];
+ int pm;
+ time_t start_epoch;
+ int idle;
+ int active;
+ int active_max;
+ unsigned long int requests;
+ unsigned int max_children_reached;
+ int lq;
+ int lq_max;
+ unsigned int lq_len;
+ unsigned int nprocs;
+ int free_proc;
+ unsigned long int slow_rq;
+ struct fpm_scoreboard_proc_s *procs[];
+};
+
+int fpm_scoreboard_init_main();
+int fpm_scoreboard_init_child(struct fpm_worker_pool_s *wp);
+
+void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, int action, struct fpm_scoreboard_s *scoreboard);
+struct fpm_scoreboard_s *fpm_scoreboard_get();
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index);
+
+struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang);
+void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard);
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang);
+void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc);
+
+void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard);
+
+void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid);
+
+void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index);
+int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index);
+
+#ifdef HAVE_TIMES
+float fpm_scoreboard_get_tick();
+#endif
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_shm.c b/sapi/fpm/fpm/fpm_shm.c
new file mode 100644
index 0000000..9226adf
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_shm.c
@@ -0,0 +1,70 @@
+
+ /* $Id: fpm_shm.c,v 1.3 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin, Jerome Loyet */
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+#include "fpm_shm.h"
+#include "zlog.h"
+
+
+/* MAP_ANON is deprecated, but not in macosx */
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+static size_t fpm_shm_size = 0;
+
+void *fpm_shm_alloc(size_t size) /* {{{ */
+{
+ void *mem;
+
+ mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+
+#ifdef MAP_FAILED
+ if (mem == MAP_FAILED) {
+ zlog(ZLOG_SYSERROR, "unable to allocate %zu bytes in shared memory: %s", size, strerror(errno));
+ return NULL;
+ }
+#endif
+
+ if (!mem) {
+ zlog(ZLOG_SYSERROR, "unable to allocate %zu bytes in shared memory", size);
+ return NULL;
+ }
+
+ memset(mem, 0, size);
+ fpm_shm_size += size;
+ return mem;
+}
+/* }}} */
+
+int fpm_shm_free(void *mem, size_t size) /* {{{ */
+{
+ if (!mem) {
+ zlog(ZLOG_ERROR, "mem is NULL");
+ return 0;
+ }
+
+ if (munmap(mem, size) == -1) {
+ zlog(ZLOG_SYSERROR, "Unable to free shm");
+ return 0;
+ }
+
+ if (fpm_shm_size - size > 0) {
+ fpm_shm_size -= size;
+ } else {
+ fpm_shm_size = 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+size_t fpm_shm_get_size_allocated() /* {{{*/
+{
+ return fpm_shm_size;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_shm.h b/sapi/fpm/fpm/fpm_shm.h
new file mode 100644
index 0000000..bcb6099
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_shm.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_shm.h,v 1.3 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_SHM_H
+#define FPM_SHM_H 1
+
+void *fpm_shm_alloc(size_t size);
+int fpm_shm_free(void *mem, size_t size);
+size_t fpm_shm_get_size_allocated();
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_signals.c b/sapi/fpm/fpm/fpm_signals.c
new file mode 100644
index 0000000..8993a86
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_signals.c
@@ -0,0 +1,251 @@
+
+ /* $Id: fpm_signals.c,v 1.24 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "fpm.h"
+#include "fpm_signals.h"
+#include "fpm_sockets.h"
+#include "fpm_php.h"
+#include "zlog.h"
+
+static int sp[2];
+
+const char *fpm_signal_names[NSIG + 1] = {
+#ifdef SIGHUP
+ [SIGHUP] = "SIGHUP",
+#endif
+#ifdef SIGINT
+ [SIGINT] = "SIGINT",
+#endif
+#ifdef SIGQUIT
+ [SIGQUIT] = "SIGQUIT",
+#endif
+#ifdef SIGILL
+ [SIGILL] = "SIGILL",
+#endif
+#ifdef SIGTRAP
+ [SIGTRAP] = "SIGTRAP",
+#endif
+#ifdef SIGABRT
+ [SIGABRT] = "SIGABRT",
+#endif
+#ifdef SIGEMT
+ [SIGEMT] = "SIGEMT",
+#endif
+#ifdef SIGBUS
+ [SIGBUS] = "SIGBUS",
+#endif
+#ifdef SIGFPE
+ [SIGFPE] = "SIGFPE",
+#endif
+#ifdef SIGKILL
+ [SIGKILL] = "SIGKILL",
+#endif
+#ifdef SIGUSR1
+ [SIGUSR1] = "SIGUSR1",
+#endif
+#ifdef SIGSEGV
+ [SIGSEGV] = "SIGSEGV",
+#endif
+#ifdef SIGUSR2
+ [SIGUSR2] = "SIGUSR2",
+#endif
+#ifdef SIGPIPE
+ [SIGPIPE] = "SIGPIPE",
+#endif
+#ifdef SIGALRM
+ [SIGALRM] = "SIGALRM",
+#endif
+#ifdef SIGTERM
+ [SIGTERM] = "SIGTERM",
+#endif
+#ifdef SIGCHLD
+ [SIGCHLD] = "SIGCHLD",
+#endif
+#ifdef SIGCONT
+ [SIGCONT] = "SIGCONT",
+#endif
+#ifdef SIGSTOP
+ [SIGSTOP] = "SIGSTOP",
+#endif
+#ifdef SIGTSTP
+ [SIGTSTP] = "SIGTSTP",
+#endif
+#ifdef SIGTTIN
+ [SIGTTIN] = "SIGTTIN",
+#endif
+#ifdef SIGTTOU
+ [SIGTTOU] = "SIGTTOU",
+#endif
+#ifdef SIGURG
+ [SIGURG] = "SIGURG",
+#endif
+#ifdef SIGXCPU
+ [SIGXCPU] = "SIGXCPU",
+#endif
+#ifdef SIGXFSZ
+ [SIGXFSZ] = "SIGXFSZ",
+#endif
+#ifdef SIGVTALRM
+ [SIGVTALRM] = "SIGVTALRM",
+#endif
+#ifdef SIGPROF
+ [SIGPROF] = "SIGPROF",
+#endif
+#ifdef SIGWINCH
+ [SIGWINCH] = "SIGWINCH",
+#endif
+#ifdef SIGINFO
+ [SIGINFO] = "SIGINFO",
+#endif
+#ifdef SIGIO
+ [SIGIO] = "SIGIO",
+#endif
+#ifdef SIGPWR
+ [SIGPWR] = "SIGPWR",
+#endif
+#ifdef SIGSYS
+ [SIGSYS] = "SIGSYS",
+#endif
+#ifdef SIGWAITING
+ [SIGWAITING] = "SIGWAITING",
+#endif
+#ifdef SIGLWP
+ [SIGLWP] = "SIGLWP",
+#endif
+#ifdef SIGFREEZE
+ [SIGFREEZE] = "SIGFREEZE",
+#endif
+#ifdef SIGTHAW
+ [SIGTHAW] = "SIGTHAW",
+#endif
+#ifdef SIGCANCEL
+ [SIGCANCEL] = "SIGCANCEL",
+#endif
+#ifdef SIGLOST
+ [SIGLOST] = "SIGLOST",
+#endif
+};
+
+static void sig_soft_quit(int signo) /* {{{ */
+{
+ int saved_errno = errno;
+
+ /* closing fastcgi listening socket will force fcgi_accept() exit immediately */
+ close(0);
+ socket(AF_UNIX, SOCK_STREAM, 0);
+ fpm_php_soft_quit();
+ errno = saved_errno;
+}
+/* }}} */
+
+static void sig_handler(int signo) /* {{{ */
+{
+ static const char sig_chars[NSIG + 1] = {
+ [SIGTERM] = 'T',
+ [SIGINT] = 'I',
+ [SIGUSR1] = '1',
+ [SIGUSR2] = '2',
+ [SIGQUIT] = 'Q',
+ [SIGCHLD] = 'C'
+ };
+ char s;
+ int saved_errno;
+
+ if (fpm_globals.parent_pid != getpid()) {
+ /* prevent a signal race condition when child process
+ have not set up it's own signal handler yet */
+ return;
+ }
+
+ saved_errno = errno;
+ s = sig_chars[signo];
+ write(sp[1], &s, sizeof(s));
+ errno = saved_errno;
+}
+/* }}} */
+
+int fpm_signals_init_main() /* {{{ */
+{
+ struct sigaction act;
+
+ if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
+ zlog(ZLOG_SYSERROR, "failed to init signals: socketpair()");
+ return -1;
+ }
+
+ if (0 > fd_set_blocked(sp[0], 0) || 0 > fd_set_blocked(sp[1], 0)) {
+ zlog(ZLOG_SYSERROR, "failed to init signals: fd_set_blocked()");
+ return -1;
+ }
+
+ if (0 > fcntl(sp[0], F_SETFD, FD_CLOEXEC) || 0 > fcntl(sp[1], F_SETFD, FD_CLOEXEC)) {
+ zlog(ZLOG_SYSERROR, "falied to init signals: fcntl(F_SETFD, FD_CLOEXEC)");
+ return -1;
+ }
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_handler;
+ sigfillset(&act.sa_mask);
+
+ if (0 > sigaction(SIGTERM, &act, 0) ||
+ 0 > sigaction(SIGINT, &act, 0) ||
+ 0 > sigaction(SIGUSR1, &act, 0) ||
+ 0 > sigaction(SIGUSR2, &act, 0) ||
+ 0 > sigaction(SIGCHLD, &act, 0) ||
+ 0 > sigaction(SIGQUIT, &act, 0)) {
+
+ zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_signals_init_child() /* {{{ */
+{
+ struct sigaction act, act_dfl;
+
+ memset(&act, 0, sizeof(act));
+ memset(&act_dfl, 0, sizeof(act_dfl));
+
+ act.sa_handler = &sig_soft_quit;
+ act.sa_flags |= SA_RESTART;
+
+ act_dfl.sa_handler = SIG_DFL;
+
+ close(sp[0]);
+ close(sp[1]);
+
+ if (0 > sigaction(SIGTERM, &act_dfl, 0) ||
+ 0 > sigaction(SIGINT, &act_dfl, 0) ||
+ 0 > sigaction(SIGUSR1, &act_dfl, 0) ||
+ 0 > sigaction(SIGUSR2, &act_dfl, 0) ||
+ 0 > sigaction(SIGCHLD, &act_dfl, 0) ||
+ 0 > sigaction(SIGQUIT, &act, 0)) {
+
+ zlog(ZLOG_SYSERROR, "failed to init child signals: sigaction()");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_signals_get_fd() /* {{{ */
+{
+ return sp[0];
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_signals.h b/sapi/fpm/fpm/fpm_signals.h
new file mode 100644
index 0000000..eb80fae
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_signals.h
@@ -0,0 +1,16 @@
+
+ /* $Id: fpm_signals.h,v 1.5 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_SIGNALS_H
+#define FPM_SIGNALS_H 1
+
+#include <signal.h>
+
+int fpm_signals_init_main();
+int fpm_signals_init_child();
+int fpm_signals_get_fd();
+
+extern const char *fpm_signal_names[NSIG + 1];
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c
new file mode 100644
index 0000000..76759e7
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_sockets.c
@@ -0,0 +1,477 @@
+
+ /* $Id: fpm_sockets.c,v 1.20.2.1 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h> /* for chmod(2) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "zlog.h"
+#include "fpm_arrays.h"
+#include "fpm_sockets.h"
+#include "fpm_worker_pool.h"
+#include "fpm_unix.h"
+#include "fpm_str.h"
+#include "fpm_env.h"
+#include "fpm_cleanup.h"
+#include "fpm_scoreboard.h"
+
+struct listening_socket_s {
+ int refcount;
+ int sock;
+ int type;
+ char *key;
+};
+
+static struct fpm_array_s sockets_list;
+
+static int fpm_sockets_resolve_af_inet(char *node, char *service, struct sockaddr_in *addr) /* {{{ */
+{
+ struct addrinfo *res;
+ struct addrinfo hints;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ ret = getaddrinfo(node, service, &hints, &res);
+
+ if (ret != 0) {
+ zlog(ZLOG_ERROR, "can't resolve hostname '%s%s%s': getaddrinfo said: %s%s%s\n",
+ node, service ? ":" : "", service ? service : "",
+ gai_strerror(ret), ret == EAI_SYSTEM ? ", system error: " : "", ret == EAI_SYSTEM ? strerror(errno) : "");
+ return -1;
+ }
+
+ *addr = *(struct sockaddr_in *) res->ai_addr;
+ freeaddrinfo(res);
+ return 0;
+}
+/* }}} */
+
+enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
+
+static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
+{
+ unsigned i;
+ char *env_value = 0;
+ int p = 0;
+ struct listening_socket_s *ls = sockets_list.data;
+
+ for (i = 0; i < sockets_list.used; i++, ls++) {
+ if (which != FPM_CLEANUP_PARENT_EXEC) {
+ close(ls->sock);
+ } else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
+ char fd[32];
+ sprintf(fd, "%d", ls->sock);
+ env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + 1);
+ p += sprintf(env_value + p, "%s%s=%s", p ? "," : "", ls->key, fd);
+ }
+
+ if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
+ if (ls->type == FPM_AF_UNIX) {
+ unlink(ls->key);
+ }
+ }
+ free(ls->key);
+ }
+
+ if (env_value) {
+ setenv("FPM_SOCKETS", env_value, 1);
+ free(env_value);
+ }
+
+ fpm_array_free(&sockets_list);
+}
+/* }}} */
+
+static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
+{
+ if (key == NULL) {
+ switch (type) {
+ case FPM_AF_INET : {
+ struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
+ key = alloca(sizeof("xxx.xxx.xxx.xxx:ppppp"));
+ sprintf(key, "%u.%u.%u.%u:%u", IPQUAD(&sa_in->sin_addr), (unsigned int) ntohs(sa_in->sin_port));
+ break;
+ }
+
+ case FPM_AF_UNIX : {
+ struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
+ key = alloca(strlen(sa_un->sun_path) + 1);
+ strcpy(key, sa_un->sun_path);
+ break;
+ }
+
+ default :
+ return -1;
+ }
+ }
+
+ switch (op) {
+
+ case FPM_GET_USE_SOCKET :
+ {
+ unsigned i;
+ struct listening_socket_s *ls = sockets_list.data;
+
+ for (i = 0; i < sockets_list.used; i++, ls++) {
+ if (!strcmp(ls->key, key)) {
+ ++ls->refcount;
+ return ls->sock;
+ }
+ }
+ break;
+ }
+
+ case FPM_STORE_SOCKET : /* inherited socket */
+ case FPM_STORE_USE_SOCKET : /* just created */
+ {
+ struct listening_socket_s *ls;
+
+ ls = fpm_array_push(&sockets_list);
+ if (!ls) {
+ break;
+ }
+
+ if (op == FPM_STORE_SOCKET) {
+ ls->refcount = 0;
+ } else {
+ ls->refcount = 1;
+ }
+ ls->type = type;
+ ls->sock = sock;
+ ls->key = strdup(key);
+
+ return 0;
+ }
+ }
+ return -1;
+}
+/* }}} */
+
+static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
+{
+ int flags = 1;
+ int sock;
+ mode_t saved_umask;
+
+ sock = socket(sa->sa_family, SOCK_STREAM, 0);
+
+ if (0 > sock) {
+ zlog(ZLOG_SYSERROR, "failed to create new listening socket: socket()");
+ return -1;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
+
+ if (wp->listen_address_domain == FPM_AF_UNIX) {
+ if (fpm_socket_unix_test_connect((struct sockaddr_un *)sa, socklen) == 0) {
+ zlog(ZLOG_ERROR, "An another FPM instance seems to already listen on %s", ((struct sockaddr_un *) sa)->sun_path);
+ return -1;
+ }
+ unlink( ((struct sockaddr_un *) sa)->sun_path);
+ saved_umask = umask(0777 ^ wp->socket_mode);
+ }
+
+ if (0 > bind(sock, sa, socklen)) {
+ zlog(ZLOG_SYSERROR, "unable to bind listening socket for address '%s'", wp->config->listen_address);
+ if (wp->listen_address_domain == FPM_AF_UNIX) {
+ umask(saved_umask);
+ }
+ return -1;
+ }
+
+ if (wp->listen_address_domain == FPM_AF_UNIX) {
+ char *path = ((struct sockaddr_un *) sa)->sun_path;
+
+ umask(saved_umask);
+
+ if (wp->socket_uid != -1 || wp->socket_gid != -1) {
+ if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
+ zlog(ZLOG_SYSERROR, "failed to chown() the socket '%s'", wp->config->listen_address);
+ return -1;
+ }
+ }
+ }
+
+ if (0 > listen(sock, wp->config->listen_backlog)) {
+ zlog(ZLOG_SYSERROR, "failed to listen to address '%s'", wp->config->listen_address);
+ return -1;
+ }
+
+ return sock;
+}
+/* }}} */
+
+static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
+{
+ int sock;
+
+ sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
+ if (sock >= 0) {
+ return sock;
+ }
+
+ sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
+ fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
+
+ return sock;
+}
+/* }}} */
+
+enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */
+{
+ if (strchr(address, ':')) {
+ return FPM_AF_INET;
+ }
+
+ if (strlen(address) == strspn(address, "0123456789")) {
+ return FPM_AF_INET;
+ }
+ return FPM_AF_UNIX;
+}
+/* }}} */
+
+static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct sockaddr_in sa_in;
+ char *dup_address = strdup(wp->config->listen_address);
+ char *port_str = strchr(dup_address, ':');
+ char *addr = NULL;
+ int port = 0;
+
+ if (port_str) { /* this is host:port pair */
+ *port_str++ = '\0';
+ port = atoi(port_str);
+ addr = dup_address;
+ } else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
+ port = atoi(dup_address);
+ port_str = dup_address;
+ }
+
+ if (port == 0) {
+ zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
+ return -1;
+ }
+
+ memset(&sa_in, 0, sizeof(sa_in));
+
+ if (addr) {
+ sa_in.sin_addr.s_addr = inet_addr(addr);
+ if (sa_in.sin_addr.s_addr == INADDR_NONE) { /* do resolve */
+ if (0 > fpm_sockets_resolve_af_inet(addr, NULL, &sa_in)) {
+ return -1;
+ }
+ zlog(ZLOG_NOTICE, "address '%s' resolved as %u.%u.%u.%u", addr, IPQUAD(&sa_in.sin_addr));
+ }
+ } else {
+ sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ sa_in.sin_family = AF_INET;
+ sa_in.sin_port = htons(port);
+ free(dup_address);
+ return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
+}
+/* }}} */
+
+static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct sockaddr_un sa_un;
+
+ memset(&sa_un, 0, sizeof(sa_un));
+ strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
+ sa_un.sun_family = AF_UNIX;
+ return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
+}
+/* }}} */
+
+int fpm_sockets_init_main() /* {{{ */
+{
+ unsigned i, lq_len;
+ struct fpm_worker_pool_s *wp;
+ char *inherited = getenv("FPM_SOCKETS");
+ struct listening_socket_s *ls;
+
+ if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
+ return -1;
+ }
+
+ /* import inherited sockets */
+ while (inherited && *inherited) {
+ char *comma = strchr(inherited, ',');
+ int type, fd_no;
+ char *eq;
+
+ if (comma) {
+ *comma = '\0';
+ }
+
+ eq = strchr(inherited, '=');
+ if (eq) {
+ *eq = '\0';
+ fd_no = atoi(eq + 1);
+ type = fpm_sockets_domain_from_address(inherited);
+ zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
+ fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
+ }
+
+ if (comma) {
+ inherited = comma + 1;
+ } else {
+ inherited = 0;
+ }
+ }
+
+ /* create all required sockets */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ switch (wp->listen_address_domain) {
+ case FPM_AF_INET :
+ wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
+ break;
+
+ case FPM_AF_UNIX :
+ if (0 > fpm_unix_resolve_socket_premissions(wp)) {
+ return -1;
+ }
+ wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
+ break;
+ }
+
+ if (wp->listening_socket == -1) {
+ return -1;
+ }
+
+ if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
+ fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
+ }
+ }
+
+ /* close unused sockets that was inherited */
+ ls = sockets_list.data;
+
+ for (i = 0; i < sockets_list.used; ) {
+ if (ls->refcount == 0) {
+ close(ls->sock);
+ if (ls->type == FPM_AF_UNIX) {
+ unlink(ls->key);
+ }
+ free(ls->key);
+ fpm_array_item_remove(&sockets_list, i);
+ } else {
+ ++i;
+ ++ls;
+ }
+ }
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+#if HAVE_FPM_LQ
+
+#ifdef HAVE_LQ_TCP_INFO
+
+#include <netinet/tcp.h>
+
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
+{
+ struct tcp_info info;
+ socklen_t len = sizeof(info);
+
+ if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
+ zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
+ return -1;
+ }
+
+ /* kernel >= 2.6.24 return non-zero here, that means operation is supported */
+ if (info.tcpi_sacked == 0) {
+ return -1;
+ }
+
+ if (cur_lq) {
+ *cur_lq = info.tcpi_unacked;
+ }
+
+ if (max_lq) {
+ *max_lq = info.tcpi_sacked;
+ }
+
+ return 0;
+}
+
+#endif
+
+#ifdef HAVE_LQ_SO_LISTENQ
+
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
+{
+ int val;
+ socklen_t len = sizeof(val);
+
+ if (cur_lq) {
+ if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
+ return -1;
+ }
+
+ *cur_lq = val;
+ }
+
+ if (max_lq) {
+ if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
+ return -1;
+ }
+
+ *max_lq = val;
+ }
+
+ return 0;
+}
+
+#endif
+
+#else
+
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
+{
+ return -1;
+}
+
+#endif
+
+int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen) /* {{{ */
+{
+ int fd;
+
+ if (!sock || sock->sun_family != AF_UNIX) {
+ return -1;
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ if (connect(fd, (struct sockaddr *)sock, socklen) == -1) {
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_sockets.h b/sapi/fpm/fpm/fpm_sockets.h
new file mode 100644
index 0000000..cce5712
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_sockets.h
@@ -0,0 +1,54 @@
+
+ /* $Id: fpm_sockets.h,v 1.12 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_MISC_H
+#define FPM_MISC_H 1
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "fpm_worker_pool.h"
+
+/*
+ On FreeBSD and OpenBSD, backlog negative values are truncated to SOMAXCONN
+*/
+#if (__FreeBSD__) || (__OpenBSD__)
+#define FPM_BACKLOG_DEFAULT -1
+#else
+#define FPM_BACKLOG_DEFAULT 128
+#endif
+
+enum fpm_address_domain fpm_sockets_domain_from_address(char *addr);
+int fpm_sockets_init_main();
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq);
+int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen);
+
+
+static inline int fd_set_blocked(int fd, int blocked) /* {{{ */
+{
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags < 0) {
+ return -1;
+ }
+
+ if (blocked) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+ return fcntl(fd, F_SETFL, flags);
+}
+/* }}} */
+
+#define IPQUAD(sin_addr) \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[0], \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[1], \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[2], \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[3]
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c
new file mode 100644
index 0000000..2363b57
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_status.c
@@ -0,0 +1,477 @@
+
+ /* $Id$ */
+ /* (c) 2009 Jerome Loyet */
+
+#include "php.h"
+#include "SAPI.h"
+#include <stdio.h>
+
+#include "fpm_config.h"
+#include "fpm_scoreboard.h"
+#include "fpm_status.h"
+#include "fpm_clock.h"
+#include "fpm_scoreboard.h"
+#include "zlog.h"
+#include "fpm_atomic.h"
+#include "fpm_conf.h"
+#include "fpm_php.h"
+#include <ext/standard/html.h>
+
+static char *fpm_status_uri = NULL;
+static char *fpm_status_ping_uri = NULL;
+static char *fpm_status_ping_response = NULL;
+
+
+int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (!wp || !wp->config) {
+ zlog(ZLOG_ERROR, "unable to init fpm_status because conf structure is NULL");
+ return -1;
+ }
+
+ if (wp->config->pm_status_path) {
+ fpm_status_uri = strdup(wp->config->pm_status_path);
+ }
+
+ if (wp->config->ping_path) {
+ if (!wp->config->ping_response) {
+ zlog(ZLOG_ERROR, "[pool %s] ping is set (%s) but ping.response is not set.", wp->config->name, wp->config->ping_path);
+ return -1;
+ }
+ fpm_status_ping_uri = strdup(wp->config->ping_path);
+ fpm_status_ping_response = strdup(wp->config->ping_response);
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_status_handle_request(TSRMLS_D) /* {{{ */
+{
+ struct fpm_scoreboard_s scoreboard, *scoreboard_p;
+ struct fpm_scoreboard_proc_s proc;
+ char *buffer, *time_format, time_buffer[64];
+ time_t now_epoch;
+ int full, encode;
+ char *short_syntax, *short_post;
+ char *full_pre, *full_syntax, *full_post, *full_separator;
+
+ if (!SG(request_info).request_uri) {
+ return 0;
+ }
+
+ /* PING */
+ if (fpm_status_ping_uri && fpm_status_ping_response && !strcmp(fpm_status_ping_uri, SG(request_info).request_uri)) {
+ fpm_request_executing();
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ SG(sapi_headers).http_response_code = 200;
+
+ /* handle HEAD */
+ if (SG(request_info).headers_only) {
+ return 1;
+ }
+
+ PUTS(fpm_status_ping_response);
+ return 1;
+ }
+
+ /* STATUS */
+ if (fpm_status_uri && !strcmp(fpm_status_uri, SG(request_info).request_uri)) {
+ fpm_request_executing();
+
+ scoreboard_p = fpm_scoreboard_get();
+ if (!scoreboard_p) {
+ zlog(ZLOG_ERROR, "status: unable to find or access status shared memory");
+ SG(sapi_headers).http_response_code = 500;
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ PUTS("Internal error. Please review log file for errors.");
+ return 1;
+ }
+
+ if (!fpm_spinlock(&scoreboard_p->lock, 1)) {
+ zlog(ZLOG_NOTICE, "[pool %s] status: scoreboard already in used.", scoreboard_p->pool);
+ SG(sapi_headers).http_response_code = 503;
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ PUTS("Server busy. Please try again later.");
+ return 1;
+ }
+ /* copy the scoreboard not to bother other processes */
+ scoreboard = *scoreboard_p;
+ fpm_unlock(scoreboard_p->lock);
+
+ if (scoreboard.idle < 0 || scoreboard.active < 0) {
+ zlog(ZLOG_ERROR, "[pool %s] invalid status values", scoreboard.pool);
+ SG(sapi_headers).http_response_code = 500;
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ PUTS("Internal error. Please review log file for errors.");
+ return 1;
+ }
+
+ /* send common headers */
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ SG(sapi_headers).http_response_code = 200;
+
+ /* handle HEAD */
+ if (SG(request_info).headers_only) {
+ return 1;
+ }
+
+ /* full status ? */
+ full = (fpm_php_get_string_from_table("_GET", "full" TSRMLS_CC) != NULL);
+ short_syntax = short_post = NULL;
+ full_separator = full_pre = full_syntax = full_post = NULL;
+ encode = 0;
+
+ /* HTML */
+ if (fpm_php_get_string_from_table("_GET", "html" TSRMLS_CC)) {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/html"), 1, 1 TSRMLS_CC);
+ time_format = "%d/%b/%Y:%H:%M:%S %z";
+ encode = 1;
+
+ short_syntax =
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
+ "<head><title>PHP-FPM Status Page</title></head>\n"
+ "<body>\n"
+ "<table>\n"
+ "<tr><th>pool</th><td>%s</td></tr>\n"
+ "<tr><th>process manager</th><td>%s</td></tr>\n"
+ "<tr><th>start time</th><td>%s</td></tr>\n"
+ "<tr><th>start since</th><td>%lu</td></tr>\n"
+ "<tr><th>accepted conn</th><td>%lu</td></tr>\n"
+#ifdef HAVE_FPM_LQ
+ "<tr><th>listen queue</th><td>%u</td></tr>\n"
+ "<tr><th>max listen queue</th><td>%u</td></tr>\n"
+ "<tr><th>listen queue len</th><td>%d</td></tr>\n"
+#endif
+ "<tr><th>idle processes</th><td>%d</td></tr>\n"
+ "<tr><th>active processes</th><td>%d</td></tr>\n"
+ "<tr><th>total processes</th><td>%d</td></tr>\n"
+ "<tr><th>max active processes</th><td>%d</td></tr>\n"
+ "<tr><th>max children reached</th><td>%u</td></tr>\n"
+ "<tr><th>slow requests</th><td>%lu</td></tr>\n"
+ "</table>\n";
+
+ if (!full) {
+ short_post = "</body></html>";
+ } else {
+ full_pre =
+ "<table border=\"1\">\n"
+ "<tr>"
+ "<th>pid</th>"
+ "<th>state</th>"
+ "<th>start time</th>"
+ "<th>start since</th>"
+ "<th>requests</th>"
+ "<th>request duration</th>"
+ "<th>request method</th>"
+ "<th>request uri</th>"
+ "<th>content length</th>"
+ "<th>user</th>"
+ "<th>script</th>"
+#ifdef HAVE_FPM_LQ
+ "<th>last request cpu</th>"
+#endif
+ "<th>last request memory</th>"
+ "</tr>\n";
+
+ full_syntax =
+ "<tr>"
+ "<td>%d</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td>%lu</td>"
+ "<td>%lu</td>"
+ "<td>%lu</td>"
+ "<td>%s</td>"
+ "<td>%s%s%s</td>"
+ "<td>%zu</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+#ifdef HAVE_FPM_LQ
+ "<td>%.2f</td>"
+#endif
+ "<td>%zu</td>"
+ "</tr>\n";
+
+ full_post = "</table></body></html>";
+ }
+
+ /* XML */
+ } else if (fpm_php_get_string_from_table("_GET", "xml" TSRMLS_CC)) {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/xml"), 1, 1 TSRMLS_CC);
+ time_format = "%s";
+ encode = 1;
+
+ short_syntax =
+ "<?xml version=\"1.0\" ?>\n"
+ "<status>\n"
+ "<pool>%s</pool>\n"
+ "<process-manager>%s</process-manager>\n"
+ "<start-time>%s</start-time>\n"
+ "<start-since>%lu</start-since>\n"
+ "<accepted-conn>%lu</accepted-conn>\n"
+#ifdef HAVE_FPM_LQ
+ "<listen-queue>%u</listen-queue>\n"
+ "<max-listen-queue>%u</max-listen-queue>\n"
+ "<listen-queue-len>%d</listen-queue-len>\n"
+#endif
+ "<idle-processes>%d</idle-processes>\n"
+ "<active-processes>%d</active-processes>\n"
+ "<total-processes>%d</total-processes>\n"
+ "<max-active-processes>%d</max-active-processes>\n"
+ "<max-children-reached>%u</max-children-reached>\n"
+ "<slow-requests>%lu</slow-requests>\n";
+
+ if (!full) {
+ short_post = "</status>";
+ } else {
+ full_pre = "<processes>\n";
+ full_syntax =
+ "<process>"
+ "<pid>%d</pid>"
+ "<state>%s</state>"
+ "<start-time>%s</start-time>"
+ "<start-since>%lu</start-since>"
+ "<requests>%lu</requests>"
+ "<request-duration>%lu</request-duration>"
+ "<request-method>%s</request-method>"
+ "<request-uri>%s%s%s</request-uri>"
+ "<content-length>%zu</content-length>"
+ "<user>%s</user>"
+ "<script>%s</script>"
+#ifdef HAVE_FPM_LQ
+ "<last-request-cpu>%.2f</last-request-cpu>"
+#endif
+ "<last-request-memory>%zu</last-request-memory>"
+ "</process>\n"
+ ;
+ full_post = "</processes>\n</status>";
+ }
+
+ /* JSON */
+ } else if (fpm_php_get_string_from_table("_GET", "json" TSRMLS_CC)) {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: application/json"), 1, 1 TSRMLS_CC);
+ time_format = "%s";
+
+ short_syntax =
+ "{"
+ "\"pool\":\"%s\","
+ "\"process manager\":\"%s\","
+ "\"start time\":%s,"
+ "\"start since\":%lu,"
+ "\"accepted conn\":%lu,"
+#ifdef HAVE_FPM_LQ
+ "\"listen queue\":%u,"
+ "\"max listen queue\":%u,"
+ "\"listen queue len\":%d,"
+#endif
+ "\"idle processes\":%d,"
+ "\"active processes\":%d,"
+ "\"total processes\":%d,"
+ "\"max active processes\":%d,"
+ "\"max children reached\":%u,"
+ "\"slow requests\":%lu";
+
+ if (!full) {
+ short_post = "}";
+ } else {
+ full_separator = ",";
+ full_pre = ", \"processes\":[";
+
+ full_syntax = "{"
+ "\"pid\":%d,"
+ "\"state\":\"%s\","
+ "\"start time\":%s,"
+ "\"start since\":%lu,"
+ "\"requests\":%lu,"
+ "\"request duration\":%lu,"
+ "\"request method\":\"%s\","
+ "\"request uri\":\"%s%s%s\","
+ "\"content length\":%zu,"
+ "\"user\":\"%s\","
+ "\"script\":\"%s\","
+#ifdef HAVE_FPM_LQ
+ "\"last request cpu\":%.2f,"
+#endif
+ "\"last request memory\":%zu"
+ "}";
+
+ full_post = "]}";
+ }
+
+ /* TEXT */
+ } else {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ time_format = "%d/%b/%Y:%H:%M:%S %z";
+
+ short_syntax =
+ "pool: %s\n"
+ "process manager: %s\n"
+ "start time: %s\n"
+ "start since: %lu\n"
+ "accepted conn: %lu\n"
+#ifdef HAVE_FPM_LQ
+ "listen queue: %u\n"
+ "max listen queue: %u\n"
+ "listen queue len: %d\n"
+#endif
+ "idle processes: %d\n"
+ "active processes: %d\n"
+ "total processes: %d\n"
+ "max active processes: %d\n"
+ "max children reached: %u\n"
+ "slow requests: %lu\n";
+
+ if (full) {
+ full_syntax =
+ "\n"
+ "************************\n"
+ "pid: %d\n"
+ "state: %s\n"
+ "start time: %s\n"
+ "start since: %lu\n"
+ "requests: %lu\n"
+ "request duration: %lu\n"
+ "request method: %s\n"
+ "request URI: %s%s%s\n"
+ "content length: %zu\n"
+ "user: %s\n"
+ "script: %s\n"
+#ifdef HAVE_FPM_LQ
+ "last request cpu: %.2f\n"
+#endif
+ "last request memory: %zu\n";
+ }
+ }
+
+ strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&scoreboard.start_epoch));
+ now_epoch = time(NULL);
+ spprintf(&buffer, 0, short_syntax,
+ scoreboard.pool,
+ PM2STR(scoreboard.pm),
+ time_buffer,
+ now_epoch - scoreboard.start_epoch,
+ scoreboard.requests,
+#ifdef HAVE_FPM_LQ
+ scoreboard.lq,
+ scoreboard.lq_max,
+ scoreboard.lq_len,
+#endif
+ scoreboard.idle,
+ scoreboard.active,
+ scoreboard.idle + scoreboard.active,
+ scoreboard.active_max,
+ scoreboard.max_children_reached,
+ scoreboard.slow_rq);
+
+ PUTS(buffer);
+ efree(buffer);
+
+ if (short_post) {
+ PUTS(short_post);
+ }
+
+ /* no need to test the var 'full' */
+ if (full_syntax) {
+ int i, first;
+ size_t len;
+ char *query_string;
+ struct timeval duration, now;
+#ifdef HAVE_FPM_LQ
+ float cpu;
+#endif
+
+ fpm_clock_get(&now);
+
+ if (full_pre) {
+ PUTS(full_pre);
+ }
+
+ first = 1;
+ for (i=0; i<scoreboard_p->nprocs; i++) {
+ if (!scoreboard_p->procs[i] || !scoreboard_p->procs[i]->used) {
+ continue;
+ }
+ proc = *scoreboard_p->procs[i];
+
+ if (first) {
+ first = 0;
+ } else {
+ if (full_separator) {
+ PUTS(full_separator);
+ }
+ }
+
+ query_string = NULL;
+ len = 0;
+ if (proc.query_string[0] != '\0') {
+ if (!encode) {
+ query_string = proc.query_string;
+ } else {
+ query_string = php_escape_html_entities_ex((unsigned char *)proc.query_string, strlen(proc.query_string), &len, 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, NULL, 1 TSRMLS_CC);
+ }
+ }
+
+#ifdef HAVE_FPM_LQ
+ /* prevent NaN */
+ if (proc.cpu_duration.tv_sec == 0 && proc.cpu_duration.tv_usec == 0) {
+ cpu = 0.;
+ } else {
+ cpu = (proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.;
+ }
+#endif
+
+ if (proc.request_stage == FPM_REQUEST_ACCEPTING) {
+ duration = proc.duration;
+ } else {
+ timersub(&now, &proc.accepted, &duration);
+ }
+ strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&proc.start_epoch));
+ spprintf(&buffer, 0, full_syntax,
+ proc.pid,
+ fpm_request_get_stage_name(proc.request_stage),
+ time_buffer,
+ now_epoch - proc.start_epoch,
+ proc.requests,
+ duration.tv_sec * 1000000UL + duration.tv_usec,
+ proc.request_method[0] != '\0' ? proc.request_method : "-",
+ proc.request_uri[0] != '\0' ? proc.request_uri : "-",
+ query_string ? "?" : "",
+ query_string ? query_string : "",
+ proc.content_length,
+ proc.auth_user[0] != '\0' ? proc.auth_user : "-",
+ proc.script_filename[0] != '\0' ? proc.script_filename : "-",
+#ifdef HAVE_FPM_LQ
+ proc.request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.,
+#endif
+ proc.request_stage == FPM_REQUEST_ACCEPTING ? proc.memory : 0);
+ PUTS(buffer);
+ efree(buffer);
+
+ if (len > 0 && query_string) {
+ efree(query_string);
+ }
+ }
+
+ if (full_post) {
+ PUTS(full_post);
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_status.h b/sapi/fpm/fpm/fpm_status.h
new file mode 100644
index 0000000..8f3daf9
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_status.h
@@ -0,0 +1,35 @@
+
+ /* $Id$ */
+ /* (c) 2009 Jerome Loyet */
+
+#ifndef FPM_STATUS_H
+#define FPM_STATUS_H 1
+#include "fpm_worker_pool.h"
+#include "fpm_shm.h"
+
+#define FPM_STATUS_BUFFER_SIZE 512
+
+struct fpm_status_s {
+ int pm;
+ int idle;
+ int active;
+ int total;
+ unsigned cur_lq;
+ int max_lq;
+ unsigned long int accepted_conn;
+ unsigned int max_children_reached;
+ struct timeval last_update;
+};
+
+int fpm_status_init_child(struct fpm_worker_pool_s *wp);
+void fpm_status_update_activity(struct fpm_shm_s *shm, int idle, int active, int total, unsigned cur_lq, int max_lq, int clear_last_update);
+void fpm_status_update_accepted_conn(struct fpm_shm_s *shm, unsigned long int accepted_conn);
+void fpm_status_increment_accepted_conn(struct fpm_shm_s *shm);
+void fpm_status_set_pm(struct fpm_shm_s *shm, int pm);
+void fpm_status_update_max_children_reached(struct fpm_shm_s *shm, unsigned int max_children_reached);
+void fpm_status_increment_max_children_reached(struct fpm_shm_s *shm);
+int fpm_status_handle_request(TSRMLS_D);
+
+extern struct fpm_shm_s *fpm_status_shm;
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c
new file mode 100644
index 0000000..6a587d0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_stdio.c
@@ -0,0 +1,301 @@
+
+ /* $Id: fpm_stdio.c,v 1.22.2.2 2008/12/13 03:32:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "php_syslog.h"
+
+#include "fpm.h"
+#include "fpm_children.h"
+#include "fpm_events.h"
+#include "fpm_sockets.h"
+#include "fpm_stdio.h"
+#include "zlog.h"
+
+static int fd_stdout[2];
+static int fd_stderr[2];
+
+int fpm_stdio_init_main() /* {{{ */
+{
+ int fd = open("/dev/null", O_RDWR);
+
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "failed to init stdio: open(\"/dev/null\")");
+ return -1;
+ }
+
+ if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) {
+ zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_init_final() /* {{{ */
+{
+ if (fpm_global_config.daemonize) {
+ /* prevent duping if logging to syslog */
+ if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) {
+
+ /* there might be messages to stderr from other parts of the code, we need to log them all */
+ if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
+ zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
+ return -1;
+ }
+ }
+ }
+ zlog_set_launched();
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+#ifdef HAVE_SYSLOG_H
+ if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
+ closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */
+ } else
+#endif
+ if (fpm_globals.error_log_fd > 0) {
+ close(fpm_globals.error_log_fd);
+ }
+ fpm_globals.error_log_fd = -1;
+ zlog_set_fd(-1);
+
+ if (wp->listening_socket != STDIN_FILENO) {
+ if (0 > dup2(wp->listening_socket, STDIN_FILENO)) {
+ zlog(ZLOG_SYSERROR, "failed to init child stdio: dup2()");
+ return -1;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ static const int max_buf_size = 1024;
+ int fd = ev->fd;
+ char buf[max_buf_size];
+ struct fpm_child_s *child;
+ int is_stdout;
+ struct fpm_event_s *event;
+ int fifo_in = 1, fifo_out = 1;
+ int is_last_message = 0;
+ int in_buf = 0;
+ int res;
+
+ if (!arg) {
+ return;
+ }
+ child = (struct fpm_child_s *)arg;
+ is_stdout = (fd == child->fd_stdout);
+ if (is_stdout) {
+ event = &child->ev_stdout;
+ } else {
+ event = &child->ev_stderr;
+ }
+
+ while (fifo_in || fifo_out) {
+ if (fifo_in) {
+ res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
+ if (res <= 0) { /* no data */
+ fifo_in = 0;
+ if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ /* just no more data ready */
+ } else { /* error or pipe is closed */
+
+ if (res < 0) { /* error */
+ zlog(ZLOG_SYSERROR, "unable to read what child say");
+ }
+
+ fpm_event_del(event);
+ is_last_message = 1;
+
+ if (is_stdout) {
+ close(child->fd_stdout);
+ child->fd_stdout = -1;
+ } else {
+ close(child->fd_stderr);
+ child->fd_stderr = -1;
+ }
+ }
+ } else {
+ in_buf += res;
+ }
+ }
+
+ if (fifo_out) {
+ if (in_buf == 0) {
+ fifo_out = 0;
+ } else {
+ char *nl;
+ int should_print = 0;
+ buf[in_buf] = '\0';
+
+ /* FIXME: there might be binary data */
+
+ /* we should print if no more space in the buffer */
+ if (in_buf == max_buf_size - 1) {
+ should_print = 1;
+ }
+
+ /* we should print if no more data to come */
+ if (!fifo_in) {
+ should_print = 1;
+ }
+
+ nl = strchr(buf, '\n');
+ if (nl || should_print) {
+
+ if (nl) {
+ *nl = '\0';
+ }
+
+ zlog(ZLOG_WARNING, "[pool %s] child %d said into %s: \"%s\"%s", child->wp->config->name,
+ (int) child->pid, is_stdout ? "stdout" : "stderr", buf, is_last_message ? ", pipe is closed" : "");
+
+ if (nl) {
+ int out_buf = 1 + nl - buf;
+ memmove(buf, buf + out_buf, in_buf - out_buf);
+ in_buf -= out_buf;
+ } else {
+ in_buf = 0;
+ }
+ }
+ }
+ }
+ }
+}
+/* }}} */
+
+int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (0 == child->wp->config->catch_workers_output) { /* not required */
+ return 0;
+ }
+
+ if (0 > pipe(fd_stdout)) {
+ zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe");
+ return -1;
+ }
+
+ if (0 > pipe(fd_stderr)) {
+ zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe");
+ close(fd_stdout[0]);
+ close(fd_stdout[1]);
+ return -1;
+ }
+
+ if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
+ zlog(ZLOG_SYSERROR, "failed to unblock pipes");
+ close(fd_stdout[0]);
+ close(fd_stdout[1]);
+ close(fd_stderr[0]);
+ close(fd_stderr[1]);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (0 == child->wp->config->catch_workers_output) { /* not required */
+ return 0;
+ }
+
+ close(fd_stdout[1]);
+ close(fd_stderr[1]);
+
+ child->fd_stdout = fd_stdout[0];
+ child->fd_stderr = fd_stderr[0];
+
+ fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
+ fpm_event_add(&child->ev_stdout, 0);
+
+ fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
+ fpm_event_add(&child->ev_stderr, 0);
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (0 == child->wp->config->catch_workers_output) { /* not required */
+ return 0;
+ }
+
+ close(fd_stdout[1]);
+ close(fd_stderr[1]);
+
+ close(fd_stdout[0]);
+ close(fd_stderr[0]);
+ return 0;
+}
+/* }}} */
+
+void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (child->wp->config->catch_workers_output) {
+ dup2(fd_stdout[1], STDOUT_FILENO);
+ dup2(fd_stderr[1], STDERR_FILENO);
+ close(fd_stdout[0]); close(fd_stdout[1]);
+ close(fd_stderr[0]); close(fd_stderr[1]);
+ } else {
+ /* stdout of parent is always /dev/null */
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ }
+}
+/* }}} */
+
+int fpm_stdio_open_error_log(int reopen) /* {{{ */
+{
+ int fd;
+
+#ifdef HAVE_SYSLOG_H
+ if (!strcasecmp(fpm_global_config.error_log, "syslog")) {
+ openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility);
+ fpm_globals.error_log_fd = ZLOG_SYSLOG;
+ if (fpm_global_config.daemonize) {
+ zlog_set_fd(fpm_globals.error_log_fd);
+ }
+ return 0;
+ }
+#endif
+
+ fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
+ return -1;
+ }
+
+ if (reopen) {
+ if (fpm_global_config.daemonize) {
+ dup2(fd, STDERR_FILENO);
+ }
+
+ dup2(fd, fpm_globals.error_log_fd);
+ close(fd);
+ fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
+ } else {
+ fpm_globals.error_log_fd = fd;
+ if (fpm_global_config.daemonize) {
+ zlog_set_fd(fpm_globals.error_log_fd);
+ }
+ }
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_stdio.h b/sapi/fpm/fpm/fpm_stdio.h
new file mode 100644
index 0000000..d3d61e4
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_stdio.h
@@ -0,0 +1,20 @@
+
+ /* $Id: fpm_stdio.h,v 1.9 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_STDIO_H
+#define FPM_STDIO_H 1
+
+#include "fpm_worker_pool.h"
+
+int fpm_stdio_init_main();
+int fpm_stdio_init_final();
+int fpm_stdio_init_child(struct fpm_worker_pool_s *wp);
+int fpm_stdio_prepare_pipes(struct fpm_child_s *child);
+void fpm_stdio_child_use_pipes(struct fpm_child_s *child);
+int fpm_stdio_parent_use_pipes(struct fpm_child_s *child);
+int fpm_stdio_discard_pipes(struct fpm_child_s *child);
+int fpm_stdio_open_error_log(int reopen);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_str.h b/sapi/fpm/fpm/fpm_str.h
new file mode 100644
index 0000000..65db545
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_str.h
@@ -0,0 +1,29 @@
+
+ /* $Id: fpm_str.h,v 1.3 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_STR_H
+#define FPM_STR_H 1
+
+static inline char *str_purify_filename(char *dst, char *src, size_t size) /* {{{ */
+{
+ char *d, *end;
+
+ d = dst;
+ end = dst + size - 1;
+
+ for (; d < end && *src; ++d, ++src) {
+ if (* (unsigned char *) src < ' ' || * (unsigned char *) src > '\x7f') {
+ *d = '.';
+ } else {
+ *d = *src;
+ }
+ }
+
+ *d = '\0';
+
+ return d;
+}
+/* }}} */
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_trace.c b/sapi/fpm/fpm/fpm_trace.c
new file mode 100644
index 0000000..366af5a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace.c
@@ -0,0 +1,41 @@
+
+ /* $Id: fpm_trace.c,v 1.1 2008/07/20 20:59:00 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+
+#include "fpm_trace.h"
+
+int fpm_trace_get_strz(char *buf, size_t sz, long addr) /* {{{ */
+{
+ int i;
+ long l;
+ char *lc = (char *) &l;
+
+ if (0 > fpm_trace_get_long(addr, &l)) {
+ return -1;
+ }
+
+ i = l % SIZEOF_LONG;
+ l -= i;
+ for (addr = l; ; addr += SIZEOF_LONG) {
+ if (0 > fpm_trace_get_long(addr, &l)) {
+ return -1;
+ }
+ for ( ; i < SIZEOF_LONG; i++) {
+ --sz;
+ if (sz && lc[i]) {
+ *buf++ = lc[i];
+ continue;
+ }
+ *buf = '\0';
+ return 0;
+ }
+ i = 0;
+ }
+}
+/* }}} */
+
+
diff --git a/sapi/fpm/fpm/fpm_trace.h b/sapi/fpm/fpm/fpm_trace.h
new file mode 100644
index 0000000..b421172
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace.h
@@ -0,0 +1,17 @@
+
+ /* $Id: fpm_trace.h,v 1.3 2008/07/20 22:43:39 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_TRACE_H
+#define FPM_TRACE_H 1
+
+#include <unistd.h>
+
+int fpm_trace_signal(pid_t pid);
+int fpm_trace_ready(pid_t pid);
+int fpm_trace_close(pid_t pid);
+int fpm_trace_get_long(long addr, long *data);
+int fpm_trace_get_strz(char *buf, size_t sz, long addr);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_trace_mach.c b/sapi/fpm/fpm/fpm_trace_mach.c
new file mode 100644
index 0000000..3b85e6a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace_mach.c
@@ -0,0 +1,99 @@
+
+ /* $Id: fpm_trace_mach.c,v 1.4 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+
+#include <unistd.h>
+
+#include "fpm_trace.h"
+#include "fpm_process_ctl.h"
+#include "fpm_unix.h"
+#include "zlog.h"
+
+
+static mach_port_name_t target;
+static vm_offset_t target_page_base;
+static vm_offset_t local_page;
+static mach_msg_type_number_t local_size;
+
+static void fpm_mach_vm_deallocate() /* {{{ */
+{
+ if (local_page) {
+ mach_vm_deallocate(mach_task_self(), local_page, local_size);
+ target_page_base = 0;
+ local_page = 0;
+ local_size = 0;
+ }
+}
+/* }}} */
+
+static int fpm_mach_vm_read_page(vm_offset_t page) /* {{{ */
+{
+ kern_return_t kr;
+
+ kr = mach_vm_read(target, page, fpm_pagesize, &local_page, &local_size);
+ if (kr != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "failed to read vm page: mach_vm_read(): %s (%d)", mach_error_string(kr), kr);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_signal(pid_t pid) /* {{{ */
+{
+ if (0 > fpm_pctl_kill(pid, FPM_PCTL_STOP)) {
+ zlog(ZLOG_SYSERROR, "failed to send SIGSTOP to %d", pid);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_ready(pid_t pid) /* {{{ */
+{
+ kern_return_t kr;
+
+ kr = task_for_pid(mach_task_self(), pid, &target);
+ if (kr != KERN_SUCCESS) {
+ char *msg = "";
+
+ if (kr == KERN_FAILURE) {
+ msg = " It seems that master process does not have enough privileges to trace processes.";
+ }
+ zlog(ZLOG_ERROR, "task_for_pid() failed: %s (%d)%s", mach_error_string(kr), kr, msg);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_close(pid_t pid) /* {{{ */
+{
+ fpm_mach_vm_deallocate();
+ target = 0;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_get_long(long addr, long *data) /* {{{ */
+{
+ size_t offset = ((uintptr_t) (addr) % fpm_pagesize);
+ vm_offset_t base = (uintptr_t) (addr) - offset;
+
+ if (base != target_page_base) {
+ fpm_mach_vm_deallocate();
+ if (0 > fpm_mach_vm_read_page(base)) {
+ return -1;
+ }
+ }
+ *data = * (long *) (local_page + offset);
+ return 0;
+}
+/* }}} */
+
+
diff --git a/sapi/fpm/fpm/fpm_trace_pread.c b/sapi/fpm/fpm/fpm_trace_pread.c
new file mode 100644
index 0000000..6a61557
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace_pread.c
@@ -0,0 +1,67 @@
+
+ /* $Id: fpm_trace_pread.c,v 1.7 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "fpm_config.h"
+
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+
+#include "fpm_trace.h"
+#include "fpm_process_ctl.h"
+#include "zlog.h"
+
+static int mem_file = -1;
+
+int fpm_trace_signal(pid_t pid) /* {{{ */
+{
+ if (0 > fpm_pctl_kill(pid, FPM_PCTL_STOP)) {
+ zlog(ZLOG_SYSERROR, "failed to send SIGSTOP to %d", pid);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_ready(pid_t pid) /* {{{ */
+{
+ char buf[128];
+
+ sprintf(buf, "/proc/%d/" PROC_MEM_FILE, (int) pid);
+ mem_file = open(buf, O_RDONLY);
+ if (0 > mem_file) {
+ zlog(ZLOG_SYSERROR, "failed to open %s", buf);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_close(pid_t pid) /* {{{ */
+{
+ close(mem_file);
+ mem_file = -1;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_get_long(long addr, long *data) /* {{{ */
+{
+ if (sizeof(*data) != pread(mem_file, (void *) data, sizeof(*data), (uintptr_t) addr)) {
+ zlog(ZLOG_SYSERROR, "pread() failed");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_trace_ptrace.c b/sapi/fpm/fpm/fpm_trace_ptrace.c
new file mode 100644
index 0000000..838c618
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace_ptrace.c
@@ -0,0 +1,82 @@
+
+ /* $Id: fpm_trace_ptrace.c,v 1.7 2008/09/18 23:34:11 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if defined(PT_ATTACH) && !defined(PTRACE_ATTACH)
+#define PTRACE_ATTACH PT_ATTACH
+#endif
+
+#if defined(PT_DETACH) && !defined(PTRACE_DETACH)
+#define PTRACE_DETACH PT_DETACH
+#endif
+
+#if defined(PT_READ_D) && !defined(PTRACE_PEEKDATA)
+#define PTRACE_PEEKDATA PT_READ_D
+#endif
+
+#include "fpm_trace.h"
+#include "zlog.h"
+
+static pid_t traced_pid;
+
+int fpm_trace_signal(pid_t pid) /* {{{ */
+{
+ if (0 > ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(ATTACH) child %d", pid);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_ready(pid_t pid) /* {{{ */
+{
+ traced_pid = pid;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_close(pid_t pid) /* {{{ */
+{
+ if (0 > ptrace(PTRACE_DETACH, pid, (void *) 1, 0)) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(DETACH) child %d", pid);
+ return -1;
+ }
+ traced_pid = 0;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_get_long(long addr, long *data) /* {{{ */
+{
+#ifdef PT_IO
+ struct ptrace_io_desc ptio = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = (void *) addr,
+ .piod_addr = (void *) data,
+ .piod_len = sizeof(long)
+ };
+
+ if (0 > ptrace(PT_IO, traced_pid, (void *) &ptio, 0)) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(PT_IO) pid %d", traced_pid);
+ return -1;
+ }
+#else
+ errno = 0;
+ *data = ptrace(PTRACE_PEEKDATA, traced_pid, (void *) addr, 0);
+ if (errno) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(PEEKDATA) pid %d", traced_pid);
+ return -1;
+ }
+#endif
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c
new file mode 100644
index 0000000..48249e8
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_unix.c
@@ -0,0 +1,368 @@
+
+ /* $Id: fpm_unix.c,v 1.25.2.1 2008/12/13 03:18:23 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_PRCTL
+#include <sys/prctl.h>
+#endif
+
+#include "fpm.h"
+#include "fpm_conf.h"
+#include "fpm_cleanup.h"
+#include "fpm_clock.h"
+#include "fpm_stdio.h"
+#include "fpm_unix.h"
+#include "fpm_signals.h"
+#include "zlog.h"
+
+size_t fpm_pagesize;
+
+int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct fpm_worker_pool_config_s *c = wp->config;
+
+ /* uninitialized */
+ wp->socket_uid = -1;
+ wp->socket_gid = -1;
+ wp->socket_mode = 0666;
+
+ if (!c) {
+ return 0;
+ }
+
+ if (c->listen_owner && *c->listen_owner) {
+ struct passwd *pwd;
+
+ pwd = getpwnam(c->listen_owner);
+ if (!pwd) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner);
+ return -1;
+ }
+
+ wp->socket_uid = pwd->pw_uid;
+ wp->socket_gid = pwd->pw_gid;
+ }
+
+ if (c->listen_group && *c->listen_group) {
+ struct group *grp;
+
+ grp = getgrnam(c->listen_group);
+ if (!grp) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group);
+ return -1;
+ }
+ wp->socket_gid = grp->gr_gid;
+ }
+
+ if (c->listen_mode && *c->listen_mode) {
+ wp->socket_mode = strtoul(c->listen_mode, 0, 8);
+ }
+ return 0;
+}
+/* }}} */
+
+static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct passwd *pwd;
+ int is_root = !geteuid();
+
+ if (is_root) {
+ if (wp->config->user && *wp->config->user) {
+ if (strlen(wp->config->user) == strspn(wp->config->user, "0123456789")) {
+ wp->set_uid = strtoul(wp->config->user, 0, 10);
+ } else {
+ struct passwd *pwd;
+
+ pwd = getpwnam(wp->config->user);
+ if (!pwd) {
+ zlog(ZLOG_ERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, wp->config->user);
+ return -1;
+ }
+
+ wp->set_uid = pwd->pw_uid;
+ wp->set_gid = pwd->pw_gid;
+
+ wp->user = strdup(pwd->pw_name);
+ wp->home = strdup(pwd->pw_dir);
+ }
+ }
+
+ if (wp->config->group && *wp->config->group) {
+ if (strlen(wp->config->group) == strspn(wp->config->group, "0123456789")) {
+ wp->set_gid = strtoul(wp->config->group, 0, 10);
+ } else {
+ struct group *grp;
+
+ grp = getgrnam(wp->config->group);
+ if (!grp) {
+ zlog(ZLOG_ERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, wp->config->group);
+ return -1;
+ }
+ wp->set_gid = grp->gr_gid;
+ }
+ }
+
+ if (!fpm_globals.run_as_root) {
+ if (wp->set_uid == 0 || wp->set_gid == 0) {
+ zlog(ZLOG_ERROR, "[pool %s] please specify user and group other than root", wp->config->name);
+ return -1;
+ }
+ }
+ } else { /* not root */
+ if (wp->config->user && *wp->config->user) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'user' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+ if (wp->config->group && *wp->config->group) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'group' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+ if (wp->config->chroot && *wp->config->chroot) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'chroot' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+ if (wp->config->process_priority != 64) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'process.priority' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+
+ /* set up HOME and USER anyway */
+ pwd = getpwuid(getuid());
+ if (pwd) {
+ wp->user = strdup(pwd->pw_name);
+ wp->home = strdup(pwd->pw_dir);
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ int is_root = !geteuid();
+ int made_chroot = 0;
+
+ if (wp->config->rlimit_files) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = (rlim_t) wp->config->rlimit_files;
+
+ if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_files for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", wp->config->name, wp->config->rlimit_files);
+ }
+ }
+
+ if (wp->config->rlimit_core) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = wp->config->rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) wp->config->rlimit_core;
+
+ if (0 > setrlimit(RLIMIT_CORE, &r)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", wp->config->name, wp->config->rlimit_core);
+ }
+ }
+
+ if (is_root && wp->config->chroot && *wp->config->chroot) {
+ if (0 > chroot(wp->config->chroot)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to chroot(%s)", wp->config->name, wp->config->chroot);
+ return -1;
+ }
+ made_chroot = 1;
+ }
+
+ if (wp->config->chdir && *wp->config->chdir) {
+ if (0 > chdir(wp->config->chdir)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to chdir(%s)", wp->config->name, wp->config->chdir);
+ return -1;
+ }
+ } else if (made_chroot) {
+ chdir("/");
+ }
+
+ if (is_root) {
+
+ if (wp->config->process_priority != 64) {
+ if (setpriority(PRIO_PROCESS, 0, wp->config->process_priority) < 0) {
+ zlog(ZLOG_SYSERROR, "[pool %s] Unable to set priority for this new process", wp->config->name);
+ return -1;
+ }
+ }
+
+ if (wp->set_gid) {
+ if (0 > setgid(wp->set_gid)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to setgid(%d)", wp->config->name, wp->set_gid);
+ return -1;
+ }
+ }
+ if (wp->set_uid) {
+ if (0 > initgroups(wp->config->user, wp->set_gid)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to initgroups(%s, %d)", wp->config->name, wp->config->user, wp->set_gid);
+ return -1;
+ }
+ if (0 > setuid(wp->set_uid)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to setuid(%d)", wp->config->name, wp->set_uid);
+ return -1;
+ }
+ }
+ }
+
+#ifdef HAVE_PRCTL
+ if (0 > prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to prctl(PR_SET_DUMPABLE)", wp->config->name);
+ }
+#endif
+
+ if (0 > fpm_clock_init()) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_unix_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int is_root = !geteuid();
+
+ if (fpm_global_config.rlimit_files) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = (rlim_t) fpm_global_config.rlimit_files;
+
+ if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
+ zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", fpm_global_config.rlimit_files);
+ return -1;
+ }
+ }
+
+ if (fpm_global_config.rlimit_core) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = fpm_global_config.rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) fpm_global_config.rlimit_core;
+
+ if (0 > setrlimit(RLIMIT_CORE, &r)) {
+ zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", fpm_global_config.rlimit_core);
+ return -1;
+ }
+ }
+
+ fpm_pagesize = getpagesize();
+ if (fpm_global_config.daemonize) {
+ /*
+ * If daemonize, the calling process will die soon
+ * and the master process continues to initialize itself.
+ *
+ * The parent process has then to wait for the master
+ * process to initialize to return a consistent exit
+ * value. For this pupose, the master process will
+ * send \"1\" into the pipe if everything went well
+ * and \"0\" otherwise.
+ */
+
+
+ struct timeval tv;
+ fd_set rfds;
+ int ret;
+
+ if (pipe(fpm_globals.send_config_pipe) == -1) {
+ zlog(ZLOG_SYSERROR, "failed to create pipe");
+ return -1;
+ }
+
+ /* then fork */
+ pid_t pid = fork();
+ switch (pid) {
+
+ case -1 : /* error */
+ zlog(ZLOG_SYSERROR, "failed to daemonize");
+ return -1;
+
+ case 0 : /* children */
+ close(fpm_globals.send_config_pipe[0]); /* close the read side of the pipe */
+ break;
+
+ default : /* parent */
+ close(fpm_globals.send_config_pipe[1]); /* close the write side of the pipe */
+
+ /*
+ * wait for 10s before exiting with error
+ * the child is supposed to send 1 or 0 into the pipe to tell the parent
+ * how it goes for it
+ */
+ FD_ZERO(&rfds);
+ FD_SET(fpm_globals.send_config_pipe[0], &rfds);
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ zlog(ZLOG_DEBUG, "The calling process is waiting for the master process to ping via fd=%d", fpm_globals.send_config_pipe[0]);
+ ret = select(fpm_globals.send_config_pipe[0] + 1, &rfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ zlog(ZLOG_SYSERROR, "failed to select");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ if (ret) { /* data available */
+ int readval;
+ ret = read(fpm_globals.send_config_pipe[0], &readval, sizeof(readval));
+ if (ret == -1) {
+ zlog(ZLOG_SYSERROR, "failed to read from pipe");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+
+ if (ret == 0) {
+ zlog(ZLOG_ERROR, "no data have been read from pipe");
+ exit(FPM_EXIT_SOFTWARE);
+ } else {
+ if (readval == 1) {
+ zlog(ZLOG_DEBUG, "I received a valid acknoledge from the master process, I can exit without error");
+ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);
+ exit(FPM_EXIT_OK);
+ } else {
+ zlog(ZLOG_DEBUG, "The master process returned an error !");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ }
+ } else { /* no date sent ! */
+ zlog(ZLOG_ERROR, "the master process didn't send back its status (via the pipe to the calling process)");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ }
+
+ /* continue as a child */
+ setsid();
+ if (0 > fpm_clock_init()) {
+ return -1;
+ }
+
+ if (fpm_global_config.process_priority != 64) {
+ if (is_root) {
+ if (setpriority(PRIO_PROCESS, 0, fpm_global_config.process_priority) < 0) {
+ zlog(ZLOG_SYSERROR, "Unable to set priority for the master process");
+ return -1;
+ }
+ } else {
+ zlog(ZLOG_NOTICE, "'process.priority' directive is ignored when FPM is not running as root");
+ }
+ }
+
+ fpm_globals.parent_pid = getpid();
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (0 > fpm_unix_conf_wp(wp)) {
+ return -1;
+ }
+ }
+
+ zlog_set_level(fpm_globals.log_level);
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_unix.h b/sapi/fpm/fpm/fpm_unix.h
new file mode 100644
index 0000000..3451db1
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_unix.h
@@ -0,0 +1,17 @@
+
+ /* $Id: fpm_unix.h,v 1.8 2008/05/25 13:21:13 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_UNIX_H
+#define FPM_UNIX_H 1
+
+#include "fpm_worker_pool.h"
+
+int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp);
+int fpm_unix_init_child(struct fpm_worker_pool_s *wp);
+int fpm_unix_init_main();
+
+extern size_t fpm_pagesize;
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c
new file mode 100644
index 0000000..123f989
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_worker_pool.c
@@ -0,0 +1,65 @@
+
+ /* $Id: fpm_worker_pool.c,v 1.15.2.1 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fpm.h"
+#include "fpm_worker_pool.h"
+#include "fpm_cleanup.h"
+#include "fpm_children.h"
+#include "fpm_shm.h"
+#include "fpm_scoreboard.h"
+#include "fpm_conf.h"
+
+struct fpm_worker_pool_s *fpm_worker_all_pools;
+
+static void fpm_worker_pool_cleanup(int which, void *arg) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp, *wp_next;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp_next) {
+ wp_next = wp->next;
+ fpm_worker_pool_config_free(wp->config);
+ fpm_children_free(wp->children);
+ if ((which & FPM_CLEANUP_CHILD) == 0 && fpm_globals.parent_pid == getpid()) {
+ fpm_scoreboard_free(wp->scoreboard);
+ }
+ free(wp->config);
+ free(wp->user);
+ free(wp->home);
+ free(wp);
+ }
+ fpm_worker_all_pools = NULL;
+}
+/* }}} */
+
+struct fpm_worker_pool_s *fpm_worker_pool_alloc() /* {{{ */
+{
+ struct fpm_worker_pool_s *ret;
+
+ ret = malloc(sizeof(struct fpm_worker_pool_s));
+ if (!ret) {
+ return 0;
+ }
+
+ memset(ret, 0, sizeof(struct fpm_worker_pool_s));
+
+ ret->idle_spawn_rate = 1;
+ ret->log_fd = -1;
+ return ret;
+}
+/* }}} */
+
+int fpm_worker_pool_init_main() /* {{{ */
+{
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_worker_pool_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h
new file mode 100644
index 0000000..6688e6d
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_worker_pool.h
@@ -0,0 +1,53 @@
+
+ /* $Id: fpm_worker_pool.h,v 1.13 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_WORKER_POOL_H
+#define FPM_WORKER_POOL_H 1
+
+#include "fpm_conf.h"
+#include "fpm_shm.h"
+
+struct fpm_worker_pool_s;
+struct fpm_child_s;
+struct fpm_child_stat_s;
+struct fpm_shm_s;
+
+enum fpm_address_domain {
+ FPM_AF_UNIX = 1,
+ FPM_AF_INET = 2
+};
+
+struct fpm_worker_pool_s {
+ struct fpm_worker_pool_s *next;
+ struct fpm_worker_pool_config_s *config;
+ char *user, *home; /* for setting env USER and HOME */
+ enum fpm_address_domain listen_address_domain;
+ int listening_socket;
+ int set_uid, set_gid; /* config uid and gid */
+ int socket_uid, socket_gid, socket_mode;
+
+ /* runtime */
+ struct fpm_child_s *children;
+ int running_children;
+ int idle_spawn_rate;
+ int warn_max_children;
+#if 0
+ int warn_lq;
+#endif
+ struct fpm_scoreboard_s *scoreboard;
+ int log_fd;
+ char **limit_extensions;
+
+ /* for ondemand PM */
+ struct fpm_event_s *ondemand_event;
+ int socket_event_set;
+};
+
+struct fpm_worker_pool_s *fpm_worker_pool_alloc();
+int fpm_worker_pool_init_main();
+
+extern struct fpm_worker_pool_s *fpm_worker_all_pools;
+
+#endif
+
diff --git a/sapi/fpm/fpm/zlog.c b/sapi/fpm/fpm/zlog.c
new file mode 100644
index 0000000..80db9d8
--- /dev/null
+++ b/sapi/fpm/fpm/zlog.c
@@ -0,0 +1,199 @@
+
+ /* $Id: zlog.c,v 1.7 2008/05/22 21:08:32 anight Exp $ */
+ /* (c) 2004-2007 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "php_syslog.h"
+
+#include "zlog.h"
+#include "fpm.h"
+
+#define MAX_LINE_LENGTH 1024
+
+static int zlog_fd = -1;
+static int zlog_level = ZLOG_NOTICE;
+static int launched = 0;
+static void (*external_logger)(int, char *, size_t) = NULL;
+
+static const char *level_names[] = {
+ [ZLOG_DEBUG] = "DEBUG",
+ [ZLOG_NOTICE] = "NOTICE",
+ [ZLOG_WARNING] = "WARNING",
+ [ZLOG_ERROR] = "ERROR",
+ [ZLOG_ALERT] = "ALERT",
+};
+
+#ifdef HAVE_SYSLOG_H
+const int syslog_priorities[] = {
+ [ZLOG_DEBUG] = LOG_DEBUG,
+ [ZLOG_NOTICE] = LOG_NOTICE,
+ [ZLOG_WARNING] = LOG_WARNING,
+ [ZLOG_ERROR] = LOG_ERR,
+ [ZLOG_ALERT] = LOG_ALERT,
+};
+#endif
+
+void zlog_set_external_logger(void (*logger)(int, char *, size_t)) /* {{{ */
+{
+ external_logger = logger;
+}
+/* }}} */
+
+const char *zlog_get_level_name(int log_level) /* {{{ */
+{
+ if (log_level < 0) {
+ log_level = zlog_level;
+ } else if (log_level < ZLOG_DEBUG || log_level > ZLOG_ALERT) {
+ return "unknown value";
+ }
+
+ return level_names[log_level];
+}
+/* }}} */
+
+void zlog_set_launched(void) {
+ launched = 1;
+}
+
+size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len) /* {{{ */
+{
+ struct tm t;
+ size_t len;
+
+ len = strftime(timebuf, timebuf_len, "[%d-%b-%Y %H:%M:%S", localtime_r((const time_t *) &tv->tv_sec, &t));
+ if (zlog_level == ZLOG_DEBUG) {
+ len += snprintf(timebuf + len, timebuf_len - len, ".%06d", (int) tv->tv_usec);
+ }
+ len += snprintf(timebuf + len, timebuf_len - len, "] ");
+ return len;
+}
+/* }}} */
+
+int zlog_set_fd(int new_fd) /* {{{ */
+{
+ int old_fd = zlog_fd;
+
+ zlog_fd = new_fd;
+ return old_fd;
+}
+/* }}} */
+
+int zlog_set_level(int new_value) /* {{{ */
+{
+ int old_value = zlog_level;
+
+ if (new_value < ZLOG_DEBUG || new_value > ZLOG_ALERT) return old_value;
+
+ zlog_level = new_value;
+ return old_value;
+}
+/* }}} */
+
+void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* {{{ */
+{
+ struct timeval tv;
+ char buf[MAX_LINE_LENGTH];
+ const size_t buf_size = MAX_LINE_LENGTH;
+ va_list args;
+ size_t len = 0;
+ int truncated = 0;
+ int saved_errno;
+
+ if (external_logger) {
+ va_start(args, fmt);
+ len = vsnprintf(buf, buf_size, fmt, args);
+ va_end(args);
+ if (len >= buf_size) {
+ memcpy(buf + buf_size - sizeof("..."), "...", sizeof("...") - 1);
+ len = buf_size - 1;
+ }
+ external_logger(flags & ZLOG_LEVEL_MASK, buf, len);
+ len = 0;
+ memset(buf, '\0', buf_size);
+ }
+
+ if ((flags & ZLOG_LEVEL_MASK) < zlog_level) {
+ return;
+ }
+
+ saved_errno = errno;
+#ifdef HAVE_SYSLOG_H
+ if (zlog_fd == ZLOG_SYSLOG /* && !fpm_globals.is_child */) {
+ len = 0;
+ if (zlog_level == ZLOG_DEBUG) {
+ len += snprintf(buf, buf_size, "[%s] %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], function, line);
+ } else {
+ len += snprintf(buf, buf_size, "[%s] ", level_names[flags & ZLOG_LEVEL_MASK]);
+ }
+ } else
+#endif
+ {
+ if (!fpm_globals.is_child) {
+ gettimeofday(&tv, 0);
+ len = zlog_print_time(&tv, buf, buf_size);
+ }
+ if (zlog_level == ZLOG_DEBUG) {
+ if (!fpm_globals.is_child) {
+ len += snprintf(buf + len, buf_size - len, "%s: pid %d, %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], getpid(), function, line);
+ } else {
+ len += snprintf(buf + len, buf_size - len, "%s: %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], function, line);
+ }
+ } else {
+ len += snprintf(buf + len, buf_size - len, "%s: ", level_names[flags & ZLOG_LEVEL_MASK]);
+ }
+ }
+
+ if (len > buf_size - 1) {
+ truncated = 1;
+ }
+
+ if (!truncated) {
+ va_start(args, fmt);
+ len += vsnprintf(buf + len, buf_size - len, fmt, args);
+ va_end(args);
+ if (len >= buf_size) {
+ truncated = 1;
+ }
+ }
+
+ if (!truncated) {
+ if (flags & ZLOG_HAVE_ERRNO) {
+ len += snprintf(buf + len, buf_size - len, ": %s (%d)", strerror(saved_errno), saved_errno);
+ if (len >= buf_size) {
+ truncated = 1;
+ }
+ }
+ }
+
+ if (truncated) {
+ memcpy(buf + buf_size - sizeof("..."), "...", sizeof("...") - 1);
+ len = buf_size - 1;
+ }
+
+#ifdef HAVE_SYSLOG_H
+ if (zlog_fd == ZLOG_SYSLOG) {
+ buf[len] = '\0';
+ php_syslog(syslog_priorities[zlog_level], "%s", buf);
+ buf[len++] = '\n';
+ } else
+#endif
+ {
+ buf[len++] = '\n';
+ write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len);
+ }
+
+ if (zlog_fd != STDERR_FILENO && zlog_fd != -1 && !launched && (flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE) {
+ write(STDERR_FILENO, buf, len);
+ }
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/zlog.h b/sapi/fpm/fpm/zlog.h
new file mode 100644
index 0000000..1945922
--- /dev/null
+++ b/sapi/fpm/fpm/zlog.h
@@ -0,0 +1,45 @@
+
+ /* $Id: zlog.h,v 1.7 2008/05/22 21:08:32 anight Exp $ */
+ /* (c) 2004-2007 Andrei Nigmatulin */
+
+#ifndef ZLOG_H
+#define ZLOG_H 1
+
+#define zlog(flags,...) zlog_ex(__func__, __LINE__, flags, __VA_ARGS__)
+
+struct timeval;
+
+void zlog_set_external_logger(void (*logger)(int, char *, size_t));
+int zlog_set_fd(int new_fd);
+int zlog_set_level(int new_value);
+const char *zlog_get_level_name(int log_level);
+void zlog_set_launched(void);
+
+size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len);
+
+void zlog_ex(const char *function, int line, int flags, const char *fmt, ...)
+ __attribute__ ((format(printf,4,5)));
+
+#ifdef HAVE_SYSLOG_H
+extern const int syslog_priorities[];
+#endif
+
+enum {
+ ZLOG_DEBUG = 1,
+ ZLOG_NOTICE = 2,
+ ZLOG_WARNING = 3,
+ ZLOG_ERROR = 4,
+ ZLOG_ALERT = 5,
+};
+
+#define ZLOG_LEVEL_MASK 7
+
+#define ZLOG_HAVE_ERRNO 0x100
+
+#define ZLOG_SYSERROR (ZLOG_ERROR | ZLOG_HAVE_ERRNO)
+
+#ifdef HAVE_SYSLOG_H
+#define ZLOG_SYSLOG -2
+#endif
+
+#endif
diff --git a/sapi/fpm/init.d.php-fpm.in b/sapi/fpm/init.d.php-fpm.in
new file mode 100644
index 0000000..49cce79
--- /dev/null
+++ b/sapi/fpm/init.d.php-fpm.in
@@ -0,0 +1,138 @@
+#! /bin/sh
+
+### BEGIN INIT INFO
+# Provides: php-fpm
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts php-fpm
+# Description: starts the PHP FastCGI Process Manager daemon
+### END INIT INFO
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+php_fpm_BIN=@sbindir@/php-fpm
+php_fpm_CONF=@sysconfdir@/php-fpm.conf
+php_fpm_PID=@localstatedir@/run/php-fpm.pid
+
+
+php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID"
+
+
+wait_for_pid () {
+ try=0
+
+ while test $try -lt 35 ; do
+
+ case "$1" in
+ 'created')
+ if [ -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+
+ 'removed')
+ if [ ! -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+ esac
+
+ echo -n .
+ try=`expr $try + 1`
+ sleep 1
+
+ done
+
+}
+
+case "$1" in
+ start)
+ echo -n "Starting php-fpm "
+
+ $php_fpm_BIN --daemonize $php_opts
+
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ fi
+
+ wait_for_pid created $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ stop)
+ echo -n "Gracefully shutting down php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -QUIT `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed. Use force-quit"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ force-quit)
+ echo -n "Terminating php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -TERM `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ restart)
+ $0 stop
+ $0 start
+ ;;
+
+ reload)
+
+ echo -n "Reload service php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -USR2 `cat $php_fpm_PID`
+
+ echo " done"
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|force-quit|restart|reload}"
+ exit 1
+ ;;
+
+esac
diff --git a/sapi/fpm/php-fpm.8.in b/sapi/fpm/php-fpm.8.in
new file mode 100644
index 0000000..a4e7e74
--- /dev/null
+++ b/sapi/fpm/php-fpm.8.in
@@ -0,0 +1,220 @@
+.TH PHP-FPM 8 "2009" "The PHP Group" "Scripting Language"
+.SH NAME
+.TP 15
+php-fpm \- PHP FastCGI Process Manager 'PHP-FPM'
+.SH SYNOPSIS
+.B php-fpm
+[options]
+.LP
+.SH DESCRIPTION
+\fBPHP\fP is a widely\-used general\-purpose scripting language that is especially suited for
+Web development and can be embedded into HTML. This is a variant of PHP that will run in the background as a daemon, listening for CGI requests. Output is logged to @php_fpm_localstatedir@/log/php-fpm.log.
+.LP
+Most options are set in the configuration file. The configuration file is @php_fpm_sysconfdir@/php-fpm.conf. By default, php-fpm will respond to CGI requests listening on localhost http port 9000. Therefore php-fpm expects your webserver to forward all requests for '.php' files to port 9000 and you should edit your webserver configuration file appropriately.
+.SH OPTIONS
+.TP 15
+.B \-C
+Do not chdir to the script's directory
+.TP
+.PD 0
+.B \-\-php\-ini \fIpath\fP|\fIfile\fP
+.TP
+.PD 1
+.B \-c \fIpath\fP|\fIfile\fP
+Look for
+.B php.ini
+file in the directory
+.IR path
+or use the specified
+.IR file
+.TP
+.PD 0
+.B \-\-no\-php\-ini
+.TP
+.PD 1
+.B \-n
+No
+.B php.ini
+file will be used
+.TP
+.PD 0
+.B \-\-define \fIfoo\fP[=\fIbar\fP]
+.TP
+.PD 1
+.B \-d \fIfoo\fP[=\fIbar\fP]
+Define INI entry
+.IR foo
+with value
+.IR bar
+.TP
+.B \-e
+Generate extended information for debugger/profiler
+.TP
+.PD 0
+.B \-\-help
+.TP
+.PD 1
+.B \-h
+This help
+.TP
+.PD 0
+.B \-\-info
+.TP
+.PD 1
+.B \-i
+PHP information and configuration
+.TP
+.PD 0
+.B \-\-modules
+.TP
+.PD 1
+.B \-m
+Show compiled in modules
+.TP
+.PD 0
+.B \-\-version
+.TP
+.PD 1
+.B \-v
+Version number
+.B \-\-prefix \fIpath\fP
+.TP
+.PD 1
+.B \-p
+Specify alternative prefix path (the default is @php_fpm_prefix@)
+.TP
+.PD 0
+.B \-\-fpm\-config \fIfile\fP
+.TP
+.PD 1
+.B \-y
+Specify alternative path to FastCGI process manager configuration file (the default is @php_fpm_sysconfdir@/php-fpm.conf)
+.TP
+.PD 0
+.B \-\-test
+.TP
+.PD 1
+.B \-t
+Test FPM configuration file and exit
+If called twice (-tt), the configuration is dumped before exiting.
+.TP
+.PD 0
+.B \-\-daemonize
+.TP
+.PD 1
+.B \-D
+Force to run in background and ignore daemonize option from configuration file.
+.TP
+.PD 0
+.B \-\-nodaemonize
+.TP
+.PD 1
+.B \-F
+Force to stay in foreground and ignore daemonize option from configuration file.
+.TP
+.PD 0
+.B \-\-zend\-extension \fIfile\fP
+.TP
+.PD 1
+.B \-z \fIfile\fP
+Load Zend extension
+.IR file
+.SH FILES
+.TP 15
+.B php-fpm.conf
+The configuration file for the php-fpm daemon.
+.TP
+.B php.ini
+The standard php configuration file.
+.SH EXAMPLES
+For any unix systems which use init.d for their main process manager, you should use the init script provided to start and stop the php-fpm daemon.
+.P
+.PD 1
+.RS
+sudo /etc/init.d/php-fpm start
+.RE
+.TP
+For any unix systems which use systemd for their main process manager, you should use the unit file provided to start and stop the php-fpm daemon.
+.P
+.PD 1
+.RS
+sudo systemctl start php-fpm.service
+.RE
+.TP
+If your installation has no appropriate init script, launch php-fpm with no arguments. It will launch as a daemon (background process) by default. The file @php_fpm_localstatedir@/run/php-fpm.pid determines whether php-fpm is already up and running. Once started, php-fpm then responds to several POSIX signals:
+.P
+.PD 0
+.RS
+.B SIGINT,SIGTERM \fPimmediate termination
+.TP
+.B SIGQUIT \fPgraceful stop
+.TP
+.B SIGUSR1 \fPre-open log file
+.TP
+.B SIGUSR2 \fPgraceful reload of all workers + reload of fpm conf/binary
+.RE
+.PD 1
+.P
+.SH TIPS
+The PHP-FPM CGI daemon will work well with most popular webservers, including Apache2, lighttpd and nginx.
+.PD 1
+.P
+.SH SEE ALSO
+The PHP-FPM website:
+.PD 0
+.P
+.B http://php-fpm.org
+.PD 1
+.P
+For a more or less complete description of PHP look here:
+.PD 0
+.P
+.B http://www.php.net/manual/
+.PD 1
+.P
+A nice introduction to PHP by Stig Bakken can be found here:
+.PD 0
+.P
+.B http://www.zend.com/zend/art/intro.php
+.PD 1
+.SH BUGS
+You can view the list of known bugs or report any new bug you
+found at:
+.PD 0
+.P
+.B http://bugs.php.net
+.PD 1
+.SH AUTHORS
+PHP-FPM SAPI was written by Andrei Nigmatulin. The mailing-lists are highload-php-en (English) and highload-php-ru (Russian).
+.P
+The PHP Group: Thies C. Arntzen, Stig Bakken, Andi Gutmans, Rasmus Lerdorf, Sam Ruby, Sascha Schumann, Zeev Suraski, Jim Winstead, Andrei Zmievski.
+.P
+A List of active developers can be found here:
+.PD 0
+.P
+.B http://www.php.net/credits.php
+.PD 1
+.P
+And last but not least PHP was developed with the help of a huge amount of
+contributors all around the world.
+.SH VERSION INFORMATION
+This manpage describes \fBphp-fpm\fP, version @PHP_VERSION@.
+.SH COPYRIGHT
+Copyright \(co 1997\-2009 The PHP Group
+.PD 0
+.P
+Copyright (c) 2007-2009, Andrei Nigmatulin
+.PD 1
+.LP
+This source file is subject to version 3.01 of the PHP license,
+that is bundled with this package in the file LICENSE, and is
+available through the world-wide-web at the following url:
+.PD 0
+.P
+.B http://www.php.net/license/3_01.txt
+.PD 1
+.P
+If you did not receive a copy of the PHP license and are unable to
+obtain it through the world-wide-web, please send a note to
+.B license@php.net
+so we can mail you a copy immediately.
diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in
new file mode 100644
index 0000000..a63dec7
--- /dev/null
+++ b/sapi/fpm/php-fpm.conf.in
@@ -0,0 +1,510 @@
+;;;;;;;;;;;;;;;;;;;;;
+; FPM Configuration ;
+;;;;;;;;;;;;;;;;;;;;;
+
+; All relative paths in this configuration file are relative to PHP's install
+; prefix (@prefix@). This prefix can be dynamicaly changed by using the
+; '-p' argument from the command line.
+
+; Include one or more files. If glob(3) exists, it is used to include a bunch of
+; files from a glob(3) pattern. This directive can be used everywhere in the
+; file.
+; Relative path can also be used. They will be prefixed by:
+; - the global prefix if it's been set (-p arguement)
+; - @prefix@ otherwise
+;include=etc/fpm.d/*.conf
+
+;;;;;;;;;;;;;;;;;;
+; Global Options ;
+;;;;;;;;;;;;;;;;;;
+
+[global]
+; Pid file
+; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
+; Default Value: none
+;pid = run/php-fpm.pid
+
+; Error log file
+; If it's set to "syslog", log is sent to syslogd instead of being written
+; in a local file.
+; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
+; Default Value: log/php-fpm.log
+;error_log = log/php-fpm.log
+
+; syslog_facility is used to specify what type of program is logging the
+; message. This lets syslogd specify that messages from different facilities
+; will be handled differently.
+; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON)
+; Default Value: daemon
+;syslog.facility = daemon
+
+; syslog_ident is prepended to every message. If you have multiple FPM
+; instances running on the same server, you can change the default value
+; which must suit common needs.
+; Default Value: php-fpm
+;syslog.ident = php-fpm
+
+; Log level
+; Possible Values: alert, error, warning, notice, debug
+; Default Value: notice
+;log_level = notice
+
+; If this number of child processes exit with SIGSEGV or SIGBUS within the time
+; interval set by emergency_restart_interval then FPM will restart. A value
+; of '0' means 'Off'.
+; Default Value: 0
+;emergency_restart_threshold = 0
+
+; Interval of time used by emergency_restart_interval to determine when
+; a graceful restart will be initiated. This can be useful to work around
+; accidental corruptions in an accelerator's shared memory.
+; Available Units: s(econds), m(inutes), h(ours), or d(ays)
+; Default Unit: seconds
+; Default Value: 0
+;emergency_restart_interval = 0
+
+; Time limit for child processes to wait for a reaction on signals from master.
+; Available units: s(econds), m(inutes), h(ours), or d(ays)
+; Default Unit: seconds
+; Default Value: 0
+;process_control_timeout = 0
+
+; The maximum number of processes FPM will fork. This has been design to control
+; the global number of processes when using dynamic PM within a lot of pools.
+; Use it with caution.
+; Note: A value of 0 indicates no limit
+; Default Value: 0
+; process.max = 128
+
+; Specify the nice(2) priority to apply to the master process (only if set)
+; The value can vary from -19 (highest priority) to 20 (lower priority)
+; Note: - It will only work if the FPM master process is launched as root
+; - The pool process will inherit the master process priority
+; unless it specified otherwise
+; Default Value: no set
+; process.priority = -19
+
+; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging.
+; Default Value: yes
+;daemonize = yes
+
+; Set open file descriptor rlimit for the master process.
+; Default Value: system defined value
+;rlimit_files = 1024
+
+; Set max core size rlimit for the master process.
+; Possible Values: 'unlimited' or an integer greater or equal to 0
+; Default Value: system defined value
+;rlimit_core = 0
+
+; Specify the event mechanism FPM will use. The following is available:
+; - select (any POSIX os)
+; - poll (any POSIX os)
+; - epoll (linux >= 2.5.44)
+; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
+; - /dev/poll (Solaris >= 7)
+; - port (Solaris >= 10)
+; Default Value: not set (auto detection)
+; events.mechanism = epoll
+
+;;;;;;;;;;;;;;;;;;;;
+; Pool Definitions ;
+;;;;;;;;;;;;;;;;;;;;
+
+; Multiple pools of child processes may be started with different listening
+; ports and different management options. The name of the pool will be
+; used in logs and stats. There is no limitation on the number of pools which
+; FPM can handle. Your system will tell you anyway :)
+
+; Start a new pool named 'www'.
+; the variable $pool can we used in any directive and will be replaced by the
+; pool name ('www' here)
+[www]
+
+; Per pool prefix
+; It only applies on the following directives:
+; - 'slowlog'
+; - 'listen' (unixsocket)
+; - 'chroot'
+; - 'chdir'
+; - 'php_values'
+; - 'php_admin_values'
+; When not set, the global prefix (or @php_fpm_prefix@) applies instead.
+; Note: This directive can also be relative to the global prefix.
+; Default Value: none
+;prefix = /path/to/pools/$pool
+
+; Unix user/group of processes
+; Note: The user is mandatory. If the group is not set, the default user's group
+; will be used.
+user = @php_fpm_user@
+group = @php_fpm_group@
+
+; The address on which to accept FastCGI requests.
+; Valid syntaxes are:
+; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on
+; a specific port;
+; 'port' - to listen on a TCP socket to all addresses on a
+; specific port;
+; '/path/to/unix/socket' - to listen on a unix socket.
+; Note: This value is mandatory.
+listen = 127.0.0.1:9000
+
+; Set listen(2) backlog.
+; Default Value: 128 (-1 on FreeBSD and OpenBSD)
+;listen.backlog = 128
+
+; Set permissions for unix socket, if one is used. In Linux, read/write
+; permissions must be set in order to allow connections from a web server. Many
+; BSD-derived systems allow connections regardless of permissions.
+; Default Values: user and group are set as the running user
+; mode is set to 0666
+;listen.owner = @php_fpm_user@
+;listen.group = @php_fpm_group@
+;listen.mode = 0666
+
+; List of ipv4 addresses of FastCGI clients which are allowed to connect.
+; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
+; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
+; must be separated by a comma. If this value is left blank, connections will be
+; accepted from any ip address.
+; Default Value: any
+;listen.allowed_clients = 127.0.0.1
+
+; Specify the nice(2) priority to apply to the pool processes (only if set)
+; The value can vary from -19 (highest priority) to 20 (lower priority)
+; Note: - It will only work if the FPM master process is launched as root
+; - The pool processes will inherit the master process priority
+; unless it specified otherwise
+; Default Value: no set
+; priority = -19
+
+; Choose how the process manager will control the number of child processes.
+; Possible Values:
+; static - a fixed number (pm.max_children) of child processes;
+; dynamic - the number of child processes are set dynamically based on the
+; following directives. With this process management, there will be
+; always at least 1 children.
+; pm.max_children - the maximum number of children that can
+; be alive at the same time.
+; pm.start_servers - the number of children created on startup.
+; pm.min_spare_servers - the minimum number of children in 'idle'
+; state (waiting to process). If the number
+; of 'idle' processes is less than this
+; number then some children will be created.
+; pm.max_spare_servers - the maximum number of children in 'idle'
+; state (waiting to process). If the number
+; of 'idle' processes is greater than this
+; number then some children will be killed.
+; ondemand - no children are created at startup. Children will be forked when
+; new requests will connect. The following parameter are used:
+; pm.max_children - the maximum number of children that
+; can be alive at the same time.
+; pm.process_idle_timeout - The number of seconds after which
+; an idle process will be killed.
+; Note: This value is mandatory.
+pm = dynamic
+
+; The number of child processes to be created when pm is set to 'static' and the
+; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
+; This value sets the limit on the number of simultaneous requests that will be
+; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
+; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
+; CGI. The below defaults are based on a server without much resources. Don't
+; forget to tweak pm.* to fit your needs.
+; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
+; Note: This value is mandatory.
+pm.max_children = 5
+
+; The number of child processes created on startup.
+; Note: Used only when pm is set to 'dynamic'
+; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
+pm.start_servers = 2
+
+; The desired minimum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.min_spare_servers = 1
+
+; The desired maximum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.max_spare_servers = 3
+
+; The number of seconds after which an idle process will be killed.
+; Note: Used only when pm is set to 'ondemand'
+; Default Value: 10s
+;pm.process_idle_timeout = 10s;
+
+; The number of requests each child process should execute before respawning.
+; This can be useful to work around memory leaks in 3rd party libraries. For
+; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
+; Default Value: 0
+;pm.max_requests = 500
+
+; The URI to view the FPM status page. If this value is not set, no URI will be
+; recognized as a status page. It shows the following informations:
+; pool - the name of the pool;
+; process manager - static, dynamic or ondemand;
+; start time - the date and time FPM has started;
+; start since - number of seconds since FPM has started;
+; accepted conn - the number of request accepted by the pool;
+; listen queue - the number of request in the queue of pending
+; connections (see backlog in listen(2));
+; max listen queue - the maximum number of requests in the queue
+; of pending connections since FPM has started;
+; listen queue len - the size of the socket queue of pending connections;
+; idle processes - the number of idle processes;
+; active processes - the number of active processes;
+; total processes - the number of idle + active processes;
+; max active processes - the maximum number of active processes since FPM
+; has started;
+; max children reached - number of times, the process limit has been reached,
+; when pm tries to start more children (works only for
+; pm 'dynamic' and 'ondemand');
+; Value are updated in real time.
+; Example output:
+; pool: www
+; process manager: static
+; start time: 01/Jul/2011:17:53:49 +0200
+; start since: 62636
+; accepted conn: 190460
+; listen queue: 0
+; max listen queue: 1
+; listen queue len: 42
+; idle processes: 4
+; active processes: 11
+; total processes: 15
+; max active processes: 12
+; max children reached: 0
+;
+; By default the status page output is formatted as text/plain. Passing either
+; 'html', 'xml' or 'json' in the query string will return the corresponding
+; output syntax. Example:
+; http://www.foo.bar/status
+; http://www.foo.bar/status?json
+; http://www.foo.bar/status?html
+; http://www.foo.bar/status?xml
+;
+; By default the status page only outputs short status. Passing 'full' in the
+; query string will also return status for each pool process.
+; Example:
+; http://www.foo.bar/status?full
+; http://www.foo.bar/status?json&full
+; http://www.foo.bar/status?html&full
+; http://www.foo.bar/status?xml&full
+; The Full status returns for each process:
+; pid - the PID of the process;
+; state - the state of the process (Idle, Running, ...);
+; start time - the date and time the process has started;
+; start since - the number of seconds since the process has started;
+; requests - the number of requests the process has served;
+; request duration - the duration in µs of the requests;
+; request method - the request method (GET, POST, ...);
+; request URI - the request URI with the query string;
+; content length - the content length of the request (only with POST);
+; user - the user (PHP_AUTH_USER) (or '-' if not set);
+; script - the main script called (or '-' if not set);
+; last request cpu - the %cpu the last request consumed
+; it's always 0 if the process is not in Idle state
+; because CPU calculation is done when the request
+; processing has terminated;
+; last request memory - the max amount of memory the last request consumed
+; it's always 0 if the process is not in Idle state
+; because memory calculation is done when the request
+; processing has terminated;
+; If the process is in Idle state, then informations are related to the
+; last request the process has served. Otherwise informations are related to
+; the current request being served.
+; Example output:
+; ************************
+; pid: 31330
+; state: Running
+; start time: 01/Jul/2011:17:53:49 +0200
+; start since: 63087
+; requests: 12808
+; request duration: 1250261
+; request method: GET
+; request URI: /test_mem.php?N=10000
+; content length: 0
+; user: -
+; script: /home/fat/web/docs/php/test_mem.php
+; last request cpu: 0.00
+; last request memory: 0
+;
+; Note: There is a real-time FPM status monitoring sample web page available
+; It's available in: @EXPANDED_DATADIR@/fpm/status.html
+;
+; Note: The value must start with a leading slash (/). The value can be
+; anything, but it may not be a good idea to use the .php extension or it
+; may conflict with a real PHP file.
+; Default Value: not set
+;pm.status_path = /status
+
+; The ping URI to call the monitoring page of FPM. If this value is not set, no
+; URI will be recognized as a ping page. This could be used to test from outside
+; that FPM is alive and responding, or to
+; - create a graph of FPM availability (rrd or such);
+; - remove a server from a group if it is not responding (load balancing);
+; - trigger alerts for the operating team (24/7).
+; Note: The value must start with a leading slash (/). The value can be
+; anything, but it may not be a good idea to use the .php extension or it
+; may conflict with a real PHP file.
+; Default Value: not set
+;ping.path = /ping
+
+; This directive may be used to customize the response of a ping request. The
+; response is formatted as text/plain with a 200 response code.
+; Default Value: pong
+;ping.response = pong
+
+; The access log file
+; Default: not set
+;access.log = log/$pool.access.log
+
+; The access log format.
+; The following syntax is allowed
+; %%: the '%' character
+; %C: %CPU used by the request
+; it can accept the following format:
+; - %{user}C for user CPU only
+; - %{system}C for system CPU only
+; - %{total}C for user + system CPU (default)
+; %d: time taken to serve the request
+; it can accept the following format:
+; - %{seconds}d (default)
+; - %{miliseconds}d
+; - %{mili}d
+; - %{microseconds}d
+; - %{micro}d
+; %e: an environment variable (same as $_ENV or $_SERVER)
+; it must be associated with embraces to specify the name of the env
+; variable. Some exemples:
+; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
+; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
+; %f: script filename
+; %l: content-length of the request (for POST request only)
+; %m: request method
+; %M: peak of memory allocated by PHP
+; it can accept the following format:
+; - %{bytes}M (default)
+; - %{kilobytes}M
+; - %{kilo}M
+; - %{megabytes}M
+; - %{mega}M
+; %n: pool name
+; %o: ouput header
+; it must be associated with embraces to specify the name of the header:
+; - %{Content-Type}o
+; - %{X-Powered-By}o
+; - %{Transfert-Encoding}o
+; - ....
+; %p: PID of the child that serviced the request
+; %P: PID of the parent of the child that serviced the request
+; %q: the query string
+; %Q: the '?' character if query string exists
+; %r: the request URI (without the query string, see %q and %Q)
+; %R: remote IP address
+; %s: status (response code)
+; %t: server time the request was received
+; it can accept a strftime(3) format:
+; %d/%b/%Y:%H:%M:%S %z (default)
+; %T: time the log has been written (the request has finished)
+; it can accept a strftime(3) format:
+; %d/%b/%Y:%H:%M:%S %z (default)
+; %u: remote user
+;
+; Default: "%R - %u %t \"%m %r\" %s"
+;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
+
+; The log file for slow requests
+; Default Value: not set
+; Note: slowlog is mandatory if request_slowlog_timeout is set
+;slowlog = log/$pool.log.slow
+
+; The timeout for serving a single request after which a PHP backtrace will be
+; dumped to the 'slowlog' file. A value of '0s' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+;request_slowlog_timeout = 0
+
+; The timeout for serving a single request after which the worker process will
+; be killed. This option should be used when the 'max_execution_time' ini option
+; does not stop script execution for some reason. A value of '0' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+;request_terminate_timeout = 0
+
+; Set open file descriptor rlimit.
+; Default Value: system defined value
+;rlimit_files = 1024
+
+; Set max core size rlimit.
+; Possible Values: 'unlimited' or an integer greater or equal to 0
+; Default Value: system defined value
+;rlimit_core = 0
+
+; Chroot to this directory at the start. This value must be defined as an
+; absolute path. When this value is not set, chroot is not used.
+; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
+; of its subdirectories. If the pool prefix is not set, the global prefix
+; will be used instead.
+; Note: chrooting is a great security feature and should be used whenever
+; possible. However, all PHP paths will be relative to the chroot
+; (error_log, sessions.save_path, ...).
+; Default Value: not set
+;chroot =
+
+; Chdir to this directory at the start.
+; Note: relative path can be used.
+; Default Value: current directory or / when chroot
+;chdir = /var/www
+
+; Redirect worker stdout and stderr into main error log. If not set, stdout and
+; stderr will be redirected to /dev/null according to FastCGI specs.
+; Note: on highloaded environement, this can cause some delay in the page
+; process time (several ms).
+; Default Value: no
+;catch_workers_output = yes
+
+; Limits the extensions of the main script FPM will allow to parse. This can
+; prevent configuration mistakes on the web server side. You should only limit
+; FPM to .php extensions to prevent malicious users to use other extensions to
+; exectute php code.
+; Note: set an empty value to allow all extensions.
+; Default Value: .php
+;security.limit_extensions = .php .php3 .php4 .php5
+
+; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
+; the current environment.
+; Default Value: clean env
+;env[HOSTNAME] = $HOSTNAME
+;env[PATH] = /usr/local/bin:/usr/bin:/bin
+;env[TMP] = /tmp
+;env[TMPDIR] = /tmp
+;env[TEMP] = /tmp
+
+; Additional php.ini defines, specific to this pool of workers. These settings
+; overwrite the values previously defined in the php.ini. The directives are the
+; same as the PHP SAPI:
+; php_value/php_flag - you can set classic ini defines which can
+; be overwritten from PHP call 'ini_set'.
+; php_admin_value/php_admin_flag - these directives won't be overwritten by
+; PHP call 'ini_set'
+; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
+
+; Defining 'extension' will load the corresponding shared extension from
+; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
+; overwrite previously defined php.ini values, but will append the new value
+; instead.
+
+; Note: path INI options can be relative and will be expanded with the prefix
+; (pool, global or @prefix@)
+
+; Default Value: nothing is defined by default except the values in php.ini and
+; specified at startup with the -d argument
+;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
+;php_flag[display_errors] = off
+;php_admin_value[error_log] = /var/log/fpm-php.www.log
+;php_admin_flag[log_errors] = on
+;php_admin_value[memory_limit] = 32M
diff --git a/sapi/fpm/php-fpm.service.in b/sapi/fpm/php-fpm.service.in
new file mode 100644
index 0000000..396a88d
--- /dev/null
+++ b/sapi/fpm/php-fpm.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=The PHP FastCGI Process Manager
+After=syslog.target network.target
+
+[Service]
+PIDFile=@localstatedir@/run/php-fpm.pid
+ExecStart=@sbindir@/php-fpm --nodaemonize --fpm-config @sysconfdir@/php-fpm.conf
+ExecReload=/bin/kill -USR2 $MAINPID
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/sapi/fpm/status.html.in b/sapi/fpm/status.html.in
new file mode 100644
index 0000000..86492d7
--- /dev/null
+++ b/sapi/fpm/status.html.in
@@ -0,0 +1,459 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+ $Id$
+ (c) 2011 Jerome Loyet
+ The PHP License, version 3.01
+ This is sample real-time status page for FPM. You can change it to better feet your needs.
+-->
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <style type="text/css">
+ body {background-color: #ffffff; color: #000000;}
+ body, td, th, h1, h2 {font-family: sans-serif;}
+ pre {margin: 0px; font-family: monospace;}
+ a:link {color: #000099; text-decoration: none; background-color: #ffffff;}
+ a:hover {text-decoration: underline;}
+ table {border-collapse: collapse;}
+ .center {text-align: center;}
+ .center table { margin-left: auto; margin-right: auto; text-align: left;}
+ .center th { text-align: center !important; }
+ td, th { border: 1px solid #000000; font-size: 75%; vertical-align: baseline;}
+ h1 {font-size: 150%;}
+ h2 {font-size: 125%;}
+ .p {text-align: left;}
+ .e {background-color: #ccccff; font-weight: bold; color: #000000;}
+ .h {background-color: #9999cc; font-weight: bold; color: #000000;}
+
+ .v {background-color: #cccccc; color: #000000;}
+ .w {background-color: #ccccff; color: #000000;}
+
+ .h th {
+ cursor: pointer;
+ }
+ img {float: right; border: 0px;}
+ hr {width: 600px; background-color: #cccccc; border: 0px; height: 1px; color: #000000;}
+ </style>
+ <title>PHP-FPM status page</title>
+ <meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>
+ <body>
+ <div class="center">
+ <table border="0" cellpadding="3" width="95%">
+ <tr class="h">
+ <td>
+ <a href="http://www.php.net/"><img border="0" src="https://static.php.net/www.php.net/images/php.gif" alt="PHP Logo" /></a><h1 class="p">PHP-FPM real-time status page</h1>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <table border="0" cellpadding="3" width="95%">
+ <tr><td class="e">Status URL</td><td class="v"><input type="text" id="url" size="45" /></td></tr>
+ <tr><td class="e">Ajax status</td><td class="v" id="status"></td></tr>
+ <tr><td class="e">Refresh Rate</td><td class="v"><input type="text" id="rate" value="1" /></td></tr>
+ <tr>
+ <td class="e">Actions</td>
+ <td class="v">
+ <button onclick="javascript:refresh();">Manual Refresh</button>
+ <button id="play" onclick="javascript:playpause();">Play</button>
+ </td>
+ </tr>
+ </table>
+ <h1>Pool Status</h1>
+ <table border="0" cellpadding="3" width="95%" id="short">
+ <tr style="display: none;"><td>&nbsp;</td></tr>
+ </table>
+ <h1>Active Processes status</h1>
+ <table border="0" cellpadding="3" width="95%" id="active">
+ <tr class="h"><th>PID&darr;</th><th>Start Time</th><th>Start Since</th><th>Requests Served</th><th>Request Duration</th><th>Request method</th><th>Request URI</th><th>Content Length</th><th>User</th><th>Script</th></tr>
+ </table>
+ <h1>Idle Processes status</h1>
+ <table border="0" cellpadding="3" width="95%" id="idle">
+ <tr class="h"><th>PID&darr;</th><th>Start Time</th><th>Start Since</th><th>Requests Served</th><th>Request Duration</th><th>Request method</th><th>Request URI</th><th>Content Length</th><th>User</th><th>Script</th><th>Last Request %CPU</th><th>Last Request Memory</th></tr>
+ </table>
+ </div>
+ <p>
+ <a href="http://validator.w3.org/check?uri=referer">
+ <img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0 Transitional" height="31" width="88" />
+ </a>
+ </p>
+ <script type="text/javascript">
+<!--
+ var xhr_object = null;
+ var doc_url = document.getElementById("url");
+ var doc_rate = document.getElementById("rate");
+ var doc_status = document.getElementById("status");
+ var doc_play = document.getElementById("play");
+ var doc_short = document.getElementById("short");
+ var doc_active = document.getElementById("active");
+ var doc_idle = document.getElementById("idle");
+ var rate = 0;
+ var play=0;
+ var delay = 1000;
+ var order_active_index = 0;
+ var order_active_reverse = 0;
+ var order_idle_index = 0;
+ var order_idle_reverse = 0;
+ var sort_index;
+ var sort_order;
+
+ doc_url.value = location.protocol + '//' + location.host + "/status?json&full";
+
+ ths = document.getElementsByTagName("th");
+ for (var i=0; i<ths.length; i++) {
+ var th = ths[i];
+ if (th.parentNode.className == "h") {
+ th.onclick = function() { order(this); return false; };
+ }
+ }
+
+ xhr_object = create_ajax();
+
+ function create_ajax() {
+ if (window.XMLHttpRequest) {
+ return new XMLHttpRequest();
+ }
+ var names = [
+ "Msxml2.XMLHTTP.6.0",
+ "Msxml2.XMLHTTP.3.0",
+ "Msxml2.XMLHTTP",
+ "Microsoft.XMLHTTP"
+ ];
+ for(var i in names)
+ {
+ try {
+ return new ActiveXObject(names[i]);
+ } catch(e){}
+ }
+ alert("Browser not compatible ...");
+ }
+
+ function order(cell) {
+ var table;
+
+ if (cell.constructor != HTMLTableCellElement && cell.constructor != HTMLTableHeaderCellElement) {
+ return;
+ }
+
+ table = cell.parentNode.parentNode.parentNode;
+
+ if (table == doc_active) {
+ if (order_active_index == cell.cellIndex) {
+ if (order_active_reverse == 0) {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&uarr;");
+ order_active_reverse = 1;
+ } else {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&darr;");
+ order_active_reverse = 0;
+ }
+ } else {
+ var c = doc_active.rows[0].cells[order_active_index];
+ c.innerHTML = c.innerHTML.replace(/.$/, "");
+ cell.innerHTML = cell.innerHTML.replace(/$/, order_active_reverse == 0 ? "&darr;" : "&uarr;");
+ order_active_index = cell.cellIndex;
+ }
+ reorder(table, order_active_index, order_active_reverse);
+ return;
+ }
+
+ if (table == doc_idle) {
+ if (order_idle_index == cell.cellIndex) {
+ if (order_idle_reverse == 0) {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&uarr;");
+ order_idle_reverse = 1;
+ } else {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&darr;");
+ order_idle_reverse = 0;
+ }
+ } else {
+ var c = doc_idle.rows[0].cells[order_idle_index];
+ c.innerHTML = c.innerHTML.replace(/.$/, "");
+ cell.innerHTML = cell.innerHTML.replace(/$/, order_idle_reverse == 0 ? "&darr;" : "&uarr;");
+ order_idle_index = cell.cellIndex;
+ }
+ reorder(table, order_idle_index, order_idle_reverse);
+ return;
+ }
+ }
+
+ function reorder(table, index, order) {
+ var rows = [];
+ while (table.rows.length > 1) {
+ rows.push(table.rows[1]);
+ table.deleteRow(1);
+ }
+ sort_index = index;
+ sort_order = order;
+ rows.sort(sort_table);
+ for (var i in rows) {
+ table.appendChild(rows[i]);
+ }
+ var odd = 1;
+ for (var i=1; i<table.rows.length; i++) {
+ table.rows[i].className = odd++ % 2 == 0 ? "v" : "w";
+ }
+ return;
+ }
+
+ function sort_table(a, b) {
+ if (a.cells[0].tagName == "TH") return -1;
+ if (b.cells[0].tagName == "TH") return 1;
+
+ if (a.cells[sort_index].__search_t == 0) { /* integer */
+ if (!sort_order) return a.cells[sort_index].__search_v - b.cells[sort_index].__search_v;
+ return b.cells[sort_index].__search_v - a.cells[sort_index].__search_v;;
+ }
+
+ /* string */
+ if (!sort_order) return a.cells[sort_index].__search_v.localeCompare(b.cells[sort_index].__search_v);
+ else return b.cells[sort_index].__search_v.localeCompare(a.cells[sort_index].__search_v);
+ }
+
+ function playpause() {
+ rate = 0;
+ if (play) {
+ play = 0;
+ doc_play.innerHTML = "Play";
+ doc_rate.disabled = false;
+ } else {
+ delay = parseInt(doc_rate.value);
+ if (!delay || delay < 1) {
+ doc_status.innerHTML = "Not valid 'refresh' value";
+ return;
+ }
+ play = 1;
+ doc_rate.disabled = true;
+ doc_play.innerHTML = "Pause";
+ setTimeout("callback()", delay * 1000);
+ }
+ }
+
+ function refresh() {
+ if (xhr_object == null) return;
+ if (xhr_object.readyState > 0 && xhr_object.readyState < 4) {
+ return; /* request is running */
+ }
+ xhr_object.open("GET", doc_url.value, true);
+ xhr_object.onreadystatechange = function() {
+ switch(xhr_object.readyState) {
+ case 0:
+ doc_status.innerHTML = "uninitialized";
+ break;
+ case 1:
+ doc_status.innerHTML = "loading ...";
+ break;
+ case 2:
+ doc_status.innerHTML = "loaded";
+ break;
+ case 3:
+ doc_status.innerHTML = "interactive";
+ break;
+ case 4:
+ doc_status.innerHTML = "complete";
+ if (xhr_object.status == 200) {
+ fpm_status(xhr_object.responseText);
+ } else {
+ doc_status.innerHTML = "Error " + xhr_object.status;
+ }
+ break;
+ }
+ }
+ xhr_object.send();
+ }
+
+ function callback() {
+ if (!play) return;
+ refresh();
+ setTimeout("callback()", delay * 1000);
+ }
+
+ function fpm_status(txt) {
+ var json = null;
+
+ while (doc_short.rows.length > 0) {
+ doc_short.deleteRow(0);
+ }
+
+ while (doc_active.rows.length > 1) {
+ doc_active.deleteRow(1);
+ }
+
+ while (doc_idle.rows.length > 1) {
+ doc_idle.deleteRow(1);
+ }
+
+ try {
+ json = JSON.parse(txt);
+ } catch (e) {
+ doc_status.innerHTML = "Error while parsing json: '" + e + "': <br /><pre>" + txt + "</pre>";
+ return;
+ }
+
+ for (var key in json) {
+ if (key == "processes") continue;
+ if (key == "state") continue;
+ var row = doc_short.insertRow(doc_short.rows.length);
+ var value = json[key];
+ if (key == "start time") {
+ value = new Date(value * 1000).toLocaleString();
+ }
+ if (key == "start since") {
+ value = time_s(value);
+ }
+ var cell = row.insertCell(row.cells.length);
+ cell.className = "e";
+ cell.innerHTML = key;
+
+ cell = row.insertCell(row.cells.length);
+ cell.className = "v";
+ cell.innerHTML = value;
+ }
+
+ if (json.processes) {
+ process_full(json.processes, doc_active, "Idle", 0, 0);
+ reorder(doc_active, order_active_index, order_active_reverse);
+
+ process_full(json.processes, doc_idle, "Idle", 1, 1);
+ reorder(doc_idle, order_idle_index, order_idle_reverse);
+ }
+ }
+
+ function process_full(processes, table, state, equal, cpumem) {
+ var odd = 1;
+
+ for (var i in processes) {
+ var proc = processes[i];
+ if ((equal && proc.state == state) || (!equal && proc.state != state)) {
+ var c = odd++ % 2 == 0 ? "v" : "w";
+ var row = table.insertRow(-1);
+ row.className = c;
+ row.insertCell(-1).innerHTML = proc.pid;
+ row.cells[row.cells.length - 1].__search_v = proc.pid;
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = date(proc['start time'] * 1000);;
+ row.cells[row.cells.length - 1].__search_v = proc['start time'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = time_s(proc['start since']);
+ row.cells[row.cells.length - 1].__search_v = proc['start since'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = proc.requests;
+ row.cells[row.cells.length - 1].__search_v = proc.requests;
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = time_u(proc['request duration']);
+ row.cells[row.cells.length - 1].__search_v = proc['request duration'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = proc['request method'];
+ row.cells[row.cells.length - 1].__search_v = proc['request method'];
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ row.insertCell(-1).innerHTML = proc['request uri'];
+ row.cells[row.cells.length - 1].__search_v = proc['request uri'];
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ row.insertCell(-1).innerHTML = proc['content length'];
+ row.cells[row.cells.length - 1].__search_v = proc['content length'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = proc.user;
+ row.cells[row.cells.length - 1].__search_v = proc.user;
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ row.insertCell(-1).innerHTML = proc.script;
+ row.cells[row.cells.length - 1].__search_v = proc.script;
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ if (cpumem) {
+ row.insertCell(-1).innerHTML = cpu(proc['last request cpu']);
+ row.cells[row.cells.length - 1].__search_v = proc['last request cpu'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = memory(proc['last request memory']);
+ row.cells[row.cells.length - 1].__search_v = proc['last request memory'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+ }
+ }
+ }
+ }
+
+ function date(d) {
+ var t = new Date(d);
+ var r = "";
+
+ r += (t.getDate() < 10 ? '0' : '') + t.getDate();
+ r += '/';
+ r += (t.getMonth() + 1 < 10 ? '0' : '') + (t.getMonth() + 1);
+ r += '/';
+ r += t.getFullYear();
+ r += ' ';
+ r += (t.getHours() < 10 ? '0' : '') + t.getHours();
+ r += ':';
+ r += (t.getMinutes() < 10 ? '0' : '') + t.getMinutes();
+ r += ':';
+ r += (t.getSeconds() < 10 ? '0' : '') + t.getSeconds();
+
+
+ return r;
+ }
+
+ function cpu(c) {
+ if (c == 0) return 0;
+ return c + "%";
+ }
+
+ function memory(mem) {
+ if (mem == 0) return 0;
+ if (mem < 1024) {
+ return mem + "B";
+ }
+ if (mem < 1024 * 1024) {
+ return mem/1024 + "KB";
+ }
+ if (mem < 1024*1024*1024) {
+ return mem/1024/1024 + "MB";
+ }
+ }
+
+ function time_s(t) {
+ var r = "";
+ if (t < 60) {
+ return t + 's';
+ }
+
+ r = (t % 60) + 's';
+ t = Math.floor(t / 60);
+ if (t < 60) {
+ return t + 'm ' + r;
+ }
+
+ r = (t % 60) + 'm ' + r;
+ t = Math.floor(t/60);
+
+ if (t < 24) {
+ return t + 'h ' + r;
+ }
+
+ return Math.floor(t/24) + 'd ' + (t % 24) + 'h ' + t;
+ }
+
+ function time_u(t) {
+ var r = "";
+ if (t < 1000) {
+ return t + '&micro;s'
+ }
+
+ r = (t % 1000) + '&micro;s';
+ t = Math.floor(t / 1000);
+ if (t < 1000) {
+ return t + 'ms ' + r;
+ }
+
+ return time_s(Math.floor(t/1000)) + ' ' + (t%1000) + 'ms ' + r;
+ }
+-->
+ </script>
+ </body>
+</html>