Merge pull request #2215 from bergware/master

Encryption: fix passphrase encryption not working in some cases
This commit is contained in:
tom mortensen
2025-05-30 11:18:29 -07:00
committed by GitHub
9 changed files with 485 additions and 344 deletions

View File

@@ -17,27 +17,36 @@ Nchan="device_list,disk_load,parity_list"
?>
<?
$keyfile = file_exists(_var($var,'luksKeyfile'));
$missing = file_exists('/var/tmp/missing.tmp');
$spot = _var($var,'mdResyncPos',0)>0;
$poolsOnly = (_var($var,'SYS_ARRAY_SLOTS') == 0 ) ? true : false;
$poolsOnly = (_var($var,'SYS_ARRAY_SLOTS') == 0 ) ? true : false;
/* only one of $present, $missing, or $wrong will be true, or all will be false */
$forced = $present = $wrong = false;
foreach ($disks as $disk) {
if (strpos(_var($disk,'fsType'),'luks:')!==false || (_var($disk,'fsType')=='auto' && strpos(_var($var,'defaultFsType'),'luks:')!==false)) $forced = true;
if (_var($disk,'luksState',0)==1) $present = true;
if (_var($disk,'luksState',0)==2) $missing = true;
if (_var($disk,'luksState',0)==3) $wrong = true;
$forced = $present = $missing = $wrong = false;
foreach (luks_filter($disks) as $disk) {
$fsType = _var($disk,'fsType');
$luks = str_starts_with($fsType,'luks:');
if ($luks || ($fsType == 'auto' && str_starts_with(_var($var,'defaultFsType'),'luks:'))) $forced = true;
if ($luks) switch (_var($disk,'luksState',0)) {
case 1: $present = true; break;
case 2: $missing = true; break;
case 3: $wrong = true; break;
}
}
$encrypt = $forced || $present || $missing || $wrong;
if ($forced && ($present || $missing || $wrong)) $forced = false;
function check_encryption() {
global $forced, $missing, $wrong;
if ($forced) $status = _('Enter new key');
elseif ($missing) $status = _('Missing key');
elseif ($wrong) $status = _('Wrong key');
else return;
if ($forced)
$status = _('Enter new key');
elseif ($missing)
$status = _('Missing key');
elseif ($wrong)
$status = _('Wrong key');
else
return;
echo "<tr><td></td><td class='gap'>",_('Encryption status').":</td><td><span class='red-text'>$status</span><span id='pass'><input name='luksReformat' type='checkbox' onchange='selectInput(this.form)'>permit reformat</span></td></tr>";
echo "<tr><td></td><td class='gap'>",_('Encryption input').":</td><td>";
echo "<select name='input' size='1' onchange='selectInput(this.form)'>";
@@ -91,15 +100,15 @@ var recover = null;
String.prototype.no_tilde = function(){return this.replace('<?=$_tilde_?>','<?=$_proxy_?>');}
String.prototype.master = function(){return this.split('<?=$_tilde_?>')[0];}
function toggle_state(device,name,action) {
function toggle_state(device, name, action) {
var button = null;
if (name) {
var group = name.replace(/(\d+|\*)$/,'');
if (name.slice(-1)!='*') {
if (name.slice(-1) != '*') {
// single device
$('#dev-'+name.no_tilde()).removeClass('fa-circle fa-square fa-warning fa-times').addClass('fa-refresh fa-spin');
} else {
if (group=='disk') {
if (group == 'disk') {
// array devices
$('[id^="dev-parity"]').removeClass('fa-circle fa-square fa-warning fa-times').addClass('fa-refresh fa-spin');
$('[id^="dev-disk"]').removeClass('fa-circle fa-square fa-warning fa-times').addClass('fa-refresh fa-spin');
@@ -108,17 +117,17 @@ function toggle_state(device,name,action) {
$('[id^="dev-'+group.master()+'"]').removeClass('fa-circle fa-square fa-warning fa-times').addClass('fa-refresh fa-spin');
}
}
} else if (device!='Clear') {
} else if (device != 'Clear') {
// all devices
$('[id^="dev-"]').removeClass('fa-circle fa-square fa-warning fa-times').addClass('fa-refresh fa-spin');
button = '[id^=button-]';
}
devices.stop();
$.post('/webGui/include/ToggleState.php',{device:device,name:name,action:action},function(){setTimeout(function(){devices.start().monitor();},1000);if (button) $(button).prop('disabled',false);});
$.post('/webGui/include/ToggleState.php', {device:device,name:name,action:action}, function(){setTimeout(function(){devices.start().monitor();},1000);if (button) $(button).prop('disabled',false);});
}
function display_diskio() {
if ($.cookie('diskio')===undefined) {
if ($.cookie('diskio') === undefined) {
$('span.diskio').show(); $('span.number').hide();
} else {
$('span.number').show(); $('span.diskio').hide();
@@ -127,9 +136,9 @@ function display_diskio() {
function toggle_diskio(init) {
if (!init) {
if ($.cookie('diskio')===undefined) $.cookie('diskio','diskio',{expires:3650}); else $.removeCookie('diskio');
if ($.cookie('diskio') === undefined) $.cookie('diskio','diskio',{expires:3650}); else $.removeCookie('diskio');
}
if ($.cookie('diskio')===undefined) {
if ($.cookie('diskio') === undefined) {
$('i.toggle').removeClass('fa-list').addClass('fa-tachometer');
$('#clearstats').addClass('hidden');
} else {
@@ -148,7 +157,7 @@ function selectInput(form) {
form.input.value = 'file';
form.input.disabled = true;
<?endif;?>
if (form.input.value=='text') {
if (form.input.value == 'text') {
form.file.value = '';
form.local.value = '';
<?if ($forced):?>
@@ -187,17 +196,17 @@ function selectInput(form) {
}
}
function getFileContent(event,form) {
function getFileContent(event, form) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){form.file.value=reader.result;selectInput(form);};
reader.readAsDataURL(input.files[0]);
}
function prepareInput(form,button,parityWarn) {
function prepareInput(form, button, parityWarn) {
button.disabled = true;
$.post('/webGui/include/Report.php',{cmd:'state',pools:'<?=implode(',',$pools)?>'},function(state) {
if (state.length==0) {
if (state.length == 0) {
$(form).append('<input type="hidden" name="cmdStart" value="Start">');
if (form.input === undefined) {
parityWarn ? parityWarning(form,button) : form.submit();
@@ -219,7 +228,13 @@ function prepareInput(form,button,parityWarn) {
form.file.disabled = false;
form.text.disabled = false;
form.copy.disabled = false;
swal({title:"_(Printable Characters Only)_",text:"_(Use **ASCII** characters from space ' ' to tilde '~')_<br>_(Otherwise use the **keyfile** method for UTF8 input)_",html:true,type:'error',confirmButtonText:"_(Ok)_"});
swal({
title:"_(Printable Characters Only)_",
text:"_(Use **ASCII** characters from space ' ' to tilde '~')_<br>_(Otherwise use the **keyfile** method for UTF8 input)_",
html:true,
type:'error',
confirmButtonText:"_(Ok)_"
});
}
return;
}
@@ -229,12 +244,20 @@ function prepareInput(form,button,parityWarn) {
data['file'] = form.file.value;
$.post('/update.php',data,function(){form.submit();});
} else {
swal({title:"_(Wrong Pool State)_",text:state,type:'error',html:true,confirmButtonText:"_(Ok)_"},function(){button.disabled=false;});
swal({
title:"_(Wrong Pool State)_",
text:state,
type:'error',
html:true,
confirmButtonText:"_(Ok)_"
},function(){
button.disabled=false;
});
}
});
}
function parityWarning(form,button) {
function parityWarning(form, button) {
if (form.md_invalidslot.checked) {
<?if (strpos(_var($disks['parity2'],'status'),'_NP')===false):?>
var text = "_(*Dual parity* valid requires **ALL** disks in their original slots)_";
@@ -244,7 +267,15 @@ function parityWarning(form,button) {
} else {
var text = "_(*Parity* disk(s) content will be overwritten)_";
}
swal({title:"_(Proceed to start)_",text:text,html:true,type:'warning',showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(confirmed){
swal({
title:"_(Proceed to start)_",
text:text,
html:true,
type:'warning',
showCancelButton:true,
confirmButtonText:"_(Proceed)_",
cancelButtonText:"_(Cancel)_"
},function(confirmed){
confirmed ? form.submit() : button.disabled=false;
});
}
@@ -257,16 +288,36 @@ function tab0() {
function stopArray(form) {
$(form).append('<input type="hidden" name="cmdStop" value="Stop">');
<?if ($confirm['stop']):?>
swal({title:"_(Proceed)_?",text:"_(This will stop the array)_",type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(p){if (p) form.submit(); else $('input[name="cmdStop"]').remove();});
swal({
title:"_(Proceed)_?",
text:"_(This will stop the array)_",
type:'warning',
html:true,
showCancelButton:true,
confirmButtonText:"_(Proceed)_",
cancelButtonText:"_(Cancel)_"
},function(p){
if (p) form.submit(); else $('input[name="cmdStop"]').remove();
});
<?else:?>
form.submit();
<?endif;?>
}
function stopParity(form,text) {
function stopParity(form, text) {
$(form).append('<input type="hidden" name="cmdCheckCancel" value="">');
<?if ($confirm['stop']):?>
swal({title:"_(Proceed)_?",text:"_(This will stop the running operation)_: "+text,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(p){if (p) form.submit(); else $('input[name="cmdCheckCancel"]').remove();});
swal({
title:"_(Proceed)_?",
text:"_(This will stop the running operation)_: "+text,
type:'warning',
html:true,
showCancelButton:true,
confirmButtonText:"_(Proceed)_",
cancelButtonText:"_(Cancel)_"
},function(p){
if (p) form.submit(); else $('input[name="cmdCheckCancel"]').remove();
});
<?else:?>
form.submit();
<?endif;?>
@@ -292,14 +343,24 @@ function parityHistory() {
openChanges("parity_history", "_(Parity Operation History)_", "phistory");
}
function shutdown_now(form,cmd) {
function shutdown_now(form, cmd) {
$(form).append('<input type="hidden" name="cmd" value="'+cmd+'">');
<?if ($confirm['down']):?>
switch (cmd) {
case 'reboot': var text = "_(This will reboot the system)_"; break;
case 'shutdown': var text = "_(This will shutdown the system)_"; break;
}
swal({title:"_(Proceed)_?",text:text,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(p){if (p) form.submit(); else $('input[name="cmd"]').remove();});
swal({
title:"_(Proceed)_?",
text:text,
type:'warning',
html:true,
showCancelButton:true,
confirmButtonText:"_(Proceed)_",
cancelButtonText:"_(Cancel)_"
},function(p){
if (p) form.submit(); else $('input[name="cmd"]').remove();
});
<?else:?>
form.submit();
<?endif;?>
@@ -311,25 +372,25 @@ function toggleApply(checked) {
<?if ($tabbed):?>
$('.tabs').append(ctrl);
if ($.cookie('tab')=='tab0') $('i.toggle').hide();
if ($.cookie('tab') == 'tab0') $('i.toggle').hide();
$('#tab'+$('input[name$="tabs"]').length).click(function(){tab0(); $('i.toggle').hide('slow');});
<?else:?>
$('div[class=title]:not(":last, .disable_diskio") .right').each(function(){$(this).append(ctrl);});
<?endif;?>
$('.tooltip_diskio').tooltipster({delay:100,trigger:'custom',triggerOpen:{mouseenter:true},triggerClose:{click:false,scroll:true,mouseleave:true}});
<?if (_var($var,'fsState')=='Started'):?>
<?if (_var($var,'fsState') == 'Started'):?>
var mymonitor = new NchanSubscriber('/sub/mymonitor',{subscriber:'websocket', reconnectTimeout:5000});
mymonitor.on('message', function(state) {
switch (state) {
case '0': // normal operation
$('#stop-button').prop('disabled',false);
$('#stop-text').html("");
<?if (_var($var,'fsState')!="Stopped"):?>
<?if (_var($var,'fsState') != "Stopped"):?>
$('#spinup-button').prop('disabled',false);
$('#spindown-button').prop('disabled',false);
<?endif;?>
<?if (_var($var,'shareUser')=='e' && $pool_devices):?>
<?if (_var($var,'shareUser') == 'e' && $pool_devices):?>
$('#mover-button').prop('disabled',false);
$('#mover-text').html("<b>_(Move)_</b> _(will immediately invoke the Mover)_.&nbsp;<a href=\"/Main/Settings/Scheduler\"<?if($tabbed):?> onclick=\"$.cookie('one','tab2')\"<?endif;?>>(_(Schedule)_)</a>");
<?endif;?>
@@ -337,11 +398,11 @@ mymonitor.on('message', function(state) {
case '1': // parity running
$('#stop-button').prop('disabled',true);
$('#stop-text').html("<br><small>_(Disabled)_ -- _(Parity operation is running)_</small>");
<?if (_var($var,'fsState')!="Stopped" && _var($var,'mdResync',0)>0):?>
<?if (_var($var,'fsState') != "Stopped" && _var($var,'mdResync',0) > 0):?>
$('#spinup-button').prop('disabled',true);
$('#spindown-button').prop('disabled',true);
<?endif;?>
<?if (_var($var,'shareUser')=='e' && $pool_devices):?>
<?if (_var($var,'shareUser') == 'e' && $pool_devices):?>
$('#mover-button').prop('disabled',true);
$('#mover-text').html("_(Disabled)_ -- _(Parity operation is running)_");
<?endif;?>
@@ -349,7 +410,7 @@ mymonitor.on('message', function(state) {
case '2': // mover running
$('#stop-button').prop('disabled',true);
$('#stop-text').html("<br><small>_(Disabled)_ -- _(Mover is running)_</small>");
<?if (_var($var,'shareUser')=='e' && $pool_devices):?>
<?if (_var($var,'shareUser') == 'e' && $pool_devices):?>
$('#mover-button').prop('disabled',true);
$('#mover-text').html("_(Disabled)_ - _(Mover is running)_.");
<?endif;?>
@@ -357,7 +418,7 @@ mymonitor.on('message', function(state) {
case '3': // btrfs running
$('#stop-button').prop('disabled',true);
$('#stop-text').html("<br><small>_(Disabled)_ -- _(BTRFS operation is running)_</small>");
<?if (_var($var,'shareUser')=='e' && $pool_devices):?>
<?if (_var($var,'shareUser') == 'e' && $pool_devices):?>
$('#mover-button').prop('disabled',true);
$('#mover-text').html("_(Disabled)_ -- _(BTRFS operation is running)_");
<?endif;?>
@@ -369,11 +430,11 @@ mymonitor.start();
var arraymonitor = new NchanSubscriber('/sub/arraymonitor',{subscriber:'websocket', reconnectTimeout:5000});
arraymonitor.on('message', function(state) {
if (state==1 && !timers.arraymonitor) timers.arraymonitor = setTimeout(refresh,1250);
if (state == 1 && !timers.arraymonitor) timers.arraymonitor = setTimeout(refresh,1250);
});
var devices = new NchanSubscriber('/sub/devices<?=$spot?",parity":""?>',{subscriber:'websocket', reconnectTimeout:5000});
devices.on('message', function(msg,meta) {
devices.on('message', function(msg, meta) {
switch (<?if($spot):?>meta.id.channel()<?else:?>0<?endif;?>) {
case 0:
// array + pool + ua + flash devices
@@ -382,9 +443,9 @@ devices.on('message', function(msg,meta) {
if (recall !== null) recall.html('&nbsp;');
display_diskio();
// stop updating when array is stopped
if (get.stop==1) {
if (get.stop == 1) {
$('thead tr').removeClass().addClass('offline');
<?if (_var($var,'fsState')=='Started'):?>
<?if (_var($var,'fsState') == 'Started'):?>
setTimeout(refresh);
<?else:?>
if (!timers.stopped) timers.stopped = setTimeout(function(){devices.stop(); arraymonitor.start();},1500);
@@ -402,17 +463,17 @@ devices.on('message', function(msg,meta) {
var get = JSON.parse(msg);
$.each(get,function(k,v) {if ($('#line'+k).length>0) $('#line'+k).html(v);});
// button control
if ($('#pauseButton').length>0 && $('#pauseButton').prop('disabled')==false) {
if ((get === "") && $('#cancelButton').val()=="_(Cancel)_") {
if ($('#pauseButton').length > 0 && $('#pauseButton').prop('disabled') == false) {
if ((get === "") && $('#cancelButton').val() == "_(Cancel)_") {
$('#cancelButton').val("_(Done)_").prop('onclick',null).off('click').click(function(){refresh();});
$('#pauseButton').prop('disabled',true);
$('#cancelText').html('');
$('#line4').html("_(completed)_");
} else {
var form = document.arrayOps;
if ($('#pauseButton').val()=="_(Pause)_" && get[1].search("_(paused)_")!=-1) {
if ($('#pauseButton').val() == "_(Pause)_" && get[1].search("_(paused)_") != -1) {
$('#pauseButton').val("_(Resume)_").prop('onclick',null).off('click').click(function(){resumeParity(form);});
} else if ($('#pauseButton').val()=="_(Resume)_" && get[1].search("_(paused)_")==-1) {
} else if ($('#pauseButton').val() == "_(Resume)_" && get[1].search("_(paused)_") == -1) {
$('#pauseButton').val("_(Pause)_").prop('onclick',null).off('click').click(function(){pauseParity(form);});
}
}
@@ -422,7 +483,7 @@ devices.on('message', function(msg,meta) {
});
devices.start().monitor();
<?if (substr(_var($var,'fsState'),-3)=='ing'):?>
<?if (substr(_var($var,'fsState'),-3) == 'ing'):?>
var fsState = new NchanSubscriber('/sub/fsState',{subscriber:'websocket', reconnectTimeout:5000});
fsState.on('message', function(msg) {
switch (msg) {
@@ -439,7 +500,7 @@ fsState.start();
setTimeout(function(){$('#pauseButton').prop('disabled',false);$('#cancelButton').prop('disabled',false);},250);
<?else:?>
var paritymonitor = new NchanSubscriber('/sub/paritymonitor',{subscriber:'websocket', reconnectTimeout:5000});
paritymonitor.on('message', function(busy){if (busy==1) refresh();});
paritymonitor.on('message', function(busy){if (busy == 1) refresh();});
setTimeout(function(){paritymonitor.start();},5000);
<?endif;?>
@@ -450,7 +511,7 @@ $(function(){
});
function formatWarning(val) {
if (val==true) {
if (val == true) {
swal({
title:"_(Format Unmountable disks)_",
text: "_(Create an empty file system on the disks shown as **Unmountable** discarding all data currently on the disks and update parity to reflect this)_. "+
@@ -465,11 +526,11 @@ function formatWarning(val) {
}
window.onunload = function(){
<?if (_var($var,'fsState')=='Started'):?>
<?if (_var($var,'fsState') == 'Started'):?>
try {mymonitor.stop();} catch(e) {}
try {devices.stop();} catch(e) {}
<?endif;?>
<?if ($spot==0):?>
<?if ($spot == 0):?>
try {paritymonitor.stop();} catch(e) {}
<?endif;?>
}
@@ -483,42 +544,42 @@ window.onunload = function(){
<tr><td><?status_indicator()?>**_(Started)_<?=((_var($var,'startMode')=='Maintenance')?' - _(Maintenance Mode)_':'')?>**</td>
<td><input type="button" id="stop-button" value="_(Stop)_" onclick="stopArray(this.form)"></td>
<td>**_(Stop)_** _(will take the array off-line)_.<span id="stop-text"></span></td></tr>
<? if (_var($var,'fsNumUnmountable',0)>0):?>
<tr><td>**<?=_('Unmountable disk'.(_var($var,'fsNumUnmountable',0)==1?'':'s').' present')?>:**<br>
<? if (_var($var,'fsNumUnmountable',0) > 0):?>
<tr><td>**<?=_('Unmountable disk'.(_var($var,'fsNumUnmountable',0) == 1 ? '' : 's').' present')?>:**<br>
<? $cache = [];
foreach ($disks as $disk) if (substr(_var($disk,'fsStatus'),0,11)=='Unmountable' || in_array(pool_name(_var($disk,'name')),$cache)) {
foreach ($disks as $disk) if (substr(_var($disk,'fsStatus'),0,11) == 'Unmountable' || in_array(pool_name(_var($disk,'name')),$cache)) {
if (strlen(_var($disk,'id'))) echo "<span class='blue-text'>".my_disk(_var($disk,'name'))."</span> &bullet; ".my_id(_var($disk,'id'))." ("._var($disk,'device').")<br>";
if (in_array(_var($disk,'name'),$pools)) $cache[] = $disk['name'];
}
?> </td><td><input type="submit" id="btnFormat" name="cmdFormat" value="_(Format)_" disabled><input type="hidden" name="unmountable_mask" value="<?=_var($var,'fsUnmountableMask')?>"></td>
<td>**_(Format)_** _(will create a file system in all **Unmountable** disks)_.<br>
<a class="info none img nohand"><input type="checkbox" name="confirmFormat" value="OFF" onclick="formatWarning(this.checked),$('#btnFormat').prop('disabled',!arrayOps.confirmFormat.checked)">
<small>_(Yes, I want to do this)_</small></a>
<small>_(Yes, I want to do this)_</small></a>
</td></tr>
<? endif;
$action = preg_split('/\s+/',_var($var,'mdResyncAction'));
if (!$spot):
if ($action[0]=="recon"):
if ($action[0] == "recon"):
$resync = resync($action[1]);
?> <tr><td></td><td><input type="submit" name="cmdCheckSync" value="_(Sync)_"></td><td>**<?=_('Sync')?>** <?=_("will start **$resync**")?>.</td></tr>
<? elseif ($action[0]=="clear"):?>
<tr><td></td><td><input type="submit" name="cmdCheckClear" value="_(Clear)_"></td><td>**_(Clear)_** _(will start **Disk-Clear** of new data disk(s))_.</td></tr>
<? else:
if ($action[0]=="check" && count($action)>1):?>
if ($action[0] == "check" && count($action) > 1):?>
<tr><td>_(Parity is valid)_.</td><td><input type="submit" name="cmdCheck" value="_(Check)_"></td><td>**_(Check)_** _(will start **Parity-Check**)_.&nbsp;<a href="/Main/Settings/Scheduler"<?if ($tabbed):?> onclick="$.cookie('one','tab1')"<?endif;?>>(_(Schedule)_)</a>
<br><input type="checkbox" name="optionCorrect" value="correct" checked><small>_(Write corrections to parity)_</small></td></tr>
<? elseif ($action[0]=="check"):?>
<? elseif ($action[0] == "check"):?>
<tr><td></td><td><input type="submit" name="cmdCheck" value="_(Check)_"></td><td>**_(Check)_** _(will start **Read-Check** of all array disks)_.</td></tr>
<? endif;?>
<? if (!$poolsOnly):?>
<tr><td></td><td><input type="button" value="_(History)_" onclick="parityHistory()"></td>
<? [$date,$duration,$speed,$status,$error,$action,$size] = last_parity_log();
if (_var($var,'sbSyncExit',0)!=0):?>
<? [$date, $duration, $speed, $status, $error, $action, $size] = last_parity_log();
if (_var($var,'sbSyncExit',0) != 0):?>
<td class="wrap"><?=sprintf(_('Last check incomplete on **%s**'),_(my_time(_var($var,'sbSynced2',0)).day_count(_var($var,'sbSynced2',0)),0))?><?if (_var($var,'sbSynced2')):?>
<br><i class="fa fa-fw fa-dot-circle-o"></i> _(Error code)_: <?=my_error(_var($var,'sbSyncExit'))?>
<br><i class="fa fa-fw fa-search"></i> <?=print_error(_var($var,'sbSyncErrs',0))?><?endif;?></td></tr>
<? elseif (_var($var,'sbSynced',0)==0):
if ($status==0):?>
<? elseif (_var($var,'sbSynced',0) == 0):
if ($status == 0):?>
<td class="wrap"><?=sprintf(_('Last checked on **%s**'),_(my_time($date).day_count($date),0))?>
<br><i class="fa fa-fw fa-clock-o"></i> _(Duration)_: <?=my_check($duration,$speed)?>
<br><i class="fa fa-fw fa-search"></i> <?=print_error($error)?></td></tr>
@@ -528,7 +589,7 @@ window.onunload = function(){
<br><i class="fa fa-fw fa-search"></i> <?=print_error($error)?></td></tr>
<? endif;
elseif (_var($var,'sbSynced2',0)==0):
if ($status==0):?>
if ($status == 0):?>
<td class="wrap"><?=sprintf(_('Last checked on **%s**'),_(my_time(_var($var,'sbSynced',0)).day_count(_var($var,'sbSynced',0)),0))?>
<br><i class="fa fa-fw fa-clock-o"></i> _(Duration)_: <?=my_check($duration,$speed)?>
<br><i class="fa fa-fw fa-search"></i> <?=print_error($error)?></td></tr>
@@ -545,24 +606,24 @@ window.onunload = function(){
endif; // end check for poolsOnly
endif;
else:
if ($action[0]=="recon"):
if ($action[0] == "recon"):
$resync = resync($action[1]);
?> <tr><td><?=_("$resync in progress")?>.</td><td>
<input type="button" id="pauseButton"<?if (_var($var,'mdResync')):?> value="_(Pause)_" onclick="pauseParity(this.form)"<?else:?> value="_(Resume)_" onclick="resumeParity(this.form)"<?endif;?> disabled>
<input type="button" id="cancelButton" value="_(Cancel)_" onclick="stopParity(this.form,'<?=$resync?>')" disabled></td>
<td id="cancelText"><?if (_var($var,'mdResync')):?>**<?=_('Pause')?>** <?=_("will pause $resync")?>.<?else:?>**<?=_('Resume')?>** <?=_("will resume $resync")?>.<?endif;?><br>**<?=_('Cancel')?>** <?=_("will stop $resync")?>.
<br>_(WARNING: canceling may leave the array unprotected)_!</td></tr>
<? elseif ($action[0]=="clear"):?>
<? elseif ($action[0] == "clear"):?>
<tr><td>_(Disk-Clear in progress)_.</td><td>
<input type="button" id="pauseButton"<?if (_var($var,'mdResync')):?> value="_(Pause)_" onclick="pauseParity(this.form)"<?else:?> value="_(Resume)_" onclick="resumeParity(this.form)"<?endif;?> disabled>
<input type="button" id="cancelButton" value="_(Cancel)_" onclick="stopParity(this.form,'Disk-Clear')" disabled></td>
<td id="cancelText"><?if (_var($var,'mdResync')):?>**_(Pause)_** _(will pause Disk-Clear)_.<?else:?>**_(Resume)_** _(will resume Disk-Clear)_.<?endif;?><br>**_(Cancel)_** _(will stop Disk-Clear)_.</td></tr>
<? elseif ($action[0]=="check" && count($action)>1):?>
<? elseif ($action[0] == "check" && count($action) > 1):?>
<tr><td>_(Parity-Check in progress)_.</td><td>
<input type="button" id="pauseButton"<?if (_var($var,'mdResync')):?> value="_(Pause)_" onclick="pauseParity(this.form)"<?else:?> value="_(Resume)_" onclick="resumeParity(this.form)"<?endif;?> disabled>
<input type="button" id="cancelButton" value="_(Cancel)_" onclick="stopParity(this.form,'Parity-Check')" disabled></td>
<td id="cancelText"><?if (_var($var,'mdResync')):?>**_(Pause)_** _(will pause Parity-Check)_.<?else:?>**_(Resume)_** _(will resume Parity-Check)_.<?endif;?><br>**_(Cancel)_** _(will stop Parity-Check)_.</td></tr>
<? elseif ($action[0]=="check"):?>
<? elseif ($action[0] == "check"):?>
<tr><td>_(Read-Check in progress)_.</td><td>
<input type="button" id="pauseButton"<?if (_var($var,'mdResync')):?> value="_(Pause)_" onclick="pauseParity(this.form)"<?else:?> value="_(Resume)_" onclick="resumeParity(this.form)"<?endif;?> disabled>
<input type="button" id="cancelButton" value="_(Cancel)_" onclick="stopParity(this.form,'Read-Check')" disabled></td>
@@ -579,11 +640,11 @@ window.onunload = function(){
<tr><td>_(Current position)_:</td><td id="line2"></td><td></td></tr>
<tr><td>_(Estimated speed)_:</td><td id="line3"></td><td></td></tr>
<tr><td>_(Estimated finish)_:</td><td id="line4"></td><td></td></tr>
<? if ($action[0]=="check"):?>
<? if (count($action)>1):?>
<tr><td><?=_var($var,'mdResyncCorr')==0 ? _('Sync errors detected') : _('Sync errors corrected')?>:</td><td id="line5"></td><td></td></tr>
<? if ($action[0] == "check"):?>
<? if (count($action) > 1):?>
<tr><td><?=_var($var,'mdResyncCorr') == 0 ? _('Sync errors detected') : _('Sync errors corrected')?>:</td><td id="line5"></td><td></td></tr>
<? else:?>
<tr><td><?=_var($var,'mdResyncCorr')==0 ? _('Read errors detected') : _('Read errors corrected')?>:</td><td id="line5"></td><td></td></tr>
<tr><td><?=_var($var,'mdResyncCorr') == 0 ? _('Read errors detected') : _('Read errors corrected')?>:</td><td id="line5"></td><td></td></tr>
<? endif;
endif;
endif;
@@ -610,19 +671,19 @@ window.onunload = function(){
<tr><td><?status_indicator()?>**_(Stopping)_...**</td><td><input type="submit" name="cmdStop" value="_(Stop)_" disabled></td><td></td></tr>
<? break;
case "Stopped":
if (_var($var,'configValid')=="error"):?>
if (_var($var,'configValid') == "error"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Invalid, missing or expired)_ <a href="/Tools/Registration">_(registration key)_</a>.</td></tr>
<? elseif (_var($var,'configValid')=="invalid"):?>
<? elseif (_var($var,'configValid') == "invalid"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Too many attached devices. Please consider upgrading your)_ <a href="/Tools/Registration">_(registration key)_</a>.</td></tr>
<? elseif (_var($var,'configValid')=="ineligible"):?>
<? elseif (_var($var,'configValid') == "ineligible"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Ineligible to run this version of Unraid OS. Please consider extending your)_ <a href="/Tools/Registration">_(registration key)_</a>.</td></tr>
<? elseif (_var($var,'configValid')=="nokeyserver"):?>
<? elseif (_var($var,'configValid') == "nokeyserver"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Cannot contact key-server. Please check your)_ <a href="/Settings/NetworkSettings">_(network settings)_</a>.</td></tr>
<? elseif (_var($var,'configValid')=="withdrawn"):?>
<? elseif (_var($var,'configValid') == "withdrawn"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(This Unraid OS release has been withdrawn and may no longer be used. Please)_ <a href="/Plugins">_(update)_</a> _(your server)_.</td></tr>
<? else:
@@ -635,18 +696,18 @@ window.onunload = function(){
break;
case "STOPPED":
$action = explode(' ',_var($var,'mdResyncAction'));
if ($action[0]=="recon"):
if ($action[0] == "recon"):
$resync = resync($action[1]);
?> <tr><td><?status_indicator()?>**_(Stopped)_**. _(Configuration valid)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**<?=_('Start')?>** <?=_("will bring the array on-line and start **$resync**")?>.</td></tr>
<? elseif ($action[0]=="clear"):?>
<? elseif ($action[0] == "clear"):?>
<tr><td><?status_indicator()?>**_(Stopped)_**. _(New data disk(s) detected)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**_(Start)_** _(will bring the array on-line and start **Disk-Clear** of new data disk(s))_.</td></tr>
<? elseif (_var($var,'sbClean')!="yes" && $action[0]=="check" && count($action)>1):?>
<? elseif (_var($var,'sbClean') != "yes" && $action[0] == "check" && count($action) > 1):?>
<tr><td><?status_indicator()?>**_(Stopped)_**. _(Unclean shutdown detected)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**_(Start)_** _(will bring the array on-line and start **Parity-Check**)_.
<br><input type="checkbox" name="optionCorrect" value="correct" checked><small>_(Write corrections to parity)_</small></td></tr>
<? elseif (_var($var,'sbClean')!="yes" && $action[0]=="check"):?>
<? elseif (_var($var,'sbClean') != "yes" && $action[0] == "check"):?>
<tr><td><?status_indicator()?>**_(Stopped)_**. _(Unclean shutdown detected)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**_(Start)_** _(will bring the array on-line)_.</td></tr>
<? elseif (missing_cache()):?>
@@ -661,7 +722,7 @@ window.onunload = function(){
check_encryption();
break;
case "NEW_ARRAY":
if (strpos(_var($disks['parity'],'status'),"DISK_NP")===0 && strpos(_var($disks['parity2'],'status'),"DISK_NP")===0):?>
if (strpos(_var($disks['parity'],'status'),"DISK_NP") === 0 && strpos(_var($disks['parity2'],'status'),"DISK_NP") === 0):?>
<tr><td><?status_indicator()?>**_(Stopped)_**. _(Configuration valid)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**_(Start)_** _(will record all disk information and bring the array on-line)_.
<br>_(The array will be immediately available, but **unprotected** since *parity* has not been assigned)_.</td></tr>
@@ -688,7 +749,7 @@ window.onunload = function(){
check_encryption();
break;
case "SWAP_DSBL":
if (_var($var,'fsCopyPrcnt')=="100"):?>
if (_var($var,'fsCopyPrcnt') == "100"):?>
<tr><td><?status_indicator()?>**_(Stopped)_**. _(Upgrading disk/swapping parity)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**_(Start)_** _(will expand the file system of the data disk (if possible); then bring the array on-line and start Data-Rebuild)_.</td></tr>
<? maintenance_mode();
@@ -731,7 +792,7 @@ endswitch;
<tr><td></td><td class="line" colspan="2"></td></tr>
</table>
</form>
<?if (_var($var,'fsState')!="Stopped"):?>
<?if (_var($var,'fsState') != "Stopped"):?>
<?if ($keyfile):?>
<form name="delete_keyfile" method="POST" action="/update.php" target="progressFrame">
<input type="hidden" name="#file" value="unused">
@@ -749,7 +810,7 @@ endswitch;
<tr id="clearstats" class="hidden"><td></td><td><input type="button" value="_(Clear Stats)_" onclick="toggle_state('Clear')"></td><td>**_(Clear Stats)_** _(will immediately clear all disk statistics)_.</td></tr>
<tr><td></td><td class="line" colspan="2"></td></tr>
</table>
<?if (_var($var,'shareUser')=='e' && $pool_devices):?>
<?if (_var($var,'shareUser') == 'e' && $pool_devices):?>
<form name="mover_schedule" method="POST" action="/update.htm" target="progressFrame">
<table markdown="1" class="array_status noshift">
<tr><td></td><td><input type="submit" id="mover-button" name="cmdStartMover" value="_(Move)_"></td><td id="mover-text"></td></tr>
@@ -776,7 +837,7 @@ endswitch;
<!-- markdown fix --></p><?if (isset($display['sleep'])) eval('?>'.parse_file($display['sleep']))?>
:array_status_help:
<?if (_var($var,'fsState')=="Stopped"):?>
<?if (_var($var,'fsState') == "Stopped"):?>
:array_devices_help:
<?if ($encrypt):?>

View File

@@ -31,6 +31,7 @@ String.prototype.celsius = function(){return Math.round((parseInt(this)-32)*5/9)
function base64(str) {
return window.btoa(unescape(encodeURIComponent(str)));
}
function doDispatch(form) {
var fields = {};
<?if ($display['unit']=='F'):?>
@@ -45,6 +46,7 @@ function doDispatch(form) {
$(form).find('select[name^="display_"]').each(function(){fields[$(this).attr('name')] = $(this).val(); $(this).prop('disabled',true);});
$.post('/webGui/include/Dispatcher.php',fields);
}
function prepareDiskSettings(form) {
var events = [];
for (var i=0; i < <?=count($preselect)?>; i++) {
@@ -58,26 +60,44 @@ function prepareDiskSettings(form) {
if (form.smEvents.value == '<?=$numbers?>') form.smEvents.value = '';
if (form.smLevel.value == 1.00) form.smLevel.value = '';
}
function setIndex(form) {
form.smIndex.value = form.smType.selectedIndex;
}
function prepareForm(form) {
<?if (!$keyfile):?>
form.oldluks.value = base64(form.oldtext.value);
form.oldluks.value = base64(form.oldtext.value.replace(/\\"/g,'"'));
form.oldtext.disabled = true;
form.oldfile.disabled = true;
<?endif;?>
form.newluks.value = base64(form.newtext.value);
form.newtext.disabled = true;
form.newcopy.disabled = true;
form.newfile.disabled = true;
var valid = new RegExp('^[ -~]+$');
if (form.newinput.value == 'file') return true;
if (valid.test(form.newtext.value)) {
form.newluks.value = base64(form.newtext.value.replace(/\\"/g,'"'));
form.newtext.disabled = true;
form.newcopy.disabled = true;
form.newfile.disabled = true;
return true;
} else {
swal({
title:"_(Printable Characters Only)_",
text:"_(Use **ASCII** characters from space ' ' to tilde '~')_<br>_(Otherwise use the **keyfile** method for UTF8 input)_",
html:true,
type:'error',
confirmButtonText:"_(Ok)_"
});
return false;
}
}
function getFileContent(event,form,file) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){$(form).find('input[name="'+file+'"]').val(reader.result);};
reader.readAsDataURL(input.files[0]);
}
function selectInput(val,old) {
if (val=='text') {
if (old==true) {
@@ -86,7 +106,7 @@ function selectInput(val,old) {
} else {
$('div#newfile').hide('slow');
$('div#newtext').show('slow');
$('input[name="newkey"]').prop('disabled',$('input[name="newtext"]').val()=='' || $('input[name="newtext"]').val()!=$('input[name="newcopy"]').val());
$('input[name="newkey"]').prop('disabled',$('input[name="newtext"]').val() == '' || $('input[name="newtext"]').val() != $('input[name="newcopy"]').val());
}
} else if (val=='file') {
if (old==true) {
@@ -95,25 +115,34 @@ function selectInput(val,old) {
} else {
$('div#newtext').hide('slow');
$('div#newfile').show('slow');
$('input[name="newkey"]').prop('disabled',$('input[name="newfile"]').val()=='');
$('input[name="newkey"]').prop('disabled',$('input[name="newfile"]').val() == '');
}
}
}
function showInput(show,old) {
function showInput(show, old) {
if (old==true) {
var input = $('input[name="oldtext"]');
} else {
var input = $('input[name="newtext"],input[name="newcopy"]');
}
input.attr('type',show ? 'text' : 'password');
input.attr('type', show ? 'text' : 'password');
}
function checkInput(form) {
$(form).find('input[name="newkey"]').prop('disabled',form.newtext.value=='' || form.newtext.value!=form.newcopy.value);
$(form).find('input[name="newkey"]').prop('disabled',form.newtext.value == '' || form.newtext.value != form.newcopy.value);
}
<?if (is_file($reply)):?>
<?[$text,$type] = explode("\0",file_get_contents($reply)); unlink($reply);?>
$(function() {
swal({title:"_(Encryption Key Update)_",text:"<?=$text?>",html:true,type:"<?=$type?>",confirmButtonText:"_(Ok)_"});
swal({
title:"_(Encryption Key Update)_",
text:"<?=$text?>",
html:true,
type:"<?=$type?>",
confirmButtonText:"_(Ok)_"
});
});
<?endif;?>
</script>
@@ -275,7 +304,7 @@ _(Default critical SSD temperature threshold)_ (&deg;<?=_var($display,'unit','C'
<i class="title fa fa-key"></i>_(Change encryption key)_
</span>
</div>
<form markdown="1" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareForm(this)">
<form markdown="1" method="POST" action="/update.php" target="progressFrame" onsubmit="return prepareForm(this)">
<input type="hidden" name="#file" value="">
<input type="hidden" name="#include" value="/webGui/include/update.encryption.php">
<input type="hidden" name="#reply" value="<?=$reply?>">

View File

@@ -1,6 +1,6 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, 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,
@@ -17,121 +17,149 @@ require_once "$docroot/webGui/include/Secure.php";
// Helper functions
function my_scale($value, &$unit, $decimals=NULL, $scale=NULL, $kilo=1000) {
global $display,$language;
global $display, $language;
$scale = $scale ?? $display['scale'];
$number = _var($display,'number','.,');
$units = explode(' ', ' '.($kilo==1000 ? ($language['prefix_SI'] ?? 'K M G T P E Z Y') : ($language['prefix_IEC'] ?? 'Ki Mi Gi Ti Pi Ei Zi Yi')));
$size = count($units);
if ($scale==0 && ($decimals===NULL || $decimals<0)) {
if ($scale == 0 && ($decimals === NULL || $decimals < 0)) {
$decimals = 0;
$unit = '';
} else {
$base = $value ? intval(floor(log($value, $kilo))) : 0;
if ($scale>0 && $base>$scale) $base = $scale;
if ($base>$size) $base = $size-1;
if ($scale > 0 && $base > $scale) $base = $scale;
if ($base > $size) $base = $size - 1;
$value /= pow($kilo, $base);
if ($decimals===NULL) $decimals = $value>=100 ? 0 : ($value>=10 ? 1 : (round($value*100)%100===0 ? 0 : 2));
elseif ($decimals<0) $decimals = $value>=100||round($value*10)%10===0 ? 0 : abs($decimals);
if ($scale<0 && round($value,-1)==1000) {$value = 1; $base++;}
if ($decimals === NULL) $decimals = $value >= 100 ? 0 : ($value >= 10 ? 1 : (round($value*100)%100 === 0 ? 0 : 2));
elseif ($decimals < 0) $decimals = $value >= 100 || round($value*10)%10 === 0 ? 0 : abs($decimals);
if ($scale < 0 && round($value,-1) == 1000) {$value = 1; $base++;}
$unit = $units[$base]._('B');
}
return number_format($value, $decimals, $number[0], $value>9999 ? $number[1] : '');
return number_format($value, $decimals, $number[0], $value > 9999 ? $number[1] : '');
}
function my_number($value) {
global $display;
$number = _var($display,'number','.,');
return number_format($value, 0, $number[0], ($value>=10000 ? $number[1] : ''));
return number_format($value, 0, $number[0], ($value >= 10000 ? $number[1] : ''));
}
function my_time($time, $fmt=NULL) {
global $display;
if (!$fmt) $fmt = _var($display,'date').(_var($display,'date')!='%c' ? ", "._var($display,'time') : "");
return $time ? my_date($fmt, $time) : _('unknown');
}
function my_temp($value) {
global $display;
$unit = _var($display,'unit','C');
$number = _var($display,'number','.,');
return is_numeric($value) ? (($unit=='F' ? fahrenheit($value) : str_replace('.', $number[0], $value)).'&#8201;&#176;'.$unit) : $value;
return is_numeric($value) ? (($unit == 'F' ? fahrenheit($value) : str_replace('.', $number[0], $value)).'&#8201;&#176;'.$unit) : $value;
}
function my_disk($name, $raw=false) {
global $display;
return _var($display,'raw')||$raw ? $name : ucfirst(preg_replace('/(\d+)$/',' $1',$name));
return _var($display,'raw') || $raw ? $name : ucfirst(preg_replace('/(\d+)$/',' $1',$name));
}
function my_disks($disk) {
return strpos(_var($disk,'status'),'_NP')===false;
return strpos(_var($disk,'status'),'_NP') === false;
}
function my_hyperlink($text, $link) {
return str_replace(['[',']'],["<a href=\"$link\">","</a>"],$text);
}
function main_only($disk) {
return _var($disk,'type')=='Parity' || _var($disk,'type')=='Data';
return _var($disk,'type') == 'Parity' || _var($disk,'type') == 'Data';
}
function parity_only($disk) {
return _var($disk,'type')=='Parity';
return _var($disk,'type') == 'Parity';
}
function data_only($disk) {
return _var($disk,'type')=='Data';
return _var($disk,'type') == 'Data';
}
function cache_only($disk) {
return _var($disk,'type')=='Cache';
return _var($disk,'type') == 'Cache';
}
function luks_only($disk) {
return _var($disk,'type') == 'Data' || _var($disk,'type') == 'Cache';
}
function main_filter($disks) {
return array_filter($disks,'main_only');
return array_filter($disks, 'main_only');
}
function parity_filter($disks) {
return array_filter($disks,'parity_only');
return array_filter($disks, 'parity_only');
}
function data_filter($disks) {
return array_filter($disks,'data_only');
return array_filter($disks, 'data_only');
}
function cache_filter($disks) {
return array_filter($disks,'cache_only');
return array_filter($disks, 'cache_only');
}
function luks_filter($disks) {
return array_filter($disks, 'luks_only');
}
function pools_filter($disks) {
return array_unique(array_map('prefix',array_keys(cache_filter($disks))));
return array_unique(array_map('prefix', array_keys(cache_filter($disks))));
}
function my_id($id) {
global $display;
$len = strlen($id);
$wwn = substr($id,-18);
return (_var($display,'wwn') || substr($wwn,0,2)!='_3' || preg_match('/.[_-]/',$wwn)) ? $id : substr($id,0,$len-18);
return (_var($display,'wwn') || substr($wwn,0,2) != '_3' || preg_match('/.[_-]/',$wwn)) ? $id : substr($id,0,$len-18);
}
function my_word($num) {
$words = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen','twenty','twenty-one','twenty-two','twenty-three','twenty-four','twenty-five','twenty-six','twenty-seven','twenty-eight','twenty-nine','thirty'];
return $num<count($words) ? _($words[$num],1) : $num;
return $num < count($words) ? _($words[$num],1) : $num;
}
function my_usage() {
global $disks,$var,$display;
$arraysize=0;
$arrayfree=0;
global $disks, $var, $display;
$arraysize = 0;
$arrayfree = 0;
foreach ($disks as $disk) {
if (strpos(_var($disk,'name'),'disk')!==false) {
if (strpos(_var($disk,'name'),'disk') !== false) {
$arraysize += _var($disk,'sizeSb',0);
$arrayfree += _var($disk,'fsFree',0);
}
}
if (_var($var,'fsNumMounted',0)>0) {
if (_var($var,'fsNumMounted',0) > 0) {
$used = $arraysize ? 100-round(100*$arrayfree/$arraysize) : 0;
echo "<div class='usage-bar'><span style='width:{$used}%' class='".usage_color($display,$used,false)."'>{$used}%</span></div>";
} else {
echo "<div class='usage-bar'><span style='text-align:center'>".($var['fsState']=='Started'?'Maintenance':'off-line')."</span></div>";
}
}
function usage_color(&$disk, $limit, $free) {
global $display;
if (_var($display,'text',0)==1 || intval(_var($display,'text',0)/10)==1) return '';
$critical = _var($disk,'critical')>=0 ? $disk['critical'] : (_var($display,'critical')>=0 ? $display['critical'] : 0);
$warning = _var($disk,'warning')>=0 ? $disk['warning'] : (_var($display,'warning')>=0 ? $display['warning'] : 0);
if (_var($display,'text',0) == 1 || intval(_var($display,'text',0)/10) == 1) return '';
$critical = _var($disk,'critical') >= 0 ? $disk['critical'] : (_var($display,'critical') >= 0 ? $display['critical'] : 0);
$warning = _var($disk,'warning') >= 0 ? $disk['warning'] : (_var($display,'warning') >= 0 ? $display['warning'] : 0);
if (!$free) {
if ($critical>0 && $limit>=$critical) return 'redbar';
if ($warning>0 && $limit>=$warning) return 'orangebar';
if ($critical > 0 && $limit >= $critical) return 'redbar';
if ($warning > 0 && $limit >= $warning) return 'orangebar';
return 'greenbar';
} else {
if ($critical>0 && $limit<=100-$critical) return 'redbar';
if ($warning>0 && $limit<=100-$warning) return 'orangebar';
if ($critical > 0 && $limit <= 100-$critical) return 'redbar';
if ($warning > 0 && $limit <= 100-$warning) return 'orangebar';
return 'greenbar';
}
}
function my_check($time, $speed) {
if (!$time) return _('unavailable (no parity-check entries logged)');
$days = floor($time/86400);
@@ -139,8 +167,9 @@ function my_check($time, $speed) {
$hour = floor($hmss/3600);
$mins = floor($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').": ".(is_numeric($speed) ? my_scale($speed,$unit,1)." $unit/s" : $speed);
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').": ".(is_numeric($speed) ? my_scale($speed,$unit,1)." $unit/s" : $speed);
}
function my_error($code) {
switch ($code) {
case -4:
@@ -149,25 +178,29 @@ function my_error($code) {
return "<strong>$code</strong>";
}
}
function mk_option($select, $value, $text, $extra="") {
return "<option value='$value'".($value==$select ? " selected" : "").(strlen($extra) ? " $extra" : "").">$text</option>";
return "<option value='$value'".($value == $select ? " selected" : "").(strlen($extra) ? " $extra" : "").">$text</option>";
}
function mk_option_check($name, $value, $text="") {
if ($text) {
$checked = in_array($value,explode(',',$name)) ? " selected" : "";
return "<option value='$value'$checked>$text</option>";
}
if (strpos($name,'disk')!==false) {
if (strpos($name,'disk') !== false) {
$checked = in_array($name,explode(',',$value)) ? " selected" : "";
return "<option value='$name'$checked>".my_disk($name)."</option>";
}
}
function mk_option_luks($name, $value, $luks) {
if (strpos($name,'disk')!==false) {
if (strpos($name,'disk') !== false) {
$checked = in_array($name,explode(',',$value)) ? " selected" : "";
return "<option luks='$luks' value='$name'$checked>".my_disk($name)."</option>";
}
}
function day_count($time) {
global $var;
if (!$time) return;
@@ -178,29 +211,33 @@ function day_count($time) {
$last = new DateTime("@".intval(($time+$offset)/86400)*86400);
$days = date_diff($last,$now)->format('%a');
switch (true) {
case ($days<0):
case ($days < 0):
return;
case ($days==0):
case ($days == 0):
return " <span class='green-text'>("._('today').")</span>";
case ($days==1):
case ($days == 1):
return " <span class='green-text'>("._('yesterday').")</span>";
case ($days<=31):
case ($days <= 31):
return " <span class='green-text'>(".sprintf(_('%s days ago'),my_word($days)).")</span>";
case ($days<=61):
case ($days <= 61):
return " <span class='orange-text'>(".sprintf(_('%s days ago'),$days).")</span>";
case ($days>61):
case ($days > 61):
return " <span class='red-text'>(".sprintf(_('%s days ago'),$days).")</span>";
}
}
function plus($val, $word, $last) {
return $val>0 ? (($val || $last) ? ($val.' '._($word.($val!=1?'s':'')).($last ?'':', ')) : '') : '';
return $val > 0 ? (($val || $last) ? ($val.' '._($word.($val != 1 ? 's' : '')).($last ? '' : ', ')) : '') : '';
}
function compress($name, $size=18, $end=6) {
return mb_strlen($name)<=$size ? $name : mb_substr($name,0,$size-($end?$end+3:0)).'...'.($end?mb_substr($name,-$end):'');
return mb_strlen($name) <= $size ? $name : mb_substr($name, 0, $size-($end ? $end+3 : 0)).'...'.($end ? mb_substr($name,-$end) : '');
}
function escapestring($name) {
return "\"$name\"";
}
function tail($file, $rows=1) {
$file = new SplFileObject($file);
$file->seek(PHP_INT_MAX);
@@ -215,61 +252,59 @@ function tail($file, $rows=1) {
/* Get the last parity check from the parity history. */
function last_parity_log() {
$log = '/boot/config/parity-checks.log';
if (file_exists($log)) {
list($date, $duration, $speed, $status, $error, $action, $size) = my_explode('|', tail($log), 7);
} else {
list($date, $duration, $speed, $status, $error, $action, $size) = array_fill(0, 7, 0);
}
if ($date) {
list($y, $m, $d, $t) = my_preg_split('/ +/', $date, 4);
$date = strtotime("$d-$m-$y $t");
}
return [$date, $duration, $speed, $status, $error, $action, $size];
$log = '/boot/config/parity-checks.log';
if (file_exists($log)) {
[$date, $duration, $speed, $status, $error, $action, $size] = my_explode('|', tail($log), 7);
} else {
[$date, $duration, $speed, $status, $error, $action, $size] = array_fill(0, 7, 0);
}
if ($date) {
[$y, $m, $d, $t] = my_preg_split('/ +/', $date, 4);
$date = strtotime("$d-$m-$y $t");
}
return [$date, $duration, $speed, $status, $error, $action, $size];
}
/* Get the last parity check from Unraid. */
function last_parity_check() {
global $var;
/* Files for the latest parity check. */
$stamps = '/var/tmp/stamps.ini';
$resync = '/var/tmp/resync.ini';
/* Get the latest parity information from Unraid. */
$synced = file_exists($stamps) ? explode(',',file_get_contents($stamps)) : [];
$sbSynced = array_shift($synced) ?: _var($var,'sbSynced',0);
$idle = [];
while (count($synced) > 1) {
$idle[] = array_pop($synced) - array_pop($synced);
}
$action = _var($var, 'mdResyncAction');
$size = _var($var, 'mdResyncSize', 0);
if (file_exists($resync)) {
list($action, $size) = my_explode(',', file_get_contents($resync));
}
$duration = $var['sbSynced2']-$sbSynced-array_sum($idle);
$status = _var($var,'sbSyncExit');
$speed = $status==0 ? round($size*1024/$duration) : 0;
$error = _var($var,'sbSyncErrs',0);
return [$duration, $speed, $status, $error, $action, $size];
global $var;
/* Files for the latest parity check. */
$stamps = '/var/tmp/stamps.ini';
$resync = '/var/tmp/resync.ini';
/* Get the latest parity information from Unraid. */
$synced = file_exists($stamps) ? explode(',',file_get_contents($stamps)) : [];
$sbSynced = array_shift($synced) ?: _var($var,'sbSynced',0);
$idle = [];
while (count($synced) > 1) {
$idle[] = array_pop($synced) - array_pop($synced);
}
$action = _var($var, 'mdResyncAction');
$size = _var($var, 'mdResyncSize', 0);
if (file_exists($resync)) {
list($action, $size) = my_explode(',', file_get_contents($resync));
}
$duration = $var['sbSynced2']-$sbSynced-array_sum($idle);
$status = _var($var,'sbSyncExit');
$speed = $status==0 ? round($size*1024/$duration) : 0;
$error = _var($var,'sbSyncErrs',0);
return [$duration, $speed, $status, $error, $action, $size];
}
function urlencode_path($path) {
return str_replace("%2F", "/", urlencode($path));
}
function pgrep($process_name, $escape_arg=true) {
$pid = exec('pgrep --ns $$ '.($escape_arg?escapeshellarg($process_name):$process_name), $output, $retval);
return $retval==0 ? $pid : false;
$pid = exec('pgrep --ns $$ '.($escape_arg ? escapeshellarg($process_name) : $process_name), $output, $retval);
return $retval == 0 ? $pid : false;
}
function is_block($path) {
return (@filetype(realpath($path))=='block');
return (@filetype(realpath($path)) == 'block');
}
function autov($file,$ret=false) {
function autov($file, $ret=false) {
global $docroot;
$path = $docroot.$file;
clearstatcache(true, $path);
@@ -280,28 +315,34 @@ function autov($file,$ret=false) {
else
echo $newFile;
}
function transpose_user_path($path) {
if (strpos($path,'/mnt/user/')===0 && file_exists($path)) {
if (strpos($path,'/mnt/user/') === 0 && file_exists($path)) {
$realdisk = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null"));
if (!empty($realdisk))
$path = str_replace('/mnt/user/', "/mnt/$realdisk/", $path);
}
return $path;
}
function cpu_list() {
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
return $cpus;
}
function my_explode($split, $text, $count=2) {
return array_pad(explode($split,$text,$count),$count,'');
return array_pad(explode($split, $text, $count), $count, '');
}
function my_preg_split($split, $text, $count=2) {
return array_pad(preg_split($split,$text,$count),$count,'');
return array_pad(preg_split($split, $text, $count), $count, '');
}
function delete_file(...$file) {
array_map('unlink',array_filter($file,'file_exists'));
array_map('unlink', array_filter($file,'file_exists'));
}
function my_mkdir($dirname,$permissions = 0777,$recursive = false,$own = "nobody",$grp = "users") {
function my_mkdir($dirname, $permissions=0777, $recursive=false, $own="nobody", $grp="users") {
write_logging("Check if dir exists\n");
if (is_dir($dirname)) {write_logging("Dir exists\n"); return(false);}
write_logging("Dir does not exist\n");
@@ -314,7 +355,7 @@ function my_mkdir($dirname,$permissions = 0777,$recursive = false,$own = "nobody
$parent = $pathinfo2["dirname"];
}
write_logging("Parent $parent\n");
if (strpos($dirname,'/mnt/user/')===0) {
if (strpos($dirname,'/mnt/user/') === 0) {
write_logging("Getting real disks\n");
$realdisk = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($parent)." 2>/dev/null"));
if (!empty($realdisk)) {
@@ -329,21 +370,21 @@ function my_mkdir($dirname,$permissions = 0777,$recursive = false,$own = "nobody
case "zfs":
if (is_dir($parent.'/.zfs')) {
write_logging("ZFS Volume\n");
$zfsdataset = trim(shell_exec("zfs list -H -o name $parent"));
$zfsdataset = trim(shell_exec("zfs list -H -o name $parent"));
write_logging("Shell $zfsdataset\n");
$zfsdataset .= str_replace($parent,"",$dirname);
write_logging("Dataset $zfsdataset\n");
$zfsoutput = array();
if ($recursive) exec("zfs create -p \"$zfsdataset\"",$zfsoutput,$rtncode);else exec("zfs create \"$zfsdataset\"",$zfsoutput,$rtncode);
write_logging("Output: {$zfsoutput[0]} $rtncode");
if ($recursive) exec("zfs create -p \"$zfsdataset\"",$zfsoutput,$rtncode);else exec("zfs create \"$zfsdataset\"", $zfsoutput, $rtncode);
write_logging("Output: {$zfsoutput[0]} $rtncode");
if ($rtncode == 0) write_logging( " ZFS Command OK\n"); else write_logging( "ZFS Command Fail\n");
} else {write_logging("Not ZFS dataset\n");$rtncode = 1;}
if ($rtncode > 0) { mkdir($dirname, $permissions, $recursive); write_logging( "created dir:$dirname\n");} else chmod($zfsdataset,$permissions);
if ($rtncode > 0) { mkdir($dirname, $permissions, $recursive); write_logging( "created dir:$dirname\n");} else chmod($zfsdataset, $permissions);
break;
case "btrfs":
$btrfsoutput = array();
if ($recursive) exec("btrfs subvolume create --parents \"$dirname\"",$btrfsoutput,$rtncode); else exec("btrfs subvolume create \"$dirname\"",$btrfsoutput,$rtncode);
if ($rtncode > 0) mkdir($dirname, $permissions, $recursive); else chmod($dirname,$permissions);
if ($recursive) exec("btrfs subvolume create --parents \"$dirname\"",$btrfsoutput,$rtncode); else exec("btrfs subvolume create \"$dirname\"", $btrfsoutput, $rtncode);
if ($rtncode > 0) mkdir($dirname, $permissions, $recursive); else chmod($dirname, $permissions);
break;
default:
mkdir($dirname, $permissions, $recursive);
@@ -353,6 +394,7 @@ function my_mkdir($dirname,$permissions = 0777,$recursive = false,$own = "nobody
chgrp($dirname, $grp);
return($rtncode);
}
function my_rmdir($dirname) {
if (!is_dir("$dirname")) {
$return = [
@@ -361,7 +403,7 @@ function my_rmdir($dirname) {
];
return($return);
}
if (strpos($dirname,'/mnt/user/')===0) {
if (strpos($dirname,'/mnt/user/') === 0) {
$realdisk = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($dirname)." 2>/dev/null"));
if (!empty($realdisk)) {
$dirname = str_replace('/mnt/user/', "/mnt/$realdisk/", "$dirname");
@@ -395,9 +437,10 @@ function my_rmdir($dirname) {
}
return($return);
}
function get_realvolume($path) {
if (strpos($path,"/mnt/user/",0) === 0)
$reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null"));
if (strpos($path,"/mnt/user/",0) === 0)
$reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null"));
else {
$realexplode = explode("/",str_replace("/mnt/","",$path));
$reallocation = $realexplode[0];
@@ -411,13 +454,11 @@ function write_logging($value) {
file_put_contents('/tmp/my_mkdir_output', $value, FILE_APPEND);
}
function device_exists($name)
{
global $disks,$devs;
function device_exists($name) {
global $disks, $devs;
return (array_key_exists($name, $disks) && !str_contains(_var($disks[$name],'status'),'_NP')) || (array_key_exists($name, $devs));
}
# Check for process Core Types.
function parse_cpu_ranges($file) {
if (!is_file($file)) return null;
@@ -425,12 +466,12 @@ function parse_cpu_ranges($file) {
$ranges = trim($ranges);
$cores = [];
foreach (explode(',', $ranges) as $range) {
if (strpos($range, '-') !== false) {
list($start, $end) = explode('-', $range);
$cores = array_merge($cores, range((int)$start, (int)$end));
} else {
$cores[] = (int)$range;
}
if (strpos($range, '-') !== false) {
list($start, $end) = explode('-', $range);
$cores = array_merge($cores, range((int)$start, (int)$end));
} else {
$cores[] = (int)$range;
}
}
return $cores;
}
@@ -454,32 +495,32 @@ function get_intel_core_types() {
return $core_types;
}
function dmidecode($key,$n,$all=true) {
$entries = array_filter(explode($key,shell_exec("dmidecode -qt$n")??""));
function dmidecode($key, $n, $all=true) {
$entries = array_filter(explode($key, shell_exec("dmidecode -qt$n")??""));
$properties = [];
foreach ($entries as $entry) {
$property = [];
foreach (explode("\n",$entry) as $line) if (strpos($line,': ')!==false) {
[$key,$value] = my_explode(': ',trim($line));
foreach (explode("\n",$entry) as $line) if (strpos($line,': ') !== false) {
[$key, $value] = my_explode(': ',trim($line));
$property[$key] = $value;
}
$properties[] = $property;
}
return $all ? $properties : $properties[0]??null;
return $all ? $properties : $properties[0] ?? null;
}
function is_intel_cpu() {
$cpu = dmidecode('Processor Information','4',0);
$cpu = dmidecode('Processor Information','4',0);
$cpu_vendor = $cpu['Manufacturer'] ?? "";
$is_intel_cpu = stripos($cpu_vendor, "intel") !== false ? true : false;
return $is_intel_cpu;
}
// Load saved PCI data
function loadSavedData($filename) {
if (file_exists($filename)) {
$saveddata = file_get_contents($filename);
} else $saveddata = "";
return json_decode($saveddata, true);
}
@@ -487,26 +528,21 @@ function loadSavedData($filename) {
function loadCurrentPCIData() {
$output = shell_exec('lspci -Dmn');
$devices = [];
if (file_exists("/boot/config/current.json")){
$devices = loadSavedData("/boot/config/current.json");
if (file_exists("/boot/config/current.json")) {
$devices = loadSavedData("/boot/config/current.json");
} else {
foreach (explode("\n", trim($output)) as $line) {
$parts = explode(" ", $line);
if (count($parts) < 6) continue; // Skip malformed lines
$description_str = shell_exec(("lspci -s ".$parts[0]));
$description = preg_replace('/^\S+\s+/', '', $description_str);
$device = [
'class' => trim($parts[1], '"'),
'vendor_id' => trim($parts[2], '"'),
'device_id' => trim($parts[3], '"'),
'description' => trim($description,'"'),
];
$devices[$parts[0]] = $device;
$parts = explode(" ", $line);
if (count($parts) < 6) continue; // Skip malformed lines
$description_str = shell_exec(("lspci -s ".$parts[0]));
$description = preg_replace('/^\S+\s+/', '', $description_str);
$device = [
'class' => trim($parts[1], '"'),
'vendor_id' => trim($parts[2], '"'),
'device_id' => trim($parts[3], '"'),
'description' => trim($description,'"'),
];
$devices[$parts[0]] = $device;
}
}
return $devices;
@@ -514,61 +550,55 @@ function loadCurrentPCIData() {
// Compare the saved and current data
function comparePCIData() {
$changes = [];
$saved = loadSavedData("/boot/config/savedpcidata.json");
if (!$saved) return [];
$current = loadCurrentPCIData();
// Compare saved devices with current devices
foreach ($saved as $pci_id => $saved_device) {
if (!isset($current[$pci_id])) {
// Device has been removed
$changes[$pci_id] = [
'status' => 'removed',
'device' => $saved_device
];
} else {
// Device exists in both, check for modifications
$current_device = $current[$pci_id];
$differences = [];
// Compare fields
foreach (['vendor_id', 'device_id', 'class'] as $field) {
if (isset($saved_device[$field]) && isset($current_device[$field]) && $saved_device[$field] !== $current_device[$field]) {
$differences[$field] = [
'old' => $saved_device[$field],
'new' => $current_device[$field]
];
}
}
if (!empty($differences)) {
$changes[$pci_id] = [
'status' => 'changed',
'device' => $current_device,
'differences' => $differences
];
}
$changes = [];
$saved = loadSavedData("/boot/config/savedpcidata.json");
if (!$saved) return [];
$current = loadCurrentPCIData();
// Compare saved devices with current devices
foreach ($saved as $pci_id => $saved_device) {
if (!isset($current[$pci_id])) {
// Device has been removed
$changes[$pci_id] = [
'status' => 'removed',
'device' => $saved_device
];
} else {
// Device exists in both, check for modifications
$current_device = $current[$pci_id];
$differences = [];
// Compare fields
foreach (['vendor_id', 'device_id', 'class'] as $field) {
if (isset($saved_device[$field]) && isset($current_device[$field]) && $saved_device[$field] !== $current_device[$field]) {
$differences[$field] = [
'old' => $saved_device[$field],
'new' => $current_device[$field]
];
}
}
if (!empty($differences)) {
$changes[$pci_id] = [
'status' => 'changed',
'device' => $current_device,
'differences' => $differences
];
}
}
// Check for added devices
foreach ($current as $pci_id => $current_device) {
if (!isset($saved[$pci_id])) {
// Device has been added
$changes[$pci_id] = [
'status' => 'added',
'device' => $current_device
];
}
}
// Check for added devices
foreach ($current as $pci_id => $current_device) {
if (!isset($saved[$pci_id])) {
// Device has been added
$changes[$pci_id] = [
'status' => 'added',
'device' => $current_device
];
}
return $changes;
}
return $changes;
}
function clone_list($disk) {
global $pools;
return strpos($disk['status'],'_NP')===false && ($disk['type']=='Data' || in_array($disk['name'],$pools));
return strpos($disk['status'],'_NP') === false && ($disk['type'] == 'Data' || in_array($disk['name'], $pools));
}
?>

View File

@@ -1,6 +1,6 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, 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,
@@ -11,21 +11,15 @@
*/
?>
<?
$var = parse_ini_file('/var/local/emhttp/var.ini');
$ini = '/var/local/emhttp/keyfile.ini';
$tmp = '/var/tmp/missing.tmp';
$var = parse_ini_file('/var/local/emhttp/var.ini');
$luks = $var['luksKeyfile'];
$text = $_POST['text'] ?? false;
$file = $_POST['file'] ?? false;
if ($text) {
file_put_contents($luks, $text);
} elseif ($file) {
file_put_contents($luks, base64_decode(preg_replace('/^data:.*;base64,/','',$file)));
@unlink($tmp);
} elseif (file_exists($luks)) {
if ($file) {
file_put_contents($luks, base64_decode(explode(';base64,',$file)[1]));
} elseif ($text && file_exists($luks)) {
unlink($luks);
touch($tmp);
}
$save = false;
?>

View File

@@ -25,8 +25,7 @@ $_arrow_ = '&#187;';
function file_put_contents_atomic($filename,$data) {
while (true) {
$suffix = rand();
if ( ! is_file("$filename$suffix") )
break;
if (!is_file("$filename$suffix")) break;
}
$renResult = false;
$writeResult = @file_put_contents("$filename$suffix",$data) === strlen($data);
@@ -217,7 +216,7 @@ function my_logger($message, $logger='webgui') {
* @param ?array $getinfo Empty array passed by reference, will contain results of curl_getinfo and curl_error, or null if not needed
* @return string|false $out The fetched content
*/
function http_get_contents(string $url, array $opts = [], ?array &$getinfo = NULL) {
function http_get_contents(string $url, array $opts=[], ?array &$getinfo=NULL) {
$ch = curl_init();
if(isset($getinfo)) {
curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);
@@ -272,4 +271,8 @@ function lan_port($port, $state=false) {
$exist = file_exists("$system/$port");
return !$state ? $exist : ($exist ? (@file_get_contents("$system/$port/carrier") ?: 0) : false);
}
function shieldarg(...$args) {
return implode(' ', array_map('escapeshellarg', $args));
}
?>

View File

@@ -1,6 +1,6 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, 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,
@@ -22,63 +22,65 @@ $save = false;
$disks = parse_ini_file('state/disks.ini',true);
$newkey = parse_ini_file('state/var.ini')['luksKeyfile'] ?: '/root/keyfile';
$oldkey = dirname($newkey).'/oldfile';
$delkey = !is_file($newkey);
$crypto = [];
foreach (glob('/dev/disk/by-id/*CRYPT-LUKS*',GLOB_NOSORT) as $disk) {
foreach (glob('/dev/disk/by-id/*CRYPT-LUKS*', GLOB_NOSORT) as $disk) {
$disk = explode('-',$disk);
$crypto[] = array_pop($disk);
}
if (count($crypto)==0) die();
if (count($crypto) == 0) reply(_('No encrypted disks found'),'warning');
function delete_file(...$file) {
array_map('unlink',array_filter($file,'is_file'));
array_map('unlink', array_filter($file,'is_file'));
}
function removeKey($key,$disk) {
function removeKey($key, $disk) {
$match = $slots = 0;
$dump = popen("cryptsetup luksDump /dev/$disk",'r');
while (($row = fgets($dump))!==false) {
if (strncmp($row,'Version:',8)==0) {
switch (trim(explode(':',$row)[1])) {
$dump = popen("cryptsetup luksDump ".escapeshellarg("/dev/$disk"), 'r');
while (($row = fgets($dump)) !== false) {
if (strncmp($row,'Version:',8) == 0) {
switch (trim(explode(':', $row)[1])) {
case 1: $match = '/^Key Slot \d+: ENABLED$/'; break;
case 2: $match = '/^\s+\d+: luks2$/'; break;
}
}
if ($match && preg_match($match,$row)) $slots++;
if ($match && preg_match($match, $row)) $slots++;
}
pclose($dump);
if ($slots > 1) exec("cryptsetup luksRemoveKey /dev/$disk $key &>/dev/null");
if ($slots > 1) exec("cryptsetup luksRemoveKey ".shieldarg("/dev/$disk", $key)." &>/dev/null");
}
function diskname($name) {
global $disks;
foreach ($disks as $disk) if (strncmp($name,$disk['device'],strlen($disk['device']))==0) return $disk['name'];
foreach ($disks as $disk) if (strncmp($name, $disk['device'], strlen($disk['device'])) == 0) return $disk['name'];
return $name;
}
function reply($text,$type) {
global $oldkey,$newkey,$delkey;
function reply($text, $type) {
global $oldkey, $newkey;
$reply = _var($_POST,'#reply');
if (realpath(dirname($reply))=='/var/tmp') file_put_contents($reply,$text."\0".$type);
if (realpath(dirname($reply)) == '/var/tmp') file_put_contents($reply, $text."\0".$type);
delete_file($oldkey);
if (_var($_POST,'newinput','text')=='text' || $delkey) delete_file($newkey);
if (_var($_POST,'newinput','text') == 'text') delete_file($newkey);
die();
}
if (isset($_POST['oldinput'])) {
switch ($_POST['oldinput']) {
case 'text':
file_put_contents($oldkey,base64_decode(_var($_POST,'oldluks')));
file_put_contents($oldkey, base64_decode(_var($_POST,'oldluks')));
break;
case 'file':
file_put_contents($oldkey,base64_decode(explode(';base64,',_var($_POST,'olddata','x;base64,'))[1]));
file_put_contents($oldkey, base64_decode(explode(';base64,',_var($_POST,'olddata','x;base64,'))[1]));
break;
}
} else {
if (is_file($newkey)) copy($newkey,$oldkey);
if (is_file($newkey)) copy($newkey, $oldkey);
}
if (is_file($oldkey)) {
$disk = $crypto[0]; // check first disk only (key is the same for all disks)
exec("cryptsetup luksOpen --test-passphrase --key-file $oldkey /dev/$disk &>/dev/null",$null,$error);
exec("cryptsetup luksOpen --test-passphrase --key-file ".shieldarg($oldkey, "/dev/$disk")." &>/dev/null", $null, $error);
} else $error = 1;
if ($error > 0) reply(_('Incorrect existing key'),'warning');
@@ -86,25 +88,25 @@ if ($error > 0) reply(_('Incorrect existing key'),'warning');
if (isset($_POST['newinput'])) {
switch ($_POST['newinput']) {
case 'text':
file_put_contents($newkey,base64_decode(_var($_POST,'newluks')));
file_put_contents($newkey, base64_decode(_var($_POST,'newluks')));
$luks = 'luksKey';
$data = _var($_POST,'newluks');
$data = str_replace('+', '%2B', _var($_POST,'newluks'));
break;
case 'file':
file_put_contents($newkey,base64_decode(explode(';base64,',_var($_POST,'newdata','x;base64,'))[1]));
file_put_contents($newkey, base64_decode(explode(';base64,',_var($_POST,'newdata','x;base64,'))[1]));
$luks = 'luksKey=&luksKeyfile';
$data = $newkey;
break;
}
$good = $bad = [];
foreach ($crypto as $disk) {
exec("cryptsetup luksAddKey --key-file $oldkey /dev/$disk $newkey &>/dev/null",$null,$error);
if ($error==0) $good[] = $disk; else $bad[] = diskname($disk);
exec("cryptsetup luksAddKey --key-file ".shieldarg($oldkey, "/dev/$disk", $newkey)." &>/dev/null", $null, $error);
if ($error == 0) $good[] = $disk; else $bad[] = diskname($disk);
}
if (count($bad)==0) {
if (count($bad) == 0) {
// all okay, remove the old key
foreach ($good as $disk) removeKey($oldkey,$disk);
exec("emcmd 'changeDisk=apply&$luks=$data'");
foreach ($good as $disk) removeKey($oldkey, $disk);
exec("emcmd ".escapeshellarg("changeDisk=apply&$luks=$data"));
reply(_('Key successfully changed'),'success');
} else {
// something went wrong, restore key

View File

@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, 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,
@@ -34,14 +34,17 @@ $locale_init = $locale;
function initSum() {
return ['count'=>0, 'temp'=>0, 'power'=>0, 'fsSize'=>0, 'fsUsed'=>0, 'fsFree'=>0, 'ioReads'=>0, 'ioWrites'=>0, 'numReads'=>0, 'numWrites'=>0, 'numErrors'=>0];
}
function get_model($id) {
return substr($id,0,strrpos($id,'_'));
}
function my_power($power) {
global $display;
$number = _var($display,'number','.,');
return _var($display,'power') && $power ? number_format($power,$power<10?2:1,$number[0]).' '._('W').' / ' : '';
}
function device_info(&$disk,$online) {
global $pools, $var;
if (!$online || _var($disk,'fsStatus')!='Mounted' || (in_array(_var($disk,'type'),['Parity','Cache']) && (!in_array(_var($disk,'name'),$pools) || isSubpool(_var($disk,'name'))))) {
@@ -84,6 +87,7 @@ function device_info(&$disk,$online) {
: $fancy;
return $view.$status.$link;
}
function device_desc(&$disk) {
global $var;
$size = my_scale(_var($disk,'sectors',0)*_var($disk,'sector_size',0),$unit,-1);
@@ -101,6 +105,7 @@ function device_desc(&$disk) {
return my_id(_var($disk,'id'))." - $size $unit ("._var($disk,'device').")";
}
}
function assignment(&$disk) {
global $var, $devs;
$echo = [];
@@ -119,18 +124,18 @@ function assignment(&$disk) {
$echo[] = "</select></form>";
return implode($echo);
}
function vfs_luks($fs) {
return str_starts_with($fs,'luks:');
}
function vfs_type(&$disk,$online = false) {
global $disks, $pools, $crypto;
$fsType = _var($disk,'fsType','');
$luks = '';
if (empty($fsType))
return $fsType;
if ($crypto) switch (_var($disk,'luksState',0)) {
if (empty($fsType)) return;
if (vfs_luks($fsType) && $crypto) switch (_var($disk,'luksState',0)) {
case 0:
if (vfs_luks($fsType))
$luks = "<a class='info'><i class='padlock fa fa-unlock-alt orange-text'></i><span>"._('Device to be encrypted')."</span></a>";
break;
case 1:
@@ -151,6 +156,7 @@ function vfs_type(&$disk,$online = false) {
}
return $luks.str_replace('luks:','',$fsType);
}
function fs_info(&$disk,$online = false) {
global $display;
$echo = [];
@@ -176,9 +182,11 @@ function fs_info(&$disk,$online = false) {
}
return implode($echo);
}
function my_diskio($data) {
return my_scale($data,$unit,1)." $unit/s";
}
function array_offline(&$disk, $pool='') {
global $var, $disks, $display;
$disk['power'] ??= (_var($display,'power') && _var($disk,'transport')=='nvme' ? get_nvme_info(_var($disk,'device'),'power') : 0);
@@ -256,6 +264,7 @@ function array_offline(&$disk, $pool='') {
$echo[] = "</tr>";
return implode($echo);
}
function array_online(&$disk, $fstype='') {
global $pools, $sum, $diskio;
$disk['power'] ??= (_var($disk,'transport')=='nvme' ? get_nvme_info(_var($disk,'device'),'power') : 0);
@@ -310,6 +319,7 @@ function array_online(&$disk, $fstype='') {
$echo[] = "</tr>";
return implode($echo);
}
function show_totals($text,$array,$name) {
global $var, $display, $sum, $locale;
$number = _var($display,'number','.,');
@@ -346,6 +356,7 @@ function show_totals($text,$array,$name) {
$echo[] = "</tr>";
return implode($echo);
}
function array_slots() {
global $var;
$min = max(_var($var,'sbNumDisks',0),3);
@@ -364,6 +375,7 @@ function array_slots() {
$echo[] = "</select></form>";
return implode($echo);
}
function cache_slots($off,$pool,$min,$slots) {
global $var, $disks;
// $off = $off && $min ? ' disabled' : '';
@@ -386,6 +398,7 @@ function cache_slots($off,$pool,$min,$slots) {
$echo[] = "</select></form>";
return implode($echo);
}
function update_translation($locale) {
global $docroot,$language;
$language = [];
@@ -404,6 +417,7 @@ function update_translation($locale) {
}
}
}
while (true) {
$var = (array)@parse_ini_file("$varroot/var.ini");
$devs = (array)@parse_ini_file("$varroot/devs.ini",true);

View File

@@ -15,6 +15,14 @@ else
log "no queued job present"
fi
echo "sleep ${1:-1}; /usr/local/emhttp/webGui/scripts/reload_services" | at -M now 2>/dev/null
log "queue new job $(queue), wait for ${1:-1}s"
# Validate delay parameter is numeric
if [[ -n $1 && ! $1 =~ ^[0-9]+$ ]]; then
log "invalid delay parameter: $1, using default"
DELAY=1
else
DELAY=${1:-1}
fi
echo "sleep $DELAY; /usr/local/emhttp/webGui/scripts/reload_services" | at -M now 2>/dev/null
log "queue new job $(queue), wait for ${DELAY}s"
exit 0

View File

@@ -16,7 +16,7 @@ nchan_idle(){
idle=3
for n in {1..3}; do
subs=$(nchan_subs)
[[ -z $subs || $subs =~ ^[0-9]+$ && $subs -eq 0 ]] && ((idle--))
[[ -z $subs || ( $subs =~ ^[0-9]+$ && $subs -eq 0 ) ]] && ((idle--))
sleep 3
done
[[ $idle -eq 0 ]]