Replaced multithreading in WebServer with a full multithreading library that properly handles IPC and child management
#!/usr/bin/env php<?php/** * Greyhound - real web management for Amarok * Copyright (C) 2008 Dan Fuhry * * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. */define('GREY_VERSION', '0.1a4');// Try to trap termination signals to cleanly close the socket when needed// AmaroK sends a SIGTERM when it is shut down or the user requests to stop// the scriptif ( function_exists('pcntl_signal') ){ // required for signal handling to work declare(ticks=1); // trap SIGTERM pcntl_signal(SIGTERM, 'sigterm'); pcntl_signal(SIGINT, 'sigterm'); pcntl_signal(SIGUSR1, 'handle_refresh_signal');}@ini_set('display_errors', 'on');// get the rootdefine('GREY_ROOT', dirname(__FILE__));// what kind of terminal do we have?$use_colors = ( @in_array(@$_SERVER['TERM'], array('linux', 'xterm', 'vt100')) ) ? true : false;require(GREY_ROOT . '/functions.php');// start up...status('Starting Greyhound Web Control v' . GREY_VERSION);status('loading files');require_once(GREY_ROOT . '/webserver.php');define('SMARTY_DIR', GREY_ROOT . '/smarty/');require_once(GREY_ROOT . '/smarty/Smarty.class.php');require_once(GREY_ROOT . '/playlist.php');require_once(GREY_ROOT . '/json.php');require_once(GREY_ROOT . '/ajax.php');require_once(GREY_ROOT . '/uiconfig.php');require_once(GREY_ROOT . '/imagetools.php');require_once(GREY_ROOT . '/sessions.php');//// LOAD OUR CONFIG// Amarok launches Greyhound with a different wd than Greyhound's root. This means// that we can drop our own config (set up from the web UI) in there and load that// instead of the default config, which comes with greyhound.//grey_reload_config();// create directories@mkdir('./compiled');// signal handlerfunction sigterm($signal){ global $httpd, $avahi_process; if ( !defined('HTTPD_WS_CHILD') ) { status("Caught SIGTERM, cleaning up."); if ( is_resource($avahi_process) ) { @proc_terminate($avahi_process); } } exit(0);}status('doing PHP capabilities check');if ( !function_exists('pcntl_signal') ){ warning('System does not support POSIX functions. Termination signals will result in unclean shutdown.');}if ( !function_exists('simplexml_load_file') ){ warning('Can\'t find support for SimpleXML, which is needed to parse the Amarok playlist file.'); burnout('SimpleXML required to continue. You may have an outdated version of PHP; most versions of PHP 5 have SimpleXML built-in. Check your distribution\'s documentation to find out how to enable PHP\'s SimpleXML support.');}if ( !function_exists('imagepng') || !function_exists('imagecopyresampled') || !function_exists('imagecreatefromjpeg') || !function_exists('imagecreatefromwbmp') ){ warning('Can\'t find support for GD 2.0. Artwork will not be displayed. Look around in your distro\'s package manager for php-gd or php5-gd.');}status('initializing playlist');// init playlist object$playlist = array();$amarok_home = false;rebuild_playlist();// startup webserver$ip = ( $public ) ? '0.0.0.0' : '127.0.0.1';$port = 7447;try{ status('starting PhpHttpd'); $httpd = new WebServer($ip, $port); // if we have avahi and proc_open support, publish the service (new) if ( $allowcontrol && function_exists('proc_open') && $path = which('avahi-publish') ) { // get our current hostname (hack, sort of) $hostfile = tempnam('hostname', ( function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp' )); system("hostname > '$hostfile' 2>/dev/null"); $hostname = trim(@file_get_contents($hostfile)); unlink($hostfile); if ( !empty($hostname) ) { status('Publishing service on local network with Avahi'); $descriptorspec = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ); $thisuser = get_current_user(); $avahi_command = "'$path' -s \"{$thisuser}'s\"' AmaroK playlist on $hostname' _greyhound._tcp $port"; $avahi_process = proc_open($avahi_command, $descriptorspec, $avahi_pipes); if ( !$avahi_process ) { warning('proc_open() failed; could not start announcement of service on Avahi network'); } } } // setup handlers status('initializing handlers'); $httpd->add_handler('index', 'function', 'amarok_playlist'); $httpd->add_handler('login', 'function', 'greyhound_login_page'); $httpd->add_handler('logout', 'function', 'greyhound_logout'); $httpd->add_handler('config', 'function', 'greyhound_config'); $httpd->add_handler('action.json', 'function', 'ajax_request_handler'); $httpd->add_handler('artwork', 'function', 'artwork_request_handler'); $httpd->add_handler('scripts', 'dir', GREY_ROOT . '/scripts'); $httpd->add_handler('favicon.ico', 'file', GREY_ROOT . '/amarok_icon.ico'); $httpd->add_handler('apple-touch-icon.png', 'file', GREY_ROOT . '/apple-touch-icon.png'); $httpd->add_handler('spacer.gif', 'file', GREY_ROOT . '/spacer.gif'); $httpd->threader->ipc_register('reloadconfig', 'grey_reload_config'); // load all themes if forking is enabled // Themes are loaded when the playlist is requested. This is fine for // single-threaded operation, but if the playlist handler is only loaded // in a child process, we need to preload all themes into the parent before // children can respond to theme resource requests. // if ( $allow_fork ) // { status('Preloading themes'); $dh = @opendir(GREY_ROOT . '/themes'); if ( !$dh ) burnout('Could not open themes directory'); while ( $dir = @readdir($dh) ) { if ( $dir == '.' || $dir == '..' ) continue; if ( is_dir( GREY_ROOT . "/themes/$dir" ) ) load_theme($dir); } // } $httpd->allow_dir_list = true; $httpd->allow_fork = ( $allow_fork ) ? true : false; $httpd->default_document = 'index'; status("Entering main server loop - ^C to interrupt, listening on port $port"); $httpd->serve();}catch( Exception $e ){ if ( strstr(strval($e), "Could not bind") || strstr(strval($e), "Address already in use") ) { burnout("Could not bind to the port $ip:$port. Is Greyhound already running? Sometimes browsers don't close off their connections until Greyhound has been dead for about a minute, so try starting Greyhound again in roughly 60 seconds. If that doesn't work, type \"killall -9 php\" at a terminal and try starting Greyhound again in 60 seconds."); } burnout("Exception caught while running webserver:\n$e");}function handle_refresh_signal(){ global $httpd; if ( !is_object($httpd) ) // we're not serving yet. return false; // we've got an httpd instance; rebuild the playlist rebuild_playlist();}