Program: CWE-79
Project: CVE-2013-1770
Buggy source:
<?php

#
# Some common functions for the Ganglia PHP website.
# Assumes the Gmeta XML tree has already been parsed,
# and the global variables $metrics, $clusters, and $hosts
# have been set.
#

include_once ( dirname(__FILE__) . "/lib/json.php" );

#
# Load event API driver.
#
$driver = ucfirst(strtolower( !isset($conf['overlay_events_provider']) ? "Json" : $conf['overlay_events_provider'] ));
if (file_exists( dirname(__FILE__) . "/lib/Events/Driver_${driver}.php")) {
  include_once( dirname(__FILE__) . "/lib/Events/Driver_${driver}.php" );
}

#------------------------------------------------------------------------------
# Allows a form of inheritance for template files.
# If a file does not exist in the chosen template, the
# default is used. Cuts down on code duplication.
function template ($name)
{
   global $conf;

   $fn = "./templates/${conf['template_name']}/$name";
   $default = "./templates/default/$name";

   if (file_exists($fn)) {
      return $fn;
   }
   else {
      return $default;
   }
}

#------------------------------------------------------------------------------
# Creates a hidden input field in a form. Used to save CGI variables.
function hiddenvar ($name, $var)
{

   $hidden = "";
   if ($var) {
      #$url = rawurlencode($var);
      $hidden = "<input type=\"hidden\" name=\"$name\" value=\"$var\">\n";
   }
   return $hidden;
}

#------------------------------------------------------------------------------
# Gives a readable time string, from a "number of seconds" integer.
# Often used to compute uptime.
function uptime($uptimeS)
{
   $uptimeD=intval($uptimeS/86400);
   $uptimeS=$uptimeD ? $uptimeS % ($uptimeD*86400) : $uptimeS;
   $uptimeH=intval($uptimeS/3600);
   $uptimeS=$uptimeH ? $uptimeS % ($uptimeH*3600) : $uptimeS;
   $uptimeM=intval($uptimeS/60);
   $uptimeS=$uptimeM ? $uptimeS % ($uptimeM*60) : $uptimeS;

   $s = ($uptimeD!=1) ? "s" : "";
   return sprintf("$uptimeD day$s, %d:%02d:%02d",$uptimeH,$uptimeM,$uptimeS);
}

#------------------------------------------------------------------------------
# Try to determine a nodes location in the cluster. Attempts to find the
# LOCATION attribute first. Requires the host attribute array from 
# $hosts[$cluster][$name], where $name is the hostname.
# Returns [-1,-1,-1] if we could not determine location.
#
function findlocation($attrs)
{
   $rack=$rank=$plane=-1;

   $loc=$attrs['LOCATION'];
   if ($loc) {
      sscanf($loc, "%d,%d,%d", $rack, $rank, $plane);
      #echo "Found LOCATION: $rack, $rank, $plane.<br>";
   }
   if ($rack<0 or $rank<0) {
      # Try to parse the host name. Assumes a compute-<rack>-<rank>
      # naming scheme.
      $n=sscanf($attrs['NAME'], "compute-%d-%d", $rack, $rank);
      $plane=0;
   }
   return array($rack,$rank,$plane);
}


#------------------------------------------------------------------------------
function cluster_sum($name, $metrics)
{
   $sum = 0;

   foreach ($metrics as $host => $val)
      {
         if(isset($val[$name]['VAL'])) $sum += $val[$name]['VAL'];
      }

   return $sum;
}

#------------------------------------------------------------------------------
function cluster_min($name, $metrics)
{
   $min = "";

   foreach ($metrics as $host => $val)
      {
         $v = $val[$name]['VAL'];
         if (!is_numeric($min) or $min < $v)
            {
               $min = $v;
               $minhost = $host;
            }
      }
   return array($min, $minhost);
}

#------------------------------------------------------------------------------
#
# A useful function for giving the correct picture for a given
# load. Scope is "node | cluster | grid". Value is 0 <= v <= 1.
function load_image ($scope, $value)
{
   global $conf;

   $scaled_load = $value / $conf['load_scale'];
   if ($scaled_load>1.00) {
      $image = template("images/${scope}_overloaded.jpg");
   }
   else if ($scaled_load>=0.75) {
      $image = template("images/${scope}_75-100.jpg");
   }
   else if ($scaled_load >= 0.50) {
      $image = template("images/${scope}_50-74.jpg");
   }
   else if ($scaled_load>=0.25) {
      $image = template("images/${scope}_25-49.jpg");
   }
   else {
      $image = template("images/${scope}_0-24.jpg");
   }

   return $image;
}

#------------------------------------------------------------------------------
# A similar function that specifies the background color for a graph
# based on load. Quantizes the load figure into 6 sets.
function load_color ($value)
{
   global $conf;

   $scaled_load = $value / $conf['load_scale'];
   if ($scaled_load>1.00) {
      $color = $conf['load_colors']["100+"];
   }
   else if ($scaled_load>=0.75) {
      $color = $conf['load_colors']["75-100"];
   }
   else if ($scaled_load >= 0.50) {
      $color = $conf['load_colors']["50-75"];
   }
   else if ($scaled_load>=0.25) {
      $color = $conf['load_colors']["25-50"];
   }
   else if ($scaled_load < 0.0)
      $color = $conf['load_colors']["down"];
   else {
      $color = $conf['load_colors']["0-25"];
   }

   return $color;
}

#------------------------------------------------------------------------------
#
# Just a useful function to print the HTML for
# the load/death of a cluster node
function node_image ($metrics)
{
   global $hosts_down;

   # More rigorous checking if variables are set before trying to use them.
   if ( isset($metrics['cpu_num']['VAL']) and $metrics['cpu_num']['VAL'] != 0 ) {
		$cpu_num = $metrics['cpu_num']['VAL'];
   } else {
		$cpu_num = 1;
   }

   if ( isset($metrics['load_one']['VAL']) ) {
		$load_one = $metrics['load_one']['VAL'];
   } else {
		$load_one = 0;
   }

   $value = $load_one / $cpu_num;

   # Check if the host is down
   # RFM - Added isset() check to eliminate error messages in ssl_error_log
   if (isset($hosts_down) and $hosts_down)
         $image = template("images/node_dead.jpg");
   else
         $image = load_image("node", $value);

   return $image;
}

#------------------------------------------------------------------------------
#
# Finds the min/max over a set of metric graphs. Nodes is
# an array keyed by host names.
#
function find_limits($nodes, $metricname)
{
   global $conf, $metrics, $clustername, $rrd_dir, $start, $end, $rrd_options;

   if (!count($metrics))
      return array(0, 0);

   $firsthost = key($metrics);
   
   if (array_key_exists($metricname,$metrics[$firsthost])) {
     if ($metrics[$firsthost][$metricname]['TYPE'] == "string"
        or $metrics[$firsthost][$metricname]['SLOPE'] == "zero")
           return array(0,0);
   }
   else {
     return array(0,0);
   }

   $max=0;
   $min=0;
   if ($conf['graph_engine'] == "graphite") {
     $target = $conf['graphite_prefix'] . $clustername . ".[a-zA-Z0-9]*." . $metricname . ".sum";
     $raw_highestMax = file_get_contents($conf['graphite_url_base'] . "?target=highestMax(" . $target . ",1)&from=" . $start . "&until=" . $end . "&format=json");
     $highestMax = json_decode($raw_highestMax, TRUE);
     $highestMaxDatapoints = $highestMax[0]['datapoints'];
     $maxdatapoints = array();
     foreach ( $highestMaxDatapoints as $datapoint ) {
       array_push($maxdatapoints, $datapoint[0]);
     }
     $max = max($maxdatapoints);
   }
   else {
     foreach ( $nodes as $host => $value ) {
       $out = array();

       $rrd_dir = "${conf['rrds']}/$clustername/$host";
       $rrd_file = "$rrd_dir/$metricname.rrd";
       if (file_exists($rrd_file)) {
          if ( extension_loaded( 'rrd' ) ) {
            $values = rrd_fetch($rrd_file,
              array(
                "--start", $start,
                "--end", $end,
                "AVERAGE"
              )
            );

            $values = (array_filter(array_values($values['data']['sum']), 'is_finite'));
            $thismax = max($values);
            $thismin = min($values);
          } else {
            $command = $conf['rrdtool'] . " graph /dev/null $rrd_options ".
               "--start $start --end $end ".
               "DEF:limits='$rrd_dir/$metricname.rrd':'sum':AVERAGE ".
               "PRINT:limits:MAX:%.2lf ".
               "PRINT:limits:MIN:%.2lf";
            exec($command, $out);
            if(isset($out[1])) {
               $thismax = $out[1];
            } else {
               $thismax = NULL;
            }
            if (!is_numeric($thismax)) continue;
            $thismin=$out[2];
            if (!is_numeric($thismin)) continue;
          }

          if ($max < $thismax) $max = $thismax;

          if ($min > $thismin) $min = $thismin;
          #echo "$host: $thismin - $thismax (now $value)<br>\n";
       }
     }
   }
   return array($min, $max);
}

#------------------------------------------------------------------------------
#
# Finds the avg of the given cluster & metric from the summary rrds.
#
function find_avg($clustername, $hostname, $metricname)
{
    global $conf, $start, $end, $rrd_options;
    $avg = 0;

    if ($hostname)
        $sum_dir = "${conf['rrds']}/$clustername/$hostname";
    else
        $sum_dir = "${conf['rrds']}/$clustername/__SummaryInfo__";

    $command = $conf['rrdtool'] . " graph /dev/null $rrd_options ".
        "--start $start --end $end ".
        "DEF:avg='$sum_dir/$metricname.rrd':'sum':AVERAGE ".
        "PRINT:avg:AVERAGE:%.2lf ";
    exec($command, $out);
    if ( isset($out[1]) ) 
      $avg = $out[1];
    else
      $avg = 0;
    #echo "$sum_dir: avg($metricname)=$avg<br>\n";
    return $avg;
}

#------------------------------------------------------------------------------
# Alternate between even and odd row styles.
function rowstyle()
{
   static $style;

   if ($style == "even") { $style = "odd"; }
   else { $style = "even"; }

   return $style;
}

#------------------------------------------------------------------------------
# Return a version of the string which is safe for display on a web page.
# Potentially dangerous characters are converted to HTML entities.  
# Resulting string is not URL-encoded.
function clean_string( $string )
{
  return htmlentities( $string );
}
#------------------------------------------------------------------------------
function sanitize ( $string ) {
  return  escapeshellcmd( clean_string( rawurldecode( $string ) ) ) ;
}

#------------------------------------------------------------------------------
# If arg is a valid number, return it.  Otherwise, return null.
function clean_number( $value )
{
  return is_numeric( $value ) ? $value : null;
}

#------------------------------------------------------------------------------
# Return true if string is a 3 or 6 character hex color.Return false otherwise.
function is_valid_hex_color( $string )
{
  $return_value = false;
  if( strlen( $string ) == 6 || strlen( $string ) == 3 ) {
    if( preg_match( '/^[0-9a-fA-F]+$/', $string ) ) {
      $return_value = true;
    }
  }
  return $return_value;
    
}

#------------------------------------------------------------------------------
# Return a shortened version of a FQDN
# if "hostname" is numeric only, assume it is an IP instead
# 
function strip_domainname( $hostname ) {
    $postition = strpos($hostname, '.');
    $name = substr( $hostname , 0, $postition );
    if ( FALSE === $postition || is_numeric($name) ) {
        return $hostname;
    } else {
        return $name;
    }
}

#------------------------------------------------------------------------------
# Read a file containing key value pairs
function file_to_hash($filename, $sep)
{
  
  $lines = file($filename, FILE_IGNORE_NEW_LINES);
  
  foreach ($lines as $line) 
  {
    list($k, $v) = explode($sep, rtrim($line));
    $params[$k] = $v;
  }

  return $params;
}

#------------------------------------------------------------------------------
# Read a file containing key value pairs
# Multiple values permitted for each key
function file_to_hash_multi($filename, $sep)
{
 
  $lines = file($filename);
 
  foreach ($lines as $line)
  {
    list($k, $v) = explode($sep, rtrim($line));
    $params[$k][] = $v;
  }

  return $params;
}

#------------------------------------------------------------------------------
# Obtain a list of distinct values from an array of arrays
function hash_get_distinct_values($h)
{
  $values = array();
  $values_done = array();
  foreach($h as $k => $v)
  {
    if($values_done[$v] != "x")
    {
      $values_done[$v] = "x";
      $values[] = $v;
    } 
  }
  return $values;
}

$filter_defs = array();

#------------------------------------------------------------------------------
# Scan $conf['filter_dir'] and populate $filter_defs
function discover_filters()
{
  global $conf, $filter_defs;

  # Check whether filtering is configured or not
  if(!isset($conf['filter_dir']))
    return;

  if(!is_dir($conf['filter_dir']))
  {
    error_log("discover_filters(): not a directory: ${conf['filter_dir']}");
    return;
  }

  if($dh = opendir($conf['filter_dir']))
  {
    while(($filter_conf_filename = readdir($dh)) !== false) {
      if(!is_dir($filter_conf_filename))
      {
        # Parse the file contents
        $full_filename = "${conf['filter_dir']}/$filter_conf_filename";
        $filter_params = file_to_hash($full_filename, '=');
        $filter_shortname = $filter_params["shortname"];
        $filter_type = $filter_params["type"];
        if($filter_type = "url")
        {
          $filter_data_url = $filter_params['url'];
          $filter_defs[$filter_shortname] = $filter_params;
          $filter_defs[$filter_shortname]["data"] = file_to_hash($filter_data_url, ',');
          $filter_defs[$filter_shortname]["choices"] = hash_get_distinct_values($filter_defs[$filter_shortname]["data"]);
        }
      }
    }
    closedir($dh);
  }
}

$filter_permit_list = NULL;

#------------------------------------------------------------------------------
# Initialise the filter permit list, if necessary
function filter_init()
{
   global $conf, $filter_permit_list, $filter_defs, $choose_filter;

   if(!is_null($filter_permit_list))
   {
      return;
   }

   if(!isset($conf['filter_dir']))
   {
      $filter_permit_list = FALSE;
      return;
   }

   $filter_permit_list = array();
   $filter_count = 0;

   foreach($choose_filter as $filter_shortname => $filter_choice)
   {
      if($filter_choice == "")
         continue; 

      $filter_params = $filter_defs[$filter_shortname];
      if($filter_count == 0)
      {
         foreach($filter_params["data"] as $key => $value)
         {
            if($value == $filter_choice)
               $filter_permit_list[$key] = $key;
         }
      }
      else
      {
         foreach($filter_permit_list as $key => $value)
         {
            $remove_key = TRUE;
            if(isset($filter_params["data"][$key]))
            {
               if($filter_params["data"][$key] == $filter_choice)
               {
                  $remove_key = FALSE;
               } 
            }
            if($remove_key)
            {
               unset($filter_permit_list[$key]);
            }
         }
      }
      $filter_count++;
   }

   if($filter_count == 0)
      $filter_permit_list = FALSE;

}

#------------------------------------------------------------------------------
# Decide whether the given source is permitted by the filters, if any
function filter_permit($source_name)
{
   global $filter_permit_list;

   filter_init();
   
   # Handle the case where filtering is not active
   if(!is_array($filter_permit_list))
      return true;

   return isset($filter_permit_list[$source_name]);
}


///////////////////////////////////////////////////////////////////////////////
// Get all the available views
///////////////////////////////////////////////////////////////////////////////
function get_available_views() {
  global $conf;
  
  /* -----------------------------------------------------------------------
  Find available views by looking in the GANGLIA_DIR/conf directory
  anything that matches view_*.json. Read them all and build a available_views
  array
  ----------------------------------------------------------------------- */
  $available_views = array();

  if ($handle = opendir($conf['views_dir'])) {

      while (false !== ($file = readdir($handle))) {

	if ( preg_match("/^view_(.*)\.json$/", $file, $out) ) {
	  $view_config_file = $conf['views_dir'] . "/" . $file;
	  if ( ! is_file ($view_config_file) ) {
	    echo("Can't read view config file " . $view_config_file . ". Please check permissions");
	  }

	  $view = json_decode(file_get_contents($view_config_file), TRUE);
	  // Check whether view type has been specified ie. regex. 
          // If not it's standard view
	  $view_type = 
            isset($view['view_type']) ? $view['view_type'] : "standard";
          $default_size = isset($view['default_size']) ? 
	    $view['default_size'] : $conf['default_view_graph_size'];
	  $available_views[] = array ("file_name" => $view_config_file, 
                                      "view_name" => $view['view_name'],
                                      "default_size" => $default_size, 
                                      "items" => $view['items'], 
                                      "view_type" => $view_type);

	  unset($view);

	}
      }

      closedir($handle);
  }

  foreach ($available_views as $key => $row) {
    $name[$key]  = strtolower($row['view_name']);
  }

  @array_multisort($name,SORT_ASC, $available_views);

  return $available_views;

}

///////////////////////////////////////////////////////////////////////////////
// Get image graph URLS
// This function returns an array of graph URLs to be used when rendering the 
// view. It returns only the base ie. cluster, host, metric information. 
// It is up to the caller to add proper size information, time ranges etc.
///////////////////////////////////////////////////////////////////////////////
function get_view_graph_elements($view) {

  global $conf, $index_array;

  retrieve_metrics_cache();

  $view_elements = array();

  // set the default size from the view or global config
  if ( isset($conf['default_view_graph_size']) ) {
    $default_size = $conf['default_view_graph_size'];
  }
  if ( isset($view['default_size']) ) {
    $default_size = $view['default_size'];
  }


  switch ( $view['view_type'] ) {

    case "standard":
    // Does view have any items/graphs defined
    if ( sizeof($view['items']) == 0 ) {
      continue;
      // print "No graphs defined for this view. Please add some";
    } else {


      // Loop through graph items
      foreach ( $view['items'] as $item_id => $item ) {

	// Check if item is an aggregate graph
	if ( isset($item['aggregate_graph']) ) {

	  foreach ( $item['host_regex'] as $reg_id => $regex_array ) {
	    $graph_args_array[] = "hreg[]=" . urlencode($regex_array["regex"]);
	  }

	  if ( isset($item['metric_regex']) ) {
	    foreach ( $item['metric_regex'] as $reg_id => $regex_array ) {
	      $graph_args_array[] = "mreg[]=" . urlencode($regex_array["regex"]);
              $mreg[] = $regex_array["regex"];
	    }
	  }

          if ( isset($item['size']) ) {
            $graph_args_array[] = "z=" . $item['size'];
          } else {
            $graph_args_array[] = "z=" . $default_size;
          }


	  // If graph type is not specified default to line graph
	  if ( isset($item['graph_type']) && in_array($item['graph_type'], array('line', 'stack') ) )
	    $graph_args_array[] = "gtype=" . $item['graph_type'];
	  else
	    $graph_args_array[] = "gtype=line";

	  if (isset($item['upper_limit']))
	    $graph_args_array[] = "x=" .$item['upper_limit'];

	  if (isset($item['lower_limit']))
	    $graph_args_array[] = "n=" .$item['lower_limit'];

	  if (isset($item['vertical_label']))
	    $graph_args_array[] = "vl=" . urlencode($item['vertical_label']);

	  if (isset($item['title']))
	    $graph_args_array[] = "title=" . urlencode($item['title']);

	  if ( isset($item['metric']) ) {
	    $graph_args_array[] = "m=" . $item['metric'];
	  }

          if ( isset($item['glegend']) )
            $graph_args_array[] = "glegend=" . $item["glegend"];

	  if ( isset($item['cluster']) ) {
	    $graph_args_array[] = "c=" . urlencode($item['cluster']);
	  }

	  if ( isset($item['exclude_host_from_legend_label']) ) {
	    $graph_args_array[] = "lgnd_xh=" . $item['exclude_host_from_legend_label'];
	  }
	  
	  $graph_args_array[] = "aggregate=1";
	  $view_elements[] = array ( "graph_args" => join("&", $graph_args_array), 
	      "aggregate_graph" => 1,
	      "name" => isset($item['title']) && $item['title'] != "" ? $item['title'] : $mreg[0] . " Aggregate graph"
	  );

	  unset($graph_args_array);
          
        // Check whether it's a composite graph/report. It needs to have an item id
	} else if ( $item['item_id'] ) {
          
	  $graph_args_array[] = "vn=" . $view['view_name'];
          $graph_args_array[] = "item_id=" . $item['item_id'];
	  $view_elements[] = array ( "graph_args" => join("&", $graph_args_array)
	  );
          unset($graph_args_array);
          
	// It's standard metric graph          
        } else {
	  // Is it a metric or a graph(report)
	  if ( isset($item['metric']) ) {
	    $graph_args_array[] = "m=" . $item['metric'];
	    $name = $item['metric'];
	  } else {
	    $graph_args_array[] = "g=" . urlencode($item['graph']);
	    $name = $item['graph'];
	  }
          if ( isset($item['size']) ) {
            $graph_args_array[] = "z=" . $item['size'];
          } else {
            $graph_args_array[] = "z=" . $default_size;
          }

	  if (isset($item['hostname'])) {
            $hostname = $item['hostname'];
            $cluster = array_key_exists($hostname, $index_array['cluster']) ?
	      $index_array['cluster'][$hostname][0] : NULL;
          } else if (isset($item['cluster'])) {
	    $hostname = "";
            $cluster = $item['cluster'];
	  } else {
            $hostname = "";
            $cluster = "";
	  }

	  $graph_args_array[] = "h=" . urlencode($hostname);
	  $graph_args_array[] = "c=" . urlencode($cluster);

	  if (isset($item['vertical_label']))
	    $graph_args_array[] = "vl=" . urlencode($item['vertical_label']);

	  if (isset($item['title']))
	    $graph_args_array[] = "title=" . urlencode($item['title']);

          if (isset($item['warning'])) {
            $view_e['warning'] = $item['warning'];
            $graph_args_array[] = "warn=" . $item['warning'];
          }
          if (isset($item['critical'])) {
            $view_e['critical'] = $item['critical'];
            $graph_args_array[] = "crit=" . $item['critical'];
          }


          $view_e["graph_args"] = join("&", $graph_args_array);
          $view_e['hostname'] = $hostname;
          $view_e['cluster'] = $cluster;
          $view_e['name'] = $name;
                  
	  $view_elements[] = $view_e;

          unset($view_e);
	  unset($graph_args_array);

	}

      } // end of foreach ( $view['items']
    } // end of if ( sizeof($view['items'])
    break;
    ;;

    ///////////////////////////////////////////////////////////////////////////
    // Currently only supports matching hosts.
    ///////////////////////////////////////////////////////////////////////////
    case "regex":
      foreach ( $view['items'] as $item_id => $item ) {
	// Is it a metric or a graph(report)
	if ( isset($item['metric']) ) {
	  $metric_suffix = "m=" . $item['metric'];
	  $name = $item['metric'];
	} else {
	  $metric_suffix = "g=" . $item['graph'];
	  $name = $item['graph'];
	}

	// Find hosts matching a criteria
	$query = $item['hostname'];
	foreach ( $index_array['hosts'] as $key => $host_name ) {
	  if ( preg_match("/$query/", $host_name ) ) {
	    $clusters = $index_array['cluster'][$host_name];
	    foreach ($clusters AS $cluster) {
	    $graph_args_array[] = "h=" . urlencode($host_name);
	    $graph_args_array[] = "c=" . urlencode($cluster);

	    $view_elements[] = array ( "graph_args" => $metric_suffix . "&" . join("&", $graph_args_array), 
	      "hostname" => $host_name,
	      "cluster" => $cluster,
	      "name" => $name);

	    unset($graph_args_array);
            }

	  }
	}
	
      } // end of foreach ( $view['items'] as $item_id => $item )
    break;;
  
  } // end of switch ( $view['view_type'] ) {


  return ($view_elements);

}

function legendEntry($vname, $legend_items) {
  $legend = "";
  if (in_array("now", $legend_items))
    $legend .= "VDEF:{$vname}_last={$vname},LAST ";

  if (in_array("min", $legend_items))
    $legend .= "VDEF:{$vname}_min={$vname},MINIMUM ";

  if (in_array("avg", $legend_items))
    $legend .= "VDEF:{$vname}_avg={$vname},AVERAGE ";

  if (in_array("max", $legend_items))
    $legend .= "VDEF:{$vname}_max={$vname},MAXIMUM ";

  $terminate = FALSE;
  if (in_array("now", $legend_items)) {
    $legend .= "GPRINT:'{$vname}_last':'Now\:%5.1lf%s";
    $terminate = TRUE;
  }

  if (in_array("min", $legend_items)) {
    if ($terminate)
      $legend .= "' ";
    $legend .= "GPRINT:'{$vname}_min':'Min\:%5.1lf%s";
    $terminate = TRUE;
  }

  if (in_array("avg", $legend_items)) {
    if ($terminate)
      $legend .= "' ";
    $legend .= "GPRINT:'{$vname}_avg':'Avg\:%5.1lf%s";
    $terminate = TRUE;
  }

  if (in_array("max", $legend_items)) {
    if ($terminate)
      $legend .= "' ";
    $legend .= "GPRINT:'{$vname}_max':'Max\:%5.1lf%s";
    $terminate = TRUE;
  }
  
  if ($terminate)
    $legend .= "\\l' ";

  return $legend;
}

/**
 * Check if current user has a privilege (view, edit, etc) on a resource.
 * If resource is unspecified, we assume GangliaAcl::ALL.
 *
 * Examples
 *   checkAccess( GangliaAcl::ALL_CLUSTERS, GangliaAcl::EDIT, $conf ); // user has global edit?
 *   checkAccess( GangliaAcl::ALL_CLUSTERS, GangliaAcl::VIEW, $conf ); // user has global view?
 *   checkAccess( $cluster, GangliaAcl::EDIT, $conf ); // user can edit current cluster?
 *   checkAccess( 'cluster1', GangliaAcl::EDIT, $conf ); // user has edit privilege on cluster1?
 *   checkAccess( 'cluster1', GangliaAcl::VIEW, $conf ); // user has view privilege on cluster1?
 */
function checkAccess($resource, $privilege, $conf) {
  
  if(!is_array($conf)) {
    trigger_error('checkAccess: $conf is not an array.', E_USER_ERROR);
  }
  if(!isSet($conf['auth_system'])) {
    trigger_error("checkAccess: \$conf['auth_system'] is not defined.", E_USER_ERROR);
  }
  
  switch( $conf['auth_system'] ) {
    case 'readonly':
      $out = ($privilege == GangliaAcl::VIEW);
      break;
      
    case 'enabled':
      // TODO: 'edit' needs to check for writeability of data directory.  error log if edit is allowed but we're unable to due to fs problems.
      
      $acl = GangliaAcl::getInstance();
      $auth = GangliaAuth::getInstance();
      
      if(!$auth->isAuthenticated()) {
        $user = GangliaAcl::GUEST;
      } else {
        $user = $auth->getUser();
      }
      
      if(!$acl->has($resource)) {
        $resource = GangliaAcl::ALL_CLUSTERS;
      }
      
      $out = false;
      if($acl->hasRole($user)) {
        $out = (bool) $acl->isAllowed($user, $resource, $privilege);
      }
      // error_log("checkAccess() user=$user, resource=$resource, priv=$privilege == $out");
      break;
    
    case 'disabled':
      $out = true;
      break;
    
    default:
      trigger_error( "Invalid value '".$conf['auth_system']."' for \$conf['auth_system'].", E_USER_ERROR );
      return false;
  }
  
  return $out;
}

function viewId($view_name) {
  $id = 'v_' . preg_replace('/[^a-zA-Z0-9_]/', '_', $view_name);
  return $id;
}

///////////////////////////////////////////////////////////////////////////////
// Taken from
// http://au2.php.net/manual/en/function.json-encode.php#80339
// Pretty print JSON 
///////////////////////////////////////////////////////////////////////////////
function json_prettyprint($json) 
{ 
    $tab = "  "; 
    $new_json = ""; 
    $indent_level = 0; 
    $in_string = false; 

    $len = strlen($json); 

    for($c = 0; $c < $len; $c++) 
    { 
        $char = $json[$c]; 
        switch($char) 
        { 
            case '{': 
            case '[': 
                if(!$in_string) 
                { 
                    $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); 
                    $indent_level++; 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case '}': 
            case ']': 
                if(!$in_string) 
                { 
                    $indent_level--; 
                    $new_json .= "\n" . str_repeat($tab, $indent_level) . $char; 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case ',': 
                if(!$in_string) 
                { 
                    $new_json .= ",\n" . str_repeat($tab, $indent_level); 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case ':': 
                if(!$in_string) 
                { 
                    $new_json .= ": "; 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case '"': 
                if($c > 0 && $json[$c-1] != '\\') 
                { 
                    $in_string = !$in_string; 
                } 
            default: 
                $new_json .= $char; 
                break;                    
        } 
    } 

    return $new_json; 
} 

function ganglia_cache_metrics() {
    global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics;

    if (!isset($debug)) { $debug = 0; }

    if($conf['cachedata'] == 1 && file_exists($conf['cachefile'])) {
        // check for the cached file
        // snag it and return it if it is still fresh
        $time_diff = time() - filemtime($conf['cachefile']);
        $expires_in = $conf['cachetime'] - $time_diff;
        if( $time_diff < $conf['cachetime']){
                if ( $debug == 1 ) {
                  echo("DEBUG: Fetching data from cache. Expires in " . $expires_in . " seconds.\n");
                }
                $index_array = unserialize(file_get_contents($conf['cachefile']));
        }
    }

    if ( ! isset($index_array) ) {

        if ( $debug == 1 ) {
            echo("DEBUG: Querying GMond for new data\n");
        }
        // Set up for cluster summary
        $context = "index_array";
        include_once $conf['gweb_root'] . "/functions.php";
        include_once $conf['gweb_root'] . "/ganglia.php";
        include_once $conf['gweb_root'] . "/get_ganglia.php";

        foreach ( $index_array['cluster'] as $hostname => $elements ) {
            $hosts[] = $hostname;
        }

        asort($hosts);
        $index_array['hosts'] = $hosts;

        file_put_contents($conf['cachefile'], serialize($index_array));

    }

} // end function ganglia_cache_metrics


//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
function build_aggregate_graph_config ($graph_type, 
                                       $line_width, 
                                       $hreg,
                                       $mreg,
                                       $glegend,
                                       $exclude_host_from_legend_label) {

  global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics;
  
  retrieve_metrics_cache();
  
  $color_count = sizeof($conf['graph_colors']);

  $graph_config["report_name"]=isset($mreg)  ?  sanitize(implode($mreg))   : NULL;
  $graph_config["title"]=isset($mreg)  ?  sanitize(implode($mreg))   : NULL;
  $graph_config["glegend"]=isset($glegend) ? sanitize($glegend) : "show";

  $counter = 0;

  ///////////////////////////////////////////////////////////////////////////
  // Find matching hosts    
  foreach ( $hreg as $key => $query ) {
    foreach ( $index_array['hosts'] as $key => $host_name ) {
      if ( preg_match("/$query/i", $host_name ) ) {
        // We can have same hostname in multiple clusters
        foreach ($index_array['cluster'][$host_name] AS $cluster) {
            $matches[] = $host_name . "|" . $cluster;
        }
      }
    }
  } 

  if( isset($mreg)) {
    // Find matching metrics
    foreach ( $mreg as $key => $query ) {
      foreach ( $index_array['metrics'] as $key => $m_name ) {
        if ( preg_match("/$query/i", $key, $metric_subexpr ) ) {
          if (isset($metric_subexpr) && count($metric_subexpr) > 1) {
            $legend = array();
            for ($i = 1; $i < count($metric_subexpr); $i++) {
              $legend[] = $metric_subexpr[$i];
            }
	    $metric_matches[$key] = implode(' ', $legend);
          } else {
            $metric_matches[$key] = $key;
          }
        }
      }
    }
    ksort($metric_matches);
  }
  
  if( isset($metric_matches)){
    $metric_matches_unique = array_unique($metric_matches);
  }
  else{
    $metric_matches_unique = array($metric_name => $metric_name);
  }

  if ( isset($matches)) {

    $matches_unique = array_unique($matches);

    // Create graph_config series from matched hosts and metrics
    foreach ( $matches_unique as $key => $host_cluster ) {

      $out = explode("|", $host_cluster);

      $host_name = $out[0];
      $cluster_name = $out[1];

      foreach ( $metric_matches_unique as $m_name => $legend ) {

        // We need to cycle the available colors
        $color_index = $counter % $color_count;

        // next loop if there is no metric for this hostname
        if( !in_array($host_name, $index_array['metrics'][$m_name]))
          continue;

        $label = '';
        if ($exclude_host_from_legend_label) {
	  $label = $legend;
        } else {
          if ($conf['strip_domainname'] == True )
            $label = strip_domainname($host_name);
          else
            $label = $host_name;

 	  if (isset($metric_matches) and sizeof($metric_matches_unique) > 1)
            $label .= " $legend";
	}

        $graph_config['series'][] = array ( "hostname" => $host_name , "clustername" => $cluster_name,
          "metric" => $m_name,  "color" => $conf['graph_colors'][$color_index], "label" => $label, "line_width" => $line_width, "type" => $graph_type);

        $counter++;

      }
      }
   }

   return $graph_config;

} // function build_aggregate_graph_config () {


//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
function retrieve_metrics_cache () {

   global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics, $context;

   if($conf['cachedata'] == 1 && file_exists($conf['cachefile'])) {
      // check for the cached file
      // snag it and return it if it is still fresh
      $time_diff = time() - filemtime($conf['cachefile']);
      $expires_in = $conf['cachetime'] - $time_diff;
      if( $time_diff < $conf['cachetime']){
          if ( $debug == 1 ) {
            echo("DEBUG: Fetching data from cache. Expires in " . $expires_in . " seconds.\n");
          }
          $index_array = unserialize(file_get_contents($conf['cachefile']));
      }
   }

   if ( ! isset($index_array) || sizeof($index_array) == 0 ) {

      if ( $debug == 1 ) {
         echo("DEBUG: Querying GMond for new data\n");
      }
      // Set up for cluster summary
      $context = "index_array";
      include_once $conf['gweb_root'] . "/ganglia.php";
      Gmetad($conf['ganglia_ip'], $conf['ganglia_port']);

      foreach ( $index_array['cluster'] as $hostname => $elements ) {
         $hosts[] = $hostname;
      }
      asort($hosts);
      $index_array['hosts'] = $hosts;

      file_put_contents($conf['cachefile'], serialize($index_array));

   }

} // end of function get_metrics_cache () {

function getHostOverViewData($hostname, 
                             $metrics, 
                             $cluster,
                             $hosts_up, 
                             $hosts_down, 
                             $always_timestamp, 
                             $always_constant, 
                             $data) {
  $data->assign("extra", template("host_extra.tpl"));

  $data->assign("host", $hostname);
  $data->assign("node_image", node_image($metrics));

  if ($hosts_up)
    $data->assign("node_msg", "This host is up and running."); 
  else
    $data->assign("node_msg", "This host is down."); 

  # No reason to go on if this node is down.
  if ($hosts_down)
    return;

  foreach ($metrics as $name => $v) {
    if ($v['TYPE'] == "string" or $v['TYPE']=="timestamp" or
        (isset($always_timestamp[$name]) and $always_timestamp[$name])) {
      $s_metrics[$name] = $v;
    } elseif ($v['SLOPE'] == "zero" or
              (isset($always_constant[$name]) and $always_constant[$name])) {
      $c_metrics[$name] = $v;
    }
  }

  # in case this is not defined, set to LOCALTIME so uptime will be 0 in the display
  $boottime = null;
  if (isset($metrics['boottime']['VAL']))
    $boottime = $metrics['boottime']['VAL'];
  else
    $boottime = $cluster['LOCALTIME'];

  # Add the uptime metric for this host. Cannot be done in ganglia.php,
  # since it requires a fully-parsed XML tree. The classic contructor problem.
  $s_metrics['uptime']['TYPE'] = "string";
  $s_metrics['uptime']['VAL'] = uptime($cluster['LOCALTIME'] - $boottime);
  $s_metrics['uptime']['TITLE'] = "Uptime";

  # Add the gmond started timestamps & last reported time (in uptime format) from
  # the HOST tag:
  $s_metrics['gmond_started']['TYPE'] = "timestamp";
  $s_metrics['gmond_started']['VAL'] = $hosts_up['GMOND_STARTED'];
  $s_metrics['gmond_started']['TITLE'] = "Gmond Started";
  $s_metrics['last_reported']['TYPE'] = "string";
  $s_metrics['last_reported']['VAL'] = uptime($cluster['LOCALTIME'] - $hosts_up['REPORTED']);
  $s_metrics['last_reported']['TITLE'] = "Last Reported";

  $s_metrics['ip_address']['TITLE'] = "IP Address";
  $s_metrics['ip_address']['VAL'] = $hosts_up['IP'];
  $s_metrics['ip_address']['TYPE'] = "string";
  $s_metrics['location']['TITLE'] = "Location";
  $s_metrics['location']['VAL'] = $hosts_up['LOCATION'];
  $s_metrics['location']['TYPE'] = "string";

  # String metrics
  if (is_array($s_metrics)) {
    $s_metrics_data = array();
    ksort($s_metrics);
    foreach ($s_metrics as $name => $v) {
      # RFM - If units aren't defined for metric, make it be the empty string
      ! array_key_exists('UNITS', $v) and $v['UNITS'] = "";
      if (isset($v['TITLE'])) {
        $s_metrics_data[$name]["name"] = $v['TITLE'];
      } else {
        $s_metrics_data[$name]["name"] = $name;
      }
      if ($v['TYPE']=="timestamp" or 
          (isset($always_timestamp[$name]) and $always_timestamp[$name])) {
        $s_metrics_data[$name]["value"] = date("r", $v['VAL']);
      } else {
        $s_metrics_data[$name]["value"] = $v['VAL'] . " " . $v['UNITS'];
      }
    }
  }
  $data->assign("s_metrics_data", $s_metrics_data);

  # Constant metrics.
  $c_metrics_data = null;
  if (isset($c_metrics) and is_array($c_metrics)) {
    $c_metrics_data = array();
    ksort($c_metrics);
    foreach ($c_metrics as $name => $v) {
      if (isset($v['TITLE']))  {
        $c_metrics_data[$name]["name"] =  $v['TITLE'];
      } else {
        $c_metrics_data[$name]["name"] = $name;
      }
      $c_metrics_data[$name]["value"] = "$v[VAL] $v[UNITS]";
    }
  }
  $data->assign("c_metrics_data", $c_metrics_data);
}

function buildMetricMaps($metrics,
			 $always_timestamp,
			 $always_constant,
			 $baseGraphArgs) {
  $metricMap = NULL;
  $metricGroupMap = NULL;
  foreach ($metrics as $name => $metric) {
    if ($metric['TYPE'] == "string" or 
	$metric['TYPE'] == "timestamp" or
	(isset($always_timestamp[$name]) and $always_timestamp[$name])) {
    } elseif ($metric['SLOPE'] == "zero" or
	      (isset($always_constant[$name]) and $always_constant[$name])) {
    } else {
      $graphArgs = $baseGraphArgs . "&amp;v=$metric[VAL]&amp;m=$name";
      # Adding units to graph 2003 by Jason Smith <smithj4@bnl.gov>.
      if ($metric['UNITS']) {
	$encodeUnits = rawurlencode($metric['UNITS']);
	$graphArgs .= "&amp;vl=$encodeUnits";
      }
      if (isset($metric['TITLE'])) {
	$title = $metric['TITLE'];
	$encodeTitle = rawurlencode($title);
	$graphArgs .= "&amp;ti=$encodeTitle";
      }
      // dump_var($graphArgs, "graphArgs");

      $metricMap[$name]['graph'] = $graphArgs;
      $metricMap[$name]['description'] = 
	isset($metric['DESC']) ? $metric['DESC'] : '';
      $metricMap[$name]['title'] = 
	isset($metric['TITLE']) ? $metric['TITLE'] : '';

      # Setup an array of groups that can be used for sorting in group view
      if ( isset($metrics[$name]['GROUP']) ) {
	$groups = $metrics[$name]['GROUP'];
      } else {
	$groups = array("");
      }

      foreach ($groups as $group) {
	if (isset($metricGroupMap[$group])) {
	  $metricGroupMap[$group] = 
	    array_merge($metricGroupMap[$group], (array)$name);
	} else {
	  $metricGroupMap[$group] = array($name);
	}
      }
    } // if
  } // foreach
  return array($metricMap, $metricGroupMap);
}
?>


Fixed version (ground truth):
<?php

#
# Some common functions for the Ganglia PHP website.
# Assumes the Gmeta XML tree has already been parsed,
# and the global variables $metrics, $clusters, and $hosts
# have been set.
#

include_once ( dirname(__FILE__) . "/lib/json.php" );

#
# Load event API driver.
#
$driver = ucfirst(strtolower( !isset($conf['overlay_events_provider']) ? "Json" : $conf['overlay_events_provider'] ));
if (file_exists( dirname(__FILE__) . "/lib/Events/Driver_${driver}.php")) {
  include_once( dirname(__FILE__) . "/lib/Events/Driver_${driver}.php" );
}

#------------------------------------------------------------------------------
# Allows a form of inheritance for template files.
# If a file does not exist in the chosen template, the
# default is used. Cuts down on code duplication.
function template ($name)
{
   global $conf;

   $fn = "./templates/${conf['template_name']}/$name";
   $default = "./templates/default/$name";

   if (file_exists($fn)) {
      return $fn;
   }
   else {
      return $default;
   }
}

#------------------------------------------------------------------------------
# Creates a hidden input field in a form. Used to save CGI variables.
function hiddenvar ($name, $var)
{

   $hidden = "";
   if ($var) {
      #$url = rawurlencode($var);
      $hidden = "<input type=\"hidden\" name=\"$name\" value=\"$var\">\n";
   }
   return $hidden;
}

#------------------------------------------------------------------------------
# Gives a readable time string, from a "number of seconds" integer.
# Often used to compute uptime.
function uptime($uptimeS)
{
   $uptimeD=intval($uptimeS/86400);
   $uptimeS=$uptimeD ? $uptimeS % ($uptimeD*86400) : $uptimeS;
   $uptimeH=intval($uptimeS/3600);
   $uptimeS=$uptimeH ? $uptimeS % ($uptimeH*3600) : $uptimeS;
   $uptimeM=intval($uptimeS/60);
   $uptimeS=$uptimeM ? $uptimeS % ($uptimeM*60) : $uptimeS;

   $s = ($uptimeD!=1) ? "s" : "";
   return sprintf("$uptimeD day$s, %d:%02d:%02d",$uptimeH,$uptimeM,$uptimeS);
}

#------------------------------------------------------------------------------
# Try to determine a nodes location in the cluster. Attempts to find the
# LOCATION attribute first. Requires the host attribute array from 
# $hosts[$cluster][$name], where $name is the hostname.
# Returns [-1,-1,-1] if we could not determine location.
#
function findlocation($attrs)
{
   $rack=$rank=$plane=-1;

   $loc=$attrs['LOCATION'];
   if ($loc) {
      sscanf($loc, "%d,%d,%d", $rack, $rank, $plane);
      #echo "Found LOCATION: $rack, $rank, $plane.<br>";
   }
   if ($rack<0 or $rank<0) {
      # Try to parse the host name. Assumes a compute-<rack>-<rank>
      # naming scheme.
      $n=sscanf($attrs['NAME'], "compute-%d-%d", $rack, $rank);
      $plane=0;
   }
   return array($rack,$rank,$plane);
}


#------------------------------------------------------------------------------
function cluster_sum($name, $metrics)
{
   $sum = 0;

   foreach ($metrics as $host => $val)
      {
         if(isset($val[$name]['VAL'])) $sum += $val[$name]['VAL'];
      }

   return $sum;
}

#------------------------------------------------------------------------------
function cluster_min($name, $metrics)
{
   $min = "";

   foreach ($metrics as $host => $val)
      {
         $v = $val[$name]['VAL'];
         if (!is_numeric($min) or $min < $v)
            {
               $min = $v;
               $minhost = $host;
            }
      }
   return array($min, $minhost);
}

#------------------------------------------------------------------------------
#
# A useful function for giving the correct picture for a given
# load. Scope is "node | cluster | grid". Value is 0 <= v <= 1.
function load_image ($scope, $value)
{
   global $conf;

   $scaled_load = $value / $conf['load_scale'];
   if ($scaled_load>1.00) {
      $image = template("images/${scope}_overloaded.jpg");
   }
   else if ($scaled_load>=0.75) {
      $image = template("images/${scope}_75-100.jpg");
   }
   else if ($scaled_load >= 0.50) {
      $image = template("images/${scope}_50-74.jpg");
   }
   else if ($scaled_load>=0.25) {
      $image = template("images/${scope}_25-49.jpg");
   }
   else {
      $image = template("images/${scope}_0-24.jpg");
   }

   return $image;
}

#------------------------------------------------------------------------------
# A similar function that specifies the background color for a graph
# based on load. Quantizes the load figure into 6 sets.
function load_color ($value)
{
   global $conf;

   $scaled_load = $value / $conf['load_scale'];
   if ($scaled_load>1.00) {
      $color = $conf['load_colors']["100+"];
   }
   else if ($scaled_load>=0.75) {
      $color = $conf['load_colors']["75-100"];
   }
   else if ($scaled_load >= 0.50) {
      $color = $conf['load_colors']["50-75"];
   }
   else if ($scaled_load>=0.25) {
      $color = $conf['load_colors']["25-50"];
   }
   else if ($scaled_load < 0.0)
      $color = $conf['load_colors']["down"];
   else {
      $color = $conf['load_colors']["0-25"];
   }

   return $color;
}

#------------------------------------------------------------------------------
#
# Just a useful function to print the HTML for
# the load/death of a cluster node
function node_image ($metrics)
{
   global $hosts_down;

   # More rigorous checking if variables are set before trying to use them.
   if ( isset($metrics['cpu_num']['VAL']) and $metrics['cpu_num']['VAL'] != 0 ) {
		$cpu_num = $metrics['cpu_num']['VAL'];
   } else {
		$cpu_num = 1;
   }

   if ( isset($metrics['load_one']['VAL']) ) {
		$load_one = $metrics['load_one']['VAL'];
   } else {
		$load_one = 0;
   }

   $value = $load_one / $cpu_num;

   # Check if the host is down
   # RFM - Added isset() check to eliminate error messages in ssl_error_log
   if (isset($hosts_down) and $hosts_down)
         $image = template("images/node_dead.jpg");
   else
         $image = load_image("node", $value);

   return $image;
}

#------------------------------------------------------------------------------
#
# Finds the min/max over a set of metric graphs. Nodes is
# an array keyed by host names.
#
function find_limits($nodes, $metricname)
{
   global $conf, $metrics, $clustername, $rrd_dir, $start, $end, $rrd_options;

   if (!count($metrics))
      return array(0, 0);

   $firsthost = key($metrics);
   
   if (array_key_exists($metricname,$metrics[$firsthost])) {
     if ($metrics[$firsthost][$metricname]['TYPE'] == "string"
        or $metrics[$firsthost][$metricname]['SLOPE'] == "zero")
           return array(0,0);
   }
   else {
     return array(0,0);
   }

   $max=0;
   $min=0;
   if ($conf['graph_engine'] == "graphite") {
     $target = $conf['graphite_prefix'] . $clustername . ".[a-zA-Z0-9]*." . $metricname . ".sum";
     $raw_highestMax = file_get_contents($conf['graphite_url_base'] . "?target=highestMax(" . $target . ",1)&from=" . $start . "&until=" . $end . "&format=json");
     $highestMax = json_decode($raw_highestMax, TRUE);
     $highestMaxDatapoints = $highestMax[0]['datapoints'];
     $maxdatapoints = array();
     foreach ( $highestMaxDatapoints as $datapoint ) {
       array_push($maxdatapoints, $datapoint[0]);
     }
     $max = max($maxdatapoints);
   }
   else {
     foreach ( $nodes as $host => $value ) {
       $out = array();

       $rrd_dir = "${conf['rrds']}/$clustername/$host";
       $rrd_file = "$rrd_dir/$metricname.rrd";
       if (file_exists($rrd_file)) {
          if ( extension_loaded( 'rrd' ) ) {
            $values = rrd_fetch($rrd_file,
              array(
                "--start", $start,
                "--end", $end,
                "AVERAGE"
              )
            );

            $values = (array_filter(array_values($values['data']['sum']), 'is_finite'));
            $thismax = max($values);
            $thismin = min($values);
          } else {
            $command = $conf['rrdtool'] . " graph /dev/null $rrd_options ".
               "--start $start --end $end ".
               "DEF:limits='$rrd_dir/$metricname.rrd':'sum':AVERAGE ".
               "PRINT:limits:MAX:%.2lf ".
               "PRINT:limits:MIN:%.2lf";
            exec($command, $out);
            if(isset($out[1])) {
               $thismax = $out[1];
            } else {
               $thismax = NULL;
            }
            if (!is_numeric($thismax)) continue;
            $thismin=$out[2];
            if (!is_numeric($thismin)) continue;
          }

          if ($max < $thismax) $max = $thismax;

          if ($min > $thismin) $min = $thismin;
          #echo "$host: $thismin - $thismax (now $value)<br>\n";
       }
     }
   }
   return array($min, $max);
}

#------------------------------------------------------------------------------
#
# Finds the avg of the given cluster & metric from the summary rrds.
#
function find_avg($clustername, $hostname, $metricname)
{
    global $conf, $start, $end, $rrd_options;
    $avg = 0;

    if ($hostname)
        $sum_dir = "${conf['rrds']}/$clustername/$hostname";
    else
        $sum_dir = "${conf['rrds']}/$clustername/__SummaryInfo__";

    $command = $conf['rrdtool'] . " graph /dev/null $rrd_options ".
        "--start $start --end $end ".
        "DEF:avg='$sum_dir/$metricname.rrd':'sum':AVERAGE ".
        "PRINT:avg:AVERAGE:%.2lf ";
    exec($command, $out);
    if ( isset($out[1]) ) 
      $avg = $out[1];
    else
      $avg = 0;
    #echo "$sum_dir: avg($metricname)=$avg<br>\n";
    return $avg;
}

#------------------------------------------------------------------------------
# Alternate between even and odd row styles.
function rowstyle()
{
   static $style;

   if ($style == "even") { $style = "odd"; }
   else { $style = "even"; }

   return $style;
}

#------------------------------------------------------------------------------
# Return a version of the string which is safe for display on a web page.
# Potentially dangerous characters are converted to HTML entities.  
# Resulting string is not URL-encoded.
function clean_string( $string )
{
  return htmlentities( $string );
}
#------------------------------------------------------------------------------
function sanitize ( $string ) {
  return  escapeshellcmd( clean_string( rawurldecode( $string ) ) ) ;
}

#------------------------------------------------------------------------------
# If arg is a valid number, return it.  Otherwise, return null.
function clean_number( $value )
{
  return is_numeric( $value ) ? $value : null;
}

#------------------------------------------------------------------------------
# Return true if string is a 3 or 6 character hex color.Return false otherwise.
function is_valid_hex_color( $string )
{
  $return_value = false;
  if( strlen( $string ) == 6 || strlen( $string ) == 3 ) {
    if( preg_match( '/^[0-9a-fA-F]+$/', $string ) ) {
      $return_value = true;
    }
  }
  return $return_value;
    
}

#------------------------------------------------------------------------------
# Allowed view name characters are alphanumeric plus space, dash and underscore
function is_proper_view_name( $string )
{
  if(preg_match("/[^a-zA-z0-9_\-\ ]/", $string)){
    return false;
  } else {
    return true;
  }
}


#------------------------------------------------------------------------------
# Return a shortened version of a FQDN
# if "hostname" is numeric only, assume it is an IP instead
# 
function strip_domainname( $hostname ) {
    $postition = strpos($hostname, '.');
    $name = substr( $hostname , 0, $postition );
    if ( FALSE === $postition || is_numeric($name) ) {
        return $hostname;
    } else {
        return $name;
    }
}

#------------------------------------------------------------------------------
# Read a file containing key value pairs
function file_to_hash($filename, $sep)
{
  
  $lines = file($filename, FILE_IGNORE_NEW_LINES);
  
  foreach ($lines as $line) 
  {
    list($k, $v) = explode($sep, rtrim($line));
    $params[$k] = $v;
  }

  return $params;
}

#------------------------------------------------------------------------------
# Read a file containing key value pairs
# Multiple values permitted for each key
function file_to_hash_multi($filename, $sep)
{
 
  $lines = file($filename);
 
  foreach ($lines as $line)
  {
    list($k, $v) = explode($sep, rtrim($line));
    $params[$k][] = $v;
  }

  return $params;
}

#------------------------------------------------------------------------------
# Obtain a list of distinct values from an array of arrays
function hash_get_distinct_values($h)
{
  $values = array();
  $values_done = array();
  foreach($h as $k => $v)
  {
    if($values_done[$v] != "x")
    {
      $values_done[$v] = "x";
      $values[] = $v;
    } 
  }
  return $values;
}

$filter_defs = array();

#------------------------------------------------------------------------------
# Scan $conf['filter_dir'] and populate $filter_defs
function discover_filters()
{
  global $conf, $filter_defs;

  # Check whether filtering is configured or not
  if(!isset($conf['filter_dir']))
    return;

  if(!is_dir($conf['filter_dir']))
  {
    error_log("discover_filters(): not a directory: ${conf['filter_dir']}");
    return;
  }

  if($dh = opendir($conf['filter_dir']))
  {
    while(($filter_conf_filename = readdir($dh)) !== false) {
      if(!is_dir($filter_conf_filename))
      {
        # Parse the file contents
        $full_filename = "${conf['filter_dir']}/$filter_conf_filename";
        $filter_params = file_to_hash($full_filename, '=');
        $filter_shortname = $filter_params["shortname"];
        $filter_type = $filter_params["type"];
        if($filter_type = "url")
        {
          $filter_data_url = $filter_params['url'];
          $filter_defs[$filter_shortname] = $filter_params;
          $filter_defs[$filter_shortname]["data"] = file_to_hash($filter_data_url, ',');
          $filter_defs[$filter_shortname]["choices"] = hash_get_distinct_values($filter_defs[$filter_shortname]["data"]);
        }
      }
    }
    closedir($dh);
  }
}

$filter_permit_list = NULL;

#------------------------------------------------------------------------------
# Initialise the filter permit list, if necessary
function filter_init()
{
   global $conf, $filter_permit_list, $filter_defs, $choose_filter;

   if(!is_null($filter_permit_list))
   {
      return;
   }

   if(!isset($conf['filter_dir']))
   {
      $filter_permit_list = FALSE;
      return;
   }

   $filter_permit_list = array();
   $filter_count = 0;

   foreach($choose_filter as $filter_shortname => $filter_choice)
   {
      if($filter_choice == "")
         continue; 

      $filter_params = $filter_defs[$filter_shortname];
      if($filter_count == 0)
      {
         foreach($filter_params["data"] as $key => $value)
         {
            if($value == $filter_choice)
               $filter_permit_list[$key] = $key;
         }
      }
      else
      {
         foreach($filter_permit_list as $key => $value)
         {
            $remove_key = TRUE;
            if(isset($filter_params["data"][$key]))
            {
               if($filter_params["data"][$key] == $filter_choice)
               {
                  $remove_key = FALSE;
               } 
            }
            if($remove_key)
            {
               unset($filter_permit_list[$key]);
            }
         }
      }
      $filter_count++;
   }

   if($filter_count == 0)
      $filter_permit_list = FALSE;

}

#------------------------------------------------------------------------------
# Decide whether the given source is permitted by the filters, if any
function filter_permit($source_name)
{
   global $filter_permit_list;

   filter_init();
   
   # Handle the case where filtering is not active
   if(!is_array($filter_permit_list))
      return true;

   return isset($filter_permit_list[$source_name]);
}


///////////////////////////////////////////////////////////////////////////////
// Get all the available views
///////////////////////////////////////////////////////////////////////////////
function get_available_views() {
  global $conf;
  
  /* -----------------------------------------------------------------------
  Find available views by looking in the GANGLIA_DIR/conf directory
  anything that matches view_*.json. Read them all and build a available_views
  array
  ----------------------------------------------------------------------- */
  $available_views = array();

  if ($handle = opendir($conf['views_dir'])) {

      while (false !== ($file = readdir($handle))) {

	if ( preg_match("/^view_(.*)\.json$/", $file, $out) ) {
	  $view_config_file = $conf['views_dir'] . "/" . $file;
	  if ( ! is_file ($view_config_file) ) {
	    echo("Can't read view config file " . $view_config_file . ". Please check permissions");
	  }

	  $view = json_decode(file_get_contents($view_config_file), TRUE);
	  // Check whether view type has been specified ie. regex. 
          // If not it's standard view
	  $view_type = 
            isset($view['view_type']) ? $view['view_type'] : "standard";
          $default_size = isset($view['default_size']) ? 
	    $view['default_size'] : $conf['default_view_graph_size'];
	  $available_views[] = array ("file_name" => $view_config_file, 
                                      "view_name" => $view['view_name'],
                                      "default_size" => $default_size, 
                                      "items" => $view['items'], 
                                      "view_type" => $view_type);

	  unset($view);

	}
      }

      closedir($handle);
  }

  foreach ($available_views as $key => $row) {
    $name[$key]  = strtolower($row['view_name']);
  }

  @array_multisort($name,SORT_ASC, $available_views);

  return $available_views;

}

///////////////////////////////////////////////////////////////////////////////
// Get image graph URLS
// This function returns an array of graph URLs to be used when rendering the 
// view. It returns only the base ie. cluster, host, metric information. 
// It is up to the caller to add proper size information, time ranges etc.
///////////////////////////////////////////////////////////////////////////////
function get_view_graph_elements($view) {

  global $conf, $index_array;

  retrieve_metrics_cache();

  $view_elements = array();

  // set the default size from the view or global config
  if ( isset($conf['default_view_graph_size']) ) {
    $default_size = $conf['default_view_graph_size'];
  }
  if ( isset($view['default_size']) ) {
    $default_size = $view['default_size'];
  }


  switch ( $view['view_type'] ) {

    case "standard":
    // Does view have any items/graphs defined
    if ( sizeof($view['items']) == 0 ) {
      continue;
      // print "No graphs defined for this view. Please add some";
    } else {


      // Loop through graph items
      foreach ( $view['items'] as $item_id => $item ) {

	// Check if item is an aggregate graph
	if ( isset($item['aggregate_graph']) ) {

	  foreach ( $item['host_regex'] as $reg_id => $regex_array ) {
	    $graph_args_array[] = "hreg[]=" . urlencode($regex_array["regex"]);
	  }

	  if ( isset($item['metric_regex']) ) {
	    foreach ( $item['metric_regex'] as $reg_id => $regex_array ) {
	      $graph_args_array[] = "mreg[]=" . urlencode($regex_array["regex"]);
              $mreg[] = $regex_array["regex"];
	    }
	  }

          if ( isset($item['size']) ) {
            $graph_args_array[] = "z=" . $item['size'];
          } else {
            $graph_args_array[] = "z=" . $default_size;
          }


	  // If graph type is not specified default to line graph
	  if ( isset($item['graph_type']) && in_array($item['graph_type'], array('line', 'stack') ) )
	    $graph_args_array[] = "gtype=" . $item['graph_type'];
	  else
	    $graph_args_array[] = "gtype=line";

	  if (isset($item['upper_limit']))
	    $graph_args_array[] = "x=" .$item['upper_limit'];

	  if (isset($item['lower_limit']))
	    $graph_args_array[] = "n=" .$item['lower_limit'];

	  if (isset($item['vertical_label']))
	    $graph_args_array[] = "vl=" . urlencode($item['vertical_label']);

	  if (isset($item['title']))
	    $graph_args_array[] = "title=" . urlencode($item['title']);

	  if ( isset($item['metric']) ) {
	    $graph_args_array[] = "m=" . $item['metric'];
	  }

          if ( isset($item['glegend']) )
            $graph_args_array[] = "glegend=" . $item["glegend"];

	  if ( isset($item['cluster']) ) {
	    $graph_args_array[] = "c=" . urlencode($item['cluster']);
	  }

	  if ( isset($item['exclude_host_from_legend_label']) ) {
	    $graph_args_array[] = "lgnd_xh=" . $item['exclude_host_from_legend_label'];
	  }
	  
	  $graph_args_array[] = "aggregate=1";
	  $view_elements[] = array ( "graph_args" => join("&", $graph_args_array), 
	      "aggregate_graph" => 1,
	      "name" => isset($item['title']) && $item['title'] != "" ? $item['title'] : $mreg[0] . " Aggregate graph"
	  );

	  unset($graph_args_array);
          
        // Check whether it's a composite graph/report. It needs to have an item id
	} else if ( $item['item_id'] ) {
          
	  $graph_args_array[] = "vn=" . $view['view_name'];
          $graph_args_array[] = "item_id=" . $item['item_id'];
	  $view_elements[] = array ( "graph_args" => join("&", $graph_args_array)
	  );
          unset($graph_args_array);
          
	// It's standard metric graph          
        } else {
	  // Is it a metric or a graph(report)
	  if ( isset($item['metric']) ) {
	    $graph_args_array[] = "m=" . $item['metric'];
	    $name = $item['metric'];
	  } else {
	    $graph_args_array[] = "g=" . urlencode($item['graph']);
	    $name = $item['graph'];
	  }
          if ( isset($item['size']) ) {
            $graph_args_array[] = "z=" . $item['size'];
          } else {
            $graph_args_array[] = "z=" . $default_size;
          }

	  if (isset($item['hostname'])) {
            $hostname = $item['hostname'];
            $cluster = array_key_exists($hostname, $index_array['cluster']) ?
	      $index_array['cluster'][$hostname][0] : NULL;
          } else if (isset($item['cluster'])) {
	    $hostname = "";
            $cluster = $item['cluster'];
	  } else {
            $hostname = "";
            $cluster = "";
	  }

	  $graph_args_array[] = "h=" . urlencode($hostname);
	  $graph_args_array[] = "c=" . urlencode($cluster);

	  if (isset($item['vertical_label']))
	    $graph_args_array[] = "vl=" . urlencode($item['vertical_label']);

	  if (isset($item['title']))
	    $graph_args_array[] = "title=" . urlencode($item['title']);

          if (isset($item['warning'])) {
            $view_e['warning'] = $item['warning'];
            $graph_args_array[] = "warn=" . $item['warning'];
          }
          if (isset($item['critical'])) {
            $view_e['critical'] = $item['critical'];
            $graph_args_array[] = "crit=" . $item['critical'];
          }


          $view_e["graph_args"] = join("&", $graph_args_array);
          $view_e['hostname'] = $hostname;
          $view_e['cluster'] = $cluster;
          $view_e['name'] = $name;
                  
	  $view_elements[] = $view_e;

          unset($view_e);
	  unset($graph_args_array);

	}

      } // end of foreach ( $view['items']
    } // end of if ( sizeof($view['items'])
    break;
    ;;

    ///////////////////////////////////////////////////////////////////////////
    // Currently only supports matching hosts.
    ///////////////////////////////////////////////////////////////////////////
    case "regex":
      foreach ( $view['items'] as $item_id => $item ) {
	// Is it a metric or a graph(report)
	if ( isset($item['metric']) ) {
	  $metric_suffix = "m=" . $item['metric'];
	  $name = $item['metric'];
	} else {
	  $metric_suffix = "g=" . $item['graph'];
	  $name = $item['graph'];
	}

	// Find hosts matching a criteria
	$query = $item['hostname'];
	foreach ( $index_array['hosts'] as $key => $host_name ) {
	  if ( preg_match("/$query/", $host_name ) ) {
	    $clusters = $index_array['cluster'][$host_name];
	    foreach ($clusters AS $cluster) {
	    $graph_args_array[] = "h=" . urlencode($host_name);
	    $graph_args_array[] = "c=" . urlencode($cluster);

	    $view_elements[] = array ( "graph_args" => $metric_suffix . "&" . join("&", $graph_args_array), 
	      "hostname" => $host_name,
	      "cluster" => $cluster,
	      "name" => $name);

	    unset($graph_args_array);
            }

	  }
	}
	
      } // end of foreach ( $view['items'] as $item_id => $item )
    break;;
  
  } // end of switch ( $view['view_type'] ) {


  return ($view_elements);

}

function legendEntry($vname, $legend_items) {
  $legend = "";
  if (in_array("now", $legend_items))
    $legend .= "VDEF:{$vname}_last={$vname},LAST ";

  if (in_array("min", $legend_items))
    $legend .= "VDEF:{$vname}_min={$vname},MINIMUM ";

  if (in_array("avg", $legend_items))
    $legend .= "VDEF:{$vname}_avg={$vname},AVERAGE ";

  if (in_array("max", $legend_items))
    $legend .= "VDEF:{$vname}_max={$vname},MAXIMUM ";

  $terminate = FALSE;
  if (in_array("now", $legend_items)) {
    $legend .= "GPRINT:'{$vname}_last':'Now\:%5.1lf%s";
    $terminate = TRUE;
  }

  if (in_array("min", $legend_items)) {
    if ($terminate)
      $legend .= "' ";
    $legend .= "GPRINT:'{$vname}_min':'Min\:%5.1lf%s";
    $terminate = TRUE;
  }

  if (in_array("avg", $legend_items)) {
    if ($terminate)
      $legend .= "' ";
    $legend .= "GPRINT:'{$vname}_avg':'Avg\:%5.1lf%s";
    $terminate = TRUE;
  }

  if (in_array("max", $legend_items)) {
    if ($terminate)
      $legend .= "' ";
    $legend .= "GPRINT:'{$vname}_max':'Max\:%5.1lf%s";
    $terminate = TRUE;
  }
  
  if ($terminate)
    $legend .= "\\l' ";

  return $legend;
}

/**
 * Check if current user has a privilege (view, edit, etc) on a resource.
 * If resource is unspecified, we assume GangliaAcl::ALL.
 *
 * Examples
 *   checkAccess( GangliaAcl::ALL_CLUSTERS, GangliaAcl::EDIT, $conf ); // user has global edit?
 *   checkAccess( GangliaAcl::ALL_CLUSTERS, GangliaAcl::VIEW, $conf ); // user has global view?
 *   checkAccess( $cluster, GangliaAcl::EDIT, $conf ); // user can edit current cluster?
 *   checkAccess( 'cluster1', GangliaAcl::EDIT, $conf ); // user has edit privilege on cluster1?
 *   checkAccess( 'cluster1', GangliaAcl::VIEW, $conf ); // user has view privilege on cluster1?
 */
function checkAccess($resource, $privilege, $conf) {
  
  if(!is_array($conf)) {
    trigger_error('checkAccess: $conf is not an array.', E_USER_ERROR);
  }
  if(!isSet($conf['auth_system'])) {
    trigger_error("checkAccess: \$conf['auth_system'] is not defined.", E_USER_ERROR);
  }
  
  switch( $conf['auth_system'] ) {
    case 'readonly':
      $out = ($privilege == GangliaAcl::VIEW);
      break;
      
    case 'enabled':
      // TODO: 'edit' needs to check for writeability of data directory.  error log if edit is allowed but we're unable to due to fs problems.
      
      $acl = GangliaAcl::getInstance();
      $auth = GangliaAuth::getInstance();
      
      if(!$auth->isAuthenticated()) {
        $user = GangliaAcl::GUEST;
      } else {
        $user = $auth->getUser();
      }
      
      if(!$acl->has($resource)) {
        $resource = GangliaAcl::ALL_CLUSTERS;
      }
      
      $out = false;
      if($acl->hasRole($user)) {
        $out = (bool) $acl->isAllowed($user, $resource, $privilege);
      }
      // error_log("checkAccess() user=$user, resource=$resource, priv=$privilege == $out");
      break;
    
    case 'disabled':
      $out = true;
      break;
    
    default:
      trigger_error( "Invalid value '".$conf['auth_system']."' for \$conf['auth_system'].", E_USER_ERROR );
      return false;
  }
  
  return $out;
}

function viewId($view_name) {
  $id = 'v_' . preg_replace('/[^a-zA-Z0-9_]/', '_', $view_name);
  return $id;
}

///////////////////////////////////////////////////////////////////////////////
// Taken from
// http://au2.php.net/manual/en/function.json-encode.php#80339
// Pretty print JSON 
///////////////////////////////////////////////////////////////////////////////
function json_prettyprint($json) 
{ 
    $tab = "  "; 
    $new_json = ""; 
    $indent_level = 0; 
    $in_string = false; 

    $len = strlen($json); 

    for($c = 0; $c < $len; $c++) 
    { 
        $char = $json[$c]; 
        switch($char) 
        { 
            case '{': 
            case '[': 
                if(!$in_string) 
                { 
                    $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); 
                    $indent_level++; 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case '}': 
            case ']': 
                if(!$in_string) 
                { 
                    $indent_level--; 
                    $new_json .= "\n" . str_repeat($tab, $indent_level) . $char; 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case ',': 
                if(!$in_string) 
                { 
                    $new_json .= ",\n" . str_repeat($tab, $indent_level); 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case ':': 
                if(!$in_string) 
                { 
                    $new_json .= ": "; 
                } 
                else 
                { 
                    $new_json .= $char; 
                } 
                break; 
            case '"': 
                if($c > 0 && $json[$c-1] != '\\') 
                { 
                    $in_string = !$in_string; 
                } 
            default: 
                $new_json .= $char; 
                break;                    
        } 
    } 

    return $new_json; 
} 

function ganglia_cache_metrics() {
    global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics;

    if (!isset($debug)) { $debug = 0; }

    if($conf['cachedata'] == 1 && file_exists($conf['cachefile'])) {
        // check for the cached file
        // snag it and return it if it is still fresh
        $time_diff = time() - filemtime($conf['cachefile']);
        $expires_in = $conf['cachetime'] - $time_diff;
        if( $time_diff < $conf['cachetime']){
                if ( $debug == 1 ) {
                  echo("DEBUG: Fetching data from cache. Expires in " . $expires_in . " seconds.\n");
                }
                $index_array = unserialize(file_get_contents($conf['cachefile']));
        }
    }

    if ( ! isset($index_array) ) {

        if ( $debug == 1 ) {
            echo("DEBUG: Querying GMond for new data\n");
        }
        // Set up for cluster summary
        $context = "index_array";
        include_once $conf['gweb_root'] . "/functions.php";
        include_once $conf['gweb_root'] . "/ganglia.php";
        include_once $conf['gweb_root'] . "/get_ganglia.php";

        foreach ( $index_array['cluster'] as $hostname => $elements ) {
            $hosts[] = $hostname;
        }

        asort($hosts);
        $index_array['hosts'] = $hosts;

        file_put_contents($conf['cachefile'], serialize($index_array));

    }

} // end function ganglia_cache_metrics


//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
function build_aggregate_graph_config ($graph_type, 
                                       $line_width, 
                                       $hreg,
                                       $mreg,
                                       $glegend,
                                       $exclude_host_from_legend_label) {

  global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics;
  
  retrieve_metrics_cache();
  
  $color_count = sizeof($conf['graph_colors']);

  $graph_config["report_name"]=isset($mreg)  ?  sanitize(implode($mreg))   : NULL;
  $graph_config["title"]=isset($mreg)  ?  sanitize(implode($mreg))   : NULL;
  $graph_config["glegend"]=isset($glegend) ? sanitize($glegend) : "show";

  $counter = 0;

  ///////////////////////////////////////////////////////////////////////////
  // Find matching hosts    
  foreach ( $hreg as $key => $query ) {
    foreach ( $index_array['hosts'] as $key => $host_name ) {
      if ( preg_match("/$query/i", $host_name ) ) {
        // We can have same hostname in multiple clusters
        foreach ($index_array['cluster'][$host_name] AS $cluster) {
            $matches[] = $host_name . "|" . $cluster;
        }
      }
    }
  } 

  if( isset($mreg)) {
    // Find matching metrics
    foreach ( $mreg as $key => $query ) {
      foreach ( $index_array['metrics'] as $key => $m_name ) {
        if ( preg_match("/$query/i", $key, $metric_subexpr ) ) {
          if (isset($metric_subexpr) && count($metric_subexpr) > 1) {
            $legend = array();
            for ($i = 1; $i < count($metric_subexpr); $i++) {
              $legend[] = $metric_subexpr[$i];
            }
	    $metric_matches[$key] = implode(' ', $legend);
          } else {
            $metric_matches[$key] = $key;
          }
        }
      }
    }
    ksort($metric_matches);
  }
  
  if( isset($metric_matches)){
    $metric_matches_unique = array_unique($metric_matches);
  }
  else{
    $metric_matches_unique = array($metric_name => $metric_name);
  }

  if ( isset($matches)) {

    $matches_unique = array_unique($matches);

    // Create graph_config series from matched hosts and metrics
    foreach ( $matches_unique as $key => $host_cluster ) {

      $out = explode("|", $host_cluster);

      $host_name = $out[0];
      $cluster_name = $out[1];

      foreach ( $metric_matches_unique as $m_name => $legend ) {

        // We need to cycle the available colors
        $color_index = $counter % $color_count;

        // next loop if there is no metric for this hostname
        if( !in_array($host_name, $index_array['metrics'][$m_name]))
          continue;

        $label = '';
        if ($exclude_host_from_legend_label) {
	  $label = $legend;
        } else {
          if ($conf['strip_domainname'] == True )
            $label = strip_domainname($host_name);
          else
            $label = $host_name;

 	  if (isset($metric_matches) and sizeof($metric_matches_unique) > 1)
            $label .= " $legend";
	}

        $graph_config['series'][] = array ( "hostname" => $host_name , "clustername" => $cluster_name,
          "metric" => $m_name,  "color" => $conf['graph_colors'][$color_index], "label" => $label, "line_width" => $line_width, "type" => $graph_type);

        $counter++;

      }
      }
   }

   return $graph_config;

} // function build_aggregate_graph_config () {


//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
function retrieve_metrics_cache () {

   global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics, $context;

   if($conf['cachedata'] == 1 && file_exists($conf['cachefile'])) {
      // check for the cached file
      // snag it and return it if it is still fresh
      $time_diff = time() - filemtime($conf['cachefile']);
      $expires_in = $conf['cachetime'] - $time_diff;
      if( $time_diff < $conf['cachetime']){
          if ( $debug == 1 ) {
            echo("DEBUG: Fetching data from cache. Expires in " . $expires_in . " seconds.\n");
          }
          $index_array = unserialize(file_get_contents($conf['cachefile']));
      }
   }

   if ( ! isset($index_array) || sizeof($index_array) == 0 ) {

      if ( $debug == 1 ) {
         echo("DEBUG: Querying GMond for new data\n");
      }
      // Set up for cluster summary
      $context = "index_array";
      include_once $conf['gweb_root'] . "/ganglia.php";
      Gmetad($conf['ganglia_ip'], $conf['ganglia_port']);

      foreach ( $index_array['cluster'] as $hostname => $elements ) {
         $hosts[] = $hostname;
      }
      asort($hosts);
      $index_array['hosts'] = $hosts;

      file_put_contents($conf['cachefile'], serialize($index_array));

   }

} // end of function get_metrics_cache () {

function getHostOverViewData($hostname, 
                             $metrics, 
                             $cluster,
                             $hosts_up, 
                             $hosts_down, 
                             $always_timestamp, 
                             $always_constant, 
                             $data) {
  $data->assign("extra", template("host_extra.tpl"));

  $data->assign("host", $hostname);
  $data->assign("node_image", node_image($metrics));

  if ($hosts_up)
    $data->assign("node_msg", "This host is up and running."); 
  else
    $data->assign("node_msg", "This host is down."); 

  # No reason to go on if this node is down.
  if ($hosts_down)
    return;

  foreach ($metrics as $name => $v) {
    if ($v['TYPE'] == "string" or $v['TYPE']=="timestamp" or
        (isset($always_timestamp[$name]) and $always_timestamp[$name])) {
      $s_metrics[$name] = $v;
    } elseif ($v['SLOPE'] == "zero" or
              (isset($always_constant[$name]) and $always_constant[$name])) {
      $c_metrics[$name] = $v;
    }
  }

  # in case this is not defined, set to LOCALTIME so uptime will be 0 in the display
  $boottime = null;
  if (isset($metrics['boottime']['VAL']))
    $boottime = $metrics['boottime']['VAL'];
  else
    $boottime = $cluster['LOCALTIME'];

  # Add the uptime metric for this host. Cannot be done in ganglia.php,
  # since it requires a fully-parsed XML tree. The classic contructor problem.
  $s_metrics['uptime']['TYPE'] = "string";
  $s_metrics['uptime']['VAL'] = uptime($cluster['LOCALTIME'] - $boottime);
  $s_metrics['uptime']['TITLE'] = "Uptime";

  # Add the gmond started timestamps & last reported time (in uptime format) from
  # the HOST tag:
  $s_metrics['gmond_started']['TYPE'] = "timestamp";
  $s_metrics['gmond_started']['VAL'] = $hosts_up['GMOND_STARTED'];
  $s_metrics['gmond_started']['TITLE'] = "Gmond Started";
  $s_metrics['last_reported']['TYPE'] = "string";
  $s_metrics['last_reported']['VAL'] = uptime($cluster['LOCALTIME'] - $hosts_up['REPORTED']);
  $s_metrics['last_reported']['TITLE'] = "Last Reported";

  $s_metrics['ip_address']['TITLE'] = "IP Address";
  $s_metrics['ip_address']['VAL'] = $hosts_up['IP'];
  $s_metrics['ip_address']['TYPE'] = "string";
  $s_metrics['location']['TITLE'] = "Location";
  $s_metrics['location']['VAL'] = $hosts_up['LOCATION'];
  $s_metrics['location']['TYPE'] = "string";

  # String metrics
  if (is_array($s_metrics)) {
    $s_metrics_data = array();
    ksort($s_metrics);
    foreach ($s_metrics as $name => $v) {
      # RFM - If units aren't defined for metric, make it be the empty string
      ! array_key_exists('UNITS', $v) and $v['UNITS'] = "";
      if (isset($v['TITLE'])) {
        $s_metrics_data[$name]["name"] = $v['TITLE'];
      } else {
        $s_metrics_data[$name]["name"] = $name;
      }
      if ($v['TYPE']=="timestamp" or 
          (isset($always_timestamp[$name]) and $always_timestamp[$name])) {
        $s_metrics_data[$name]["value"] = date("r", $v['VAL']);
      } else {
        $s_metrics_data[$name]["value"] = $v['VAL'] . " " . $v['UNITS'];
      }
    }
  }
  $data->assign("s_metrics_data", $s_metrics_data);

  # Constant metrics.
  $c_metrics_data = null;
  if (isset($c_metrics) and is_array($c_metrics)) {
    $c_metrics_data = array();
    ksort($c_metrics);
    foreach ($c_metrics as $name => $v) {
      if (isset($v['TITLE']))  {
        $c_metrics_data[$name]["name"] =  $v['TITLE'];
      } else {
        $c_metrics_data[$name]["name"] = $name;
      }
      $c_metrics_data[$name]["value"] = "$v[VAL] $v[UNITS]";
    }
  }
  $data->assign("c_metrics_data", $c_metrics_data);
}

function buildMetricMaps($metrics,
			 $always_timestamp,
			 $always_constant,
			 $baseGraphArgs) {
  $metricMap = NULL;
  $metricGroupMap = NULL;
  foreach ($metrics as $name => $metric) {
    if ($metric['TYPE'] == "string" or 
	$metric['TYPE'] == "timestamp" or
	(isset($always_timestamp[$name]) and $always_timestamp[$name])) {
    } elseif ($metric['SLOPE'] == "zero" or
	      (isset($always_constant[$name]) and $always_constant[$name])) {
    } else {
      $graphArgs = $baseGraphArgs . "&amp;v=$metric[VAL]&amp;m=$name";
      # Adding units to graph 2003 by Jason Smith <smithj4@bnl.gov>.
      if ($metric['UNITS']) {
	$encodeUnits = rawurlencode($metric['UNITS']);
	$graphArgs .= "&amp;vl=$encodeUnits";
      }
      if (isset($metric['TITLE'])) {
	$title = $metric['TITLE'];
	$encodeTitle = rawurlencode($title);
	$graphArgs .= "&amp;ti=$encodeTitle";
      }
      // dump_var($graphArgs, "graphArgs");

      $metricMap[$name]['graph'] = $graphArgs;
      $metricMap[$name]['description'] = 
	isset($metric['DESC']) ? $metric['DESC'] : '';
      $metricMap[$name]['title'] = 
	isset($metric['TITLE']) ? $metric['TITLE'] : '';

      # Setup an array of groups that can be used for sorting in group view
      if ( isset($metrics[$name]['GROUP']) ) {
	$groups = $metrics[$name]['GROUP'];
      } else {
	$groups = array("");
      }

      foreach ($groups as $group) {
	if (isset($metricGroupMap[$group])) {
	  $metricGroupMap[$group] = 
	    array_merge($metricGroupMap[$group], (array)$name);
	} else {
	  $metricGroupMap[$group] = array($name);
	}
      }
    } // if
  } // foreach
  return array($metricMap, $metricGroupMap);
}
?>