332 $this->bind_address = $address; |
339 $this->bind_address = $address; |
333 $this->port = $port; |
340 $this->port = $port; |
334 $this->server_string = "PhpHttpd/" . HTTPD_VERSION . " PHP/" . PHP_VERSION . "\r\n"; |
341 $this->server_string = "PhpHttpd/" . HTTPD_VERSION . " PHP/" . PHP_VERSION . "\r\n"; |
335 $this->parent_pid = getmypid(); |
342 $this->parent_pid = getmypid(); |
336 $this->threader = new Threader(); |
343 $this->threader = new Threader(); |
337 $this->threader->ipc_register('ws_reboot', array(&$this, 'reboot')); |
344 $this->threader->ipc_register('ws_reboot', array(&$this, 'reboot_ipc')); |
338 |
345 |
339 // create a UUID |
346 // create a UUID |
340 $uuid_base = md5(microtime() . ( function_exists('mt_rand') ? mt_rand() : rand() )); |
347 $uuid_base = md5(microtime() . ( function_exists('mt_rand') ? mt_rand() : rand() )); |
341 $this->uuid = substr($uuid_base, 0, 8) . '-' . |
348 $this->uuid = substr($uuid_base, 0, 8) . '-' . |
342 substr($uuid_base, 8, 4) . '-' . |
349 substr($uuid_base, 8, 4) . '-' . |
382 function reboot($addr = null, $port = null, $allow_fork = null) |
389 function reboot($addr = null, $port = null, $allow_fork = null) |
383 { |
390 { |
384 if ( function_exists('status') ) |
391 if ( function_exists('status') ) |
385 status('Reboot request has been received'); |
392 status('Reboot request has been received'); |
386 |
393 |
387 $addr = ( !is_string($addr) ) ? $this->bind_address : $addr; |
394 // The goal of the reboot() function is to only initiate the reboot - not |
388 $port = ( !is_int($port) ) ? $this->port : $port; |
395 // actually start it. This is because reboot() is often called from signal |
389 $fork = ( !is_bool($allow_fork) ) ? $this->allow_fork : $allow_fork; |
396 // handlers, thus trying to kill all children can result in a lockup. |
390 |
397 |
391 // |
|
392 // REBOOTING IS A COMPLICATED THING. |
|
393 // We need to ask all children to close any existing connections so |
|
394 // that all relevant socket resources can be freed. Then we need to |
|
395 // call the constructor again to respawn the server, and finally |
|
396 // re-enter the server loop. |
|
397 // |
|
398 // However, reboot() is often called from a PHP-based handler. This |
|
399 // means that some config page probably still needs to be sent. What |
|
400 // we can do here is send an IPC event that fires the actual reboot, |
|
401 // then return to allow the current page to finish up. We also need |
|
402 // to signal the current process to shut down any existing keep- |
|
403 // alive connections. This can be accomplished by setting in_keepalive |
|
404 // to false. |
|
405 // |
|
406 |
|
407 // Kill the entire child after this response is sent |
|
408 $this->in_keepalive = false; |
|
409 |
|
410 // If we're the parent process, we need to know that a reboot event |
|
411 // was fired, and thus the server's main socket needs to be destroyed |
|
412 // and recreated. This is just done with another boolean switch. |
|
413 $this->reboot_sent = true; |
398 $this->reboot_sent = true; |
414 |
399 |
415 // this is really to track if there are any children |
400 // if we're a child, we have to tell the parent to reboot. during the next |
416 $oldfork = $this->allow_fork; |
401 // server loop, the child will die on its own (ok, with a little help from |
417 |
402 // the reboot_sent flag) |
418 // Set our new server flags |
403 |
419 $this->bind_address = $addr; |
|
420 $this->port = $port; |
|
421 $this->allow_fork = $fork; |
|
422 |
|
423 // If we're a child, we have to tell the parent what the hell is |
|
424 // going on, and then get out of here as quickly as possible |
|
425 // (and other children should do the same). If this is a child, |
|
426 // fire an IPC reboot event. Else, fire a "die all" event |
|
427 if ( $this->threader->is_child() ) |
404 if ( $this->threader->is_child() ) |
428 { |
405 { |
429 if ( function_exists('status') ) |
|
430 status('Signaling parent with parameter changes (fork = ' . intval($fork) . ') + reboot request'); |
|
431 // this is the child |
|
432 $this->threader->ipc_send(array( |
406 $this->threader->ipc_send(array( |
433 'action' => 'ws_reboot', |
407 'action' => 'ws_reboot', |
434 'addr' => $addr, |
408 'addr' => $addr, |
435 'port' => $port, |
409 'port' => $port, |
436 'fork' => $fork |
410 'fork' => $allow_fork |
437 )); |
411 )); |
438 } |
|
439 else if ( !$this->threader->is_child() && $oldfork ) |
|
440 { |
|
441 // we are the parent and have been asked to respawn. there are (presumably) |
|
442 // still children running around; when one of them dies, we'll receive a |
|
443 // SIGCHLD, but often times this is already being called from the SIGUSR2 |
|
444 // handler. whoops. |
|
445 if ( function_exists('status') ) |
|
446 status('Waiting on all children'); |
|
447 |
|
448 // this is the parent, and there are children present |
|
449 $this->threader->kill_all_children(); |
|
450 |
|
451 // all children are dead, we are ok to respawn |
|
452 $this->respawn(); |
|
453 } |
412 } |
454 else |
413 else |
455 { |
414 { |
456 // not sure what to do in this particular scenario. |
415 // If we're not a child, finish the job |
|
416 $this->reboot_ipc(array( |
|
417 'addr' => $addr, |
|
418 'port' => $port, |
|
419 'fork' => $allow_fork |
|
420 )); |
|
421 } |
|
422 } |
|
423 |
|
424 /** |
|
425 * Internal method called from an IPC reboot event |
|
426 * @access private |
|
427 */ |
|
428 |
|
429 function reboot_ipc($params) |
|
430 { |
|
431 if ( function_exists('status') ) |
|
432 status('IPC reboot is in stage 2'); |
|
433 |
|
434 $this->reboot_sent = true; |
|
435 $this->old_allow_fork = $this->allow_fork; |
|
436 |
|
437 if ( is_string($params['addr']) ) |
|
438 { |
|
439 $this->bind_address = $params['addr']; |
|
440 } |
|
441 if ( is_int($params['port']) ) |
|
442 { |
|
443 $this->port = $params['port']; |
|
444 } |
|
445 if ( is_bool($params['fork']) ) |
|
446 { |
|
447 $this->allow_fork = $params['fork']; |
457 } |
448 } |
458 } |
449 } |
459 |
450 |
460 /** |
451 /** |
461 * Respawns the server. All children should be dead, and any client |
452 * Respawns the server. All children should be dead, and any client |
514 { |
506 { |
515 ## |
507 ## |
516 ## STAGE 0: CLEANUP FROM PREVIOUS RUN |
508 ## STAGE 0: CLEANUP FROM PREVIOUS RUN |
517 ## |
509 ## |
518 |
510 |
519 // if this is a child process, we're finished - close up shop |
511 // if this is a child process, we may need to exit here. |
520 if ( $this->threader->is_child() && !$this->in_keepalive ) |
512 if ( $this->threader->is_child() && ( !$this->in_keepalive || $this->reboot_sent ) ) |
521 { |
513 { |
522 if ( function_exists('status') ) |
514 if ( function_exists('status') ) |
523 status('Exiting child process'); |
515 status('Exiting child process'); |
524 |
516 |
525 $remote->destroy(); |
517 $remote->destroy(); |
526 |
518 |
527 exit(0); |
519 exit(0); |
|
520 } |
|
521 |
|
522 // If we're waiting on a reboot, take care of it now |
|
523 if ( $this->reboot_sent ) |
|
524 { |
|
525 if ( $this->old_allow_fork ) |
|
526 { |
|
527 // we are the parent and have been asked to respawn. there are (presumably) |
|
528 // still children running around; when one of them dies, we'll receive a |
|
529 // SIGCHLD, but often times this is already being called from the SIGUSR2 |
|
530 // handler. whoops. |
|
531 if ( function_exists('status') ) |
|
532 status('Waiting on all children'); |
|
533 |
|
534 // this is the parent, and there are children present |
|
535 $this->threader->kill_all_children(); |
|
536 } |
|
537 |
|
538 if ( function_exists('status') ) |
|
539 status('Reboot is a go'); |
|
540 |
|
541 $this->respawn(); |
528 } |
542 } |
529 |
543 |
530 ## |
544 ## |
531 ## STAGE 1: LISTENER AND INIT |
545 ## STAGE 1: LISTENER AND INIT |
532 ## |
546 ## |