greyhound.php
author Dan
Tue, 23 Sep 2008 23:26:18 -0400
changeset 50 1b4288399b1f
parent 48 d643bfb862d8
child 54 6d8b9497e44b
permissions -rwxr-xr-x
Added graphical configuration, at this point only for the grey theme but others will follow soon. (This has been nearly done for two weeks or more but was on hold due to the bugs with multithreading)

#!/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');

// get the root
define('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 handler
function 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();
}