292 * Destructor. |
319 * Destructor. |
293 */ |
320 */ |
294 |
321 |
295 function __destruct() |
322 function __destruct() |
296 { |
323 { |
297 if ( !defined('HTTPD_WS_CHILD') && $this->socket_initted ) |
324 if ( !$this->threader->is_child() && $this->socket_initted ) |
298 { |
325 { |
299 if ( function_exists('status') ) |
326 if ( function_exists('status') ) |
300 status('WebServer: destroying socket'); |
327 status('WebServer: destroying socket'); |
301 $this->server->destroy(); |
328 $this->server->destroy(); |
302 |
329 |
303 // tell all children to shut down |
330 // tell all children to shut down |
304 if ( $this->allow_fork ) |
331 if ( $this->allow_fork ) |
305 { |
332 { |
306 if ( function_exists('status') ) |
333 if ( function_exists('status') ) |
307 status('WebServer: asking all children to exit'); |
334 status('WebServer: asking all children to exit'); |
308 $this->send_ipc_event("die _"); |
335 $this->threader->kill_all_children(); |
309 } |
336 } |
310 |
337 } |
311 // that last operation should have been asynchronous, so shut everything down now |
338 } |
312 @socket_shutdown($this->parent_sock); |
339 |
313 @socket_close($this->parent_sock); |
340 /** |
314 } |
341 * Reboot the server. Useful for applying new settings. |
315 else if ( defined('HTTPD_WS_CHILD') ) |
342 * @param string Optional, new IP address to bind to |
316 { |
343 * @param int Optional, new port to bind to |
317 @socket_shutdown($this->child_sock); |
344 * @param bool Optional, whether to allow forking or not |
318 @socket_close($this->child_sock); |
345 */ |
319 } |
346 |
|
347 function reboot($addr = null, $port = null, $allow_fork = null) |
|
348 { |
|
349 if ( function_exists('status') ) |
|
350 status('Reboot request has been received'); |
|
351 |
|
352 $addr = ( !is_string($addr) ) ? $this->bind_address : $addr; |
|
353 $port = ( !is_int($port) ) ? $this->port : $port; |
|
354 $fork = ( !is_bool($allow_fork) ) ? $this->allow_fork : $allow_fork; |
|
355 |
|
356 // |
|
357 // REBOOTING IS A COMPLICATED THING. |
|
358 // We need to ask all children to close any existing connections so |
|
359 // that all relevant socket resources can be freed. Then we need to |
|
360 // call the constructor again to respawn the server, and finally |
|
361 // re-enter the server loop. |
|
362 // |
|
363 // However, reboot() is often called from a PHP-based handler. This |
|
364 // means that some config page probably still needs to be sent. What |
|
365 // we can do here is send an IPC event that fires the actual reboot, |
|
366 // then return to allow the current page to finish up. We also need |
|
367 // to signal the current process to shut down any existing keep- |
|
368 // alive connections. This can be accomplished by setting in_keepalive |
|
369 // to false. |
|
370 // |
|
371 |
|
372 // Kill the entire child after this response is sent |
|
373 $this->in_keepalive = false; |
|
374 |
|
375 // If we're the parent process, we need to know that a reboot event |
|
376 // was fired, and thus the server's main socket needs to be destroyed |
|
377 // and recreated. This is just done with another boolean switch. |
|
378 $this->reboot_sent = true; |
|
379 |
|
380 // this is really to track if there are any children |
|
381 $oldfork = $this->allow_fork; |
|
382 |
|
383 // Set our new server flags |
|
384 $this->bind_address = $addr; |
|
385 $this->port = $port; |
|
386 $this->allow_fork = $fork; |
|
387 |
|
388 // If we're a child, we have to tell the parent what the hell is |
|
389 // going on, and then get out of here as quickly as possible |
|
390 // (and other children should do the same). If this is a child, |
|
391 // fire an IPC reboot event. Else, fire a "die all" event |
|
392 if ( $this->threader->is_child() ) |
|
393 { |
|
394 if ( function_exists('status') ) |
|
395 status('Signaling parent with parameter changes (fork = ' . intval($fork) . ') + reboot request'); |
|
396 // this is the child |
|
397 $this->threader->ipc_send(array( |
|
398 'action' => 'ws_reboot', |
|
399 'addr' => $addr, |
|
400 'port' => $port, |
|
401 'fork' => $fork |
|
402 )); |
|
403 } |
|
404 else if ( !$this->threader->is_child() && $oldfork ) |
|
405 { |
|
406 if ( function_exists('status') ) |
|
407 status('Waiting on all children'); |
|
408 |
|
409 // this is the parent, and there are children present |
|
410 $this->threader->kill_all_children(); |
|
411 |
|
412 // all children are dead, we are ok to respawn |
|
413 $this->respawn(); |
|
414 } |
|
415 else |
|
416 { |
|
417 // this is a childless parent; delay any action until the current |
|
418 // request has been sent (do nothing now) |
|
419 } |
|
420 } |
|
421 |
|
422 /** |
|
423 * Respawns the server. All children should be dead, and any client |
|
424 * connections must be closed. |
|
425 */ |
|
426 |
|
427 function respawn() |
|
428 { |
|
429 $this->reboot_sent = false; |
|
430 |
|
431 if ( function_exists('status') ) |
|
432 status('Respawn event sent'); |
|
433 $this->server->destroy(); |
|
434 unset($this->server); |
|
435 |
|
436 // try to spawn up to 10 times |
|
437 for ( $i = 0; $i < 10; $i++ ) |
|
438 { |
|
439 try |
|
440 { |
|
441 $this->__construct($this->bind_address, $this->port); |
|
442 } |
|
443 catch ( Exception $e ) |
|
444 { |
|
445 if ( $i == 9 ) |
|
446 { |
|
447 if ( function_exists('burnout') ) |
|
448 { |
|
449 burnout("Couldn't respawn because one of the child processes did not die, and thus the port was not freed."); |
|
450 } |
|
451 exit(1); |
|
452 } |
|
453 if ( function_exists('status') ) |
|
454 { |
|
455 status("Respawn failed, retrying in 2 seconds"); |
|
456 } |
|
457 usleep(2000000); |
|
458 continue; |
|
459 } |
|
460 break; |
|
461 } |
|
462 |
|
463 if ( function_exists('status') ) |
|
464 status('Respawn is complete, entering server loop with bind_address = ' . $this->bind_address . ' allow_fork = ' . strval(intval($this->allow_fork))); |
|
465 |
|
466 // all handlers should already be set up, so just break out and we should automatically continue the server loop |
320 } |
467 } |
321 |
468 |
322 /** |
469 /** |
323 * Main server loop |
470 * Main server loop |
324 */ |
471 */ |
325 |
472 |
326 function serve() |
473 function serve() |
327 { |
474 { |
328 // If we're allowed to use multithreading, set up to handle SIGUSR2 which waits on the child |
|
329 if ( function_exists('pcntl_signal') && $this->allow_fork ) |
|
330 { |
|
331 // required for signal handling to work |
|
332 declare(ticks=1); |
|
333 |
|
334 // trap SIGTERM |
|
335 pcntl_signal(SIGUSR2, array(&$this, '_ipc_event')); |
|
336 |
|
337 if ( !($sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP)) ) |
|
338 { |
|
339 throw new Exception("Could not set up private IPC socket. Reason: " . socket_strerror(socket_last_error())); |
|
340 } |
|
341 |
|
342 $this->parent_sock =& $sockets[0]; |
|
343 $this->child_sock =& $sockets[1]; |
|
344 } |
|
345 |
|
346 while ( true ) |
475 while ( true ) |
347 { |
476 { |
|
477 ## |
|
478 ## STAGE 0: CLEANUP FROM PREVIOUS RUN |
|
479 ## |
|
480 |
348 // if this is a child process, we're finished - close up shop |
481 // if this is a child process, we're finished - close up shop |
349 if ( defined('HTTPD_WS_CHILD') && !$this->in_keepalive ) |
482 if ( $this->threader->is_child() && !$this->in_keepalive ) |
350 { |
483 { |
351 if ( function_exists('status') ) |
484 if ( function_exists('status') ) |
352 status('Exiting child process'); |
485 status('Exiting child process'); |
353 |
486 |
354 $remote->destroy(); |
487 $remote->destroy(); |
355 |
488 |
356 // let the parent know that we're out of here |
|
357 $this->send_ipc_event("exit " . getmypid()); |
|
358 |
|
359 // bye |
|
360 exit(0); |
489 exit(0); |
361 } |
490 } |
362 |
491 |
|
492 ## |
|
493 ## STAGE 1: LISTENER AND INIT |
|
494 ## |
|
495 |
363 // wait for connection... |
496 // wait for connection... |
364 // trick from http://us.php.net/manual/en/function.socket-accept.php |
497 if ( !$this->threader->is_child() ) |
365 if ( !defined('HTTPD_WS_CHILD') ) |
|
366 { |
498 { |
367 $remote = $this->server->accept(); |
499 $remote = $this->server->accept(); |
368 } |
500 } |
369 |
501 |
370 if ( !$remote ) |
502 if ( !$remote ) |
374 } |
506 } |
375 |
507 |
376 // fork off if possible |
508 // fork off if possible |
377 if ( function_exists('pcntl_fork') && $this->allow_fork && !$this->in_keepalive ) |
509 if ( function_exists('pcntl_fork') && $this->allow_fork && !$this->in_keepalive ) |
378 { |
510 { |
379 $pid = pcntl_fork(); |
511 if ( $this->threader->fork() == FORK_CHILD ) |
380 if ( $pid == -1 ) |
512 { |
381 { |
513 // this is the child |
382 // do nothing; continue responding to request in single-threaded mode |
514 define('HTTPD_WS_CHILD', 1); |
383 } |
515 } |
384 else if ( $pid ) |
516 else |
385 { |
517 { |
386 // we are the parent, continue listening |
518 // we are the parent, continue listening |
387 $remote->soft_shutdown(); |
519 $remote->soft_shutdown(); |
388 $this->child_list[] = $pid; |
520 $this->child_list[] = $pid; |
389 continue; |
521 continue; |
390 } |
522 } |
391 else |
|
392 { |
|
393 // this is the child |
|
394 define('HTTPD_WS_CHILD', 1); |
|
395 |
|
396 // setup to handle signals |
|
397 if ( function_exists('pcntl_signal') ) |
|
398 { |
|
399 // required for signal handling to work |
|
400 declare(ticks=1); |
|
401 |
|
402 // trap SIGTERM |
|
403 pcntl_signal(SIGUSR2, array(&$this, '_ipc_event')); |
|
404 } |
|
405 } |
|
406 } |
523 } |
407 |
524 |
408 $this->in_keepalive = false; |
525 $this->in_keepalive = false; |
409 $this->headers_sent = false; |
526 $this->headers_sent = false; |
410 $this->in_scriptlet = false; |
527 $this->in_scriptlet = false; |
411 |
528 |
412 // |
529 ## |
413 // READ THE REQUEST |
530 ## STAGE 2: READ REQUEST |
414 // |
531 ## |
415 |
532 |
416 // this is a complicated situation because we need to keep enough ticks going to properly handle |
533 // this is a complicated situation because we need to keep enough ticks going to properly handle |
417 // signals, meaning we can't use stream_set_timeout() and instead need to rely on our own timing |
534 // signals, meaning we can't use stream_set_timeout() and instead need to rely on our own timing |
418 // logic. setting the timeout to a short period, say 200,000 usec, we can minimize CPU usage and |
535 // logic. setting the timeout to a short period, say 200,000 usec, we can minimize CPU usage and |
419 // have a good response time. |
536 // have a good response time. |
513 $postdata = ''; |
634 $postdata = ''; |
514 $_POST = array(); |
635 $_POST = array(); |
515 $_FILES = array(); |
636 $_FILES = array(); |
516 if ( $method == 'POST' ) |
637 if ( $method == 'POST' ) |
517 { |
638 { |
518 // read POST data |
639 $this->parse_post_data($remote); |
519 if ( isset($_SERVER['HTTP_CONTENT_TYPE']) && preg_match('#^multipart/form-data; ?boundary=([A-z0-9_-]+)$#i', $_SERVER['HTTP_CONTENT_TYPE'], $match) ) |
|
520 { |
|
521 // this is a multipart request |
|
522 $boundary =& $match[1]; |
|
523 $mode = 'data'; |
|
524 $last_line = ''; |
|
525 $i = 0; |
|
526 while ( $data = $remote->read_normal(8388608) ) |
|
527 { |
|
528 $data_trim = trim($data, "\r\n"); |
|
529 if ( $mode != 'data' ) |
|
530 { |
|
531 $data = str_replace("\r", '', $data); |
|
532 } |
|
533 if ( ( $data_trim === "--$boundary" || $data_trim === "--$boundary--" ) && $i > 0 ) |
|
534 { |
|
535 // trim off the first LF and the last CRLF |
|
536 $currval_data = substr($currval_data, 1, strlen($currval_data)-3); |
|
537 |
|
538 // this is the end of a part of the message; parse it into either $_POST or $_FILES |
|
539 if ( is_string($have_a_file) ) |
|
540 { |
|
541 |
|
542 // write data to a temporary file |
|
543 $errcode = UPLOAD_ERR_OK; |
|
544 $tempfile = tempnam('phpupload', ( function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp' )); |
|
545 if ( $fh = @fopen($tempfile, 'w') ) |
|
546 { |
|
547 if ( empty($have_a_file) ) |
|
548 { |
|
549 $errcode = UPLOAD_ERR_NO_FILE; |
|
550 } |
|
551 else |
|
552 { |
|
553 fwrite($fh, $currval_data); |
|
554 } |
|
555 fclose($fh); |
|
556 } |
|
557 else |
|
558 { |
|
559 $errcode = UPLOAD_ERR_CANT_WRITE; |
|
560 } |
|
561 $_FILES[$currval_name] = array( |
|
562 'name' => $have_a_file, |
|
563 'type' => $currval_type, |
|
564 'size' => filesize($tempfile), |
|
565 'tmp_name' => $tempfile, |
|
566 'error' => $errcode |
|
567 ); |
|
568 } |
|
569 else |
|
570 { |
|
571 $_POST[$currval_name] = $currval_data; |
|
572 } |
|
573 } |
|
574 |
|
575 if ( $data_trim === "--$boundary" ) |
|
576 { |
|
577 // switch from "data" mode to "headers" mode |
|
578 $currval_name = ''; |
|
579 $currval_data = ''; |
|
580 $currval_type = ''; |
|
581 $have_a_file = false; |
|
582 $mode = 'headers'; |
|
583 } |
|
584 else if ( $data_trim === "--$boundary--" ) |
|
585 { |
|
586 // end of request |
|
587 break; |
|
588 } |
|
589 else if ( ( empty($data_trim) && empty($last_line) ) && $mode == 'headers' ) |
|
590 { |
|
591 // start of data |
|
592 $mode = 'data'; |
|
593 } |
|
594 else if ( $mode == 'headers' ) |
|
595 { |
|
596 // read header |
|
597 // we're only looking for Content-Disposition and Content-Type |
|
598 if ( preg_match('#^Content-Disposition: form-data; name="([^"\a\t\r\n]+)"(?:; filename="([^"\a\t\r\n]+)")?#i', $data_trim, $match) ) |
|
599 { |
|
600 // content-disposition header, set name and mode. |
|
601 $currval_name = $match[1]; |
|
602 if ( isset($match[2]) ) |
|
603 { |
|
604 $have_a_file = $match[2]; |
|
605 } |
|
606 else |
|
607 { |
|
608 $have_a_file = false; |
|
609 } |
|
610 } |
|
611 else if ( preg_match('#^Content-Type: ([a-z0-9-]+/[a-z0-9/-]+)$#i', $data_trim, $match) ) |
|
612 { |
|
613 $currval_type = $match[1]; |
|
614 } |
|
615 } |
|
616 else if ( $mode == 'data' ) |
|
617 { |
|
618 $currval_data .= $data; |
|
619 } |
|
620 $last_line = $data_trim; |
|
621 $i++; |
|
622 } |
|
623 } |
|
624 else |
|
625 { |
|
626 if ( isset($_SERVER['HTTP_CONTENT_LENGTH']) ) |
|
627 { |
|
628 $postdata = $remote->read_binary(intval($_SERVER['HTTP_CONTENT_LENGTH'])); |
|
629 } |
|
630 else |
|
631 { |
|
632 $postdata = $remote->read_normal(8388608); |
|
633 } |
|
634 if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]-]+)(=[^ &]+)?/', $postdata, $matches) ) |
|
635 { |
|
636 if ( isset($matches[1]) ) |
|
637 { |
|
638 foreach ( $matches[0] as $i => $_ ) |
|
639 { |
|
640 $_POST[$matches[2][$i]] = ( !empty($matches[3][$i]) ) ? urldecode(substr($matches[3][$i], 1)) : true; |
|
641 } |
|
642 } |
|
643 } |
|
644 } |
|
645 } |
640 } |
646 |
641 |
647 // parse URI |
642 // parse URI |
648 $params = ''; |
643 $params = ''; |
649 if ( strstr($uri, '?') ) |
644 if ( strstr($uri, '?') ) |
743 @unlink($file_data['tmp_name']); |
752 @unlink($file_data['tmp_name']); |
744 } |
753 } |
745 } |
754 } |
746 } |
755 } |
747 |
756 |
748 if ( !$this->in_keepalive && defined('HTTPD_WS_CHILD') ) |
757 if ( !$this->in_keepalive && $this->threader->is_child() ) |
749 { |
758 { |
750 // connection: close |
759 // connection: close |
751 // continue on to the shutdown handler |
760 // continue on to the shutdown handler |
752 continue; |
761 continue; |
753 } |
762 } |
754 else if ( defined('HTTPD_WS_CHILD') ) |
763 else if ( $this->threader->is_child() ) |
755 { |
764 { |
756 // if ( defined('HTTPD_WS_CHILD') ) |
765 // if ( $this->threader->is_child() ) |
757 // status('Continuing connection'); |
766 // status('Continuing connection'); |
758 // $remote->write("\r\n\r\n"); |
767 // $remote->write("\r\n\r\n"); |
759 $last_finish_time = microtime(true); |
768 $last_finish_time = microtime(true); |
760 } |
769 } |
761 else |
770 else |
762 { |
771 { |
|
772 // standalone process |
763 $remote->destroy(); |
773 $remote->destroy(); |
|
774 |
|
775 // if a reboot was fired and we're running in single-process mode, now is the time to respawn |
|
776 if ( !$this->threader->is_child() && $this->reboot_sent ) |
|
777 { |
|
778 $this->respawn(); |
|
779 } |
|
780 } |
|
781 } |
|
782 } |
|
783 |
|
784 /** |
|
785 * Parse POST data and format $_POST and $_FILES. |
|
786 * @param resource Remote socket |
|
787 */ |
|
788 |
|
789 function parse_post_data($remote) |
|
790 { |
|
791 $postdata = ''; |
|
792 |
|
793 // read POST data |
|
794 if ( isset($_SERVER['HTTP_CONTENT_TYPE']) && preg_match('#^multipart/form-data; ?boundary=([A-z0-9_-]+)$#i', $_SERVER['HTTP_CONTENT_TYPE'], $match) ) |
|
795 { |
|
796 // this is a multipart request |
|
797 $boundary =& $match[1]; |
|
798 $mode = 'data'; |
|
799 $last_line = ''; |
|
800 $i = 0; |
|
801 while ( $data = $remote->read_normal(8388608) ) |
|
802 { |
|
803 $data_trim = trim($data, "\r\n"); |
|
804 if ( $mode != 'data' ) |
|
805 { |
|
806 $data = str_replace("\r", '', $data); |
|
807 } |
|
808 if ( ( $data_trim === "--$boundary" || $data_trim === "--$boundary--" ) && $i > 0 ) |
|
809 { |
|
810 // trim off the first LF and the last CRLF |
|
811 if ( HTTPD_SOCKET_LAYER == 'Raw' ) |
|
812 $currval_data = substr($currval_data, 1, strlen($currval_data)-3); |
|
813 else |
|
814 $currval_data = substr($currval_data, 0, strlen($currval_data)-2); |
|
815 |
|
816 // this is the end of a part of the message; parse it into either $_POST or $_FILES |
|
817 if ( is_string($have_a_file) ) |
|
818 { |
|
819 // write data to a temporary file |
|
820 $errcode = UPLOAD_ERR_OK; |
|
821 $tempfile = tempnam('phpupload', ( function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp' )); |
|
822 if ( $fh = @fopen($tempfile, 'w') ) |
|
823 { |
|
824 if ( empty($have_a_file) ) |
|
825 { |
|
826 $errcode = UPLOAD_ERR_NO_FILE; |
|
827 } |
|
828 else |
|
829 { |
|
830 fwrite($fh, $currval_data); |
|
831 } |
|
832 fclose($fh); |
|
833 } |
|
834 else |
|
835 { |
|
836 $errcode = UPLOAD_ERR_CANT_WRITE; |
|
837 } |
|
838 $_FILES[$currval_name] = array( |
|
839 'name' => $have_a_file, |
|
840 'type' => $currval_type, |
|
841 'size' => filesize($tempfile), |
|
842 'tmp_name' => $tempfile, |
|
843 'error' => $errcode |
|
844 ); |
|
845 } |
|
846 else |
|
847 { |
|
848 if ( preg_match('/\[\]$/', $currval_name) ) |
|
849 { |
|
850 if ( !isset($_POST[$currval_name]) || ( isset($_POST[$currval_name]) && !is_array($_POST[$currval_name]) ) ) |
|
851 $_POST[$currval_name] = array(); |
|
852 |
|
853 $_POST[$currval_name][] = $currval_data; |
|
854 } |
|
855 else |
|
856 { |
|
857 $_POST[$currval_name] = $currval_data; |
|
858 } |
|
859 } |
|
860 } |
|
861 |
|
862 if ( $data_trim === "--$boundary" ) |
|
863 { |
|
864 // switch from "data" mode to "headers" mode |
|
865 $currval_name = ''; |
|
866 $currval_data = ''; |
|
867 $currval_type = ''; |
|
868 $have_a_file = false; |
|
869 $mode = 'headers'; |
|
870 } |
|
871 else if ( $data_trim === "--$boundary--" ) |
|
872 { |
|
873 // end of request |
|
874 break; |
|
875 } |
|
876 else if ( ( empty($data_trim) && ( ( HTTPD_SOCKET_LAYER == 'Raw' && empty($last_line) ) || HTTPD_SOCKET_LAYER != 'Raw' ) ) && $mode == 'headers' ) |
|
877 { |
|
878 // start of data |
|
879 $mode = 'data'; |
|
880 } |
|
881 else if ( $mode == 'headers' ) |
|
882 { |
|
883 // read header |
|
884 // we're only looking for Content-Disposition and Content-Type |
|
885 if ( preg_match('#^Content-Disposition: form-data; name="([^"\a\t\r\n]+)"(?:; filename="([^"\a\t\r\n]+)")?#i', $data_trim, $match) ) |
|
886 { |
|
887 // content-disposition header, set name and mode. |
|
888 $currval_name = $match[1]; |
|
889 if ( isset($match[2]) ) |
|
890 { |
|
891 $have_a_file = $match[2]; |
|
892 } |
|
893 else |
|
894 { |
|
895 $have_a_file = false; |
|
896 } |
|
897 } |
|
898 else if ( preg_match('#^Content-Type: ([a-z0-9-]+/[a-z0-9/-]+)$#i', $data_trim, $match) ) |
|
899 { |
|
900 $currval_type = $match[1]; |
|
901 } |
|
902 } |
|
903 else if ( $mode == 'data' ) |
|
904 { |
|
905 $currval_data .= $data; |
|
906 } |
|
907 $last_line = $data_trim; |
|
908 $i++; |
|
909 } |
|
910 } |
|
911 else |
|
912 { |
|
913 if ( isset($_SERVER['HTTP_CONTENT_LENGTH']) ) |
|
914 { |
|
915 $postdata = $remote->read_binary(intval($_SERVER['HTTP_CONTENT_LENGTH'])); |
|
916 } |
|
917 else |
|
918 { |
|
919 $postdata = $remote->read_normal(8388608); |
|
920 } |
|
921 if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]%-]+)(=[^ &]+)?/i', $postdata, $matches) ) |
|
922 { |
|
923 if ( isset($matches[1]) ) |
|
924 { |
|
925 foreach ( $matches[0] as $i => $_ ) |
|
926 { |
|
927 $currval_name =& $matches[2][$i]; |
|
928 $currval_data = ( !empty($matches[3][$i]) ) ? urldecode(substr($matches[3][$i], 1)) : true; |
|
929 $currval_name = urldecode($currval_name); |
|
930 |
|
931 if ( preg_match('/\[\]$/', $currval_name) ) |
|
932 { |
|
933 $basename = preg_replace('/\[\]$/', '', $currval_name); |
|
934 if ( !isset($_POST[$basename]) || ( isset($_POST[$basename]) && !is_array($_POST[$basename]) ) ) |
|
935 $_POST[$basename] = array(); |
|
936 |
|
937 $_POST[$basename][] = $currval_data; |
|
938 } |
|
939 else |
|
940 { |
|
941 $_POST[$currval_name] = $currval_data; |
|
942 } |
|
943 } |
|
944 } |
764 } |
945 } |
765 } |
946 } |
766 } |
947 } |
767 |
948 |
768 /** |
949 /** |
1623 * Handle an IPC event. Called only upon SIGUSR2. |
1811 * Handle an IPC event. Called only upon SIGUSR2. |
1624 */ |
1812 */ |
1625 |
1813 |
1626 function _ipc_event() |
1814 function _ipc_event() |
1627 { |
1815 { |
1628 $pid = getmypid() . ':' . $this->parent_pid; |
1816 /* |
1629 |
1817 case 'set_addr': |
1630 // decide which socket to use |
1818 $this->bind_address = $param; |
1631 if ( defined('HTTPD_WS_CHILD') ) |
1819 break; |
1632 $sock =& $this->parent_sock; |
1820 case 'set_port': |
1633 else |
1821 $this->port = intval($param); |
1634 $sock =& $this->child_sock; |
1822 break; |
1635 |
1823 case 'set_fork': |
1636 // try to read the event |
1824 $this->allow_fork = ( $param == '1' ); |
1637 // this sometimes gets hung up because socket_set_timeout() doesn't seem to work on its own set of |
1825 break; |
1638 // functions (it only works on PHP's normal streams) |
1826 case 'reboot': |
1639 if ( $line = @fgets($sock, 1024) ) |
1827 if ( !$this->threader->is_child() ) |
1640 { |
1828 { |
1641 $line = trim($line); |
1829 list(, $addr, $port, $fork) = explode(' ', $line); |
1642 list($action, $param) = explode(' ', $line); |
1830 $fork = ( $fork === '1' ); |
1643 switch($action) |
1831 $this->reboot($addr, intval($port), $fork); |
1644 { |
1832 } |
1645 case 'exit': |
1833 break; |
1646 // this is to prevent zombie children |
1834 default: |
1647 pcntl_waitpid(intval($param), $status); |
1835 if ( isset($this->ipc_handlers[$action]) ) |
1648 // we know this child is dead now, remove them from the list |
|
1649 foreach ( $this->child_list as $i => $pid ) |
|
1650 { |
1836 { |
1651 if ( $pid === intval($param) ) |
1837 @call_user_func($this->ipc_handlers[$action], $line); |
1652 { |
|
1653 unset($this->child_list[$i]); |
|
1654 $this->child_list = array_values($this->child_list); |
|
1655 break; |
|
1656 } |
|
1657 } |
1838 } |
1658 break; |
1839 break; |
1659 case 'die': |
1840 } |
1660 // only do this if this is a child (both security and design) |
1841 */ |
1661 if ( defined('HTTPD_WS_CHILD') ) |
1842 } |
1662 { |
|
1663 if ( function_exists('status') ) |
|
1664 { |
|
1665 status('Received shutdown request, complying'); |
|
1666 } |
|
1667 $this->send_ipc_event("exit " . getmypid()); |
|
1668 exit(0); |
|
1669 } |
|
1670 break; |
|
1671 default: |
|
1672 break; |
|
1673 } |
|
1674 } |
|
1675 } |
|
1676 |
|
1677 /** |
|
1678 * Send an IPC event. |
|
1679 * @param string Data to write to the socket, newline will be added automatically |
|
1680 */ |
|
1681 |
|
1682 function send_ipc_event($data) |
|
1683 { |
|
1684 if ( defined('HTTPD_WS_CHILD') ) |
|
1685 $sock =& $this->parent_sock; |
|
1686 else |
|
1687 $sock =& $this->child_sock; |
|
1688 |
|
1689 $data = rtrim($data, "\r\n") . "\n"; |
|
1690 @fwrite($sock, $data); |
|
1691 |
|
1692 // if we're a child, signal the parent |
|
1693 if ( defined('HTTPD_WS_CHILD') ) |
|
1694 { |
|
1695 posix_kill($this->parent_pid, SIGUSR2); |
|
1696 } |
|
1697 // if we're the parent, signal all children |
|
1698 else |
|
1699 { |
|
1700 foreach ( $this->child_list as $pid ) |
|
1701 { |
|
1702 posix_kill($pid, SIGUSR2); |
|
1703 } |
|
1704 } |
|
1705 } |
|
1706 |
|
1707 } |
1843 } |
1708 |
1844 |
1709 /** |
1845 /** |
1710 * Socket abstraction layer - low-level socket functions (socket_*) |
1846 * Socket abstraction layer - low-level socket functions (socket_*) |
1711 */ |
1847 */ |