--- a/webserver.php Mon Sep 01 17:03:44 2008 -0400
+++ b/webserver.php Tue Sep 23 23:24:13 2008 -0400
@@ -13,6 +13,8 @@
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
*/
+require('multithreading.php');
+
/**
* Version of the server
* @const string
@@ -60,6 +62,13 @@
var $bind_address = '127.0.0.1';
/**
+ * Port we're listening on
+ * @var int
+ */
+
+ var $port = 8080;
+
+ /**
* Socket abstraction object
* @var object
*/
@@ -130,6 +139,13 @@
var $allow_fork = true;
/**
+ * Multi-threading manager.
+ * @var object
+ */
+
+ var $threader = false;
+
+ /**
* Keep-alive support uses this to track what the client requested.
* Only used if $allow_fork is set to true.
* @var bool
@@ -180,6 +196,14 @@
var $parent_pid = 0;
/**
+ * List of IPC request handlers
+ * @var array
+ * @access private
+ */
+
+ var $ipc_handlers = array();
+
+ /**
* Sockets for parent and child to communicate
* @var resource
* @var resource
@@ -189,6 +213,13 @@
var $child_sock = null;
/**
+ * Switched on when a graceful reboot event is sent.
+ * @var bool
+ */
+
+ var $reboot_sent = false;
+
+ /**
* Constructor.
* @param string IPv4 address to bind to
* @param int Port number
@@ -201,12 +232,6 @@
@set_time_limit(0);
@ini_set('memory_limit', '128M');
- // do we have socket functions?
- if ( !function_exists('socket_create') )
- {
- burnout('System does not support socket functions. Please rebuild your PHP or install an appropriate extension.');
- }
-
// make sure we're not running as root
// note that if allow_root is true, you must specify a UID/GID (or user/group) to switch to once the socket is bound
$allow_root = ( $port < 1024 ) ? true : false;
@@ -275,8 +300,10 @@
}
$this->bind_address = $address;
+ $this->port = $port;
$this->server_string = "PhpHttpd/" . HTTPD_VERSION . " PHP/" . PHP_VERSION . "\r\n";
$this->parent_pid = getmypid();
+ $this->threader = new Threader();
// create a UUID
$uuid_base = md5(microtime() . ( function_exists('mt_rand') ? mt_rand() : rand() ));
@@ -294,7 +321,7 @@
function __destruct()
{
- if ( !defined('HTTPD_WS_CHILD') && $this->socket_initted )
+ if ( !$this->threader->is_child() && $this->socket_initted )
{
if ( function_exists('status') )
status('WebServer: destroying socket');
@@ -305,18 +332,138 @@
{
if ( function_exists('status') )
status('WebServer: asking all children to exit');
- $this->send_ipc_event("die _");
+ $this->threader->kill_all_children();
}
+ }
+ }
+
+ /**
+ * Reboot the server. Useful for applying new settings.
+ * @param string Optional, new IP address to bind to
+ * @param int Optional, new port to bind to
+ * @param bool Optional, whether to allow forking or not
+ */
+
+ function reboot($addr = null, $port = null, $allow_fork = null)
+ {
+ if ( function_exists('status') )
+ status('Reboot request has been received');
+
+ $addr = ( !is_string($addr) ) ? $this->bind_address : $addr;
+ $port = ( !is_int($port) ) ? $this->port : $port;
+ $fork = ( !is_bool($allow_fork) ) ? $this->allow_fork : $allow_fork;
+
+ //
+ // REBOOTING IS A COMPLICATED THING.
+ // We need to ask all children to close any existing connections so
+ // that all relevant socket resources can be freed. Then we need to
+ // call the constructor again to respawn the server, and finally
+ // re-enter the server loop.
+ //
+ // However, reboot() is often called from a PHP-based handler. This
+ // means that some config page probably still needs to be sent. What
+ // we can do here is send an IPC event that fires the actual reboot,
+ // then return to allow the current page to finish up. We also need
+ // to signal the current process to shut down any existing keep-
+ // alive connections. This can be accomplished by setting in_keepalive
+ // to false.
+ //
+
+ // Kill the entire child after this response is sent
+ $this->in_keepalive = false;
+
+ // If we're the parent process, we need to know that a reboot event
+ // was fired, and thus the server's main socket needs to be destroyed
+ // and recreated. This is just done with another boolean switch.
+ $this->reboot_sent = true;
+
+ // this is really to track if there are any children
+ $oldfork = $this->allow_fork;
+
+ // Set our new server flags
+ $this->bind_address = $addr;
+ $this->port = $port;
+ $this->allow_fork = $fork;
+
+ // If we're a child, we have to tell the parent what the hell is
+ // going on, and then get out of here as quickly as possible
+ // (and other children should do the same). If this is a child,
+ // fire an IPC reboot event. Else, fire a "die all" event
+ if ( $this->threader->is_child() )
+ {
+ if ( function_exists('status') )
+ status('Signaling parent with parameter changes (fork = ' . intval($fork) . ') + reboot request');
+ // this is the child
+ $this->threader->ipc_send(array(
+ 'action' => 'ws_reboot',
+ 'addr' => $addr,
+ 'port' => $port,
+ 'fork' => $fork
+ ));
+ }
+ else if ( !$this->threader->is_child() && $oldfork )
+ {
+ if ( function_exists('status') )
+ status('Waiting on all children');
- // that last operation should have been asynchronous, so shut everything down now
- @socket_shutdown($this->parent_sock);
- @socket_close($this->parent_sock);
+ // this is the parent, and there are children present
+ $this->threader->kill_all_children();
+
+ // all children are dead, we are ok to respawn
+ $this->respawn();
+ }
+ else
+ {
+ // this is a childless parent; delay any action until the current
+ // request has been sent (do nothing now)
}
- else if ( defined('HTTPD_WS_CHILD') )
+ }
+
+ /**
+ * Respawns the server. All children should be dead, and any client
+ * connections must be closed.
+ */
+
+ function respawn()
+ {
+ $this->reboot_sent = false;
+
+ if ( function_exists('status') )
+ status('Respawn event sent');
+ $this->server->destroy();
+ unset($this->server);
+
+ // try to spawn up to 10 times
+ for ( $i = 0; $i < 10; $i++ )
{
- @socket_shutdown($this->child_sock);
- @socket_close($this->child_sock);
+ try
+ {
+ $this->__construct($this->bind_address, $this->port);
+ }
+ catch ( Exception $e )
+ {
+ if ( $i == 9 )
+ {
+ if ( function_exists('burnout') )
+ {
+ burnout("Couldn't respawn because one of the child processes did not die, and thus the port was not freed.");
+ }
+ exit(1);
+ }
+ if ( function_exists('status') )
+ {
+ status("Respawn failed, retrying in 2 seconds");
+ }
+ usleep(2000000);
+ continue;
+ }
+ break;
}
+
+ if ( function_exists('status') )
+ status('Respawn is complete, entering server loop with bind_address = ' . $this->bind_address . ' allow_fork = ' . strval(intval($this->allow_fork)));
+
+ // all handlers should already be set up, so just break out and we should automatically continue the server loop
}
/**
@@ -325,44 +472,29 @@
function serve()
{
- // If we're allowed to use multithreading, set up to handle SIGUSR2 which waits on the child
- if ( function_exists('pcntl_signal') && $this->allow_fork )
- {
- // required for signal handling to work
- declare(ticks=1);
-
- // trap SIGTERM
- pcntl_signal(SIGUSR2, array(&$this, '_ipc_event'));
-
- if ( !($sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP)) )
- {
- throw new Exception("Could not set up private IPC socket. Reason: " . socket_strerror(socket_last_error()));
- }
-
- $this->parent_sock =& $sockets[0];
- $this->child_sock =& $sockets[1];
- }
-
while ( true )
{
+ ##
+ ## STAGE 0: CLEANUP FROM PREVIOUS RUN
+ ##
+
// if this is a child process, we're finished - close up shop
- if ( defined('HTTPD_WS_CHILD') && !$this->in_keepalive )
+ if ( $this->threader->is_child() && !$this->in_keepalive )
{
if ( function_exists('status') )
status('Exiting child process');
$remote->destroy();
- // let the parent know that we're out of here
- $this->send_ipc_event("exit " . getmypid());
-
- // bye
exit(0);
}
+ ##
+ ## STAGE 1: LISTENER AND INIT
+ ##
+
// wait for connection...
- // trick from http://us.php.net/manual/en/function.socket-accept.php
- if ( !defined('HTTPD_WS_CHILD') )
+ if ( !$this->threader->is_child() )
{
$remote = $this->server->accept();
}
@@ -376,42 +508,27 @@
// fork off if possible
if ( function_exists('pcntl_fork') && $this->allow_fork && !$this->in_keepalive )
{
- $pid = pcntl_fork();
- if ( $pid == -1 )
+ if ( $this->threader->fork() == FORK_CHILD )
{
- // do nothing; continue responding to request in single-threaded mode
+ // this is the child
+ define('HTTPD_WS_CHILD', 1);
}
- else if ( $pid )
+ else
{
// we are the parent, continue listening
$remote->soft_shutdown();
$this->child_list[] = $pid;
continue;
}
- else
- {
- // this is the child
- define('HTTPD_WS_CHILD', 1);
-
- // setup to handle signals
- if ( function_exists('pcntl_signal') )
- {
- // required for signal handling to work
- declare(ticks=1);
-
- // trap SIGTERM
- pcntl_signal(SIGUSR2, array(&$this, '_ipc_event'));
- }
- }
}
$this->in_keepalive = false;
$this->headers_sent = false;
$this->in_scriptlet = false;
- //
- // READ THE REQUEST
- //
+ ##
+ ## STAGE 2: READ REQUEST
+ ##
// this is a complicated situation because we need to keep enough ticks going to properly handle
// signals, meaning we can't use stream_set_timeout() and instead need to rely on our own timing
@@ -427,7 +544,7 @@
if ( $start_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) || $remote->is_eof() )
{
// request expired -- end the process here
- if ( !defined('HTTPD_WS_CHILD') )
+ if ( !$this->threader->is_child() )
$remote->destroy();
continue 2;
@@ -445,10 +562,14 @@
$last_line = $line;
}
+ ##
+ ## STAGE 3: PARSE REQUEST AND HEADERS
+ ##
+
// parse request
$client_headers = trim($client_headers);
- if ( isset($last_finish_time) && empty($client_headers) && defined('HTTPD_WS_CHILD') && $last_finish_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) )
+ if ( isset($last_finish_time) && empty($client_headers) && $this->threader->is_child() && $last_finish_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) )
{
status('[debug] keep-alive connection timed out (checkpoint 2)');
continue; // will jump back to the start of the loop and kill the child process
@@ -466,7 +587,7 @@
$method =& $match[1];
$uri =& $match[2];
- // set client headers
+ // set client header SERVER variables
foreach ( $_SERVER as $key => $_ )
{
if ( preg_match('/^HTTP_/', $key) )
@@ -482,7 +603,7 @@
}
// enable keep-alive if requested
- if ( isset($_SERVER['HTTP_CONNECTION']) && defined('HTTPD_WS_CHILD') )
+ if ( isset($_SERVER['HTTP_CONNECTION']) && $this->threader->is_child() )
{
$this->in_keepalive = ( strtolower($_SERVER['HTTP_CONNECTION']) === 'keep-alive' );
}
@@ -515,133 +636,7 @@
$_FILES = array();
if ( $method == 'POST' )
{
- // read POST data
- if ( isset($_SERVER['HTTP_CONTENT_TYPE']) && preg_match('#^multipart/form-data; ?boundary=([A-z0-9_-]+)$#i', $_SERVER['HTTP_CONTENT_TYPE'], $match) )
- {
- // this is a multipart request
- $boundary =& $match[1];
- $mode = 'data';
- $last_line = '';
- $i = 0;
- while ( $data = $remote->read_normal(8388608) )
- {
- $data_trim = trim($data, "\r\n");
- if ( $mode != 'data' )
- {
- $data = str_replace("\r", '', $data);
- }
- if ( ( $data_trim === "--$boundary" || $data_trim === "--$boundary--" ) && $i > 0 )
- {
- // trim off the first LF and the last CRLF
- $currval_data = substr($currval_data, 1, strlen($currval_data)-3);
-
- // this is the end of a part of the message; parse it into either $_POST or $_FILES
- if ( is_string($have_a_file) )
- {
-
- // write data to a temporary file
- $errcode = UPLOAD_ERR_OK;
- $tempfile = tempnam('phpupload', ( function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp' ));
- if ( $fh = @fopen($tempfile, 'w') )
- {
- if ( empty($have_a_file) )
- {
- $errcode = UPLOAD_ERR_NO_FILE;
- }
- else
- {
- fwrite($fh, $currval_data);
- }
- fclose($fh);
- }
- else
- {
- $errcode = UPLOAD_ERR_CANT_WRITE;
- }
- $_FILES[$currval_name] = array(
- 'name' => $have_a_file,
- 'type' => $currval_type,
- 'size' => filesize($tempfile),
- 'tmp_name' => $tempfile,
- 'error' => $errcode
- );
- }
- else
- {
- $_POST[$currval_name] = $currval_data;
- }
- }
-
- if ( $data_trim === "--$boundary" )
- {
- // switch from "data" mode to "headers" mode
- $currval_name = '';
- $currval_data = '';
- $currval_type = '';
- $have_a_file = false;
- $mode = 'headers';
- }
- else if ( $data_trim === "--$boundary--" )
- {
- // end of request
- break;
- }
- else if ( ( empty($data_trim) && empty($last_line) ) && $mode == 'headers' )
- {
- // start of data
- $mode = 'data';
- }
- else if ( $mode == 'headers' )
- {
- // read header
- // we're only looking for Content-Disposition and Content-Type
- if ( preg_match('#^Content-Disposition: form-data; name="([^"\a\t\r\n]+)"(?:; filename="([^"\a\t\r\n]+)")?#i', $data_trim, $match) )
- {
- // content-disposition header, set name and mode.
- $currval_name = $match[1];
- if ( isset($match[2]) )
- {
- $have_a_file = $match[2];
- }
- else
- {
- $have_a_file = false;
- }
- }
- else if ( preg_match('#^Content-Type: ([a-z0-9-]+/[a-z0-9/-]+)$#i', $data_trim, $match) )
- {
- $currval_type = $match[1];
- }
- }
- else if ( $mode == 'data' )
- {
- $currval_data .= $data;
- }
- $last_line = $data_trim;
- $i++;
- }
- }
- else
- {
- if ( isset($_SERVER['HTTP_CONTENT_LENGTH']) )
- {
- $postdata = $remote->read_binary(intval($_SERVER['HTTP_CONTENT_LENGTH']));
- }
- else
- {
- $postdata = $remote->read_normal(8388608);
- }
- if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]-]+)(=[^ &]+)?/', $postdata, $matches) )
- {
- if ( isset($matches[1]) )
- {
- foreach ( $matches[0] as $i => $_ )
- {
- $_POST[$matches[2][$i]] = ( !empty($matches[3][$i]) ) ? urldecode(substr($matches[3][$i], 1)) : true;
- }
- }
- }
- }
+ $this->parse_post_data($remote);
}
// parse URI
@@ -659,6 +654,7 @@
// get remote IP and port
$remote->get_peer_info($_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT']);
+ // process $_GET
$_GET = array();
if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]-]+)(=[^ &]+)?/', $params, $matches) )
{
@@ -671,10 +667,15 @@
}
}
+ // Parse GET, POST, and FILES into multi-depth arrays
$_GET = $this->parse_multi_depth_array($_GET);
$_POST = $this->parse_multi_depth_array($_POST);
$_FILES = $this->parse_multi_depth_array($_FILES);
+ ##
+ ## STAGE 4: HANDLER RESOLUTION
+ ##
+
// init handler
$handler = false;
@@ -731,8 +732,16 @@
}
}
+ ##
+ ## STAGE 5: HANDLER CALL
+ ##
+
$this->send_standard_response($remote, $handler, $uri, $params);
+ ##
+ ## STAGE 6: CLEANUP
+ ##
+
// now that we're done sending the response, delete any temporary uploaded files
if ( !empty($_FILES) )
{
@@ -745,22 +754,194 @@
}
}
- if ( !$this->in_keepalive && defined('HTTPD_WS_CHILD') )
+ if ( !$this->in_keepalive && $this->threader->is_child() )
{
// connection: close
// continue on to the shutdown handler
continue;
}
- else if ( defined('HTTPD_WS_CHILD') )
+ else if ( $this->threader->is_child() )
{
- // if ( defined('HTTPD_WS_CHILD') )
+ // if ( $this->threader->is_child() )
// status('Continuing connection');
// $remote->write("\r\n\r\n");
$last_finish_time = microtime(true);
}
else
{
+ // standalone process
$remote->destroy();
+
+ // if a reboot was fired and we're running in single-process mode, now is the time to respawn
+ if ( !$this->threader->is_child() && $this->reboot_sent )
+ {
+ $this->respawn();
+ }
+ }
+ }
+ }
+
+ /**
+ * Parse POST data and format $_POST and $_FILES.
+ * @param resource Remote socket
+ */
+
+ function parse_post_data($remote)
+ {
+ $postdata = '';
+
+ // read POST data
+ if ( isset($_SERVER['HTTP_CONTENT_TYPE']) && preg_match('#^multipart/form-data; ?boundary=([A-z0-9_-]+)$#i', $_SERVER['HTTP_CONTENT_TYPE'], $match) )
+ {
+ // this is a multipart request
+ $boundary =& $match[1];
+ $mode = 'data';
+ $last_line = '';
+ $i = 0;
+ while ( $data = $remote->read_normal(8388608) )
+ {
+ $data_trim = trim($data, "\r\n");
+ if ( $mode != 'data' )
+ {
+ $data = str_replace("\r", '', $data);
+ }
+ if ( ( $data_trim === "--$boundary" || $data_trim === "--$boundary--" ) && $i > 0 )
+ {
+ // trim off the first LF and the last CRLF
+ if ( HTTPD_SOCKET_LAYER == 'Raw' )
+ $currval_data = substr($currval_data, 1, strlen($currval_data)-3);
+ else
+ $currval_data = substr($currval_data, 0, strlen($currval_data)-2);
+
+ // this is the end of a part of the message; parse it into either $_POST or $_FILES
+ if ( is_string($have_a_file) )
+ {
+ // write data to a temporary file
+ $errcode = UPLOAD_ERR_OK;
+ $tempfile = tempnam('phpupload', ( function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp' ));
+ if ( $fh = @fopen($tempfile, 'w') )
+ {
+ if ( empty($have_a_file) )
+ {
+ $errcode = UPLOAD_ERR_NO_FILE;
+ }
+ else
+ {
+ fwrite($fh, $currval_data);
+ }
+ fclose($fh);
+ }
+ else
+ {
+ $errcode = UPLOAD_ERR_CANT_WRITE;
+ }
+ $_FILES[$currval_name] = array(
+ 'name' => $have_a_file,
+ 'type' => $currval_type,
+ 'size' => filesize($tempfile),
+ 'tmp_name' => $tempfile,
+ 'error' => $errcode
+ );
+ }
+ else
+ {
+ if ( preg_match('/\[\]$/', $currval_name) )
+ {
+ if ( !isset($_POST[$currval_name]) || ( isset($_POST[$currval_name]) && !is_array($_POST[$currval_name]) ) )
+ $_POST[$currval_name] = array();
+
+ $_POST[$currval_name][] = $currval_data;
+ }
+ else
+ {
+ $_POST[$currval_name] = $currval_data;
+ }
+ }
+ }
+
+ if ( $data_trim === "--$boundary" )
+ {
+ // switch from "data" mode to "headers" mode
+ $currval_name = '';
+ $currval_data = '';
+ $currval_type = '';
+ $have_a_file = false;
+ $mode = 'headers';
+ }
+ else if ( $data_trim === "--$boundary--" )
+ {
+ // end of request
+ break;
+ }
+ else if ( ( empty($data_trim) && ( ( HTTPD_SOCKET_LAYER == 'Raw' && empty($last_line) ) || HTTPD_SOCKET_LAYER != 'Raw' ) ) && $mode == 'headers' )
+ {
+ // start of data
+ $mode = 'data';
+ }
+ else if ( $mode == 'headers' )
+ {
+ // read header
+ // we're only looking for Content-Disposition and Content-Type
+ if ( preg_match('#^Content-Disposition: form-data; name="([^"\a\t\r\n]+)"(?:; filename="([^"\a\t\r\n]+)")?#i', $data_trim, $match) )
+ {
+ // content-disposition header, set name and mode.
+ $currval_name = $match[1];
+ if ( isset($match[2]) )
+ {
+ $have_a_file = $match[2];
+ }
+ else
+ {
+ $have_a_file = false;
+ }
+ }
+ else if ( preg_match('#^Content-Type: ([a-z0-9-]+/[a-z0-9/-]+)$#i', $data_trim, $match) )
+ {
+ $currval_type = $match[1];
+ }
+ }
+ else if ( $mode == 'data' )
+ {
+ $currval_data .= $data;
+ }
+ $last_line = $data_trim;
+ $i++;
+ }
+ }
+ else
+ {
+ if ( isset($_SERVER['HTTP_CONTENT_LENGTH']) )
+ {
+ $postdata = $remote->read_binary(intval($_SERVER['HTTP_CONTENT_LENGTH']));
+ }
+ else
+ {
+ $postdata = $remote->read_normal(8388608);
+ }
+ if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]%-]+)(=[^ &]+)?/i', $postdata, $matches) )
+ {
+ if ( isset($matches[1]) )
+ {
+ foreach ( $matches[0] as $i => $_ )
+ {
+ $currval_name =& $matches[2][$i];
+ $currval_data = ( !empty($matches[3][$i]) ) ? urldecode(substr($matches[3][$i], 1)) : true;
+ $currval_name = urldecode($currval_name);
+
+ if ( preg_match('/\[\]$/', $currval_name) )
+ {
+ $basename = preg_replace('/\[\]$/', '', $currval_name);
+ if ( !isset($_POST[$basename]) || ( isset($_POST[$basename]) && !is_array($_POST[$basename]) ) )
+ $_POST[$basename] = array();
+
+ $_POST[$basename][] = $currval_data;
+ }
+ else
+ {
+ $_POST[$currval_name] = $currval_data;
+ }
+ }
+ }
}
}
}
@@ -1603,7 +1784,7 @@
{
foreach ( $array as $key => $value )
{
- if ( preg_match('/^([^\[\]]+)\[([^\]]+)\]/', $key, $match) )
+ if ( preg_match('/^([^\[\]]+)\[([^\]]*)\]/', $key, $match) )
{
$parent =& $match[1];
$child =& $match[2];
@@ -1611,7 +1792,14 @@
{
$array[$parent] = array();
}
- $array[$parent][$child] = $value;
+ if ( empty($child) )
+ {
+ $array[$parent][] = $value;
+ }
+ else
+ {
+ $array[$parent][$child] = $value;
+ }
unset($array[$key]);
$array[$parent] = $this->parse_multi_depth_array($array[$parent]);
}
@@ -1625,85 +1813,33 @@
function _ipc_event()
{
- $pid = getmypid() . ':' . $this->parent_pid;
-
- // decide which socket to use
- if ( defined('HTTPD_WS_CHILD') )
- $sock =& $this->parent_sock;
- else
- $sock =& $this->child_sock;
-
- // try to read the event
- // this sometimes gets hung up because socket_set_timeout() doesn't seem to work on its own set of
- // functions (it only works on PHP's normal streams)
- if ( $line = @fgets($sock, 1024) )
- {
- $line = trim($line);
- list($action, $param) = explode(' ', $line);
- switch($action)
+ /*
+ case 'set_addr':
+ $this->bind_address = $param;
+ break;
+ case 'set_port':
+ $this->port = intval($param);
+ break;
+ case 'set_fork':
+ $this->allow_fork = ( $param == '1' );
+ break;
+ case 'reboot':
+ if ( !$this->threader->is_child() )
{
- case 'exit':
- // this is to prevent zombie children
- pcntl_waitpid(intval($param), $status);
- // we know this child is dead now, remove them from the list
- foreach ( $this->child_list as $i => $pid )
+ list(, $addr, $port, $fork) = explode(' ', $line);
+ $fork = ( $fork === '1' );
+ $this->reboot($addr, intval($port), $fork);
+ }
+ break;
+ default:
+ if ( isset($this->ipc_handlers[$action]) )
{
- if ( $pid === intval($param) )
- {
- unset($this->child_list[$i]);
- $this->child_list = array_values($this->child_list);
- break;
- }
+ @call_user_func($this->ipc_handlers[$action], $line);
}
break;
- case 'die':
- // only do this if this is a child (both security and design)
- if ( defined('HTTPD_WS_CHILD') )
- {
- if ( function_exists('status') )
- {
- status('Received shutdown request, complying');
- }
- $this->send_ipc_event("exit " . getmypid());
- exit(0);
- }
- break;
- default:
- break;
}
- }
+ */
}
-
- /**
- * Send an IPC event.
- * @param string Data to write to the socket, newline will be added automatically
- */
-
- function send_ipc_event($data)
- {
- if ( defined('HTTPD_WS_CHILD') )
- $sock =& $this->parent_sock;
- else
- $sock =& $this->child_sock;
-
- $data = rtrim($data, "\r\n") . "\n";
- @fwrite($sock, $data);
-
- // if we're a child, signal the parent
- if ( defined('HTTPD_WS_CHILD') )
- {
- posix_kill($this->parent_pid, SIGUSR2);
- }
- // if we're the parent, signal all children
- else
- {
- foreach ( $this->child_list as $pid )
- {
- posix_kill($pid, SIGUSR2);
- }
- }
- }
-
}
/**
@@ -1717,6 +1853,12 @@
function tcp_listen($address, $port)
{
+ // do we have socket functions?
+ if ( !function_exists('socket_create') )
+ {
+ burnout('System does not support socket functions. Please rebuild your PHP or install an appropriate extension.');
+ }
+
$this->sock = @socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
if ( !$this->sock )
throw new Exception('Could not create socket');
@@ -1823,6 +1965,12 @@
function tcp_listen($address, $port)
{
+ // does PHP support this?
+ if ( !function_exists('stream_socket_server') )
+ {
+ burnout('System does not support stream functions. Please rebuild your PHP or install an appropriate extension.');
+ }
+
$this->sock = @stream_socket_server("tcp://$address:$port", $errno, $errstr);
if ( !$this->sock )
throw new Exception("Could not create the socket: error $errno: $errstr");
@@ -1837,13 +1985,16 @@
{
@stream_socket_shutdown($this->sock, STREAM_SHUT_RDWR);
}
- fclose($this->sock);
+ while ( !@fclose($this->sock) )
+ {
+ usleep(100000);
+ }
}
}
function accept()
{
- // the goal of a custom accept() with *_select() is to tick every 5 seconds to allow signals.
+ // the goal of a custom accept() with *_select() is to tick every 200ms to allow signals.
stream_set_blocking($this->sock, 1);
$timeout = 5;
$selection = @stream_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), $timeout);