#!/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 script
if ( 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');
// include files
require('functions.php');
// get the root
define('GREY_ROOT', dirname(__FILE__));
// ignore this, it allows using a different config file when a Mercurial repository
// exists in Greyhound's root directory (to allow the devs to have their own config
// separate from the default)
if ( @is_dir(GREY_ROOT . '/.hg') )
require(GREY_ROOT . '/config.dev.php');
else
require(GREY_ROOT . '/config.php');
// create directories
@mkdir('./compiled');
// what kind of terminal do we have?
$use_colors = ( @in_array(@$_SERVER['TERM'], array('linux', 'xterm', 'vt100')) ) ? true : false;
// start up...
status('Starting Greyhound Web Control v' . GREY_VERSION);
status('loading files');
require('webserver.php');
define('SMARTY_DIR', GREY_ROOT . '/smarty/');
require(GREY_ROOT . '/smarty/Smarty.class.php');
require(GREY_ROOT . '/playlist.php');
require(GREY_ROOT . '/json.php');
require(GREY_ROOT . '/ajax.php');
require(GREY_ROOT . '/imagetools.php');
require(GREY_ROOT . '/sessions.php');
// signal handler
function sigterm($signal)
{
global $httpd;
if ( !defined('HTTPD_WS_CHILD') )
status("Caught SIGTERM, cleaning up.");
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);
// 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('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');
// 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();
// if this is the parent, also ask the children to rebuild.
if ( !defined('HTTPD_WS_CHILD') )
{
foreach ( $httpd->child_list as $pid )
{
posix_kill($pid, SIGUSR1);
}
}
}