#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
 * Copyright 2012-2023, 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.
 */
?>
<?
$docroot  = '/usr/local/emhttp';
$varroot  = '/var/local/emhttp';
$pool_log = '/var/tmp/pool_log.tmp';
$smartALL = '/boot/config/smart-all.cfg';
$smartONE = '/boot/config/smart-one.cfg';

require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/publish.php";
extract(parse_plugin_cfg('dynamix',true));

// add translations
$_SERVER['REQUEST_URI'] = 'main';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";

// remember current language
$locale_init = $locale;

function initSum() {
  return ['count'=>0, 'temp'=>0, 'fsSize'=>0, 'fsUsed'=>0, 'fsFree'=>0, 'ioReads'=>0, 'ioWrites'=>0, 'numReads'=>0, 'numWrites'=>0, 'numErrors'=>0];
}
function model($id) {
  return substr($id,0,strrpos($id,'_'));
}
function device_info(&$disk,$online) {
  global $pools, $var, $crypto;
  if (!$online || _var($disk,'fsStatus')!='Mounted' || (in_array(_var($disk,'type'),['Parity','Cache']) && !in_array(_var($disk,'name'),$pools))) {
    $view = "<a class='view'></a>";
  } else {
    $dir = _var($disk,'name')=='flash' ? "/boot" : "/mnt/"._var($disk,'name');
    $view = "<a class='view' href=\"/Main/Browse?dir=".htmlspecialchars($dir)."\"><i class=\"icon-u-tab\" title=\""._('Browse')." $dir\"></i></a>";
  }
  $name = _var($disk,'name');
  $fancyname = compress(_(my_disk($name),3),16,5);
  $type = _var($disk,'type')=='Flash' ? $disk['type'] : 'Device';
  $action = strpos(_var($disk,'color'),'blink')===false ? 'down' : 'up';
  switch (_var($disk,'color')) {
    case 'green-on':     $orb = 'circle';  $color = 'green';  $help = _('Normal operation, device is active'); break;
    case 'green-blink':  $orb = 'circle';  $color = 'grey';   $help = _('Device is in standby mode (spun-down)'); break;
    case 'blue-on':      $orb = 'square';  $color = 'blue';   $help = _('New device'); break;
    case 'blue-blink':   $orb = 'square';  $color = 'grey';   $help = _('New device, in standby mode (spun-down)'); break;
    case 'yellow-on':    $orb = 'warning'; $color = 'yellow'; $help = _var($disk,'type')=='Parity' ? _('Parity is invalid') : _('Device contents emulated'); break;
    case 'yellow-blink': $orb = 'warning'; $color = 'grey';   $help = _var($disk,'type')=='Parity' ? _('Parity is invalid, in standby mode (spun-down)') : _('Device contents emulated, in standby mode (spun-down)'); break;
    case 'red-on':
    case 'red-blink':    $orb = 'times';   $color = 'red';    $help = _var($disk,'type')=='Parity' ? _('Parity device is disabled') : _('Device is disabled, contents emulated'); break;
    case 'red-off':      $orb = 'times';   $color = 'red';    $help = _var($disk,'type')=='Parity' ? _('Parity device is missing') : _('Device is missing (disabled), contents emulated'); break;
    case 'grey-off':     $orb = 'square';  $color = 'grey';   $help = _('Device not present'); break;
  }
  $ctrl = '';
  $disk_status = _var($disk,'status');
  if (_var($var,'fsState')=='Started' && $type!='Flash' && strpos($disk_status,'_NP')===false) {
    $ctrl = " style='cursor:pointer' onclick=\"toggle_state('$type','$name','$action')\"";
    $help .= "<br>"._("Click to spin $action device");
  }
  $status = "<a class='info'><i ".($ctrl?"id='dev-$name' ":"")."class='fa fa-$orb orb $color-orb'$ctrl></i><span>$help</span></a>";
  $link   = (_var($disk,'type')=='Parity' && strpos($disk_status,'_NP')===false) ||
            (_var($disk,'type')=='Data' && $disk_status!='DISK_NP') ||
            (_var($disk,'type')=='Cache' && $disk_status!='DISK_NP') ||
            (_var($disk,'name')=='flash') || in_array(_var($disk,'name'),$pools) ||
             _var($disk,'type')=='New' ? "<a href=\"".htmlspecialchars("/Main/Settings/$type?name=$name")."\">$fancyname</a>" : $fancyname;
  if ($crypto) switch (_var($disk,'luksState',0)) {
    case 0:
      if (!vfs_luks(_var($disk,'fsType')))
        $luks = "<i class='nolock fa fa-lock'></i>";
      else
        $luks = "<a class='info'><i class='padlock fa fa-unlock orange-text'></i><span>"._('Device to be encrypted')."</span></a>";
      break;
    case 1:
      if ($online) {
        $luks = "<a class='info'><i class='padlock fa fa-unlock-alt green-text'></i><span>"._('Device encrypted and unlocked')."</span></a>";
        break;
      }
      /* fall thru */
    case 2:
      $luks = "<a class='info'><i class='padlock fa fa-lock green-text'></i><span>"._('Device encrypted')."</span></a>";
      break;
    case 3:
      $luks = "<a class='info'><i class='padlock fa fa-lock red-text'></i><span>"._('Device locked: wrong encryption key')."</span></a>";
      break;
   default:
      $luks = "<a class='info'><i class='padlock fa fa-lock red-text'></i><span>"._('Device locked: unknown error')."</span></a>";
      break;
  } else $luks = '';
  return $view.$status.$luks.$link;
}
function device_desc(&$disk) {
  global $var;
  $size = my_scale(_var($disk,'size',0)*1024 ?: _var($disk,'sectors',0)*_var($disk,'sector_size',0),$unit,-1);
  switch (_var($disk,'type')) {
    case 'Flash':  $type = 'usb'; break;
    case 'Parity': $type = _var($disk,'rotational') ? 'disk' : 'nvme'; break;
    case 'Data':
    case 'Cache':  $type = _var($disk,'rotational') ? (_var($disk,'luksState') ? 'disk-encrypted' : 'disk') : 'nvme'; break;
    default:       $type = 'disk'; break;
  }
  $log = _var($var,'fsState')=='Started'
       ? "<a class='info hand' onclick=\"openTerminal('disklog','"._var($disk,'device')."','')\"><i class='icon-$type icon'></i><span>"._('Disk Log Information')."</span></a>"
       : "<a class='static'><i class='icon-$type icon'></i></a>";
  return  $log."<span style='font-family:bitstream'>".my_id(_var($disk,'id'))."</span> - $size $unit ("._var($disk,'device').")";
}
function assignment(&$disk) {
  global $var, $devs;
  $echo = [];
  $echo[] = "<form method='POST' id=\""._var($disk,'name')."Form\" action='/update.htm' target='progressFrame'>";
  $echo[] = "<input type='hidden' name='changeDevice' value='apply'>";
  $echo[] = "<input type='hidden' name='csrf_token' value='"._var($var,'csrf_token')."'>";
  $echo[] = "<select class='slot' name='slotId."._var($disk,'idx')."' onChange='\$(\"#"._var($disk,'name')."Form\").submit()'>";
  $empty = _var($disk,'idSb')!='' ? _('no device') : _('unassigned');
  if (_var($disk,'id')) {
    $echo[] = "<option value=\"{$disk['id']}\" selected>".device_desc($disk)."</option>";
    $echo[] = "<option value=''>$empty</option>";
  } else {
    $echo[] = "<option value='' selected>$empty</option>";
  }
  foreach ($devs as $dev) $echo[] = "<option value=\""._var($dev,'id')."\">".device_desc($dev)."</option>";
  $echo[] = "</select></form>";
  return implode($echo);
}
function vfs_type($fs) {
  return str_replace('luks:','',$fs);
}
function vfs_luks($fs) {
  return ($fs != vfs_type($fs));
}
function vfs_info($fs) {
  $fs = vfs_type($fs);
  return $fs; // no special action on reiserfs (may come in future)
}
function fs_info(&$disk) {
  global $display, $pools;
  $echo = [];
  if (empty($disk['fsStatus']) || $disk['fsStatus']=='-') {
    return (_var($disk,'type')=='Cache' && !in_array(_var($disk,'name'),$pools)) ? "<td colspan='4'>"._('Device is part of a pool')."</td>" : "<td colspan='5'></td>";
  } elseif (_var($disk,'fsStatus')=='Mounted') {
    $echo[] = "<td>".vfs_info(_var($disk,'fsType'))."</td>";
    $echo[] = "<td>".my_scale(_var($disk,'fsSize',0)*1024,$unit,-1)." $unit</td>";
    if ($display['text']%10==0) {
      $echo[] = "<td>".my_scale(_var($disk,'fsUsed',0)*1024,$unit)." $unit</td>";
    } else {
      $used = _var($disk,'fsSize',0)>0 ? 100-round(100*_var($disk,'fsFree',0)/$disk['fsSize']) : 0;
      $echo[] = "<td><div class='usage-disk'><span style='width:$used%' class='".usage_color($disk,$used,false)."'></span><span>".my_scale(_var($disk,'fsUsed',0)*1024,$unit)." $unit</span></div></td>";
    }
    if (_var($display,'text',0)<10 ? _var($display,'text',0)%10==0 : _var($display,'text',0)%10!=0) {
      $echo[] = "<td>".my_scale(_var($disk,'fsFree',0)*1024,$unit)." $unit</td>";
    } else {
      $free = _var($disk,'fsSize',0)>0 ? round(100*_var($disk,'fsFree',0)/$disk['fsSize']) : 0;
      $echo[] = "<td><div class='usage-disk'><span style='width:$free%' class='".usage_color($disk,$free,true)."'></span><span>".my_scale(_var($disk,'fsFree',0)*1024,$unit)." $unit</span></div></td>";
    }
  } else {
    $echo[] = "<td>".vfs_info(_var($disk,'fsType'))."</td><td colspan='3' style='text-align:center'>"._(_var($disk,'fsStatus'))."</td>";
  }
  return implode($echo);
}
function my_diskio($data) {
  return my_scale($data,$unit,1)." $unit/s";
}
function array_offline(&$disk, $pool='') {
  global $var, $disks;
  $echo = []; $warning = '';
  if (strpos(_var($var,'mdState'),'ERROR:')===false) {
    $text = "<span class='red-text'><em>"._('All existing data on this device will be OVERWRITTEN when array is Started')."</em></span>";
    if (_var($disk,'type')=='Cache') {
      if (_var($disks[$pool],'uuid') && _var($disk,'status')=='DISK_NEW') $warning = $text;
    } else {
      if (_var($var,'mdState')=='NEW_ARRAY') {
        if (_var($disk,'type')=='Parity') $warning = $text;
      } elseif (_var($var,'mdNumInvalid',0)<=1) {
        if (in_array(_var($disk,'status'),['DISK_INVALID','DISK_DSBL_NEW','DISK_WRONG','DISK_NEW'])) $warning = $text;
      }
    }
  }
  $echo[] = "<tr>";
  switch (_var($disk,'status')) {
  case 'DISK_NP':
  case 'DISK_NP_DSBL':
    $echo[] = "<td>".device_info($disk,false)."</td>";
    $echo[] = "<td>".assignment($disk)."</td>";
    $echo[] = "<td colspan='8'></td>";
    break;
  case 'DISK_NP_MISSING':
    $echo[] = "<td>".device_info($disk,false)."<br><span class='diskinfo'><em>"._('Missing')."</em></span></td>";
    $echo[] = "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale(_var($disk,'sizeSb',0)*1024,$unit)." $unit</em></td>";
    $echo[] = "<td colspan='8'></td>";
    break;
  case 'DISK_OK':
  case 'DISK_DSBL':
  case 'DISK_INVALID':
  case 'DISK_DSBL_NEW':
  case 'DISK_NEW':
    $echo[] = "<td>".device_info($disk,false)."</td>";
    $echo[] = "<td>".assignment($disk)."</td>";
    $echo[] = "<td>".my_temp(_var($disk,'temp','*'))."</td>";
    if ($warning) {
      $echo[] = "<td colspan='7'>$warning</td>";
    } else {
      $echo[] = "<td colspan='3'></td>";
      $echo[] = "<td>".vfs_type(_var($disk,'fsType'))."</td>";
      $echo[] = "<td colspan='3'></td>";
    }
    break;
  case 'DISK_WRONG':
    $echo[] = "<td>".device_info($disk,false)."<br><span class='diskinfo'><em>"._('Wrong')."</em></span></td>";
    $echo[] = "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale(_var($disk,'sizeSb',0)*1024,$unit)." $unit</em></td>";
    $echo[] = "<td>".my_temp(_var($disk,'temp','*'))."</td>";
    if ($warning) {
      $echo[] = "<td colspan='7'>$warning</td>";
    } else {
      $echo[] = "<td colspan='3'></td>";
      $echo[] = "<td>".vfs_type(_var($disk,'fsType'))."</td>";
      $echo[] = "<td colspan='3'></td>";
    }
    break;
  }
  $echo[] = "</tr>";
  return implode($echo);
}
function array_online(&$disk, $fstype='') {
  global $pools, $sum, $diskio;
  $echo = [];
  $data = [0,0];
  if (_var($disk,'device')) {
    $dev = $disk['device'];
    $data = explode(' ',$diskio[$dev] ?? '0 0');
    $sum['ioReads'] += $data[0];
    $sum['ioWrites'] += $data[1];
  }
  if (is_numeric(_var($disk,'temp','*'))) {
    $sum['count']++;
    $sum['temp'] += $disk['temp'];
  }
  $sum['numReads'] += _var($disk,'numReads',0);
  $sum['numWrites'] += _var($disk,'numWrites',0);
  $sum['numErrors'] += _var($disk,'numErrors',0);
  if (isset($disk['fsFree'])) {
    $sum['fsSize'] += _var($disk,'fsSize',0);
    $sum['fsUsed'] += _var($disk,'fsUsed',0);
    $sum['fsFree'] += _var($disk,'fsFree',0);
  }
  $echo[] = "<tr>";
  switch (_var($disk,'status')) {
  case 'DISK_NP':
    if (in_array(_var($disk,'name'),$pools) || $fstype=='zfs') {
      $echo[] = "<td>".device_info($disk,true)."</td>";
      $echo[] = "<td><a class='static'><i class='icon-disk icon'></i><span></span></a><em>".($fstype=='zfs' ? _('Not present') : _('Not installed'))."</em></td>";
      $echo[] = "<td colspan='4'></td>";
      $echo[] = fs_info($disk);
    }
    break;
  case 'DISK_NP_DSBL':
    $echo[] = "<td>".device_info($disk,true)."</td>";
    $echo[] = "<td><a class='static'><i class='icon-disk icon'></i><span></span></a><em>"._('Not installed')."</em></td>";
    $echo[] = "<td colspan='4'></td>";
    $echo[] = fs_info($disk);
    break;
  case 'DISK_DSBL':
  default:
    $echo[] = "<td>".device_info($disk,true)."</td>";
    $echo[] = "<td>".device_desc($disk)."</td>";
    $echo[] = "<td>".my_temp(_var($disk,'temp','*'))."</td>";
    $echo[] = "<td><span class='diskio'>".my_diskio($data[0])."</span><span class='number'>".my_number(_var($disk,'numReads',0))."</span></td>";
    $echo[] = "<td><span class='diskio'>".my_diskio($data[1])."</span><span class='number'>".my_number(_var($disk,'numWrites',0))."</span></td>";
    $echo[] = "<td>".my_number(_var($disk,'numErrors',0))."</td>";
    $echo[] = fs_info($disk);
    break;
  }
  $echo[] = "</tr>";
  return implode($echo);
}
function show_totals($text,$array,$name) {
  global $var, $display, $sum, $locale;
  $ctrl1 = "onclick=\"toggle_state('Device','$name','down')\"";
  $ctrl2 = "onclick=\"toggle_state('Device','$name','up')\"";
  $help1 = _('Spin Down').' '._(ucfirst(substr($name,0,-1)));
  $help2 = _('Spin Up').' '._(ucfirst(substr($name,0,-1)));
  $echo  = [];
  $echo[] = "<tr class='tr_last'>";
  $echo[] = "<td><a class='info'><i class='fa fa-fw fa-toggle-down control' $ctrl1></i><span>$help1</span></a><a class='info'><i class='fa fa-fw fa-toggle-up control' $ctrl2></i><span>$help2</span></a></td>";
  $echo[] = "<td><a class='static'><i class='icon-disks icon'></i></a><span></span>$text</td>";
  $echo[] = "<td>".($sum['count']>0 ? my_temp(round($sum['temp']/$sum['count'],1)) : '*')."</td>";
  $echo[] = "<td><span class='diskio'>".my_diskio($sum['ioReads'])."</span><span class='number'>".my_number($sum['numReads'])."</span></td>";
  $echo[] = "<td><span class='diskio'>".my_diskio($sum['ioWrites'])."</span><span class='number'>".my_number($sum['numWrites'])."</span></td>";
  $echo[] = "<td>".my_number($sum['numErrors'])."</td>";
  $echo[] = "<td></td>";
  if ($array && _var($var,'startMode')=='Normal') {
    $echo[] = "<td>".my_scale($sum['fsSize']*1024,$unit,-1)." $unit</td>";
    if ($display['text']%10==0) {
      $echo[] = "<td>".my_scale($sum['fsUsed']*1024,$unit)." $unit</td>";
    } else {
      $used = $sum['fsSize'] ? 100-round(100*$sum['fsFree']/$sum['fsSize']) : 0;
      $echo[] = "<td><div class='usage-disk'><span style='width:$used%' class='".usage_color($display,$used,false)."'></span><span>".my_scale($sum['fsUsed']*1024,$unit)." $unit</span></div></td>";
    }
    if ($display['text']<10 ? $display['text']%10==0 : $display['text']%10!=0) {
      $echo[] = "<td>".my_scale($sum['fsFree']*1024,$unit)." $unit</td>";
    } else {
      $free = $sum['fsSize'] ? round(100*$sum['fsFree']/$sum['fsSize']) : 0;
      $echo[] = "<td><div class='usage-disk'><span style='width:$free%' class='".usage_color($display,$free,true)."'></span><span>".my_scale($sum['fsFree']*1024,$unit)." $unit</span></div></td>";
    }
  } else {
    $echo[] = "<td colspan=3></td>";
  }
  $echo[] = "</tr>";
  return implode($echo);
}
function array_slots() {
  global $var;
  $min  = max(_var($var,'sbNumDisks',0),3);
  $max  = _var($var,'MAX_ARRAYSZ');
  $echo = [];
  $echo[] = "<form method='POST' action='/update.htm' target='progressFrame'>";
  $echo[] = "<input type='hidden' name='csrf_token' value='"._var($var,'csrf_token')."'>";
  $echo[] = "<input type='hidden' name='changeSlots' value='apply'>";
  $echo[] = "<select class='narrow' name='SYS_ARRAY_SLOTS' onChange='this.form.submit()'>";
  for ($n=$min; $n<=$max; $n++) {
    $selected = $n==_var($var,'SYS_ARRAY_SLOTS') ? ' selected' : '';
    $echo[] = "<option value='$n'{$selected}>$n</option>";
  }
  $echo[] = "</select></form>";
  return implode($echo);
}
function cache_slots($off,$pool,$min,$slots) {
  global $var;
  $off  = $off && $min ? ' disabled' : '';
  $max  = _var($var,'MAX_CACHESZ');
  $echo = [];
  $echo[] = "<form method='POST' action='/update.htm' target='progressFrame'>";
  $echo[] = "<input type='hidden' name='csrf_token' value='"._var($var,'csrf_token')."'>";
  $echo[] = "<input type='hidden' name='changeSlots' value='apply'>";
  $echo[] = "<input type='hidden' name='poolName' value='$pool'>";
  $echo[] = "<select class='narrow' name='poolSlots' onChange='this.form.submit()'{$off}>";
  for ($n=$min; $n<=$max; $n++) {
    $option = $n ?: _('none');
    $selected = ($n==$slots) ? ' selected' : '';
    $echo[] = "<option value='$n'{$selected}>$option</option>";
  }
  $echo[] = "</select></form>";
  return implode($echo);
}
function update_translation($locale) {
  global $docroot,$language;
  $language = [];
  if ($locale) {
    $text = "$docroot/languages/$locale/translations.txt";
    if (file_exists($text)) {
      $store = "$docroot/languages/$locale/translations.dot";
      if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
      $language = unserialize(file_get_contents($store));
    }
    $text = "$docroot/languages/$locale/main.txt";
    if (file_exists($text)) {
      $store = "$docroot/languages/$locale/main.dot";
      if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
      $language = array_merge($language,unserialize(file_get_contents($store)));
    }
  }
}
while (true) {
  $var    = @parse_ini_file("$varroot/var.ini") ?: [];
  $devs   = @parse_ini_file("$varroot/devs.ini",true) ?: [];
  $disks  = @parse_ini_file("$varroot/disks.ini",true) ?: [];
  $sec    = @parse_ini_file("$varroot/sec.ini",true) ?: [];
  $diskio = @parse_ini_file("$varroot/diskload.ini") ?: [];
  $crypto = false;
  $pools  = pools_filter($disks);
  $echo   = [];
  // check for language changes
  extract(parse_plugin_cfg('dynamix',true));
  if (_var($display,'locale') != $locale_init) {
    $locale_init = _var($display,'locale');
    update_translation($locale_init);
  }
  // sort unassigned devices on disk identification
  if (count($devs)>1) array_multisort(array_column($devs,'sectors'),SORT_DESC,array_map('model',array_column($devs,'id')),SORT_NATURAL|SORT_FLAG_CASE,array_column($devs,'device'),$devs);

  // merge device custom settings
  if (file_exists($smartALL)) $var = array_merge($var,parse_ini_file($smartALL));
  if (file_exists($smartONE)) {
    $smarts = parse_ini_file($smartONE,true);
    foreach ($smarts as $id => $smart) {
      if (isset($disks)) {
        foreach ($disks as $key => $disk) {
          if (_var($disk,'id')==$id) $disks[$key] = array_merge($disks[$key], $smart);
        }
      }
      if (isset($devs)) {
        foreach ($devs as $key => $disk) {
          if (_var($disk,'id')==$id) $devs[$key] = array_merge($devs[$key], $smart);
        }
      }
    }
  }

  $sum = initSum();
  $echo[0] = "array_devices\n";
  $parity = parity_filter($disks);
  $data = data_filter($disks);
  foreach ($data as $disk) $crypto |= _var($disk,'luksState',0)!=0 || vfs_luks(_var($disk,'fsType'));
  if (_var($var,'fsState')=='Stopped') {
    foreach ($parity as $disk) $echo[0] .= array_offline($disk);
    $echo[0] .= "<tr class='tr_last'><td style='height:12px' colspan='10'></td></tr>";
    foreach ($data as $disk) $echo[0] .= array_offline($disk);
    $echo[0] .= "<tr class='tr_last'><td>"._('Slots').":</td><td colspan='8'>".array_slots()."</td><td></td></tr>";
  } else {
    foreach ($parity as $disk) if ($disk['status']!='DISK_NP_DSBL') $echo[0] .= array_online($disk);
    foreach ($data as $disk) $echo[0] .= array_online($disk);
    if (_var($display,'total') && _var($var,'mdNumDisks',0)>1) $echo[0] .= show_totals(sprintf(_('Array of %s devices'),my_word($var['mdNumDisks'])),true,'array*');
  }

  $echo[1] = "boot_device\n";
  $disk = &$disks['flash'];
  $data = explode(' ',$diskio[_var($disk,'device')] ?? '0 0');
  $flash = &$sec['flash'];
  $share = (_var($var,'shareSMBEnabled')=='yes' && _var($flash,'export')=='e' && _var($flash,'security')=='public')
           ? "&nbsp;<a class='info'><i class='fa fa-warning fa-fw orange-text'></i><span>"._('Flash device is set as public share')."<br>"._('Please change share SMB security')."<br>"._('Click on **FLASH** above this message')."</span></a>"
           : "";
  $echo[1] .= "<tr>";
  $echo[1] .= "<td>".device_info($disk,true).$share."</td>";
  $echo[1] .= "<td>".device_desc($disk)."</td>";
  $echo[1] .= "<td>*</td>";
  $echo[1] .= "<td><span class='diskio'>".my_diskio($data[0])."</span><span class='number'>".my_number(_var($disk,'numReads',0))."</span></td>";
  $echo[1] .= "<td><span class='diskio'>".my_diskio($data[1])."</span><span class='number'>".my_number(_var($disk,'numWrites',0))."</span></td>";
  $echo[1] .= "<td>".my_number(_var($disk,'numErrors',0))."</td>";
  $echo[1] .= fs_info($disk);
  $echo[1] .= "</tr>";

  $sum = initSum();
  $cache  = cache_filter($disks); $n = 2;
  foreach ($pools as $pool) {
    $echo[$n] = "pool_device".($n-2)."\n";
    foreach ($cache as $disk) if (prefix(_var($disk,'name'))==$pool) $crypto |= _var($disk,'luksState',0)!=0 || vfs_luks(_var($disk,'fsType'));
    if (_var($var,'fsState')=='Stopped') {
      $log = file_exists($pool_log) ? parse_ini_file($pool_log) : [];
      $off = false;
      foreach ($cache as $disk) if (prefix(_var($disk,'name'))==$pool) {
        $echo[$n] .= array_offline($disk,$pool);
        if (isset($log[_var($disk,'name')])) $off |= ($log[$disk['name']]!=_var($disk,'id')); else $log[_var($disk,'name')] = _var($disk,'id');
      }
      $data = []; foreach ($log as $key => $value) $data[] = "$key=\"$value\"";
      file_put_contents($pool_log,implode("\n",$data));
      $echo[$n] .= "<tr class='tr_last'><td>"._('Slots').":</td><td colspan='8'>".cache_slots($off,$pool,_var($cache[$pool],'devicesSb'),_var($cache[$pool],'slots',0))."</td><td></td></tr>";
    } else {
      foreach ($cache as $disk) if (prefix($disk['name'])==$pool) {
        if (isset($disk['fsType'])) $fstype = vfs_type($disk['fsType']);
        if (substr(_var($cache[$pool],'fsStatus'),0,11)=='Unmountable' && empty($disk['fsStatus'])) $disk['fsStatus'] = _var($cache[$pool],'fsStatus');
        $echo[$n] .= array_online($disk,$fstype);
      }
      delete_file($pool_log);
      if (_var($display,'total') && _var($cache[$pool],'devices',0)>1) $echo[$n] .= show_totals(sprintf(_('Pool of %s devices'),my_word($cache[$pool]['devices'])),false,"$pool*");
      $sum = initSum();
    }
    $n++;
  }

  $echo[$n] = "open_devices\n";
  foreach ($devs as $disk) {
    $dev = _var($disk,'device');
    $data = explode(' ',$diskio[$dev] ?? '0 0 0 0');
    $disk['type'] = 'New';
    $disk['color'] = $disk['spundown']=="0" ? 'blue-on' : 'blue-blink';
    $echo[$n] .= "<tr>";
    $echo[$n] .= "<td>".device_info($disk,true)."</td>";
    $echo[$n] .= "<td>".device_desc($disk)."</td>";
    $echo[$n] .= "<td>".my_temp($disk['temp'])."</td>";
    $echo[$n] .= "<td><span class='diskio'>".my_diskio($data[0])."</span><span class='number'>".my_number(_var($disk,'numReads',0))."</span></td>";
    $echo[$n] .= "<td><span class='diskio'>".my_diskio($data[1])."</span><span class='number'>".my_number(_var($disk,'numWrites',0))."</span></td>";
    $echo[$n] .= "<td>".my_number(_var($disk,'numErrors',0))."</td>";
    if (file_exists("/tmp/preclear_stat_$dev")) {
      $text = exec("cut -d'|' -f3 /tmp/preclear_stat_$dev|sed 's:\^n:\<br\>:g'");
      if (strpos($text,'Total time')===false) $text = _('Preclear in progress').'... '.$text;
      $echo[$n] .= "<td colspan='4' style='text-align:right'><em>$text</em></td>";
    } else
      $echo[$n] .= "<td colspan='4'></td>";
    $echo[$n] .= "</tr>";
  }

  $n++;
  $echo[$n] = _var($var,'fsState')=='Stopped' ? 1 : 0;

  publish('devices', implode("\0",$echo));
  publish('arraymonitor', _var($var,'fsState')=='Started' ? 1 : 0);
  sleep(1);
}
?>
