summaryrefslogtreecommitdiffstats
path: root/modules-available/passthrough/page.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/passthrough/page.inc.php')
-rw-r--r--modules-available/passthrough/page.inc.php195
1 files changed, 195 insertions, 0 deletions
diff --git a/modules-available/passthrough/page.inc.php b/modules-available/passthrough/page.inc.php
new file mode 100644
index 00000000..89ee7719
--- /dev/null
+++ b/modules-available/passthrough/page.inc.php
@@ -0,0 +1,195 @@
+<?php
+
+class Page_Passthrough extends Page
+{
+
+ protected function doPreprocess()
+ {
+ User::load();
+ User::assertPermission('view');
+ $action = Request::post('action');
+ if ($action === 'save-hwlist') {
+ $this->saveHwList();
+ } elseif ($action === 'save-location') {
+ $this->saveLocation();
+ }
+ if (Request::isPost()) {
+ Util::redirect('?do=passthrough');
+ }
+ }
+
+ private function saveHwList()
+ {
+ User::assertPermission('edit.group');
+ $newgroups = Request::post('newgroup', [], 'array');
+ foreach ($newgroups as $id => $title) {
+ $id = strtoupper(preg_replace('/[^a-z0-9_\-]/i', '', $id));
+ if (empty($id))
+ continue;
+ Database::exec("INSERT IGNORE INTO passthrough_group (groupid, title)
+ VALUES (:group, :title)",
+ ['group' => $id, 'title' => $title]);
+ }
+ $groups = Request::post('ptgroup', Request::REQUIRED, 'array');
+ $insert = [];
+ $delete = [];
+ foreach ($groups as $hwid => $group) {
+ if (empty($group)) {
+ $delete[] = $hwid;
+ } else {
+ $insert[] = [$hwid, '@PASSTHROUGH', $group];
+ }
+ }
+ if (!empty($insert)) {
+ Database::exec("INSERT INTO statistic_hw_prop (hwid, prop, `value`) VALUES :list
+ ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)",
+ ['list' => $insert]);
+ }
+ if (!empty($delete)) {
+ Database::exec("DELETE FROM statistic_hw_prop WHERE hwid IN (:list) AND prop = '@PASSTHROUGH'", ['list' => $delete]);
+ }
+ Message::addSuccess('list-updated');
+ Util::redirect('?do=passthrough&show=hwlist');
+ }
+
+ private function saveLocation()
+ {
+ $locationId = Request::post('locationid', Request::REQUIRED, 'int');
+ User::assertPermission('edit.location', $locationId);
+ $list = [];
+ $groups = [];
+ foreach (Request::post('enabled', [], 'array') as $groupId) {
+ $groupId = (string)$groupId;
+ $list[] = [$groupId, $locationId];
+ $groups[] = $groupId;
+ }
+ if (!empty($list)) {
+ Database::exec("INSERT IGNORE INTO passthrough_group_x_location (groupid, locationid)
+ VALUES :list", ['list' => $list]);
+ Database::exec("DELETE FROM passthrough_group_x_location
+ WHERE locationid = :lid AND groupid NOT IN (:groups)", ['lid' => $locationId, 'groups' => $groups]);
+ } else {
+ Database::exec("DELETE FROM passthrough_group_x_location
+ WHERE locationid = :lid", ['lid' => $locationId]);
+ }
+ Message::addSuccess('location-updated', Location::getName($locationId));
+ Util::redirect('?do=passthrough&show=assignlocation&locationid=' . $locationId);
+ }
+
+ /*
+ *
+ */
+
+ protected function doRender()
+ {
+ $show = Request::get('show');
+ if ($show === 'hwlist') {
+ $this->showHardwareList();
+ } elseif ($show === 'assignlocation') {
+ $this->showLocationMapping();
+ } else {
+ Util::redirect('?do=passthrough&show=hwlist');
+ }
+ }
+
+ /**
+ * Show all the hardware that is known. Start with video adapters.
+ * @return void
+ */
+ private function showHardwareList()
+ {
+ $q = new HardwareQuery(HardwareInfo::PCI_DEVICE, null, false);
+ $q->addGlobalColumn('vendor');
+ $q->addGlobalColumn('device');
+ $q->addGlobalColumn('rev');
+ $q->addGlobalColumn('class');
+ $q->addGlobalColumn('@PASSTHROUGH');
+ $rows = [];
+ foreach ($q->query('`shw`.`hwid`') as $row) {
+ $row['ptlist'] = Passthrough::getGroupDropdown($row);
+ $rows[] = $row;
+ }
+ // Sort Graphics Cards first, rest by class, vendor, device
+ usort($rows, function ($row1, $row2) {
+ $a = $row1['class'];
+ $b = $row2['class'];
+ if ($a === $b)
+ return hexdec($row1['vendor'].$row1['device']) - hexdec($row2['vendor'] . $row2['device']);
+ if ($a === '0300')
+ return -1;
+ if ($b === '0300')
+ return 1;
+ return hexdec($a) - hexdec($b);
+ });
+ $finalRows = [];
+ $missing = [];
+ $lastClass = '';
+ foreach ($rows as $row) {
+ if ($row['class'] !== $lastClass) {
+ // Add class row header
+ $lastClass = $row['class'];
+ $finalRows[$lastClass] = [
+ 'collapse' => $row['class'] !== '0300',
+ 'class' => $row['class'],
+ 'class_name' => PciId::getPciId(PciId::DEVCLASS, $row['class'], true) ?: 'Unknown',
+ 'devlist' => [],
+ ];
+ }
+ $row['vendor_name'] = PciId::getPciId(PciId::VENDOR, $row['vendor'] ?? '');
+ $row['device_name'] = PciId::getPciId(PciId::DEVICE, $row['vendor'] . ':' . $row['device']);
+ $finalRows[$lastClass]['devlist'][] = $row;
+ // Build up query
+ if ($row['vendor_name'] === false) {
+ $missing[$row['vendor']] = true;
+ }
+ if ($row['device_name'] === false) {
+ $missing[$row['vendor'] . ':' . $row['device']] = true;
+ }
+ }
+ Render::addTemplate('hardware-list', ['classlist' => array_values($finalRows)]);
+ if (!empty($missing)) {
+ Render::addTemplate('js-pciquery',
+ ['missing_ids' => json_encode(array_keys($missing))], 'statistics');
+ }
+ }
+
+ /**
+ * Show mapping between specific location and passthrough groups.
+ * @return void
+ */
+ private function showLocationMapping()
+ {
+ $locationId = Request::get('locationid', Request::REQUIRED, 'int');
+ $locationIds = Location::getLocationRootChain($locationId);
+ $res = Database::queryAll("SELECT g.groupid, g.title, GROUP_CONCAT(gxl.locationid) AS lids FROM passthrough_group g
+ LEFT JOIN passthrough_group_x_location gxl ON (g.groupid = gxl.groupid AND gxl.locationid IN (:lids))
+ GROUP BY groupid, title
+ ORDER BY lids ASC",
+ ['lids' => $locationIds]);
+ foreach ($res as &$item) {
+ if ($item['lids'] === null)
+ continue;
+ $item['checked'] = 'checked';
+ $list = explode(',', $item['lids']);
+ if (!in_array($locationId, $list)) {
+ $item['disabled'] = true;
+ $item['parent_location'] = Location::getName($list[0]);
+ }
+ }
+ Render::addTemplate('location-assign', [
+ 'list' => array_reverse($res),
+ 'locationid' => $locationId,
+ 'locationname' => Location::getName($locationId),
+ ]);
+ }
+
+ /*
+ *
+ */
+
+ protected function doAjax()
+ {
+ //
+ }
+
+} \ No newline at end of file