mirror of
https://github.com/unraid/webgui.git
synced 2026-04-24 19:29:13 -05:00
Merge pull request #2068 from SimonFair/general-protection-fix
7.x Save PCI Data.
This commit is contained in:
@@ -39,6 +39,7 @@ $i = 0;
|
||||
$kvm = ['var kvm=[];'];
|
||||
$show = explode(',',unscript(_var($_GET,'show')));
|
||||
$path = _var($domain_cfg,'MEDIADIR');
|
||||
$pci_device_changes = comparePCIData();
|
||||
|
||||
foreach ($vms as $vm) {
|
||||
$res = $lv->get_domain_by_name($vm);
|
||||
@@ -52,6 +53,11 @@ foreach ($vms as $vm) {
|
||||
$image = substr($icon,-4)=='.png' ? "<img src='$icon' class='img'>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
|
||||
$arrConfig = domain_to_config($uuid);
|
||||
$snapshots = getvmsnapshots($vm) ;
|
||||
$vmpciids = $lv->domain_get_vm_pciids($vm);
|
||||
$pcierror = false;
|
||||
foreach($vmpciids as $pciid => $pcidetail) {
|
||||
if (isset($pci_device_changes["0000:".$pciid])) $pcierror = true;
|
||||
}
|
||||
$cdroms = $lv->get_cdrom_stats($res,true,true) ;
|
||||
if ($state == 'running') {
|
||||
$mem = $dom['memory']/1024;
|
||||
@@ -117,7 +123,7 @@ foreach ($vms as $vm) {
|
||||
unset($dom);
|
||||
if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web" ; else $vmrcconsole = $domain_cfg["CONSOLE"] ;
|
||||
if (!isset($domain_cfg["RDPOPT"])) $vmrcconsole .= ";no" ; else $vmrcconsole .= ";".$domain_cfg["RDPOPT"] ;
|
||||
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s', '%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log),addslashes($fstype), $vmrcconsole,false,addslashes(str_replace('"',"'",$WebUI)));
|
||||
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s', '%s', %s)\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log),addslashes($fstype), $vmrcconsole,false,addslashes(str_replace('"',"'",$WebUI)),$pcierror);
|
||||
$kvm[] = "kvm.push({id:'$uuid',state:'$state'});";
|
||||
switch ($state) {
|
||||
case 'running':
|
||||
@@ -196,7 +202,10 @@ foreach ($vms as $vm) {
|
||||
$title = _('Select ISO image');
|
||||
$cdstr = $cdromcount." / 2<a class='hand' title='$title' href='#' onclick='$changemedia'><i class='fa fa-dot-circle-o'></i></a>";
|
||||
echo "<tr parent-id='$i' class='sortable'><td class='vm-name' style='width:220px;padding:8px'><i class='fa fa-arrows-v mover orange-text'></i>";
|
||||
echo "<span class='outer'><span id='vm-$uuid' $menu class='hand'>$image</span><span class='inner'><a href='#' onclick='return toggle_id(\"name-$i\")' title='click for more VM info'>$vm</a><br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)." </span></span></span></td>";
|
||||
echo "<span class='outer'><span id='vm-$uuid' $menu class='hand'>$image</span>";
|
||||
echo "<span class='inner'><a href='#' onclick='return toggle_id(\"name-$i\")' title='click for more VM info'>$vm</a>";
|
||||
if ($pcierror) echo "<i class=\"fa fa-warning fa-fw orange-text\" title=\""._('PCI Changed')."\n"._('Start disabled')."\"></i>";
|
||||
echo "<br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)." </span></span></span></td>";
|
||||
echo "<td>$desc</td>";
|
||||
echo "<td><a class='vcpu-$uuid' style='cursor:pointer'>$vcpu</a></td>";
|
||||
echo "<td>$mem</td>";
|
||||
|
||||
@@ -2257,6 +2257,18 @@ class Libvirt {
|
||||
return ['pci' => $devs_pci, 'usb' => $devs_usb];
|
||||
}
|
||||
|
||||
function domain_get_vm_pciids($domain) {
|
||||
$hostdevs=$this->domain_get_host_devices_pci($domain);
|
||||
$vmpcidevs=[];
|
||||
foreach($hostdevs as $key => $dev) {
|
||||
$vmpcidevs[$dev['id']] = [
|
||||
'vendor_id' => ltrim($dev['vendor_id'] ?? "", '0x'),
|
||||
'device_id' => ltrim($dev['product_id'] ?? "", '0x'),
|
||||
];
|
||||
}
|
||||
return $vmpcidevs;
|
||||
}
|
||||
|
||||
function get_nic_info($domain) {
|
||||
$macs = $this->get_xpath($domain, "//domain/devices/interface/mac/@address", false);
|
||||
if (!$macs) return $this->_set_last_error();
|
||||
|
||||
@@ -83,7 +83,7 @@ function ajaxVMDispatchWebUI(params, spin){
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, fstype="QEMU",consolein="web;no",usage=false,webui=""){
|
||||
function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, fstype="QEMU",consolein="web;no",usage=false,webui="",pcierror=false){
|
||||
var opts = [];
|
||||
var path = location.pathname;
|
||||
var x = path.indexOf("?");
|
||||
@@ -172,22 +172,28 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, f
|
||||
ajaxVMDispatch({action:"domain-destroy", uuid:uuid}, "loadlist");
|
||||
}});
|
||||
} else {
|
||||
opts.push({text:_("Start"), icon:"fa-play", action:function(e) {
|
||||
e.preventDefault();
|
||||
ajaxVMDispatch({action:"domain-start", uuid:uuid}, "loadlist");
|
||||
}});
|
||||
if (vmrcprotocol == "VNC" || vmrcprotocol == "SPICE") {
|
||||
if (console == "web" || console == "both") {
|
||||
opts.push({text:_("Start with console")+ " (" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
|
||||
e.preventDefault();
|
||||
ajaxVMDispatchconsole({action:"domain-start-console", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
|
||||
}});}
|
||||
if (console == "remote" || console == "both") {
|
||||
opts.push({text:_("Start with remote-viewer")+ " (" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
|
||||
e.preventDefault();
|
||||
ajaxVMDispatchconsoleRV({action:"domain-start-consoleRV", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
|
||||
}});
|
||||
if (!pcierror) {
|
||||
opts.push({text:_("Start"), icon:"fa-play", action:function(e) {
|
||||
e.preventDefault();
|
||||
ajaxVMDispatch({action:"domain-start", uuid:uuid}, "loadlist");
|
||||
}});
|
||||
if (vmrcprotocol == "VNC" || vmrcprotocol == "SPICE") {
|
||||
if (console == "web" || console == "both") {
|
||||
opts.push({text:_("Start with console")+ " (" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
|
||||
e.preventDefault();
|
||||
ajaxVMDispatchconsole({action:"domain-start-console", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
|
||||
}});}
|
||||
if (console == "remote" || console == "both") {
|
||||
opts.push({text:_("Start with remote-viewer")+ " (" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
|
||||
e.preventDefault();
|
||||
ajaxVMDispatchconsoleRV({action:"domain-start-consoleRV", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
|
||||
}});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
opts.push({text:_("Start disabled due to PCI Changes"), icon:"fa fa-minus-circle orb red-orb", action:function(e) {
|
||||
e.preventDefault();
|
||||
}});
|
||||
}
|
||||
}
|
||||
opts.push({divider:true});
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
/* Copyright 2005-2024, Lime Technology
|
||||
* Copyright 2024, Simon Fairweather
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
# Command for bash script /usr/libexec/virtiofsd
|
||||
# eval exec /usr/bin/virtiofsd $(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/virtiofsd.php "$@")
|
||||
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
|
||||
$pci_device_changes = comparePCIData();
|
||||
$pcierror = false;
|
||||
$pci_addresses = [];
|
||||
foreach ($argv as $arg) {
|
||||
if (preg_match('/"host"\s*:\s*"([^"]+)"/', $arg, $matches)) {
|
||||
$pci_addresses[] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($pci_addresses as $pciid) {
|
||||
if (isset($pci_device_changes[$pciid])) {
|
||||
$pcierror = true;
|
||||
}
|
||||
}
|
||||
|
||||
echo $pcierror == true ? "yes" : "no";
|
||||
?>
|
||||
@@ -46,6 +46,58 @@ function applyCfg() {
|
||||
$("#applycfg").attr("disabled",true);
|
||||
});
|
||||
}
|
||||
function formatFullInput(input) {
|
||||
return input
|
||||
.split(';')
|
||||
.filter(Boolean) // remove empty trailing entry
|
||||
.map(entry => {
|
||||
let [pci, status] = entry.split(',');
|
||||
status = status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
|
||||
return `${pci} _(${status})_`;
|
||||
})
|
||||
.join('<br>');
|
||||
}
|
||||
function formatVMS(input) {
|
||||
return input
|
||||
.split(';')
|
||||
.filter(Boolean) // remove empty trailing entry
|
||||
.join('<br>');
|
||||
}
|
||||
function ackPCI(pcidevice, action) {
|
||||
$.post('/webGui/include/PCIUpdate.php', { action: "getvm", pciid: pcidevice }).done(function(vms) {
|
||||
let swaltext = "";
|
||||
|
||||
switch(action) {
|
||||
case 'removed':
|
||||
swaltext = "_(Acknowledge removal of PCI Address)_: " + pcidevice + "<br>_(VMs where PCI device used)_<br>" + formatVMS(vms);
|
||||
break;
|
||||
case 'changed':
|
||||
swaltext = "_(Acknowledge update of PCI Address)_: " + pcidevice + "<br>_(VMs where PCI device used)_<br>"+ formatVMS(vms);
|
||||
break;
|
||||
case 'added':
|
||||
swaltext = "_(Acknowledge addition of PCI Address)_: " + pcidevice + "<br>_(VMs where PCI device used)_<br>" + formatVMS(vms);
|
||||
break;
|
||||
case 'all':
|
||||
swaltext = "_(Acknowledge all PCI Address modifications)_: <br>" + formatFullInput(pcidevice) + "<br>_(VMs where PCI device used)_<br>" + formatVMS(vms);
|
||||
break;
|
||||
}
|
||||
|
||||
swal({
|
||||
title: "Are you sure?",
|
||||
text: swaltext,
|
||||
type: "warning",
|
||||
html: true,
|
||||
showCancelButton: true
|
||||
}, function() {
|
||||
$.post('/webGui/include/PCIUpdate.php', { action: action, pciid: pcidevice }).done(function(d) {
|
||||
if (d === "OK") {
|
||||
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<?exec('ls /sys/kernel/iommu_groups/',$groups); ?>
|
||||
|
||||
@@ -115,6 +115,7 @@ if ($_POST['vms']) {
|
||||
}
|
||||
echo "<tr title='' class='updated'><td>";
|
||||
$running = 0;
|
||||
$pci_device_changes = comparePCIData();
|
||||
foreach ($vms as $vm) {
|
||||
$res = $lv->get_domain_by_name($vm);
|
||||
$uuid = libvirt_domain_get_uuid_string($res);
|
||||
@@ -155,8 +156,15 @@ if ($_POST['vms']) {
|
||||
$log = (is_file("/var/log/libvirt/qemu/$vm.log") ? "libvirt/qemu/$vm.log" : '');
|
||||
if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web"; else $vmrcconsole = $domain_cfg["CONSOLE"];
|
||||
if (!isset($domain_cfg["RDPOPT"])) $vmrcconsole .= ";no"; else $vmrcconsole .= ";".$domain_cfg["RDPOPT"];
|
||||
$WebUI = html_entity_decode($arrConfig["template"]["webui"]);
|
||||
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm), addslashes($uuid), addslashes($template), $state, addslashes($vmrcurl), strtoupper($vmrcprotocol), addslashes($log),addslashes($fstype), $vmrcconsole,false,addslashes(str_replace('"',"'",$WebUI)));
|
||||
$WebUI = html_entity_decode($arrConfig["template"]["webui"]??"");
|
||||
$vmpciids = $lv->domain_get_vm_pciids($vm);
|
||||
$pcierror = false;
|
||||
foreach($vmpciids as $pciid => $pcidetail) {
|
||||
if (isset($pci_device_changes["0000:".$pciid])) {
|
||||
$pcierror = true;
|
||||
}
|
||||
}
|
||||
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm), addslashes($uuid), addslashes($template), $state, addslashes($vmrcurl), strtoupper($vmrcprotocol), addslashes($log),addslashes($fstype), $vmrcconsole,false,addslashes(str_replace('"',"'",$WebUI)),$pcierror);
|
||||
$icon = $lv->domain_get_icon_url($res);
|
||||
switch ($state) {
|
||||
case 'running':
|
||||
@@ -178,7 +186,9 @@ if ($_POST['vms']) {
|
||||
break;
|
||||
}
|
||||
$image = substr($icon,-4)=='.png' ? "<img src='$icon' class='img'>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
|
||||
echo "<span class='outer solid vms $status'><span id='vm-$uuid' $menu class='hand'>$image</span><span class='inner'>$vm<br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span></span>";
|
||||
echo "<span class='outer solid vms $status'><span id='vm-$uuid' $menu class='hand'>$image</span><span class='inner'>$vm";
|
||||
if ($pcierror) echo "<i class=\"fa fa-warning fa-fw orange-text\" title=\""._('PCI Changed')."\n"._('Start disabled')."\"></i>";
|
||||
echo "<br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span></span>";
|
||||
if ($state == "running") {
|
||||
#Build VM Usage array.
|
||||
$menuusage = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm), addslashes($uuid), addslashes($template), $state, addslashes($vmrcurl), strtoupper($vmrcprotocol), addslashes($log),addslashes($fstype), $vmrcconsole,true,addslashes(str_replace('"',"'",$WebUI)));
|
||||
|
||||
@@ -416,4 +416,99 @@ 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));
|
||||
}
|
||||
|
||||
// Load saved PCI data
|
||||
function loadSavedData($filename) {
|
||||
if (file_exists($filename)) {
|
||||
$saveddata = file_get_contents($filename);
|
||||
} else $saveddata = "";
|
||||
|
||||
return json_decode($saveddata, true);
|
||||
}
|
||||
|
||||
// Run lspci -Dmn to get the current devices
|
||||
function loadCurrentPCIData() {
|
||||
$output = shell_exec('lspci -Dmn');
|
||||
$devices = [];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return $devices;
|
||||
}
|
||||
|
||||
// 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
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2025, Lime Technology
|
||||
* Copyright 2012-2025, Bergware International.
|
||||
* Copyright 2012-2025, Simon Fairweather.
|
||||
*
|
||||
* 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 ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
|
||||
|
||||
function process_action($pciaddr,$action)
|
||||
{
|
||||
global $saved,$current;
|
||||
switch ($action) {
|
||||
case 'removed':
|
||||
unset($saved[$pciaddr]);
|
||||
break;
|
||||
case 'changed':
|
||||
case 'added':
|
||||
$saved[$pciaddr] = $current[$pciaddr];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function build_pci_vm_map() {
|
||||
global $lv;
|
||||
$pci_device_changes = comparePCIData();
|
||||
$vms = $lv->get_domains();
|
||||
foreach ($vms as $vm) {
|
||||
$vmpciids = $lv->domain_get_vm_pciids($vm);
|
||||
foreach($vmpciids as $pciid => $pcidetail) {
|
||||
if (isset($pci_device_changes["0000:".$pciid])) {
|
||||
$pcitovm["0000:".$pciid][$vm] = $vm;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pcitovm;
|
||||
}
|
||||
|
||||
$savedfile = "/boot/config/savedpcidata.json";
|
||||
$saved = loadSavedData($savedfile);
|
||||
if (!$saved) {echo "ERROR"; return;};
|
||||
$current = loadCurrentPCIData();
|
||||
$pciaddr = $_POST['pciid'];
|
||||
$action = $_POST['action']??'';
|
||||
|
||||
switch($action) {
|
||||
case "all":
|
||||
$pciaddrs = explode(";", $pciaddr);
|
||||
foreach ($pciaddrs as $pciaddraction){
|
||||
if ($pciaddraction == "") continue;
|
||||
$values = explode(',',$pciaddraction);
|
||||
process_action($values[0],$values[1]);
|
||||
}
|
||||
file_put_contents($savedfile,json_encode($saved,JSON_PRETTY_PRINT));
|
||||
break;
|
||||
case "removed":
|
||||
case "added":
|
||||
case "changed":
|
||||
process_action($pciaddr,$action);
|
||||
file_put_contents($savedfile,json_encode($saved,JSON_PRETTY_PRINT));
|
||||
break;
|
||||
case "getvm":
|
||||
$pcimap = build_pci_vm_map();
|
||||
$pciaddrs = explode(";", $pciaddr);
|
||||
$vmact =[];
|
||||
|
||||
foreach ($pciaddrs as $pcidev) {
|
||||
if ($pcidev == "") continue;
|
||||
if (strpos($pcidev,",")) {
|
||||
$values = explode(',',$pcidev);
|
||||
$pcidev = $values[0];
|
||||
}
|
||||
foreach ($pcimap[$pcidev] as $key => $vmname) {
|
||||
$vmact[$vmname]= $vmname;
|
||||
}
|
||||
}
|
||||
$ret = implode(";",$vmact);
|
||||
|
||||
echo $ret;
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
file_put_contents($savedfile,json_encode($saved,JSON_PRETTY_PRINT));
|
||||
echo "OK";
|
||||
?>
|
||||
@@ -18,6 +18,8 @@ require_once "$docroot/webGui/include/Helpers.php";
|
||||
$_SERVER['REQUEST_URI'] = 'tools';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$pci_device_diffs = comparePCIData();
|
||||
|
||||
function usb_physical_port($usbbusdev) {
|
||||
if (preg_match('/^Bus (?P<bus>\S+) Device (?P<dev>\S+): ID (?P<id>\S+)(?P<name>.*)$/', $usbbusdev, $usbMatch)) {
|
||||
//udevadm info -a --name=/dev/bus/usb/003/002 | grep KERNEL==
|
||||
@@ -115,6 +117,15 @@ case 't1':
|
||||
$iommuinuse[] = (strpos($string,'/')) ? strstr($string, '/', true) : $string;
|
||||
}
|
||||
exec('lsscsi -s',$lsscsi);
|
||||
// Filter for 'removed' devices
|
||||
$removedArr = array_filter($pci_device_diffs, function($entry) {
|
||||
return isset($entry['status']) && $entry['status'] === 'removed';
|
||||
});
|
||||
foreach ($removedArr as $removedpci => $removeddata) {
|
||||
$groups[] = "IOMMU "._("Removed");
|
||||
$groups[] = "\tR[{$removeddata['device']['vendor_id']}:{$removeddata['device']['device_id']}] ".str_replace("0000:","",$removedpci)." ".trim($removeddata['device']['description'],"\n");
|
||||
}
|
||||
$ackparm = "";
|
||||
foreach ($groups as $line) {
|
||||
if (!$line) continue;
|
||||
if ($line[0]=='I') {
|
||||
@@ -126,6 +137,8 @@ case 't1':
|
||||
$line = preg_replace("/^\t/","",$line);
|
||||
$vd = trim(explode(" ", $line)[0], "[]");
|
||||
$pciaddress = explode(" ", $line)[1];
|
||||
$removed = $line[0]=='R' ? true : false;
|
||||
if ($removed) $line=preg_replace('/R/', '', $line, 1);
|
||||
if (preg_match($BDF_REGEX, $pciaddress)) {
|
||||
// By default lspci does not output the <Domain> when the only domain in the system is 0000. Add it back.
|
||||
$pciaddress = "0000:".$pciaddress;
|
||||
@@ -140,13 +153,32 @@ case 't1':
|
||||
if ((strpos($line, 'Host bridge') === false) && (strpos($line, 'PCI bridge') === false)) {
|
||||
if (file_exists('/sys/kernel/iommu_groups/'.$iommu.'/devices/'.$pciaddress.'/reset')) echo "<i class=\"fa fa-retweet grey-orb middle\" title=\"",_('Function Level Reset (FLR) supported'),".\"></i>";
|
||||
echo "</td><td>";
|
||||
if (!$removed) {
|
||||
echo in_array($iommu, $iommuinuse) ? '<input type="checkbox" value="" title="'._('In use by Unraid').'" disabled ' : '<input type="checkbox" class="iommu'.$iommu.'" value="'.$pciaddress."|".$vd.'" ';
|
||||
// check config file for two formats: <Domain:Bus:Device.Function>|<Vendor:Device> or just <Domain:Bus:Device.Function>
|
||||
echo (in_array($pciaddress."|".$vd, $vfio_cfg_devices) || in_array($pciaddress, $vfio_cfg_devices)) ? " checked>" : ">";
|
||||
}
|
||||
} else { echo "</td><td>"; }
|
||||
echo '</td><td title="';
|
||||
foreach ($outputvfio as $line2) echo htmlentities($line2,ENT_QUOTES)." ";
|
||||
echo '">',$line,'</td></tr>';
|
||||
if (array_key_exists($pciaddress,$pci_device_diffs)) {
|
||||
echo "<tr><td></td><td><td></td><td></td><td>";
|
||||
echo "<i class=\"fa fa-warning fa-fw orange-text\" title=\""._('PCI Change')."\n"._('Click to acknowledge').".\" onclick=\"ackPCI('".htmlentities($pciaddress)."','".htmlentities($pci_device_diffs[$pciaddress]['status'])."')\"></i>";
|
||||
echo _("PCI Device change");
|
||||
echo " "._("Action").":".ucfirst(_($pci_device_diffs[$pciaddress]['status']))." ";
|
||||
$ackparm .= $pciaddress.",".$pci_device_diffs[$pciaddress]['status'].";";
|
||||
if ($pci_device_diffs[$pciaddress]['status']!="removed") echo $pci_device_diffs[$pciaddress]['device']['description'];
|
||||
echo "</td></tr>";
|
||||
if ($pci_device_diffs[$pciaddress]['status']=="changed") {
|
||||
echo "<tr><td></td><td><td></td><td></td><td>";
|
||||
echo _("Differences");
|
||||
foreach($pci_device_diffs[$pciaddress]['differences'] as $key => $changes){
|
||||
echo " $key "._("before").":{$changes['old']} "._("after").":{$changes['new']} ";
|
||||
}
|
||||
echo "</td></tr>";
|
||||
}
|
||||
}
|
||||
unset($outputvfio);
|
||||
switch (true) {
|
||||
case (strpos($line, 'USB controller') !== false):
|
||||
@@ -194,8 +226,10 @@ case 't1':
|
||||
if (file_exists("/var/log/vfio-pci") && filesize("/var/log/vfio-pci")) {
|
||||
echo '<input id="viewlog" type="button" value="'._('View VFIO-PCI Log').'" onclick="openTerminal(\'log\',\'vfio-pci\',\'vfio-pci\')">';
|
||||
}
|
||||
if ($ackparm == "") $ackdisable =" disabled "; else $ackdisable = "";
|
||||
echo '<input id="applycfg" type="submit" disabled value="'._('Bind selected to VFIO at Boot').'" onclick="applyCfg();" '.(isset($noiommu) ? "style=\"display:none\"" : "").'>';
|
||||
echo '<span id="warning"></span>';
|
||||
echo '<input id="applypci" type="submit"'.$ackdisable.' value="'._('Acknowledge all PCI changes').'" onclick="ackPCI(\''.htmlentities($ackparm).'\',\'all\')" >';
|
||||
echo '</td></tr>';
|
||||
echo <<<EOT
|
||||
<script>
|
||||
|
||||
@@ -76,6 +76,10 @@ if [[ -x /sbin/hwclock ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Save PCI data to validate system changes at system start.
|
||||
log "Save PCI Configuration."
|
||||
/usr/local/sbin/savepcidata
|
||||
|
||||
# Run any local shutdown scripts:
|
||||
if [[ -x /etc/rc.d/rc.local_shutdown ]]; then
|
||||
/etc/rc.d/rc.local_shutdown stop
|
||||
|
||||
@@ -6,4 +6,11 @@ if [ $DISABLE == "yes" ]
|
||||
printf '\n%s\n' "Start/autostart is disabled in VM settings." >&2 ## Send message to stderr.
|
||||
exit 1 ;
|
||||
fi
|
||||
PCI="no"
|
||||
PCI=$(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php "$@");
|
||||
if [ $PCI == "yes" ]
|
||||
then
|
||||
printf '\n%s\n' "Start/autostart is disabled PCI Change detected." >&2 ## Send message to stderr.
|
||||
exit 1 ;
|
||||
fi
|
||||
eval exec /usr/bin/qemu-system-x86_64 $(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/qemu.php "$@")
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
$output = shell_exec('lspci -Dmn');
|
||||
$devices = [];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
file_put_contents("/boot/config/savedpcidata.json",json_encode($devices,JSON_PRETTY_PRINT));
|
||||
?>
|
||||
Reference in New Issue
Block a user