summaryrefslogblamecommitdiffstats
path: root/modules-available/passthrough/page.inc.php
blob: 89ee7719cbfd8782636e07a296b1426db9bf1edf (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                  

                                                        







                                                          
                                                     






























                                                                                                                                             























                                                                                                                                            








                                                  

                                                       




                                                                      

                                                                          
                       
           




                                                                              
                                           


                                                    
                                                             





















                                                                                                                           

                                                                               

                                                                                                                           
                                                        



                                                                                                                    
                                                                   







                                                                                       
                                                                                                



                                                                                                    

         






                                                                                   















                                                                                                                                     


                                                        
                                                                         


                   









                                   
<?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()
	{
		//
	}

}