Skip to content

Instantly share code, notes, and snippets.

@kenee
Created June 26, 2013 16:41
Show Gist options
  • Save kenee/5869076 to your computer and use it in GitHub Desktop.
Save kenee/5869076 to your computer and use it in GitHub Desktop.
double fork
<?php
/*
PHP forked daemon
Standalone PHP binary must be compiled with --enable-sockets and --enable-pcntl
Dave M. -2002
Online Services USA
*/
define(DEBUG,true);
require dirname(__FILE__)."/../../config/config.php";
$todo = $_SERVER['argv'][1];
$daemon = new svhost_daemon;
if ( $todo == 'start' ){
$daemon->start();
}
if ( $todo == 'stop' ){
$daemon->stop();
}
if ( $todo == 'restart' ){
$daemon->stop();
$daemon->start();
}
if ( $todo == 'status' ){
$daemon->status();
}
if ( $todo == '' or !isset($todo) ){
echo "usage: php daemon (start|stop|restart|status)\n";
}
class svhost_daemon{
private $pid_file;
private $sv_log_file;
private $underpriv_uid;
private $underpriv_gid;
private $port;
private $address;
function __construct() {
error_reporting (4);
set_time_limit (0);
ob_implicit_flush ();
$this->pid_file = DATA_DIR.'/svhost.pid';
$this->sv_log_file = DATA_DIR."/logs/svhost.log";
$this->underpriv_uid = '99'; // uid 99 == user nobody, at least on my system.
$this->underpriv_gid = '99';
$this->port = '10000';
$this->address = '127.0.0.1'; // 0 binds to all addresses, may not work on fbsd
//pcntl_signal( SIGCHLD, array('this','sig_handler') );
//pcntl_signal( SIGTERM, array('this','sig_handler') );
//pcntl_signal( SIGINT, array('this','sig_handler') );
pcntl_signal(SIGCHLD, SIG_IGN);
}
function __destruct(){
if(!file_exists($this->pid_file)) {
return;
}
if(posix_getpid() == $this->pid) {
unlink($this->pid_file);
}
}
function interact($connection) {
$cid=getmypid();
$msg = "\nWelcome to the PHP Execute Server. \n" .
"To quit, type 'quit'. To shut down the server type 'shutdown'.\n";
socket_write($connection, $msg, strlen($msg));
while ( true ){
if (!is_resource($connection)){
posix_kill($cid, SIGTERM);
exit;
}
if (false === ($buf = socket_read($connection, 2048))) {
next;
}
if ( substr($buf,0,4) == 'quit' ) {
$talkback = "bye\n";
socket_write($connection, $talkback, strlen($talkback));
$this->sv_log ('Client Disconnect');
posix_kill($cid, SIGTERM);
exit;
} elseif ( substr($buf,0,4) == 'exec' ) {
$cmd = trim(substr($buf,5));
$cmd = escapeshellcmd($cmd);
$cmd = $cmd.' 2>&1 ';
$this->sv_log( "exec $cmd" );
$ret = @shell_exec ($cmd);
if ( $ret == '' or !isset($ret)){
$ret = "$cmd exec failed";
}
$talkback = $ret."\n";
if (DEBUG){
$this->sv_log( $ret );
}
socket_write($connection, $talkback, strlen($talkback));
}else{
$talkback = "Unknown command: " . str_replace("\r\n", '\r\n', $buf) ."\n";
socket_write($connection, $talkback, strlen($talkback));
}
$buf = '';
}
}
function start() {
if (($this->sock = socket_create (AF_INET, SOCK_STREAM, 0)) < 0) {
$this->sv_log("socket_create() failed: reason: " . socket_strerror ($this->sock) );
}
if (($ret = socket_bind ($this->sock, $this->address, $this->port)) < 0) {
$this->sv_log( "socket_bind() failed: reason: " . socket_strerror ($ret) );
}
if (($ret = socket_listen ($this->sock, 0)) < 0) {
$this->sv_log( "socket_listen() failed: reason: " . socket_strerror ($ret) );
}
#change_identity($underpriv_uid,$underpriv_gid);
$this->sv_log("Server ready. Waiting for connections.....");
$fh = $this->open_pid_file($this->pid_file);
$pid = $this->become_daemon();
fputs($fh,$pid);
fclose($fh);
while(true ) {
if (($connection = socket_accept($this->sock)) < 0) {
next;
}
if( ($child = pcntl_fork()) == -1 ) {
$this->sv_log("Could not fork!!");
$this->sv_log("Dying...");
exit;
}
elseif($child == 0) {
$this->interact($connection);
socket_close($this->sock);
exit;
}else{
pcntl_wait($status,1);
}
socket_close($connection);
}
}
function stop(){
if(!file_exists($this->pid_file)) {
$this->sv_log("PID file ".$this->pid_file." not found");
exit;
}
$fp = fopen($this->pid_file,"r");
$pid = fgets($fp,1024);
if(!posix_kill($pid,9)) {
$this->sv_log("can not kill PID: $pid");
exit;
}
$this->sv_log("server process $pid stoped");
if(!unlink($this->pid_file)) {
$this->sv_log("Cannot unlink PID file ".$this->pid_file);
exit;
}
}
function status(){
}
function sig_handler($signo) {
switch($signo) {
case SIGTERM:
// handle shutdown tasks
exit;
break;
case SIGHUP:
// handle restart tasks
break;
case SIGUSR1:
$this->sv_log( "Caught SIGUSR1...");
break;
case SIGCHLD:
while( pcntl_waitpid(-1,$status,WNOHANG)>0 ) {
}
break;
case SIGINT:
exit;
default:
// not implemented yet...
break;
}
}
function sv_log($msg){
$sv_log = date("Y-m-d H:i:s")." ".$msg."\r\n";
error_log($sv_log,3,$this->sv_log_file);
}
function become_daemon() {
$child = pcntl_fork();
if($child) {
exit; // kill parent
}
posix_setsid(); // become session leader
chdir("/");
umask(0); // clear umask
return posix_getpid();
}
function open_pid_file($file) {
if(file_exists($file)) {
$fp = fopen($file,"r");
$pid = fgets($fp,1024);
fclose($fp);
if(posix_kill($pid,0)) {
$this->sv_log("Server already running with PID: $pid");
exit;
}
$this->sv_log("Removing PID file for defunct server process $pid");
if(!unlink($file)) {
$this->sv_log("Cannot unlink PID file $file");
exit;
}
}
if($fp = fopen($file,"w")) {
return $fp;
} else {
$this->sv_log( "Unable to open PID file $file for writing..." );
exit;
}
}
function change_identity($uid,$gid) {
if(!posix_setgid($gid)) {
$this->sv_log("Unable to setgid to $gid!");
unlink($this->pid_file);
exit;
}
if(!posix_setuid($uid)) {
$this->sv_log("Unable to setuid to $uid!");
unlink($this->pid_file);
exit;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment