#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2018, Lime Technology
 * Copyright 2012-2018, Bergware International.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */
?>
<?
$var   = parse_ini_file("/var/local/emhttp/var.ini");
$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);

$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/webGui/include/CustomMerge.php";

$notify = "$docroot/webGui/scripts/notify";
$unraid = parse_plugin_cfg("dynamix",true);
$output = $unraid['notify']['report'];
$server = strtoupper($var['NAME']);
$data   = [];
$parity = false;
$cache  = false;
$error0 = 0;
$error1 = 0;
$error2 = 0;
$error3 = 0;

function plus($val, $word, $last) {
  return $val>0 ? (($val || $last) ? ($val.' '.$word.($val!=1?'s':'').($last ?'':', ')) : '') : '';
}
function my_temp($value) {
  global $unraid;
  if ($value=='*') return ' - standby';
  $unit = $unraid['display']['unit'];
  return ' - active '.($unit=='F' ? round(9/5*$value+32) : str_replace('.', $unraid['display']['number'][0], $value)).' '.$unit;
}
function my_disk($name) {
  return ucfirst(preg_replace('/(\d+)$/',' $1',$name));
}
function my_scale($value, &$unit, $precision = NULL) {
  global $unraid;
  $scale = $unraid['display']['scale'];
  $number = $unraid['display']['number'];
  $units = ['B','KB','MB','GB','TB','PB'];
  if ($scale==0 && $precision===NULL) {
    $unit = '';
    return number_format($value, 0, $number[0], ($value>=10000 ? $number[1] : ''));
  } else {
    $base = $value ? floor(log($value, 1000)) : 0;
    if ($scale>0 && $base>$scale) $base = $scale;
    $value = round($value/pow(1000, $base), $precision===NULL ? 2 : $precision);
    if ($value>=1000 && $scale<0) { $value = 1; $base++; }
    $unit = $units[$base];
    return number_format($value, $precision===NULL ? (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)) : $precision, $number[0], ($value>=10000 ? $number[1] : ''));
  }
}
function my_check($time,$speed) {
  if (!$time) return 'unavailable (no parity-check entries logged)';
  $days = floor($time/86400);
  $hmss = $time-$days*86400;
  $hour = floor($hmss/3600);
  $mins = $hmss/60%60;
  $secs = $hmss%60;
  return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true).". Average speed: $speed";
}
function my_time($time) {
  global $unraid;
  $date = strftime($unraid['display']['date'].($unraid['display']['date']!='%c' ? ", {$unraid['display']['time']}" : ""), $time);
  $now  = new DateTime("@".intval(time()/86400)*86400);
  $last = new DateTime("@".intval($time/86400)*86400);
  $days = date_diff($last,$now)->format('%a');
  switch (true) {
  case ($days<0):
    return $date;
  case ($days==0):
    return "$date (today)";
  case ($days==1):
    return "$date (yesterday)";
  default:
    return "$date ($days days ago)";
  }
}
function my_clock($time) {
  if (!$time) return 'less than a minute';
  $days = floor($time/1440);
  $hour = $time/60%24;
  $mins = $time%60;
  return plus($days,'day',($hour|$mins)==0).plus($hour,'hour',$mins==0).plus($mins,'minute',true);
}

function my_array(&$disk) {
  global $data,$unraid,$error0,$error1,$error2,$error3;
  $name = $disk['name'];
  $hot  = strlen($disk['hotTemp']) ? $disk['hotTemp'] : $unraid['display']['hot'];
  $max  = strlen($disk['maxTemp']) ? $disk['maxTemp'] : $unraid['display']['max'];
  if (strpos($disk['status'],'_NP')!==false) return false;
  $temp = $disk['temp'];
  if ($temp>=$max) {
    $fail = ' (disk is overheated';
    $error0++;
  } elseif ($temp>=$hot) {
    $fail = ' (disk is hot';
    $error1++;
  } else {
    $fail = '';
  }
  if ($disk['numErrors']>0) {
    if ($fail) $fail .= ', '; else $fail = ' (';
    $fail .= 'disk has read errors';
    $error2++;
  }
  if ($fail) $fail .= ')';
  $status = $fail ? ' [NOK]' : ' [OK]';
  $color = strtok($disk['color'],'-');
  if ($color=='red'||$color=='yellow') { $error3++; $status = ' ['.str_replace(['NP_','_'],['',' '],$disk['status']).']'; }
  $info = "{$disk['id']} ({$disk['device']})";
  if ($info==" ()") $info = 'No device identification present';
  $data[] = my_disk($name)." - $info".my_temp($temp).$fail.$status;
  return true;
}

// generate report of array devices
foreach ($disks as $disk) if ($disk['type']=='Parity') $parity |= my_array($disk);
foreach ($disks as $disk) if ($disk['type']=='Data') my_array($disk);
foreach ($disks as $disk) if ($disk['type']=='Cache') $cache |= my_array($disk);

$size = count($data);

// generate parity report
$data[] = "";
$mdResync = $var['mdResync'];
if ($mdResync>0) {
  $mdResyncPos = $var['mdResyncPos'];
  $mdResyncDb = $var['mdResyncDb'];
  $mdResyncDt = $var['mdResyncDt'];
  $mode = '';
  if (strstr($var['mdResyncAction'],"recon")) {
    $mode = 'Parity sync / Data rebuild';
  } elseif (strstr($var['mdResyncAction'],"clear")) {
    $mode = 'Disk clear';
  } elseif ($var['mdResyncAction']=="check") {
    $mode = 'Read check';
  } elseif (strstr($var['mdResyncAction'],"check")) {
    $mode = 'Parity check';
  }
  $data[] = $mode." in progress.";
  $data[] = "Total size: ".my_scale($mdResync*1024, $unit)." $unit";
  $data[] = "Elapsed time: ".my_clock(floor((time()-$var['sbUpdated'])/60));
  $data[] = "Current position: ".my_scale($mdResyncPos*1024, $unit)." $unit (".number_format(($mdResyncPos/($mdResync/100+1)),1,$unraid['display']['number'][0],'')." %)";
  $data[] = "Estimated speed: ".my_scale($mdResyncDb/$mdResyncDt*1024, $unit, 1)." $unit/sec";
  $data[] = "Estimated finish: ".my_clock(round(((($mdResyncDt*(($mdResync-$mdResyncPos)/($mdResyncDb/100+1)))/100)/60),0));
  $data[] = "Sync errors ".($var['mdResyncCorr']==0 ? 'detected: ' : 'corrected: ').$var['sbSyncErrs'];
} else {
  $sbSynced = $var['sbSynced'];
  $sbSynced2 = $var['sbSynced2'];
  $sbSyncErrs = $var['sbSyncErrs'];
  if ($var['sbSyncExit']!=0) {
    $data[] = "Last check incomplete on ".my_time($sbSynced2).", finding $sbSyncErrs error".($sbSyncErrs==1?'.':'s.');
    $data[] = "Error code: ".$var['sbSyncExit'];
  } elseif ($sbSynced==0) {
    $data[] = "Parity has not been checked yet";
  } elseif ($sbSynced2>0) {
    if (strstr($var['mdResyncAction'],"recon")) {
      $data[] = 'Parity or Data is invalid';
    } else {
      $data[] = 'Parity is valid';
    }
    $duration = $sbSynced2 - $sbSynced;
    $speed = my_scale($var['mdResyncSize']*1024/$duration,$unit,1)." $unit/s";
    $data[] = "Last checked on ".my_time($sbSynced2).", finding $sbSyncErrs error".($sbSyncErrs==1?'.':'s.');
    $data[] = "Duration: ".my_check($duration,$speed);
  }
}

$word = $size==1 ? "" : "including ";
$warn = ($error0 || $error3) ? "alert" : (($error1 || $error2) ? "warning" : "normal");
$stat = $warn=="normal" ? "[PASS]" : "[FAIL]";
$info = "Array has $size disk".($size==1 ? "" : "s").($parity ? " ({$word}parity".($cache ? " & cache)" : ")") : ($cache ? " ({$word}cache)" : ""));
$message = implode('\n', $data);
exec("$notify -s ".escapeshellarg("Notice [$server] - array health report $stat")." -d ".escapeshellarg("$info")." -m ".escapeshellarg("$message")." -i ".escapeshellarg("$warn $output")." -l '/Main'");

exit(0);
?>
