Browse Source

re-add updated FPM

experimental/lemon
Antony Dovgal 16 years ago
parent
commit
d77d6153ec
  1. 2
      sapi/fpm/CREDITS
  2. 23
      sapi/fpm/LICENSE
  3. 25
      sapi/fpm/Makefile.frag
  4. 613
      sapi/fpm/config.m4
  5. 1321
      sapi/fpm/fpm/fastcgi.c
  6. 151
      sapi/fpm/fpm/fastcgi.h
  7. 84
      sapi/fpm/fpm/fpm.c
  8. 30
      sapi/fpm/fpm/fpm.h
  9. 116
      sapi/fpm/fpm/fpm_arrays.h
  10. 148
      sapi/fpm/fpm/fpm_atomic.h
  11. 439
      sapi/fpm/fpm/fpm_children.c
  12. 35
      sapi/fpm/fpm/fpm_children.h
  13. 53
      sapi/fpm/fpm/fpm_cleanup.c
  14. 21
      sapi/fpm/fpm/fpm_cleanup.h
  15. 121
      sapi/fpm/fpm/fpm_clock.c
  16. 13
      sapi/fpm/fpm/fpm_clock.h
  17. 658
      sapi/fpm/fpm/fpm_conf.c
  18. 78
      sapi/fpm/fpm/fpm_conf.h
  19. 44
      sapi/fpm/fpm/fpm_config.h
  20. 183
      sapi/fpm/fpm/fpm_env.c
  21. 24
      sapi/fpm/fpm/fpm_env.h
  22. 142
      sapi/fpm/fpm/fpm_events.c
  23. 16
      sapi/fpm/fpm/fpm_events.h
  24. 1915
      sapi/fpm/fpm/fpm_main.c
  25. 198
      sapi/fpm/fpm/fpm_php.c
  26. 23
      sapi/fpm/fpm/fpm_php.h
  27. 175
      sapi/fpm/fpm/fpm_php_trace.c
  28. 13
      sapi/fpm/fpm/fpm_php_trace.h
  29. 463
      sapi/fpm/fpm/fpm_process_ctl.c
  30. 45
      sapi/fpm/fpm/fpm_process_ctl.h
  31. 169
      sapi/fpm/fpm/fpm_request.c
  32. 28
      sapi/fpm/fpm/fpm_request.h
  33. 101
      sapi/fpm/fpm/fpm_shm.c
  34. 23
      sapi/fpm/fpm/fpm_shm.h
  35. 119
      sapi/fpm/fpm/fpm_shm_slots.c
  36. 43
      sapi/fpm/fpm/fpm_shm_slots.h
  37. 251
      sapi/fpm/fpm/fpm_signals.c
  38. 16
      sapi/fpm/fpm/fpm_signals.h
  39. 379
      sapi/fpm/fpm/fpm_sockets.c
  40. 40
      sapi/fpm/fpm/fpm_sockets.h
  41. 282
      sapi/fpm/fpm/fpm_status.c
  42. 32
      sapi/fpm/fpm/fpm_status.h
  43. 270
      sapi/fpm/fpm/fpm_stdio.c
  44. 20
      sapi/fpm/fpm/fpm_stdio.h
  45. 52
      sapi/fpm/fpm/fpm_str.h
  46. 41
      sapi/fpm/fpm/fpm_trace.c
  47. 17
      sapi/fpm/fpm/fpm_trace.h
  48. 99
      sapi/fpm/fpm/fpm_trace_mach.c
  49. 67
      sapi/fpm/fpm/fpm_trace_pread.c
  50. 82
      sapi/fpm/fpm/fpm_trace_ptrace.c
  51. 261
      sapi/fpm/fpm/fpm_unix.c
  52. 17
      sapi/fpm/fpm/fpm_unix.h
  53. 72
      sapi/fpm/fpm/fpm_worker_pool.c
  54. 50
      sapi/fpm/fpm/fpm_worker_pool.h
  55. 275
      sapi/fpm/fpm/xml_config.c
  56. 47
      sapi/fpm/fpm/xml_config.h
  57. 113
      sapi/fpm/fpm/zlog.c
  58. 34
      sapi/fpm/fpm/zlog.h
  59. 138
      sapi/fpm/init.d.php-fpm.in
  60. 186
      sapi/fpm/php-fpm.1.in
  61. 206
      sapi/fpm/php-fpm.conf.in

2
sapi/fpm/CREDITS

@ -0,0 +1,2 @@
FastCGI Process Manager
Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet

23
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.

25
sapi/fpm/Makefile.frag

@ -0,0 +1,25 @@
fpm: $(SAPI_FPM_PATH)
$(SAPI_FPM_PATH): $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(SAPI_EXTRA_DEPS)
$(BUILD_FPM)
$(builddir)/fpm/fpm_conf.lo: $(builddir)/../../main/build-defs.h
install-build: install-fpm
install-fpm: install-sapi
@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)/man1/"
@$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man1
@$(INSTALL_DATA) sapi/fpm/php-fpm.1 $(INSTALL_ROOT)$(mandir)/man1/php-fpm$(program_suffix).1

613
sapi/fpm/config.m4

@ -0,0 +1,613 @@
dnl
dnl $Id$
dnl
fpm_version="0.6.5"
minimum_libevent_version="1.4.11"
PHP_ARG_ENABLE(fpm,,
[ --enable-fpm EXPERIMENTAL: Enable building of the fpm SAPI executable], no, no)
if test -z "$PHP_LIBXML_DIR"; then
PHP_ARG_WITH(libxml-dir, libxml2 install dir,
[ --with-libxml-dir=DIR FPM: libxml2 install prefix], no, no)
fi
dnl libevent check function {{{
dnl @synopsis AC_LIB_EVENT([MINIMUM-VERSION])
dnl
dnl Test for the libevent library of a particular version (or newer).
dnl Source: http://svn.apache.org/repos/asf/incubator/thrift/trunk/aclocal/ax_lib_event.m4
dnl Modified: This file was modified for autoconf-2.13 and the PHP_ARG_WITH macro.
dnl
dnl If no path to the installed libevent is given, the macro will first try
dnl using no -I or -L flags, then searches under /usr, /usr/local, /opt,
dnl and /opt/libevent.
dnl If these all fail, it will try the $LIBEVENT_ROOT environment variable.
dnl
dnl This macro requires that #include <sys/types.h> works and defines u_char.
dnl
dnl This macro calls:
dnl AC_SUBST(LIBEVENT_CFLAGS)
dnl AC_SUBST(LIBEVENT_LIBS)
dnl
dnl And (if libevent is found):
dnl AC_DEFINE(HAVE_LIBEVENT)
dnl
dnl It also leaves the shell variables "success" and "ac_have_libevent"
dnl set to "yes" or "no".
dnl
dnl NOTE: This macro does not currently work for cross-compiling,
dnl but it can be easily modified to allow it. (grep "cross").
dnl
dnl @category InstalledPackages
dnl @category C
dnl @version 2007-09-12
dnl @license AllPermissive
dnl
dnl Copyright (C) 2009 David Reiss
dnl Copying and distribution of this file, with or without modification,
dnl are permitted in any medium without royalty provided the copyright
dnl notice and this notice are preserved.
AC_DEFUN([AC_LIB_EVENT_DO_CHECK],
[
# Save our flags.
CPPFLAGS_SAVED="$CPPFLAGS"
LDFLAGS_SAVED="$LDFLAGS"
LIBS_SAVED="$LIBS"
LD_LIBRARY_PATH_SAVED="$LD_LIBRARY_PATH"
# Set our flags if we are checking a specific directory.
if test -n "$ac_libevent_path" ; then
LIBEVENT_CPPFLAGS="-I$ac_libevent_path/include"
if test -z "$PHP_LIBDIR"; then
LIBEVENT_LDFLAGS="-L$ac_libevent_path/lib"
else
LIBEVENT_LDFLAGS="-L$ac_libevent_path/$PHP_LIBDIR"
fi
LD_LIBRARY_PATH="$ac_libevent_path/lib:$LD_LIBRARY_PATH"
else
LIBEVENT_CPPFLAGS=""
LIBEVENT_LDFLAGS=""
fi
# Required flag for libevent.
LIBEVENT_LIBS="-levent"
# Prepare the environment for compilation.
CPPFLAGS="$CPPFLAGS $LIBEVENT_CPPFLAGS"
LDFLAGS="$LDFLAGS $LIBEVENT_LDFLAGS"
LIBS="$LIBS $LIBEVENT_LIBS"
export CPPFLAGS
export LDFLAGS
export LIBS
export LD_LIBRARY_PATH
success=no
# Compile, link, and run the program. This checks:
# - event.h is available for including.
# - event_get_version() is available for linking.
# - The event version string is lexicographically greater
# than the required version.
AC_TRY_RUN([
#include <sys/types.h>
#include <event.h>
int main(int argc, char *argv[])
{
const char* lib_version = event_get_version();
const char* wnt_version = "$WANT_LIBEVENT_VERSION";
for (;;) {
/* If we reached the end of the want version. We have it. */
if (*wnt_version == '\0' || *wnt_version == '-') {
return 0;
}
/* If the want version continues but the lib version does not, */
/* we are missing a letter. We don't have it. */
if (*lib_version == '\0' || *lib_version == '-') {
return 1;
}
/* In the 1.4 version numbering style, if there are more digits */
/* in one version than the other, that one is higher. */
int lib_digits;
for (lib_digits = 0;
lib_version[lib_digits] >= '0' &&
lib_version[lib_digits] <= '9';
lib_digits++)
;
int wnt_digits;
for (wnt_digits = 0;
wnt_version[wnt_digits] >= '0' &&
wnt_version[wnt_digits] <= '9';
wnt_digits++)
;
if (lib_digits > wnt_digits) {
return 0;
}
if (lib_digits < wnt_digits) {
return 1;
}
/* If we have greater than what we want. We have it. */
if (*lib_version > *wnt_version) {
return 0;
}
/* If we have less, we don't. */
if (*lib_version < *wnt_version) {
return 1;
}
lib_version++;
wnt_version++;
}
return 0;
}
],[
success=yes
])
# Restore flags.
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
LIBS="$LIBS_SAVED"
LD_LIBRARY_PATH="$LD_LIBRARY_PATH_SAVED"
])
AC_DEFUN([AC_LIB_EVENT],
[
PHP_ARG_WITH(libevent-dir,,
[ --with-libevent-dir[=PATH] libevent install prefix, for fpm SAPI. (default: /usr/local)], /usr/local, yes)
if test "$PHP_LIBEVENT_DIR" != "no"; then
WANT_LIBEVENT_VERSION=ifelse([$1], ,1.2,$1)
AC_MSG_CHECKING(for libevent >= $WANT_LIBEVENT_VERSION install prefix)
libevent_prefix=$ac_default_prefix
if test $prefix != "NONE" -a $prefix != "" -a $prefix != "no" ; then
libevent_prefix=$prefix
fi
if test "$PHP_LIBEVENT_DIR" = "yes"; then
PHP_LIBEVENT_DIR=$libevent_prefix
fi
if test "$PHP_LIBEVENT_DIR" != "yes" && test "$PHP_LIBEVENT_DIR" != "/usr/local"; then
dnl don't try to be too smart, check only $PHP_LIBEVENT_DIR if specified
ac_libevent_path=$PHP_LIBEVENT_DIR
AC_LIB_EVENT_DO_CHECK
if test "$success" = "no"; then
AC_MSG_ERROR([Could not find libevent >= $WANT_LIBEVENT_VERSION in $PHP_LIBEVENT_DIR])
fi
else
dnl check default prefixes then
for ac_libevent_path in "" $PHP_LIBEVENT_DIR /usr /usr/local /opt /opt/local /opt/libevent ; do
AC_LIB_EVENT_DO_CHECK
if test "$success" = "yes"; then
break;
fi
done
fi
if test "$success" != "yes" ; then
AC_MSG_RESULT(no)
ac_have_libevent=no
AC_MSG_ERROR([libevent >= $WANT_LIBEVENT_VERSION could not be found])
else
AC_MSG_RESULT($ac_libevent_path)
ac_have_libevent=yes
AC_DEFINE(HAVE_LIBEVENT, 1, [define if libevent is available])
fi
LIBEVENT_LIBS="-levent"
if test -n "$ac_libevent_path"; then
LIBEVENT_CFLAGS="-I$ac_libevent_path/include"
LIBEVENT_LIBS="-L$ac_libevent_path/$PHP_LIBDIR $LIBEVENT_LIBS"
fi
AC_SUBST(LIBEVENT_CFLAGS)
AC_SUBST(LIBEVENT_LIBS)
else
AC_MSG_ERROR([FPM requires libevent >= $WANT_LIBEVENT_VERSION. Please specify libevent install prefix with --with-libevent-dir=yes])
fi
])
dnl }}}
dnl configure checks {{{
AC_DEFUN([AC_FPM_STDLIBS],
[
AC_CHECK_FUNCS(setenv clearenv)
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_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 1;
}
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 = 1;
}
#else
errno = 0;
v2 = ptrace(PTRACE_PEEKDATA, child, (void *) &v1, 0);
if (errno) {
ret = 1;
}
#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])
])
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])
])
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_ERROR([FPM Trace - ptrace, pread, or mach: could not be found])
fi
])
dnl }}}
AC_MSG_CHECKING(for FPM build)
if test "$PHP_FPM" != "no"; then
AC_MSG_RESULT($PHP_FPM)
AC_LIB_EVENT([$minimum_libevent_version])
PHP_TEST_BUILD(event_init, [ ], [
AC_MSG_RESULT(no)
AC_MSG_ERROR([build test failed. Please check the config.log for details.])
], $LIBEVENT_LIBS)
PHP_SETUP_LIBXML(FPM_SHARED_LIBADD, [
], [
AC_MSG_ERROR([xml2-config not found. Please check your libxml2 installation.])
])
AC_FPM_STDLIBS
AC_FPM_PRCTL
AC_FPM_CLOCK
AC_FPM_TRACE
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(fpm_version)
PHP_SUBST_OLD(php_fpm_user)
PHP_SUBST_OLD(php_fpm_group)
AC_DEFINE_UNQUOTED(PHP_FPM_VERSION, "$fpm_version", [fpm version])
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_OUTPUT(sapi/fpm/php-fpm.conf sapi/fpm/init.d.php-fpm sapi/fpm/php-fpm.1)
PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/fpm/Makefile.frag], [$abs_srcdir/sapi/fpm], [sapi/fpm])
SAPI_FPM_PATH=sapi/fpm/php-fpm
PHP_SUBST(SAPI_FPM_PATH)
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="$LIBEVENT_CFLAGS -I$abs_srcdir/sapi/fpm"
SAPI_EXTRA_LIBS="$LIBEVENT_LIBS"
PHP_SUBST(SAPI_EXTRA_LIBS)
INSTALL_IT=":"
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_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_shm_slots.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/xml_config.c \
fpm/zlog.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_SAPI_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_SAPI_OBJS) \$(EXTRA_LIBS) \$(SAPI_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_SAPI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(SAPI_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_SAPI_OBJS) \$(EXTRA_LIBS) \$(SAPI_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
;;
esac
PHP_SUBST(BUILD_FPM)
else
AC_MSG_RESULT(no)
fi

1321
sapi/fpm/fpm/fastcgi.c
File diff suppressed because it is too large
View File

151
sapi/fpm/fpm/fastcgi.h

@ -0,0 +1,151 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2009 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);
int fcgi_is_fastcgi(void);
int fcgi_in_shutdown(void);
int fcgi_listen(const char *path, int backlog);
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_is_fastcgi(int new_value);
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);
int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len);
int fcgi_flush(fcgi_request *req, int close);
#ifdef PHP_WIN32
void fcgi_impersonate(void);
#endif
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);
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/

84
sapi/fpm/fpm/fpm.c

@ -0,0 +1,84 @@
/* $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_stdio.h"
#include "zlog.h"
struct fpm_globals_s fpm_globals;
int fpm_init(int argc, char **argv, char *config, struct event_base **base) /* {{{ */
{
fpm_globals.argc = argc;
fpm_globals.argv = argv;
fpm_globals.config = config;
if (0 > fpm_php_init_main() ||
0 > fpm_stdio_init_main() ||
0 > fpm_conf_init_main() ||
0 > fpm_unix_init_main() ||
0 > fpm_env_init_main() ||
0 > fpm_signals_init_main() ||
0 > fpm_pctl_init_main() ||
0 > fpm_children_init_main() ||
0 > fpm_sockets_init_main() ||
0 > fpm_worker_pool_init_main() ||
0 > fpm_event_init_main(base)) {
return -1;
}
if (0 > fpm_conf_write_pid()) {
return -1;
}
zlog(ZLOG_STUFF, 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 event_base *base) /* {{{ */
{
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, base);
if (!is_parent) {
goto run_child;
}
}
/* run event loop forever */
fpm_event_loop(base);
run_child: /* only workers reach this point */
fpm_cleanups_run(FPM_CLEANUP_CHILD);
*max_requests = fpm_globals.max_requests;
return fpm_globals.listening_socket;
}
/* }}} */

30
sapi/fpm/fpm/fpm.h

@ -0,0 +1,30 @@
/* $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>
#include <sys/types.h> /* for event.h below */
#include <event.h>
int fpm_run(int *max_requests, struct event_base *base);
int fpm_init(int argc, char **argv, char *config, struct event_base **base);
struct fpm_globals_s {
pid_t parent_pid;
int argc;
char **argv;
char *config;
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;
};
extern struct fpm_globals_s fpm_globals;
#endif

116
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

148
sapi/fpm/fpm/fpm_atomic.h

@ -0,0 +1,148 @@
/* $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>
#if ( __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 (__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
#error unsupported processor. please write a patch and send it to me
#endif
static inline int fpm_spinlock(atomic_t *lock, int try_once) /* {{{ */
{
if (try_once) {
return atomic_cmp_set(lock, 0, 1) ? 0 : -1;
}
for (;;) {
if (atomic_cmp_set(lock, 0, 1)) {
break;
}
sched_yield();
}
return 0;
}
/* }}} */
#endif

439
sapi/fpm/fpm/fpm_children.c

@ -0,0 +1,439 @@
/* $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_shm_slots.h"
#include "fpm_status.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));
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->max_requests;
if (0 > fpm_stdio_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_STUFF, ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
exit(255);
}
}
/* }}} */
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(struct event_base *base) /* {{{ */
{
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) != 0) {
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) == SIGTERM) {
restart_child = 0;
}
if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
severity = ZLOG_WARNING;
}
} else if (WIFSTOPPED(status)) {
zlog(ZLOG_STUFF, 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_shm_slots_discard_slot(child);
fpm_clock_get(&tv1);
timersub(&tv1, &child->started, &tv2);
if (restart_child) {
if (!fpm_pctl_can_spawn_children()) {
severity = ZLOG_DEBUG;
}
zlog(ZLOG_STUFF, 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_STUFF, 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_STUFF, 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, base);
}
}
if (restart_child) {
fpm_children_make(wp, 1 /* in event loop */, 1, 0, base);
if (fpm_globals.is_child) {
break;
}
}
} else {
zlog(ZLOG_STUFF, ZLOG_ALERT, "oops, unknown child exited %s", 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_STUFF, ZLOG_ERROR, "[pool %s] malloc failed", 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_shm_slots_prepare_slot(c)) {
fpm_stdio_discard_pipes(c);
fpm_child_free(c);
return 0;
}
return c;
}
/* }}} */
static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
{
fpm_shm_slots_discard_slot(child);
fpm_stdio_discard_pipes(child);
fpm_child_free(child);
}
/* }}} */
static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
{
fpm_shm_slots_child_use_slot(child);
fpm_stdio_child_use_pipes(child);
fpm_child_free(child);
}
/* }}} */
static void fpm_parent_resources_use(struct fpm_child_s *child, struct event_base *base) /* {{{ */
{
fpm_shm_slots_parent_use_slot(child);
fpm_stdio_parent_use_pipes(child, base);
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, struct event_base *base) /* {{{ */
{
int enough = 0;
pid_t pid;
struct fpm_child_s *child;
int max;
if (wp->config->pm->style == PM_STYLE_DYNAMIC) {
if (!in_event_loop) { /* starting */
max = wp->config->pm->dynamic.start_servers;
} else {
max = wp->running_children + nb_to_spawn;
}
} else { /* PM_STYLE_STATIC */
max = wp->config->pm->max_children;
}
while (!enough && fpm_pctl_can_spawn_children() && wp->running_children < max) {
child = fpm_resources_prepare(wp);
if (!child) {
enough = 1;
break;
}
pid = fork();
switch (pid) {
case 0 :
event_reinit(base); /* reinitialize event base after fork() */
fpm_child_resources_use(child);
fpm_globals.is_child = 1;
if (in_event_loop) {
fpm_event_exit_loop(base);
}
fpm_child_init(wp);
return 0;
case -1 :
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fork() failed");
enough = 1;
fpm_resources_discard(child);
break; /* dont try any more on error */
default :
child->pid = pid;
fpm_clock_get(&child->started);
fpm_parent_resources_use(child, base);
zlog(ZLOG_STUFF, is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
}
}
return 1; /* we are done */
}
/* }}} */
int fpm_children_create_initial(struct fpm_worker_pool_s *wp, struct event_base *base) /* {{{ */
{
return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1, base);
}
/* }}} */
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;
}
/* }}} */

35
sapi/fpm/fpm/fpm_children.h

@ -0,0 +1,35 @@
/* $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 <event.h>
#include "fpm_worker_pool.h"
int fpm_children_create_initial(struct fpm_worker_pool_s *wp, struct event_base *base);
int fpm_children_free(struct fpm_child_s *child);
void fpm_children_bury(struct event_base *base);
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 event_base *base);
struct fpm_child_s;
struct fpm_child_s {
struct fpm_child_s *prev, *next;
struct timeval started;
struct fpm_worker_pool_s *wp;
struct event 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;
};
#endif

53
sapi/fpm/fpm/fpm_cleanup.c

@ -0,0 +1,53 @@
/* $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"
#include "zlog.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);
}
/* }}} */

21
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

121
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_STUFF, 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_STUFF, 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_STUFF, 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_STUFF, 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

13
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

658
sapi/fpm/fpm/fpm_conf.c

@ -0,0 +1,658 @@
/* $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
#include <stdio.h>
#include <unistd.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 "xml_config.h"
#include "zlog.h"
struct fpm_global_config_s fpm_global_config;
static void *fpm_global_config_ptr() /* {{{ */
{
return &fpm_global_config;
}
/* }}} */
static char *fpm_conf_set_log_level(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
if (!strcmp(value, "debug")) {
fpm_globals.log_level = ZLOG_DEBUG;
} else if (!strcmp(value, "notice")) {
fpm_globals.log_level = ZLOG_NOTICE;
} else if (!strcmp(value, "warn")) {
fpm_globals.log_level = ZLOG_WARNING;
} else if (!strcmp(value, "error")) {
fpm_globals.log_level = ZLOG_ERROR;
} else if (!strcmp(value, "alert")) {
fpm_globals.log_level = ZLOG_ALERT;
} else {
return "invalid value for 'log_level'";
}
return NULL;
}
/* }}} */
static struct xml_conf_section xml_section_fpm_global_options = {
.conf = &fpm_global_config_ptr,
.path = "/configuration/global_options",
.parsers = (struct xml_value_parser []) {
{ XML_CONF_SCALAR, "emergency_restart_threshold", &xml_conf_set_slot_integer, offsetof(struct fpm_global_config_s, emergency_restart_threshold) },
{ XML_CONF_SCALAR, "emergency_restart_interval", &xml_conf_set_slot_time, offsetof(struct fpm_global_config_s, emergency_restart_interval) },
{ XML_CONF_SCALAR, "process_control_timeout", &xml_conf_set_slot_time, offsetof(struct fpm_global_config_s, process_control_timeout) },
{ XML_CONF_SCALAR, "daemonize", &xml_conf_set_slot_boolean, offsetof(struct fpm_global_config_s, daemonize) },
{ XML_CONF_SCALAR, "pid_file", &xml_conf_set_slot_string, offsetof(struct fpm_global_config_s, pid_file) },
{ XML_CONF_SCALAR, "error_log", &xml_conf_set_slot_string, offsetof(struct fpm_global_config_s, error_log) },
{ XML_CONF_SCALAR, "log_level", &fpm_conf_set_log_level, 0 },
{ 0, 0, 0, 0 }
}
};
static char *fpm_conf_set_pm_style(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
struct fpm_pm_s *c = *conf;
if (!strcmp(value, "static")) {
c->style = PM_STYLE_STATIC;
} else if (!strcmp(value, "dynamic")) {
c->style = PM_STYLE_DYNAMIC;
} else {
return "invalid value for 'style'";
}
return NULL;
}
/* }}} */
static char *fpm_conf_set_rlimit_core(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
struct fpm_worker_pool_config_s *c = *conf;
if (!strcmp(value, "unlimited")) {
c->rlimit_core = -1;
} else {
int int_value;
void *subconf = &int_value;
char *error;
error = xml_conf_set_slot_integer(&subconf, name, vv, 0);
if (error) {
return error;
}
if (int_value < 0) {
return "invalid value for 'rlimit_core'";
}
c->rlimit_core = int_value;
}
return NULL;
}
/* }}} */
static char *fpm_conf_set_catch_workers_output(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
struct fpm_worker_pool_config_s *c = *conf;
int int_value;
void *subconf = &int_value;
char *error;
error = xml_conf_set_slot_boolean(&subconf, name, vv, 0);
if (error) {
return error;
}
c->catch_workers_output = int_value;
return NULL;
}
/* }}} */
static struct xml_conf_section fpm_conf_set_dynamic_subsection_conf = {
.path = "dynamic somewhere", /* fixme */
.parsers = (struct xml_value_parser []) {
{ XML_CONF_SCALAR, "start_servers", &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, dynamic.start_servers) },
{ XML_CONF_SCALAR, "min_spare_servers", &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, dynamic.min_spare_servers) },
{ XML_CONF_SCALAR, "max_spare_servers", &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, dynamic.max_spare_servers) },
{ 0, 0, 0, 0 }
}
};
static char *fpm_conf_set_dynamic_subsection(void **conf, char *name, void *xml_node, intptr_t offset) /* {{{ */
{
return xml_conf_parse_section(conf, &fpm_conf_set_dynamic_subsection_conf, xml_node);
}
/* }}} */
static struct xml_conf_section fpm_conf_set_listen_options_subsection_conf = {
.path = "listen options somewhere", /* fixme */
.parsers = (struct xml_value_parser []) {
{ XML_CONF_SCALAR, "backlog", &xml_conf_set_slot_integer, offsetof(struct fpm_listen_options_s, backlog) },
{ XML_CONF_SCALAR, "owner", &xml_conf_set_slot_string, offsetof(struct fpm_listen_options_s, owner) },
{ XML_CONF_SCALAR, "group", &xml_conf_set_slot_string, offsetof(struct fpm_listen_options_s, group) },
{ XML_CONF_SCALAR, "mode", &xml_conf_set_slot_string, offsetof(struct fpm_listen_options_s, mode) },
{ 0, 0, 0, 0 }
}
};
static char *fpm_conf_set_listen_options_subsection(void **conf, char *name, void *xml_node, intptr_t offset) /* {{{ */
{
void *subconf = (char *) *conf + offset;
struct fpm_listen_options_s *lo;
lo = malloc(sizeof(*lo));
if (!lo) {
return "malloc() failed";
}
memset(lo, 0, sizeof(*lo));
lo->backlog = -1;
* (struct fpm_listen_options_s **) subconf = lo;
subconf = lo;
return xml_conf_parse_section(&subconf, &fpm_conf_set_listen_options_subsection_conf, xml_node);
}
/* }}} */
static struct xml_conf_section fpm_conf_set_pm_subsection_conf = {
.path = "pm settings somewhere", /* fixme */
.parsers = (struct xml_value_parser []) {
{ XML_CONF_SCALAR, "style", &fpm_conf_set_pm_style, 0 },
{ XML_CONF_SCALAR, "max_children", &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, max_children) },
{ XML_CONF_SCALAR, "status", &xml_conf_set_slot_string, offsetof(struct fpm_pm_s, status) },
{ XML_CONF_SCALAR, "ping", &xml_conf_set_slot_string, offsetof(struct fpm_pm_s, ping) },
{ XML_CONF_SCALAR, "pong", &xml_conf_set_slot_string, offsetof(struct fpm_pm_s, pong) },
{ XML_CONF_SUBSECTION, "dynamic", &fpm_conf_set_dynamic_subsection, offsetof(struct fpm_pm_s, dynamic) },
{ 0, 0, 0, 0 }
}
};
static char *fpm_conf_set_pm_subsection(void **conf, char *name, void *xml_node, intptr_t offset) /* {{{ */
{
void *subconf = (char *) *conf + offset;
struct fpm_pm_s *pm;
pm = malloc(sizeof(*pm));
if (!pm) {
return "fpm_conf_set_pm_subsection(): malloc failed";
}
memset(pm, 0, sizeof(*pm));
*(struct fpm_pm_s **) subconf = pm;
subconf = pm;
return xml_conf_parse_section(&subconf, &fpm_conf_set_pm_subsection_conf, xml_node);
}
/* }}} */
static char *xml_conf_set_slot_key_value_pair(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
struct key_value_s *kv;
struct key_value_s ***parent = (struct key_value_s ***) conf;
kv = malloc(sizeof(*kv));
if (!kv) {
return "malloc() failed";
}
memset(kv, 0, sizeof(*kv));
kv->key = strdup(name);
kv->value = strdup(value);
if (!kv->key || !kv->value) {
return "xml_conf_set_slot_key_value_pair(): strdup() failed";
}
**parent = kv;
*parent = &kv->next;
return NULL;
}
/* }}} */
static struct xml_conf_section fpm_conf_set_key_value_pairs_subsection_conf = {
.path = "key_value_pairs somewhere", /* fixme */
.parsers = (struct xml_value_parser []) {
{ XML_CONF_SCALAR, 0, &xml_conf_set_slot_key_value_pair, 0 },
{ 0, 0, 0, 0 }
}
};
static char *fpm_conf_set_key_value_pairs_subsection(void **conf, char *name, void *xml_node, intptr_t offset) /* {{{ */
{
void *next_kv = (char *) *conf + offset;
return xml_conf_parse_section(&next_kv, &fpm_conf_set_key_value_pairs_subsection_conf, xml_node);
}
/* }}} */
static void *fpm_worker_pool_config_alloc() /* {{{ */
{
static struct fpm_worker_pool_s *current_wp = 0;
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));
if (current_wp) {
current_wp->next = wp;
}
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->listen_address);
free(wpc->pm->status);
free(wpc->pm->ping);
free(wpc->pm->pong);
if (wpc->listen_options) {
free(wpc->listen_options->owner);
free(wpc->listen_options->group);
free(wpc->listen_options->mode);
free(wpc->listen_options);
}
for (kv = wpc->php_defines; kv; kv = kv_next) {
kv_next = kv->next;
free(kv->key);
free(kv->value);
free(kv);
}
for (kv = wpc->environment; kv; kv = kv_next) {
kv_next = kv->next;
free(kv->key);
free(kv->value);
free(kv);
}
free(wpc->pm);
free(wpc->user);
free(wpc->group);
free(wpc->chroot);
free(wpc->chdir);
free(wpc->allowed_clients);
free(wpc->slowlog);
return 0;
}
/* }}} */
static struct xml_conf_section xml_section_fpm_worker_pool_config = {
.conf = &fpm_worker_pool_config_alloc,
.path = "/configuration/workers/pool",
.parsers = (struct xml_value_parser []) {
{ XML_CONF_SCALAR, "name", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, name) },
{ XML_CONF_SCALAR, "listen_address", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, listen_address) },
{ XML_CONF_SUBSECTION, "listen_options", &fpm_conf_set_listen_options_subsection, offsetof(struct fpm_worker_pool_config_s, listen_options) },
{ XML_CONF_SUBSECTION, "php_defines", &fpm_conf_set_key_value_pairs_subsection, offsetof(struct fpm_worker_pool_config_s, php_defines) },
{ XML_CONF_SCALAR, "user", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, user) },
{ XML_CONF_SCALAR, "group", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, group) },
{ XML_CONF_SCALAR, "chroot", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, chroot) },
{ XML_CONF_SCALAR, "chdir", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, chdir) },
{ XML_CONF_SCALAR, "allowed_clients", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, allowed_clients) },
{ XML_CONF_SUBSECTION, "environment", &fpm_conf_set_key_value_pairs_subsection, offsetof(struct fpm_worker_pool_config_s, environment) },
{ XML_CONF_SCALAR, "request_terminate_timeout", &xml_conf_set_slot_time, offsetof(struct fpm_worker_pool_config_s, request_terminate_timeout) },
{ XML_CONF_SCALAR, "request_slowlog_timeout", &xml_conf_set_slot_time, offsetof(struct fpm_worker_pool_config_s, request_slowlog_timeout) },
{ XML_CONF_SCALAR, "slowlog", &xml_conf_set_slot_string, offsetof(struct fpm_worker_pool_config_s, slowlog) },
{ XML_CONF_SCALAR, "rlimit_files", &xml_conf_set_slot_integer, offsetof(struct fpm_worker_pool_config_s, rlimit_files) },
{ XML_CONF_SCALAR, "rlimit_core", &fpm_conf_set_rlimit_core, 0 },
{ XML_CONF_SCALAR, "max_requests", &xml_conf_set_slot_integer, offsetof(struct fpm_worker_pool_config_s, max_requests) },
{ XML_CONF_SCALAR, "catch_workers_output", &fpm_conf_set_catch_workers_output, 0 },
{ XML_CONF_SUBSECTION, "pm", &fpm_conf_set_pm_subsection, offsetof(struct fpm_worker_pool_config_s, pm) },
{ 0, 0, 0, 0 }
}
};
static struct xml_conf_section *fpm_conf_all_sections[] = {
&xml_section_fpm_global_options,
&xml_section_fpm_worker_pool_config,
0
};
static int fpm_evaluate_full_path(char **path) /* {{{ */
{
if (**path != '/') {
char *full_path;
full_path = malloc(sizeof(PHP_PREFIX) + strlen(*path) + 1);
if (!full_path) {
return -1;
}
sprintf(full_path, "%s/%s", PHP_PREFIX, *path);
free(*path);
*path = full_path;
}
return 0;
}
/* }}} */
static int fpm_conf_process_all_pools() /* {{{ */
{
struct fpm_worker_pool_s *wp;
if (!fpm_worker_all_pools) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "at least one pool section must be specified in config file");
return -1;
}
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
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);
}
} else {
wp->is_template = 1;
}
if (wp->config->pm == NULL) {
zlog(ZLOG_STUFF, ZLOG_ALERT, "[pool %s] the process manager is missing (static or dynamic)", wp->config->name);
return -1;
}
if (wp->config->pm->style == PM_STYLE_DYNAMIC) {
struct fpm_pm_s *pm = wp->config->pm;
if (pm->dynamic.min_spare_servers <= 0) {
zlog(ZLOG_STUFF, ZLOG_ALERT, "[pool %s] min_spare_servers(%d) must be a positive value", wp->config->name, pm->dynamic.min_spare_servers);
return -1;
}
if (pm->dynamic.max_spare_servers <= 0) {
zlog(ZLOG_STUFF, ZLOG_ALERT, "[pool %s] max_spare_servers(%d) must be a positive value", wp->config->name, pm->dynamic.max_spare_servers);
return -1;
}
if (pm->dynamic.min_spare_servers > pm->max_children ||
pm->dynamic.max_spare_servers > pm->max_children) {
zlog(ZLOG_STUFF, ZLOG_ALERT, "[pool %s] min_spare_servers(%d) and max_spare_servers(%d) cannot be greater than max_children(%d)",
wp->config->name, pm->dynamic.min_spare_servers, pm->dynamic.max_spare_servers, pm->max_children);
return -1;
}
if (pm->dynamic.max_spare_servers < pm->dynamic.min_spare_servers) {
zlog(ZLOG_STUFF, ZLOG_ALERT, "[pool %s] max_spare_servers(%d) must not be less than min_spare_servers(%d)", wp->config->name, pm->dynamic.max_spare_servers, pm->dynamic.min_spare_servers);
return -1;
}
if (pm->dynamic.start_servers <= 0) {
pm->dynamic.start_servers = pm->dynamic.min_spare_servers + ((pm->dynamic.max_spare_servers - pm->dynamic.min_spare_servers) / 2);
zlog(ZLOG_STUFF, ZLOG_NOTICE, "[pool %s] start_servers has been set to %d", wp->config->name, pm->dynamic.start_servers);
} else if (pm->dynamic.start_servers < pm->dynamic.min_spare_servers || pm->dynamic.start_servers > pm->dynamic.max_spare_servers) {
zlog(ZLOG_STUFF, ZLOG_ALERT, "[pool %s] start_servers(%d) must not be less than min_spare_servers(%d) and not greater than max_spare_servers(%d)", wp->config->name, pm->dynamic.start_servers, pm->dynamic.min_spare_servers, pm->dynamic.max_spare_servers);
return -1;
}
}
if (wp->config->request_slowlog_timeout) {
#if HAVE_FPM_TRACE
if (! (wp->config->slowlog && *wp->config->slowlog)) {
zlog(ZLOG_STUFF, 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_STUFF, 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->request_slowlog_timeout && wp->config->slowlog && *wp->config->slowlog) {
int fd;
fpm_evaluate_full_path(&wp->config->slowlog);
if (wp->config->request_slowlog_timeout) {
fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if (0 > fd) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "open(%s) failed", wp->config->slowlog);
return -1;
}
close(fd);
}
}
if (wp->config->pm->ping && *wp->config->pm->ping) {
char *ping = wp->config->pm->ping;
int i;
if (*ping != '/') {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] the ping page '%s' must start with a '/'", wp->config->name, ping);
return -1;
}
if (strlen(ping) < 2) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] the ping page '%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_STUFF, ZLOG_ERROR, "[pool %s] the ping page '%s' must containt only the following characters '[alphanum]/_-.'", wp->config->name, ping);
return -1;
}
}
if (!wp->config->pm->pong) {
wp->config->pm->pong = strdup("pong");
} else {
if (strlen(wp->config->pm->pong) < 1) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] the ping response page '%s' is not long enough", wp->config->name, wp->config->pm->pong);
return -1;
}
}
} else {
if (wp->config->pm->pong) {
free(wp->config->pm->pong);
wp->config->pm->pong = NULL;
}
}
if (wp->config->pm->status && *wp->config->pm->status) {
int i;
char *status = wp->config->pm->status;
/* struct fpm_status_s fpm_status; */
if (*status != '/') {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] the status page '%s' must start with a '/'", wp->config->name, status);
return -1;
}
if (strlen(status) < 2) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] the status page '%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_STUFF, ZLOG_ERROR, "[pool %s] the status page '%s' must containt only the following characters '[alphanum]/_-.'", wp->config->name, status);
return -1;
}
}
wp->shm_status = fpm_shm_alloc(sizeof(struct fpm_status_s));
if (!wp->shm_status) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] unable to allocate shared memory for status page '%s'", wp->config->name, status);
return -1;
}
fpm_status_update_accepted_conn(wp->shm_status, 0);
fpm_status_update_activity(wp->shm_status, -1, -1, -1, 1);
fpm_status_set_pm(wp->shm_status, wp->config->pm->style);
/* memset(&fpm_status.last_update, 0, sizeof(fpm_status.last_update)); */
}
}
return 0;
}
/* }}} */
int fpm_conf_unlink_pid() /* {{{ */
{
if (fpm_global_config.pid_file) {
if (0 > unlink(fpm_global_config.pid_file)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "unlink(\"%s\") failed", 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_STUFF, ZLOG_SYSERROR, "creat(\"%s\") failed", fpm_global_config.pid_file);
return -1;
}
len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
if (len != write(fd, buf, len)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "write() failed");
return -1;
}
close(fd);
}
return 0;
}
/* }}} */
static int fpm_conf_post_process() /* {{{ */
{
if (fpm_global_config.pid_file) {
fpm_evaluate_full_path(&fpm_global_config.pid_file);
}
if (!fpm_global_config.error_log) {
char *tmp_log_path;
spprintf(&tmp_log_path, 0, "%s/log/php-fpm.log", PHP_LOCALSTATEDIR);
fpm_global_config.error_log = strdup(tmp_log_path);
efree(tmp_log_path);
}
fpm_evaluate_full_path(&fpm_global_config.error_log);
if (0 > fpm_stdio_open_error_log(0)) {
return -1;
}
return fpm_conf_process_all_pools();
}
/* }}} */
static void fpm_conf_cleanup(int which, void *arg) /* {{{ */
{
free(fpm_global_config.pid_file);
free(fpm_global_config.error_log);
fpm_global_config.pid_file = 0;
fpm_global_config.error_log = 0;
}
/* }}} */
int fpm_conf_init_main() /* {{{ */
{
char *filename = fpm_globals.config;
char *err;
if (0 > xml_conf_sections_register(fpm_conf_all_sections)) {
return -1;
}
if (filename == NULL) {
spprintf(&filename, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
err = xml_conf_load_file(filename);
efree(filename);
} else {
err = xml_conf_load_file(filename);
}
if (err) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "failed to load configuration file: %s", err);
return -1;
}
if (0 > fpm_conf_post_process()) {
return -1;
}
xml_conf_clean();
if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
return -1;
}
return 0;
}
/* }}} */

78
sapi/fpm/fpm/fpm_conf.h

@ -0,0 +1,78 @@
/* $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
#define FPM_CONF_MAX_PONG_LENGTH 64
struct key_value_s;
struct key_value_s {
struct key_value_s *next;
char *key;
char *value;
};
struct fpm_global_config_s {
int emergency_restart_threshold;
int emergency_restart_interval;
int process_control_timeout;
int daemonize;
char *pid_file;
char *error_log;
};
extern struct fpm_global_config_s fpm_global_config;
struct fpm_pm_s {
int style;
int max_children;
char *status;
char *ping;
char *pong;
struct {
int start_servers;
int min_spare_servers;
int max_spare_servers;
} dynamic;
};
struct fpm_listen_options_s {
int backlog;
char *owner;
char *group;
char *mode;
};
struct fpm_worker_pool_config_s {
char *name;
char *listen_address;
struct fpm_listen_options_s *listen_options;
struct key_value_s *php_defines;
char *user;
char *group;
char *chroot;
char *chdir;
char *allowed_clients;
struct key_value_s *environment;
struct fpm_pm_s *pm;
int request_terminate_timeout;
int request_slowlog_timeout;
char *slowlog;
int max_requests;
int rlimit_files;
int rlimit_core;
unsigned catch_workers_output:1;
};
enum { PM_STYLE_STATIC = 1, PM_STYLE_DYNAMIC = 2 };
int fpm_conf_init_main();
int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc);
int fpm_conf_write_pid();
int fpm_conf_unlink_pid();
#endif

44
sapi/fpm/fpm/fpm_config.h

@ -0,0 +1,44 @@
/* $Id: fpm_config.h,v 1.16 2008/05/25 00:30:43 anight Exp $ */
/* (c) 2007,2008 Andrei Nigmatulin */
#include <php_config.h>
#ifdef FPM_AUTOCONFIG_H
#include <fpm_autoconfig.h>
#endif
/* 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
/* Solaris does not have it */
#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 MIN
#define MIN(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

183
sapi/fpm/fpm/fpm_env.c

@ -0,0 +1,183 @@
/* $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 "zlog.h"
#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
int fpm_env_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
{
struct key_value_s *kv;
clearenv();
for (kv = wp->config->environment; 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;
kv = wp->config->environment;
for (kv = wp->config->environment; 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;
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
if (0 > fpm_env_conf_wp(wp)) {
return -1;
}
}
return 0;
}
/* }}} */

24
sapi/fpm/fpm/fpm_env.h

@ -0,0 +1,24 @@
/* $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"
int fpm_env_init_child(struct fpm_worker_pool_s *wp);
int fpm_env_init_main();
extern char **environ;
#ifndef HAVE_SETENV
int setenv(char *name, char *value, int overwrite);
#endif
#ifndef HAVE_CLEARENV
void clearenv();
#endif
#endif

142
sapi/fpm/fpm/fpm_events.c

@ -0,0 +1,142 @@
/* $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 "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"
static void fpm_event_cleanup(int which, void *arg) /* {{{ */
{
struct event_base *base = (struct event_base *)arg;
event_base_free(base);
}
/* }}} */
static void fpm_got_signal(int fd, short ev, void *arg) /* {{{ */
{
char c;
int res;
struct event_base *base = (struct event_base *)arg;
do {
do {
res = read(fd, &c, 1);
} while (res == -1 && errno == EINTR);
if (res <= 0) {
if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "read() failed");
}
return;
}
switch (c) {
case 'C' : /* SIGCHLD */
zlog(ZLOG_STUFF, ZLOG_DEBUG, "received SIGCHLD");
fpm_children_bury(base);
break;
case 'I' : /* SIGINT */
zlog(ZLOG_STUFF, ZLOG_DEBUG, "received SIGINT");
zlog(ZLOG_STUFF, ZLOG_NOTICE, "Terminating ...");
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET, base);
break;
case 'T' : /* SIGTERM */
zlog(ZLOG_STUFF, ZLOG_DEBUG, "received SIGTERM");
zlog(ZLOG_STUFF, ZLOG_NOTICE, "Terminating ...");
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET, base);
break;
case 'Q' : /* SIGQUIT */
zlog(ZLOG_STUFF, ZLOG_DEBUG, "received SIGQUIT");
zlog(ZLOG_STUFF, ZLOG_NOTICE, "Finishing ...");
fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET, base);
break;
case '1' : /* SIGUSR1 */
zlog(ZLOG_STUFF, ZLOG_DEBUG, "received SIGUSR1");
if (0 == fpm_stdio_open_error_log(1)) {
zlog(ZLOG_STUFF, ZLOG_NOTICE, "log file re-opened");
} else {
zlog(ZLOG_STUFF, ZLOG_ERROR, "unable to re-opened log file");
}
break;
case '2' : /* SIGUSR2 */
zlog(ZLOG_STUFF, ZLOG_DEBUG, "received SIGUSR2");
zlog(ZLOG_STUFF, ZLOG_NOTICE, "Reloading in progress ...");
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET, base);
break;
}
if (fpm_globals.is_child) {
break;
}
} while (1);
return;
}
/* }}} */
int fpm_event_init_main(struct event_base **base) /* {{{ */
{
*base = event_base_new();
zlog(ZLOG_STUFF, ZLOG_NOTICE, "libevent: using %s", event_base_get_method(*base));
if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, *base)) {
return -1;
}
return 0;
}
/* }}} */
int fpm_event_loop(struct event_base *base) /* {{{ */
{
static struct event signal_fd_event;
event_set(&signal_fd_event, fpm_signals_get_fd(), EV_PERSIST | EV_READ, &fpm_got_signal, base);
event_base_set(base, &signal_fd_event);
event_add(&signal_fd_event, 0);
fpm_pctl_heartbeat(-1, 0, base);
fpm_pctl_perform_idle_server_maintenance_heartbeat(-1, 0, base);
zlog(ZLOG_STUFF, ZLOG_NOTICE, "ready to handle connections");
event_base_dispatch(base);
return 0;
}
/* }}} */
int fpm_event_add(int fd, struct event_base *base, struct event *ev, void (*callback)(int, short, void *), void *arg) /* {{{ */
{
event_set(ev, fd, EV_PERSIST | EV_READ, callback, arg);
event_base_set(base, ev);
return event_add(ev, 0);
}
/* }}} */
int fpm_event_del(struct event *ev) /* {{{ */
{
return event_del(ev);
}
/* }}} */
void fpm_event_exit_loop(struct event_base *base) /* {{{ */
{
event_base_loopbreak(base);
}
/* }}} */
void fpm_event_fire(struct event *ev) /* {{{ */
{
(*ev->ev_callback)( (int) ev->ev_fd, (short) ev->ev_res, ev->ev_arg);
}
/* }}} */

16
sapi/fpm/fpm/fpm_events.h

@ -0,0 +1,16 @@
/* $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
void fpm_event_exit_loop(struct event_base *base);
int fpm_event_loop(struct event_base *base);
int fpm_event_add(int fd, struct event_base *base, struct event *ev, void (*callback)(int, short, void *), void *arg);
int fpm_event_del(struct event *ev);
void fpm_event_fire(struct event *ev);
int fpm_event_init_main(struct event_base **base);
#endif

1915
sapi/fpm/fpm/fpm_main.c
File diff suppressed because it is too large
View File

198
sapi/fpm/fpm/fpm_php.c

@ -0,0 +1,198 @@
/* $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"
static int zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, 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;
} 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);
}
}
/* }}} */
static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */
{
TSRMLS_FETCH();
struct key_value_s *kv;
for (kv = wp->config->php_defines; kv; kv = kv->next) {
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;
#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID >= 50300)
php_dl(value, MODULE_PERSISTENT, &zv, 1 TSRMLS_CC);
#else
zval filename;
ZVAL_STRINGL(&filename, value, value_len, 0);
#if (PHP_MAJOR_VERSION >= 5)
php_dl(&filename, MODULE_PERSISTENT, &zv, 1 TSRMLS_CC);
#else
php_dl(&filename, MODULE_PERSISTENT, &zv TSRMLS_CC);
#endif
#endif
continue;
}
zend_ini_alter_master(name, name_len + 1, value, value_len, PHP_INI_STAGE_ACTIVATE TSRMLS_CC);
if (!strcmp(name, "disable_functions") && *value) {
char *v = strdup(value);
#if (PHP_MAJOR_VERSION >= 5)
PG(disable_functions) = v;
#endif
fpm_php_disable(v, zend_disable_function TSRMLS_CC);
}
else if (!strcmp(name, "disable_classes") && *value) {
char *v = strdup(value);
#if (PHP_MAJOR_VERSION >= 5)
PG(disable_classes) = v;
#endif
fpm_php_disable(v, zend_disable_class TSRMLS_CC);
}
}
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->allowed_clients);
}
return 0;
}
/* }}} */
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;
}
/* }}} */
char *fpm_php_script_filename(TSRMLS_D) /* {{{ */
{
return SG(request_info).path_translated;
}
/* }}} */
char *fpm_php_request_method(TSRMLS_D) /* {{{ */
{
return (char *) SG(request_info).request_method;
}
/* }}} */
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;
}
return 0;
}
/* }}} */

23
sapi/fpm/fpm/fpm_php.h

@ -0,0 +1,23 @@
/* $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 */
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_method(TSRMLS_D);
size_t fpm_php_content_length(TSRMLS_D);
void fpm_php_soft_quit();
int fpm_php_init_main();
#endif

175
sapi/fpm/fpm/fpm_php_trace.c

@ -0,0 +1,175 @@
/* $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 "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();
FILE *slowlog;
zlog(ZLOG_STUFF, ZLOG_NOTICE, "about to trace %d", (int) child->pid);
slowlog = fopen(child->wp->config->slowlog, "a+");
if (!slowlog) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fopen(%s) failed", 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_STUFF, ZLOG_NOTICE, "finished trace of %d", (int) child->pid);
}
/* }}} */
#endif

13
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

463
sapi/fpm/fpm/fpm_process_ctl.c

@ -0,0 +1,463 @@
/* $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_status.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 event pctl_event;
static void fpm_pctl_action(int fd, short which, void *arg) /* {{{ */
{
struct event_base *base = (struct event_base *)arg;
evtimer_del(&pctl_event);
memset(&pctl_event, 0, sizeof(pctl_event));
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT, base);
}
/* }}} */
static int fpm_pctl_timeout_set(int sec, struct event_base *base) /* {{{ */
{
struct timeval tv = { .tv_sec = sec, .tv_usec = 0 };
if (evtimer_initialized(&pctl_event)) {
evtimer_del(&pctl_event);
}
evtimer_set(&pctl_event, &fpm_pctl_action, base);
event_base_set(base, &pctl_event);
evtimer_add(&pctl_event, &tv);
return 0;
}
/* }}} */
static void fpm_pctl_exit() /* {{{ */
{
zlog(ZLOG_STUFF, ZLOG_NOTICE, "exiting, bye-bye!");
fpm_conf_unlink_pid();
fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN);
exit(0);
}
/* }}} */
#define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
static void fpm_pctl_exec() /* {{{ */
{
zlog(ZLOG_STUFF, 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_STUFF, ZLOG_SYSERROR, "execvp() failed");
exit(1);
}
/* }}} */
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;
default :
break;
}
return kill(pid, s);
}
/* }}} */
static 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_STUFF, 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_STUFF, ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
}
}
/* }}} */
static void fpm_pctl_action_next(struct event_base *base) /* {{{ */
{
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, base);
}
/* }}} */
void fpm_pctl(int new_state, int action, struct event_base *base) /* {{{ */
{
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_STUFF, 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_STUFF, ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
/* fall down */
case FPM_PCTL_ACTION_TIMEOUT :
fpm_pctl_action_next(base);
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(struct event_base *base) /* {{{ */
{
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, base);
}
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 event_base *base) /* {{{ */
{
struct fpm_worker_pool_s *wp;
struct fpm_child_s *last_idle_child = NULL;
int i;
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
struct fpm_child_s *child;
int idle = 0;
int active = 0;
if (wp->config == NULL) continue;
for (child = wp->children; child; child = child->next) {
int ret = fpm_request_is_idle(child);
if (ret == 1) {
if (last_idle_child == NULL) {
last_idle_child = child;
} else {
if (child->started.tv_sec < last_idle_child->started.tv_sec) {
last_idle_child = child;
}
}
idle++;
} else if (ret == 0) {
active++;
}
}
if ((active + idle) != wp->running_children) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] unable to retrieve process activity of one or more child(ren). Will try again later.", wp->config->name);
continue;
}
/* update status structure for all PMs */
fpm_status_update_activity(wp->shm_status, idle, active, idle + active, 0);
/* the rest is only used by PM_STYLE_DYNAMIC */
if (wp->config->pm->style != PM_STYLE_DYNAMIC) continue;
zlog(ZLOG_STUFF, 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->dynamic.max_spare_servers && last_idle_child) {
last_idle_child->idle_kill = 1;
fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_TERM);
wp->idle_spawn_rate = 1;
continue;
}
if (idle < wp->config->pm->dynamic.min_spare_servers) {
if (wp->running_children >= wp->config->pm->max_children) {
if (!wp->warn_max_children) {
zlog(ZLOG_STUFF, 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;
}
wp->idle_spawn_rate = 1;
continue;
}
if (wp->idle_spawn_rate >= 8) {
zlog(ZLOG_STUFF, ZLOG_WARNING, "[pool %s] seems busy (you may need to increase start_servers, or 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 */
i = MIN(wp->idle_spawn_rate, wp->config->pm->dynamic.min_spare_servers - idle);
/* get sure it won't exceed max_children */
i = MIN(i, wp->config->pm->max_children - wp->running_children);
if (i <= 0) {
if (!wp->warn_max_children) {
zlog(ZLOG_STUFF, 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;
}
wp->idle_spawn_rate = 1;
continue;
}
wp->warn_max_children = 0;
fpm_children_make(wp, 1, i, 1, base);
/* 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_STUFF, ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, i);
/* 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(int fd, short which, void *arg) /* {{{ */
{
static struct event heartbeat;
struct timeval tv = { .tv_sec = 0, .tv_usec = 130000 };
struct timeval now;
struct event_base *base = (struct event_base *)arg;
if (which == EV_TIMEOUT) {
evtimer_del(&heartbeat);
fpm_clock_get(&now);
fpm_pctl_check_request_timeout(&now);
}
evtimer_set(&heartbeat, &fpm_pctl_heartbeat, base);
event_base_set(base, &heartbeat);
evtimer_add(&heartbeat, &tv);
}
/* }}} */
void fpm_pctl_perform_idle_server_maintenance_heartbeat(int fd, short which, void *arg) /* {{{ */
{
static struct event heartbeat;
struct timeval tv = { .tv_sec = 0, .tv_usec = FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT };
struct timeval now;
struct event_base *base = (struct event_base *)arg;
if (which == EV_TIMEOUT) {
evtimer_del(&heartbeat);
fpm_clock_get(&now);
if (fpm_pctl_can_spawn_children()) {
fpm_pctl_perform_idle_server_maintenance(&now, base);
/* 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;
}
}
}
evtimer_set(&heartbeat, &fpm_pctl_perform_idle_server_maintenance_heartbeat, base);
event_base_set(base, &heartbeat);
evtimer_add(&heartbeat, &tv);
}
/* }}} */

45
sapi/fpm/fpm/fpm_process_ctl.h

@ -0,0 +1,45 @@
/* $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
/* spawn max 32 children at once */
#define FPM_MAX_SPAWN_RATE (32)
/* 1s (in µs here) heatbeat for idle server maintenance */
#define FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT (1000000)
struct fpm_child_s;
void fpm_pctl(int new_state, int action, struct event_base *base);
int fpm_pctl_can_spawn_children();
int fpm_pctl_kill(pid_t pid, int how);
void fpm_pctl_heartbeat(int fd, short which, void *arg);
void fpm_pctl_perform_idle_server_maintenance_heartbeat(int fd, 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
};
#endif

169
sapi/fpm/fpm/fpm_request.c

@ -0,0 +1,169 @@
/* $Id: fpm_request.c,v 1.9.2.1 2008/11/15 00:57:24 anight Exp $ */
/* (c) 2007,2008 Andrei Nigmatulin */
#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_shm_slots.h"
#include "fpm_status.h"
#include "fpm_request.h"
#include "zlog.h"
void fpm_request_accepting() /* {{{ */
{
struct fpm_shm_slot_s *slot;
slot = fpm_shm_slots_acquire(0, 0);
slot->request_stage = FPM_REQUEST_ACCEPTING;
fpm_clock_get(&slot->tv);
memset(slot->request_method, 0, sizeof(slot->request_method));
slot->content_length = 0;
memset(slot->script_filename, 0, sizeof(slot->script_filename));
fpm_shm_slots_release(slot);
}
/* }}} */
void fpm_request_reading_headers() /* {{{ */
{
struct fpm_shm_slot_s *slot;
slot = fpm_shm_slots_acquire(0, 0);
slot->request_stage = FPM_REQUEST_READING_HEADERS;
fpm_clock_get(&slot->tv);
slot->accepted = slot->tv;
fpm_shm_slots_release(slot);
fpm_status_increment_accepted_conn(fpm_status_shm);
}
/* }}} */
void fpm_request_info() /* {{{ */
{
TSRMLS_FETCH();
struct fpm_shm_slot_s *slot;
char *request_method = fpm_php_request_method(TSRMLS_C);
char *script_filename = fpm_php_script_filename(TSRMLS_C);
slot = fpm_shm_slots_acquire(0, 0);
slot->request_stage = FPM_REQUEST_INFO;
fpm_clock_get(&slot->tv);
if (request_method) {
cpystrn(slot->request_method, request_method, sizeof(slot->request_method));
}
slot->content_length = fpm_php_content_length(TSRMLS_C);
/* 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) {
cpystrn(slot->script_filename, script_filename, sizeof(slot->script_filename));
}
fpm_shm_slots_release(slot);
}
/* }}} */
void fpm_request_executing() /* {{{ */
{
struct fpm_shm_slot_s *slot;
slot = fpm_shm_slots_acquire(0, 0);
slot->request_stage = FPM_REQUEST_EXECUTING;
fpm_clock_get(&slot->tv);
fpm_shm_slots_release(slot);
}
/* }}} */
void fpm_request_finished() /* {{{ */
{
struct fpm_shm_slot_s *slot;
slot = fpm_shm_slots_acquire(0, 0);
slot->request_stage = FPM_REQUEST_FINISHED;
fpm_clock_get(&slot->tv);
memset(&slot->accepted, 0, sizeof(slot->accepted));
fpm_shm_slots_release(slot);
}
/* }}} */
void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *now, int terminate_timeout, int slowlog_timeout) /* {{{ */
{
struct fpm_shm_slot_s *slot;
struct fpm_shm_slot_s slot_c;
slot = fpm_shm_slot(child);
if (!fpm_shm_slots_acquire(slot, 1)) {
return;
}
slot_c = *slot;
fpm_shm_slots_release(slot);
#if HAVE_FPM_TRACE
if (child->slow_logged.tv_sec) {
if (child->slow_logged.tv_sec != slot_c.accepted.tv_sec || child->slow_logged.tv_usec != slot_c.accepted.tv_usec) {
child->slow_logged.tv_sec = 0;
child->slow_logged.tv_usec = 0;
}
}
#endif
if (slot_c.request_stage > FPM_REQUEST_ACCEPTING && slot_c.request_stage < FPM_REQUEST_FINISHED) {
char purified_script_filename[sizeof(slot_c.script_filename)];
struct timeval tv;
timersub(now, &slot_c.accepted, &tv);
#if HAVE_FPM_TRACE
if (child->slow_logged.tv_sec == 0 && slowlog_timeout &&
slot_c.request_stage == FPM_REQUEST_EXECUTING && tv.tv_sec >= slowlog_timeout) {
str_purify_filename(purified_script_filename, slot_c.script_filename, sizeof(slot_c.script_filename));
child->slow_logged = slot_c.accepted;
child->tracer = fpm_php_trace;
fpm_trace_signal(child->pid);
zlog(ZLOG_STUFF, ZLOG_WARNING, "[pool %s] child %d, script '%s' executing too slow (%d.%06d sec), logging",
child->wp->config->name, (int) child->pid, purified_script_filename, (int) tv.tv_sec, (int) tv.tv_usec);
}
else
#endif
if (terminate_timeout && tv.tv_sec >= terminate_timeout) {
str_purify_filename(purified_script_filename, slot_c.script_filename, sizeof(slot_c.script_filename));
fpm_pctl_kill(child->pid, FPM_PCTL_TERM);
zlog(ZLOG_STUFF, ZLOG_WARNING, "[pool %s] child %d, script '%s' execution timed out (%d.%06d sec), terminating",
child->wp->config->name, (int) child->pid, purified_script_filename, (int) tv.tv_sec, (int) tv.tv_usec);
}
}
}
/* }}} */
int fpm_request_is_idle(struct fpm_child_s *child) /* {{{ */
{
struct fpm_shm_slot_s *slot;
struct fpm_shm_slot_s slot_c;
slot = fpm_shm_slot(child);
if (!fpm_shm_slots_acquire(slot, 1)) {
return -1;
}
slot_c = *slot;
fpm_shm_slots_release(slot);
return(!slot_c.accepted.tv_sec && !slot_c.accepted.tv_usec ? 1 : 0);
}
/* }}} */

28
sapi/fpm/fpm/fpm_request.h

@ -0,0 +1,28 @@
/* $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_finished(); /* request processed: script response have been sent to web server */
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);
enum fpm_request_stage_e {
FPM_REQUEST_ACCEPTING = 1,
FPM_REQUEST_READING_HEADERS,
FPM_REQUEST_INFO,
FPM_REQUEST_EXECUTING,
FPM_REQUEST_FINISHED
};
#endif

101
sapi/fpm/fpm/fpm_shm.c

@ -0,0 +1,101 @@
/* $Id: fpm_shm.c,v 1.3 2008/05/24 17:38:47 anight Exp $ */
/* (c) 2007,2008 Andrei Nigmatulin */
#include "fpm_config.h"
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.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
struct fpm_shm_s *fpm_shm_alloc(size_t sz) /* {{{ */
{
struct fpm_shm_s *shm;
shm = malloc(sizeof(*shm));
if (!shm) {
return 0;
}
shm->mem = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (!shm->mem) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "mmap(MAP_ANONYMOUS | MAP_SHARED) failed");
free(shm);
return 0;
}
shm->used = 0;
shm->sz = sz;
return shm;
}
/* }}} */
void fpm_shm_free(struct fpm_shm_s *shm, int do_unmap) /* {{{ */
{
if (do_unmap) {
munmap(shm->mem, shm->sz);
}
free(shm);
}
/* }}} */
void fpm_shm_free_list(struct fpm_shm_s *shm, void *mem) /* {{{ */
{
struct fpm_shm_s *next;
for (; shm; shm = next) {
next = shm->next;
fpm_shm_free(shm, mem != shm->mem);
}
}
/* }}} */
void *fpm_shm_alloc_chunk(struct fpm_shm_s **head, size_t sz, void **mem) /* {{{ */
{
size_t pagesize = getpagesize();
static const size_t cache_line_size = 16;
size_t aligned_sz;
struct fpm_shm_s *shm;
void *ret;
sz = (sz + cache_line_size - 1) & -cache_line_size;
shm = *head;
if (0 == shm || shm->sz - shm->used < sz) {
/* allocate one more shm segment */
aligned_sz = (sz + pagesize - 1) & -pagesize;
shm = fpm_shm_alloc(aligned_sz);
if (!shm) {
return 0;
}
shm->next = *head;
if (shm->next) {
shm->next->prev = shm;
}
shm->prev = 0;
*head = shm;
}
*mem = shm->mem;
ret = (char *) shm->mem + shm->used;
shm->used += sz;
return ret;
}
/* }}} */

23
sapi/fpm/fpm/fpm_shm.h

@ -0,0 +1,23 @@
/* $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
struct fpm_shm_s;
struct fpm_shm_s {
struct fpm_shm_s *prev, *next;
void *mem;
size_t sz;
size_t used;
};
struct fpm_shm_s *fpm_shm_alloc(size_t sz);
void fpm_shm_free(struct fpm_shm_s *shm, int do_unmap);
void fpm_shm_free_list(struct fpm_shm_s *, void *);
void *fpm_shm_alloc_chunk(struct fpm_shm_s **head, size_t sz, void **mem);
#endif

119
sapi/fpm/fpm/fpm_shm_slots.c

@ -0,0 +1,119 @@
/* $Id: fpm_shm_slots.c,v 1.2 2008/05/24 17:38:47 anight Exp $ */
/* (c) 2007,2008 Andrei Nigmatulin */
#include "fpm_config.h"
#include "fpm_atomic.h"
#include "fpm_worker_pool.h"
#include "fpm_children.h"
#include "fpm_shm.h"
#include "fpm_shm_slots.h"
#include "zlog.h"
static void *shm_mem;
static struct fpm_shm_slot_s *shm_slot;
int fpm_shm_slots_prepare_slot(struct fpm_child_s *child) /* {{{ */
{
struct fpm_worker_pool_s *wp = child->wp;
struct fpm_shm_slot_ptr_s *shm_slot_ptr;
child->shm_slot_i = wp->slots_used.used;
shm_slot_ptr = fpm_array_push(&wp->slots_used);
if (0 == shm_slot_ptr) {
return -1;
}
if (0 == wp->slots_free.used) {
shm_slot_ptr->shm_slot = fpm_shm_alloc_chunk(&wp->shm_list, sizeof(struct fpm_shm_slot_s), &shm_slot_ptr->mem);
if (!shm_slot_ptr->shm_slot) {
return -1;
}
} else {
*shm_slot_ptr = *(struct fpm_shm_slot_ptr_s *) fpm_array_item_last(&wp->slots_free);
--wp->slots_free.used;
}
memset(shm_slot_ptr->shm_slot, 0, sizeof(struct fpm_shm_slot_s));
shm_slot_ptr->child = child;
return 0;
}
/* }}} */
void fpm_shm_slots_discard_slot(struct fpm_child_s *child) /* {{{ */
{
struct fpm_shm_slot_ptr_s *shm_slot_ptr;
struct fpm_worker_pool_s *wp = child->wp;
int n;
shm_slot_ptr = fpm_array_push(&wp->slots_free);
if (shm_slot_ptr) {
struct fpm_shm_slot_ptr_s *shm_slot_ptr_used;
shm_slot_ptr_used = fpm_array_item(&wp->slots_used, child->shm_slot_i);
*shm_slot_ptr = *shm_slot_ptr_used;
shm_slot_ptr->child = 0;
}
n = fpm_array_item_remove(&wp->slots_used, child->shm_slot_i);
if (n > -1) {
shm_slot_ptr = fpm_array_item(&wp->slots_used, n);
shm_slot_ptr->child->shm_slot_i = n;
}
}
/* }}} */
void fpm_shm_slots_child_use_slot(struct fpm_child_s *child) /* {{{ */
{
struct fpm_shm_slot_ptr_s *shm_slot_ptr;
struct fpm_worker_pool_s *wp = child->wp;
shm_slot_ptr = fpm_array_item(&wp->slots_used, child->shm_slot_i);
shm_slot = shm_slot_ptr->shm_slot;
shm_mem = shm_slot_ptr->mem;
}
/* }}} */
void fpm_shm_slots_parent_use_slot(struct fpm_child_s *child) /* {{{ */
{
/* nothing to do */
}
/* }}} */
void *fpm_shm_slots_mem() /* {{{ */
{
return shm_mem;
}
/* }}} */
struct fpm_shm_slot_s *fpm_shm_slot(struct fpm_child_s *child) /* {{{ */
{
struct fpm_shm_slot_ptr_s *shm_slot_ptr;
struct fpm_worker_pool_s *wp = child->wp;
shm_slot_ptr = fpm_array_item(&wp->slots_used, child->shm_slot_i);
return shm_slot_ptr->shm_slot;
}
/* }}} */
struct fpm_shm_slot_s *fpm_shm_slots_acquire(struct fpm_shm_slot_s *s, int nohang) /* {{{ */
{
if (s == 0) {
s = shm_slot;
}
if (0 > fpm_spinlock(&s->lock, nohang)) {
return 0;
}
return s;
}
/* }}} */
void fpm_shm_slots_release(struct fpm_shm_slot_s *s) /* {{{ */
{
s->lock = 0;
}
/* }}} */

43
sapi/fpm/fpm/fpm_shm_slots.h

@ -0,0 +1,43 @@
/* $Id: fpm_shm_slots.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
/* (c) 2007,2008 Andrei Nigmatulin */
#ifndef FPM_SHM_SLOTS_H
#define FPM_SHM_SLOTS_H 1
#include "fpm_atomic.h"
#include "fpm_worker_pool.h"
#include "fpm_request.h"
struct fpm_child_s;
struct fpm_shm_slot_s {
union {
atomic_t lock;
char dummy[16];
};
enum fpm_request_stage_e request_stage;
struct timeval accepted;
struct timeval tv;
char request_method[16];
size_t content_length; /* used with POST only */
char script_filename[256];
};
struct fpm_shm_slot_ptr_s {
void *mem;
struct fpm_shm_slot_s *shm_slot;
struct fpm_child_s *child;
};
int fpm_shm_slots_prepare_slot(struct fpm_child_s *child);
void fpm_shm_slots_discard_slot(struct fpm_child_s *child);
void fpm_shm_slots_child_use_slot(struct fpm_child_s *child);
void fpm_shm_slots_parent_use_slot(struct fpm_child_s *child);
void *fpm_shm_slots_mem();
struct fpm_shm_slot_s *fpm_shm_slot(struct fpm_child_s *child);
struct fpm_shm_slot_s *fpm_shm_slots_acquire(struct fpm_shm_slot_s *, int nohang);
void fpm_shm_slots_release(struct fpm_shm_slot_s *);
#endif

251
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_STUFF, ZLOG_SYSERROR, "socketpair() failed");
return -1;
}
if (0 > fd_set_blocked(sp[0], 0) || 0 > fd_set_blocked(sp[1], 0)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fd_set_blocked() failed");
return -1;
}
if (0 > fcntl(sp[0], F_SETFD, FD_CLOEXEC) || 0 > fcntl(sp[1], F_SETFD, FD_CLOEXEC)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fcntl(F_SETFD, FD_CLOEXEC) failed");
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_STUFF, ZLOG_SYSERROR, "sigaction() failed");
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_STUFF, ZLOG_SYSERROR, "sigaction() failed");
return -1;
}
return 0;
}
/* }}} */
int fpm_signals_get_fd() /* {{{ */
{
return sp[0];
}
/* }}} */

16
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

379
sapi/fpm/fpm/fpm_sockets.c

@ -0,0 +1,379 @@
/* $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"
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_STUFF, 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) /* {{{ */
{
int 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 :
{
int 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 backlog = -1;
int flags = 1;
int sock;
mode_t saved_umask;
/* we have custom backlog value */
if (wp->config->listen_options) {
backlog = wp->config->listen_options->backlog;
}
sock = socket(sa->sa_family, SOCK_STREAM, 0);
if (0 > sock) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "socket() failed");
return -1;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
if (wp->listen_address_domain == FPM_AF_UNIX) {
unlink( ((struct sockaddr_un *) sa)->sun_path);
}
saved_umask = umask(0777 ^ wp->socket_mode);
if (0 > bind(sock, sa, socklen)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "bind() for address '%s' failed", wp->config->listen_address);
return -1;
}
if (wp->listen_address_domain == FPM_AF_UNIX) {
char *path = ((struct sockaddr_un *) sa)->sun_path;
if (wp->socket_uid != -1 || wp->socket_gid != -1) {
if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "chown() for address '%s' failed", wp->config->listen_address);
return -1;
}
}
}
umask(saved_umask);
if (0 > listen(sock, backlog)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "listen() for address '%s' failed", 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_STUFF, 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_STUFF, 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));
cpystrn(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() /* {{{ */
{
int i;
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_STUFF, 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) {
if (!wp->is_template) {
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;
}
}
}
/* 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;
}
/* }}} */

40
sapi/fpm/fpm/fpm_sockets.h

@ -0,0 +1,40 @@
/* $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 <unistd.h>
#include <fcntl.h>
#include "fpm_worker_pool.h"
enum fpm_address_domain fpm_sockets_domain_from_address(char *addr);
int fpm_sockets_init_main();
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

282
sapi/fpm/fpm/fpm_status.c

@ -0,0 +1,282 @@
/* $Id$ */
/* (c) 2009 Jerome Loyet */
#include "php.h"
#include <stdio.h>
#include "fpm_config.h"
#include "fpm_status.h"
#include "fpm_clock.h"
#include "zlog.h"
struct fpm_shm_s *fpm_status_shm = NULL;
static char *fpm_status_pool = NULL;
static char *fpm_status_uri = NULL;
static char *fpm_status_ping= NULL;
static char *fpm_status_pong= NULL;
int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
{
if (!wp || !wp->config) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "unable to init fpm_status because conf structure is NULL");
return -1;
}
if (wp->config->pm->status || wp->config->pm->ping) {
if (wp->config->pm->status) {
if (!wp->shm_status) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] unable to init fpm_status because the dedicated SHM has not been set", wp->config->name);
return -1;
}
fpm_status_shm = wp->shm_status;
}
fpm_status_pool = strdup(wp->config->name);
if (wp->config->pm->status) {
fpm_status_uri = strdup(wp->config->pm->status);
}
if (wp->config->pm->ping) {
if (!wp->config->pm->pong) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] ping is set (%s) but pong is not set.", wp->config->name, wp->config->pm->ping);
return -1;
}
fpm_status_ping = strdup(wp->config->pm->ping);
fpm_status_pong = strdup(wp->config->pm->pong);
}
}
return 0;
}
/* }}} */
void fpm_status_set_pm(struct fpm_shm_s *shm, int pm) /* {{{ */
{
struct fpm_status_s status;
if (!shm) shm = fpm_status_shm;
if (!shm || !shm->mem) return;
/* one shot operation */
status = *(struct fpm_status_s *)shm->mem;
status.pm = pm;
/* one shot operation */
*(struct fpm_status_s *)shm->mem = status;
}
/* }}} */
void fpm_status_increment_accepted_conn(struct fpm_shm_s *shm) /* {{{ */
{
struct fpm_status_s status;
if (!shm) shm = fpm_status_shm;
if (!shm || !shm->mem) return;
/* one shot operation */
status = *(struct fpm_status_s *)shm->mem;
status.accepted_conn++;
/* one shot operation */
*(struct fpm_status_s *)shm->mem = status;
}
/* }}} */
void fpm_status_update_accepted_conn(struct fpm_shm_s *shm, unsigned long int accepted_conn) /* {{{ */
{
struct fpm_status_s status;
if (!shm) shm = fpm_status_shm;
if (!shm || !shm->mem) return;
/* one shot operation */
status = *(struct fpm_status_s *)shm->mem;
status.accepted_conn = accepted_conn;
/* one shot operation */
*(struct fpm_status_s *)shm->mem = status;
}
/* }}} */
void fpm_status_update_activity(struct fpm_shm_s *shm, int idle, int active, int total, int clear_last_update) /* {{{ */
{
struct fpm_status_s status;
if (!shm) shm = fpm_status_shm;
if (!shm || !shm->mem) return;
/* one shot operation */
status = *(struct fpm_status_s *)shm->mem;
status.idle = idle;
status.active = active;
status.total = total;
if (clear_last_update) {
memset(&status.last_update, 0, sizeof(status.last_update));
} else {
fpm_clock_get(&status.last_update);
}
/* one shot operation */
*(struct fpm_status_s *)shm->mem = status;
}
/* }}} */
int fpm_status_get(int *idle, int *active, int *total, int *pm) /* {{{ */
{
struct fpm_status_s status;
if (!fpm_status_shm || !fpm_status_shm->mem) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] unable to access status shared memory", fpm_status_pool);
return 0;
}
if (!idle || !active || !total) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] unable to get status information : pointers are NULL", fpm_status_pool);
return 0;
}
/* one shot operation */
status = *(struct fpm_status_s *)fpm_status_shm->mem;
if (idle) {
*idle = status.idle;
}
if (active) {
*active = status.active;
}
if (total) {
*total = status.total;
}
if (pm) {
*pm = status.pm;
}
return 1;
}
/* }}} */
static void fpm_status_handle_status_txt(struct fpm_status_s *status, char **output, char **content_type) /* {{{ */
{
if (!status || !output || !content_type) {
return;
}
spprintf(output, 0,
"accepted conn: %lu\n"
"pool: %s\n"
"process manager: %s\n"
"idle processes: %d\n"
"active processes: %d\n"
"total processes: %d\n",
status->accepted_conn, fpm_status_pool, status->pm == PM_STYLE_STATIC ? "static" : "dynamic", status->idle, status->active, status->total);
spprintf(content_type, 0, "text/plain");
}
/* }}} */
static void fpm_status_handle_status_html(struct fpm_status_s *status, char **output, char **content_type) /* {{{ */
{
if (!status || !output || !content_type) {
return;
}
spprintf(output, 0,
"<table>\n"
"<tr><th>accepted conn</th><td>%lu</td></tr>\n"
"<tr><th>pool</th><td>%s</td></tr>\n"
"<tr><th>process manager</th><td>%s</td></tr>\n"
"<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"
"</table>",
status->accepted_conn, fpm_status_pool, status->pm == PM_STYLE_STATIC ? "static" : "dynamic", status->idle, status->active, status->total);
spprintf(content_type, 0, "text/html");
}
/* }}} */
static void fpm_status_handle_status_json(struct fpm_status_s *status, char **output, char **content_type) /* {{{ */
{
if (!status || !output || !content_type) {
return;
}
spprintf(output, 0,
"{"
"\"accepted conn\":%lu,"
"\"pool\":\"%s\","
"\"process manager\":\"%s\","
"\"idle processes\":%d,"
"\"active processes\":%d,"
"\"total processes\":%d"
"}",
status->accepted_conn, fpm_status_pool, status->pm == PM_STYLE_STATIC ? "static" : "dynamic", status->idle, status->active, status->total);
spprintf(content_type, 0, "application/jsonrequest");
}
/* }}} */
/* return 0 if it's not the request page
* return 1 if ouput has been set)
* *output unchanged: error (return 500)
* *output changed: no error (return 200)
*/
int fpm_status_handle_status(char *uri, char *query_string, char **output, char **content_type) /* {{{ */
{
struct fpm_status_s status;
if (!fpm_status_uri || !uri) {
return 0;
}
/* It's not the status page */
if (strcmp(fpm_status_uri, uri)) {
return 0;
}
if (!output || !content_type || !fpm_status_shm) {
return 1;
}
if (!fpm_status_shm->mem) {
return 1;
}
/* one shot operation */
status = *(struct fpm_status_s *)fpm_status_shm->mem;
if (status.idle < 0 || status.active < 0 || status.total < 0) {
return 1;
}
if (query_string && strstr(query_string, "html")) {
fpm_status_handle_status_html(&status, output, content_type);
} else if (query_string && strstr(query_string, "json")) {
fpm_status_handle_status_json(&status, output, content_type);
} else {
fpm_status_handle_status_txt(&status, output, content_type);
}
if (!*output || !content_type) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] unable to allocate status ouput buffer", fpm_status_pool);
return 1;
}
return 1;
}
/* }}} */
char *fpm_status_handle_ping(char *uri) /* {{{ */
{
if (!fpm_status_ping || !fpm_status_pong || !uri) {
return NULL;
}
/* It's not the status page */
if (strcmp(fpm_status_ping, uri)) {
return NULL;
}
return fpm_status_pong;
}
/* }}} */

32
sapi/fpm/fpm/fpm_status.h

@ -0,0 +1,32 @@
/* $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 long int accepted_conn;
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, 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);
int fpm_status_get(int *idle, int *active, int *total, int *pm);
int fpm_status_handle_status(char *uri, char *query_string, char **output, char **content_type);
char* fpm_status_handle_ping(char *uri);
extern struct fpm_shm_s *fpm_status_shm;
#endif

270
sapi/fpm/fpm/fpm_stdio.c

@ -0,0 +1,270 @@
/* $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 "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_STUFF, ZLOG_SYSERROR, "open(\"/dev/null\") failed");
return -1;
}
if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "dup2() failed");
return -1;
}
close(fd);
return 0;
}
/* }}} */
int fpm_stdio_init_final() /* {{{ */
{
zlog_set_level(fpm_globals.log_level);
if (fpm_global_config.daemonize) {
if (fpm_globals.error_log_fd != STDERR_FILENO) {
/* there might be messages to stderr from libevent, we need to log them all */
if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "dup2() failed");
return -1;
}
}
zlog_set_fd(fpm_globals.error_log_fd);
}
return 0;
}
/* }}} */
int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
{
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_STUFF, ZLOG_SYSERROR, "dup2() failed");
return -1;
}
}
return 0;
}
/* }}} */
static void fpm_stdio_child_said(int fd, short which, void *arg) /* {{{ */
{
static const int max_buf_size = 1024;
char buf[max_buf_size];
struct fpm_child_s *child = arg;
int is_stdout = fd == child->fd_stdout;
struct event *ev = is_stdout ? &child->ev_stdout : &child->ev_stderr;
int fifo_in = 1, fifo_out = 1;
int is_last_message = 0;
int in_buf = 0;
int res;
#if 0
zlog(ZLOG_STUFF, ZLOG_DEBUG, "child %d said %s", (int) child->pid, is_stdout ? "stdout" : "stderr");
#endif
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_STUFF, ZLOG_SYSERROR, "read() failed");
}
fpm_event_del(ev);
is_last_message = 1;
if (is_stdout) {
close(child->fd_stdout);
child->fd_stdout = -1;
} else {
close(child->fd_stderr);
child->fd_stderr = -1;
}
#if 0
if (in_buf == 0 && !fpm_globals.is_child) {
zlog(ZLOG_STUFF, ZLOG_DEBUG, "[pool %s] child %d, %s pipe is closed", child->wp->config->name,
(int) child->pid, is_stdout ? "stdout" : "stderr");
}
#endif
}
} 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_STUFF, 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_STUFF, ZLOG_SYSERROR, "pipe() failed");
return -1;
}
if (0 > pipe(fd_stderr)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "pipe() failed");
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_STUFF, ZLOG_SYSERROR, "fd_set_blocked() failed");
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, struct event_base *base) /* {{{ */
{
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_add(child->fd_stdout, base, &child->ev_stdout, fpm_stdio_child_said, child);
fpm_event_add(child->fd_stderr, base, &child->ev_stderr, fpm_stdio_child_said, child);
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;
fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if (0 > fd) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "open(\"%s\") failed", 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;
}
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
return 0;
}
/* }}} */

20
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, struct event_base *base);
int fpm_stdio_discard_pipes(struct fpm_child_s *child);
int fpm_stdio_open_error_log(int reopen);
#endif

52
sapi/fpm/fpm/fpm_str.h

@ -0,0 +1,52 @@
/* $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 *cpystrn(char *dst, const char *src, size_t dst_size) /* {{{ */
{
char *d, *end;
if (!dst_size) {
return dst;
}
d = dst;
end = dst + dst_size - 1;
for (; d < end; ++d, ++src) {
if (!(*d = *src)) {
return d;
}
}
*d = '\0';
return d;
}
/* }}} */
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

41
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;
}
}
/* }}} */

17
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

99
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_STUFF, ZLOG_ERROR, "mach_vm_read() failed: %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_STUFF, ZLOG_SYSERROR, "kill(SIGSTOP) failed");
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_STUFF, 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;
}
/* }}} */

67
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_STUFF, ZLOG_SYSERROR, "kill(SIGSTOP) failed");
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_STUFF, ZLOG_SYSERROR, "open(%s) failed", 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_STUFF, ZLOG_SYSERROR, "pread() failed");
return -1;
}
return 0;
}
/* }}} */

82
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_STUFF, ZLOG_SYSERROR, "ptrace(ATTACH) failed");
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_STUFF, ZLOG_SYSERROR, "ptrace(DETACH) failed");
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_STUFF, ZLOG_SYSERROR, "ptrace(PT_IO) failed");
return -1;
}
#else
errno = 0;
*data = ptrace(PTRACE_PEEKDATA, traced_pid, (void *) addr, 0);
if (errno) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "ptrace(PEEKDATA) failed");
return -1;
}
#endif
return 0;
}
/* }}} */

261
sapi/fpm/fpm/fpm_unix.c

@ -0,0 +1,261 @@
/* $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 "zlog.h"
size_t fpm_pagesize;
int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
{
struct fpm_listen_options_s *lo = wp->config->listen_options;
/* uninitialized */
wp->socket_uid = -1;
wp->socket_gid = -1;
wp->socket_mode = 0666;
if (!lo) {
return 0;
}
if (lo->owner && *lo->owner) {
struct passwd *pwd;
pwd = getpwnam(lo->owner);
if (!pwd) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, lo->owner);
return -1;
}
wp->socket_uid = pwd->pw_uid;
wp->socket_gid = pwd->pw_gid;
}
if (lo->group && *lo->group) {
struct group *grp;
grp = getgrnam(lo->group);
if (!grp) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, lo->group);
return -1;
}
wp->socket_gid = grp->gr_gid;
}
if (lo->mode && *lo->mode) {
wp->socket_mode = strtoul(lo->mode, 0, 8);
}
return 0;
}
/* }}} */
static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
{
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_STUFF, 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_STUFF, ZLOG_ERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, wp->config->group);
return -1;
}
wp->set_gid = grp->gr_gid;
}
}
#ifndef I_REALLY_WANT_ROOT_PHP
if (wp->set_uid == 0 || wp->set_gid == 0) {
zlog(ZLOG_STUFF, ZLOG_ERROR, "[pool %s] please specify user and group other than root", wp->config->name);
return -1;
}
#endif
} else { /* not root */
if (wp->config->user && *wp->config->user) {
zlog(ZLOG_STUFF, ZLOG_WARNING, "[pool %s] 'user' directive is ignored", wp->config->name);
}
if (wp->config->group && *wp->config->group) {
zlog(ZLOG_STUFF, ZLOG_WARNING, "[pool %s] 'group' directive is ignored", wp->config->name);
}
if (wp->config->chroot && *wp->config->chroot) {
zlog(ZLOG_STUFF, ZLOG_WARNING, "[pool %s] 'chroot' directive is ignored", wp->config->name);
}
{ /* set up HOME and USER anyway */
struct passwd *pwd;
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;
getrlimit(RLIMIT_NOFILE, &r);
r.rlim_cur = (rlim_t) wp->config->rlimit_files;
if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] setrlimit(RLIMIT_NOFILE) failed", wp->config->name);
}
}
if (wp->config->rlimit_core) {
struct rlimit r;
getrlimit(RLIMIT_CORE, &r);
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_STUFF, ZLOG_SYSERROR, "[pool %s] setrlimit(RLIMIT_CORE) failed", wp->config->name);
}
}
if (is_root && wp->config->chroot && *wp->config->chroot) {
if (0 > chroot(wp->config->chroot)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] chroot(%s) failed", 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_STUFF, ZLOG_SYSERROR, "[pool %s] chdir(%s) failed", wp->config->name, wp->config->chdir);
return -1;
}
} else if (made_chroot) {
chdir("/");
}
if (is_root) {
if (wp->set_gid) {
if (0 > setgid(wp->set_gid)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] setgid(%d) failed", wp->config->name, wp->set_gid);
return -1;
}
}
if (wp->set_uid) {
if (0 > initgroups(wp->config->user, wp->set_gid)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] initgroups(%s, %d) failed", wp->config->name, wp->config->user, wp->set_gid);
return -1;
}
if (0 > setuid(wp->set_uid)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] setuid(%d) failed", wp->config->name, wp->set_uid);
return -1;
}
}
}
#ifdef HAVE_PRCTL
if (0 > prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) {
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "[pool %s] prctl(PR_SET_DUMPABLE) failed", wp->config->name);
}
#endif
if (0 > fpm_clock_init()) {
return -1;
}
return 0;
}
/* }}} */
int fpm_unix_init_main() /* {{{ */
{
struct fpm_worker_pool_s *wp;
fpm_pagesize = getpagesize();
if (fpm_global_config.daemonize) {
switch (fork()) {
case -1 :
zlog(ZLOG_STUFF, ZLOG_SYSERROR, "daemonized fork() failed");
return -1;
case 0 :
break;
default :
fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);
exit(0);
}
}
setsid();
if (0 > fpm_clock_init()) {
return -1;
}
fpm_globals.parent_pid = getpid();
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
if (0 > fpm_unix_conf_wp(wp)) {
return -1;
}
}
fpm_stdio_init_final();
{
struct rlimit r;
getrlimit(RLIMIT_NOFILE, &r);
zlog(ZLOG_STUFF, ZLOG_NOTICE, "getrlimit(nofile): max:%lld, cur:%lld",
(long long) r.rlim_max, (long long) r.rlim_cur);
}
return 0;
}
/* }}} */

17
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

72
sapi/fpm/fpm/fpm_worker_pool.c

@ -0,0 +1,72 @@
/* $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_shm_slots.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);
fpm_array_free(&wp->slots_used);
fpm_array_free(&wp->slots_free);
fpm_shm_free_list(wp->shm_list, which == FPM_CLEANUP_CHILD ? fpm_shm_slots_mem() : 0);
if (wp->shm_status && which != FPM_CLEANUP_CHILD) {
fpm_shm_free(wp->shm_status, !fpm_globals.is_child);
}
free(wp->config);
free(wp->user);
free(wp->home);
free(wp);
}
fpm_worker_all_pools = 0;
}
/* }}} */
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));
if (!fpm_worker_all_pools) {
fpm_worker_all_pools = ret;
}
fpm_array_init(&ret->slots_used, sizeof(struct fpm_shm_slot_ptr_s), 50);
fpm_array_init(&ret->slots_free, sizeof(struct fpm_shm_slot_ptr_s), 50);
ret->idle_spawn_rate = 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;
}
/* }}} */

50
sapi/fpm/fpm/fpm_worker_pool.h

@ -0,0 +1,50 @@
/* $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_arrays.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 */
unsigned is_template:1; /* just config template, no processes will be created */
int socket_uid, socket_gid, socket_mode;
struct fpm_shm_s *shm_list;
struct fpm_array_s slots_used;
struct fpm_array_s slots_free;
/* runtime */
struct fpm_child_s *children;
int running_children;
int idle_spawn_rate;
int warn_max_children;
struct fpm_shm_s *shm_status;
};
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

275
sapi/fpm/fpm/xml_config.c

@ -0,0 +1,275 @@
/* $Id: xml_config.c,v 1.9 2008/08/26 15:09:15 anight Exp $ */
/* (c) 2004-2007 Andrei Nigmatulin */
#include "fpm_config.h"
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "xml_config.h"
static struct xml_conf_section **xml_conf_sections = 0;
static int xml_conf_sections_allocated = 0;
static int xml_conf_sections_used = 0;
char *xml_conf_set_slot_boolean(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
long value_y = !strcasecmp(value, "yes") || !strcmp(value, "1") || !strcasecmp(value, "on");
long value_n = !strcasecmp(value, "no") || !strcmp(value, "0") || !strcasecmp(value, "off");
if (!value_y && !value_n) {
return "xml_conf_set_slot(): invalid boolean value";
}
#ifdef XML_CONF_DEBUG
fprintf(stderr, "setting boolean '%s' => %s\n", name, value_y ? "TRUE" : "FALSE");
#endif
* (int *) ((char *) *conf + offset) = value_y ? 1 : 0;
return NULL;
}
/* }}} */
char *xml_conf_set_slot_string(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
char *v = strdup(value);
if (!v) {
return "xml_conf_set_slot_string(): strdup() failed";
}
#ifdef XML_CONF_DEBUG
fprintf(stderr, "setting string '%s' => '%s'\n", name, v);
#endif
* (char **) ((char *) *conf + offset) = v;
return NULL;
}
/* }}} */
char *xml_conf_set_slot_integer(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
int v = atoi(value);
* (int *) ((char *) *conf + offset) = v;
#ifdef XML_CONF_DEBUG
fprintf(stderr, "setting integer '%s' => %d\n", name, v);
#endif
return NULL;
}
/* }}} */
char *xml_conf_set_slot_time(void **conf, char *name, void *vv, intptr_t offset) /* {{{ */
{
char *value = vv;
int len = strlen(value);
char suffix;
int seconds;
if (!len) {
return "xml_conf_set_slot_timeval(): invalid timeval value";
}
suffix = value[len-1];
value[len-1] = '\0';
switch (suffix) {
case 's' :
seconds = atoi(value);
break;
case 'm' :
seconds = 60 * atoi(value);
break;
case 'h' :
seconds = 60 * 60 * atoi(value);
break;
case 'd' :
seconds = 24 * 60 * 60 * atoi(value);
break;
default :
return "xml_conf_set_slot_timeval(): unknown suffix used in timeval value";
}
* (int *) ((char *) *conf + offset) = seconds;
#ifdef XML_CONF_DEBUG
fprintf(stderr, "setting time '%s' => %d:%02d:%02d:%02d\n", name, expand_dhms(seconds));
#endif
return NULL;
}
/* }}} */
char *xml_conf_parse_section(void **conf, struct xml_conf_section *section, void *xml_node) /* {{{ */
{
xmlNode *element = xml_node;
char *ret = 0;
#ifdef XML_CONF_DEBUG
fprintf(stderr, "processing a section %s\n", section->path);
#endif
for ( ; element; element = element->next) {
if (element->type == XML_ELEMENT_NODE && !strcmp((const char *) element->name, "value") && element->children) {
xmlChar *name = xmlGetProp(element, (unsigned char *) "name");
if (name) {
int i;
#ifdef XML_CONF_DEBUG
fprintf(stderr, "found a value: %s\n", name);
#endif
for (i = 0; section->parsers[i].parser; i++) {
if (!section->parsers[i].name || !strcmp(section->parsers[i].name, (char *) name)) {
break;
}
}
if (section->parsers[i].parser) {
if (section->parsers[i].type == XML_CONF_SCALAR) {
if (element->children->type == XML_TEXT_NODE) {
ret = section->parsers[i].parser(conf, (char *) name, element->children->content, section->parsers[i].offset);
} else {
ret = "XML_TEXT_NODE is expected, something different is given";
}
} else {
ret = section->parsers[i].parser(conf, (char *) name, element->children, section->parsers[i].offset);
}
xmlFree(name);
if (ret) {
return ret;
} else {
continue;
}
}
fprintf(stderr, "Warning, unknown setting '%s' in section '%s'\n", (char *) name, section->path);
xmlFree(name);
}
}
}
return NULL;
}
/* }}} */
static char *xml_conf_parse_file(xmlNode *element) /* {{{ */
{
char *ret = 0;
for ( ; element; element = element->next) {
if (element->parent && element->type == XML_ELEMENT_NODE && !strcmp((const char *) element->name, "section")) {
xmlChar *name = xmlGetProp(element, (unsigned char *) "name");
if (name) {
char *parent_name = (char *) xmlGetNodePath(element->parent);
char *full_name;
int i;
struct xml_conf_section *section = NULL;
#ifdef XML_CONF_DEBUG
fprintf(stderr, "got a section: %s/%s\n", parent_name, name);
#endif
full_name = alloca(strlen(parent_name) + strlen((char *) name) + 1 + 1);
sprintf(full_name, "%s/%s", parent_name, (char *) name);
xmlFree(parent_name);
xmlFree(name);
for (i = 0; i < xml_conf_sections_used; i++) {
if (!strcmp(xml_conf_sections[i]->path, full_name)) {
section = xml_conf_sections[i];
}
}
if (section) { /* found a registered section */
void *conf = section->conf();
ret = xml_conf_parse_section(&conf, section, element->children);
if (ret) break;
}
}
}
if (element->children) {
ret = xml_conf_parse_file(element->children);
if (ret) break;
}
}
return ret;
}
/* }}} */
char *xml_conf_load_file(char *file) /* {{{ */
{
char *ret = 0;
xmlDoc *doc;
LIBXML_TEST_VERSION
doc = xmlParseFile(file);
if (doc) {
ret = xml_conf_parse_file(doc->children);
xmlFreeDoc(doc);
} else {
ret = "failed to parse conf file";
}
xmlCleanupParser();
return ret;
}
/* }}} */
int xml_conf_init() /* {{{ */
{
return 0;
}
/* }}} */
void xml_conf_clean() /* {{{ */
{
if (xml_conf_sections) {
free(xml_conf_sections);
}
}
/* }}} */
int xml_conf_section_register(struct xml_conf_section *section) /* {{{ */
{
if (xml_conf_sections_allocated == xml_conf_sections_used) {
int new_size = xml_conf_sections_used + 10;
void *new_ptr = realloc(xml_conf_sections, sizeof(struct xml_conf_section *) * new_size);
if (new_ptr) {
xml_conf_sections = new_ptr;
xml_conf_sections_allocated = new_size;
} else {
fprintf(stderr, "xml_conf_section_register(): out of memory\n");
return -1;
}
}
xml_conf_sections[xml_conf_sections_used++] = section;
return 0;
}
/* }}} */
int xml_conf_sections_register(struct xml_conf_section *sections[]) /* {{{ */
{
for ( ; sections && *sections; sections++) {
if (0 > xml_conf_section_register(*sections)) {
return -1;
}
}
return 0;
}
/* }}} */

47
sapi/fpm/fpm/xml_config.h

@ -0,0 +1,47 @@
/* $Id: xml_config.h,v 1.3 2008/09/18 23:02:58 anight Exp $ */
/* (c) 2004-2007 Andrei Nigmatulin */
#ifndef XML_CONFIG_H
#define XML_CONFIG_H 1
#if HAVE_INTTYPES_H
# include <inttypes.h>
#else
# include <stdint.h>
#endif
struct xml_value_parser;
struct xml_value_parser {
int type;
char *name;
char *(*parser)(void **, char *, void *, intptr_t offset);
intptr_t offset;
};
struct xml_conf_section {
void *(*conf)();
char *path;
struct xml_value_parser *parsers;
};
char *xml_conf_set_slot_boolean(void **conf, char *name, void *value, intptr_t offset);
char *xml_conf_set_slot_string(void **conf, char *name, void *value, intptr_t offset);
char *xml_conf_set_slot_integer(void **conf, char *name, void *value, intptr_t offset);
char *xml_conf_set_slot_time(void **conf, char *name, void *value, intptr_t offset);
int xml_conf_init();
void xml_conf_clean();
char *xml_conf_load_file(char *file);
char *xml_conf_parse_section(void **conf, struct xml_conf_section *section, void *ve);
int xml_conf_section_register(struct xml_conf_section *section);
int xml_conf_sections_register(struct xml_conf_section *sections[]);
#define expand_hms(value) (value) / 3600, ((value) % 3600) / 60, (value) % 60
#define expand_dhms(value) (value) / 86400, ((value) % 86400) / 3600, ((value) % 3600) / 60, (value) % 60
enum { XML_CONF_SCALAR = 1, XML_CONF_SUBSECTION = 2 };
#endif

113
sapi/fpm/fpm/zlog.c

@ -0,0 +1,113 @@
/* $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 "zlog.h"
#define MAX_LINE_LENGTH 1024
static int zlog_fd = -1;
static int zlog_level = ZLOG_NOTICE;
static const char *level_names[] = {
[ZLOG_DEBUG] = "DEBUG",
[ZLOG_NOTICE] = "NOTICE",
[ZLOG_WARNING] = "WARNING",
[ZLOG_ERROR] = "ERROR",
[ZLOG_ALERT] = "ALERT",
};
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, "%b %d %H:%M:%S", localtime_r((const time_t *) &tv->tv_sec, &t));
len += snprintf(timebuf + len, timebuf_len - len, ".%06d", (int) tv->tv_usec);
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;
zlog_level = new_value;
return old_value;
}
/* }}} */
void zlog(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;
int truncated = 0;
int saved_errno;
if ((flags & ZLOG_LEVEL_MASK) < zlog_level) {
return;
}
saved_errno = errno;
gettimeofday(&tv, 0);
len = zlog_print_time(&tv, buf, buf_size);
if (zlog_level == ZLOG_DEBUG) {
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] ", 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;
}
buf[len++] = '\n';
write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len);
}
/* }}} */

34
sapi/fpm/fpm/zlog.h

@ -0,0 +1,34 @@
/* $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_STUFF __func__, __LINE__
struct timeval;
int zlog_set_fd(int new_fd);
int zlog_set_level(int new_value);
size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len);
void zlog(const char *function, int line, int flags, const char *fmt, ...)
__attribute__ ((format(printf,4,5)));
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)
#endif

138
sapi/fpm/init.d.php-fpm.in

@ -0,0 +1,138 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: php-fpm
# Required-Start: $all
# Required-Stop: $all
# 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"
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 $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-exit"
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

186
sapi/fpm/php-fpm.1.in

@ -0,0 +1,186 @@
.TH PHP-FPM 1 "2009" "The PHP Group" "Scripting Language"
.SH NAME
.TP 15
@php_fpm_bin@ \- PHP FastCGI Process Manager 'PHP-FPM'
.SH SYNOPSIS
.B @php_fpm_bin@
[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_log_path@.
.LP
Most options are set in the configuration file. The configuration file is @php_fpm_conf_path@. By default, @php_fpm_bin@ will respond to CGI requests listening on localhost http port 9000. Therefore @php_fpm_bin@ 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
.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_conf_path@)
.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_bin@.conf
The configuration file for the @php_fpm_bin@ daemon.
.TP
.B php.ini
The standard php configuration file.
.SH EXAMPLES
You should use the init script provided to start and stop the @php_fpm_bin@ daemon. This situation applies for any unix systems which use init.d for their main process manager.
.P
.PD 1
.RS
sudo /etc/init.d/@php_fpm_bin@ start
.RE
.TP
If your installation has no appropriate init script, launch @php_fpm_bin_path@ with no arguments. It will launch as a daemon (background process) by default. The file @php_fpm_pid_path@ determines whether @php_fpm_bin@ is already up and running. Once started, @php_fpm_bin@ 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\fP, version @PHP_VERSION@, \fBfpm\fP, version @fpm_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.

206
sapi/fpm/php-fpm.conf.in

@ -0,0 +1,206 @@
<?xml version="1.0" ?>
<configuration>
All relative paths in this config are relative to php's install prefix
<section name="global_options">
Pid file
<value name="pid_file">@EXPANDED_LOCALSTATEDIR@/run/php-fpm.pid</value>
Error log file
<value name="error_log">@EXPANDED_LOCALSTATEDIR@/log/php-fpm.log</value>
Log level
<value name="log_level">notice</value>
When this amount of php processes exited with SIGSEGV or SIGBUS ...
<value name="emergency_restart_threshold">10</value>
... in a less than this interval of time, a graceful restart will be initiated.
Useful to work around accidental curruptions in accelerator's shared memory.
<value name="emergency_restart_interval">1m</value>
Time limit on waiting child's reaction on signals from master
<value name="process_control_timeout">5s</value>
Set to 'no' to debug fpm
<value name="daemonize">yes</value>
</section>
<workers>
<section name="pool">
Name of pool. Used in logs and stats.
<value name="name">default</value>
Address to accept fastcgi requests on.
Valid syntax is 'ip.ad.re.ss:port' or just 'port' or '/path/to/unix/socket'
<value name="listen_address">127.0.0.1:9000</value>
<value name="listen_options">
Set listen(2) backlog
<value name="backlog">-1</value>
Set permissions for unix socket, if one used.
In Linux read/write permissions must be set in order to allow connections from web server.
Many BSD-derrived systems allow connections regardless of permissions.
<value name="owner">@php_fpm_user@</value>
<value name="group">@php_fpm_group@</value>
<value name="mode">0666</value>
</value>
Additional php.ini defines, specific to this pool of workers.
These settings overwrite the values previously defined in the php.ini.
<value name="php_defines">
<!-- <value name="sendmail_path">/usr/sbin/sendmail -t -i</value> -->
<!-- <value name="display_errors">0</value> -->
<!-- <value name="error_log">/var/log/php-error.log</value> -->
<!-- <value name="log_errors">true</value> -->
</value>
Unix user of processes
<value name="user">@php_fpm_user@</value>
Unix group of processes
<value name="group">@php_fpm_group@</value>
Process manager settings
<value name="pm">
Sets style of controling worker process count.
Valid values are 'static' and 'dynamic'
<value name="style">static</value>
Sets the limit on the number of simultaneous requests that will be served.
Equivalent to Apache MaxClients directive.
Equivalent to PHP_FCGI_CHILDREN environment in original php.fcgi
Used with any pm_style.
<value name="max_children">50</value>
Sets the status URI to call to obtain php-fpm status page.
If not set, no URI will be recognized as a status page.
By default, it returns text/plain looking like:
accepted conn: 12073
pool: default
process manager: static
idle processes: 35
active processes: 65
total processes: 100
"accepted conn" : the number of request accepted by the pool
"pool" : the name of the pool
"process manager": static or dynamic
"idle processes": the number of idle processes
"active processes": the number of active processes
"total processes": idle + active
The last three number are uptaded every second.
The "accepted conn" is updated in real time
*** Output ***
By default it returns text/plain
But passing as a query string html or json, it will returns
the corresponding output syntax:
http://www.foo.bar/status
http://www.foo.bar/status?json
http://www.foo.bar/status?html
*** WARNING ***
It has to start with a /. It could be named has you want.
It's maybe not a good idea to use .php extension to be certain
not to conflict with a real PHP file
<value name="status">/status</value>
Set the ping URI to call the monitoring page of php-fpm
If not set, no URI will be recognized as a ping page.
This could be used to test from outside that php-fpm
is alive and responding:
- have a graph of php-fpm availability (rrd or such)
- remove a server from a pool if it's not responding (load balancing systems)
- trigger alerts for the operating team (24/7)
*** WARNING ***
It has to start with a /. It could be named has you want.
It's maybe not a good idea to use .php extension to be certain
not to conflict with a real PHP file
<value name="ping">/ping</value>
Set the response to custom the response of a ping request
If 'pong' is not set, the default is "pong".
The response is text/plain with a 200 response code
<value name="pong">pong</value>
Settings group for 'dynamic' pm style
<value name="dynamic">
Sets the number of server processes created on startup.
Used only when 'dynamic' pm_style is selected
<value name="start_servers">20</value>
Sets the desired minimum number of idle server processes.
Used only when 'dynamic' pm_style is selected
<value name="min_spare_servers">5</value>
Sets the desired maximum number of idle server processes.
Used only when 'dynamic' pm_style is selected
<value name="max_spare_servers">35</value>
</value>
</value>
The timeout (in seconds) for serving a single request after which the worker process will be terminated
Should be used when 'max_execution_time' ini option does not stop script execution for some reason
'0s' means 'off'
<value name="request_terminate_timeout">0s</value>
The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file
'0s' means 'off'
<value name="request_slowlog_timeout">0s</value>
The log file for slow requests
<value name="slowlog">@EXPANDED_LOCALSTATEDIR@/log/php-fpm.log.slow</value>
Set open file desc rlimit
<value name="rlimit_files">1024</value>
Set max core size rlimit
<value name="rlimit_core">0</value>
Chroot to this directory at the start, absolute path
<value name="chroot"></value>
Chdir to this directory at the start, absolute path
<value name="chdir"></value>
Redirect workers' stdout and stderr into main error log.
If not set, they will be redirected to /dev/null, according to FastCGI specs
<value name="catch_workers_output">yes</value>
How much requests each process should execute before respawn.
Useful to work around memory leaks in 3rd party libraries.
For endless request processing please specify 0
Equivalent to PHP_FCGI_MAX_REQUESTS
<value name="max_requests">500</value>
Comma separated list of ipv4 addresses of FastCGI clients that allowed to connect.
Equivalent to FCGI_WEB_SERVER_ADDRS environment in original php.fcgi (5.2.2+)
Makes sense only with AF_INET listening socket.
<value name="allowed_clients">127.0.0.1</value>
Pass environment variables like LD_LIBRARY_PATH
All $VARIABLEs are taken from current environment
<value name="environment">
<value name="HOSTNAME">$HOSTNAME</value>
<value name="PATH">/usr/local/bin:/usr/bin:/bin</value>
<value name="TMP">/tmp</value>
<value name="TMPDIR">/tmp</value>
<value name="TEMP">/tmp</value>
<value name="OSTYPE">$OSTYPE</value>
<value name="MACHTYPE">$MACHTYPE</value>
<value name="MALLOC_CHECK_">2</value>
</value>
</section>
</workers>
</configuration>
Loading…
Cancel
Save