fork.php

#!/usr/bin/php5
<?php
/* Listens for requests and forks on each connection */

$__server_listening = true;
$__sleeping = 0;

error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
declare(ticks = 1);

//become_daemon();

/* nobody/nogroup, change to your host's uid/gid of the non-priv user */
//change_identity(65534, 65534);

/* handle signals */
pcntl_signal(SIGTERM, 'sig_handler');
pcntl_signal(SIGINT, 'sig_handler');
pcntl_signal(SIGCHLD, 'sig_handler');

/* change this to your own host / port */
server_loop("0.0.0.0", 6969);

/**
  * Change the identity to a non-priv user
  */
function change_identity( $uid, $gid ) {
    if( !posix_setgid( $gid ) ) {
        print "Unable to setgid to " . $gid . "!\n";
        exit;
    }

    if( !posix_setuid( $uid ) ) {
        print "Unable to setuid to " . $uid . "!\n";
        exit;
    }
}

/**
  * Creates a server socket and listens for incoming client connections
  * @param string $address The address to listen on
  * @param int $port The port to listen on
  */
function server_loop($address, $port) {
    GLOBAL $__server_listening, $__sleeping;

    if(($sock = socket_create(AF_INET, SOCK_STREAM, 0)) < 0) {
        echo "failed to create socket: ".socket_strerror($sock)."\n";
        exit();
    }

    if(($ret = socket_bind($sock, $address, $port)) < 0) {
        echo "failed to bind socket: ".socket_strerror($ret)."\n";
        exit();
    }

    if( ( $ret = socket_listen( $sock, 0 ) ) < 0 ) {
        echo "failed to listen to socket: ".socket_strerror($ret)."\n";
        exit();
    }

    socket_set_nonblock($sock);
   
    echo "waiting for clients to connect\n";

    while ($__server_listening) {
        $connection = @socket_accept($sock);
        if ($connection == 0) {
            usleep($__sleeping);
            if($__sleeping < 5000) {
                $__sleeping += 5;
            }
        } elseif ($connection > 0) {
            $__sleeping = 0;
            handle_client($sock, $connection);
        } else {
            echo "error: ".socket_strerror($connection);
            die;
        }
    }
}

/**
  * Signal handler
  */
function sig_handler($sig) {
    switch($sig) {
        case SIGTERM:
        case SIGINT:
            exit();
            break;
        case SIGCHLD:
            pcntl_waitpid(-1, $status);
            break;
    }
}

/**
  * Handle a new client connection
  */
function handle_client($ssock, $csock) {
    GLOBAL $__server_listening;

    $pid = pcntl_fork();

    if ($pid == -1) {
        /* fork failed */
        echo "fork failure!\n";
        die;
    } elseif ($pid == 0) {
        /* child process */
        $__server_listening = false;
        socket_close($ssock);
        interact($csock);
        socket_close($csock);
    } else {
        socket_close($csock);
    }
}

function interact($socket) {
    /* TALK TO YOUR CLIENT */
    $header = "HTTP/1.0 200\nServer: PHP5 Socket Server\nContent-Type: text/html\nConnection: close\n\n";
    $data = "<html><body><h3>Fishpoop and evil cookies!</h3></body></html>";
    handle_write($socket, $header.$data);
}

function handle_write($socket, $data) {
    $size = strlen($data);
    $offset = 0;
    while($offset < $size) {
        $sent = socket_write($socket, substr($data, $offset), $size-$offset);
        if($sent === false) {
            break;
        }
        $offset += $sent;
    }
    if($offset < $size) {
        $error = socket_strerror(socket_last_error());
        return false;
    } else {
        return true;
    }
}

/**
  * Become a daemon by forking and closing the parent
  */
function become_daemon() {
    $pid = pcntl_fork();
   
    if ($pid == -1) {
        /* fork failed */
        echo "fork failure!\n";
        exit();
    } elseif ($pid) {
        /* close the parent */
        exit();
    } else {
        /* child becomes our daemon */
        posix_setsid();
        chdir('/');
        umask(0);
        return posix_getpid();

    }
}

?>