]> git.pld-linux.org Git - packages/php.git/blob - php-fpm.patch
move php.1 manual to -program (link to actual php-cli)
[packages/php.git] / php-fpm.patch
1 diff --git a/configure b/configure
2 index 2d88ed7..c490abf 100755
3 diff --git a/configure.in b/configure.in
4 index e181ac9..4424930 100644
5 --- a/configure.in
6 +++ b/configure.in
7 @@ -295,6 +295,12 @@ if test "$enable_maintainer_zts" = "yes"; then
8    PTHREADS_FLAGS
9  fi
10  
11 +if test "$PHP_FASTCGI" = "yes" -a "$PHP_FPM" = "yes"; then
12 +  PHP_CONFIGURE_PART(Running FastCGI Process Manager checks)
13 +  sinclude(sapi/cgi/fpm/acinclude.m4)
14 +  sinclude(sapi/cgi/fpm/config.m4)
15 +fi
16 +
17  divert(3)
18  
19  dnl ## In diversion 3 we check for compile-time options to the PHP
20 @@ -511,6 +517,7 @@ AC_CHECK_FUNCS(
21  alphasort \
22  asctime_r \
23  chroot \
24 +clearenv \
25  ctime_r \
26  cuserid \
27  crypt \
28 @@ -1253,6 +1260,8 @@ PHP_SUBST_OLD(EXTENSION_DIR)
29  PHP_SUBST_OLD(EXTRA_LDFLAGS)
30  PHP_SUBST_OLD(EXTRA_LDFLAGS_PROGRAM)
31  PHP_SUBST_OLD(EXTRA_LIBS)
32 +PHP_SUBST_OLD(SAPI_EXTRA_LIBS)
33 +PHP_SUBST_OLD(SAPI_EXTRA_DEPS)
34  PHP_SUBST_OLD(ZEND_EXTRA_LIBS)
35  PHP_SUBST_OLD(INCLUDES)
36  PHP_SUBST_OLD(EXTRA_INCLUDES)
37 @@ -1364,7 +1373,7 @@ case $PHP_SAPI in
38      install_targets="$PHP_INSTALL_CLI_TARGET $install_targets"
39      ;;
40    *)
41 -    install_targets="install-sapi $PHP_INSTALL_CLI_TARGET $install_targets"
42 +    install_targets="install-sapi $install_fpm $PHP_INSTALL_CLI_TARGET $install_targets"
43      ;;
44  esac
45  
46 diff --git a/libevent/ChangeLog b/libevent/ChangeLog
47 new file mode 100644
48 index 0000000..c592139
49 diff --git a/libevent/Makefile.am b/libevent/Makefile.am
50 new file mode 100644
51 index 0000000..5ccfd2c
52 diff --git a/libevent/Makefile.in b/libevent/Makefile.in
53 new file mode 100644
54 index 0000000..0600dcc
55 diff --git a/libevent/README b/libevent/README
56 new file mode 100644
57 index 0000000..b065039
58 diff --git a/libevent/aclocal.m4 b/libevent/aclocal.m4
59 new file mode 100644
60 index 0000000..74de4a1
61 diff --git a/libevent/autogen.sh b/libevent/autogen.sh
62 new file mode 100644
63 index 0000000..218dbf4
64 diff --git a/libevent/buffer.c b/libevent/buffer.c
65 new file mode 100644
66 index 0000000..e66080f
67 diff --git a/libevent/compat/sys/_time.h b/libevent/compat/sys/_time.h
68 new file mode 100644
69 index 0000000..8cabb0d
70 diff --git a/libevent/compat/sys/queue.h b/libevent/compat/sys/queue.h
71 new file mode 100644
72 index 0000000..c0956dd
73 diff --git a/libevent/config.h.in b/libevent/config.h.in
74 new file mode 100644
75 index 0000000..d151f87
76 diff --git a/libevent/configure b/libevent/configure
77 new file mode 100644
78 index 0000000..0a74aec
79 diff --git a/libevent/configure.in b/libevent/configure.in
80 new file mode 100644
81 index 0000000..852d3c5
82 diff --git a/libevent/depcomp b/libevent/depcomp
83 new file mode 100644
84 index 0000000..ffcd540
85 diff --git a/libevent/devpoll.c b/libevent/devpoll.c
86 new file mode 100644
87 index 0000000..cbd2730
88 diff --git a/libevent/epoll.c b/libevent/epoll.c
89 new file mode 100644
90 index 0000000..cf3c859
91 diff --git a/libevent/epoll_sub.c b/libevent/epoll_sub.c
92 new file mode 100644
93 index 0000000..431970c
94 diff --git a/libevent/evbuffer.c b/libevent/evbuffer.c
95 new file mode 100644
96 index 0000000..f2179a5
97 diff --git a/libevent/event-config.h b/libevent/event-config.h
98 new file mode 100644
99 index 0000000..0a278b3
100 diff --git a/libevent/event-fpm.h b/libevent/event-fpm.h
101 new file mode 100644
102 index 0000000..8625ca5
103 diff --git a/libevent/event-internal.h b/libevent/event-internal.h
104 new file mode 100644
105 index 0000000..7485f21
106 diff --git a/libevent/event.3 b/libevent/event.3
107 new file mode 100644
108 index 0000000..5b33ec6
109 diff --git a/libevent/event.c b/libevent/event.c
110 new file mode 100644
111 index 0000000..826b7db
112 diff --git a/libevent/event.h b/libevent/event.h
113 new file mode 100644
114 index 0000000..d67ecb5
115 diff --git a/libevent/evhttp.h b/libevent/evhttp.h
116 new file mode 100644
117 index 0000000..0d35f9e
118 diff --git a/libevent/evport.c b/libevent/evport.c
119 new file mode 100644
120 index 0000000..31523b4
121 diff --git a/libevent/evsignal.h b/libevent/evsignal.h
122 new file mode 100644
123 index 0000000..8be9cbd
124 diff --git a/libevent/evutil.c b/libevent/evutil.c
125 new file mode 100644
126 index 0000000..86205b2
127 diff --git a/libevent/evutil.h b/libevent/evutil.h
128 new file mode 100644
129 index 0000000..0a018b8
130 diff --git a/libevent/http-internal.h b/libevent/http-internal.h
131 new file mode 100644
132 index 0000000..bc9a1ed
133 diff --git a/libevent/http.c b/libevent/http.c
134 new file mode 100644
135 index 0000000..1d60fc5
136 diff --git a/libevent/install-sh b/libevent/install-sh
137 new file mode 100644
138 index 0000000..1a83534
139 diff --git a/libevent/kqueue.c b/libevent/kqueue.c
140 new file mode 100644
141 index 0000000..38a1819
142 diff --git a/libevent/log.c b/libevent/log.c
143 new file mode 100644
144 index 0000000..b62a619
145 diff --git a/libevent/log.h b/libevent/log.h
146 new file mode 100644
147 index 0000000..7bc6632
148 diff --git a/libevent/min_heap.h b/libevent/min_heap.h
149 new file mode 100644
150 index 0000000..d47e563
151 diff --git a/libevent/missing b/libevent/missing
152 new file mode 100644
153 index 0000000..09edd88
154 diff --git a/libevent/poll.c b/libevent/poll.c
155 new file mode 100644
156 index 0000000..b67c6ff
157 diff --git a/libevent/select.c b/libevent/select.c
158 new file mode 100644
159 index 0000000..7faafe4
160 diff --git a/libevent/signal.c b/libevent/signal.c
161 new file mode 100644
162 index 0000000..bcaa3f9
163 diff --git a/libevent/strlcpy-internal.h b/libevent/strlcpy-internal.h
164 new file mode 100644
165 index 0000000..22b5f61
166 diff --git a/libevent/strlcpy.c b/libevent/strlcpy.c
167 new file mode 100644
168 index 0000000..a1a413d
169 diff --git a/main/php_config.h.in b/main/php_config.h.in
170 --- a/main/php_config.h.in
171 +++ b/main/php_config.h.in
172 @@ -170,6 +170,9 @@
173  /* Define if you have the chroot function.  */
174  #undef HAVE_CHROOT
175  
176 +/* Define if you have the clearenv function.  */
177 +#undef HAVE_CLEARENV
178 +
179  /* Define if you have the crypt function.  */
180  #undef HAVE_CRYPT
181  
182 @@ -935,6 +938,9 @@
183  /*   */
184  #undef PHP_FASTCGI
185  
186 +/* Is experimental fastcgi process manager code activated */
187 +#undef PHP_FASTCGI_PM
188 +
189  /*   */
190  #undef FORCE_CGI_REDIRECT
191  
192 @@ -944,6 +950,27 @@
193  /*   */
194  #undef ENABLE_PATHINFO_CHECK
195  
196 +/* do we have libxml? */
197 +#undef HAVE_LIBXML
198 +
199 +/* do we have prctl? */
200 +#undef HAVE_PRCTL
201 +
202 +/* do we have clock_gettime? */
203 +#undef HAVE_CLOCK_GETTIME
204 +
205 +/* do we have clock_get_time? */
206 +#undef HAVE_CLOCK_GET_TIME
207 +
208 +/* do we have ptrace? */
209 +#undef HAVE_PTRACE
210 +
211 +/* do we have mach_vm_read? */
212 +#undef HAVE_MACH_VM_READ
213 +
214 +/* /proc/pid/mem interface */
215 +#undef PROC_MEM_FILE
216 +
217  /* Define if system uses EBCDIC */
218  #undef CHARSET_EBCDIC
219  
220 --- php-5.2.17/sapi/cgi/Makefile.frag~  2012-02-27 14:44:23.000000000 +0200
221 +++ php-5.2.17/sapi/cgi/Makefile.frag   2012-02-27 14:46:13.037731342 +0200
222 @@ -1,2 +1,2 @@
223 -$(SAPI_CGI_PATH): libphp_common.la $(PHP_SAPI_OBJS)
224 +$(SAPI_CGI_PATH): libphp_common.la $(PHP_SAPI_OBJS) $(SAPI_EXTRA_DEPS)
225         $(BUILD_CGI)
226 diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
227 --- a/sapi/cgi/cgi_main.c
228 +++ b/sapi/cgi/cgi_main.c
229 @@ -55,6 +55,9 @@
230  #if HAVE_SYS_WAIT_H
231  #include <sys/wait.h>
232  #endif
233 +#if HAVE_FCNTL_H
234 +#include <fcntl.h>
235 +#endif
236  #include "zend.h"
237  #include "zend_extensions.h"
238  #include "php_ini.h"
239 @@ -83,6 +86,11 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
240  #if PHP_FASTCGI
241  #include "fastcgi.h"
242  
243 +#if PHP_FASTCGI_PM
244 +#include "fpm/fpm.h"
245 +#include "fpm/fpm_request.h"
246 +#endif
247 +
248  #ifndef PHP_WIN32
249  /* XXX this will need to change later when threaded fastcgi is
250     implemented.  shane */
251 @@ -115,8 +123,12 @@ static int parent_waiting = 0;
252  static pid_t pgroup;
253  #endif
254  
255 +static int request_body_fd;
256 +
257  #endif
258  
259 +static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC);
260 +
261  #define PHP_MODE_STANDARD      1
262  #define PHP_MODE_HIGHLIGHT     2
263  #define PHP_MODE_INDENT                3
264 @@ -146,6 +158,10 @@ static const opt_struct OPTIONS[] = {
265         {'w', 0, "strip"},
266         {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
267         {'v', 0, "version"},
268 +#if PHP_FASTCGI_PM
269 +       {'x', 0, "fpm"},
270 +       {'y', 1, "fpm-config"},
271 +#endif
272         {'z', 1, "zend-extension"},
273  #if PHP_FASTCGI
274         {'T', 1, "timing"},
275 @@ -170,6 +186,7 @@ typedef struct _php_cgi_globals_struct {
276         zend_bool impersonate;
277  # endif
278  #endif
279 +       char *error_header;
280  } php_cgi_globals_struct;
281  
282  #ifdef ZTS
283 @@ -474,7 +491,28 @@ static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
284  #if PHP_FASTCGI
285                 if (fcgi_is_fastcgi()) {
286                         fcgi_request *request = (fcgi_request*) SG(server_context);
287 -                       tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
288 +
289 +                       if (request_body_fd == -1) {
290 +                               char *request_body_filename = sapi_cgibin_getenv((char *) "REQUEST_BODY_FILE",
291 +                                               sizeof("REQUEST_BODY_FILE")-1 TSRMLS_CC);
292 +
293 +                               if (request_body_filename && *request_body_filename) {
294 +                                       request_body_fd = open(request_body_filename, O_RDONLY);
295 +
296 +                                       if (0 > request_body_fd) {
297 +                                               php_error(E_WARNING, "REQUEST_BODY_FILE: open('%s') failed: %s (%d)",
298 +                                                               request_body_filename, strerror(errno), errno);
299 +                                               return 0;
300 +                                       }
301 +                               }
302 +                       }
303 +
304 +                       /* If REQUEST_BODY_FILE variable not available - read post body from fastcgi stream */
305 +                       if (request_body_fd < 0) {
306 +                               tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
307 +                       } else {
308 +                               tmp_read_bytes = read(request_body_fd, buffer + read_bytes, count_bytes - read_bytes);
309 +                       }
310                 } else {
311                         tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
312                 }
313 @@ -786,7 +824,12 @@ static void php_cgi_usage(char *argv0)
314                            "  -s               Display colour syntax highlighted source.\n"
315                            "  -v               Version number\n"
316                            "  -w               Display source with stripped comments and whitespace.\n"
317 -                          "  -z <file>        Load Zend extension <file>.\n"
318 +#if PHP_FASTCGI_PM
319 +                          "  -x, --fpm        Run in FastCGI process manager mode.\n"
320 +                          "  -y, --fpm-config <file>\n"
321 +                          "                   Specify alternative path to FastCGI process manager config file.\n"
322 +#endif
323 +                          "  -z <file>        Load Zend extension <file>.\n"
324  #if PHP_FASTCGI
325                            "  -T <count>       Measure execution time of script repeated <count> times.\n"
326  #endif
327 @@ -1236,6 +1279,7 @@ PHP_INI_BEGIN()
328  # ifdef PHP_WIN32
329         STD_PHP_INI_ENTRY("fastcgi.impersonate",     "0",  PHP_INI_SYSTEM, OnUpdateBool,   impersonate, php_cgi_globals_struct, php_cgi_globals)
330  # endif
331 +       STD_PHP_INI_ENTRY("fastcgi.error_header",    NULL, PHP_INI_SYSTEM, OnUpdateString, error_header, php_cgi_globals_struct, php_cgi_globals)
332  #endif
333  PHP_INI_END()
334  
335 @@ -1258,6 +1302,7 @@ static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_
336  # ifdef PHP_WIN32
337         php_cgi_globals->impersonate = 0;
338  # endif
339 +       php_cgi_globals->error_header = NULL;
340  #endif
341  }
342  /* }}} */
343 @@ -1290,9 +1335,47 @@ static PHP_MSHUTDOWN_FUNCTION(cgi)
344  static PHP_MINFO_FUNCTION(cgi)
345  {
346         DISPLAY_INI_ENTRIES();
347 +
348 +#if PHP_FASTCGI_PM
349 +
350 +#include "fpm/fpm_autoconf.h"
351 +
352 +       php_info_print_table_start();
353 +       php_info_print_table_row(2, "php-fpm", fpm ? "active" : "inactive");
354 +       php_info_print_table_row(2, "php-fpm version", PHP_FPM_VERSION);
355 +       php_info_print_table_end();
356 +#endif
357 +
358  }
359  /* }}} */
360  
361 +#if PHP_FASTCGI
362 +PHP_FUNCTION(fastcgi_finish_request)
363 +{
364 +       fcgi_request *request = (fcgi_request*) SG(server_context);
365 +
366 +       if (fcgi_is_fastcgi() && request->fd >= 0) {
367 +
368 +               php_end_ob_buffers(1 TSRMLS_CC);
369 +               php_header(TSRMLS_C);
370 +
371 +               fcgi_flush(request, 1);
372 +               fcgi_close(request, 0, 0);
373 +               RETURN_TRUE;
374 +       }
375 +
376 +       RETURN_FALSE;
377 +
378 +}
379 +#endif
380 +
381 +function_entry cgi_fcgi_sapi_functions[] = {
382 +#if PHP_FASTCGI
383 +       PHP_FE(fastcgi_finish_request,                          NULL)
384 +#endif
385 +       {NULL, NULL, NULL}
386 +};
387 +
388  static zend_module_entry cgi_module_entry = {
389         STANDARD_MODULE_HEADER,
390  #if PHP_FASTCGI
391 @@ -1300,7 +1383,7 @@ static zend_module_entry cgi_module_entry = {
392  #else
393         "cgi",
394  #endif
395 -       NULL, 
396 +       cgi_fcgi_sapi_functions, 
397         PHP_MINIT(cgi), 
398         PHP_MSHUTDOWN(cgi), 
399         NULL, 
400 @@ -1340,6 +1423,7 @@ int main(int argc, char *argv[])
401         char *bindpath = NULL;
402         int fcgi_fd = 0;
403         fcgi_request request;
404 +       char *fpm_config = NULL;
405         int repeats = 1;
406         int benchmark = 0;
407  #if HAVE_GETTIMEOFDAY
408 @@ -1460,6 +1544,14 @@ int main(int argc, char *argv[])
409                         case 's': /* generate highlighted HTML from source */
410                                 behavior = PHP_MODE_HIGHLIGHT;
411                                 break;
412 +#if PHP_FASTCGI_PM
413 +                       case 'y':
414 +                               fpm_config = php_optarg;
415 +                               break;
416 +                       case 'x':
417 +                               fpm = 1;
418 +                               break;
419 +#endif
420  
421                 }
422  
423 @@ -1524,6 +1616,19 @@ consult the installation file that came with this distribution, or visit \n\
424  #endif /* FORCE_CGI_REDIRECT */
425  
426  #if PHP_FASTCGI
427 +#if PHP_FASTCGI_PM
428 +       if (fpm) {
429 +               if (0 > fpm_init(argc, argv, fpm_config)) {
430 +                       return FAILURE;
431 +               }
432 +
433 +               fcgi_fd = fpm_run(&max_requests);
434 +
435 +               fcgi_set_is_fastcgi(fastcgi = 1);
436 +       }
437 +       else
438 +#endif
439 +
440         if (bindpath) {
441                 fcgi_fd = fcgi_listen(bindpath, 128);
442                 if (fcgi_fd < 0) {
443 @@ -1538,6 +1643,9 @@ consult the installation file that came with this distribution, or visit \n\
444         
445         if (fastcgi) {
446                 /* How many times to run PHP scripts before dying */
447 +#if PHP_FASTCGI_PM
448 +               if (!fpm)
449 +#endif
450                 if (getenv("PHP_FCGI_MAX_REQUESTS")) {
451                         max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
452                         if (max_requests < 0) {
453 @@ -1555,6 +1663,9 @@ consult the installation file that came with this distribution, or visit \n\
454  
455  #ifndef PHP_WIN32
456         /* Pre-fork, if required */
457 +#if PHP_FASTCGI_PM
458 +       if (!fpm)
459 +#endif
460         if (getenv("PHP_FCGI_CHILDREN")) {
461                 char * children_str = getenv("PHP_FCGI_CHILDREN");
462                 children = atoi(children_str);
463 @@ -1704,6 +1815,8 @@ consult the installation file that came with this distribution, or visit \n\
464  #endif
465  
466  #if PHP_FASTCGI
467 +               request_body_fd = -1;
468 +
469                 SG(server_context) = (void *) &request;
470  #else
471                 SG(server_context) = (void *) 1; /* avoid server_context==NULL checks */
472 @@ -1711,6 +1824,10 @@ consult the installation file that came with this distribution, or visit \n\
473                 init_request_info(TSRMLS_C);
474                 CG(interactive) = 0;
475  
476 +#if PHP_FASTCGI_PM
477 +               if (fpm) fpm_request_info();
478 +#endif
479 +
480                 if (!cgi
481  #if PHP_FASTCGI
482                         && !fastcgi
483 @@ -1994,6 +2111,10 @@ consult the installation file that came with this distribution, or visit \n\
484                         }
485                 }
486  
487 +#if PHP_FASTCGI_PM
488 +               if (fpm) fpm_request_executing();
489 +#endif
490 +
491                 switch (behavior) {
492                         case PHP_MODE_STANDARD:
493                                 php_execute_script(&file_handle TSRMLS_CC);
494 @@ -2046,6 +2167,10 @@ consult the installation file that came with this distribution, or visit \n\
495  
496  #if PHP_FASTCGI
497  fastcgi_request_done:
498 +
499 +               if (request_body_fd != -1) close(request_body_fd);
500 +
501 +               request_body_fd = -2;
502  #endif
503                 {
504                         char *path_translated;
505 @@ -2059,6 +2184,16 @@ fastcgi_request_done:
506                                 SG(request_info).path_translated = path_translated;
507                         }
508  
509 +                       if (EG(exit_status) == 255) {
510 +                               if (CGIG(error_header) && *CGIG(error_header)) {
511 +                                       sapi_header_line ctr = {0};
512 +
513 +                                       ctr.line = CGIG(error_header);
514 +                                       ctr.line_len = strlen(CGIG(error_header));
515 +                                       sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
516 +                               }
517 +                       }
518 +                       
519                         php_request_shutdown((void *) 0);
520                         if (exit_status == 0) {
521                                 exit_status = EG(exit_status);
522 @@ -2096,15 +2231,20 @@ fastcgi_request_done:
523                                 if (bindpath) {
524                                         free(bindpath);
525                                 }
526 -                               if (max_requests != 1) {
527 -                                       /* no need to return exit_status of the last request */
528 -                                       exit_status = 0;
529 -                               }
530                                 break;
531                         }
532                         /* end of fastcgi loop */
533                 }
534                 fcgi_shutdown();
535 +
536 +               if (fcgi_in_shutdown() ||                                                               /* graceful shutdown by a signal */
537 +                               (max_requests && (requests == max_requests))    /* we were told to process max_requests and we are done */
538 +                       ) {
539 +                       exit_status = 0;
540 +               }
541 +               else {
542 +                       exit_status = 255;
543 +               }
544  #endif
545  
546                 if (cgi_sapi_module.php_ini_path_override) {
547 --- php-5.2.17/sapi/cgi/config9.m4~     2012-02-27 14:44:23.000000000 +0200
548 +++ php-5.2.17/sapi/cgi/config9.m4      2012-02-27 14:48:07.279580606 +0200
549 @@ -22,6 +22,10 @@ PHP_ARG_ENABLE(path-info-check,,
550  [  --disable-path-info-check CGI: If this is disabled, paths such as
551                              /info.php/test?a=b will fail to work], yes, no)
552  
553 +PHP_ARG_ENABLE(fpm,,
554 +[  --enable-fpm              FastCGI: If this is enabled, the fastcgi support
555 +                            will include experimental process manager code], no, no)
556 +
557  dnl
558  dnl CGI setup
559  dnl
560 @@ -54,6 +58,20 @@ if test "$PHP_SAPI" = "default"; then
561      AC_DEFINE_UNQUOTED(PHP_FASTCGI, $PHP_ENABLE_FASTCGI, [ ])
562      AC_MSG_RESULT($PHP_FASTCGI)
563  
564 +    dnl --enable-fpm
565 +    if test "$PHP_FASTCGI" = "yes"; then
566 +      AC_MSG_CHECKING(whether to enable FastCGI Process Manager)
567 +      if test "$PHP_FPM" = "yes"; then
568 +        PHP_FASTCGI_PM=1
569 +      else
570 +        PHP_FASTCGI_PM=0
571 +      fi
572 +      AC_MSG_RESULT($PHP_FPM)
573 +    else
574 +      PHP_FASTCGI_PM=0
575 +    fi
576 +    AC_DEFINE_UNQUOTED(PHP_FASTCGI_PM, $PHP_FASTCGI_PM, [Is experimental fastcgi process manager code activated])
577 +
578      dnl --enable-force-cgi-redirect
579      AC_MSG_CHECKING(whether to force Apache CGI redirect)
580      if test "$PHP_FORCE_CGI_REDIRECT" = "yes"; then
581 @@ -111,10 +111,10 @@
582          BUILD_CGI="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) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
583          ;;
584        *darwin*)
585 -        BUILD_CGI="\$(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) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
586 +        BUILD_CGI="\$(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_CGI_PATH)"
587        ;;
588        *)
589 -        BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) libphp_common.la \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
590 +        BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) libphp_common.la \$(PHP_SAPI_OBJS) \$(EXTRA_LIBS) \$(SAPI_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
591        ;;
592      esac
593  
594 diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c
595 --- a/sapi/cgi/fastcgi.c
596 +++ b/sapi/cgi/fastcgi.c
597 @@ -27,6 +27,11 @@
598  #include <stdarg.h>
599  #include <errno.h>
600  
601 +#if PHP_FASTCGI_PM
602 +#include "fpm/fpm.h"
603 +#include "fpm/fpm_request.h"
604 +#endif
605 +
606  #ifdef _WIN32
607  
608  #include <windows.h>
609 @@ -234,6 +239,8 @@ int fcgi_init(void)
610                 } else {
611                         return is_fastcgi = 0;
612                 }
613 +
614 +               fcgi_set_allowed_clients(getenv("FCGI_WEB_SERVER_ADDRS"));
615  #endif
616         }
617         return is_fastcgi;
618 @@ -249,14 +256,26 @@ int fcgi_is_fastcgi(void)
619         }
620  }
621  
622 +void fcgi_set_is_fastcgi(int new_value)
623 +{
624 +       is_fastcgi = new_value;
625 +}
626 +
627 +void fcgi_set_in_shutdown(int new_value)
628 +{
629 +       in_shutdown = new_value;
630 +}
631 +
632  void fcgi_shutdown(void)
633  {
634         if (is_initialized) {
635                 zend_hash_destroy(&fcgi_mgmt_vars);
636         }
637         is_fastcgi = 0;
638 +
639         if (allowed_clients) {
640                 free(allowed_clients);
641 +               allowed_clients = 0;
642         }
643  }
644  
645 @@ -330,6 +349,41 @@ out_fail:
646  }
647  #endif
648  
649 +void fcgi_set_allowed_clients(char *ip)
650 +{
651 +    char *cur, *end;
652 +    int n;
653 +           
654 +    if (ip) {
655 +       ip = strdup(ip);
656 +       cur = ip;
657 +       n = 0;
658 +       while (*cur) {
659 +               if (*cur == ',') n++;
660 +               cur++;
661 +       }
662 +               if (allowed_clients) free(allowed_clients);
663 +       allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
664 +       n = 0;
665 +       cur = ip;
666 +       while (cur) {
667 +               end = strchr(cur, ',');
668 +               if (end) {
669 +                       *end = 0;
670 +                       end++;
671 +               }
672 +               allowed_clients[n] = inet_addr(cur);
673 +               if (allowed_clients[n] == INADDR_NONE) {
674 +                               fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
675 +               }
676 +               n++;
677 +               cur = end;
678 +       }
679 +       allowed_clients[n] = INADDR_NONE;
680 +               free(ip);
681 +       }
682 +}
683 +
684  static int is_port_number(const char *bindpath)
685  {
686         while (*bindpath) {
687 @@ -458,38 +512,6 @@ int fcgi_listen(const char *path, int backlog)
688  
689         if (!tcp) {
690                 chmod(path, 0777);
691 -       } else {
692 -                       char *ip = getenv("FCGI_WEB_SERVER_ADDRS");
693 -                       char *cur, *end;
694 -                       int n;
695 -                       
696 -                       if (ip) {
697 -                               ip = strdup(ip);
698 -                               cur = ip;
699 -                               n = 0;
700 -                               while (*cur) {
701 -                                       if (*cur == ',') n++;
702 -                                       cur++;
703 -                               }
704 -                               allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
705 -                               n = 0;
706 -                               cur = ip;
707 -                               while (cur) {
708 -                                       end = strchr(cur, ',');
709 -                                       if (end) {
710 -                                               *end = 0;
711 -                                               end++;
712 -                                       }
713 -                                       allowed_clients[n] = inet_addr(cur);
714 -                                       if (allowed_clients[n] == INADDR_NONE) {
715 -                                       fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
716 -                                       }
717 -                                       n++;
718 -                                       cur = end;
719 -                               }
720 -                               allowed_clients[n] = INADDR_NONE;
721 -                       free(ip);
722 -               }
723         }
724  
725         if (!is_initialized) {
726 @@ -866,7 +888,7 @@ int fcgi_read(fcgi_request *req, char *str, int len)
727         return n;
728  }
729  
730 -static inline void fcgi_close(fcgi_request *req, int force, int destroy)
731 +void fcgi_close(fcgi_request *req, int force, int destroy)
732  {
733         if (destroy) {
734                 zend_hash_destroy(&req->env);
735 @@ -906,6 +928,10 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy)
736                 close(req->fd);
737  #endif
738                 req->fd = -1;
739 +
740 +#if PHP_FASTCGI_PM
741 +               if (fpm) fpm_request_finished();
742 +#endif
743         }
744  }
745  
746 @@ -953,6 +979,10 @@ int fcgi_accept_request(fcgi_request *req)
747                                         sa_t sa;
748                                         socklen_t len = sizeof(sa);
749  
750 +#if PHP_FASTCGI_PM
751 +                                       if (fpm) fpm_request_accepting();
752 +#endif
753 +
754                                         FCGI_LOCK(req->listen_socket);
755                                         req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
756                                         FCGI_UNLOCK(req->listen_socket);
757 @@ -988,6 +1018,11 @@ int fcgi_accept_request(fcgi_request *req)
758                                 break;
759  #else
760                                 if (req->fd >= 0) {
761 +
762 +#if PHP_FASTCGI_PM
763 +                                       if (fpm) fpm_request_reading_headers();
764 +#endif
765 +
766  #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
767                                         struct pollfd fds;
768                                         int ret;
769 diff --git a/sapi/cgi/fastcgi.h b/sapi/cgi/fastcgi.h
770 --- a/sapi/cgi/fastcgi.h
771 +++ b/sapi/cgi/fastcgi.h
772 @@ -114,6 +114,9 @@ typedef struct _fcgi_request {
773  int fcgi_init(void);
774  void fcgi_shutdown(void);
775  int fcgi_is_fastcgi(void);
776 +void fcgi_set_is_fastcgi(int);
777 +void fcgi_set_in_shutdown(int);
778 +void fcgi_set_allowed_clients(char *);
779  int fcgi_in_shutdown(void);
780  int fcgi_listen(const char *path, int backlog);
781  void fcgi_init_request(fcgi_request *req, int listen_socket);
782 @@ -128,6 +131,8 @@ int fcgi_read(fcgi_request *req, char *str, int len);
783  int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len);
784  int fcgi_flush(fcgi_request *req, int close);
785  
786 +void fcgi_close(fcgi_request *req, int force, int destroy);
787 +
788  #ifdef PHP_WIN32
789  void fcgi_impersonate(void);
790  #endif
791 diff --git a/sapi/cgi/fpm/Makefile.frag b/sapi/cgi/fpm/Makefile.frag
792 new file mode 100644
793 --- /dev/null
794 +++ b/sapi/cgi/fpm/Makefile.frag
795 @@ -0,0 +1,21 @@
796 +
797 +install-fpm: sapi/cgi/fpm/php-fpm.conf sapi/cgi/fpm/php-fpm
798 +       @echo "Installing FPM config:            $(INSTALL_ROOT)$(php_fpm_conf_path)"
799 +       -@$(mkinstalldirs) \
800 +               $(INSTALL_ROOT)$(prefix)/sbin \
801 +               `dirname "$(INSTALL_ROOT)$(php_fpm_conf_path)"` \
802 +               `dirname "$(INSTALL_ROOT)$(php_fpm_log_path)"` \
803 +               `dirname "$(INSTALL_ROOT)$(php_fpm_pid_path)"`
804 +       -@if test -r "$(INSTALL_ROOT)$(php_fpm_conf_path)" ; then \
805 +               dest=`basename "$(php_fpm_conf_path)"`.default ; \
806 +               echo "                                  (installing as $$dest)" ; \
807 +       else \
808 +               dest=`basename "$(php_fpm_conf_path)"` ; \
809 +       fi ; \
810 +       $(INSTALL_DATA) $(top_builddir)/sapi/cgi/fpm/php-fpm.conf $(INSTALL_ROOT)`dirname "$(php_fpm_conf_path)"`/$$dest
811 +       @echo "Installing init.d script:         $(INSTALL_ROOT)$(prefix)/sbin/php-fpm"
812 +       -@$(INSTALL) -m 0755 $(top_builddir)/sapi/cgi/fpm/php-fpm $(INSTALL_ROOT)$(prefix)/sbin/php-fpm
813 +
814 +$(top_builddir)/libevent/libevent.a: $(top_builddir)/libevent/Makefile
815 +       cd $(top_builddir)/libevent && $(MAKE) libevent.a
816 +
817 diff --git a/sapi/cgi/fpm/acinclude.m4 b/sapi/cgi/fpm/acinclude.m4
818 new file mode 100644
819 --- /dev/null
820 +++ b/sapi/cgi/fpm/acinclude.m4
821 @@ -0,0 +1,377 @@
822 +
823 +AC_DEFUN([AC_FPM_CHECK_FUNC],
824 +[
825 +       SAVED_CFLAGS="$CFLAGS"
826 +       CFLAGS="$CFLAGS $2"
827 +       SAVED_LIBS="$LIBS"
828 +       LIBS="$LIBS $3"
829 +
830 +       AC_CHECK_FUNC([$1],[$4],[$5])
831 +
832 +       CFLAGS="$SAVED_CFLAGS"
833 +       LIBS="$SAVED_LIBS"
834 +])
835 +
836 +AC_DEFUN([AC_FPM_LIBEVENT],
837 +[
838 +       AC_ARG_WITH([libevent],
839 +       [  --with-libevent=DIR         FPM: libevent install directory])
840 +
841 +       LIBEVENT_CFLAGS=""
842 +       LIBEVENT_LIBS="-levent"
843 +       LIBEVENT_INCLUDE_PATH=""
844 +
845 +       if test "$with_libevent" != "no" -a -n "$with_libevent"; then
846 +               LIBEVENT_CFLAGS="-I$with_libevent/include"
847 +               LIBEVENT_LIBS="-L$with_libevent/lib $LIBEVENT_LIBS"
848 +               LIBEVENT_INCLUDE_PATH="$with_libevent/include"
849 +       fi
850 +
851 +       AC_MSG_CHECKING([for event.h])
852 +
853 +       found=no
854 +
855 +       for dir in "$LIBEVENT_INCLUDE_PATH" /usr/include ; do
856 +               if test -r "$dir/event.h" ; then
857 +                       found=yes
858 +                       break
859 +               fi
860 +       done
861 +
862 +       AC_MSG_RESULT([$found])
863 +
864 +       AC_FPM_CHECK_FUNC([event_set], [$LIBEVENT_CFLAGS], [$LIBEVENT_LIBS], ,
865 +               [AC_MSG_ERROR([Failed to link with libevent. Perhaps --with-libevent=DIR option could help.])])
866 +
867 +       AC_FPM_CHECK_FUNC([event_base_free], [$LIBEVENT_CFLAGS], [$LIBEVENT_LIBS], ,
868 +               [AC_MSG_ERROR([You have too old version. libevent version >= 1.2 is required.])])
869 +
870 +])
871 +
872 +AC_DEFUN([AC_FPM_LIBXML],
873 +[
874 +       AC_MSG_RESULT([checking for XML configuration])
875 +
876 +       AC_ARG_WITH(xml-config,
877 +       [  --with-xml-config=PATH      FPM: use xml-config in PATH to find libxml],
878 +               [XMLCONFIG="$withval"],
879 +               [AC_PATH_PROGS(XMLCONFIG, [xml2-config xml-config], "")]
880 +       )
881 +
882 +       if test "x$XMLCONFIG" = "x"; then
883 +               AC_MSG_ERROR([XML configuration could not be found])
884 +       else
885 +        AC_MSG_CHECKING([for libxml library])
886 +
887 +               if test ! -x "$XMLCONFIG"; then
888 +                       AC_MSG_ERROR([$XMLCONFIG cannot be executed])
889 +               fi
890 +
891 +               LIBXML_LIBS="`$XMLCONFIG --libs`"
892 +               LIBXML_CFLAGS="`$XMLCONFIG --cflags`"
893 +               LIBXML_VERSION="`$XMLCONFIG --version`"
894 +
895 +        AC_MSG_RESULT([yes, $LIBXML_VERSION])
896 +
897 +               AC_FPM_CHECK_FUNC([xmlParseFile], [$LIBXML_CFLAGS], [$LIBXML_LIBS], ,
898 +                       [AC_MSG_ERROR([Failed to link with libxml])])
899 +
900 +               AC_DEFINE(HAVE_LIBXML, 1, [do we have libxml?])
901 +       fi
902 +])
903 +
904 +AC_DEFUN([AC_FPM_JUDY],
905 +[
906 +       AC_ARG_WITH([Judy],
907 +       [  --with-Judy=DIR             FPM: Judy install directory])
908 +
909 +       JUDY_CFLAGS=""
910 +       JUDY_LIBS="-lJudy"
911 +       JUDY_INCLUDE_PATH=""
912 +
913 +       if test "$with_Judy" != "no" -a -n "$with_Judy"; then
914 +               JUDY_INCLUDE_PATH="$with_Judy/include"
915 +               JUDY_CFLAGS="-I$with_Judy/include $JUDY_CFLAGS"
916 +               JUDY_LIBS="-L$with_Judy/lib $JUDY_LIBS"
917 +       fi
918 +
919 +       AC_MSG_CHECKING([for Judy.h])
920 +
921 +       found=no
922 +
923 +       for dir in "$JUDY_INCLUDE_PATH" /usr/include ; do
924 +               if test -r "$dir/Judy.h" ; then
925 +                       found=yes
926 +                       break
927 +               fi
928 +       done
929 +
930 +       AC_MSG_RESULT([$found])
931 +
932 +       AC_FPM_CHECK_FUNC([JudyLCount], [$JUDY_CFLAGS], [$JUDY_LIBS], ,
933 +               [AC_MSG_ERROR([Failed to link with Judy])])
934 +
935 +])
936 +
937 +AC_DEFUN([AC_FPM_CLOCK],
938 +[
939 +       have_clock_gettime=no
940 +
941 +       AC_MSG_CHECKING([for clock_gettime])
942 +
943 +       AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
944 +               have_clock_gettime=yes
945 +               AC_MSG_RESULT([yes])
946 +       ], [
947 +               AC_MSG_RESULT([no])
948 +       ])
949 +
950 +       if test "$have_clock_gettime" = "no"; then
951 +               AC_MSG_CHECKING([for clock_gettime in -lrt])
952 +
953 +               SAVED_LIBS="$LIBS"
954 +               LIBS="$LIBS -lrt"
955 +
956 +               AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
957 +                       have_clock_gettime=yes
958 +                       AC_MSG_RESULT([yes])
959 +               ], [
960 +                       LIBS="$SAVED_LIBS"
961 +                       AC_MSG_RESULT([no])
962 +               ])
963 +       fi
964 +
965 +       if test "$have_clock_gettime" = "yes"; then
966 +               AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [do we have clock_gettime?])
967 +       fi
968 +
969 +       have_clock_get_time=no
970 +
971 +       if test "$have_clock_gettime" = "no"; then
972 +               AC_MSG_CHECKING([for clock_get_time])
973 +
974 +               AC_TRY_RUN([ #include <mach/mach.h>
975 +                       #include <mach/clock.h>
976 +                       #include <mach/mach_error.h>
977 +
978 +                       int main()
979 +                       {
980 +                               kern_return_t ret; clock_serv_t aClock; mach_timespec_t aTime;
981 +                               ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &aClock);
982 +
983 +                               if (ret != KERN_SUCCESS) {
984 +                                       return 1;
985 +                               }
986 +
987 +                               ret = clock_get_time(aClock, &aTime);
988 +                               if (ret != KERN_SUCCESS) {
989 +                                       return 2;
990 +                               }
991 +
992 +                               return 0;
993 +                       }
994 +               ], [
995 +                       have_clock_get_time=yes
996 +                       AC_MSG_RESULT([yes])
997 +               ], [
998 +                       AC_MSG_RESULT([no])
999 +               ])
1000 +       fi
1001 +
1002 +       if test "$have_clock_get_time" = "yes"; then
1003 +               AC_DEFINE([HAVE_CLOCK_GET_TIME], 1, [do we have clock_get_time?])
1004 +       fi
1005 +])
1006 +
1007 +AC_DEFUN([AC_FPM_TRACE],
1008 +[
1009 +       have_ptrace=no
1010 +       have_broken_ptrace=no
1011 +
1012 +       AC_MSG_CHECKING([for ptrace])
1013 +
1014 +       AC_TRY_COMPILE([
1015 +               #include <sys/types.h>
1016 +               #include <sys/ptrace.h> ], [ptrace(0, 0, (void *) 0, 0);], [
1017 +               have_ptrace=yes
1018 +               AC_MSG_RESULT([yes])
1019 +       ], [
1020 +               AC_MSG_RESULT([no])
1021 +       ])
1022 +
1023 +       if test "$have_ptrace" = "yes"; then
1024 +               AC_MSG_CHECKING([whether ptrace works])
1025 +
1026 +               AC_TRY_RUN([
1027 +                       #include <unistd.h>
1028 +                       #include <signal.h>
1029 +                       #include <sys/wait.h>
1030 +                       #include <sys/types.h>
1031 +                       #include <sys/ptrace.h>
1032 +                       #include <errno.h>
1033 +
1034 +                       #if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
1035 +                       #define PTRACE_ATTACH PT_ATTACH
1036 +                       #endif
1037 +
1038 +                       #if !defined(PTRACE_DETACH) && defined(PT_DETACH)
1039 +                       #define PTRACE_DETACH PT_DETACH
1040 +                       #endif
1041 +
1042 +                       #if !defined(PTRACE_PEEKDATA) && defined(PT_READ_D)
1043 +                       #define PTRACE_PEEKDATA PT_READ_D
1044 +                       #endif
1045 +
1046 +                       int main()
1047 +                       {
1048 +                               long v1 = (unsigned int) -1; /* copy will fail if sizeof(long) == 8 and we've got "int ptrace()" */
1049 +                               long v2;
1050 +                               pid_t child;
1051 +                               int status;
1052 +
1053 +                               if ( (child = fork()) ) { /* parent */
1054 +                                       int ret = 0;
1055 +
1056 +                                       if (0 > ptrace(PTRACE_ATTACH, child, 0, 0)) {
1057 +                                               return 1;
1058 +                                       }
1059 +
1060 +                                       waitpid(child, &status, 0);
1061 +
1062 +                       #ifdef PT_IO
1063 +                                       struct ptrace_io_desc ptio = {
1064 +                                               .piod_op = PIOD_READ_D,
1065 +                                               .piod_offs = &v1,
1066 +                                               .piod_addr = &v2,
1067 +                                               .piod_len = sizeof(v1)
1068 +                                       };
1069 +
1070 +                                       if (0 > ptrace(PT_IO, child, (void *) &ptio, 0)) {
1071 +                                               ret = 1;
1072 +                                       }
1073 +                       #else
1074 +                                       errno = 0;
1075 +
1076 +                                       v2 = ptrace(PTRACE_PEEKDATA, child, (void *) &v1, 0);
1077 +
1078 +                                       if (errno) {
1079 +                                               ret = 1;
1080 +                                       }
1081 +                       #endif
1082 +                                       ptrace(PTRACE_DETACH, child, (void *) 1, 0);
1083 +
1084 +                                       kill(child, SIGKILL);
1085 +
1086 +                                       return ret ? ret : (v1 != v2);
1087 +                               }
1088 +                               else { /* child */
1089 +                                       sleep(10);
1090 +                                       return 0;
1091 +                               }
1092 +                       }
1093 +               ], [
1094 +                       AC_MSG_RESULT([yes])
1095 +               ], [
1096 +                       have_ptrace=no
1097 +                       have_broken_ptrace=yes
1098 +                       AC_MSG_RESULT([no])
1099 +               ])
1100 +       fi
1101 +
1102 +       if test "$have_ptrace" = "yes"; then
1103 +               AC_DEFINE([HAVE_PTRACE], 1, [do we have ptrace?])
1104 +       fi
1105 +
1106 +       have_mach_vm_read=no
1107 +
1108 +       if test "$have_broken_ptrace" = "yes"; then
1109 +               AC_MSG_CHECKING([for mach_vm_read])
1110 +
1111 +               AC_TRY_COMPILE([ #include <mach/mach.h>
1112 +                       #include <mach/mach_vm.h>
1113 +               ], [
1114 +                       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);
1115 +               ], [
1116 +                       have_mach_vm_read=yes
1117 +                       AC_MSG_RESULT([yes])
1118 +               ], [
1119 +                       AC_MSG_RESULT([no])
1120 +               ])
1121 +       fi
1122 +
1123 +       if test "$have_mach_vm_read" = "yes"; then
1124 +               AC_DEFINE([HAVE_MACH_VM_READ], 1, [do we have mach_vm_read?])
1125 +       fi
1126 +
1127 +       proc_mem_file=""
1128 +
1129 +       if test -r /proc/$$/mem ; then
1130 +               proc_mem_file="mem"
1131 +       else
1132 +               if test -r /proc/$$/as ; then
1133 +                       proc_mem_file="as"
1134 +               fi
1135 +       fi
1136 +
1137 +       if test -n "$proc_mem_file" ; then
1138 +               AC_MSG_CHECKING([for proc mem file])
1139 +
1140 +               AC_TRY_RUN([
1141 +                       #define _GNU_SOURCE
1142 +                       #define _FILE_OFFSET_BITS 64
1143 +                       #include <stdint.h>
1144 +                       #include <unistd.h>
1145 +                       #include <sys/types.h>
1146 +                       #include <sys/stat.h>
1147 +                       #include <fcntl.h>
1148 +                       #include <stdio.h>
1149 +                       int main()
1150 +                       {
1151 +                               long v1 = (unsigned int) -1, v2 = 0;
1152 +                               char buf[128];
1153 +                               int fd;
1154 +                               sprintf(buf, "/proc/%d/$proc_mem_file", getpid());
1155 +                               fd = open(buf, O_RDONLY);
1156 +                               if (0 > fd) {
1157 +                                       return 1;
1158 +                               }
1159 +                               if (sizeof(long) != pread(fd, &v2, sizeof(long), (uintptr_t) &v1)) {
1160 +                                       close(fd);
1161 +                                       return 1;
1162 +                               }
1163 +                               close(fd);
1164 +                               return v1 != v2;
1165 +                       }
1166 +               ], [
1167 +                       AC_MSG_RESULT([$proc_mem_file])
1168 +               ], [
1169 +                       proc_mem_file=""
1170 +                       AC_MSG_RESULT([no])
1171 +               ])
1172 +       fi
1173 +
1174 +       if test -n "$proc_mem_file"; then
1175 +               AC_DEFINE_UNQUOTED([PROC_MEM_FILE], "$proc_mem_file", [/proc/pid/mem interface])
1176 +       fi
1177 +
1178 +       if test "$have_ptrace" = "yes"; then
1179 +               FPM_SOURCES="$FPM_SOURCES fpm_trace.c fpm_trace_ptrace.c"
1180 +       elif test -n "$proc_mem_file"; then
1181 +               FPM_SOURCES="$FPM_SOURCES fpm_trace.c fpm_trace_pread.c"
1182 +       elif test "$have_mach_vm_read" = "yes" ; then
1183 +               FPM_SOURCES="$FPM_SOURCES fpm_trace.c fpm_trace_mach.c"
1184 +       fi
1185 +
1186 +])
1187 +
1188 +AC_DEFUN([AC_FPM_PRCTL],
1189 +[
1190 +       AC_MSG_CHECKING([for prctl])
1191 +
1192 +       AC_TRY_COMPILE([ #include <sys/prctl.h> ], [prctl(0, 0, 0, 0, 0);], [
1193 +               AC_DEFINE([HAVE_PRCTL], 1, [do we have prctl?])
1194 +               AC_MSG_RESULT([yes])
1195 +       ], [
1196 +               AC_MSG_RESULT([no])
1197 +       ])
1198 +])
1199 diff --git a/sapi/cgi/fpm/conf/php-fpm.conf.in b/sapi/cgi/fpm/conf/php-fpm.conf.in
1200 new file mode 100644
1201 --- /dev/null
1202 +++ b/sapi/cgi/fpm/conf/php-fpm.conf.in
1203 @@ -0,0 +1,156 @@
1204 +<?xml version="1.0" ?>
1205 +<configuration>
1206 +
1207 +       All relative paths in this config are relative to php's install prefix
1208 +
1209 +       <section name="global_options">
1210 +
1211 +               Pid file
1212 +               <value name="pid_file">@php_fpm_pid_path@</value>
1213 +
1214 +               Error log file
1215 +               <value name="error_log">@php_fpm_log_path@</value>
1216 +
1217 +               Log level
1218 +               <value name="log_level">notice</value>
1219 +
1220 +               When this amount of php processes exited with SIGSEGV or SIGBUS ...
1221 +               <value name="emergency_restart_threshold">10</value>
1222 +
1223 +               ... in a less than this interval of time, a graceful restart will be initiated.
1224 +               Useful to work around accidental curruptions in accelerator's shared memory.
1225 +               <value name="emergency_restart_interval">1m</value>
1226 +
1227 +               Time limit on waiting child's reaction on signals from master
1228 +               <value name="process_control_timeout">5s</value>
1229 +
1230 +               Set to 'no' to debug fpm
1231 +               <value name="daemonize">yes</value>
1232 +
1233 +       </section>
1234 +
1235 +       <workers>
1236 +
1237 +               <section name="pool">
1238 +
1239 +                       Name of pool. Used in logs and stats.
1240 +                       <value name="name">default</value>
1241 +
1242 +                       Address to accept fastcgi requests on.
1243 +                       Valid syntax is 'ip.ad.re.ss:port' or just 'port' or '/path/to/unix/socket'
1244 +                       <value name="listen_address">127.0.0.1:9000</value>
1245 +
1246 +                       <value name="listen_options">
1247 +
1248 +                               Set listen(2) backlog
1249 +                               <value name="backlog">-1</value>
1250 +
1251 +                               Set permissions for unix socket, if one used.
1252 +                               In Linux read/write permissions must be set in order to allow connections from web server.
1253 +                               Many BSD-derrived systems allow connections regardless of permissions.
1254 +                               <value name="owner"></value>
1255 +                               <value name="group"></value>
1256 +                               <value name="mode">0666</value>
1257 +                       </value>
1258 +
1259 +                       Additional php.ini defines, specific to this pool of workers.
1260 +                       <value name="php_defines">
1261 +               <!--            <value name="sendmail_path">/usr/sbin/sendmail -t -i</value>            -->
1262 +               <!--            <value name="display_errors">0</value>                                                          -->
1263 +                       </value>
1264 +
1265 +                       Unix user of processes
1266 +               <!--    <value name="user">nobody</value>                               -->
1267 +
1268 +                       Unix group of processes
1269 +               <!--    <value name="group">@php_fpm_group@</value>             -->
1270 +
1271 +                       Process manager settings
1272 +                       <value name="pm">
1273 +
1274 +                               Sets style of controling worker process count.
1275 +                               Valid values are 'static' and 'apache-like'
1276 +                               <value name="style">static</value>
1277 +
1278 +                               Sets the limit on the number of simultaneous requests that will be served.
1279 +                               Equivalent to Apache MaxClients directive.
1280 +                               Equivalent to PHP_FCGI_CHILDREN environment in original php.fcgi
1281 +                               Used with any pm_style.
1282 +                               <value name="max_children">5</value>
1283 +
1284 +                               Settings group for 'apache-like' pm style
1285 +                               <value name="apache_like">
1286 +
1287 +                                       Sets the number of server processes created on startup.
1288 +                                       Used only when 'apache-like' pm_style is selected
1289 +                                       <value name="StartServers">20</value>
1290 +
1291 +                                       Sets the desired minimum number of idle server processes.
1292 +                                       Used only when 'apache-like' pm_style is selected
1293 +                                       <value name="MinSpareServers">5</value>
1294 +
1295 +                                       Sets the desired maximum number of idle server processes.
1296 +                                       Used only when 'apache-like' pm_style is selected
1297 +                                       <value name="MaxSpareServers">35</value>
1298 +
1299 +                               </value>
1300 +
1301 +                       </value>
1302 +
1303 +                       The timeout (in seconds) for serving a single request after which the worker process will be terminated
1304 +                       Should be used when 'max_execution_time' ini option does not stop script execution for some reason
1305 +                       '0s' means 'off'
1306 +                       <value name="request_terminate_timeout">0s</value>
1307 +
1308 +                       The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file
1309 +                       '0s' means 'off'
1310 +                       <value name="request_slowlog_timeout">0s</value>
1311 +
1312 +                       The log file for slow requests
1313 +                       <value name="slowlog">logs/slow.log</value>
1314 +
1315 +                       Set open file desc rlimit
1316 +                       <value name="rlimit_files">1024</value>
1317 +
1318 +                       Set max core size rlimit
1319 +                       <value name="rlimit_core">0</value>
1320 +
1321 +                       Chroot to this directory at the start, absolute path
1322 +                       <value name="chroot"></value>
1323 +
1324 +                       Chdir to this directory at the start, absolute path
1325 +                       <value name="chdir"></value>
1326 +
1327 +                       Redirect workers' stdout and stderr into main error log.
1328 +                       If not set, they will be redirected to /dev/null, according to FastCGI specs
1329 +                       <value name="catch_workers_output">yes</value>
1330 +
1331 +                       How much requests each process should execute before respawn.
1332 +                       Useful to work around memory leaks in 3rd party libraries.
1333 +                       For endless request processing please specify 0
1334 +                       Equivalent to PHP_FCGI_MAX_REQUESTS
1335 +                       <value name="max_requests">500</value>
1336 +
1337 +                       Comma separated list of ipv4 addresses of FastCGI clients that allowed to connect.
1338 +                       Equivalent to FCGI_WEB_SERVER_ADDRS environment in original php.fcgi (5.2.2+)
1339 +                       Makes sense only with AF_INET listening socket.
1340 +                       <value name="allowed_clients">127.0.0.1</value>
1341 +
1342 +                       Pass environment variables like LD_LIBRARY_PATH
1343 +                       All $VARIABLEs are taken from current environment
1344 +                       <value name="environment">
1345 +                               <value name="HOSTNAME">$HOSTNAME</value>
1346 +                               <value name="PATH">/usr/local/bin:/usr/bin:/bin</value>
1347 +                               <value name="TMP">/tmp</value>
1348 +                               <value name="TMPDIR">/tmp</value>
1349 +                               <value name="TEMP">/tmp</value>
1350 +                               <value name="OSTYPE">$OSTYPE</value>
1351 +                               <value name="MACHTYPE">$MACHTYPE</value>
1352 +                               <value name="MALLOC_CHECK_">2</value>
1353 +                       </value>
1354 +
1355 +               </section>
1356 +
1357 +       </workers>
1358 +
1359 +</configuration>
1360 diff --git a/sapi/cgi/fpm/config.m4 b/sapi/cgi/fpm/config.m4
1361 new file mode 100644
1362 --- /dev/null
1363 +++ b/sapi/cgi/fpm/config.m4
1364 @@ -0,0 +1,117 @@
1365 +
1366 +FPM_VERSION="0.5.14"
1367 +
1368 +PHP_ARG_WITH(fpm-conf, for php-fpm config file path,
1369 +[  --with-fpm-conf=PATH        Set the path for php-fpm configuration file [PREFIX/etc/php-fpm.conf]], \$prefix/etc/php-fpm.conf, no)
1370 +
1371 +PHP_ARG_WITH(fpm-log, for php-fpm log file path,
1372 +[  --with-fpm-log=PATH         Set the path for php-fpm log file [PREFIX/logs/php-fpm.log]], \$prefix/logs/php-fpm.log, no)
1373 +
1374 +PHP_ARG_WITH(fpm-pid, for php-fpm pid file path,
1375 +[  --with-fpm-pid=PATH         Set the path for php-fpm pid file [PREFIX/logs/php-fpm.pid]], \$prefix/logs/php-fpm.pid, no)
1376 +
1377 +FPM_SOURCES="fpm.c \
1378 +       fpm_conf.c \
1379 +       fpm_signals.c \
1380 +       fpm_children.c \
1381 +       fpm_worker_pool.c \
1382 +       fpm_unix.c \
1383 +       fpm_cleanup.c \
1384 +       fpm_sockets.c \
1385 +       fpm_stdio.c \
1386 +       fpm_env.c \
1387 +       fpm_events.c \
1388 +       fpm_php.c \
1389 +       fpm_php_trace.c \
1390 +       fpm_process_ctl.c \
1391 +       fpm_request.c \
1392 +       fpm_clock.c \
1393 +       fpm_shm.c \
1394 +       fpm_shm_slots.c \
1395 +       xml_config.c \
1396 +       zlog.c"
1397 +
1398 +dnl AC_FPM_LIBEVENT
1399 +AC_FPM_LIBXML
1400 +AC_FPM_PRCTL
1401 +AC_FPM_CLOCK
1402 +AC_FPM_TRACE
1403 +dnl AC_FPM_JUDY
1404 +
1405 +LIBEVENT_CFLAGS=""
1406 +LIBEVENT_LIBS="-levent"
1407 +
1408 +SAPI_EXTRA_DEPS="$LIBEVENT_LIBS"
1409 +
1410 +FPM_CFLAGS="$LIBEVENT_CFLAGS $LIBXML_CFLAGS $JUDY_CFLAGS"
1411 +
1412 +dnl FPM_CFLAGS="$FPM_CFLAGS -DJUDYERROR_NOTEST" # for Judy
1413 +FPM_CFLAGS="$FPM_CFLAGS -I$abs_srcdir/sapi/cgi" # for fastcgi.h
1414 +
1415 +if test "$ICC" = "yes" ; then
1416 +       FPM_ADD_CFLAGS="-Wall -wd279,310,869,810,981"
1417 +elif test "$GCC" = "yes" ; then
1418 +       FPM_ADD_CFLAGS="-Wall -Wpointer-arith -Wno-unused-parameter -Wunused-variable -Wunused-value -fno-strict-aliasing"
1419 +fi
1420 +
1421 +if test -n "$FPM_WERROR" ; then
1422 +       FPM_ADD_CFLAGS="$FPM_ADD_CFLAGS -Werror"
1423 +fi
1424 +
1425 +FPM_CFLAGS="$FPM_ADD_CFLAGS $FPM_CFLAGS"
1426 +
1427 +PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cgi/fpm/Makefile.frag)
1428 +
1429 +PHP_ADD_SOURCES(sapi/cgi/fpm, $FPM_SOURCES, $FPM_CFLAGS, sapi)
1430 +
1431 +PHP_ADD_BUILD_DIR(sapi/cgi/fpm)
1432 +
1433 +install_fpm="install-fpm"
1434 +
1435 +SAPI_EXTRA_LIBS="$LIBEVENT_LIBS $LIBXML_LIBS $JUDY_LIBS"
1436 +
1437 +
1438 +if test "$prefix" = "NONE" ; then
1439 +       fpm_prefix=/usr/local
1440 +else
1441 +       fpm_prefix="$prefix"
1442 +fi
1443 +
1444 +if test "$PHP_FPM_CONF" = "\$prefix/etc/php-fpm.conf" ; then
1445 +       php_fpm_conf_path="$fpm_prefix/etc/php-fpm.conf"
1446 +else
1447 +       php_fpm_conf_path="$PHP_FPM_CONF"
1448 +fi
1449 +
1450 +if test "$PHP_FPM_LOG" = "\$prefix/logs/php-fpm.log" ; then
1451 +       php_fpm_log_path="$fpm_prefix/logs/php-fpm.log"
1452 +else
1453 +       php_fpm_log_path="$PHP_FPM_LOG"
1454 +fi
1455 +
1456 +if test "$PHP_FPM_PID" = "\$prefix/logs/php-fpm.pid" ; then
1457 +       php_fpm_pid_path="$fpm_prefix/logs/php-fpm.pid"
1458 +else
1459 +       php_fpm_pid_path="$PHP_FPM_PID"
1460 +fi
1461 +
1462 +
1463 +if grep nobody /etc/group >/dev/null 2>&1; then
1464 +       php_fpm_group=nobody
1465 +else
1466 +       if grep nogroup /etc/group >/dev/null 2>&1; then
1467 +               php_fpm_group=nogroup
1468 +       else
1469 +               php_fpm_group=nobody
1470 +       fi
1471 +fi
1472 +
1473 +PHP_SUBST_OLD(php_fpm_conf_path)
1474 +PHP_SUBST_OLD(php_fpm_log_path)
1475 +PHP_SUBST_OLD(php_fpm_pid_path)
1476 +PHP_SUBST_OLD(php_fpm_group)
1477 +PHP_SUBST_OLD(FPM_VERSION)
1478 +
1479 +PHP_OUTPUT(sapi/cgi/fpm/fpm_autoconf.h)
1480 +PHP_OUTPUT(sapi/cgi/fpm/php-fpm.conf:sapi/cgi/fpm/conf/php-fpm.conf.in)
1481 +PHP_OUTPUT(sapi/cgi/fpm/php-fpm:sapi/cgi/fpm/init.d/php-fpm.in)
1482 diff --git a/sapi/cgi/fpm/fpm.c b/sapi/cgi/fpm/fpm.c
1483 new file mode 100644
1484 index 0000000..9db2c9b
1485 --- /dev/null
1486 +++ b/sapi/cgi/fpm/fpm.c
1487 @@ -0,0 +1,84 @@
1488 +
1489 +       /* $Id$ */
1490 +       /* (c) 2007,2008 Andrei Nigmatulin */
1491 +
1492 +#include "fpm_config.h"
1493 +
1494 +#include <stdlib.h> /* for exit */
1495 +
1496 +#include "fpm.h"
1497 +#include "fpm_children.h"
1498 +#include "fpm_signals.h"
1499 +#include "fpm_env.h"
1500 +#include "fpm_events.h"
1501 +#include "fpm_cleanup.h"
1502 +#include "fpm_php.h"
1503 +#include "fpm_sockets.h"
1504 +#include "fpm_unix.h"
1505 +#include "fpm_process_ctl.h"
1506 +#include "fpm_conf.h"
1507 +#include "fpm_worker_pool.h"
1508 +#include "fpm_stdio.h"
1509 +#include "zlog.h"
1510 +
1511 +int fpm;
1512 +
1513 +struct fpm_globals_s fpm_globals;
1514 +
1515 +int fpm_init(int argc, char **argv, char *config)
1516 +{
1517 +       fpm_globals.argc = argc;
1518 +       fpm_globals.argv = argv;
1519 +       fpm_globals.config = config;
1520 +
1521 +       if (0 > fpm_php_init_main()              ||
1522 +               0 > fpm_stdio_init_main()            ||
1523 +               0 > fpm_conf_init_main()             ||
1524 +               0 > fpm_unix_init_main()             ||
1525 +               0 > fpm_env_init_main()              ||
1526 +               0 > fpm_signals_init_main()          ||
1527 +               0 > fpm_pctl_init_main()             ||
1528 +               0 > fpm_children_init_main()         ||
1529 +               0 > fpm_sockets_init_main()          ||
1530 +               0 > fpm_worker_pool_init_main()      ||
1531 +               0 > fpm_event_init_main()) {
1532 +               return -1;
1533 +       }
1534 +
1535 +       if (0 > fpm_conf_write_pid()) {
1536 +               return -1;
1537 +       }
1538 +
1539 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "fpm is running, pid %d", (int) fpm_globals.parent_pid);
1540 +
1541 +       return 0;
1542 +}
1543 +
1544 +/*     children: return listening socket
1545 +       parent: never return */
1546 +int fpm_run(int *max_requests)
1547 +{
1548 +       struct fpm_worker_pool_s *wp;
1549 +
1550 +       /* create initial children in all pools */
1551 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1552 +               int is_parent;
1553 +
1554 +               is_parent = fpm_children_create_initial(wp);
1555 +
1556 +               if (!is_parent) {
1557 +                       goto run_child;
1558 +               }
1559 +       }
1560 +
1561 +       /* run event loop forever */
1562 +       fpm_event_loop();
1563 +
1564 +run_child: /* only workers reach this point */
1565 +
1566 +       fpm_cleanups_run(FPM_CLEANUP_CHILD);
1567 +
1568 +       *max_requests = fpm_globals.max_requests;
1569 +       return fpm_globals.listening_socket;
1570 +}
1571 +
1572 diff --git a/sapi/cgi/fpm/fpm.h b/sapi/cgi/fpm/fpm.h
1573 new file mode 100644
1574 --- /dev/null
1575 +++ b/sapi/cgi/fpm/fpm.h
1576 @@ -0,0 +1,30 @@
1577 +
1578 +       /* $Id$ */
1579 +       /* (c) 2007,2008 Andrei Nigmatulin */
1580 +
1581 +#ifndef FPM_H
1582 +#define FPM_H 1
1583 +
1584 +#include <unistd.h>
1585 +
1586 +int fpm_run(int *max_requests);
1587 +int fpm_init(int argc, char **argv, char *config);
1588 +
1589 +struct fpm_globals_s {
1590 +       pid_t parent_pid;
1591 +       int argc;
1592 +       char **argv;
1593 +       char *config;
1594 +       int running_children;
1595 +       int error_log_fd;
1596 +       int log_level;
1597 +       int listening_socket; /* for this child */
1598 +       int max_requests; /* for this child */
1599 +       int is_child;
1600 +};
1601 +
1602 +extern struct fpm_globals_s fpm_globals;
1603 +
1604 +extern int fpm;
1605 +
1606 +#endif
1607 diff --git a/sapi/cgi/fpm/fpm_arrays.h b/sapi/cgi/fpm/fpm_arrays.h
1608 new file mode 100644
1609 --- /dev/null
1610 +++ b/sapi/cgi/fpm/fpm_arrays.h
1611 @@ -0,0 +1,110 @@
1612 +
1613 +       /* $Id$ */
1614 +       /* (c) 2007,2008 Andrei Nigmatulin */
1615 +
1616 +#ifndef FPM_ARRAYS_H
1617 +#define FPM_ARRAYS_H 1
1618 +
1619 +#include <stdlib.h>
1620 +#include <string.h>
1621 +
1622 +struct fpm_array_s {
1623 +       void *data;
1624 +       size_t sz;
1625 +       size_t used;
1626 +       size_t allocated;
1627 +};
1628 +
1629 +static inline struct fpm_array_s *fpm_array_init(struct fpm_array_s *a, unsigned int sz, unsigned int initial_num)
1630 +{
1631 +       void *allocated = 0;
1632 +
1633 +       if (!a) {
1634 +               a = malloc(sizeof(struct fpm_array_s));
1635 +
1636 +               if (!a) {
1637 +                       return 0;
1638 +               }
1639 +
1640 +               allocated = a;
1641 +       }
1642 +
1643 +       a->sz = sz;
1644 +
1645 +       a->data = calloc(sz, initial_num);
1646 +
1647 +       if (!a->data) {
1648 +               free(allocated);
1649 +               return 0;
1650 +       }
1651 +
1652 +       a->allocated = initial_num;
1653 +       a->used = 0;
1654 +
1655 +       return a;
1656 +}
1657 +
1658 +static inline void *fpm_array_item(struct fpm_array_s *a, unsigned int n)
1659 +{
1660 +       char *ret;
1661 +
1662 +       ret = (char *) a->data + a->sz * n;
1663 +
1664 +       return ret;
1665 +}
1666 +
1667 +static inline void *fpm_array_item_last(struct fpm_array_s *a)
1668 +{
1669 +       return fpm_array_item(a, a->used - 1);
1670 +}
1671 +
1672 +static inline int fpm_array_item_remove(struct fpm_array_s *a, unsigned int n)
1673 +{
1674 +       int ret = -1;
1675 +
1676 +       if (n < a->used - 1) {
1677 +               void *last = fpm_array_item(a, a->used - 1);
1678 +               void *to_remove = fpm_array_item(a, n);
1679 +
1680 +               memcpy(to_remove, last, a->sz);
1681 +
1682 +               ret = n;
1683 +       }
1684 +
1685 +       --a->used;
1686 +
1687 +       return ret;
1688 +}
1689 +
1690 +static inline void *fpm_array_push(struct fpm_array_s *a)
1691 +{
1692 +       void *ret;
1693 +
1694 +       if (a->used == a->allocated) {
1695 +               size_t new_allocated = a->allocated ? a->allocated * 2 : 20;
1696 +               void *new_ptr = realloc(a->data, a->sz * new_allocated);
1697 +
1698 +               if (!new_ptr) {
1699 +                       return 0;
1700 +               }
1701 +
1702 +               a->data = new_ptr;
1703 +               a->allocated = new_allocated;
1704 +       }
1705 +
1706 +       ret = fpm_array_item(a, a->used);
1707 +
1708 +       ++a->used;
1709 +
1710 +       return ret;
1711 +}
1712 +
1713 +static inline void fpm_array_free(struct fpm_array_s *a)
1714 +{
1715 +       free(a->data);
1716 +       a->data = 0;
1717 +       a->sz = 0;
1718 +       a->used = a->allocated = 0;
1719 +}
1720 +
1721 +#endif
1722 diff --git a/sapi/cgi/fpm/fpm_atomic.h b/sapi/cgi/fpm/fpm_atomic.h
1723 new file mode 100644
1724 --- /dev/null
1725 +++ b/sapi/cgi/fpm/fpm_atomic.h
1726 @@ -0,0 +1,85 @@
1727 +
1728 +       /* $Id$ */
1729 +       /* (c) 2007,2008 Andrei Nigmatulin */
1730 +
1731 +#ifndef FPM_ATOMIC_H
1732 +#define FPM_ATOMIC_H 1
1733 +
1734 +#include <stdint.h>
1735 +#include <sched.h>
1736 +
1737 +#if ( __i386__ || __i386 )
1738 +
1739 +typedef int32_t                     atomic_int_t;
1740 +typedef uint32_t                    atomic_uint_t;
1741 +typedef volatile atomic_uint_t      atomic_t;
1742 +
1743 +
1744 +static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add)
1745 +{
1746 +       __asm__ volatile ( "lock;" "xaddl %0, %1;" :
1747 +               "+r" (add) : "m" (*value) : "memory");
1748 +
1749 +       return add;
1750 +}
1751 +
1752 +static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set)
1753 +{
1754 +       unsigned char res;
1755 +
1756 +       __asm__ volatile ( "lock;" "cmpxchgl %3, %1;" "sete %0;" :
1757 +               "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");
1758 +
1759 +    return res;
1760 +}
1761 +
1762 +#elif ( __amd64__ || __amd64 )
1763 +
1764 +typedef int64_t                     atomic_int_t;
1765 +typedef uint64_t                    atomic_uint_t;
1766 +typedef volatile atomic_uint_t      atomic_t;
1767 +
1768 +static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add)
1769 +{
1770 +       __asm__ volatile ( "lock;" "xaddq %0, %1;" :
1771 +               "+r" (add) : "m" (*value) : "memory");
1772 +
1773 +       return add;
1774 +}
1775 +
1776 +static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set)
1777 +{
1778 +       unsigned char res;
1779 +
1780 +       __asm__ volatile ( "lock;" "cmpxchgq %3, %1;" "sete %0;" :
1781 +               "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");
1782 +
1783 +       return res;
1784 +}
1785 +
1786 +#else
1787 +
1788 +#error unsupported processor. please write a patch and send it to me
1789 +
1790 +#endif
1791 +
1792 +static inline int fpm_spinlock(atomic_t *lock, int try_once)
1793 +{
1794 +       if (try_once) {
1795 +               return atomic_cmp_set(lock, 0, 1) ? 0 : -1;
1796 +       }
1797 +
1798 +       for (;;) {
1799 +
1800 +               if (atomic_cmp_set(lock, 0, 1)) {
1801 +                       break;
1802 +               }
1803 +
1804 +               sched_yield();
1805 +       }
1806 +
1807 +       return 0;
1808 +}
1809 +
1810 +#endif
1811 +
1812 diff --git a/sapi/cgi/fpm/fpm_autoconf.h.in b/sapi/cgi/fpm/fpm_autoconf.h.in
1813 new file mode 100644
1814 --- /dev/null
1815 +++ b/sapi/cgi/fpm/fpm_autoconf.h.in
1816 @@ -0,0 +1,9 @@
1817 +
1818 +       /* $Id$ */
1819 +       /* (c) 2007,2008 Andrei Nigmatulin */
1820 +
1821 +#define PHP_FPM_VERSION   "@FPM_VERSION@"
1822 +#define PHP_FPM_CONF_PATH "@php_fpm_conf_path@"
1823 +#define PHP_FPM_LOG_PATH  "@php_fpm_log_path@"
1824 +#define PHP_FPM_PID_PATH  "@php_fpm_pid_path@"
1825 +
1826 diff --git a/sapi/cgi/fpm/fpm_children.c b/sapi/cgi/fpm/fpm_children.c
1827 new file mode 100644
1828 --- /dev/null
1829 +++ b/sapi/cgi/fpm/fpm_children.c
1830 @@ -0,0 +1,385 @@
1831 +
1832 +       /* $Id$ */
1833 +       /* (c) 2007,2008 Andrei Nigmatulin */
1834 +
1835 +#include "fpm_config.h"
1836 +
1837 +#include <sys/types.h>
1838 +#include <sys/wait.h>
1839 +#include <time.h>
1840 +#include <unistd.h>
1841 +#include <string.h>
1842 +#include <stdio.h>
1843 +
1844 +#include "fpm.h"
1845 +#include "fpm_children.h"
1846 +#include "fpm_signals.h"
1847 +#include "fpm_worker_pool.h"
1848 +#include "fpm_sockets.h"
1849 +#include "fpm_process_ctl.h"
1850 +#include "fpm_php.h"
1851 +#include "fpm_conf.h"
1852 +#include "fpm_cleanup.h"
1853 +#include "fpm_events.h"
1854 +#include "fpm_clock.h"
1855 +#include "fpm_stdio.h"
1856 +#include "fpm_unix.h"
1857 +#include "fpm_env.h"
1858 +#include "fpm_shm_slots.h"
1859 +
1860 +#include "zlog.h"
1861 +
1862 +static time_t *last_faults;
1863 +static int fault;
1864 +
1865 +static int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop);
1866 +
1867 +static void fpm_children_cleanup(int which, void *arg)
1868 +{
1869 +       free(last_faults);
1870 +}
1871 +
1872 +static struct fpm_child_s *fpm_child_alloc()
1873 +{
1874 +       struct fpm_child_s *ret;
1875 +
1876 +       ret = malloc(sizeof(struct fpm_child_s));
1877 +
1878 +       if (!ret) return 0;
1879 +
1880 +       memset(ret, 0, sizeof(*ret));
1881 +
1882 +       return ret;
1883 +}
1884 +
1885 +static void fpm_child_free(struct fpm_child_s *child)
1886 +{
1887 +       free(child);
1888 +}
1889 +
1890 +static void fpm_child_close(struct fpm_child_s *child, int in_event_loop)
1891 +{
1892 +       if (child->fd_stdout != -1) {
1893 +               if (in_event_loop) {
1894 +                       fpm_event_fire(&child->ev_stdout);
1895 +               }
1896 +               if (child->fd_stdout != -1) {
1897 +                       close(child->fd_stdout);
1898 +               }
1899 +       }
1900 +
1901 +       if (child->fd_stderr != -1) {
1902 +               if (in_event_loop) {
1903 +                       fpm_event_fire(&child->ev_stderr);
1904 +               }
1905 +               if (child->fd_stderr != -1) {
1906 +                       close(child->fd_stderr);
1907 +               }
1908 +       }
1909 +
1910 +       fpm_child_free(child);
1911 +}
1912 +
1913 +static void fpm_child_link(struct fpm_child_s *child)
1914 +{
1915 +       struct fpm_worker_pool_s *wp = child->wp;
1916 +
1917 +       ++wp->running_children;
1918 +       ++fpm_globals.running_children;
1919 +
1920 +       child->next = wp->children;
1921 +       if (child->next) child->next->prev = child;
1922 +       child->prev = 0;
1923 +       wp->children = child;
1924 +}
1925 +
1926 +static void fpm_child_unlink(struct fpm_child_s *child)
1927 +{
1928 +       --child->wp->running_children;
1929 +       --fpm_globals.running_children;
1930 +
1931 +       if (child->prev) child->prev->next = child->next;
1932 +       else child->wp->children = child->next;
1933 +       if (child->next) child->next->prev = child->prev;
1934 +
1935 +}
1936 +
1937 +static struct fpm_child_s *fpm_child_find(pid_t pid)
1938 +{
1939 +       struct fpm_worker_pool_s *wp;
1940 +       struct fpm_child_s *child = 0;
1941 +
1942 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1943 +
1944 +               for (child = wp->children; child; child = child->next) {
1945 +                       if (child->pid == pid) {
1946 +                               break;
1947 +                       }
1948 +               }
1949 +
1950 +               if (child) break;
1951 +       }
1952 +
1953 +       if (!child) {
1954 +               return 0;
1955 +       }
1956 +
1957 +       return child;
1958 +}
1959 +
1960 +static void fpm_child_init(struct fpm_worker_pool_s *wp)
1961 +{
1962 +       fpm_globals.max_requests = wp->config->max_requests;
1963 +
1964 +       if (0 > fpm_stdio_init_child(wp) ||
1965 +               0 > fpm_unix_init_child(wp) ||
1966 +               0 > fpm_signals_init_child() ||
1967 +               0 > fpm_env_init_child(wp) ||
1968 +               0 > fpm_php_init_child(wp)) {
1969 +
1970 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "child failed to initialize (pool %s)", wp->config->name);
1971 +               exit(255);
1972 +       }
1973 +}
1974 +
1975 +int fpm_children_free(struct fpm_child_s *child)
1976 +{
1977 +       struct fpm_child_s *next;
1978 +
1979 +       for (; child; child = next) {
1980 +               next = child->next;
1981 +               fpm_child_close(child, 0 /* in_event_loop */);
1982 +       }
1983 +
1984 +       return 0;
1985 +}
1986 +
1987 +void fpm_children_bury()
1988 +{
1989 +       int status;
1990 +       pid_t pid;
1991 +       struct fpm_child_s *child;
1992 +
1993 +       while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
1994 +               char buf[128];
1995 +               int severity = ZLOG_NOTICE;
1996 +
1997 +               child = fpm_child_find(pid);
1998 +
1999 +               if (WIFEXITED(status)) {
2000 +
2001 +                       snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
2002 +
2003 +                       if (WEXITSTATUS(status) != 0) {
2004 +                               severity = ZLOG_WARNING;
2005 +                       }
2006 +
2007 +               }
2008 +               else if (WIFSIGNALED(status)) {
2009 +                       const char *signame = fpm_signal_names[WTERMSIG(status)];
2010 +                       const char *have_core = WCOREDUMP(status) ? " (core dumped)" : "";
2011 +
2012 +                       if (signame == NULL) {
2013 +                               signame = "";
2014 +                       }
2015 +
2016 +                       snprintf(buf, sizeof(buf), "on signal %d %s%s", WTERMSIG(status), signame, have_core);
2017 +
2018 +                       if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
2019 +                               severity = ZLOG_WARNING;
2020 +                       }
2021 +               }
2022 +               else if (WIFSTOPPED(status)) {
2023 +
2024 +                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
2025 +
2026 +                       if (child && child->tracer) {
2027 +                               child->tracer(child);
2028 +                       }
2029 +
2030 +                       continue;
2031 +               }
2032 +
2033 +               if (child) {
2034 +                       struct fpm_worker_pool_s *wp = child->wp;
2035 +                       struct timeval tv1, tv2;
2036 +
2037 +                       fpm_child_unlink(child);
2038 +
2039 +                       fpm_shm_slots_discard_slot(child);
2040 +
2041 +                       fpm_clock_get(&tv1);
2042 +
2043 +                       timersub(&tv1, &child->started, &tv2);
2044 +
2045 +                       zlog(ZLOG_STUFF, severity, "child %d (pool %s) exited %s after %ld.%06d seconds from start", (int) pid,
2046 +                                               child->wp->config->name, buf, tv2.tv_sec, (int) tv2.tv_usec);
2047 +
2048 +                       fpm_child_close(child, 1 /* in event_loop */);
2049 +
2050 +                       fpm_pctl_child_exited();
2051 +
2052 +                       if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
2053 +                               time_t now = tv1.tv_sec;
2054 +                               int restart_condition = 1;
2055 +                               int i;
2056 +
2057 +                               last_faults[fault++] = now;
2058 +
2059 +                               if (fault == fpm_global_config.emergency_restart_threshold) {
2060 +                                       fault = 0;
2061 +                               }
2062 +
2063 +                               for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
2064 +                                       if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
2065 +                                               restart_condition = 0;
2066 +                                               break;
2067 +                                       }
2068 +                               }
2069 +
2070 +                               if (restart_condition) {
2071 +
2072 +                                       zlog(ZLOG_STUFF, ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload",
2073 +                                               fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval);
2074 +
2075 +                                       fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
2076 +                               }
2077 +                       }
2078 +
2079 +                       fpm_children_make(wp, 1 /* in event loop */);
2080 +
2081 +                       if (fpm_globals.is_child) {
2082 +                               break;
2083 +                       }
2084 +               }
2085 +               else {
2086 +                       zlog(ZLOG_STUFF, ZLOG_ALERT, "oops, unknown child exited %s", buf);
2087 +               }
2088 +       }
2089 +
2090 +}
2091 +
2092 +static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp)
2093 +{
2094 +       struct fpm_child_s *c;
2095 +
2096 +       c = fpm_child_alloc();
2097 +
2098 +       if (!c) {
2099 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "malloc failed (pool %s)", wp->config->name);
2100 +               return 0;
2101 +       }
2102 +
2103 +       c->wp = wp;
2104 +       c->fd_stdout = -1; c->fd_stderr = -1;
2105 +
2106 +       if (0 > fpm_stdio_prepare_pipes(c)) {
2107 +               fpm_child_free(c);
2108 +               return 0;
2109 +       }
2110 +
2111 +       if (0 > fpm_shm_slots_prepare_slot(c)) {
2112 +               fpm_stdio_discard_pipes(c);
2113 +               fpm_child_free(c);
2114 +               return 0;
2115 +       }
2116 +
2117 +       return c;
2118 +}
2119 +
2120 +static void fpm_resources_discard(struct fpm_child_s *child)
2121 +{
2122 +       fpm_shm_slots_discard_slot(child);
2123 +       fpm_stdio_discard_pipes(child);
2124 +       fpm_child_free(child);
2125 +}
2126 +
2127 +static void fpm_child_resources_use(struct fpm_child_s *child)
2128 +{
2129 +       fpm_shm_slots_child_use_slot(child);
2130 +       fpm_stdio_child_use_pipes(child);
2131 +       fpm_child_free(child);
2132 +}
2133 +
2134 +static void fpm_parent_resources_use(struct fpm_child_s *child)
2135 +{
2136 +       fpm_shm_slots_parent_use_slot(child);
2137 +       fpm_stdio_parent_use_pipes(child);
2138 +       fpm_child_link(child);
2139 +}
2140 +
2141 +static int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop)
2142 +{
2143 +       int enough = 0;
2144 +       pid_t pid;
2145 +       struct fpm_child_s *child;
2146 +
2147 +       while (!enough && fpm_pctl_can_spawn_children() && wp->running_children < wp->config->pm->max_children) {
2148 +
2149 +               child = fpm_resources_prepare(wp);
2150 +
2151 +               if (!child) {
2152 +                       enough = 1;
2153 +                       break;
2154 +               }
2155 +
2156 +               pid = fork();
2157 +
2158 +               switch (pid) {
2159 +
2160 +                       case 0 :
2161 +                               fpm_child_resources_use(child);
2162 +                               fpm_globals.is_child = 1;
2163 +                               if (in_event_loop) {
2164 +                                       fpm_event_exit_loop();
2165 +                               }
2166 +                               fpm_child_init(wp);
2167 +                               return 0;
2168 +
2169 +                       case -1 :
2170 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fork() failed");
2171 +                               enough = 1;
2172 +
2173 +                               fpm_resources_discard(child);
2174 +
2175 +                               break; /* dont try any more on error */
2176 +
2177 +                       default :
2178 +                               child->pid = pid;
2179 +                               fpm_clock_get(&child->started);
2180 +                               fpm_parent_resources_use(child);
2181 +
2182 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "child %d (pool %s) started", (int) pid, wp->config->name);
2183 +               }
2184 +
2185 +       }
2186 +
2187 +       return 1; /* we are done */
2188 +}
2189 +
2190 +int fpm_children_create_initial(struct fpm_worker_pool_s *wp)
2191 +{
2192 +       return fpm_children_make(wp, 0 /* not in event loop yet */);
2193 +}
2194 +
2195 +int fpm_children_init_main()
2196 +{
2197 +       if (fpm_global_config.emergency_restart_threshold &&
2198 +               fpm_global_config.emergency_restart_interval) {
2199 +
2200 +               last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
2201 +
2202 +               if (!last_faults) {
2203 +                       return -1;
2204 +               }
2205 +
2206 +               memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
2207 +       }
2208 +
2209 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
2210 +               return -1;
2211 +       }
2212 +
2213 +       return 0;
2214 +}
2215 +
2216 diff --git a/sapi/cgi/fpm/fpm_children.h b/sapi/cgi/fpm/fpm_children.h
2217 new file mode 100644
2218 --- /dev/null
2219 +++ b/sapi/cgi/fpm/fpm_children.h
2220 @@ -0,0 +1,33 @@
2221 +
2222 +       /* $Id$ */
2223 +       /* (c) 2007,2008 Andrei Nigmatulin */
2224 +
2225 +#ifndef FPM_CHILDREN_H
2226 +#define FPM_CHILDREN_H 1
2227 +
2228 +#include <sys/time.h>
2229 +#include <sys/types.h>
2230 +#include <event.h>
2231 +
2232 +#include "fpm_worker_pool.h"
2233 +
2234 +int fpm_children_create_initial(struct fpm_worker_pool_s *wp);
2235 +int fpm_children_free(struct fpm_child_s *child);
2236 +void fpm_children_bury();
2237 +int fpm_children_init_main();
2238 +
2239 +struct fpm_child_s;
2240 +
2241 +struct fpm_child_s {
2242 +       struct fpm_child_s *prev, *next;
2243 +       struct timeval started;
2244 +       struct fpm_worker_pool_s *wp;
2245 +       struct event ev_stdout, ev_stderr;
2246 +       int shm_slot_i;
2247 +       int fd_stdout, fd_stderr;
2248 +       void (*tracer)(struct fpm_child_s *);
2249 +       struct timeval slow_logged;
2250 +       pid_t pid;
2251 +};
2252 +
2253 +#endif
2254 diff --git a/sapi/cgi/fpm/fpm_cleanup.c b/sapi/cgi/fpm/fpm_cleanup.c
2255 new file mode 100644
2256 --- /dev/null
2257 +++ b/sapi/cgi/fpm/fpm_cleanup.c
2258 @@ -0,0 +1,51 @@
2259 +
2260 +       /* $Id$ */
2261 +       /* (c) 2007,2008 Andrei Nigmatulin */
2262 +
2263 +#include "fpm_config.h"
2264 +
2265 +#include <stdlib.h>
2266 +
2267 +#include "fpm_arrays.h"
2268 +#include "fpm_cleanup.h"
2269 +#include "zlog.h"
2270 +
2271 +struct cleanup_s {
2272 +       int type;
2273 +       void (*cleanup)(int, void *);
2274 +       void *arg;
2275 +};
2276 +
2277 +static struct fpm_array_s cleanups = { .sz = sizeof(struct cleanup_s) };
2278 +
2279 +int fpm_cleanup_add(int type, void (*cleanup)(int, void *), void *arg)
2280 +{
2281 +       struct cleanup_s *c;
2282 +
2283 +       c = fpm_array_push(&cleanups);
2284 +
2285 +       if (!c) {
2286 +               return -1;
2287 +       }
2288 +
2289 +       c->type = type;
2290 +       c->cleanup = cleanup;
2291 +       c->arg = arg;
2292 +
2293 +       return 0;
2294 +}
2295 +
2296 +void fpm_cleanups_run(int type)
2297 +{
2298 +       struct cleanup_s *c = fpm_array_item_last(&cleanups);
2299 +       int cl = cleanups.used;
2300 +
2301 +       for ( ; cl--; c--) {
2302 +               if (c->type & type) {
2303 +                       c->cleanup(type, c->arg);
2304 +               }
2305 +       }
2306 +
2307 +       fpm_array_free(&cleanups);
2308 +}
2309 +
2310 diff --git a/sapi/cgi/fpm/fpm_cleanup.h b/sapi/cgi/fpm/fpm_cleanup.h
2311 new file mode 100644
2312 --- /dev/null
2313 +++ b/sapi/cgi/fpm/fpm_cleanup.h
2314 @@ -0,0 +1,21 @@
2315 +
2316 +       /* $Id$ */
2317 +       /* (c) 2007,2008 Andrei Nigmatulin */
2318 +
2319 +#ifndef FPM_CLEANUP_H
2320 +#define FPM_CLEANUP_H 1
2321 +
2322 +int fpm_cleanup_add(int type, void (*cleanup)(int, void *), void *);
2323 +void fpm_cleanups_run(int type);
2324 +
2325 +enum {
2326 +       FPM_CLEANUP_CHILD                                       = (1 << 0),
2327 +       FPM_CLEANUP_PARENT_EXIT                         = (1 << 1),
2328 +       FPM_CLEANUP_PARENT_EXIT_MAIN            = (1 << 2),
2329 +       FPM_CLEANUP_PARENT_EXEC                         = (1 << 3),
2330 +       FPM_CLEANUP_PARENT                                      = (1 << 1) | (1 << 2) | (1 << 3),
2331 +       FPM_CLEANUP_ALL                                         = ~0,
2332 +};
2333 +
2334 +#endif
2335 +
2336 diff --git a/sapi/cgi/fpm/fpm_clock.c b/sapi/cgi/fpm/fpm_clock.c
2337 new file mode 100644
2338 --- /dev/null
2339 +++ b/sapi/cgi/fpm/fpm_clock.c
2340 @@ -0,0 +1,115 @@
2341 +
2342 +       /* $Id$ */
2343 +       /* (c) 2007,2008 Andrei Nigmatulin */
2344 +
2345 +#include "fpm_config.h"
2346 +
2347 +#if defined(HAVE_CLOCK_GETTIME)
2348 +#include <time.h> /* for CLOCK_MONOTONIC */
2349 +#endif
2350 +
2351 +#include "fpm_clock.h"
2352 +#include "zlog.h"
2353 +
2354 +
2355 +/* posix monotonic clock - preferred source of time */
2356 +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
2357 +
2358 +static int monotonic_works;
2359 +
2360 +int fpm_clock_init()
2361 +{
2362 +       struct timespec ts;
2363 +
2364 +       monotonic_works = 0;
2365 +
2366 +       if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) {
2367 +               monotonic_works = 1;
2368 +       }
2369 +
2370 +       return 0;
2371 +}
2372 +
2373 +int fpm_clock_get(struct timeval *tv)
2374 +{
2375 +       if (monotonic_works) {
2376 +               struct timespec ts;
2377 +
2378 +               if (0 > clock_gettime(CLOCK_MONOTONIC, &ts)) {
2379 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "clock_gettime() failed");
2380 +                       return -1;
2381 +               }
2382 +
2383 +               tv->tv_sec = ts.tv_sec;
2384 +               tv->tv_usec = ts.tv_nsec / 1000;
2385 +               return 0;
2386 +       }
2387 +
2388 +       return gettimeofday(tv, 0);
2389 +}
2390 +
2391 +/* macosx clock */
2392 +#elif defined(HAVE_CLOCK_GET_TIME)
2393 +
2394 +#include <mach/mach.h>
2395 +#include <mach/clock.h>
2396 +#include <mach/mach_error.h>
2397 +
2398 +static clock_serv_t mach_clock;
2399 +
2400 +/* this code borrowed from here: http://lists.apple.com/archives/Darwin-development/2002/Mar/msg00746.html */
2401 +/* mach_clock also should be re-initialized in child process after fork */
2402 +int fpm_clock_init()
2403 +{
2404 +       kern_return_t ret;
2405 +       mach_timespec_t aTime;
2406 +
2407 +       ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &mach_clock);
2408 +
2409 +       if (ret != KERN_SUCCESS) {
2410 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "host_get_clock_service() failed: %s", mach_error_string(ret));
2411 +               return -1;
2412 +       }
2413 +
2414 +       /* test if it works */
2415 +       ret = clock_get_time(mach_clock, &aTime);
2416 +
2417 +       if (ret != KERN_SUCCESS) {
2418 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "clock_get_time() failed: %s", mach_error_string(ret));
2419 +               return -1;
2420 +       }
2421 +
2422 +       return 0;
2423 +}
2424 +
2425 +int fpm_clock_get(struct timeval *tv)
2426 +{
2427 +       kern_return_t ret;
2428 +       mach_timespec_t aTime;
2429 +
2430 +       ret = clock_get_time(mach_clock, &aTime);
2431 +
2432 +       if (ret != KERN_SUCCESS) {
2433 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "clock_get_time() failed: %s", mach_error_string(ret));
2434 +               return -1;
2435 +       }
2436 +
2437 +       tv->tv_sec = aTime.tv_sec;
2438 +       tv->tv_usec = aTime.tv_nsec / 1000;
2439 +
2440 +       return 0;
2441 +}
2442 +
2443 +#else /* no clock */
2444 +
2445 +int fpm_clock_init()
2446 +{
2447 +       return 0;
2448 +}
2449 +
2450 +int fpm_clock_get(struct timeval *tv)
2451 +{
2452 +       return gettimeofday(tv, 0);
2453 +}
2454 +
2455 +#endif
2456 diff --git a/sapi/cgi/fpm/fpm_clock.h b/sapi/cgi/fpm/fpm_clock.h
2457 new file mode 100644
2458 --- /dev/null
2459 +++ b/sapi/cgi/fpm/fpm_clock.h
2460 @@ -0,0 +1,13 @@
2461 +
2462 +       /* $Id$ */
2463 +       /* (c) 2007,2008 Andrei Nigmatulin */
2464 +
2465 +#ifndef FPM_CLOCK_H
2466 +#define FPM_CLOCK_H 1
2467 +
2468 +#include <sys/time.h>
2469 +
2470 +int fpm_clock_init();
2471 +int fpm_clock_get(struct timeval *tv);
2472 +
2473 +#endif
2474 diff --git a/sapi/cgi/fpm/fpm_conf.c b/sapi/cgi/fpm/fpm_conf.c
2475 new file mode 100644
2476 --- /dev/null
2477 +++ b/sapi/cgi/fpm/fpm_conf.c
2478 @@ -0,0 +1,532 @@
2479 +
2480 +       /* $Id$ */
2481 +       /* (c) 2007,2008 Andrei Nigmatulin */
2482 +
2483 +#include "fpm_config.h"
2484 +
2485 +#include <sys/types.h>
2486 +#include <sys/stat.h>
2487 +#include <fcntl.h>
2488 +#include <string.h>
2489 +#include <stdlib.h>
2490 +#include <stddef.h>
2491 +#include <stdint.h>
2492 +#include <stdio.h>
2493 +#include <unistd.h>
2494 +
2495 +#include "fpm.h"
2496 +#include "fpm_conf.h"
2497 +#include "fpm_stdio.h"
2498 +#include "fpm_worker_pool.h"
2499 +#include "fpm_cleanup.h"
2500 +#include "fpm_php.h"
2501 +#include "fpm_sockets.h"
2502 +#include "xml_config.h"
2503 +#include "zlog.h"
2504 +
2505 +
2506 +struct fpm_global_config_s fpm_global_config;
2507 +
2508 +static void *fpm_global_config_ptr()
2509 +{
2510 +       return &fpm_global_config;
2511 +}
2512 +
2513 +static char *fpm_conf_set_log_level(void **conf, char *name, void *vv, intptr_t offset)
2514 +{
2515 +       char *value = vv;
2516 +
2517 +       if (!strcmp(value, "debug")) {
2518 +               fpm_globals.log_level = ZLOG_DEBUG;
2519 +       }
2520 +       else if (!strcmp(value, "notice")) {
2521 +               fpm_globals.log_level = ZLOG_NOTICE;
2522 +       }
2523 +       else if (!strcmp(value, "warn")) {
2524 +               fpm_globals.log_level = ZLOG_WARNING;
2525 +       }
2526 +       else if (!strcmp(value, "error")) {
2527 +               fpm_globals.log_level = ZLOG_ERROR;
2528 +       }
2529 +       else if (!strcmp(value, "alert")) {
2530 +               fpm_globals.log_level = ZLOG_ALERT;
2531 +       }
2532 +       else {
2533 +               return "invalid value for 'log_level'";
2534 +       }
2535 +
2536 +       return NULL;
2537 +}
2538 +
2539 +static struct xml_conf_section xml_section_fpm_global_options = {
2540 +       .conf = &fpm_global_config_ptr,
2541 +       .path = "/configuration/global_options",
2542 +       .parsers = (struct xml_value_parser []) {
2543 +               { XML_CONF_SCALAR,      "emergency_restart_threshold",          &xml_conf_set_slot_integer,             offsetof(struct fpm_global_config_s, emergency_restart_threshold) },
2544 +               { XML_CONF_SCALAR,      "emergency_restart_interval",           &xml_conf_set_slot_time,                offsetof(struct fpm_global_config_s, emergency_restart_interval) },
2545 +               { XML_CONF_SCALAR,      "process_control_timeout",                      &xml_conf_set_slot_time,                offsetof(struct fpm_global_config_s, process_control_timeout) },
2546 +               { XML_CONF_SCALAR,      "daemonize",                                            &xml_conf_set_slot_boolean,             offsetof(struct fpm_global_config_s, daemonize) },
2547 +               { XML_CONF_SCALAR,      "pid_file",                                                     &xml_conf_set_slot_string,              offsetof(struct fpm_global_config_s, pid_file) },
2548 +               { XML_CONF_SCALAR,      "error_log",                                            &xml_conf_set_slot_string,              offsetof(struct fpm_global_config_s, error_log) },
2549 +               { XML_CONF_SCALAR,  "log_level",                                                &fpm_conf_set_log_level,                0 },
2550 +               { 0, 0, 0, 0 }
2551 +       }
2552 +};
2553 +
2554 +static char *fpm_conf_set_pm_style(void **conf, char *name, void *vv, intptr_t offset)
2555 +{
2556 +       char *value = vv;
2557 +       struct fpm_pm_s *c = *conf;
2558 +
2559 +       if (!strcmp(value, "static")) {
2560 +               c->style = PM_STYLE_STATIC;
2561 +       }
2562 +       else if (!strcmp(value, "apache-like")) {
2563 +               c->style = PM_STYLE_APACHE_LIKE;
2564 +       }
2565 +       else {
2566 +               return "invalid value for 'style'";
2567 +       }
2568 +
2569 +       return NULL;
2570 +}
2571 +
2572 +static char *fpm_conf_set_rlimit_core(void **conf, char *name, void *vv, intptr_t offset)
2573 +{
2574 +       char *value = vv;
2575 +       struct fpm_worker_pool_config_s *c = *conf;
2576 +
2577 +       if (!strcmp(value, "unlimited")) {
2578 +               c->rlimit_core = -1;
2579 +       }
2580 +       else {
2581 +               int int_value;
2582 +               void *subconf = &int_value;
2583 +               char *error;
2584 +
2585 +               error = xml_conf_set_slot_integer(&subconf, name, vv, 0);
2586 +
2587 +               if (error) return error;
2588 +
2589 +               if (int_value < 0) return "invalid value for 'rlimit_core'";
2590 +
2591 +               c->rlimit_core = int_value;
2592 +       }
2593 +
2594 +       return NULL;
2595 +}
2596 +
2597 +static char *fpm_conf_set_catch_workers_output(void **conf, char *name, void *vv, intptr_t offset)
2598 +{
2599 +       struct fpm_worker_pool_config_s *c = *conf;
2600 +       int int_value;
2601 +       void *subconf = &int_value;
2602 +       char *error;
2603 +
2604 +       error = xml_conf_set_slot_boolean(&subconf, name, vv, 0);
2605 +
2606 +       if (error) return error;
2607 +
2608 +       c->catch_workers_output = int_value;
2609 +
2610 +       return NULL;
2611 +}
2612 +
2613 +static struct xml_conf_section fpm_conf_set_apache_like_subsection_conf = {
2614 +       .path = "apache_like somewhere", /* fixme */
2615 +       .parsers = (struct xml_value_parser []) {
2616 +               { XML_CONF_SCALAR, "StartServers",              &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, options_apache_like.StartServers) },
2617 +               { XML_CONF_SCALAR, "MinSpareServers",   &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, options_apache_like.MinSpareServers) },
2618 +               { XML_CONF_SCALAR, "MaxSpareServers",   &xml_conf_set_slot_integer, offsetof(struct fpm_pm_s, options_apache_like.MaxSpareServers) },
2619 +               { 0, 0, 0, 0 }
2620 +       }
2621 +};
2622 +
2623 +static char *fpm_conf_set_apache_like_subsection(void **conf, char *name, void *xml_node, intptr_t offset)
2624 +{
2625 +       return xml_conf_parse_section(conf, &fpm_conf_set_apache_like_subsection_conf, xml_node);
2626 +}
2627 +
2628 +static struct xml_conf_section fpm_conf_set_listen_options_subsection_conf = {
2629 +       .path = "listen options somewhere", /* fixme */
2630 +       .parsers = (struct xml_value_parser []) {
2631 +               { XML_CONF_SCALAR,              "backlog",              &xml_conf_set_slot_integer,             offsetof(struct fpm_listen_options_s, backlog) },
2632 +               { XML_CONF_SCALAR,              "owner",                &xml_conf_set_slot_string,              offsetof(struct fpm_listen_options_s, owner) },
2633 +               { XML_CONF_SCALAR,              "group",                &xml_conf_set_slot_string,              offsetof(struct fpm_listen_options_s, group) },
2634 +               { XML_CONF_SCALAR,              "mode",                 &xml_conf_set_slot_string,              offsetof(struct fpm_listen_options_s, mode) },
2635 +               { 0, 0, 0, 0 }
2636 +       }
2637 +};
2638 +
2639 +static char *fpm_conf_set_listen_options_subsection(void **conf, char *name, void *xml_node, intptr_t offset)
2640 +{
2641 +       void *subconf = (char *) *conf + offset;
2642 +       struct fpm_listen_options_s *lo;
2643 +
2644 +       lo = malloc(sizeof(*lo));
2645 +
2646 +       if (!lo) {
2647 +               return "malloc() failed";
2648 +       }
2649 +
2650 +       memset(lo, 0, sizeof(*lo));
2651 +
2652 +       lo->backlog = -1;
2653 +
2654 +       * (struct fpm_listen_options_s **) subconf = lo;
2655 +
2656 +       subconf = lo;
2657 +
2658 +       return xml_conf_parse_section(&subconf, &fpm_conf_set_listen_options_subsection_conf, xml_node);
2659 +}
2660 +
2661 +static struct xml_conf_section fpm_conf_set_pm_subsection_conf = {
2662 +       .path = "pm settings somewhere", /* fixme */
2663 +       .parsers = (struct xml_value_parser []) {
2664 +               { XML_CONF_SCALAR,              "style",                                &fpm_conf_set_pm_style,                                         0 },
2665 +               { XML_CONF_SCALAR,              "max_children",                 &xml_conf_set_slot_integer,                                     offsetof(struct fpm_pm_s, max_children) },
2666 +               { XML_CONF_SUBSECTION,  "apache_like",                  &fpm_conf_set_apache_like_subsection,           offsetof(struct fpm_pm_s, options_apache_like) },
2667 +               { 0, 0, 0, 0 }
2668 +       }
2669 +};
2670 +
2671 +static char *fpm_conf_set_pm_subsection(void **conf, char *name, void *xml_node, intptr_t offset)
2672 +{
2673 +       void *subconf = (char *) *conf + offset;
2674 +       struct fpm_pm_s *pm;
2675 +
2676 +       pm = malloc(sizeof(*pm));
2677 +
2678 +       if (!pm) {
2679 +               return "fpm_conf_set_pm_subsection(): malloc failed";
2680 +       }
2681 +
2682 +       memset(pm, 0, sizeof(*pm));
2683 +
2684 +       * (struct fpm_pm_s **) subconf = pm;
2685 +
2686 +       subconf = pm;
2687 +
2688 +       return xml_conf_parse_section(&subconf, &fpm_conf_set_pm_subsection_conf, xml_node);
2689 +}
2690 +
2691 +static char *xml_conf_set_slot_key_value_pair(void **conf, char *name, void *vv, intptr_t offset)
2692 +{
2693 +       char *value = vv;
2694 +       struct key_value_s *kv;
2695 +       struct key_value_s ***parent = (struct key_value_s ***) conf;
2696 +
2697 +       kv = malloc(sizeof(*kv));
2698 +
2699 +       if (!kv) {
2700 +               return "malloc() failed";
2701 +       }
2702 +
2703 +       memset(kv, 0, sizeof(*kv));
2704 +
2705 +       kv->key = strdup(name);
2706 +       kv->value = strdup(value);
2707 +
2708 +       if (!kv->key || !kv->value) {
2709 +               return "xml_conf_set_slot_key_value_pair(): strdup() failed";
2710 +       }
2711 +
2712 +       **parent = kv;
2713 +
2714 +       *parent = &kv->next;
2715 +
2716 +       return NULL;
2717 +}
2718 +
2719 +static struct xml_conf_section fpm_conf_set_key_value_pairs_subsection_conf = {
2720 +       .path = "key_value_pairs somewhere", /* fixme */
2721 +       .parsers = (struct xml_value_parser []) {
2722 +               { XML_CONF_SCALAR, 0, &xml_conf_set_slot_key_value_pair, 0 },
2723 +               { 0, 0, 0, 0 }
2724 +       }
2725 +};
2726 +
2727 +static char *fpm_conf_set_key_value_pairs_subsection(void **conf, char *name, void *xml_node, intptr_t offset)
2728 +{
2729 +       void *next_kv = (char *) *conf + offset;
2730 +
2731 +       return xml_conf_parse_section(&next_kv, &fpm_conf_set_key_value_pairs_subsection_conf, xml_node);
2732 +}
2733 +
2734 +static void *fpm_worker_pool_config_alloc()
2735 +{
2736 +       static struct fpm_worker_pool_s *current_wp = 0;
2737 +       struct fpm_worker_pool_s *wp;
2738 +
2739 +       wp = fpm_worker_pool_alloc();
2740 +
2741 +       if (!wp) return 0;
2742 +
2743 +       wp->config = malloc(sizeof(struct fpm_worker_pool_config_s));
2744 +
2745 +       if (!wp->config) return 0;
2746 +
2747 +       memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s));
2748 +
2749 +       if (current_wp) current_wp->next = wp;
2750 +
2751 +       current_wp = wp;
2752 +
2753 +       return wp->config;
2754 +}
2755 +
2756 +int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc)
2757 +{
2758 +       struct key_value_s *kv, *kv_next;
2759 +
2760 +       free(wpc->name);
2761 +       free(wpc->listen_address);
2762 +       if (wpc->listen_options) {
2763 +               free(wpc->listen_options->owner);
2764 +               free(wpc->listen_options->group);
2765 +               free(wpc->listen_options->mode);
2766 +               free(wpc->listen_options);
2767 +       }
2768 +       for (kv = wpc->php_defines; kv; kv = kv_next) {
2769 +               kv_next = kv->next;
2770 +               free(kv->key);
2771 +               free(kv->value);
2772 +               free(kv);
2773 +       }
2774 +       for (kv = wpc->environment; kv; kv = kv_next) {
2775 +               kv_next = kv->next;
2776 +               free(kv->key);
2777 +               free(kv->value);
2778 +               free(kv);
2779 +       }
2780 +       free(wpc->pm);
2781 +       free(wpc->user);
2782 +       free(wpc->group);
2783 +       free(wpc->chroot);
2784 +       free(wpc->chdir);
2785 +       free(wpc->allowed_clients);
2786 +       free(wpc->slowlog);
2787 +
2788 +       return 0;
2789 +}
2790 +
2791 +static struct xml_conf_section xml_section_fpm_worker_pool_config = {
2792 +       .conf = &fpm_worker_pool_config_alloc,
2793 +       .path = "/configuration/workers/pool",
2794 +       .parsers = (struct xml_value_parser []) {
2795 +               { XML_CONF_SCALAR,              "name",                                                 &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, name) },
2796 +               { XML_CONF_SCALAR,              "listen_address",                               &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, listen_address) },
2797 +               { XML_CONF_SUBSECTION,  "listen_options",                               &fpm_conf_set_listen_options_subsection,        offsetof(struct fpm_worker_pool_config_s, listen_options) },
2798 +               { XML_CONF_SUBSECTION,  "php_defines",                                  &fpm_conf_set_key_value_pairs_subsection,       offsetof(struct fpm_worker_pool_config_s, php_defines) },
2799 +               { XML_CONF_SCALAR,              "user",                                                 &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, user) },
2800 +               { XML_CONF_SCALAR,              "group",                                                &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, group) },
2801 +               { XML_CONF_SCALAR,              "chroot",                                               &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, chroot) },
2802 +               { XML_CONF_SCALAR,              "chdir",                                                &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, chdir) },
2803 +               { XML_CONF_SCALAR,              "allowed_clients",                              &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, allowed_clients) },
2804 +               { XML_CONF_SUBSECTION,  "environment",                                  &fpm_conf_set_key_value_pairs_subsection,       offsetof(struct fpm_worker_pool_config_s, environment) },
2805 +               { XML_CONF_SCALAR,              "request_terminate_timeout",    &xml_conf_set_slot_time,                                        offsetof(struct fpm_worker_pool_config_s, request_terminate_timeout) },
2806 +               { XML_CONF_SCALAR,              "request_slowlog_timeout",              &xml_conf_set_slot_time,                                        offsetof(struct fpm_worker_pool_config_s, request_slowlog_timeout) },
2807 +               { XML_CONF_SCALAR,              "slowlog",                                              &xml_conf_set_slot_string,                                      offsetof(struct fpm_worker_pool_config_s, slowlog) },
2808 +               { XML_CONF_SCALAR,              "rlimit_files",                                 &xml_conf_set_slot_integer,                                     offsetof(struct fpm_worker_pool_config_s, rlimit_files) },
2809 +               { XML_CONF_SCALAR,              "rlimit_core",                                  &fpm_conf_set_rlimit_core,                                      0 },
2810 +               { XML_CONF_SCALAR,              "max_requests",                                 &xml_conf_set_slot_integer,                                     offsetof(struct fpm_worker_pool_config_s, max_requests) },
2811 +               { XML_CONF_SCALAR,              "catch_workers_output",                 &fpm_conf_set_catch_workers_output,                     0 },
2812 +               { XML_CONF_SUBSECTION,  "pm",                                                   &fpm_conf_set_pm_subsection,                            offsetof(struct fpm_worker_pool_config_s, pm) },
2813 +               { 0, 0, 0, 0 }
2814 +       }
2815 +};
2816 +
2817 +static struct xml_conf_section *fpm_conf_all_sections[] = {
2818 +       &xml_section_fpm_global_options,
2819 +       &xml_section_fpm_worker_pool_config,
2820 +       0
2821 +};
2822 +
2823 +static int fpm_evaluate_full_path(char **path)
2824 +{
2825 +       if (**path != '/') {
2826 +               char *full_path;
2827 +
2828 +               full_path = malloc(sizeof(PHP_PREFIX) + strlen(*path) + 1);
2829 +
2830 +               if (!full_path) return -1;
2831 +
2832 +               sprintf(full_path, "%s/%s", PHP_PREFIX, *path);
2833 +
2834 +               free(*path);
2835 +
2836 +               *path = full_path;
2837 +       }
2838 +
2839 +       return 0;
2840 +}
2841 +
2842 +static int fpm_conf_process_all_pools()
2843 +{
2844 +       struct fpm_worker_pool_s *wp;
2845 +
2846 +       if (!fpm_worker_all_pools) {
2847 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "at least one pool section must be specified in config file");
2848 +               return -1;
2849 +       }
2850 +
2851 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
2852 +
2853 +               if (wp->config->listen_address && *wp->config->listen_address) {
2854 +
2855 +                       wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address);
2856 +
2857 +                       if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') {
2858 +                               fpm_evaluate_full_path(&wp->config->listen_address);
2859 +                       }
2860 +
2861 +               }
2862 +               else {
2863 +
2864 +                       wp->is_template = 1;
2865 +
2866 +               }
2867 +
2868 +               if (wp->config->request_slowlog_timeout) {
2869 +#if HAVE_FPM_TRACE
2870 +                       if (! (wp->config->slowlog && *wp->config->slowlog)) {
2871 +                               zlog(ZLOG_STUFF, ZLOG_ERROR, "pool %s: 'slowlog' must be specified for use with 'request_slowlog_timeout'",
2872 +                                       wp->config->name);
2873 +                               return -1;
2874 +                       }
2875 +#else
2876 +                       static int warned = 0;
2877 +
2878 +                       if (!warned) {
2879 +                               zlog(ZLOG_STUFF, ZLOG_WARNING, "pool %s: 'request_slowlog_timeout' is not supported on your system",
2880 +                                       wp->config->name);
2881 +                               warned = 1;
2882 +                       }
2883 +
2884 +                       wp->config->request_slowlog_timeout = 0;
2885 +#endif
2886 +               }
2887 +
2888 +               if (wp->config->request_slowlog_timeout && wp->config->slowlog && *wp->config->slowlog) {
2889 +                       int fd;
2890 +
2891 +                       fpm_evaluate_full_path(&wp->config->slowlog);
2892 +
2893 +                       if (wp->config->request_slowlog_timeout) {
2894 +                               fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
2895 +
2896 +                               if (0 > fd) {
2897 +                                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "open(%s) failed", wp->config->slowlog);
2898 +                                       return -1;
2899 +                               }
2900 +                               close(fd);
2901 +                       }
2902 +               }
2903 +       }
2904 +
2905 +       return 0;
2906 +}
2907 +
2908 +int fpm_conf_unlink_pid()
2909 +{
2910 +       if (fpm_global_config.pid_file) {
2911 +
2912 +               if (0 > unlink(fpm_global_config.pid_file)) {
2913 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "unlink(\"%s\") failed", fpm_global_config.pid_file);
2914 +                       return -1;
2915 +               }
2916 +
2917 +       }
2918 +
2919 +       return 0;
2920 +}
2921 +
2922 +int fpm_conf_write_pid()
2923 +{
2924 +       int fd;
2925 +
2926 +       if (fpm_global_config.pid_file) {
2927 +               char buf[64];
2928 +               int len;
2929 +
2930 +               unlink(fpm_global_config.pid_file);
2931 +
2932 +               fd = creat(fpm_global_config.pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2933 +
2934 +               if (fd < 0) {
2935 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "creat(\"%s\") failed", fpm_global_config.pid_file);
2936 +                       return -1;
2937 +               }
2938 +
2939 +               len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
2940 +
2941 +               if (len != write(fd, buf, len)) {
2942 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "write() failed");
2943 +                       return -1;
2944 +               }
2945 +
2946 +               close(fd);
2947 +       }
2948 +
2949 +       return 0;
2950 +}
2951 +
2952 +static int fpm_conf_post_process()
2953 +{
2954 +       if (fpm_global_config.pid_file) {
2955 +               fpm_evaluate_full_path(&fpm_global_config.pid_file);
2956 +       }
2957 +
2958 +       if (!fpm_global_config.error_log) {
2959 +               fpm_global_config.error_log = strdup(PHP_FPM_LOG_PATH);
2960 +       }
2961 +
2962 +       fpm_evaluate_full_path(&fpm_global_config.error_log);
2963 +
2964 +       if (0 > fpm_stdio_open_error_log(0)) {
2965 +               return -1;
2966 +       }
2967 +
2968 +       return fpm_conf_process_all_pools();
2969 +}
2970 +
2971 +static void fpm_conf_cleanup(int which, void *arg)
2972 +{
2973 +       free(fpm_global_config.pid_file);
2974 +       free(fpm_global_config.error_log);
2975 +       fpm_global_config.pid_file = 0;
2976 +       fpm_global_config.error_log = 0;
2977 +}
2978 +
2979 +int fpm_conf_init_main()
2980 +{
2981 +       char *filename = fpm_globals.config;
2982 +       char *err;
2983 +
2984 +       if (0 > xml_conf_sections_register(fpm_conf_all_sections)) {
2985 +               return -1;
2986 +       }
2987 +
2988 +       if (filename == NULL) {
2989 +               filename = PHP_FPM_CONF_PATH;
2990 +       }
2991 +
2992 +       err = xml_conf_load_file(filename);
2993 +
2994 +       if (err) {
2995 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "failed to load configuration file: %s", err);
2996 +               return -1;
2997 +       }
2998 +
2999 +       if (0 > fpm_conf_post_process()) {
3000 +               return -1;
3001 +       }
3002 +
3003 +       xml_conf_clean();
3004 +
3005 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
3006 +               return -1;
3007 +       }
3008 +
3009 +       return 0;
3010 +}
3011 diff --git a/sapi/cgi/fpm/fpm_conf.h b/sapi/cgi/fpm/fpm_conf.h
3012 new file mode 100644
3013 --- /dev/null
3014 +++ b/sapi/cgi/fpm/fpm_conf.h
3015 @@ -0,0 +1,73 @@
3016 +
3017 +       /* $Id$ */
3018 +       /* (c) 2007,2008 Andrei Nigmatulin */
3019 +
3020 +#ifndef FPM_CONF_H
3021 +#define FPM_CONF_H 1
3022 +
3023 +struct key_value_s;
3024 +
3025 +struct key_value_s {
3026 +       struct key_value_s *next;
3027 +       char *key;
3028 +       char *value;
3029 +};
3030 +
3031 +struct fpm_global_config_s {
3032 +       int emergency_restart_threshold;
3033 +       int emergency_restart_interval;
3034 +       int process_control_timeout;
3035 +       int daemonize;
3036 +       char *pid_file;
3037 +       char *error_log;
3038 +};
3039 +
3040 +extern struct fpm_global_config_s fpm_global_config;
3041 +
3042 +struct fpm_pm_s {
3043 +       int style;
3044 +       int max_children;
3045 +       struct {
3046 +               int StartServers;
3047 +               int MinSpareServers;
3048 +               int MaxSpareServers;
3049 +       } options_apache_like;
3050 +};
3051 +
3052 +struct fpm_listen_options_s {
3053 +       int backlog;
3054 +       char *owner;
3055 +       char *group;
3056 +       char *mode;
3057 +};
3058 +
3059 +struct fpm_worker_pool_config_s {
3060 +       char *name;
3061 +       char *listen_address;
3062 +       struct fpm_listen_options_s *listen_options;
3063 +       struct key_value_s *php_defines;
3064 +       char *user;
3065 +       char *group;
3066 +       char *chroot;
3067 +       char *chdir;
3068 +       char *allowed_clients;
3069 +       struct key_value_s *environment;
3070 +       struct fpm_pm_s *pm;
3071 +       int request_terminate_timeout;
3072 +       int request_slowlog_timeout;
3073 +       char *slowlog;
3074 +       int max_requests;
3075 +       int rlimit_files;
3076 +       int rlimit_core;
3077 +       unsigned catch_workers_output:1;
3078 +};
3079 +
3080 +enum { PM_STYLE_STATIC = 1, PM_STYLE_APACHE_LIKE = 2 };
3081 +
3082 +int fpm_conf_init_main();
3083 +int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc);
3084 +int fpm_conf_write_pid();
3085 +int fpm_conf_unlink_pid();
3086 +
3087 +#endif
3088 +
3089 diff --git a/sapi/cgi/fpm/fpm_config.h b/sapi/cgi/fpm/fpm_config.h
3090 new file mode 100644
3091 --- /dev/null
3092 +++ b/sapi/cgi/fpm/fpm_config.h
3093 @@ -0,0 +1,39 @@
3094 +
3095 +       /* $Id$ */
3096 +       /* (c) 2007,2008 Andrei Nigmatulin */
3097 +
3098 +#include "php_config.h"
3099 +#include "fpm_autoconf.h"
3100 +
3101 +
3102 +/* Solaris does not have it */
3103 +#ifndef INADDR_NONE
3104 +#define INADDR_NONE (-1)
3105 +#endif
3106 +
3107 +
3108 +/* If we're not using GNU C, elide __attribute__ */
3109 +#ifndef __GNUC__
3110 +#  define  __attribute__(x)  /*NOTHING*/
3111 +#endif
3112 +
3113 +
3114 +/* Solaris does not have it */
3115 +#ifndef timersub
3116 +#define        timersub(tvp, uvp, vvp)                                         \
3117 +       do {                                                            \
3118 +               (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;          \
3119 +               (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;       \
3120 +               if ((vvp)->tv_usec < 0) {                               \
3121 +                       (vvp)->tv_sec--;                                \
3122 +                       (vvp)->tv_usec += 1000000;                      \
3123 +               }                                                       \
3124 +       } while (0)
3125 +#endif
3126 +
3127 +#if defined(HAVE_PTRACE) || defined(PROC_MEM_FILE) || defined(HAVE_MACH_VM_READ)
3128 +#define HAVE_FPM_TRACE 1
3129 +#else
3130 +#define HAVE_FPM_TRACE 0
3131 +#endif
3132 +
3133 diff --git a/sapi/cgi/fpm/fpm_env.c b/sapi/cgi/fpm/fpm_env.c
3134 new file mode 100644
3135 --- /dev/null
3136 +++ b/sapi/cgi/fpm/fpm_env.c
3137 @@ -0,0 +1,125 @@
3138 +
3139 +       /* $Id$ */
3140 +       /* (c) 2007,2008 Andrei Nigmatulin */
3141 +
3142 +#include "fpm_config.h"
3143 +
3144 +#ifdef HAVE_ALLOCA_H
3145 +#include <alloca.h>
3146 +#endif
3147 +#include <stdio.h>
3148 +#include <stdlib.h>
3149 +#include <string.h>
3150 +
3151 +#include "fpm_env.h"
3152 +#include "zlog.h"
3153 +
3154 +#ifndef HAVE_SETENV
3155 +int setenv(char *name, char *value, int overwrite)
3156 +{
3157 +       int name_len = strlen(name);
3158 +       int value_len = strlen(value);
3159 +       char *var = alloca(name_len + 1 + value_len + 1);
3160 +
3161 +       memcpy(var, name, name_len);
3162 +
3163 +       var[name_len] = '=';
3164 +
3165 +       memcpy(var + name_len + 1, value, value_len);
3166 +
3167 +       var[name_len + 1 + value_len] = '\0';
3168 +
3169 +       return putenv(var);
3170 +}
3171 +#endif
3172 +
3173 +#ifndef HAVE_CLEARENV
3174 +void clearenv()
3175 +{
3176 +       char **envp;
3177 +       char *s;
3178 +
3179 +       /* this algo is the only one known to me
3180 +               that works well on all systems */
3181 +       while (*(envp = environ)) {
3182 +               char *eq = strchr(*envp, '=');
3183 +
3184 +               s = strdup(*envp);
3185 +
3186 +               if (eq) s[eq - *envp] = '\0';
3187 +
3188 +               unsetenv(s);
3189 +               free(s);
3190 +       }
3191 +
3192 +}
3193 +#endif
3194 +
3195 +
3196 +int fpm_env_init_child(struct fpm_worker_pool_s *wp)
3197 +{
3198 +       struct key_value_s *kv;
3199 +
3200 +       clearenv();
3201 +
3202 +       for (kv = wp->config->environment; kv; kv = kv->next) {
3203 +               setenv(kv->key, kv->value, 1);
3204 +       }
3205 +
3206 +       if (wp->user) {
3207 +               setenv("USER", wp->user, 1);
3208 +       }
3209 +
3210 +       if (wp->home) {
3211 +               setenv("HOME", wp->home, 1);
3212 +       }
3213 +
3214 +       return 0;
3215 +}
3216 +
3217 +static int fpm_env_conf_wp(struct fpm_worker_pool_s *wp)
3218 +{
3219 +       struct key_value_s *kv;
3220 +
3221 +       kv = wp->config->environment;
3222 +
3223 +       for (kv = wp->config->environment; kv; kv = kv->next) {
3224 +               if (*kv->value == '$') {
3225 +                       char *value = getenv(kv->value + 1);
3226 +
3227 +                       if (!value) value = "";
3228 +
3229 +                       free(kv->value);
3230 +                       kv->value = strdup(value);
3231 +               }
3232 +
3233 +               /* autodetected values should be removed
3234 +                       if these vars specified in config */
3235 +               if (!strcmp(kv->key, "USER")) {
3236 +                       free(wp->user);
3237 +                       wp->user = 0;
3238 +               }
3239 +
3240 +               if (!strcmp(kv->key, "HOME")) {
3241 +                       free(wp->home);
3242 +                       wp->home = 0;
3243 +               }
3244 +       }
3245 +
3246 +       return 0;
3247 +}
3248 +
3249 +int fpm_env_init_main()
3250 +{
3251 +       struct fpm_worker_pool_s *wp;
3252 +
3253 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
3254 +
3255 +               if (0 > fpm_env_conf_wp(wp)) {
3256 +                       return -1;
3257 +               }
3258 +
3259 +       }
3260 +
3261 +       return 0;
3262 +}
3263 diff --git a/sapi/cgi/fpm/fpm_env.h b/sapi/cgi/fpm/fpm_env.h
3264 new file mode 100644
3265 --- /dev/null
3266 +++ b/sapi/cgi/fpm/fpm_env.h
3267 @@ -0,0 +1,24 @@
3268 +
3269 +       /* $Id$ */
3270 +       /* (c) 2007,2008 Andrei Nigmatulin */
3271 +
3272 +#ifndef FPM_ENV_H
3273 +#define FPM_ENV_H 1
3274 +
3275 +#include "fpm_worker_pool.h"
3276 +
3277 +int fpm_env_init_child(struct fpm_worker_pool_s *wp);
3278 +int fpm_env_init_main();
3279 +
3280 +extern char **environ;
3281 +
3282 +#ifndef HAVE_SETENV
3283 +int setenv(char *name, char *value, int overwrite);
3284 +#endif
3285 +
3286 +#ifndef HAVE_CLEARENV
3287 +void clearenv();
3288 +#endif
3289 +
3290 +#endif
3291 +
3292 diff --git a/sapi/cgi/fpm/fpm_events.c b/sapi/cgi/fpm/fpm_events.c
3293 new file mode 100644
3294 --- /dev/null
3295 +++ b/sapi/cgi/fpm/fpm_events.c
3296 @@ -0,0 +1,135 @@
3297 +
3298 +       /* $Id$ */
3299 +       /* (c) 2007,2008 Andrei Nigmatulin */
3300 +
3301 +#include "fpm_config.h"
3302 +
3303 +#include <unistd.h>
3304 +#include <errno.h>
3305 +#include <stdlib.h> /* for putenv */
3306 +#include <string.h>
3307 +#include <sys/types.h> /* for event.h below */
3308 +#include <event.h>
3309 +
3310 +#include "fpm.h"
3311 +#include "fpm_process_ctl.h"
3312 +#include "fpm_events.h"
3313 +#include "fpm_cleanup.h"
3314 +#include "fpm_stdio.h"
3315 +#include "fpm_signals.h"
3316 +#include "fpm_children.h"
3317 +#include "zlog.h"
3318 +
3319 +static void fpm_event_cleanup(int which, void *arg)
3320 +{
3321 +       event_base_free(0);
3322 +}
3323 +
3324 +static void fpm_got_signal(int fd, short ev, void *arg)
3325 +{
3326 +       char c;
3327 +       int res;
3328 +
3329 +       do {
3330 +
3331 +               do {
3332 +                       res = read(fd, &c, 1);
3333 +               } while (res == -1 && errno == EINTR);
3334 +
3335 +               if (res <= 0) {
3336 +                       if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
3337 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "read() failed");
3338 +                       }
3339 +                       return;
3340 +               }
3341 +
3342 +               switch (c) {
3343 +                       case 'C' :                  /* SIGCHLD */
3344 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "received SIGCHLD");
3345 +                               fpm_children_bury();
3346 +                               break;
3347 +                       case 'I' :                  /* SIGINT  */
3348 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "received SIGINT");
3349 +                               fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
3350 +                               break;
3351 +                       case 'T' :                  /* SIGTERM */
3352 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "received SIGTERM");
3353 +                               fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
3354 +                               break;
3355 +                       case 'Q' :                  /* SIGQUIT */
3356 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "received SIGQUIT");
3357 +                               fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET);
3358 +                               break;
3359 +                       case '1' :                  /* SIGUSR1 */
3360 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "received SIGUSR1");
3361 +                               if (0 == fpm_stdio_open_error_log(1)) {
3362 +                                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "log file re-opened");
3363 +                               }
3364 +                               break;
3365 +                       case '2' :                  /* SIGUSR2 */
3366 +                               zlog(ZLOG_STUFF, ZLOG_NOTICE, "received SIGUSR2");
3367 +                               fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
3368 +                               break;
3369 +               }
3370 +
3371 +               if (fpm_globals.is_child) {
3372 +                       break;
3373 +               }
3374 +
3375 +       } while (1);
3376 +
3377 +       return;
3378 +}
3379 +
3380 +int fpm_event_init_main()
3381 +{
3382 +       event_init();
3383 +
3384 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "libevent: using %s", event_get_method());
3385 +
3386 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, 0)) {
3387 +               return -1;
3388 +       }
3389 +
3390 +       return 0;
3391 +}
3392 +
3393 +int fpm_event_loop()
3394 +{
3395 +       static struct event signal_fd_event;
3396 +
3397 +       event_set(&signal_fd_event, fpm_signals_get_fd(), EV_PERSIST | EV_READ, &fpm_got_signal, 0);
3398 +
3399 +       event_add(&signal_fd_event, 0);
3400 +
3401 +       fpm_pctl_heartbeat(-1, 0, 0);
3402 +
3403 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "libevent: entering main loop");
3404 +
3405 +       event_loop(0);
3406 +
3407 +       return 0;
3408 +}
3409 +
3410 +int fpm_event_add(int fd, struct event *ev, void (*callback)(int, short, void *), void *arg)
3411 +{
3412 +       event_set(ev, fd, EV_PERSIST | EV_READ, callback, arg);
3413 +
3414 +       return event_add(ev, 0);
3415 +}
3416 +
3417 +int fpm_event_del(struct event *ev)
3418 +{
3419 +       return event_del(ev);
3420 +}
3421 +
3422 +void fpm_event_exit_loop()
3423 +{
3424 +       event_loopbreak();
3425 +}
3426 +
3427 +void fpm_event_fire(struct event *ev)
3428 +{
3429 +       (*ev->ev_callback)( (int) ev->ev_fd, (short) ev->ev_res, ev->ev_arg);   
3430 +}
3431 +
3432 diff --git a/sapi/cgi/fpm/fpm_events.h b/sapi/cgi/fpm/fpm_events.h
3433 new file mode 100644
3434 --- /dev/null
3435 +++ b/sapi/cgi/fpm/fpm_events.h
3436 @@ -0,0 +1,16 @@
3437 +
3438 +       /* $Id$ */
3439 +       /* (c) 2007,2008 Andrei Nigmatulin */
3440 +
3441 +#ifndef FPM_EVENTS_H
3442 +#define FPM_EVENTS_H 1
3443 +
3444 +void fpm_event_exit_loop();
3445 +int fpm_event_loop();
3446 +int fpm_event_add(int fd, struct event *ev, void (*callback)(int, short, void *), void *arg);
3447 +int fpm_event_del(struct event *ev);
3448 +void fpm_event_fire(struct event *ev);
3449 +int fpm_event_init_main();
3450 +
3451 +
3452 +#endif
3453 diff --git a/sapi/cgi/fpm/fpm_php.c b/sapi/cgi/fpm/fpm_php.c
3454 new file mode 100644
3455 --- /dev/null
3456 +++ b/sapi/cgi/fpm/fpm_php.c
3457 @@ -0,0 +1,190 @@
3458 +
3459 +       /* $Id$ */
3460 +       /* (c) 2007,2008 Andrei Nigmatulin */
3461 +
3462 +#include "fpm_config.h"
3463 +
3464 +#include <stdlib.h>
3465 +#include <string.h>
3466 +#include <stdio.h>
3467 +
3468 +#include "php.h"
3469 +#include "php_main.h"
3470 +#include "php_ini.h"
3471 +#include "ext/standard/dl.h"
3472 +
3473 +#include "fastcgi.h"
3474 +
3475 +#include "fpm.h"
3476 +#include "fpm_php.h"
3477 +#include "fpm_cleanup.h"
3478 +#include "fpm_worker_pool.h"
3479 +
3480 +static int zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int stage TSRMLS_DC)
3481 +{
3482 +       zend_ini_entry *ini_entry;
3483 +       char *duplicate;
3484 +
3485 +       if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
3486 +               return FAILURE;
3487 +       }
3488 +
3489 +       duplicate = strdup(new_value);
3490 +
3491 +       if (!ini_entry->on_modify
3492 +               || ini_entry->on_modify(ini_entry, duplicate, new_value_length,
3493 +                       ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC) == SUCCESS) {
3494 +               ini_entry->value = duplicate;
3495 +               ini_entry->value_length = new_value_length;
3496 +       } else {
3497 +               free(duplicate);
3498 +       }
3499 +
3500 +       return SUCCESS;
3501 +}
3502 +
3503 +static void fpm_php_disable(char *value, int (*zend_disable)(char *, uint TSRMLS_DC) TSRMLS_DC)
3504 +{
3505 +       char *s = 0, *e = value;
3506 +
3507 +       while (*e) {
3508 +               switch (*e) {
3509 +                       case ' ':
3510 +                       case ',':
3511 +                               if (s) {
3512 +                                       *e = '\0';
3513 +                                       zend_disable(s, e - s TSRMLS_CC);
3514 +                                       s = 0;
3515 +                               }
3516 +                               break;
3517 +                       default:
3518 +                               if (!s) {
3519 +                                       s = e;
3520 +                               }
3521 +                               break;
3522 +               }
3523 +               e++;
3524 +       }
3525 +
3526 +       if (s) {
3527 +               zend_disable(s, e - s TSRMLS_CC);
3528 +       }
3529 +}
3530 +
3531 +static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp)
3532 +{
3533 +       TSRMLS_FETCH();
3534 +       struct key_value_s *kv;
3535 +
3536 +       for (kv = wp->config->php_defines; kv; kv = kv->next) {
3537 +               char *name = kv->key;
3538 +               char *value = kv->value;
3539 +               int name_len = strlen(name);
3540 +               int value_len = strlen(value);
3541 +
3542 +               if (!strcmp(name, "extension") && *value) {
3543 +                       zval zv;
3544 +
3545 +#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID >= 50300)
3546 +                       php_dl(value, MODULE_PERSISTENT, &zv, 1 TSRMLS_CC);
3547 +#else
3548 +                       zval filename;
3549 +                       ZVAL_STRINGL(&filename, value, value_len, 0);
3550 +#if (PHP_MAJOR_VERSION >= 5)
3551 +                       php_dl(&filename, MODULE_PERSISTENT, &zv, 1 TSRMLS_CC);
3552 +#else
3553 +                       php_dl(&filename, MODULE_PERSISTENT, &zv TSRMLS_CC);
3554 +#endif
3555 +#endif
3556 +                       continue;
3557 +               }
3558 +
3559 +               zend_ini_alter_master(name, name_len + 1, value, value_len, PHP_INI_STAGE_ACTIVATE TSRMLS_CC);
3560 +
3561 +               if (!strcmp(name, "disable_functions") && *value) {
3562 +                       char *v = strdup(value);
3563 +#if (PHP_MAJOR_VERSION >= 5)
3564 +                       PG(disable_functions) = v;
3565 +#endif
3566 +                       fpm_php_disable(v, zend_disable_function TSRMLS_CC);
3567 +               }
3568 +               else if (!strcmp(name, "disable_classes") && *value) {
3569 +                       char *v = strdup(value);
3570 +#if (PHP_MAJOR_VERSION >= 5)
3571 +                       PG(disable_classes) = v;
3572 +#endif
3573 +                       fpm_php_disable(v, zend_disable_class TSRMLS_CC);
3574 +               }
3575 +       }
3576 +
3577 +       return 0;
3578 +}
3579 +
3580 +static int fpm_php_set_allowed_clients(struct fpm_worker_pool_s *wp)
3581 +{
3582 +       if (wp->listen_address_domain == FPM_AF_INET) {
3583 +               fcgi_set_allowed_clients(wp->config->allowed_clients);
3584 +       }
3585 +
3586 +       return 0;
3587 +}
3588 +
3589 +static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp)
3590 +{
3591 +       char max_workers[10 + 1]; /* 4294967295 */
3592 +       int len;
3593 +
3594 +       len = sprintf(max_workers, "%u", (unsigned int) wp->config->pm->max_children);
3595 +
3596 +       fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, max_workers, len);
3597 +       fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  max_workers, len);
3598 +
3599 +       return 0;
3600 +}
3601 +
3602 +char *fpm_php_script_filename(TSRMLS_D)
3603 +{
3604 +       return SG(request_info).path_translated;
3605 +}
3606 +
3607 +char *fpm_php_request_method(TSRMLS_D)
3608 +{
3609 +       return (char *) SG(request_info).request_method;
3610 +}
3611 +
3612 +size_t fpm_php_content_length(TSRMLS_D)
3613 +{
3614 +       return SG(request_info).content_length;
3615 +}
3616 +
3617 +static void fpm_php_cleanup(int which, void *arg)
3618 +{
3619 +       TSRMLS_FETCH();
3620 +       php_module_shutdown(TSRMLS_C);
3621 +       sapi_shutdown();
3622 +}
3623 +
3624 +void fpm_php_soft_quit()
3625 +{
3626 +       fcgi_set_in_shutdown(1);
3627 +}
3628 +
3629 +int fpm_php_init_main()
3630 +{
3631 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) {
3632 +               return -1;
3633 +       }
3634 +
3635 +       return 0;
3636 +}
3637 +
3638 +int fpm_php_init_child(struct fpm_worker_pool_s *wp)
3639 +{
3640 +       if (0 > fpm_php_apply_defines(wp) ||
3641 +               0 > fpm_php_set_allowed_clients(wp) ||
3642 +               0 > fpm_php_set_fcgi_mgmt_vars(wp)) {
3643 +               return -1;
3644 +       }
3645 +
3646 +       return 0;
3647 +}
3648 diff --git a/sapi/cgi/fpm/fpm_php.h b/sapi/cgi/fpm/fpm_php.h
3649 new file mode 100644
3650 --- /dev/null
3651 +++ b/sapi/cgi/fpm/fpm_php.h
3652 @@ -0,0 +1,22 @@
3653 +
3654 +       /* $Id$ */
3655 +       /* (c) 2007,2008 Andrei Nigmatulin */
3656 +
3657 +#ifndef FPM_PHP_H
3658 +#define FPM_PHP_H 1
3659 +
3660 +#include <TSRM.h>
3661 +
3662 +#include "build-defs.h" /* for PHP_ defines */
3663 +
3664 +struct fpm_worker_pool_s;
3665 +
3666 +int fpm_php_init_child(struct fpm_worker_pool_s *wp);
3667 +char *fpm_php_script_filename(TSRMLS_D);
3668 +char *fpm_php_request_method(TSRMLS_D);
3669 +size_t fpm_php_content_length(TSRMLS_D);
3670 +void fpm_php_soft_quit();
3671 +int fpm_php_init_main();
3672 +
3673 +#endif
3674 +
3675 diff --git a/sapi/cgi/fpm/fpm_php_trace.c b/sapi/cgi/fpm/fpm_php_trace.c
3676 new file mode 100644
3677 --- /dev/null
3678 +++ b/sapi/cgi/fpm/fpm_php_trace.c
3679 @@ -0,0 +1,171 @@
3680 +
3681 +       /* $Id$ */
3682 +       /* (c) 2007,2008 Andrei Nigmatulin */
3683 +
3684 +#include "fpm_config.h"
3685 +
3686 +#if HAVE_FPM_TRACE
3687 +
3688 +#include "php.h"
3689 +#include "php_main.h"
3690 +
3691 +#include <stdio.h>
3692 +#include <stddef.h>
3693 +#include <stdint.h>
3694 +#include <unistd.h>
3695 +#include <sys/time.h>
3696 +#include <sys/types.h>
3697 +#include <errno.h>
3698 +
3699 +#include "fpm_trace.h"
3700 +#include "fpm_php_trace.h"
3701 +#include "fpm_children.h"
3702 +#include "fpm_worker_pool.h"
3703 +#include "fpm_process_ctl.h"
3704 +
3705 +#include "zlog.h"
3706 +
3707 +
3708 +#define valid_ptr(p) ((p) && 0 == ((p) & (sizeof(long) - 1)))
3709 +
3710 +#if SIZEOF_LONG == 4
3711 +#define PTR_FMT "08"
3712 +#elif SIZEOF_LONG == 8
3713 +#define PTR_FMT "016"
3714 +#endif
3715 +
3716 +
3717 +static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog TSRMLS_DC)
3718 +{
3719 +       int callers_limit = 20;
3720 +       pid_t pid = child->pid;
3721 +       struct timeval tv;
3722 +       static const int buf_size = 1024;
3723 +       char buf[buf_size];
3724 +       long execute_data;
3725 +       long l;
3726 +
3727 +       gettimeofday(&tv, 0);
3728 +
3729 +       zlog_print_time(&tv, buf, buf_size);
3730 +
3731 +       fprintf(slowlog, "\n%s pid %d (pool %s)\n", buf, (int) pid, child->wp->config->name);
3732 +
3733 +       if (0 > fpm_trace_get_strz(buf, buf_size, (long) &SG(request_info).path_translated)) {
3734 +               return -1;
3735 +       }
3736 +
3737 +       fprintf(slowlog, "script_filename = %s\n", buf);
3738 +
3739 +       if (0 > fpm_trace_get_long((long) &EG(current_execute_data), &l)) {
3740 +               return -1;
3741 +       }
3742 +
3743 +       execute_data = l;
3744 +
3745 +       while (execute_data) {
3746 +               long function;
3747 +               uint lineno = 0;
3748 +
3749 +               fprintf(slowlog, "[0x%" PTR_FMT "lx] ", execute_data);
3750 +
3751 +               if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, function_state.function), &l)) {
3752 +                       return -1;
3753 +               }
3754 +
3755 +               function = l;
3756 +
3757 +               if (valid_ptr(function)) {
3758 +                       if (0 > fpm_trace_get_strz(buf, buf_size, function + offsetof(zend_function, common.function_name))) {
3759 +                               return -1;
3760 +                       }
3761 +
3762 +                       fprintf(slowlog, "%s()", buf);
3763 +               }
3764 +               else {
3765 +                       fprintf(slowlog, "???");
3766 +               }
3767 +
3768 +               if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, op_array), &l)) {
3769 +                       return -1;
3770 +               }
3771 +
3772 +               *buf = '\0';
3773 +
3774 +               if (valid_ptr(l)) {
3775 +                       long op_array = l;
3776 +
3777 +                       if (0 > fpm_trace_get_strz(buf, buf_size, op_array + offsetof(zend_op_array, filename))) {
3778 +                               return -1;
3779 +                       }
3780 +               }
3781 +
3782 +               if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, opline), &l)) {
3783 +                       return -1;
3784 +               }
3785 +
3786 +               if (valid_ptr(l)) {
3787 +                       long opline = l;
3788 +                       uint *lu = (uint *) &l;
3789 +
3790 +                       if (0 > fpm_trace_get_long(opline + offsetof(struct _zend_op, lineno), &l)) {
3791 +                               return -1;
3792 +                       }
3793 +
3794 +                       lineno = *lu;
3795 +               }
3796 +
3797 +               fprintf(slowlog, " %s:%u\n", *buf ? buf : "unknown", lineno);
3798 +
3799 +               if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, prev_execute_data), &l)) {
3800 +                       return -1;
3801 +               }
3802 +
3803 +               execute_data = l;
3804 +
3805 +               if (0 == --callers_limit) {
3806 +                       break;
3807 +               }
3808 +       }
3809 +
3810 +       return 0;
3811 +}
3812 +
3813 +void fpm_php_trace(struct fpm_child_s *child)
3814 +{
3815 +       TSRMLS_FETCH();
3816 +       FILE *slowlog;
3817 +
3818 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "about to trace %d", (int) child->pid);
3819 +
3820 +       slowlog = fopen(child->wp->config->slowlog, "a+");
3821 +
3822 +       if (!slowlog) {
3823 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fopen(%s) failed", child->wp->config->slowlog);
3824 +               goto done0;
3825 +       }
3826 +
3827 +       if (0 > fpm_trace_ready(child->pid)) {
3828 +               goto done1;
3829 +       }
3830 +
3831 +       if (0 > fpm_php_trace_dump(child, slowlog TSRMLS_CC)) {
3832 +               fprintf(slowlog, "+++ dump failed\n");
3833 +       }
3834 +
3835 +       if (0 > fpm_trace_close(child->pid)) {
3836 +               goto done1;
3837 +       }
3838 +
3839 +done1:
3840 +       fclose(slowlog);
3841 +
3842 +done0:
3843 +       fpm_pctl_kill(child->pid, FPM_PCTL_CONT);
3844 +       child->tracer = 0;
3845 +
3846 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "finished trace of %d", (int) child->pid);
3847 +}
3848 +
3849 +#endif
3850 +
3851 diff --git a/sapi/cgi/fpm/fpm_php_trace.h b/sapi/cgi/fpm/fpm_php_trace.h
3852 new file mode 100644
3853 --- /dev/null
3854 +++ b/sapi/cgi/fpm/fpm_php_trace.h
3855 @@ -0,0 +1,13 @@
3856 +
3857 +       /* $Id$ */
3858 +       /* (c) 2007,2008 Andrei Nigmatulin */
3859 +
3860 +#ifndef FPM_PHP_TRACE_H
3861 +#define FPM_PHP_TRACE_H 1
3862 +
3863 +struct fpm_child_s;
3864 +
3865 +void fpm_php_trace(struct fpm_child_s *);
3866 +
3867 +#endif
3868 +
3869 diff --git a/sapi/cgi/fpm/fpm_process_ctl.c b/sapi/cgi/fpm/fpm_process_ctl.c
3870 new file mode 100644
3871 --- /dev/null
3872 +++ b/sapi/cgi/fpm/fpm_process_ctl.c
3873 @@ -0,0 +1,354 @@
3874 +
3875 +       /* $Id$ */
3876 +       /* (c) 2007,2008 Andrei Nigmatulin */
3877 +
3878 +#include "fpm_config.h"
3879 +
3880 +#include <sys/types.h>
3881 +#include <signal.h>
3882 +#include <unistd.h>
3883 +#include <stdlib.h>
3884 +
3885 +#include "fpm.h"
3886 +#include "fpm_clock.h"
3887 +#include "fpm_children.h"
3888 +#include "fpm_signals.h"
3889 +#include "fpm_events.h"
3890 +#include "fpm_process_ctl.h"
3891 +#include "fpm_cleanup.h"
3892 +#include "fpm_request.h"
3893 +#include "fpm_worker_pool.h"
3894 +#include "zlog.h"
3895 +
3896 +
3897 +static int fpm_state = FPM_PCTL_STATE_NORMAL;
3898 +static int fpm_signal_sent = 0;
3899 +
3900 +
3901 +static const char *fpm_state_names[] = {
3902 +       [FPM_PCTL_STATE_NORMAL] = "normal",
3903 +       [FPM_PCTL_STATE_RELOADING] = "reloading",
3904 +       [FPM_PCTL_STATE_TERMINATING] = "terminating",
3905 +       [FPM_PCTL_STATE_FINISHING] = "finishing"
3906 +};
3907 +
3908 +static int saved_argc;
3909 +static char **saved_argv;
3910 +
3911 +static void fpm_pctl_cleanup(int which, void *arg)
3912 +{
3913 +       int i;
3914 +
3915 +       if (which != FPM_CLEANUP_PARENT_EXEC) {
3916 +
3917 +               for (i = 0; i < saved_argc; i++) {
3918 +                       free(saved_argv[i]);
3919 +               }
3920 +
3921 +               free(saved_argv);
3922 +
3923 +       }
3924 +}
3925 +
3926 +static struct event pctl_event;
3927 +
3928 +static void fpm_pctl_action(int fd, short which, void *arg)
3929 +{
3930 +       evtimer_del(&pctl_event);
3931 +
3932 +       memset(&pctl_event, 0, sizeof(pctl_event));
3933 +
3934 +       fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
3935 +}
3936 +
3937 +static int fpm_pctl_timeout_set(int sec)
3938 +{
3939 +       struct timeval tv = { .tv_sec = sec, .tv_usec = 0 };
3940 +
3941 +       if (evtimer_initialized(&pctl_event)) {
3942 +               evtimer_del(&pctl_event);
3943 +       }
3944 +
3945 +       evtimer_set(&pctl_event, &fpm_pctl_action, 0);
3946 +
3947 +       evtimer_add(&pctl_event, &tv);
3948 +
3949 +       return 0;
3950 +}
3951 +
3952 +static void fpm_pctl_exit()
3953 +{
3954 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "exiting, bye-bye!");
3955 +
3956 +       fpm_conf_unlink_pid();
3957 +
3958 +       fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN);
3959 +
3960 +       exit(0);
3961 +}
3962 +
3963 +#define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
3964 +
3965 +static void fpm_pctl_exec()
3966 +{
3967 +
3968 +       zlog(ZLOG_STUFF, ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
3969 +                       "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
3970 +                       "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
3971 +               "})",
3972 +               saved_argv[0], saved_argv[0],
3973 +               optional_arg(1),
3974 +               optional_arg(2),
3975 +               optional_arg(3),
3976 +               optional_arg(4),
3977 +               optional_arg(5),
3978 +               optional_arg(6),
3979 +               optional_arg(7),
3980 +               optional_arg(8),
3981 +               optional_arg(9),
3982 +               optional_arg(10)
3983 +       );
3984 +
3985 +       fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC);
3986 +
3987 +       execvp(saved_argv[0], saved_argv);
3988 +
3989 +       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "execvp() failed");
3990 +
3991 +       exit(1);
3992 +}
3993 +
3994 +static void fpm_pctl_action_last()
3995 +{
3996 +       switch (fpm_state) {
3997 +
3998 +               case FPM_PCTL_STATE_RELOADING :
3999 +
4000 +                       fpm_pctl_exec();
4001 +                       break;
4002 +
4003 +               case FPM_PCTL_STATE_FINISHING :
4004 +
4005 +               case FPM_PCTL_STATE_TERMINATING :
4006 +
4007 +                       fpm_pctl_exit();
4008 +                       break;
4009 +       }
4010 +}
4011 +
4012 +int fpm_pctl_kill(pid_t pid, int how)
4013 +{
4014 +       int s = 0;
4015 +
4016 +       switch (how) {
4017 +               case FPM_PCTL_TERM :
4018 +                       s = SIGTERM;
4019 +                       break;
4020 +               case FPM_PCTL_STOP :
4021 +                       s = SIGSTOP;
4022 +                       break;
4023 +               case FPM_PCTL_CONT :
4024 +                       s = SIGCONT;
4025 +                       break;
4026 +               default :
4027 +                       break;
4028 +       }
4029 +
4030 +       return kill(pid, s);
4031 +}
4032 +
4033 +static void fpm_pctl_kill_all(int signo)
4034 +{
4035 +       struct fpm_worker_pool_s *wp;
4036 +       int alive_children = 0;
4037 +
4038 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
4039 +               struct fpm_child_s *child;
4040 +
4041 +               for (child = wp->children; child; child = child->next) {
4042 +
4043 +                       int res = kill(child->pid, signo);
4044 +
4045 +                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "sending signal %d %s to child %d (pool %s)", signo,
4046 +                               fpm_signal_names[signo] ? fpm_signal_names[signo] : "",
4047 +                               (int) child->pid, child->wp->config->name);
4048 +
4049 +                       if (res == 0) ++alive_children;
4050 +               }
4051 +       }
4052 +
4053 +       if (alive_children) {
4054 +               zlog(ZLOG_STUFF, ZLOG_NOTICE, "%d %s still alive", alive_children, alive_children == 1 ? "child is" : "children are");
4055 +       }
4056 +}
4057 +
4058 +static void fpm_pctl_action_next()
4059 +{
4060 +       int sig, timeout;
4061 +
4062 +       if (!fpm_globals.running_children) fpm_pctl_action_last();
4063 +
4064 +       if (fpm_signal_sent == 0) {
4065 +               if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
4066 +                       sig = SIGTERM;
4067 +               }
4068 +               else {
4069 +                       sig = SIGQUIT;
4070 +               }
4071 +               timeout = fpm_global_config.process_control_timeout;
4072 +       }
4073 +       else {
4074 +               if (fpm_signal_sent == SIGQUIT) {
4075 +                       sig = SIGTERM;
4076 +               }
4077 +               else {
4078 +                       sig = SIGKILL;
4079 +               }
4080 +               timeout = 1;
4081 +       }
4082 +
4083 +       fpm_pctl_kill_all(sig);
4084 +
4085 +       fpm_signal_sent = sig;
4086 +
4087 +       fpm_pctl_timeout_set(timeout);
4088 +}
4089 +
4090 +void fpm_pctl(int new_state, int action)
4091 +{
4092 +       switch (action) {
4093 +
4094 +               case FPM_PCTL_ACTION_SET :
4095 +
4096 +                       if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
4097 +                               return;
4098 +                       }
4099 +
4100 +                       switch (fpm_state) { /* check which states can be overridden */
4101 +
4102 +                               case FPM_PCTL_STATE_NORMAL :
4103 +
4104 +                                       /* 'normal' can be overridden by any other state */
4105 +                                       break;
4106 +
4107 +                               case FPM_PCTL_STATE_RELOADING :
4108 +
4109 +                                       /* 'reloading' can be overridden by 'finishing' */
4110 +                                       if (new_state == FPM_PCTL_STATE_FINISHING) break;
4111 +
4112 +                               case FPM_PCTL_STATE_FINISHING :
4113 +
4114 +                                       /* 'reloading' and 'finishing' can be overridden by 'terminating' */
4115 +                                       if (new_state == FPM_PCTL_STATE_TERMINATING) break;
4116 +
4117 +                               case FPM_PCTL_STATE_TERMINATING :
4118 +
4119 +                                       /* nothing can override 'terminating' state */
4120 +                                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "not switching to '%s' state, because already in '%s' state",
4121 +                                               fpm_state_names[new_state], fpm_state_names[fpm_state]);
4122 +
4123 +                                       return;
4124 +                       }
4125 +
4126 +                       fpm_signal_sent = 0;
4127 +                       fpm_state = new_state;
4128 +
4129 +                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "switching to '%s' state", fpm_state_names[fpm_state]);
4130 +
4131 +                       /* fall down */
4132 +
4133 +               case FPM_PCTL_ACTION_TIMEOUT :
4134 +
4135 +                       fpm_pctl_action_next();
4136 +
4137 +                       break;
4138 +
4139 +               case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
4140 +
4141 +                       fpm_pctl_action_last();
4142 +
4143 +                       break;
4144 +
4145 +       }
4146 +}
4147 +
4148 +int fpm_pctl_can_spawn_children()
4149 +{
4150 +       return fpm_state == FPM_PCTL_STATE_NORMAL;
4151 +}
4152 +
4153 +int fpm_pctl_child_exited()
4154 +{
4155 +       if (fpm_state == FPM_PCTL_STATE_NORMAL) return 0;
4156 +
4157 +       if (!fpm_globals.running_children) {
4158 +               fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
4159 +       }
4160 +
4161 +       return 0;
4162 +}
4163 +
4164 +int fpm_pctl_init_main()
4165 +{
4166 +       int i;
4167 +
4168 +       saved_argc = fpm_globals.argc;
4169 +
4170 +       saved_argv = malloc(sizeof(char *) * (saved_argc + 1));
4171 +
4172 +       if (!saved_argv) {
4173 +               return -1;
4174 +       }
4175 +
4176 +       for (i = 0; i < saved_argc; i++) {
4177 +               saved_argv[i] = strdup(fpm_globals.argv[i]);
4178 +
4179 +               if (!saved_argv[i]) {
4180 +                       return -1;
4181 +               }
4182 +       }
4183 +
4184 +       saved_argv[i] = 0;
4185 +
4186 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
4187 +               return -1;
4188 +       }
4189 +
4190 +       return 0;
4191 +}
4192 +
4193 +static void fpm_pctl_check_request_timeout(struct timeval *now)
4194 +{
4195 +       struct fpm_worker_pool_s *wp;
4196 +
4197 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
4198 +               int terminate_timeout = wp->config->request_terminate_timeout;
4199 +               int slowlog_timeout = wp->config->request_slowlog_timeout;
4200 +               struct fpm_child_s *child;
4201 +
4202 +               if (terminate_timeout || slowlog_timeout) {
4203 +                       for (child = wp->children; child; child = child->next) {
4204 +                               fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
4205 +                       }
4206 +               }
4207 +       }
4208 +       
4209 +}
4210 +
4211 +void fpm_pctl_heartbeat(int fd, short which, void *arg)
4212 +{
4213 +       static struct event heartbeat;
4214 +       struct timeval tv = { .tv_sec = 0, .tv_usec = 130000 };
4215 +       struct timeval now;
4216 +
4217 +       if (which == EV_TIMEOUT) {
4218 +               evtimer_del(&heartbeat);
4219 +               fpm_clock_get(&now);
4220 +               fpm_pctl_check_request_timeout(&now);
4221 +       }
4222 +
4223 +       evtimer_set(&heartbeat, &fpm_pctl_heartbeat, 0);
4224 +
4225 +       evtimer_add(&heartbeat, &tv);
4226 +}
4227 +
4228 diff --git a/sapi/cgi/fpm/fpm_process_ctl.h b/sapi/cgi/fpm/fpm_process_ctl.h
4229 new file mode 100644
4230 --- /dev/null
4231 +++ b/sapi/cgi/fpm/fpm_process_ctl.h
4232 @@ -0,0 +1,39 @@
4233 +
4234 +       /* $Id$ */
4235 +       /* (c) 2007,2008 Andrei Nigmatulin */
4236 +
4237 +#ifndef FPM_PROCESS_CTL_H
4238 +#define FPM_PROCESS_CTL_H 1
4239 +
4240 +struct fpm_child_s;
4241 +
4242 +void fpm_pctl(int new_state, int action);
4243 +int fpm_pctl_can_spawn_children();
4244 +int fpm_pctl_kill(pid_t pid, int how);
4245 +void fpm_pctl_heartbeat(int fd, short which, void *arg);
4246 +int fpm_pctl_child_exited();
4247 +int fpm_pctl_init_main();
4248 +
4249 +
4250 +enum {
4251 +       FPM_PCTL_STATE_UNSPECIFIED,
4252 +       FPM_PCTL_STATE_NORMAL,
4253 +       FPM_PCTL_STATE_RELOADING,
4254 +       FPM_PCTL_STATE_TERMINATING,
4255 +       FPM_PCTL_STATE_FINISHING
4256 +};
4257 +
4258 +enum {
4259 +       FPM_PCTL_ACTION_SET,
4260 +       FPM_PCTL_ACTION_TIMEOUT,
4261 +       FPM_PCTL_ACTION_LAST_CHILD_EXITED
4262 +};
4263 +
4264 +enum {
4265 +       FPM_PCTL_TERM,
4266 +       FPM_PCTL_STOP,
4267 +       FPM_PCTL_CONT
4268 +};
4269 +
4270 +#endif
4271 +
4272 diff --git a/sapi/cgi/fpm/fpm_request.c b/sapi/cgi/fpm/fpm_request.c
4273 new file mode 100644
4274 --- /dev/null
4275 +++ b/sapi/cgi/fpm/fpm_request.c
4276 @@ -0,0 +1,164 @@
4277 +
4278 +       /* $Id$ */
4279 +       /* (c) 2007,2008 Andrei Nigmatulin */
4280 +
4281 +#include "fpm_config.h"
4282 +
4283 +#include "fpm_php.h"
4284 +#include "fpm_str.h"
4285 +#include "fpm_clock.h"
4286 +#include "fpm_conf.h"
4287 +#include "fpm_trace.h"
4288 +#include "fpm_php_trace.h"
4289 +#include "fpm_process_ctl.h"
4290 +#include "fpm_children.h"
4291 +#include "fpm_shm_slots.h"
4292 +#include "fpm_request.h"
4293 +
4294 +#include "zlog.h"
4295 +
4296 +void fpm_request_accepting()
4297 +{
4298 +       struct fpm_shm_slot_s *slot;
4299 +
4300 +       slot = fpm_shm_slots_acquire(0, 0);
4301 +
4302 +       slot->request_stage = FPM_REQUEST_ACCEPTING;
4303 +
4304 +       fpm_clock_get(&slot->tv);
4305 +       memset(slot->request_method, 0, sizeof(slot->request_method));
4306 +       slot->content_length = 0;
4307 +       memset(slot->script_filename, 0, sizeof(slot->script_filename));
4308 +
4309 +       fpm_shm_slots_release(slot);
4310 +}
4311 +
4312 +void fpm_request_reading_headers()
4313 +{
4314 +       struct fpm_shm_slot_s *slot;
4315 +
4316 +       slot = fpm_shm_slots_acquire(0, 0);
4317 +
4318 +       slot->request_stage = FPM_REQUEST_READING_HEADERS;
4319 +
4320 +       fpm_clock_get(&slot->tv);
4321 +       slot->accepted = slot->tv;
4322 +
4323 +       fpm_shm_slots_release(slot);
4324 +}
4325 +
4326 +void fpm_request_info()
4327 +{
4328 +       TSRMLS_FETCH();
4329 +       struct fpm_shm_slot_s *slot;
4330 +       char *request_method = fpm_php_request_method(TSRMLS_C);
4331 +       char *script_filename = fpm_php_script_filename(TSRMLS_C);
4332 +
4333 +       slot = fpm_shm_slots_acquire(0, 0);
4334 +
4335 +       slot->request_stage = FPM_REQUEST_INFO;
4336 +
4337 +       fpm_clock_get(&slot->tv);
4338 +
4339 +       if (request_method) {
4340 +               cpystrn(slot->request_method, request_method, sizeof(slot->request_method));
4341 +       }
4342 +
4343 +       slot->content_length = fpm_php_content_length(TSRMLS_C);
4344 +
4345 +       /* if cgi.fix_pathinfo is set to "1" and script cannot be found (404)
4346 +               the sapi_globals.request_info.path_translated is set to NULL */
4347 +       if (script_filename) {
4348 +               cpystrn(slot->script_filename, script_filename, sizeof(slot->script_filename));
4349 +       }
4350 +
4351 +       fpm_shm_slots_release(slot);
4352 +}
4353 +
4354 +void fpm_request_executing()
4355 +{
4356 +       struct fpm_shm_slot_s *slot;
4357 +
4358 +       slot = fpm_shm_slots_acquire(0, 0);
4359 +
4360 +       slot->request_stage = FPM_REQUEST_EXECUTING;
4361 +
4362 +       fpm_clock_get(&slot->tv);
4363 +
4364 +       fpm_shm_slots_release(slot);
4365 +}
4366 +
4367 +void fpm_request_finished()
4368 +{
4369 +       struct fpm_shm_slot_s *slot;
4370 +
4371 +       slot = fpm_shm_slots_acquire(0, 0);
4372 +
4373 +       slot->request_stage = FPM_REQUEST_FINISHED;
4374 +
4375 +       fpm_clock_get(&slot->tv);
4376 +       memset(&slot->accepted, 0, sizeof(slot->accepted));
4377 +
4378 +       fpm_shm_slots_release(slot);
4379 +}
4380 +
4381 +void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *now, int terminate_timeout, int slowlog_timeout)
4382 +{
4383 +       struct fpm_shm_slot_s *slot;
4384 +       struct fpm_shm_slot_s slot_c;
4385 +
4386 +       slot = fpm_shm_slot(child);
4387 +
4388 +       if (!fpm_shm_slots_acquire(slot, 1)) {
4389 +               return;
4390 +       }
4391 +
4392 +       slot_c = *slot;
4393 +
4394 +       fpm_shm_slots_release(slot);
4395 +
4396 +#if HAVE_FPM_TRACE
4397 +       if (child->slow_logged.tv_sec) {
4398 +               if (child->slow_logged.tv_sec != slot_c.accepted.tv_sec || child->slow_logged.tv_usec != slot_c.accepted.tv_usec) {
4399 +                       child->slow_logged.tv_sec = 0;
4400 +                       child->slow_logged.tv_usec = 0;
4401 +               }
4402 +       }
4403 +#endif
4404 +
4405 +       if (slot_c.request_stage > FPM_REQUEST_ACCEPTING && slot_c.request_stage < FPM_REQUEST_FINISHED) {
4406 +               char purified_script_filename[sizeof(slot_c.script_filename)];
4407 +               struct timeval tv;
4408 +
4409 +               timersub(now, &slot_c.accepted, &tv);
4410 +
4411 +#if HAVE_FPM_TRACE
4412 +               if (child->slow_logged.tv_sec == 0 && slowlog_timeout &&
4413 +                               slot_c.request_stage == FPM_REQUEST_EXECUTING && tv.tv_sec >= slowlog_timeout) {
4414 +                       
4415 +                       str_purify_filename(purified_script_filename, slot_c.script_filename, sizeof(slot_c.script_filename));
4416 +
4417 +                       child->slow_logged = slot_c.accepted;
4418 +                       child->tracer = fpm_php_trace;
4419 +
4420 +                       fpm_trace_signal(child->pid);
4421 +
4422 +                       zlog(ZLOG_STUFF, ZLOG_WARNING, "child %d, script '%s' (pool %s) executing too slow (%d.%06d sec), logging",
4423 +                               (int) child->pid, purified_script_filename, child->wp->config->name, (int) tv.tv_sec, (int) tv.tv_usec);
4424 +               }
4425 +
4426 +               else
4427 +#endif
4428 +               if (terminate_timeout && tv.tv_sec >= terminate_timeout) {
4429 +
4430 +                       str_purify_filename(purified_script_filename, slot_c.script_filename, sizeof(slot_c.script_filename));
4431 +
4432 +                       fpm_pctl_kill(child->pid, FPM_PCTL_TERM);
4433 +
4434 +                       zlog(ZLOG_STUFF, ZLOG_WARNING, "child %d, script '%s' (pool %s) execution timed out (%d.%06d sec), terminating",
4435 +                               (int) child->pid, purified_script_filename, child->wp->config->name, (int) tv.tv_sec, (int) tv.tv_usec);
4436 +               }
4437 +       }
4438 +
4439 +}
4440 +
4441 diff --git a/sapi/cgi/fpm/fpm_request.h b/sapi/cgi/fpm/fpm_request.h
4442 new file mode 100644
4443 --- /dev/null
4444 +++ b/sapi/cgi/fpm/fpm_request.h
4445 @@ -0,0 +1,27 @@
4446 +
4447 +       /* $Id$ */
4448 +       /* (c) 2007,2008 Andrei Nigmatulin */
4449 +
4450 +#ifndef FPM_REQUEST_H
4451 +#define FPM_REQUEST_H 1
4452 +
4453 +void fpm_request_accepting();                          /* hanging in accept() */
4454 +void fpm_request_reading_headers();                    /* start reading fastcgi request from very first byte */
4455 +void fpm_request_info();                                       /* not a stage really but a point in the php code, where all request params have become known to sapi */
4456 +void fpm_request_executing();                          /* the script is executing */
4457 +void fpm_request_finished();                           /* request processed: script response have been sent to web server */
4458 +
4459 +struct fpm_child_s;
4460 +struct timeval;
4461 +
4462 +void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *tv, int terminate_timeout, int slowlog_timeout);
4463 +
4464 +enum fpm_request_stage_e {
4465 +       FPM_REQUEST_ACCEPTING = 1,
4466 +       FPM_REQUEST_READING_HEADERS,
4467 +       FPM_REQUEST_INFO,
4468 +       FPM_REQUEST_EXECUTING,
4469 +       FPM_REQUEST_FINISHED
4470 +};
4471 +
4472 +#endif
4473 diff --git a/sapi/cgi/fpm/fpm_shm.c b/sapi/cgi/fpm/fpm_shm.c
4474 new file mode 100644
4475 --- /dev/null
4476 +++ b/sapi/cgi/fpm/fpm_shm.c
4477 @@ -0,0 +1,100 @@
4478 +
4479 +       /* $Id$ */
4480 +       /* (c) 2007,2008 Andrei Nigmatulin */
4481 +
4482 +#include "fpm_config.h"
4483 +
4484 +#include <unistd.h>
4485 +#include <sys/mman.h>
4486 +#include <stdlib.h>
4487 +
4488 +#include "fpm_shm.h"
4489 +#include "zlog.h"
4490 +
4491 +
4492 +/* MAP_ANON is depricated, but not in macosx */
4493 +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
4494 +#define MAP_ANONYMOUS MAP_ANON
4495 +#endif
4496 +
4497 +
4498 +struct fpm_shm_s *fpm_shm_alloc(size_t sz)
4499 +{
4500 +       struct fpm_shm_s *shm;
4501 +
4502 +       shm = malloc(sizeof(*shm));
4503 +
4504 +       if (!shm) {
4505 +               return 0;
4506 +       }
4507 +
4508 +       shm->mem = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
4509 +
4510 +       if (!shm->mem) {
4511 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "mmap(MAP_ANONYMOUS | MAP_SHARED) failed");
4512 +               free(shm);
4513 +               return 0;
4514 +       }
4515 +
4516 +       shm->used = 0;
4517 +       shm->sz = sz;
4518 +
4519 +       return shm;
4520 +}
4521 +
4522 +static void fpm_shm_free(struct fpm_shm_s *shm, int do_unmap)
4523 +{
4524 +       if (do_unmap) {
4525 +               munmap(shm->mem, shm->sz);
4526 +       }
4527 +
4528 +       free(shm);      
4529 +}
4530 +
4531 +void fpm_shm_free_list(struct fpm_shm_s *shm, void *mem)
4532 +{
4533 +       struct fpm_shm_s *next;
4534 +
4535 +       for (; shm; shm = next) {
4536 +               next = shm->next;
4537 +
4538 +               fpm_shm_free(shm, mem != shm->mem);
4539 +       }
4540 +}
4541 +
4542 +void *fpm_shm_alloc_chunk(struct fpm_shm_s **head, size_t sz, void **mem)
4543 +{
4544 +       size_t pagesize = getpagesize();
4545 +       static const size_t cache_line_size = 16;
4546 +       size_t aligned_sz;
4547 +       struct fpm_shm_s *shm;
4548 +       void *ret;
4549 +
4550 +       sz = (sz + cache_line_size - 1) & -cache_line_size;
4551 +
4552 +       shm = *head;
4553 +
4554 +       if (0 == shm || shm->sz - shm->used < sz) {
4555 +               /* allocate one more shm segment */
4556 +
4557 +               aligned_sz = (sz + pagesize - 1) & -pagesize;
4558 +
4559 +               shm = fpm_shm_alloc(aligned_sz);
4560 +
4561 +               if (!shm) {
4562 +                       return 0;
4563 +               }
4564 +
4565 +               shm->next = *head;
4566 +               if (shm->next) shm->next->prev = shm;
4567 +               shm->prev = 0;
4568 +               *head = shm;
4569 +       }
4570 +
4571 +       *mem = shm->mem;
4572 +       ret = (char *) shm->mem + shm->used;
4573 +       shm->used += sz;
4574 +
4575 +       return ret;
4576 +}
4577 +
4578 diff --git a/sapi/cgi/fpm/fpm_shm.h b/sapi/cgi/fpm/fpm_shm.h
4579 new file mode 100644
4580 --- /dev/null
4581 +++ b/sapi/cgi/fpm/fpm_shm.h
4582 @@ -0,0 +1,22 @@
4583 +
4584 +       /* $Id$ */
4585 +       /* (c) 2007,2008 Andrei Nigmatulin */
4586 +
4587 +#ifndef FPM_SHM_H
4588 +#define FPM_SHM_H 1
4589 +
4590 +struct fpm_shm_s;
4591 +
4592 +struct fpm_shm_s {
4593 +       struct fpm_shm_s *prev, *next;
4594 +       void *mem;
4595 +       size_t sz;
4596 +       size_t used;
4597 +};
4598 +
4599 +struct fpm_shm_s *fpm_shm_alloc(size_t sz);
4600 +void fpm_shm_free_list(struct fpm_shm_s *, void *);
4601 +void *fpm_shm_alloc_chunk(struct fpm_shm_s **head, size_t sz, void **mem);
4602 +
4603 +#endif
4604 +
4605 diff --git a/sapi/cgi/fpm/fpm_shm_slots.c b/sapi/cgi/fpm/fpm_shm_slots.c
4606 new file mode 100644
4607 --- /dev/null
4608 +++ b/sapi/cgi/fpm/fpm_shm_slots.c
4609 @@ -0,0 +1,127 @@
4610 +
4611 +       /* $Id$ */
4612 +       /* (c) 2007,2008 Andrei Nigmatulin */
4613 +
4614 +#include "fpm_config.h"
4615 +
4616 +#include "fpm_atomic.h"
4617 +#include "fpm_worker_pool.h"
4618 +#include "fpm_children.h"
4619 +#include "fpm_shm.h"
4620 +#include "fpm_shm_slots.h"
4621 +#include "zlog.h"
4622 +
4623 +static void *shm_mem;
4624 +static struct fpm_shm_slot_s *shm_slot;
4625 +
4626 +int fpm_shm_slots_prepare_slot(struct fpm_child_s *child)
4627 +{
4628 +       struct fpm_worker_pool_s *wp = child->wp;
4629 +       struct fpm_shm_slot_ptr_s *shm_slot_ptr;
4630 +
4631 +       child->shm_slot_i = wp->slots_used.used;
4632 +
4633 +       shm_slot_ptr = fpm_array_push(&wp->slots_used);
4634 +
4635 +       if (0 == shm_slot_ptr) {
4636 +               return -1;
4637 +       }
4638 +
4639 +       if (0 == wp->slots_free.used) {
4640 +               shm_slot_ptr->shm_slot = fpm_shm_alloc_chunk(&wp->shm_list, sizeof(struct fpm_shm_slot_s), &shm_slot_ptr->mem);
4641 +
4642 +               if (!shm_slot_ptr->shm_slot) {
4643 +                       return -1;
4644 +               }
4645 +       }
4646 +       else {
4647 +               *shm_slot_ptr = *(struct fpm_shm_slot_ptr_s *) fpm_array_item_last(&wp->slots_free);
4648 +
4649 +               --wp->slots_free.used;
4650 +       }
4651 +
4652 +       memset(shm_slot_ptr->shm_slot, 0, sizeof(struct fpm_shm_slot_s));
4653 +
4654 +       shm_slot_ptr->child = child;
4655 +
4656 +       return 0;
4657 +}
4658 +
4659 +void fpm_shm_slots_discard_slot(struct fpm_child_s *child)
4660 +{
4661 +       struct fpm_shm_slot_ptr_s *shm_slot_ptr;
4662 +       struct fpm_worker_pool_s *wp = child->wp;
4663 +       int n;
4664 +
4665 +       shm_slot_ptr = fpm_array_push(&wp->slots_free);
4666 +
4667 +       if (shm_slot_ptr) {
4668 +
4669 +               struct fpm_shm_slot_ptr_s *shm_slot_ptr_used;
4670 +
4671 +               shm_slot_ptr_used = fpm_array_item(&wp->slots_used, child->shm_slot_i);
4672 +
4673 +               *shm_slot_ptr = *shm_slot_ptr_used;
4674 +
4675 +               shm_slot_ptr->child = 0;
4676 +
4677 +       }
4678 +
4679 +       n = fpm_array_item_remove(&wp->slots_used, child->shm_slot_i);
4680 +
4681 +       if (n > -1) {
4682 +               shm_slot_ptr = fpm_array_item(&wp->slots_used, n);
4683 +
4684 +               shm_slot_ptr->child->shm_slot_i = n;
4685 +       }
4686 +}
4687 +
4688 +void fpm_shm_slots_child_use_slot(struct fpm_child_s *child)
4689 +{
4690 +       struct fpm_shm_slot_ptr_s *shm_slot_ptr;
4691 +       struct fpm_worker_pool_s *wp = child->wp;
4692 +
4693 +       shm_slot_ptr = fpm_array_item(&wp->slots_used, child->shm_slot_i);
4694 +
4695 +       shm_slot = shm_slot_ptr->shm_slot;
4696 +       shm_mem = shm_slot_ptr->mem;
4697 +}
4698 +
4699 +void fpm_shm_slots_parent_use_slot(struct fpm_child_s *child)
4700 +{
4701 +       /* nothing to do */
4702 +}
4703 +
4704 +void *fpm_shm_slots_mem()
4705 +{
4706 +       return shm_mem;
4707 +}
4708 +
4709 +struct fpm_shm_slot_s *fpm_shm_slot(struct fpm_child_s *child)
4710 +{
4711 +       struct fpm_shm_slot_ptr_s *shm_slot_ptr;
4712 +       struct fpm_worker_pool_s *wp = child->wp;
4713 +
4714 +       shm_slot_ptr = fpm_array_item(&wp->slots_used, child->shm_slot_i);
4715 +
4716 +       return shm_slot_ptr->shm_slot;
4717 +}
4718 +
4719 +struct fpm_shm_slot_s *fpm_shm_slots_acquire(struct fpm_shm_slot_s *s, int nohang)
4720 +{
4721 +       if (s == 0) {
4722 +               s = shm_slot;
4723 +       }
4724 +
4725 +       if (0 > fpm_spinlock(&s->lock, nohang)) {
4726 +               return 0;
4727 +       }
4728 +
4729 +       return s;
4730 +}
4731 +
4732 +void fpm_shm_slots_release(struct fpm_shm_slot_s *s)
4733 +{
4734 +       s->lock = 0;
4735 +}
4736 +
4737 diff --git a/sapi/cgi/fpm/fpm_shm_slots.h b/sapi/cgi/fpm/fpm_shm_slots.h
4738 new file mode 100644
4739 --- /dev/null
4740 +++ b/sapi/cgi/fpm/fpm_shm_slots.h
4741 @@ -0,0 +1,43 @@
4742 +
4743 +       /* $Id$ */
4744 +       /* (c) 2007,2008 Andrei Nigmatulin */
4745 +
4746 +#ifndef FPM_SHM_SLOTS_H
4747 +#define FPM_SHM_SLOTS_H 1
4748 +
4749 +#include "fpm_atomic.h"
4750 +#include "fpm_worker_pool.h"
4751 +#include "fpm_request.h"
4752 +
4753 +struct fpm_child_s;
4754 +
4755 +struct fpm_shm_slot_s {
4756 +       union {
4757 +               atomic_t lock;
4758 +               char dummy[16];
4759 +       };
4760 +       enum fpm_request_stage_e request_stage;
4761 +       struct timeval accepted;
4762 +       struct timeval tv;
4763 +       char request_method[16];
4764 +       size_t content_length; /* used with POST only */
4765 +       char script_filename[256];
4766 +};
4767 +
4768 +struct fpm_shm_slot_ptr_s {
4769 +       void *mem;
4770 +       struct fpm_shm_slot_s *shm_slot;
4771 +       struct fpm_child_s *child;
4772 +};
4773 +
4774 +int fpm_shm_slots_prepare_slot(struct fpm_child_s *child);
4775 +void fpm_shm_slots_discard_slot(struct fpm_child_s *child);
4776 +void fpm_shm_slots_child_use_slot(struct fpm_child_s *child);
4777 +void fpm_shm_slots_parent_use_slot(struct fpm_child_s *child);
4778 +void *fpm_shm_slots_mem();
4779 +struct fpm_shm_slot_s *fpm_shm_slot(struct fpm_child_s *child);
4780 +struct fpm_shm_slot_s *fpm_shm_slots_acquire(struct fpm_shm_slot_s *, int nohang);
4781 +void fpm_shm_slots_release(struct fpm_shm_slot_s *);
4782 +
4783 +#endif
4784 +
4785 diff --git a/sapi/cgi/fpm/fpm_signals.c b/sapi/cgi/fpm/fpm_signals.c
4786 new file mode 100644
4787 --- /dev/null
4788 +++ b/sapi/cgi/fpm/fpm_signals.c
4789 @@ -0,0 +1,252 @@
4790 +
4791 +       /* $Id$ */
4792 +       /* (c) 2007,2008 Andrei Nigmatulin */
4793 +
4794 +#include "fpm_config.h"
4795 +
4796 +#include <signal.h>
4797 +#include <stdio.h>
4798 +#include <sys/types.h>
4799 +#include <sys/socket.h>
4800 +#include <stdlib.h>
4801 +#include <string.h>
4802 +#include <fcntl.h>
4803 +#include <unistd.h>
4804 +#include <errno.h>
4805 +
4806 +#include "fpm.h"
4807 +#include "fpm_signals.h"
4808 +#include "fpm_sockets.h"
4809 +#include "fpm_php.h"
4810 +#include "zlog.h"
4811 +
4812 +static int sp[2];
4813 +
4814 +const char *fpm_signal_names[NSIG + 1] = {
4815 +#ifdef SIGHUP
4816 +       [SIGHUP]                = "SIGHUP",
4817 +#endif
4818 +#ifdef SIGINT
4819 +       [SIGINT]                = "SIGINT",
4820 +#endif
4821 +#ifdef SIGQUIT
4822 +       [SIGQUIT]               = "SIGQUIT",
4823 +#endif
4824 +#ifdef SIGILL
4825 +       [SIGILL]                = "SIGILL",
4826 +#endif
4827 +#ifdef SIGTRAP
4828 +       [SIGTRAP]               = "SIGTRAP",
4829 +#endif
4830 +#ifdef SIGABRT
4831 +       [SIGABRT]               = "SIGABRT",
4832 +#endif
4833 +#ifdef SIGEMT
4834 +       [SIGEMT]                = "SIGEMT",
4835 +#endif
4836 +#ifdef SIGBUS
4837 +       [SIGBUS]                = "SIGBUS",
4838 +#endif
4839 +#ifdef SIGFPE
4840 +       [SIGFPE]                = "SIGFPE",
4841 +#endif
4842 +#ifdef SIGKILL
4843 +       [SIGKILL]               = "SIGKILL",
4844 +#endif
4845 +#ifdef SIGUSR1
4846 +       [SIGUSR1]               = "SIGUSR1",
4847 +#endif
4848 +#ifdef SIGSEGV
4849 +       [SIGSEGV]               = "SIGSEGV",
4850 +#endif
4851 +#ifdef SIGUSR2
4852 +       [SIGUSR2]               = "SIGUSR2",
4853 +#endif
4854 +#ifdef SIGPIPE
4855 +       [SIGPIPE]               = "SIGPIPE",
4856 +#endif
4857 +#ifdef SIGALRM
4858 +       [SIGALRM]               = "SIGALRM",
4859 +#endif
4860 +#ifdef SIGTERM
4861 +       [SIGTERM]               = "SIGTERM",
4862 +#endif
4863 +#ifdef SIGCHLD
4864 +       [SIGCHLD]               = "SIGCHLD",
4865 +#endif
4866 +#ifdef SIGCONT
4867 +       [SIGCONT]               = "SIGCONT",
4868 +#endif
4869 +#ifdef SIGSTOP
4870 +       [SIGSTOP]               = "SIGSTOP",
4871 +#endif
4872 +#ifdef SIGTSTP
4873 +       [SIGTSTP]               = "SIGTSTP",
4874 +#endif
4875 +#ifdef SIGTTIN
4876 +       [SIGTTIN]               = "SIGTTIN",
4877 +#endif
4878 +#ifdef SIGTTOU
4879 +       [SIGTTOU]               = "SIGTTOU",
4880 +#endif
4881 +#ifdef SIGURG
4882 +       [SIGURG]                = "SIGURG",
4883 +#endif
4884 +#ifdef SIGXCPU
4885 +       [SIGXCPU]               = "SIGXCPU",
4886 +#endif
4887 +#ifdef SIGXFSZ
4888 +       [SIGXFSZ]               = "SIGXFSZ",
4889 +#endif
4890 +#ifdef SIGVTALRM
4891 +       [SIGVTALRM]     = "SIGVTALRM",
4892 +#endif
4893 +#ifdef SIGPROF
4894 +       [SIGPROF]               = "SIGPROF",
4895 +#endif
4896 +#ifdef SIGWINCH
4897 +       [SIGWINCH]              = "SIGWINCH",
4898 +#endif
4899 +#ifdef SIGINFO
4900 +       [SIGINFO]               = "SIGINFO",
4901 +#endif
4902 +#ifdef SIGIO
4903 +       [SIGIO]                 = "SIGIO",
4904 +#endif
4905 +#ifdef SIGPWR
4906 +       [SIGPWR]                = "SIGPWR",
4907 +#endif
4908 +#ifdef SIGSYS
4909 +       [SIGSYS]                = "SIGSYS",
4910 +#endif
4911 +#ifdef SIGWAITING
4912 +       [SIGWAITING]    = "SIGWAITING",
4913 +#endif
4914 +#ifdef SIGLWP
4915 +       [SIGLWP]                = "SIGLWP",
4916 +#endif
4917 +#ifdef SIGFREEZE
4918 +       [SIGFREEZE]     = "SIGFREEZE",
4919 +#endif
4920 +#ifdef SIGTHAW
4921 +       [SIGTHAW]               = "SIGTHAW",
4922 +#endif
4923 +#ifdef SIGCANCEL
4924 +       [SIGCANCEL]     = "SIGCANCEL",
4925 +#endif
4926 +#ifdef SIGLOST
4927 +       [SIGLOST]               = "SIGLOST",
4928 +#endif
4929 +};
4930 +
4931 +static void sig_soft_quit(int signo)
4932 +{
4933 +       int saved_errno = errno;
4934 +
4935 +       /* closing fastcgi listening socket will force fcgi_accept() exit immediately */
4936 +       close(0);
4937 +       socket(AF_UNIX, SOCK_STREAM, 0);
4938 +
4939 +       fpm_php_soft_quit();
4940 +
4941 +       errno = saved_errno;
4942 +}
4943 +
4944 +static void sig_handler(int signo)
4945 +{
4946 +       static const char sig_chars[NSIG + 1] = {
4947 +               [SIGTERM] = 'T',
4948 +               [SIGINT]  = 'I',
4949 +               [SIGUSR1] = '1',
4950 +               [SIGUSR2] = '2',
4951 +               [SIGQUIT] = 'Q',
4952 +               [SIGCHLD] = 'C'
4953 +       };
4954 +       char s;
4955 +       int saved_errno;
4956 +
4957 +       if (fpm_globals.parent_pid != getpid()) {
4958 +               /* prevent a signal race condition when child process
4959 +                       have not set up it's own signal handler yet */
4960 +               return;
4961 +       }
4962 +
4963 +       saved_errno = errno;
4964 +
4965 +       s = sig_chars[signo];
4966 +
4967 +       write(sp[1], &s, sizeof(s));
4968 +
4969 +       errno = saved_errno;
4970 +}
4971 +
4972 +int fpm_signals_init_main()
4973 +{
4974 +       struct sigaction act;
4975 +
4976 +       if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
4977 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "socketpair() failed");
4978 +               return -1;
4979 +       }
4980 +
4981 +       if (0 > fd_set_blocked(sp[0], 0) || 0 > fd_set_blocked(sp[1], 0)) {
4982 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fd_set_blocked() failed");
4983 +               return -1;
4984 +       }
4985 +
4986 +       if (0 > fcntl(sp[0], F_SETFD, FD_CLOEXEC) || 0 > fcntl(sp[1], F_SETFD, FD_CLOEXEC)) {
4987 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fcntl(F_SETFD, FD_CLOEXEC) failed");
4988 +               return -1;
4989 +       }
4990 +
4991 +       memset(&act, 0, sizeof(act));
4992 +       act.sa_handler = sig_handler;
4993 +       sigfillset(&act.sa_mask);
4994 +
4995 +       if (0 > sigaction(SIGTERM,  &act, 0) ||
4996 +               0 > sigaction(SIGINT,   &act, 0) ||
4997 +               0 > sigaction(SIGUSR1,  &act, 0) ||
4998 +               0 > sigaction(SIGUSR2,  &act, 0) ||
4999 +               0 > sigaction(SIGCHLD,  &act, 0) ||
5000 +               0 > sigaction(SIGQUIT,  &act, 0)) {
5001 +
5002 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "sigaction() failed");
5003 +               return -1;
5004 +       }
5005 +
5006 +       return 0;
5007 +}
5008 +
5009 +int fpm_signals_init_child()
5010 +{
5011 +       struct sigaction act, act_dfl;
5012 +
5013 +       memset(&act, 0, sizeof(act));
5014 +       memset(&act_dfl, 0, sizeof(act_dfl));
5015 +
5016 +       act.sa_handler = &sig_soft_quit;
5017 +       act.sa_flags |= SA_RESTART;
5018 +
5019 +       act_dfl.sa_handler = SIG_DFL;
5020 +
5021 +       close(sp[0]);
5022 +       close(sp[1]);
5023 +
5024 +       if (0 > sigaction(SIGTERM,  &act_dfl,  0) ||
5025 +               0 > sigaction(SIGINT,   &act_dfl,  0) ||
5026 +               0 > sigaction(SIGUSR1,  &act_dfl,  0) ||
5027 +               0 > sigaction(SIGUSR2,  &act_dfl,  0) ||
5028 +               0 > sigaction(SIGCHLD,  &act_dfl,  0) ||
5029 +               0 > sigaction(SIGQUIT,  &act,      0)) {
5030 +
5031 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "sigaction() failed");
5032 +               return -1;
5033 +       }
5034 +
5035 +       return 0;
5036 +}
5037 +
5038 +int fpm_signals_get_fd()
5039 +{
5040 +       return sp[0];
5041 +}
5042 diff --git a/sapi/cgi/fpm/fpm_signals.h b/sapi/cgi/fpm/fpm_signals.h
5043 new file mode 100644
5044 --- /dev/null
5045 +++ b/sapi/cgi/fpm/fpm_signals.h
5046 @@ -0,0 +1,16 @@
5047 +
5048 +       /* $Id$ */
5049 +       /* (c) 2007,2008 Andrei Nigmatulin */
5050 +
5051 +#ifndef FPM_SIGNALS_H
5052 +#define FPM_SIGNALS_H 1
5053 +
5054 +#include <signal.h>
5055 +
5056 +int fpm_signals_init_main();
5057 +int fpm_signals_init_child();
5058 +int fpm_signals_get_fd();
5059 +
5060 +extern const char *fpm_signal_names[NSIG + 1];
5061 +
5062 +#endif
5063 diff --git a/sapi/cgi/fpm/fpm_sockets.c b/sapi/cgi/fpm/fpm_sockets.c
5064 new file mode 100644
5065 --- /dev/null
5066 +++ b/sapi/cgi/fpm/fpm_sockets.c
5067 @@ -0,0 +1,427 @@
5068 +
5069 +       /* $Id$ */
5070 +       /* (c) 2007,2008 Andrei Nigmatulin */
5071 +
5072 +#include "fpm_config.h"
5073 +
5074 +#ifdef HAVE_ALLOCA_H
5075 +#include <alloca.h>
5076 +#endif
5077 +#include <sys/types.h>
5078 +#include <sys/stat.h> /* for chmod(2) */
5079 +#include <sys/socket.h>
5080 +#include <netinet/in.h>
5081 +#include <arpa/inet.h>
5082 +#include <sys/un.h>
5083 +#include <netdb.h>
5084 +#include <stdio.h>
5085 +#include <stdlib.h>
5086 +#include <string.h>
5087 +#include <errno.h>
5088 +#include <unistd.h>
5089 +
5090 +#include "zlog.h"
5091 +#include "fpm_arrays.h"
5092 +#include "fpm_sockets.h"
5093 +#include "fpm_worker_pool.h"
5094 +#include "fpm_unix.h"
5095 +#include "fpm_str.h"
5096 +#include "fpm_env.h"
5097 +#include "fpm_cleanup.h"
5098 +
5099 +struct listening_socket_s {
5100 +       int refcount;
5101 +       int sock;
5102 +       int type;
5103 +       char *key;
5104 +};
5105 +
5106 +static struct fpm_array_s sockets_list;
5107 +
5108 +static int fpm_sockets_resolve_af_inet(char *node, char *service, struct sockaddr_in *addr)
5109 +{
5110 +       struct addrinfo *res;
5111 +       struct addrinfo hints;
5112 +       int ret;
5113 +
5114 +       memset(&hints, 0, sizeof(hints));
5115 +
5116 +       hints.ai_family = AF_INET;
5117 +
5118 +       ret = getaddrinfo(node, service, &hints, &res);
5119 +
5120 +       if (ret != 0) {
5121 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "can't resolve hostname '%s%s%s': getaddrinfo said: %s%s%s\n",
5122 +                                       node, service ? ":" : "", service ? service : "",
5123 +                                       gai_strerror(ret), ret == EAI_SYSTEM ? ", system error: " : "", ret == EAI_SYSTEM ? strerror(errno) : "");
5124 +               return -1;
5125 +       }
5126 +
5127 +       *addr = *(struct sockaddr_in *) res->ai_addr;
5128 +
5129 +       freeaddrinfo(res);
5130 +
5131 +       return 0;
5132 +}
5133 +
5134 +enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
5135 +
5136 +static void fpm_sockets_cleanup(int which, void *arg)
5137 +{
5138 +       int i;
5139 +       char *env_value = 0;
5140 +       int p = 0;
5141 +       struct listening_socket_s *ls = sockets_list.data;
5142 +
5143 +       for (i = 0; i < sockets_list.used; i++, ls++) {
5144 +
5145 +               if (which != FPM_CLEANUP_PARENT_EXEC) {
5146 +
5147 +                       close(ls->sock);
5148 +
5149 +               }
5150 +               else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
5151 +                       char fd[32];
5152 +                       sprintf(fd, "%d", ls->sock);
5153 +                       env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + 1);
5154 +                       p += sprintf(env_value + p, "%s%s=%s", p ? "," : "", ls->key, fd);
5155 +               }
5156 +
5157 +               if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
5158 +
5159 +                       if (ls->type == FPM_AF_UNIX) {
5160 +                               unlink(ls->key);
5161 +                       }
5162 +
5163 +               }
5164 +
5165 +               free(ls->key);
5166 +       }
5167 +
5168 +       if (env_value) {
5169 +               setenv("FPM_SOCKETS", env_value, 1);
5170 +               free(env_value);
5171 +       }
5172 +
5173 +       fpm_array_free(&sockets_list);
5174 +}
5175 +
5176 +static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op)
5177 +{
5178 +
5179 +       if (key == NULL) {
5180 +
5181 +               switch (type) {
5182 +
5183 +                       case FPM_AF_INET : {
5184 +                               struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
5185 +
5186 +                               key = alloca(sizeof("xxx.xxx.xxx.xxx:ppppp"));
5187 +
5188 +                               sprintf(key, "%u.%u.%u.%u:%u", IPQUAD(&sa_in->sin_addr), (unsigned int) ntohs(sa_in->sin_port));
5189 +
5190 +                               break;
5191 +                       }
5192 +
5193 +                       case FPM_AF_UNIX : {
5194 +                               struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
5195 +
5196 +                               key = alloca(strlen(sa_un->sun_path) + 1);
5197 +
5198 +                               strcpy(key, sa_un->sun_path);
5199 +
5200 +                               break;
5201 +                       }
5202 +
5203 +                       default :
5204 +
5205 +                               return -1;
5206 +               }
5207 +
5208 +       }
5209 +
5210 +       switch (op) {
5211 +
5212 +               case FPM_GET_USE_SOCKET :
5213 +               {
5214 +
5215 +                       int i;
5216 +                       struct listening_socket_s *ls = sockets_list.data;
5217 +
5218 +                       for (i = 0; i < sockets_list.used; i++, ls++) {
5219 +
5220 +                               if (!strcmp(ls->key, key)) {
5221 +                                       ++ls->refcount;
5222 +                                       return ls->sock;
5223 +                               }
5224 +                       }
5225 +
5226 +                       break;
5227 +               }
5228 +
5229 +               case FPM_STORE_SOCKET :                 /* inherited socket */
5230 +               case FPM_STORE_USE_SOCKET :             /* just created */
5231 +               {
5232 +
5233 +                       struct listening_socket_s *ls;
5234 +
5235 +                       ls = fpm_array_push(&sockets_list);
5236 +
5237 +                       if (!ls) {
5238 +                               break;
5239 +                       }
5240 +
5241 +                       if (op == FPM_STORE_SOCKET) {
5242 +                               ls->refcount = 0;
5243 +                       }
5244 +                       else {
5245 +                               ls->refcount = 1;
5246 +                       }
5247 +                       ls->type = type;
5248 +                       ls->sock = sock;
5249 +                       ls->key = strdup(key);
5250 +
5251 +                       return 0;
5252 +
5253 +               }
5254 +       }
5255 +
5256 +       return -1;
5257 +
5258 +}
5259 +
5260 +static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen)
5261 +{
5262 +       int backlog = -1;
5263 +       int flags = 1;
5264 +       int sock;
5265 +       mode_t saved_umask;
5266 +
5267 +       /* we have custom backlog value */
5268 +       if (wp->config->listen_options) {
5269 +               backlog = wp->config->listen_options->backlog;
5270 +       }
5271 +
5272 +       sock = socket(sa->sa_family, SOCK_STREAM, 0);
5273 +
5274 +       if (0 > sock) {
5275 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "socket() failed");
5276 +               return -1;
5277 +       }
5278 +
5279 +       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
5280 +
5281 +       if (wp->listen_address_domain == FPM_AF_UNIX) {
5282 +               unlink( ((struct sockaddr_un *) sa)->sun_path);
5283 +       }
5284 +
5285 +       saved_umask = umask(0777 ^ wp->socket_mode);
5286 +
5287 +       if (0 > bind(sock, sa, socklen)) {
5288 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "bind() for address '%s' failed", wp->config->listen_address);
5289 +               return -1;
5290 +       }
5291 +
5292 +       if (wp->listen_address_domain == FPM_AF_UNIX) {
5293 +
5294 +               char *path = ((struct sockaddr_un *) sa)->sun_path;
5295 +
5296 +               if (wp->socket_uid != -1 || wp->socket_gid != -1) {
5297 +
5298 +                       if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
5299 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "chown() for address '%s' failed", wp->config->listen_address);
5300 +                               return -1;
5301 +                       }
5302 +
5303 +               }
5304 +
5305 +       }
5306 +
5307 +       umask(saved_umask);
5308 +
5309 +       if (0 > listen(sock, backlog)) {
5310 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "listen() for address '%s' failed", wp->config->listen_address);
5311 +               return -1;
5312 +       }
5313 +
5314 +       return sock;
5315 +}
5316 +
5317 +static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen)
5318 +{
5319 +       int sock;
5320 +
5321 +       sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
5322 +
5323 +       if (sock >= 0) return sock;
5324 +
5325 +       sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
5326 +
5327 +       fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
5328 +
5329 +       return sock;
5330 +}
5331 +
5332 +enum fpm_address_domain fpm_sockets_domain_from_address(char *address)
5333 +{
5334 +       if (strchr(address, ':')) return FPM_AF_INET;
5335 +
5336 +       if (strlen(address) == strspn(address, "0123456789")) return FPM_AF_INET;
5337 +
5338 +       return FPM_AF_UNIX;
5339 +}
5340 +
5341 +static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp)
5342 +{
5343 +       struct sockaddr_in sa_in;
5344 +       char *dup_address = strdup(wp->config->listen_address);
5345 +       char *port_str = strchr(dup_address, ':');
5346 +       char *addr = NULL;
5347 +       int port = 0;
5348 +
5349 +       if (port_str) { /* this is host:port pair */
5350 +               *port_str++ = '\0';
5351 +               port = atoi(port_str);
5352 +               addr = dup_address;
5353 +       }
5354 +       else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
5355 +               port = atoi(dup_address);
5356 +               port_str = dup_address;
5357 +       }
5358 +
5359 +       if (port == 0) {
5360 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "invalid port value '%s'", port_str);
5361 +               return -1;
5362 +       }
5363 +
5364 +       memset(&sa_in, 0, sizeof(sa_in));
5365 +
5366 +       if (addr) {
5367 +
5368 +               sa_in.sin_addr.s_addr = inet_addr(addr);
5369 +
5370 +               if (sa_in.sin_addr.s_addr == INADDR_NONE) { /* do resolve */
5371 +                       if (0 > fpm_sockets_resolve_af_inet(addr, NULL, &sa_in)) {
5372 +                               return -1;
5373 +                       }
5374 +                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "address '%s' resolved as %u.%u.%u.%u", addr, IPQUAD(&sa_in.sin_addr));
5375 +               }
5376 +       }
5377 +       else {
5378 +
5379 +               sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
5380 +
5381 +       }
5382 +
5383 +       sa_in.sin_family = AF_INET;
5384 +       sa_in.sin_port = htons(port);
5385 +
5386 +       free(dup_address);
5387 +
5388 +       return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
5389 +}
5390 +
5391 +static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp)
5392 +{
5393 +       struct sockaddr_un sa_un;
5394 +
5395 +       memset(&sa_un, 0, sizeof(sa_un));
5396 +
5397 +       cpystrn(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
5398 +       sa_un.sun_family = AF_UNIX;
5399 +
5400 +       return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
5401 +}
5402 +
5403 +int fpm_sockets_init_main()
5404 +{
5405 +       int i;
5406 +       struct fpm_worker_pool_s *wp;
5407 +       char *inherited = getenv("FPM_SOCKETS");
5408 +       struct listening_socket_s *ls;
5409 +
5410 +       if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
5411 +               return -1;
5412 +       }
5413 +
5414 +       /* import inherited sockets */
5415 +       while (inherited && *inherited) {
5416 +               char *comma = strchr(inherited, ',');
5417 +               int type, fd_no;
5418 +               char *eq;
5419 +
5420 +               if (comma) *comma = '\0';
5421 +
5422 +               eq = strchr(inherited, '=');
5423 +
5424 +               if (eq) {
5425 +                       *eq = '\0';
5426 +
5427 +                       fd_no = atoi(eq + 1);
5428 +
5429 +                       type = fpm_sockets_domain_from_address(inherited);
5430 +
5431 +                       zlog(ZLOG_STUFF, ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
5432 +
5433 +                       fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
5434 +               }
5435 +
5436 +               if (comma) inherited = comma + 1;
5437 +               else inherited = 0;
5438 +       }
5439 +
5440 +       /* create all required sockets */
5441 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
5442 +
5443 +               if (!wp->is_template) {
5444 +
5445 +                       switch (wp->listen_address_domain) {
5446 +
5447 +                               case FPM_AF_INET :
5448 +
5449 +                                       wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
5450 +                                       break;
5451 +
5452 +                               case FPM_AF_UNIX :
5453 +
5454 +                                       if (0 > fpm_unix_resolve_socket_premissions(wp)) {
5455 +                                               return -1;
5456 +                                       }
5457 +
5458 +                                       wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
5459 +                                       break;
5460 +
5461 +                       }
5462 +
5463 +                       if (wp->listening_socket == -1) {
5464 +                               return -1;
5465 +                       }
5466 +               }
5467 +
5468 +       }
5469 +
5470 +       /* close unused sockets that was inherited */
5471 +       ls = sockets_list.data;
5472 +
5473 +       for (i = 0; i < sockets_list.used; ) {
5474 +
5475 +               if (ls->refcount == 0) {
5476 +                       close(ls->sock);
5477 +                       if (ls->type == FPM_AF_UNIX) {
5478 +                               unlink(ls->key);
5479 +                       }
5480 +                       free(ls->key);
5481 +                       fpm_array_item_remove(&sockets_list, i);
5482 +               }
5483 +               else {
5484 +                       ++i;
5485 +                       ++ls;
5486 +               }
5487 +       }
5488 +
5489 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
5490 +               return -1;
5491 +       }
5492 +
5493 +       return 0;
5494 +}
5495 diff --git a/sapi/cgi/fpm/fpm_sockets.h b/sapi/cgi/fpm/fpm_sockets.h
5496 new file mode 100644
5497 --- /dev/null
5498 +++ b/sapi/cgi/fpm/fpm_sockets.h
5499 @@ -0,0 +1,37 @@
5500 +
5501 +       /* $Id$ */
5502 +       /* (c) 2007,2008 Andrei Nigmatulin */
5503 +
5504 +#ifndef FPM_MISC_H
5505 +#define FPM_MISC_H 1
5506 +
5507 +#include <unistd.h>
5508 +#include <fcntl.h>
5509 +
5510 +#include "fpm_worker_pool.h"
5511 +
5512 +enum fpm_address_domain fpm_sockets_domain_from_address(char *addr);
5513 +int fpm_sockets_init_main();
5514 +
5515 +
5516 +static inline int fd_set_blocked(int fd, int blocked)
5517 +{
5518 +       int flags = fcntl(fd, F_GETFL);
5519 +
5520 +       if (flags < 0) return -1;
5521 +
5522 +       if (blocked)
5523 +               flags &= ~O_NONBLOCK;
5524 +       else
5525 +               flags |= O_NONBLOCK;
5526 +
5527 +       return fcntl(fd, F_SETFL, flags);
5528 +}
5529 +
5530 +#define IPQUAD(sin_addr) \
5531 +                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[0], \
5532 +                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[1], \
5533 +                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[2], \
5534 +                       (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[3]
5535 +
5536 +#endif
5537 diff --git a/sapi/cgi/fpm/fpm_stdio.c b/sapi/cgi/fpm/fpm_stdio.c
5538 new file mode 100644
5539 --- /dev/null
5540 +++ b/sapi/cgi/fpm/fpm_stdio.c
5541 @@ -0,0 +1,286 @@
5542 +
5543 +       /* $Id$ */
5544 +       /* (c) 2007,2008 Andrei Nigmatulin */
5545 +
5546 +#include "fpm_config.h"
5547 +
5548 +#include <sys/types.h>
5549 +#include <sys/stat.h>
5550 +#include <string.h>
5551 +#include <fcntl.h>
5552 +#include <unistd.h>
5553 +#include <errno.h>
5554 +
5555 +#include "fpm.h"
5556 +#include "fpm_children.h"
5557 +#include "fpm_events.h"
5558 +#include "fpm_sockets.h"
5559 +#include "fpm_stdio.h"
5560 +#include "zlog.h"
5561 +
5562 +static int fd_stdout[2];
5563 +static int fd_stderr[2];
5564 +
5565 +int fpm_stdio_init_main()
5566 +{
5567 +       int fd = open("/dev/null", O_RDWR);
5568 +
5569 +       if (0 > fd) {
5570 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "open(\"/dev/null\") failed");
5571 +               return -1;
5572 +       }
5573 +
5574 +       if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) {
5575 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "dup2() failed");
5576 +               return -1;
5577 +       }
5578 +
5579 +       close(fd);
5580 +
5581 +       return 0;
5582 +}
5583 +
5584 +int fpm_stdio_init_final()
5585 +{
5586 +       if (fpm_global_config.daemonize) {
5587 +
5588 +               if (fpm_globals.error_log_fd != STDERR_FILENO) {
5589 +                       /* there might be messages to stderr from libevent, we need to log them all */
5590 +                       if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
5591 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "dup2() failed");
5592 +                               return -1;
5593 +                       }
5594 +               }
5595 +
5596 +               zlog_set_level(fpm_globals.log_level);
5597 +
5598 +               zlog_set_fd(fpm_globals.error_log_fd);
5599 +       }
5600 +
5601 +       return 0;
5602 +}
5603 +
5604 +int fpm_stdio_init_child(struct fpm_worker_pool_s *wp)
5605 +{
5606 +       close(fpm_globals.error_log_fd);
5607 +       fpm_globals.error_log_fd = -1;
5608 +       zlog_set_fd(-1);
5609 +
5610 +       if (wp->listening_socket != STDIN_FILENO) {
5611 +               if (0 > dup2(wp->listening_socket, STDIN_FILENO)) {
5612 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "dup2() failed");
5613 +                       return -1;
5614 +               }
5615 +       }
5616 +
5617 +       return 0;
5618 +}
5619 +
5620 +static void fpm_stdio_child_said(int fd, short which, void *arg)
5621 +{
5622 +       static const int max_buf_size = 1024;
5623 +       char buf[max_buf_size];
5624 +       struct fpm_child_s *child = arg;
5625 +       int is_stdout = fd == child->fd_stdout;
5626 +       struct event *ev = is_stdout ? &child->ev_stdout : &child->ev_stderr;
5627 +       int fifo_in = 1, fifo_out = 1;
5628 +       int is_last_message = 0;
5629 +       int in_buf = 0;
5630 +       int res;
5631 +
5632 +#if 0
5633 +       zlog(ZLOG_STUFF, ZLOG_DEBUG, "child %d said %s", (int) child->pid, is_stdout ? "stdout" : "stderr");
5634 +#endif
5635 +
5636 +       while (fifo_in || fifo_out) {
5637 +
5638 +               if (fifo_in) {
5639 +
5640 +                       res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
5641 +
5642 +                       if (res <= 0) { /* no data */
5643 +                               fifo_in = 0;
5644 +
5645 +                               if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
5646 +                                       /* just no more data ready */
5647 +                               }
5648 +                               else { /* error or pipe is closed */
5649 +
5650 +                                       if (res < 0) { /* error */
5651 +                                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "read() failed");
5652 +                                       }
5653 +
5654 +                                       fpm_event_del(ev);
5655 +                                       is_last_message = 1;
5656 +
5657 +                                       if (is_stdout) {
5658 +                                               close(child->fd_stdout);
5659 +                                               child->fd_stdout = -1;
5660 +                                       }
5661 +                                       else {
5662 +                                               close(child->fd_stderr);
5663 +                                               child->fd_stderr = -1;
5664 +                                       }
5665 +
5666 +#if 0
5667 +                                       if (in_buf == 0 && !fpm_globals.is_child) {
5668 +                                               zlog(ZLOG_STUFF, ZLOG_DEBUG, "child %d (pool %s) %s pipe is closed", (int) child->pid,
5669 +                                                       child->wp->config->name, is_stdout ? "stdout" : "stderr");
5670 +                                       }
5671 +#endif
5672 +                               }
5673 +                       }
5674 +                       else {
5675 +                               in_buf += res;
5676 +                       }
5677 +               }
5678 +
5679 +               if (fifo_out) {
5680 +                       if (in_buf == 0) {
5681 +                               fifo_out = 0;
5682 +                       }
5683 +                       else {
5684 +                               char *nl;
5685 +                               int should_print = 0;
5686 +                               buf[in_buf] = '\0';
5687 +
5688 +                               /* FIXME: there might be binary data */
5689 +
5690 +                               /* we should print if no more space in the buffer */
5691 +                               if (in_buf == max_buf_size - 1) {
5692 +                                       should_print = 1;
5693 +                               }
5694 +
5695 +                               /* we should print if no more data to come */
5696 +                               if (!fifo_in) {
5697 +                                       should_print = 1;
5698 +                               }
5699 +
5700 +                               nl = strchr(buf, '\n');
5701 +
5702 +                               if (nl || should_print) {
5703 +
5704 +                                       if (nl) {
5705 +                                               *nl = '\0';
5706 +                                       }
5707 +
5708 +                                       zlog(ZLOG_STUFF, ZLOG_WARNING, "child %d (pool %s) said into %s: \"%s\"%s", (int) child->pid,
5709 +                                               child->wp->config->name, is_stdout ? "stdout" : "stderr", buf, is_last_message ? ", pipe is closed" : "");
5710 +
5711 +                                       if (nl) {
5712 +                                               int out_buf = 1 + nl - buf;
5713 +                                               memmove(buf, buf + out_buf, in_buf - out_buf);
5714 +                                               in_buf -= out_buf;
5715 +                                       }
5716 +                                       else {
5717 +                                               in_buf = 0;
5718 +                                       }
5719 +                               }
5720 +                       }
5721 +               }
5722 +       }
5723 +
5724 +}
5725 +
5726 +int fpm_stdio_prepare_pipes(struct fpm_child_s *child)
5727 +{
5728 +       if (0 == child->wp->config->catch_workers_output) { /* not required */
5729 +               return 0;
5730 +       }
5731 +
5732 +       if (0 > pipe(fd_stdout)) {
5733 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "pipe() failed");
5734 +               return -1;
5735 +       }
5736 +
5737 +       if (0 > pipe(fd_stderr)) {
5738 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "pipe() failed");
5739 +               close(fd_stdout[0]); close(fd_stdout[1]);
5740 +               return -1;
5741 +       }
5742 +
5743 +       if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
5744 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fd_set_blocked() failed");
5745 +               close(fd_stdout[0]); close(fd_stdout[1]);
5746 +               close(fd_stderr[0]); close(fd_stderr[1]);
5747 +               return -1;
5748 +       }
5749 +
5750 +       return 0;
5751 +}
5752 +
5753 +int fpm_stdio_parent_use_pipes(struct fpm_child_s *child)
5754 +{
5755 +       if (0 == child->wp->config->catch_workers_output) { /* not required */
5756 +               return 0;
5757 +       }
5758 +
5759 +       close(fd_stdout[1]);
5760 +       close(fd_stderr[1]);
5761 +
5762 +       child->fd_stdout = fd_stdout[0];
5763 +       child->fd_stderr = fd_stderr[0];
5764 +
5765 +       fpm_event_add(child->fd_stdout, &child->ev_stdout, fpm_stdio_child_said, child);
5766 +       fpm_event_add(child->fd_stderr, &child->ev_stderr, fpm_stdio_child_said, child);
5767 +
5768 +       return 0;
5769 +}
5770 +
5771 +int fpm_stdio_discard_pipes(struct fpm_child_s *child)
5772 +{
5773 +       if (0 == child->wp->config->catch_workers_output) { /* not required */
5774 +               return 0;
5775 +       }
5776 +
5777 +       close(fd_stdout[1]);
5778 +       close(fd_stderr[1]);
5779 +
5780 +       close(fd_stdout[0]);
5781 +       close(fd_stderr[0]);
5782 +
5783 +       return 0;
5784 +}
5785 +
5786 +void fpm_stdio_child_use_pipes(struct fpm_child_s *child)
5787 +{
5788 +       if (child->wp->config->catch_workers_output) {
5789 +               dup2(fd_stdout[1], STDOUT_FILENO);
5790 +               dup2(fd_stderr[1], STDERR_FILENO);
5791 +               close(fd_stdout[0]); close(fd_stdout[1]);
5792 +               close(fd_stderr[0]); close(fd_stderr[1]);
5793 +       }
5794 +       else {
5795 +               /* stdout of parent is always /dev/null */
5796 +               dup2(STDOUT_FILENO, STDERR_FILENO);
5797 +       }
5798 +}      
5799 +
5800 +int fpm_stdio_open_error_log(int reopen)
5801 +{
5802 +       int fd;
5803 +
5804 +       fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
5805 +
5806 +       if (0 > fd) {
5807 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "open(\"%s\") failed", fpm_global_config.error_log);
5808 +               return -1;
5809 +       }
5810 +
5811 +       if (reopen) {
5812 +               if (fpm_global_config.daemonize) {
5813 +                       dup2(fd, STDERR_FILENO);
5814 +               }
5815 +
5816 +               dup2(fd, fpm_globals.error_log_fd);
5817 +               close(fd);
5818 +               fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
5819 +       }
5820 +       else {
5821 +               fpm_globals.error_log_fd = fd;
5822 +       }
5823 +
5824 +       fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
5825 +
5826 +       return 0;
5827 +}
5828 diff --git a/sapi/cgi/fpm/fpm_stdio.h b/sapi/cgi/fpm/fpm_stdio.h
5829 new file mode 100644
5830 --- /dev/null
5831 +++ b/sapi/cgi/fpm/fpm_stdio.h
5832 @@ -0,0 +1,20 @@
5833 +
5834 +       /* $Id$ */
5835 +       /* (c) 2007,2008 Andrei Nigmatulin */
5836 +
5837 +#ifndef FPM_STDIO_H
5838 +#define FPM_STDIO_H 1
5839 +
5840 +#include "fpm_worker_pool.h"
5841 +
5842 +int fpm_stdio_init_main();
5843 +int fpm_stdio_init_final();
5844 +int fpm_stdio_init_child(struct fpm_worker_pool_s *wp);
5845 +int fpm_stdio_prepare_pipes(struct fpm_child_s *child);
5846 +void fpm_stdio_child_use_pipes(struct fpm_child_s *child);
5847 +int fpm_stdio_parent_use_pipes(struct fpm_child_s *child);
5848 +int fpm_stdio_discard_pipes(struct fpm_child_s *child);
5849 +int fpm_stdio_open_error_log(int reopen);
5850 +
5851 +#endif
5852 +
5853 diff --git a/sapi/cgi/fpm/fpm_str.h b/sapi/cgi/fpm/fpm_str.h
5854 new file mode 100644
5855 --- /dev/null
5856 +++ b/sapi/cgi/fpm/fpm_str.h
5857 @@ -0,0 +1,49 @@
5858 +
5859 +       /* $Id$ */
5860 +       /* (c) 2007,2008 Andrei Nigmatulin */
5861 +
5862 +#ifndef FPM_STR_H
5863 +#define FPM_STR_H 1
5864 +
5865 +static inline char *cpystrn(char *dst, const char *src, size_t dst_size)
5866 +{
5867 +       char *d, *end;
5868 +       
5869 +       if (!dst_size) return dst;
5870 +       
5871 +       d = dst;
5872 +       end = dst + dst_size - 1;
5873 +       
5874 +       for (; d < end; ++d, ++src) {
5875 +               if (!(*d = *src)) {
5876 +                       return d;
5877 +               }
5878 +       }
5879 +
5880 +       *d = '\0';
5881 +
5882 +       return d;
5883 +}
5884 +
5885 +static inline char *str_purify_filename(char *dst, char *src, size_t size)
5886 +{
5887 +       char *d, *end;
5888 +
5889 +       d = dst;
5890 +       end = dst + size - 1;
5891 +
5892 +       for (; d < end && *src; ++d, ++src) {
5893 +               if (* (unsigned char *) src < ' ' || * (unsigned char *) src > '\x7f') {
5894 +                       *d = '.';
5895 +               }
5896 +               else {
5897 +                       *d = *src;
5898 +               }
5899 +       }
5900 +
5901 +       *d = '\0';
5902 +
5903 +       return d;
5904 +}
5905 +
5906 +#endif
5907 diff --git a/sapi/cgi/fpm/fpm_trace.c b/sapi/cgi/fpm/fpm_trace.c
5908 new file mode 100644
5909 --- /dev/null
5910 +++ b/sapi/cgi/fpm/fpm_trace.c
5911 @@ -0,0 +1,46 @@
5912 +
5913 +       /* $Id$ */
5914 +       /* (c) 2007,2008 Andrei Nigmatulin */
5915 +
5916 +#include "fpm_config.h"
5917 +
5918 +#include <sys/types.h>
5919 +
5920 +#include "fpm_trace.h"
5921 +
5922 +int fpm_trace_get_strz(char *buf, size_t sz, long addr)
5923 +{
5924 +       int i;
5925 +       long l;
5926 +       char *lc = (char *) &l;
5927 +
5928 +       if (0 > fpm_trace_get_long(addr, &l)) {
5929 +               return -1;
5930 +       }
5931 +
5932 +       i = l % SIZEOF_LONG;
5933 +
5934 +       l -= i;
5935 +
5936 +       for (addr = l; ; addr += SIZEOF_LONG) {
5937 +
5938 +               if (0 > fpm_trace_get_long(addr, &l)) {
5939 +                       return -1;
5940 +               }
5941 +
5942 +               for ( ; i < SIZEOF_LONG; i++) {
5943 +                       --sz;
5944 +
5945 +                       if (sz && lc[i]) {
5946 +                               *buf++ = lc[i];
5947 +                               continue;
5948 +                       }
5949 +
5950 +                       *buf = '\0';
5951 +                       return 0;
5952 +               }
5953 +
5954 +               i = 0;
5955 +       }
5956 +}
5957 +
5958 diff --git a/sapi/cgi/fpm/fpm_trace.h b/sapi/cgi/fpm/fpm_trace.h
5959 new file mode 100644
5960 --- /dev/null
5961 +++ b/sapi/cgi/fpm/fpm_trace.h
5962 @@ -0,0 +1,17 @@
5963 +
5964 +       /* $Id$ */
5965 +       /* (c) 2007,2008 Andrei Nigmatulin */
5966 +
5967 +#ifndef FPM_TRACE_H
5968 +#define FPM_TRACE_H 1
5969 +
5970 +#include <unistd.h>
5971 +
5972 +int fpm_trace_signal(pid_t pid);
5973 +int fpm_trace_ready(pid_t pid);
5974 +int fpm_trace_close(pid_t pid);
5975 +int fpm_trace_get_long(long addr, long *data);
5976 +int fpm_trace_get_strz(char *buf, size_t sz, long addr);
5977 +
5978 +#endif
5979 +
5980 diff --git a/sapi/cgi/fpm/fpm_trace_mach.c b/sapi/cgi/fpm/fpm_trace_mach.c
5981 new file mode 100644
5982 --- /dev/null
5983 +++ b/sapi/cgi/fpm/fpm_trace_mach.c
5984 @@ -0,0 +1,102 @@
5985 +
5986 +       /* $Id$ */
5987 +       /* (c) 2007,2008 Andrei Nigmatulin */
5988 +
5989 +#include "fpm_config.h"
5990 +
5991 +#include <mach/mach.h>
5992 +#include <mach/mach_vm.h>
5993 +
5994 +#include <unistd.h>
5995 +
5996 +#include "fpm_trace.h"
5997 +#include "fpm_process_ctl.h"
5998 +#include "fpm_unix.h"
5999 +#include "zlog.h"
6000 +
6001 +
6002 +static mach_port_name_t target;
6003 +static vm_offset_t target_page_base;
6004 +static vm_offset_t local_page;
6005 +static mach_msg_type_number_t local_size;
6006 +
6007 +static void fpm_mach_vm_deallocate()
6008 +{
6009 +       if (local_page) {
6010 +               mach_vm_deallocate(mach_task_self(), local_page, local_size);
6011 +               target_page_base = 0;
6012 +               local_page = 0;
6013 +               local_size = 0;
6014 +       }
6015 +}
6016 +
6017 +static int fpm_mach_vm_read_page(vm_offset_t page)
6018 +{
6019 +       kern_return_t kr;
6020 +
6021 +       kr = mach_vm_read(target, page, fpm_pagesize, &local_page, &local_size);
6022 +
6023 +       if (kr != KERN_SUCCESS) {
6024 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "mach_vm_read() failed: %s (%d)", mach_error_string(kr), kr);
6025 +               return -1;
6026 +       }
6027 +
6028 +       return 0;
6029 +}
6030 +
6031 +int fpm_trace_signal(pid_t pid)
6032 +{
6033 +       if (0 > fpm_pctl_kill(pid, FPM_PCTL_STOP)) {
6034 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "kill(SIGSTOP) failed");
6035 +               return -1;
6036 +       }
6037 +
6038 +       return 0;
6039 +}
6040 +
6041 +int fpm_trace_ready(pid_t pid)
6042 +{
6043 +       kern_return_t kr;
6044 +
6045 +       kr = task_for_pid(mach_task_self(), pid, &target);
6046 +
6047 +       if (kr != KERN_SUCCESS) {
6048 +               char *msg = "";
6049 +
6050 +               if (kr == KERN_FAILURE) {
6051 +                       msg = " It seems that master process does not have enough privileges to trace processes.";
6052 +               }
6053 +
6054 +               zlog(ZLOG_STUFF, ZLOG_ERROR, "task_for_pid() failed: %s (%d)%s", mach_error_string(kr), kr, msg);
6055 +               return -1;
6056 +       }
6057 +
6058 +       return 0;
6059 +}
6060 +
6061 +int fpm_trace_close(pid_t pid)
6062 +{
6063 +       fpm_mach_vm_deallocate();
6064 +
6065 +       target = 0;
6066 +
6067 +       return 0;
6068 +}
6069 +
6070 +int fpm_trace_get_long(long addr, long *data)
6071 +{
6072 +       size_t offset = ((uintptr_t) (addr) % fpm_pagesize);
6073 +       vm_offset_t base = (uintptr_t) (addr) - offset;
6074 +
6075 +       if (base != target_page_base) {
6076 +               fpm_mach_vm_deallocate();
6077 +               if (0 > fpm_mach_vm_read_page(base)) {
6078 +                       return -1;
6079 +               }
6080 +       }
6081 +
6082 +       *data = * (long *) (local_page + offset);
6083 +
6084 +       return 0;
6085 +}
6086 +
6087 diff --git a/sapi/cgi/fpm/fpm_trace_pread.c b/sapi/cgi/fpm/fpm_trace_pread.c
6088 new file mode 100644
6089 --- /dev/null
6090 +++ b/sapi/cgi/fpm/fpm_trace_pread.c
6091 @@ -0,0 +1,67 @@
6092 +
6093 +       /* $Id$ */
6094 +       /* (c) 2007,2008 Andrei Nigmatulin */
6095 +
6096 +#define _GNU_SOURCE
6097 +#define _FILE_OFFSET_BITS 64
6098 +
6099 +#include "fpm_config.h"
6100 +
6101 +#include <unistd.h>
6102 +
6103 +#include <fcntl.h>
6104 +#include <stdio.h>
6105 +#include <stdint.h>
6106 +
6107 +#include "fpm_trace.h"
6108 +#include "fpm_process_ctl.h"
6109 +#include "zlog.h"
6110 +
6111 +
6112 +static int mem_file = -1;
6113 +
6114 +int fpm_trace_signal(pid_t pid)
6115 +{
6116 +       if (0 > fpm_pctl_kill(pid, FPM_PCTL_STOP)) {
6117 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "kill(SIGSTOP) failed");
6118 +               return -1;
6119 +       }
6120 +
6121 +       return 0;
6122 +}
6123 +
6124 +int fpm_trace_ready(pid_t pid)
6125 +{
6126 +       char buf[128];
6127 +
6128 +       sprintf(buf, "/proc/%d/" PROC_MEM_FILE, (int) pid);
6129 +
6130 +       mem_file = open(buf, O_RDONLY);
6131 +
6132 +       if (0 > mem_file) {
6133 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "open(%s) failed", buf);
6134 +               return -1;
6135 +       }
6136 +
6137 +       return 0;
6138 +}
6139 +
6140 +int fpm_trace_close(pid_t pid)
6141 +{
6142 +       close(mem_file);
6143 +
6144 +       mem_file = -1;
6145 +
6146 +       return 0;
6147 +}
6148 +
6149 +int fpm_trace_get_long(long addr, long *data)
6150 +{
6151 +       if (sizeof(*data) != pread(mem_file, (void *) data, sizeof(*data), (uintptr_t) addr)) {
6152 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "pread() failed");
6153 +               return -1;
6154 +       }
6155 +
6156 +       return 0;
6157 +}
6158 +
6159 diff --git a/sapi/cgi/fpm/fpm_trace_ptrace.c b/sapi/cgi/fpm/fpm_trace_ptrace.c
6160 new file mode 100644
6161 --- /dev/null
6162 +++ b/sapi/cgi/fpm/fpm_trace_ptrace.c
6163 @@ -0,0 +1,85 @@
6164 +
6165 +       /* $Id$ */
6166 +       /* (c) 2007,2008 Andrei Nigmatulin */
6167 +
6168 +#include "fpm_config.h"
6169 +
6170 +#include <sys/wait.h>
6171 +#include <sys/ptrace.h>
6172 +#include <unistd.h>
6173 +#include <errno.h>
6174 +
6175 +#if defined(PT_ATTACH) && !defined(PTRACE_ATTACH)
6176 +#define PTRACE_ATTACH PT_ATTACH
6177 +#endif
6178 +
6179 +#if defined(PT_DETACH) && !defined(PTRACE_DETACH)
6180 +#define PTRACE_DETACH PT_DETACH
6181 +#endif
6182 +
6183 +#if defined(PT_READ_D) && !defined(PTRACE_PEEKDATA)
6184 +#define PTRACE_PEEKDATA PT_READ_D
6185 +#endif
6186 +
6187 +#include "fpm_trace.h"
6188 +#include "zlog.h"
6189 +
6190 +static pid_t traced_pid;
6191 +
6192 +int fpm_trace_signal(pid_t pid)
6193 +{
6194 +       if (0 > ptrace(PTRACE_ATTACH, pid, 0, 0)) {
6195 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "ptrace(ATTACH) failed");
6196 +               return -1;
6197 +       }
6198 +
6199 +       return 0;
6200 +}
6201 +
6202 +int fpm_trace_ready(pid_t pid)
6203 +{
6204 +       traced_pid = pid;
6205 +
6206 +       return 0;
6207 +}
6208 +
6209 +int fpm_trace_close(pid_t pid)
6210 +{
6211 +       if (0 > ptrace(PTRACE_DETACH, pid, (void *) 1, 0)) {
6212 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "ptrace(DETACH) failed");
6213 +               return -1;
6214 +       }
6215 +
6216 +       traced_pid = 0;
6217 +
6218 +       return 0;
6219 +}
6220 +
6221 +int fpm_trace_get_long(long addr, long *data)
6222 +{
6223 +#ifdef PT_IO
6224 +       struct ptrace_io_desc ptio = {
6225 +               .piod_op = PIOD_READ_D,
6226 +               .piod_offs = (void *) addr,
6227 +               .piod_addr = (void *) data,
6228 +               .piod_len = sizeof(long)
6229 +       };
6230 +
6231 +       if (0 > ptrace(PT_IO, traced_pid, (void *) &ptio, 0)) {
6232 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "ptrace(PT_IO) failed");
6233 +               return -1;
6234 +       }
6235 +#else
6236 +       errno = 0;
6237 +
6238 +       *data = ptrace(PTRACE_PEEKDATA, traced_pid, (void *) addr, 0);
6239 +
6240 +       if (errno) {
6241 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "ptrace(PEEKDATA) failed");
6242 +               return -1;
6243 +       }
6244 +#endif
6245 +
6246 +       return 0;
6247 +}
6248 +
6249 diff --git a/sapi/cgi/fpm/fpm_unix.c b/sapi/cgi/fpm/fpm_unix.c
6250 new file mode 100644
6251 --- /dev/null
6252 +++ b/sapi/cgi/fpm/fpm_unix.c
6253 @@ -0,0 +1,289 @@
6254 +
6255 +       /* $Id$ */
6256 +       /* (c) 2007,2008 Andrei Nigmatulin */
6257 +
6258 +#include "fpm_config.h"
6259 +
6260 +#include <string.h>
6261 +#include <sys/time.h>
6262 +#include <sys/resource.h>
6263 +#include <stdlib.h>
6264 +#include <unistd.h>
6265 +#include <sys/types.h>
6266 +#include <pwd.h>
6267 +#include <grp.h>
6268 +
6269 +#ifdef HAVE_PRCTL
6270 +#include <sys/prctl.h>
6271 +#endif
6272 +
6273 +#include "fpm.h"
6274 +#include "fpm_conf.h"
6275 +#include "fpm_cleanup.h"
6276 +#include "fpm_clock.h"
6277 +#include "fpm_stdio.h"
6278 +#include "fpm_unix.h"
6279 +#include "zlog.h"
6280 +
6281 +size_t fpm_pagesize;
6282 +
6283 +int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp)
6284 +{
6285 +       struct fpm_listen_options_s *lo = wp->config->listen_options;
6286 +
6287 +       /* uninitialized */
6288 +       wp->socket_uid = -1;
6289 +       wp->socket_gid = -1;
6290 +       wp->socket_mode = 0666;
6291 +
6292 +       if (!lo) return 0;
6293 +
6294 +       if (lo->owner && *lo->owner) {
6295 +               struct passwd *pwd;
6296 +
6297 +               pwd = getpwnam(lo->owner);
6298 +
6299 +               if (!pwd) {
6300 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "cannot get uid for user '%s', pool '%s'", lo->owner, wp->config->name);
6301 +                       return -1;
6302 +               }
6303 +
6304 +               wp->socket_uid = pwd->pw_uid;
6305 +               wp->socket_gid = pwd->pw_gid;
6306 +       }
6307 +
6308 +       if (lo->group && *lo->group) {
6309 +               struct group *grp;
6310 +
6311 +               grp = getgrnam(lo->group);
6312 +
6313 +               if (!grp) {
6314 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "cannot get gid for group '%s', pool '%s'", lo->group, wp->config->name);
6315 +                       return -1;
6316 +               }
6317 +
6318 +               wp->socket_gid = grp->gr_gid;
6319 +       }
6320 +
6321 +       if (lo->mode && *lo->mode) {
6322 +               wp->socket_mode = strtoul(lo->mode, 0, 8);
6323 +       }
6324 +
6325 +       return 0;
6326 +}
6327 +
6328 +static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp)
6329 +{
6330 +       int is_root = !geteuid();
6331 +
6332 +       if (is_root) {
6333 +               if (wp->config->user && *wp->config->user) {
6334 +
6335 +                       if (strlen(wp->config->user) == strspn(wp->config->user, "0123456789")) {
6336 +                               wp->set_uid = strtoul(wp->config->user, 0, 10);
6337 +                       }
6338 +                       else {
6339 +                               struct passwd *pwd;
6340 +
6341 +                               pwd = getpwnam(wp->config->user);
6342 +
6343 +                               if (!pwd) {
6344 +                                       zlog(ZLOG_STUFF, ZLOG_ERROR, "cannot get uid for user '%s', pool '%s'", wp->config->user, wp->config->name);
6345 +                                       return -1;
6346 +                               }
6347 +
6348 +                               wp->set_uid = pwd->pw_uid;
6349 +                               wp->set_gid = pwd->pw_gid;
6350 +
6351 +                               wp->user = strdup(pwd->pw_name);
6352 +                               wp->home = strdup(pwd->pw_dir);
6353 +                       }
6354 +               }
6355 +
6356 +               if (wp->config->group && *wp->config->group) {
6357 +
6358 +                       if (strlen(wp->config->group) == strspn(wp->config->group, "0123456789")) {
6359 +                               wp->set_gid = strtoul(wp->config->group, 0, 10);
6360 +                       }
6361 +                       else {
6362 +                               struct group *grp;
6363 +
6364 +                               grp = getgrnam(wp->config->group);
6365 +
6366 +                               if (!grp) {
6367 +                                       zlog(ZLOG_STUFF, ZLOG_ERROR, "cannot get gid for group '%s', pool '%s'", wp->config->group, wp->config->name);
6368 +                                       return -1;
6369 +                               }
6370 +
6371 +                               wp->set_gid = grp->gr_gid;
6372 +                       }
6373 +               }
6374 +
6375 +#ifndef I_REALLY_WANT_ROOT_PHP
6376 +               if (wp->set_uid == 0 || wp->set_gid == 0) {
6377 +                       zlog(ZLOG_STUFF, ZLOG_ERROR, "please specify user and group other than root, pool '%s'", wp->config->name);
6378 +                       return -1;
6379 +               }
6380 +#endif
6381 +       }
6382 +       else { /* not root */
6383 +               if (wp->config->user && *wp->config->user) {
6384 +                       zlog(ZLOG_STUFF, ZLOG_WARNING, "'user' directive is ignored, pool '%s'", wp->config->name);
6385 +               }
6386 +               if (wp->config->group && *wp->config->group) {
6387 +                       zlog(ZLOG_STUFF, ZLOG_WARNING, "'group' directive is ignored, pool '%s'", wp->config->name);
6388 +               }
6389 +               if (wp->config->chroot && *wp->config->chroot) {
6390 +                       zlog(ZLOG_STUFF, ZLOG_WARNING, "'chroot' directive is ignored, pool '%s'", wp->config->name);
6391 +               }
6392 +
6393 +               { /* set up HOME and USER anyway */
6394 +                       struct passwd *pwd;
6395 +
6396 +                       pwd = getpwuid(getuid());
6397 +
6398 +                       if (pwd) {
6399 +                               wp->user = strdup(pwd->pw_name);
6400 +                               wp->home = strdup(pwd->pw_dir);
6401 +                       }
6402 +               }
6403 +       }
6404 +
6405 +       return 0;
6406 +}
6407 +
6408 +int fpm_unix_init_child(struct fpm_worker_pool_s *wp)
6409 +{
6410 +       int is_root = !geteuid();
6411 +       int made_chroot = 0;
6412 +
6413 +       if (wp->config->rlimit_files) {
6414 +               struct rlimit r;
6415 +
6416 +               getrlimit(RLIMIT_NOFILE, &r);
6417 +
6418 +               r.rlim_cur = (rlim_t) wp->config->rlimit_files;
6419 +       r.rlim_max = r.rlim_cur;    
6420 +               if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
6421 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "setrlimit(RLIMIT_NOFILE) failed");
6422 +               }
6423 +       }
6424 +
6425 +       if (wp->config->rlimit_core) {
6426 +               struct rlimit r;
6427 +
6428 +               getrlimit(RLIMIT_CORE, &r);
6429 +
6430 +               r.rlim_cur = wp->config->rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) wp->config->rlimit_core;
6431 +       r.rlim_max = r.rlim_cur;
6432 +               if (0 > setrlimit(RLIMIT_CORE, &r)) {
6433 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "setrlimit(RLIMIT_CORE) failed");
6434 +               }
6435 +       }
6436 +
6437 +       if (is_root && wp->config->chroot && *wp->config->chroot) {
6438 +               if (0 > chroot(wp->config->chroot)) {
6439 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "chroot(%s) failed", wp->config->chroot);
6440 +                       return -1;
6441 +               }
6442 +               made_chroot = 1;
6443 +       }
6444 +
6445 +       if (wp->config->chdir && *wp->config->chdir) {
6446 +               if (0 > chdir(wp->config->chdir)) {
6447 +                       zlog(ZLOG_STUFF, ZLOG_SYSERROR, "chdir(%s) failed", wp->config->chdir);
6448 +                       return -1;
6449 +               }
6450 +       }
6451 +       else if (made_chroot) {
6452 +               chdir("/");
6453 +       }
6454 +
6455 +       if (is_root) {
6456 +               if (wp->set_gid) {
6457 +                       if (0 > setgid(wp->set_gid)) {
6458 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "setgid(%d) failed", wp->set_gid);
6459 +                               return -1;
6460 +                       }
6461 +               }
6462 +               if (wp->set_uid) {
6463 +                       if (0 > initgroups(wp->config->user, wp->set_gid)) {
6464 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "initgroups(%s, %d) failed", wp->config->user, wp->set_gid);
6465 +                               return -1;
6466 +                       }
6467 +                       if (0 > setuid(wp->set_uid)) {
6468 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "setuid(%d) failed", wp->set_uid);
6469 +                               return -1;
6470 +                       }
6471 +               }
6472 +       }
6473 +
6474 +#ifdef HAVE_PRCTL
6475 +       if (0 > prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) {
6476 +               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "prctl(PR_SET_DUMPABLE) failed");
6477 +       }
6478 +#endif
6479 +
6480 +       if (0 > fpm_clock_init()) {
6481 +               return -1;
6482 +       }
6483 +
6484 +       return 0;
6485 +}
6486 +
6487 +int fpm_unix_init_main()
6488 +{
6489 +       struct fpm_worker_pool_s *wp;
6490 +
6491 +       fpm_pagesize = getpagesize();
6492 +
6493 +       if (fpm_global_config.daemonize) {
6494 +
6495 +               switch (fork()) {
6496 +
6497 +                       case -1 :
6498 +
6499 +                               zlog(ZLOG_STUFF, ZLOG_SYSERROR, "fork() failed");
6500 +                               return -1;
6501 +
6502 +                       case 0 :
6503 +
6504 +                               break;
6505 +
6506 +                       default :
6507 +
6508 +                               fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);
6509 +                               exit(0);
6510 +
6511 +               }
6512 +
6513 +       }
6514 +
6515 +       setsid();
6516 +
6517 +       if (0 > fpm_clock_init()) {
6518 +               return -1;
6519 +       }
6520 +
6521 +       fpm_globals.parent_pid = getpid();
6522 +
6523 +       for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
6524 +
6525 +               if (0 > fpm_unix_conf_wp(wp)) {
6526 +                       return -1;
6527 +               }
6528 +
6529 +       }
6530 +
6531 +       fpm_stdio_init_final();
6532 +
6533 +       {
6534 +               struct rlimit r;
6535 +               getrlimit(RLIMIT_NOFILE, &r);
6536 +
6537 +               zlog(ZLOG_STUFF, ZLOG_NOTICE, "getrlimit(nofile): max:%lld, cur:%lld",
6538 +                       (long long) r.rlim_max, (long long) r.rlim_cur);
6539 +       }
6540 +
6541 +       return 0;
6542 +}
6543 diff --git a/sapi/cgi/fpm/fpm_unix.h b/sapi/cgi/fpm/fpm_unix.h
6544 new file mode 100644
6545 --- /dev/null
6546 +++ b/sapi/cgi/fpm/fpm_unix.h
6547 @@ -0,0 +1,17 @@
6548 +
6549 +       /* $Id$ */
6550 +       /* (c) 2007,2008 Andrei Nigmatulin */
6551 +
6552 +#ifndef FPM_UNIX_H
6553 +#define FPM_UNIX_H 1
6554 +
6555 +#include "fpm_worker_pool.h"
6556 +
6557 +int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp);
6558 +int fpm_unix_init_child(struct fpm_worker_pool_s *wp);
6559 +int fpm_unix_init_main();
6560 +
6561 +extern size_t fpm_pagesize;
6562 +
6563 +#endif
6564 +
6565 diff --git a/sapi/cgi/fpm/fpm_worker_pool.c b/sapi/cgi/fpm/fpm_worker_pool.c
6566 new file mode 100644
6567 --- /dev/null
6568 +++ b/sapi/cgi/fpm/fpm_worker_pool.c
6569 @@ -0,0 +1,69 @@
6570 +
6571 +       /* $Id$ */
6572 +       /* (c) 2007,2008 Andrei Nigmatulin */
6573 +
6574 +#include "fpm_config.h"
6575 +
6576 +#include <string.h>
6577 +#include <stdlib.h>
6578 +#include <unistd.h>
6579 +
6580 +#include "fpm_worker_pool.h"
6581 +#include "fpm_cleanup.h"
6582 +#include "fpm_children.h"
6583 +#include "fpm_shm.h"
6584 +#include "fpm_shm_slots.h"
6585 +#include "fpm_conf.h"
6586 +
6587 +struct fpm_worker_pool_s *fpm_worker_all_pools;
6588 +
6589 +static void fpm_worker_pool_cleanup(int which, void *arg)
6590 +{
6591 +       struct fpm_worker_pool_s *wp, *wp_next;
6592 +
6593 +       for (wp = fpm_worker_all_pools; wp; wp = wp_next) {
6594 +               wp_next = wp->next;
6595 +               fpm_worker_pool_config_free(wp->config);
6596 +               fpm_children_free(wp->children);
6597 +               fpm_array_free(&wp->slots_used);
6598 +               fpm_array_free(&wp->slots_free);
6599 +               fpm_shm_free_list(wp->shm_list, which == FPM_CLEANUP_CHILD ? fpm_shm_slots_mem() : 0);
6600 +               free(wp->config);
6601 +               free(wp->user);
6602 +               free(wp->home);
6603 +               free(wp);
6604 +       }
6605 +
6606 +       fpm_worker_all_pools = 0;
6607 +}
6608 +
6609 +struct fpm_worker_pool_s *fpm_worker_pool_alloc()
6610 +{
6611 +       struct fpm_worker_pool_s *ret;
6612 +
6613 +       ret = malloc(sizeof(struct fpm_worker_pool_s));
6614 +
6615 +       if (!ret) {
6616 +               return 0;
6617 +       }
6618 +
6619 +       memset(ret, 0, sizeof(struct fpm_worker_pool_s));
6620 +
6621 +       if (!fpm_worker_all_pools) {
6622 +               fpm_worker_all_pools = ret;
6623 +       }
6624 +
6625 +       fpm_array_init(&ret->slots_used, sizeof(struct fpm_shm_slot_ptr_s), 50);
6626 +       fpm_array_init(&ret->slots_free, sizeof(struct fpm_shm_slot_ptr_s), 50);
6627 +
6628 +       return ret;
6629 +}
6630 +
6631 +int fpm_worker_pool_init_main()
6632 +{
6633 +       if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_worker_pool_cleanup, 0)) {
6634 +               return -1;
6635 +       }
6636 +
6637 +       return 0;
6638 +}
6639 diff --git a/sapi/cgi/fpm/fpm_worker_pool.h b/sapi/cgi/fpm/fpm_worker_pool.h
6640 new file mode 100644
6641 --- /dev/null
6642 +++ b/sapi/cgi/fpm/fpm_worker_pool.h
6643 @@ -0,0 +1,46 @@
6644 +
6645 +       /* $Id$ */
6646 +       /* (c) 2007,2008 Andrei Nigmatulin */
6647 +
6648 +#ifndef FPM_WORKER_POOL_H
6649 +#define FPM_WORKER_POOL_H 1
6650 +
6651 +#include "fpm_conf.h"
6652 +#include "fpm_arrays.h"
6653 +
6654 +struct fpm_worker_pool_s;
6655 +struct fpm_child_s;
6656 +struct fpm_child_stat_s;
6657 +struct fpm_shm_s;
6658 +
6659 +enum fpm_address_domain {
6660 +       FPM_AF_UNIX = 1,
6661 +       FPM_AF_INET = 2
6662 +};
6663 +
6664 +struct fpm_worker_pool_s {
6665 +       struct fpm_worker_pool_s *next;
6666 +       struct fpm_worker_pool_config_s *config;
6667 +       char *user, *home;                                                                      /* for setting env USER and HOME */
6668 +       enum fpm_address_domain listen_address_domain;
6669 +       int listening_socket;
6670 +       int set_uid, set_gid;                                                           /* config uid and gid */
6671 +       unsigned is_template:1;                                                                 /* just config template, no processes will be created */
6672 +       int socket_uid, socket_gid, socket_mode;
6673 +
6674 +       struct fpm_shm_s *shm_list;
6675 +       struct fpm_array_s slots_used;
6676 +       struct fpm_array_s slots_free;
6677 +
6678 +       /* runtime */
6679 +       struct fpm_child_s *children;
6680 +       int running_children;
6681 +};
6682 +
6683 +struct fpm_worker_pool_s *fpm_worker_pool_alloc();
6684 +int fpm_worker_pool_init_main();
6685 +
6686 +extern struct fpm_worker_pool_s *fpm_worker_all_pools;
6687 +
6688 +#endif
6689 +
6690 diff --git a/sapi/cgi/fpm/init.d/php-fpm.in b/sapi/cgi/fpm/init.d/php-fpm.in
6691 new file mode 100644
6692 --- /dev/null
6693 +++ b/sapi/cgi/fpm/init.d/php-fpm.in
6694 @@ -0,0 +1,139 @@
6695 +#! /bin/sh
6696 +
6697 +php_fpm_BIN=@prefix@/bin/php-cgi
6698 +php_fpm_CONF=@php_fpm_conf_path@
6699 +php_fpm_PID=@php_fpm_pid_path@
6700 +
6701 +
6702 +php_opts="--fpm-config $php_fpm_CONF"
6703 +
6704 +
6705 +wait_for_pid () {
6706 +       try=0
6707 +
6708 +       while test $try -lt 35 ; do
6709 +
6710 +               case "$1" in
6711 +                       'created')
6712 +                       if [ -f "$2" ] ; then
6713 +                               try=''
6714 +                               break
6715 +                       fi
6716 +                       ;;
6717 +
6718 +                       'removed')
6719 +                       if [ ! -f "$2" ] ; then
6720 +                               try=''
6721 +                               break
6722 +                       fi
6723 +                       ;;
6724 +               esac
6725 +
6726 +               echo -n .
6727 +               try=`expr $try + 1`
6728 +               sleep 1
6729 +
6730 +       done
6731 +
6732 +}
6733 +
6734 +case "$1" in
6735 +       start)
6736 +               echo -n "Starting php_fpm "
6737 +
6738 +               $php_fpm_BIN --fpm $php_opts
6739 +
6740 +               if [ "$?" != 0 ] ; then
6741 +                       echo " failed"
6742 +                       exit 1
6743 +               fi
6744 +
6745 +               wait_for_pid created $php_fpm_PID
6746 +
6747 +               if [ -n "$try" ] ; then
6748 +                       echo " failed"
6749 +                       exit 1
6750 +               else
6751 +                       echo " done"
6752 +               fi
6753 +       ;;
6754 +
6755 +       stop)
6756 +               echo -n "Shutting down php_fpm "
6757 +
6758 +               if [ ! -r $php_fpm_PID ] ; then
6759 +                       echo "warning, no pid file found - php-fpm is not running ?"
6760 +                       exit 1
6761 +               fi
6762 +
6763 +               kill -TERM `cat $php_fpm_PID`
6764 +
6765 +               wait_for_pid removed $php_fpm_PID
6766 +
6767 +               if [ -n "$try" ] ; then
6768 +                       echo " failed"
6769 +                       exit 1
6770 +               else
6771 +                       echo " done"
6772 +               fi
6773 +       ;;
6774 +
6775 +       quit)
6776 +               echo -n "Gracefully shutting down php_fpm "
6777 +
6778 +               if [ ! -r $php_fpm_PID ] ; then
6779 +                       echo "warning, no pid file found - php-fpm is not running ?"
6780 +                       exit 1
6781 +               fi
6782 +
6783 +               kill -QUIT `cat $php_fpm_PID`
6784 +
6785 +               wait_for_pid removed $php_fpm_PID
6786 +
6787 +               if [ -n "$try" ] ; then
6788 +                       echo " failed"
6789 +                       exit 1
6790 +               else
6791 +                       echo " done"
6792 +               fi
6793 +       ;;
6794 +
6795 +       restart)
6796 +               $0 stop
6797 +               $0 start
6798 +       ;;
6799 +
6800 +       reload)
6801 +
6802 +               echo -n "Reload service php-fpm "
6803 +
6804 +               if [ ! -r $php_fpm_PID ] ; then
6805 +                       echo "warning, no pid file found - php-fpm is not running ?"
6806 +                       exit 1
6807 +               fi
6808 +
6809 +               kill -USR2 `cat $php_fpm_PID`
6810 +
6811 +               echo " done"
6812 +       ;;
6813 +
6814 +       logrotate)
6815 +
6816 +               echo -n "Re-opening php-fpm log file "
6817 +
6818 +               if [ ! -r $php_fpm_PID ] ; then
6819 +                       echo "warning, no pid file found - php-fpm is not running ?"
6820 +                       exit 1
6821 +               fi
6822 +
6823 +               kill -USR1 `cat $php_fpm_PID`
6824 +
6825 +               echo " done"
6826 +       ;;
6827 +
6828 +       *)
6829 +               echo "Usage: $0 {start|stop|quit|restart|reload|logrotate}"
6830 +               exit 1
6831 +       ;;
6832 +
6833 +esac
6834 diff --git a/sapi/cgi/fpm/xml_config.c b/sapi/cgi/fpm/xml_config.c
6835 new file mode 100644
6836 --- /dev/null
6837 +++ b/sapi/cgi/fpm/xml_config.c
6838 @@ -0,0 +1,278 @@
6839 +
6840 +       /* $Id$ */
6841 +       /* (c) 2004-2007 Andrei Nigmatulin */
6842 +
6843 +#include "fpm_config.h"
6844 +
6845 +#ifdef HAVE_ALLOCA_H
6846 +#include <alloca.h>
6847 +#endif
6848 +#include <string.h>
6849 +#include <stdio.h>
6850 +#include <stddef.h>
6851 +#include <stdlib.h>
6852 +
6853 +#include <libxml/parser.h>
6854 +#include <libxml/tree.h>
6855 +
6856 +#include "xml_config.h"
6857 +
6858 +static struct xml_conf_section **xml_conf_sections = 0;
6859 +static int xml_conf_sections_allocated = 0;
6860 +static int xml_conf_sections_used = 0;
6861 +
6862 +char *xml_conf_set_slot_boolean(void **conf, char *name, void *vv, intptr_t offset)
6863 +{
6864 +       char *value = vv;
6865 +       long value_y = !strcasecmp(value, "yes") || !strcmp(value,  "1") || !strcasecmp(value, "on");
6866 +       long value_n = !strcasecmp(value, "no")  || !strcmp(value,  "0") || !strcasecmp(value, "off");
6867 +
6868 +       if (!value_y && !value_n) {
6869 +               return "xml_conf_set_slot(): invalid boolean value";
6870 +       }
6871 +
6872 +#ifdef XML_CONF_DEBUG
6873 +       fprintf(stderr, "setting boolean '%s' => %s\n", name, value_y ? "TRUE" : "FALSE");
6874 +#endif
6875 +
6876 +       * (int *) ((char *) *conf + offset) = value_y ? 1 : 0;
6877 +
6878 +       return NULL;
6879 +}
6880 +
6881 +char *xml_conf_set_slot_string(void **conf, char *name, void *vv, intptr_t offset)
6882 +{
6883 +       char *value = vv;
6884 +       char *v = strdup(value);
6885 +
6886 +       if (!v) return "xml_conf_set_slot_string(): strdup() failed";
6887 +
6888 +#ifdef XML_CONF_DEBUG
6889 +       fprintf(stderr, "setting string '%s' => '%s'\n", name, v);
6890 +#endif
6891 +
6892 +       * (char **) ((char *) *conf + offset) = v;
6893 +
6894 +       return NULL;
6895 +}
6896 +
6897 +char *xml_conf_set_slot_integer(void **conf, char *name, void *vv, intptr_t offset)
6898 +{
6899 +       char *value = vv;
6900 +       int v = atoi(value);
6901 +
6902 +       * (int *) ((char *) *conf + offset) = v;
6903 +
6904 +#ifdef XML_CONF_DEBUG
6905 +       fprintf(stderr, "setting integer '%s' => %d\n", name, v);
6906 +#endif
6907 +
6908 +       return NULL;
6909 +}
6910 +
6911 +char *xml_conf_set_slot_time(void **conf, char *name, void *vv, intptr_t offset)
6912 +{
6913 +       char *value = vv;
6914 +       int len = strlen(value);
6915 +       char suffix;
6916 +       int seconds;
6917 +
6918 +       if (!len) return "xml_conf_set_slot_timeval(): invalid timeval value";
6919 +
6920 +       suffix = value[len-1];
6921 +
6922 +       value[len-1] = '\0';
6923 +
6924 +       switch (suffix) {
6925 +               case 's' :
6926 +                       seconds = atoi(value);
6927 +                       break;
6928 +               case 'm' :
6929 +                       seconds = 60 * atoi(value);
6930 +                       break;
6931 +               case 'h' :
6932 +                       seconds = 60 * 60 * atoi(value);
6933 +                       break;
6934 +               case 'd' :
6935 +                       seconds = 24 * 60 * 60 * atoi(value);
6936 +                       break;
6937 +               default :
6938 +                       return "xml_conf_set_slot_timeval(): unknown suffix used in timeval value";
6939 +       }
6940 +
6941 +       * (int *) ((char *) *conf + offset) = seconds;
6942 +
6943 +#ifdef XML_CONF_DEBUG
6944 +       fprintf(stderr, "setting time '%s' => %d:%02d:%02d:%02d\n", name, expand_dhms(seconds));
6945 +#endif
6946 +
6947 +       return NULL;
6948 +}
6949 +
6950 +char *xml_conf_parse_section(void **conf, struct xml_conf_section *section, void *xml_node)
6951 +{
6952 +       xmlNode *element = xml_node;
6953 +       char *ret = 0;
6954 +
6955 +#ifdef XML_CONF_DEBUG
6956 +       fprintf(stderr, "processing a section %s\n", section->path);
6957 +#endif
6958 +
6959 +       for ( ; element; element = element->next) {
6960 +               if (element->type == XML_ELEMENT_NODE && !strcmp((const char *) element->name, "value") && element->children) {
6961 +                       xmlChar *name = xmlGetProp(element, (unsigned char *) "name");
6962 +
6963 +                       if (name) {
6964 +                               int i;
6965 +
6966 +#ifdef XML_CONF_DEBUG
6967 +                               fprintf(stderr, "found a value: %s\n", name);
6968 +#endif
6969 +                               for (i = 0; section->parsers[i].parser; i++) {
6970 +                                       if (!section->parsers[i].name || !strcmp(section->parsers[i].name, (char *) name)) {
6971 +                                               break;
6972 +                                       }
6973 +                               }
6974 +
6975 +                               if (section->parsers[i].parser) {
6976 +                                       if (section->parsers[i].type == XML_CONF_SCALAR) {
6977 +                                               if (element->children->type == XML_TEXT_NODE) {
6978 +                                                       ret = section->parsers[i].parser(conf, (char *) name, element->children->content, section->parsers[i].offset);
6979 +                                               }
6980 +                                               else {
6981 +                                                       ret = "XML_TEXT_NODE is expected, something different is given";
6982 +                                               }
6983 +                                       }
6984 +                                       else {
6985 +                                               ret = section->parsers[i].parser(conf, (char *) name, element->children, section->parsers[i].offset);
6986 +                                       }
6987 +
6988 +                                       xmlFree(name);
6989 +                                       if (ret) return ret;
6990 +                                       else continue;
6991 +                               }
6992 +
6993 +                               fprintf(stderr, "Warning, unknown setting '%s' in section '%s'\n", (char *) name, section->path);
6994 +
6995 +                               xmlFree(name);
6996 +                       }
6997 +               }
6998 +       }
6999 +
7000 +       return NULL;
7001 +}
7002 +
7003 +static char *xml_conf_parse_file(xmlNode *element)
7004 +{
7005 +       char *ret = 0;
7006 +
7007 +       for ( ; element; element = element->next) {
7008 +
7009 +               if (element->parent && element->type == XML_ELEMENT_NODE && !strcmp((const char *) element->name, "section")) {
7010 +                       xmlChar *name = xmlGetProp(element, (unsigned char *) "name");
7011 +
7012 +                       if (name) {
7013 +                               char *parent_name = (char *) xmlGetNodePath(element->parent);
7014 +                               char *full_name;
7015 +                               int i;
7016 +                               struct xml_conf_section *section = NULL;
7017 +
7018 +#ifdef XML_CONF_DEBUG
7019 +                               fprintf(stderr, "got a section: %s/%s\n", parent_name, name);
7020 +#endif
7021 +                               full_name = alloca(strlen(parent_name) + strlen((char *) name) + 1 + 1);
7022 +
7023 +                               sprintf(full_name, "%s/%s", parent_name, (char *) name);
7024 +
7025 +                               xmlFree(parent_name);
7026 +                               xmlFree(name);
7027 +
7028 +                               for (i = 0; i < xml_conf_sections_used; i++) {
7029 +                                       if (!strcmp(xml_conf_sections[i]->path, full_name)) {
7030 +                                               section = xml_conf_sections[i];
7031 +                                       }
7032 +                               }
7033 +
7034 +                               if (section) { /* found a registered section */
7035 +                                       void *conf = section->conf();
7036 +                                       ret = xml_conf_parse_section(&conf, section, element->children);
7037 +                                       if (ret) break;
7038 +                               }
7039 +
7040 +                       }
7041 +               }
7042 +
7043 +               if (element->children) {
7044 +                       ret = xml_conf_parse_file(element->children);
7045 +                       if (ret) break;
7046 +               }
7047 +       }
7048 +
7049 +       return ret;
7050 +}
7051 +
7052 +char *xml_conf_load_file(char *file)
7053 +{
7054 +       char *ret = 0;
7055 +       xmlDoc *doc;
7056 +
7057 +       LIBXML_TEST_VERSION
7058 +
7059 +       doc = xmlParseFile(file);
7060 +
7061 +       if (doc) {
7062 +               ret = xml_conf_parse_file(doc->children);
7063 +               xmlFreeDoc(doc);
7064 +       }
7065 +       else {
7066 +               ret = "failed to parse conf file";
7067 +       }
7068 +
7069 +       xmlCleanupParser();
7070 +       return ret;
7071 +}
7072 +
7073 +int xml_conf_init()
7074 +{
7075 +       return 0;
7076 +}
7077 +
7078 +void xml_conf_clean()
7079 +{
7080 +       if (xml_conf_sections) {
7081 +               free(xml_conf_sections);
7082 +       }
7083 +}
7084 +
7085 +int xml_conf_section_register(struct xml_conf_section *section)
7086 +{
7087 +       if (xml_conf_sections_allocated == xml_conf_sections_used) {
7088 +               int new_size = xml_conf_sections_used + 10;
7089 +               void *new_ptr = realloc(xml_conf_sections, sizeof(struct xml_conf_section *) * new_size);
7090 +
7091 +               if (new_ptr) {
7092 +                       xml_conf_sections = new_ptr;
7093 +                       xml_conf_sections_allocated = new_size;
7094 +               }
7095 +               else {
7096 +                       fprintf(stderr, "xml_conf_section_register(): out of memory\n");
7097 +                       return -1;
7098 +               }
7099 +       }
7100 +
7101 +       xml_conf_sections[xml_conf_sections_used++] = section;
7102 +
7103 +       return 0;
7104 +}
7105 +
7106 +int xml_conf_sections_register(struct xml_conf_section *sections[])
7107 +{
7108 +       for ( ; sections && *sections; sections++) {
7109 +               if (0 > xml_conf_section_register(*sections)) {
7110 +                       return -1;
7111 +               }
7112 +       }
7113 +
7114 +       return 0;
7115 +}
7116 +
7117 diff --git a/sapi/cgi/fpm/xml_config.h b/sapi/cgi/fpm/xml_config.h
7118 new file mode 100644
7119 --- /dev/null
7120 +++ b/sapi/cgi/fpm/xml_config.h
7121 @@ -0,0 +1,43 @@
7122 +
7123 +       /* $Id$ */
7124 +       /* (c) 2004-2007 Andrei Nigmatulin */
7125 +
7126 +#ifndef XML_CONFIG_H
7127 +#define XML_CONFIG_H 1
7128 +
7129 +#include <stdint.h>
7130 +
7131 +struct xml_value_parser;
7132 +
7133 +struct xml_value_parser {
7134 +       int type;
7135 +       char *name;
7136 +       char *(*parser)(void **, char *, void *, intptr_t offset);
7137 +       intptr_t offset;
7138 +};
7139 +
7140 +struct xml_conf_section {
7141 +       void *(*conf)();
7142 +       char *path;
7143 +       struct xml_value_parser *parsers;
7144 +};
7145 +
7146 +char *xml_conf_set_slot_boolean(void **conf, char *name, void *value, intptr_t offset);
7147 +char *xml_conf_set_slot_string(void **conf, char *name, void *value, intptr_t offset);
7148 +char *xml_conf_set_slot_integer(void **conf, char *name, void *value, intptr_t offset);
7149 +char *xml_conf_set_slot_time(void **conf, char *name, void *value, intptr_t offset);
7150 +
7151 +int xml_conf_init();
7152 +void xml_conf_clean();
7153 +char *xml_conf_load_file(char *file);
7154 +char *xml_conf_parse_section(void **conf, struct xml_conf_section *section, void *ve);
7155 +int xml_conf_section_register(struct xml_conf_section *section);
7156 +int xml_conf_sections_register(struct xml_conf_section *sections[]);
7157 +
7158 +#define expand_hms(value) (value) / 3600, ((value) % 3600) / 60, (value) % 60
7159 +
7160 +#define expand_dhms(value) (value) / 86400, ((value) % 86400) / 3600, ((value) % 3600) / 60, (value) % 60
7161 +
7162 +enum { XML_CONF_SCALAR = 1, XML_CONF_SUBSECTION = 2 };
7163 +
7164 +#endif
7165 diff --git a/sapi/cgi/fpm/zlog.c b/sapi/cgi/fpm/zlog.c
7166 new file mode 100644
7167 --- /dev/null
7168 +++ b/sapi/cgi/fpm/zlog.c
7169 @@ -0,0 +1,113 @@
7170 +
7171 +       /* $Id$ */
7172 +       /* (c) 2004-2007 Andrei Nigmatulin */
7173 +
7174 +#include "fpm_config.h"
7175 +
7176 +#include <stdio.h>
7177 +#include <unistd.h>
7178 +#include <time.h>
7179 +#include <string.h>
7180 +#include <stdarg.h>
7181 +#include <sys/time.h>
7182 +#include <errno.h>
7183 +
7184 +#include "zlog.h"
7185 +
7186 +#define MAX_LINE_LENGTH 1024
7187 +
7188 +static int zlog_fd = -1;
7189 +static int zlog_level = ZLOG_NOTICE;
7190 +
7191 +static const char *level_names[] = {
7192 +       [ZLOG_DEBUG]            = "DEBUG",
7193 +       [ZLOG_NOTICE]           = "NOTICE",
7194 +       [ZLOG_WARNING]          = "WARNING",
7195 +       [ZLOG_ERROR]            = "ERROR",
7196 +       [ZLOG_ALERT]            = "ALERT",
7197 +};
7198 +
7199 +size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len)
7200 +{
7201 +       struct tm t;
7202 +       size_t len;
7203 +
7204 +       len = strftime(timebuf, timebuf_len, "%b %d %H:%M:%S", localtime_r((const time_t *) &tv->tv_sec, &t));
7205 +       len += snprintf(timebuf + len, timebuf_len - len, ".%06d", (int) tv->tv_usec);
7206 +
7207 +       return len;
7208 +}
7209 +
7210 +int zlog_set_fd(int new_fd)
7211 +{
7212 +       int old_fd = zlog_fd;
7213 +       zlog_fd = new_fd;
7214 +
7215 +       return old_fd;
7216 +}
7217 +
7218 +int zlog_set_level(int new_value)
7219 +{
7220 +       int old_value = zlog_level;
7221 +
7222 +       zlog_level = new_value;
7223 +
7224 +       return old_value;
7225 +}
7226 +
7227 +void zlog(const char *function, int line, int flags, const char *fmt, ...)
7228 +{
7229 +       struct timeval tv;
7230 +       char buf[MAX_LINE_LENGTH];
7231 +       const size_t buf_size = MAX_LINE_LENGTH;
7232 +       va_list args;
7233 +       size_t len;
7234 +       int truncated = 0;
7235 +       int saved_errno;
7236 +
7237 +       if ((flags & ZLOG_LEVEL_MASK) < zlog_level) {
7238 +               return;
7239 +       }
7240 +
7241 +       saved_errno = errno;
7242 +
7243 +       gettimeofday(&tv, 0);
7244 +
7245 +       len = zlog_print_time(&tv, buf, buf_size);
7246 +
7247 +       len += snprintf(buf + len, buf_size - len, " [%s] %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], function, line);
7248 +
7249 +       if (len > buf_size - 1) {
7250 +               truncated = 1;
7251 +       }
7252 +
7253 +       if (!truncated) {
7254 +               va_start(args, fmt);
7255 +
7256 +               len += vsnprintf(buf + len, buf_size - len, fmt, args);
7257 +
7258 +               va_end(args);
7259 +
7260 +               if (len >= buf_size) {
7261 +                       truncated = 1;
7262 +               }
7263 +       }
7264 +
7265 +       if (!truncated) {
7266 +               if (flags & ZLOG_HAVE_ERRNO) {
7267 +                       len += snprintf(buf + len, buf_size - len, ": %s (%d)", strerror(saved_errno), saved_errno);
7268 +                       if (len >= buf_size) {
7269 +                               truncated = 1;
7270 +                       }
7271 +               }
7272 +       }
7273 +
7274 +       if (truncated) {
7275 +               memcpy(buf + buf_size - sizeof("..."), "...", sizeof("...") - 1);
7276 +               len = buf_size - 1;
7277 +       }
7278 +
7279 +       buf[len++] = '\n';
7280 +
7281 +       write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len);
7282 +}
7283 diff --git a/sapi/cgi/fpm/zlog.h b/sapi/cgi/fpm/zlog.h
7284 new file mode 100644
7285 --- /dev/null
7286 +++ b/sapi/cgi/fpm/zlog.h
7287 @@ -0,0 +1,34 @@
7288 +
7289 +       /* $Id$ */
7290 +       /* (c) 2004-2007 Andrei Nigmatulin */
7291 +
7292 +#ifndef ZLOG_H
7293 +#define ZLOG_H 1
7294 +
7295 +#define ZLOG_STUFF             __func__, __LINE__
7296 +
7297 +struct timeval;
7298 +
7299 +int zlog_set_fd(int new_fd);
7300 +int zlog_set_level(int new_value);
7301 +
7302 +size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len);
7303 +
7304 +void zlog(const char *function, int line, int flags, const char *fmt, ...)
7305 +               __attribute__ ((format(printf,4,5)));
7306 +
7307 +enum {
7308 +       ZLOG_DEBUG                      = 1,
7309 +       ZLOG_NOTICE                     = 2,
7310 +       ZLOG_WARNING            = 3,
7311 +       ZLOG_ERROR                      = 4,
7312 +       ZLOG_ALERT                      = 5,
7313 +};
7314 +
7315 +#define ZLOG_LEVEL_MASK 7
7316 +
7317 +#define ZLOG_HAVE_ERRNO 0x100
7318 +
7319 +#define ZLOG_SYSERROR (ZLOG_ERROR | ZLOG_HAVE_ERRNO)
7320 +
7321 +#endif
This page took 0.703062 seconds and 3 git commands to generate.