61 changed files with 10702 additions and 0 deletions
-
2sapi/fpm/CREDITS
-
23sapi/fpm/LICENSE
-
25sapi/fpm/Makefile.frag
-
613sapi/fpm/config.m4
-
1321sapi/fpm/fpm/fastcgi.c
-
151sapi/fpm/fpm/fastcgi.h
-
84sapi/fpm/fpm/fpm.c
-
30sapi/fpm/fpm/fpm.h
-
116sapi/fpm/fpm/fpm_arrays.h
-
148sapi/fpm/fpm/fpm_atomic.h
-
439sapi/fpm/fpm/fpm_children.c
-
35sapi/fpm/fpm/fpm_children.h
-
53sapi/fpm/fpm/fpm_cleanup.c
-
21sapi/fpm/fpm/fpm_cleanup.h
-
121sapi/fpm/fpm/fpm_clock.c
-
13sapi/fpm/fpm/fpm_clock.h
-
658sapi/fpm/fpm/fpm_conf.c
-
78sapi/fpm/fpm/fpm_conf.h
-
44sapi/fpm/fpm/fpm_config.h
-
183sapi/fpm/fpm/fpm_env.c
-
24sapi/fpm/fpm/fpm_env.h
-
142sapi/fpm/fpm/fpm_events.c
-
16sapi/fpm/fpm/fpm_events.h
-
1915sapi/fpm/fpm/fpm_main.c
-
198sapi/fpm/fpm/fpm_php.c
-
23sapi/fpm/fpm/fpm_php.h
-
175sapi/fpm/fpm/fpm_php_trace.c
-
13sapi/fpm/fpm/fpm_php_trace.h
-
463sapi/fpm/fpm/fpm_process_ctl.c
-
45sapi/fpm/fpm/fpm_process_ctl.h
-
169sapi/fpm/fpm/fpm_request.c
-
28sapi/fpm/fpm/fpm_request.h
-
101sapi/fpm/fpm/fpm_shm.c
-
23sapi/fpm/fpm/fpm_shm.h
-
119sapi/fpm/fpm/fpm_shm_slots.c
-
43sapi/fpm/fpm/fpm_shm_slots.h
-
251sapi/fpm/fpm/fpm_signals.c
-
16sapi/fpm/fpm/fpm_signals.h
-
379sapi/fpm/fpm/fpm_sockets.c
-
40sapi/fpm/fpm/fpm_sockets.h
-
282sapi/fpm/fpm/fpm_status.c
-
32sapi/fpm/fpm/fpm_status.h
-
270sapi/fpm/fpm/fpm_stdio.c
-
20sapi/fpm/fpm/fpm_stdio.h
-
52sapi/fpm/fpm/fpm_str.h
-
41sapi/fpm/fpm/fpm_trace.c
-
17sapi/fpm/fpm/fpm_trace.h
-
99sapi/fpm/fpm/fpm_trace_mach.c
-
67sapi/fpm/fpm/fpm_trace_pread.c
-
82sapi/fpm/fpm/fpm_trace_ptrace.c
-
261sapi/fpm/fpm/fpm_unix.c
-
17sapi/fpm/fpm/fpm_unix.h
-
72sapi/fpm/fpm/fpm_worker_pool.c
-
50sapi/fpm/fpm/fpm_worker_pool.h
-
275sapi/fpm/fpm/xml_config.c
-
47sapi/fpm/fpm/xml_config.h
-
113sapi/fpm/fpm/zlog.c
-
34sapi/fpm/fpm/zlog.h
-
138sapi/fpm/init.d.php-fpm.in
-
186sapi/fpm/php-fpm.1.in
-
206sapi/fpm/php-fpm.conf.in
@ -0,0 +1,2 @@ |
|||
FastCGI Process Manager |
|||
Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet |
|||
@ -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. |
|||
@ -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 |
|||
|
|||
@ -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
File diff suppressed because it is too large
View File
@ -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 |
|||
*/ |
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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); |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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 |
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
@ -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 |
|||
|
|||
@ -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 |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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); |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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
File diff suppressed because it is too large
View File
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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 |
|||
|
|||
@ -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 |
|||
|
|||
@ -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); |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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); |
|||
} |
|||
/* }}} */ |
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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]; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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 |
|||
@ -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; |
|||
} |
|||
} |
|||
/* }}} */ |
|||
|
|||
|
|||
@ -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 |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
@ -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 |
|||
|
|||
@ -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; |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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); |
|||
} |
|||
/* }}} */ |
|||
|
|||
@ -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 |
|||
@ -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 |
|||
@ -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. |
|||
@ -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> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue