diff options
Diffstat (limited to 'modules-available/passthrough/page.inc.php')
-rw-r--r-- | modules-available/passthrough/page.inc.php | 195 |
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 |